/*
 * Copyright (C) 2006-2008, R3vis Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA,
 * or visit http://www.gnu.org/copyleft/gpl.html.
 *
 * Contributor(s):
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: psTest1.c,v 1.5 2008/11/09 14:37:04 wes Exp $
 * $Revision: 1.5 $
 * $Name: v180-alpha-01 $
 * $Log: psTest1.c,v $
 * Revision 1.5  2008/11/09 14:37:04  wes
 * Update dates to 2008
 *
 * Revision 1.4  2007/07/22 17:37:47  wes
 * Added usrData and usrCallback parameters to all rmv routines
 *
 * Revision 1.3  2006/10/15 15:50:07  wes
 * Win32 code
 *
 * Revision 1.2  2006/08/08 03:49:21  wes
 * Updates to reflect ergonomic changes to rmaux
 *
 * Revision 1.1  2006/08/07 12:51:53  wes
 * Initial entry
 *
 */

#include <stdio.h>
#ifndef RM_WIN
#include <unistd.h>
#endif
#include <rm/rm.h>   
#include <rm/rmaux.h>
#include "procmode.h"

static RMpipe *myPipe=NULL;

#define DEFAULT_IMAGE_WIDTH 640
#define DEFAULT_IMAGE_HEIGHT 525

void
myBuildScene(const RMpipe *p, 
	     RMnode *lRoot)
{
    RMnode *sphereNode, *boxNode;
    RMprimitive *sPrim, *bPrim;
    RMvertex3D bVerts[2]={{-1., -1., -1.},{1.,1.,1.}};
    RMvertex3D sVerts[1] = {0.,0.,0,};
    float sRadius = 1.0F;
    RMcolor4D sColor={1.0, 0.0, 0.0, 1.0};
    RMcolor4D bColor={0.5, 0.5, 0.5, 0.5};

    /* 
     * make two primitives:
     * 1. a sphere centered at 0,0,0 with r=1
     * 2. an outline box with corners at -1,-1,-1 and 1,1,1
     */

    sphereNode = rmNodeNew("sphereNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
    sPrim = rmPrimitiveNew(RM_SPHERES);
    rmPrimitiveSetVertex3D(sPrim, 1, sVerts, RM_COPY_DATA, NULL);
    rmPrimitiveSetRadii(sPrim, 1, &sRadius, RM_COPY_DATA, NULL);
    rmPrimitiveSetModelFlag(sPrim, RM_SPHERES_512);
    rmNodeAddPrimitive(sphereNode, sPrim);
    rmNodeSetUnlitColor(sphereNode, &sColor);
    rmNodeSetPolygonDrawMode(sphereNode, RM_FRONT_AND_BACK, RM_POINT);
    rmNodeSetPointSize(sphereNode, 5.0F);
    rmNodeComputeBoundingBox(sphereNode);

    boxNode = rmNodeNew("boxNode", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
    bPrim = rmPrimitiveNew(RM_BOX3D_WIRE);
    rmPrimitiveSetVertex3D(bPrim, 2, bVerts, RM_COPY_DATA, NULL);
    rmNodeAddPrimitive(boxNode, bPrim);
/*    rmNodeSetPolygonDrawMode(boxNode, RM_FRONT_AND_BACK, RM_LINE); */
    rmNodeSetLineWidth(boxNode, RM_LINEWIDTH_5);
    rmNodeSetUnlitColor(boxNode, &bColor);
    rmNodeComputeBoundingBox(boxNode);

    rmNodeAddChild(lRoot, boxNode);
    rmNodeAddChild(lRoot, sphereNode);
    rmNodeUnionAllBoxes(lRoot);

    {
	/* make a text string */
	RMnode *tn = rmNodeNew("text", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
	RMprimitive *tp = rmPrimitiveNew(RM_TEXT);
	char *s= strdup("Text String");
	RMvertex3D v={0.5, 0.0, -1.20};
	rmPrimitiveSetVertex3D(tp, 1, &v, RM_COPY_DATA, NULL);
	rmPrimitiveSetText(tp, 1, &s);
	rmNodeAddPrimitive(tn, tp);
	rmNodeComputeBoundingBox(tn);
	rmNodeAddChild(lRoot, tn); /* don't want bbox in lRoot */
    }

    /* 
     * now, make the camera for the scene
     */
    {
	int w, h;
	RMcamera3D *c = rmCamera3DNew();
	rmDefaultCamera3D(c);

	rmPipeGetWindowSize(p, &w, &h);
	rmCamera3DComputeViewFromGeometry(c, lRoot, w, h);
	rmNodeSetSceneCamera3D(lRoot, c);

	rmCamera3DDelete(c);
    }

    /* 
     * add some lighting to the scene
     */
    rmDefaultLighting(lRoot);
}

int
myKeyFunc (RMpipe *p,
	   RMnode *n,
	   void *usrData,
	   char    key, 
	   KeySym  code)
{
    /* convert to lower case */
    if ((key >= 'A') && (key <= 'Z'))
	key = key - ('A' - 'a');

    /* take appropriate menu action */
    switch (key)
    {
	case 'q': /* quitting time */
	    return RM_WHACKED;
	    break;

	case 'p': 		/* make a PS file */
	{
	    RMpsSpec *ps = rmPSSpecNew();
	    char *fn = (char *)usrData;

	    printf(" writing a PS file to %s...", fn);
/*	    rmPSSetSortMethod(ps, RM_PS_SORT_FULL); */
	    rmPSSetSortMethod(ps, RM_PS_SORT_HYBRID_SCREEN_BSP);
	    rmPSSetOutputFilename(ps, fn);
	    rmFramePS(p, n, ps);
	    rmPSSpecDelete(ps);
	    printf("done. \n");
	    return RM_CHILL;
	    break;
	}

       default: /* do nothing */
	   break;
    }

    /* foil compiler warning */
    p = NULL;
    code = (KeySym)0;
    
    return (RM_CHILL);
}
void
myInitFunc(RMpipe *p,
	   RMnode *n,
	   void *usrData)
{
    /* insert code to build the initial scene graph here. be sure to
     add your scene graph to the node "n" */
    myBuildScene(p, n);

    /* set transform modes. Due to how this scene is organized,*/
    rmauxSetGeomTransform(n,p);
    rmauxSetCamera3DTransform(n, p);

    /* set up a background clear color on the pipe */
    {
	RMcolor4D c = {0.0, 0.0, 0.0, 1.0};
	rmPipeSetSceneBackgroundColor(p, &c);
    }

    /* if using a multithreaded rendering mode, then we need to
     render two frames before the first one appears */
    if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
	rmFrame(p, n);

    /* render the scene */
    rmFrame(p, n);
}

void
usage(char *s)
{
    printf("usage: %s [-w imgWidthPixels ] [-h imgHeightPixels] \n",s);
}

void
parseArgs(int ac,
	  char *av[],
	  int *imgWidth,
	  int *imgHeight)
{
    int i;

    i = 1;
    while (i < ac)
    {
	if (strcmp(av[i],"-w") == 0)
	{
	    i++;
	    sscanf(av[i],"%d", imgWidth);
	}
	else if (strcmp(av[i],"-h") == 0)
	{
	    i++;
	    sscanf(av[i],"%d", imgHeight);
	}
	else
	{
	    usage(av[0]);
	    exit(-1);
	}
	i++;
    }
}

#ifdef RM_WIN
int WINAPI WinMain (HINSTANCE hInstance,
		    HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
    MSG      msg; 
    HWND     hWnd; 
    void *fptr;
    
    int imgWidth, imgHeight;
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_WGL;

    char outputFilename[] = {"psTest1.ps"};
    
    imgWidth = DEFAULT_IMAGE_WIDTH;
    imgHeight = DEFAULT_IMAGE_HEIGHT;
    
    parseArgs(__argc, __argv, &imgWidth, &imgHeight);
    
#else  /* assume RM_X */
int
main(int argc,
     char *argv[])
{
    void *msg;			/* needed for rmauxEventLoop
				 win32/unix API consistency */
    int imgWidth, imgHeight;
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_GLX;

    char outputFilename[] = {"psTest1.ps"};

    imgWidth = DEFAULT_IMAGE_WIDTH;
    imgHeight = DEFAULT_IMAGE_HEIGHT;
    
    parseArgs(argc, argv, &imgWidth, &imgHeight);
#endif

    /* 
     * first stage of RM initialization.
     */
    rmInit();

    /* 
     * create the rendering pipe. this step is required in both
     * Win32 and X.
     */
    myPipe = rmPipeNew(targetPlatform);
    
    processingMode = RM_PIPE_MULTISTAGE;
    rmPipeSetProcessingMode(myPipe, processingMode);

#ifdef RM_WIN
    {
        /*
	 * Win32: when a window is created, we have to tell windows the
	 * name of the "WndProc," the procedure that gets called by
	 * windows with events (the event loop) (contrast to the X model 
	 * where the name of the event loop is not part of the window). 
	 * Since we're using RMaux, we know about the event handling 
	 * procedure named "rmauxWndProc" and we provide that here. 
	 */

        fptr = (void *)(rmauxWndProc);
	hWnd = rmauxCreateW32Window(myPipe,
			       NULL, /* no parent window */
			       20,20,imgWidth,imgHeight,"RM for Windows",
			       hInstance,fptr);
	if (hWnd == 0)
	  exit(-1);

	/* 
	 * assign the new window handle to the rendering pipe.
	 */
	rmPipeSetWindow(myPipe,hWnd, imgWidth, imgHeight);
    }
#endif
#ifdef RM_X
    {
	Window w;

	w = rmauxCreateXWindow(myPipe,
			       (Window)NULL, /* parent window */
			       0,0,imgWidth,imgHeight,
			       "RM for X-Windows","RM for X-Windows",RM_TRUE);

	/* 
	 * assign the window to the rendering pipe.
	 */
	rmPipeSetWindow(myPipe,w,imgWidth,imgHeight);
    }
#endif

    /* 
     * X-ism: once the window is created and assigned to the 
     * rendering pipe, rmPipeMakeCurrent makes the OpenGL rendering context
     * current for the pipe+window combination. 
     *
     * this step is required for X. in these demo programs, it is not 
     * strictly required by Win32, as the newly created context is made
     * current as part of the OpenGL initialization sequence.
     */
    rmPipeMakeCurrent(myPipe);	

    rmauxSetInitCallbackFunc(myInitFunc, NULL);
    
    /*
     * set key handler function so this prog will exit on "q" key.
     */
    /* test stuffing the output filename through the usrData associated
     with the keypress callback */
    rmauxSetKeypressCallbackFunc(myKeyFunc, outputFilename); 
    
    rmauxEventLoop(myPipe,rmRootNode(), &msg);

    rmPipeDelete(myPipe);
    rmFinish();

    return(1);
}
   
/* EOF */ 
