/*
 * Copyright (C) 2000-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: clrball.c,v 1.10 2008/11/09 14:37:04 wes Exp $
 * $Revision: 1.10 $
 * $Name: v180-alpha-01 $
 * $Log: clrball.c,v $
 * Revision 1.10  2008/11/09 14:37:04  wes
 * Update dates to 2008
 *
 * Revision 1.9  2006/08/08 03:49:21  wes
 * Updates to reflect ergonomic changes to rmaux
 *
 * Revision 1.8  2005/06/08 18:20:16  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.7  2003/04/13 18:13:23  wes
 * Updated copyright dates.
 *
 * Revision 1.6  2003/01/27 05:07:07  wes
 * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
 *
 * Revision 1.5  2003/01/16 22:22:45  wes
 * Updated all source files to reflect new organization of header files: all
 * headers that were formerly located in include/rmaux, include/rmv and
 * include/rmi are now located in include/rm.
 *
 * Revision 1.4  2002/06/17 00:34:55  wes
 * Updated copyright line.
 *
 * Revision 1.3  2001/07/15 22:33:19  wes
 * Added rmPipeDelete to the end of all demo progs. For those that use
 * an initfunc, added a new RMnode * parm (which is unused, except for rm2screen).
 *
 * Revision 1.2  2001/06/03 19:44:05  wes
 * Add calls to new rmaux routines to handle window resize events, and
 * for keyboard event handling.
 *
 * Revision 1.1  2001/03/31 17:25:50  wes
 * Initial entry.
 *
 */

#include <rm/rm.h>   
#include <rm/rmaux.h>
#include "procmode.h"

int imgWidth=400,imgHeight=300;
static RMnode *MyRoot=NULL, *MyOpaqueRoot=NULL, *MyTransparentRoot=NULL;

void
my_set_scene(RMnode *camNode,
	     RMenum stereo_format)
{
    /*
     * here, we compute the camera parameters such that all of
     * the geometry is visible.
     */
      
    RMcamera3D *c=rmCamera3DNew();

    rmDefaultCamera3D(c);	/* assign it some default parameters */
    
    /* adjust the "default" camera based upon MyRoot's geometry */
    rmCamera3DComputeViewFromGeometry(c,rmRootNode(),imgWidth,imgHeight);

    if (stereo_format != RM_MONO_CHANNEL)
    {
        rmCamera3DSetStereo(c,RM_TRUE);
	rmCamera3DSetEyeSeparation(c,2.5F);
	rmCamera3DSetFocalDistance (c,0.707F);
    }

    /* write the camera back into the rmRootNode() */
    rmNodeSetSceneCamera3D(camNode,c);
    rmCamera3DDelete(c);

    /*
     * make the background black for fun
     */
    {
	RMcolor4D c;
	c.r = c.g = c.b = c.a = 0.0;
	/*
	 * assign a background color to take effect at "MyRoot"
	 */
	rmNodeSetSceneBackgroundColor(MyOpaqueRoot,&c);
    }
    
    /* use RM's default lighting model, apply at rmRootNode() */
    rmDefaultLighting(camNode);
}

void
my_sphere(RMnode *addto,
	  float cx,
	  float cy,
	  float cz,
	  float radius,
	  RMcolor4D *color,
	  RMenum modelFlag,
	  RMenum renderPassFlag)
{
    RMnode *sn;
    RMprimitive *sprim;
    RMvertex3D v;

    sn = rmNodeNew("asphere",RM_RENDERPASS_3D, renderPassFlag);
    
    v.x = cx;
    v.y = cy;
    v.z = cz;
    
    sprim = rmPrimitiveNew(RM_SPHERES);
    rmPrimitiveSetVertex3D(sprim,1,&v,RM_COPY_DATA,NULL);

    rmPrimitiveSetRadii(sprim, 1, &radius, RM_COPY_DATA, NULL);

    if (color != NULL)
	rmPrimitiveSetColor4D(sprim, 1, color, RM_COPY_DATA, NULL);

    rmPrimitiveSetModelFlag(sprim,modelFlag);

    rmNodeAddPrimitive(sn, sprim);
    rmNodeComputeBoundingBox(sn);
    rmNodeAddChild(addto, sn);
    rmNodeUnionAllBoxes(addto);
}

void
my_build_objs(void)
{
    /*
     * Scene Graph layout & node scheduling:
     *
     * rmRootNode() - lights, camera
     *   MyRoot - root for geom transforms
     *      MyOpaqueRoot - root for opaque objects
     *      MyTransparentRoot - root for 3D transparent objs
     */
    RMcolor4D color;

    MyRoot = rmNodeNew("MyRoot",RM_RENDERPASS_3D, RM_RENDERPASS_ALL);
    MyOpaqueRoot = rmNodeNew("MyOpaqueRoot", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
    MyTransparentRoot = rmNodeNew("MyTransparentRoot",RM_RENDERPASS_3D, RM_RENDERPASS_TRANSPARENT);

    /* the center sphere should be red. */
    color.r = 1.0;
    color.g = color.b = 0.0;
    color.a = 1.0;
    my_sphere(MyOpaqueRoot,0.0F, 0.0F, 0.0F, 1.0F,&color,RM_SPHERES_32, RM_RENDERPASS_OPAQUE);

    /* transparent, blue sphere at (0,0,0) */
#if 0
    color.r = color.g = 0.0;
    color.b = 1.0;
    color.a = 0.3;
    my_sphere(MyTransparentRoot,0.0F, 0.0F, 0.0F, 4.0F,&color);
#endif
    my_sphere(MyTransparentRoot,0.0F, 0.0F, 0.0F, 3.0F,NULL,RM_SPHERES_512, RM_RENDERPASS_TRANSPARENT);
    {
	RMcolor4D white={1.0F, 1.0F, 1.0F, .5F};
	RMcolor4D blue={0.1F, 0.1F, 1.0F, .75F};
	RMcolor4D blue2={0.1F, 0.1F, 1.0F, 1.0F};
	float exp=20.0;
	rmNodeSetDiffuseColor(MyTransparentRoot, &blue);
	rmNodeSetAmbientColor(MyTransparentRoot, &white);
	rmNodeSetSpecularColor(MyTransparentRoot, &blue2);
	rmNodeSetSpecularExponent(MyTransparentRoot, exp);
    }

    rmNodeAddChild(MyRoot, MyOpaqueRoot);
    rmNodeAddChild(MyRoot, MyTransparentRoot);
    
    rmNodeAddChild(rmRootNode(),MyRoot);

    rmNodeUnionAllBoxes(rmRootNode()); 
    rmNodeComputeCenterFromBoundingBox(rmRootNode());
}

void
my_idle_func(RMpipe *p,
	     int ix,
	     int iy)
{
    RMmatrix m,old;
    double d,c,s;
    rmMatrixIdentity(&m);
    d = RM_DEGREES_TO_RADIANS(1.0);
    c = cos(d);
    s = sin(d);
    m.m[0][0] = m.m[2][2] = c;
    m.m[0][2] = -s;
    m.m[2][0] = s;
    
    if (rmNodeGetRotateMatrix(MyRoot,&old) == RM_WHACKED)
	rmMatrixIdentity(&old);
    rmMatrixMultiply(&old,&m,&old);
    rmNodeSetRotateMatrix(MyRoot,&old);

    rmFrame(p, rmRootNode());

    /* foil compiler warning */
    ix = iy = 0;
}

void
myinitfunc(RMpipe *p,
	   RMnode *n,
	   void *usrData)
{
    my_build_objs();
    my_set_scene(n, rmPipeGetChannelFormat(p));

    /*
     * set up the event handler to apply geometric transformations at
     * MyRoot. note that the lights and cameras are placed at rmRootNode().
     * therefore, the rotations & scaling applied at MyRoot do not affect
     * the cameras & lights since they are at a higher level in the
     * scene graph than MyRoot.
     */
    rmauxSetGeomTransform(MyRoot,p);
    rmauxSetCamera3DTransform(n, p);
    
    /*
     * set handler to reset aspect ratio when the window is resized.
     */
    rmauxSetResizeFunc(p, n, rmauxDefaultResizeFunc);
    
    rmStatsComputeDemography(n);

    if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
	rmFrame(p, n);
    
    rmFrame(p, n);
}

int
myrenderfunc(RMpipe *p, 
	     RMnode *n,
	     void *usrData)
{
    rmStatsStartTime();

    rmFrame(p, n);

    rmStatsEndTime();
    rmStatsPrint();
    return 1;
}

#ifdef RM_WIN
int WINAPI WinMain (HINSTANCE hInstance,
		    HINSTANCE hPrevInstance,
                    LPSTR lpszCmdLine, int nCmdShow)
{
    MSG      msg; 
    HWND     hWnd; 
    RMenum targetPlatform = RM_PIPE_WGL;
    void *fptr;
#else  /* assume RM_X */
int
main()
{
    void *msg;			/* needed for rmauxEventLoop
				 win32/unix API consistency */
    RMenum targetPlatform = RM_PIPE_GLX;
#endif
    RMpipe *myPipe=NULL;
    RMenum stereoFormat;
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */

    /* 
     * pick a stereo format:
     * RM_MONO_CHANNEL - plain old single-view
     * RM_REDBLUE_STEREO_CHANNEL - left channel in red, right channel in cyan
     * RM_BLUERED_STEREO_CHANNEL - left in cyan, right in red
     * RM_MBUF_STEREO_CHANNEL - multibuffered stereo, requires special
     *    hardware. 
     */
    stereoFormat = RM_MONO_CHANNEL;


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

    /* 
     * create the rendering pipe. this step is required in both
     * Win32 and X.
     */

    myPipe = rmPipeNew(targetPlatform);
    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;
	int managed = RM_TRUE;

	w = rmauxCreateXWindow(myPipe,
			       (Window)NULL, /* parent window */
			       0,0,imgWidth,imgHeight,
			       "RM for X-Windows","icon-title",managed);
	/* 
	 * assign the window to the rendering pipe.
	 */

	rmPipeSetWindow(myPipe,w,imgWidth,imgHeight);
    }
#endif

    /* 
     * specify the name of the "init" function. the "init" function is
     * mandatory in the Win32 world, and optional in the X world. 
     *
     * in Win32, we don't want to call RM services until OpenGL is
     * ready. we can be assured of readiness by using an init function
     * with RMaux. 
     *
     * in X, at this point, the window is mapped and OpenGL is ready,
     * and we could call our init function directly.
     */

    rmauxSetInitCallbackFunc(myinitfunc, NULL); 

    /* uncomment this next line if you want the object to rotate
       by itself while the user is idle. */
/*    rmauxSetIdleFunc(myPipe,my_idle_func); */
    
    /* 
     * X-ism: once the window is created and assigned to the 
     * rendering pipe, rmUsePipe 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); 
    rmauxSetRenderCallbackFunc(myrenderfunc, NULL);

    /*
     * set key handler function so this prog will exit on "q" key
     */
    rmauxSetKeypressCallbackFunc(rmauxDefaultKeyFunc, NULL);
    
    rmauxEventLoop(myPipe,rmRootNode(), &msg);

    rmPipeDelete(myPipe);
    rmFinish();

#ifdef RM_WIN
    return( msg.wParam );
#else
    return(1);
#endif

}
/* EOF */ 
