/*
 * Copyright (C) 2005-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: multiTexture2D.c,v 1.4 2008/11/09 14:37:04 wes Exp $
 * $Revision: 1.4 $
 * $Name: v180-alpha-01 $
 * $Log: multiTexture2D.c,v $
 * Revision 1.4  2008/11/09 14:37:04  wes
 * Update dates to 2008
 *
 * Revision 1.3  2006/08/08 03:49:21  wes
 * Updates to reflect ergonomic changes to rmaux
 *
 * Revision 1.2  2005/06/08 17:19:06  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.1  2005/05/16 02:06:45  wes
 * Initial entry
 *
 */

/*
 * usage: multiTexture2D [-w imgWidthPixels] [-h imgHeightPixels] [-p qmesh | quad | tstrip | tris | itris (select which RMprimitive to use - default is a single quad)] [-3d (select creation of 3d geometry. Default is 2d geometry) ]
 *
 */

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

static char baseImageFilename[] = {"data/seaSurfaceTemperature.jpg"};
static char topImageFilename[] = {"data/weather.jpg"};

static RMpipe *myPipe=NULL;
static RMenum primType = RM_QUADS;
static int geomDims = 2;

#define DEFAULT_IMAGE_WIDTH 640
#define DEFAULT_IMAGE_HEIGHT 525

void
computeSceneCamera(RMnode *n,
		   int w,
		   int h,
		   int geomDims)
{
    if (geomDims == 2)
    {
	RMcamera2D *c = rmCamera2DNew();
	rmCamera2DComputeViewFromGeometry(c, n);
	rmCamera2DSetAspectRatio(c, (float)w/(float)h);
	rmNodeSetSceneCamera2D(n, c);
	rmCamera2DDelete(c);
    }
    else 			/* assume geomDims == 3 */
    {
	RMcamera3D *c = rmCamera3DNew();
	rmDefaultCamera3D(c);
	rmCamera3DComputeViewFromGeometry(c, n, w, h);
	rmNodeSetSceneCamera3D(n, c);
	rmCamera3DDelete(c);

	rmDefaultLighting(n);
    }
}

RMprimitive *
buildQuadPrimitive(void)
{
    RMprimitive *prim = rmPrimitiveNew(RM_QUADS);
    RMvertex2D v[4] = {{-0.5, -0.5}, {0.5, -0.5}, {0.5, 0.5}, {-0.5, 0.5}};
    RMvertex2D tc0[4] = {{0.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, 0.0}};
    RMvertex2D tc1[4] = {{-1.0, 1.0}, {1.0, 1.0}, {1.0, -1.0}, {-1.0, -1.0}};
    
    /* populate the RMprimitive  */
    rmPrimitiveSetVertex2D(prim, 4, v, RM_COPY_DATA, NULL);
    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc0, RM_COPY_DATA, NULL, 0);
    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc1, RM_COPY_DATA, NULL, 1);

#if 0
    {
	/* color not needed. it was used to test per vertex/face colors. */
	/* to have the color actually appear, uncomment this line, and
	   comment out the lines where the RMtexture is set at textureNode.
	   With texturing active, the concept of "current color" becomes
	   slippery. */
	RMcolor4D c = {0.7, 0.5, 0.5, 0.5};
	rmPrimitiveSetColor4D(prim, 1, &c, RM_COPY_DATA, NULL);
    }
#endif
    
    return prim;
}

RMprimitive *
buildQuadmeshPrimitive(void)
{
    RMprimitive *prim = rmPrimitiveNew(RM_QUADMESH);
    RMvertex2D v[4] = {{-0.5, -0.5}, {0.5, -0.5}, {-0.5, 0.5},  {0.5, 0.5} };

    RMvertex2D tc0[4] = {{0.0, 1.0}, {1.0, 1.0}, {0.0, 0.0}, {1.0, 0.0} };
    RMvertex2D tc1[4] = {{-1.0, 1.0}, {1.0, 1.0},  {-1.0, -1.0} , {1.0, -1.0}};
    
    /* populate the RMprimitive  */
    rmPrimitiveSetVertex2D(prim, 4, v, RM_COPY_DATA, NULL);
    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc0, RM_COPY_DATA, NULL, 0);
    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc1, RM_COPY_DATA, NULL, 1);
    rmPrimitiveSetQmeshDims(prim, 2, 2);

#if 0
    {
	/* color not needed. it was used to test per vertex/face colors. */
	/* to have the color actually appear, uncomment this line, and
	   comment out the lines where the RMtexture is set at textureNode.
	   With texturing active, the concept of "current color"
	   becomes slippery. */
	RMcolor4D c[4] = { {0.7, 0.5, 0.5, 0.5},{0.7, 0.5, 0.5, 0.5},{0.7, 0.5, 0.5, 0.5},{0.7, 0.5, 0.5, 0.5}} ;
	rmPrimitiveSetColor4D(prim, 1, &c, RM_COPY_DATA, NULL);
    }
#endif
    
    return prim;
}

RMprimitive *
buildTStripPrimitive(void)
{
    RMprimitive *prim = rmPrimitiveNew(RM_TRIANGLE_STRIP);
    RMvertex2D v[4] = {{-0.5, -0.5}, {0.5, -0.5}, {-0.5, 0.5}, {0.5, 0.5}};

    RMvertex2D tc0[6] = {{0.0, 1.0}, {1.0, 1.0}, {0.0, 0.0}, {1.0, 0.0}};
    RMvertex2D tc1[6] = {{-1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}};

    /* populate the RMprimitive  */
    rmPrimitiveSetVertex2D(prim, 4, v, RM_COPY_DATA, NULL);

    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc0, RM_COPY_DATA, NULL, 0);
    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc1, RM_COPY_DATA, NULL, 1); 

#if 0
    {
	/* color not needed. it was used to test per vertex/face colors. */
	/* to have the color actually appear, uncomment this line, and
	   comment out the lines where the RMtexture is set at textureNode.
	   With texturing active, the concept of "current color"
	   becomes slippery. */
	RMcolor4D c[2] = {{0.7, 0.5, 0.0, 0.5}, {0.0, 0.5, 0.7, 0.5}};
	rmPrimitiveSetColor4D(prim, 2, c, RM_COPY_DATA, NULL);
    }
#endif

    return prim;
}

RMprimitive *
buildIndexedTrianglesPrimitive(void)
{
    RMprimitive *prim = rmPrimitiveNew(RM_INDEXED_TRIANGLES);
    RMvertex2D v[4] = {{-0.5, -0.5}, {0.5, -0.5}, {0.5, 0.5}, {-0.5, 0.5}};
    int indices[6] = {0, 1, 2, 0, 2, 3};
    RMvertex2D tc0[4] = {{0.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, 0.0}};
    RMvertex2D tc1[4] = {{-1.0, 1.0}, {1.0, 1.0}, {1.0, -1.0}, {-1.0, -1.0}};
    
    /* populate the RMprimitive  */
    rmPrimitiveSetVertex2D(prim, 4, v, RM_COPY_DATA, NULL);
    rmPrimitiveSetIndices(prim, 6, indices, RM_COPY_DATA, NULL);

    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc0, RM_COPY_DATA, NULL, 0);
    rmPrimitiveSetMultiTexcoord2D(prim, 4, tc1, RM_COPY_DATA, NULL, 1);


#if 0
    {
	/* color not needed. it was used to test per vertex/face colors. */
	/* to have the color actually appear, uncomment this line, and
	   comment out the lines where the RMtexture is set at textureNode.
	   With texturing active, the concept of "current color"
	   becomes slippery. */
	RMcolor4D c = {0.7, 0.5, 0.5, 0.5};
	rmPrimitiveSetColor4D(prim, 1, &c, RM_COPY_DATA, NULL);
    }
#endif
    
    return prim;
}

RMprimitive *
buildTrianglesPrimitive(void)
{
    RMprimitive *prim = rmPrimitiveNew(RM_TRIANGLES);
    RMvertex2D v[6] = {{-0.5, -0.5}, {0.5, -0.5}, {0.5, 0.5}, {-0.5, -0.5}, {0.5, 0.5}, {-0.5, 0.5}};
    RMvertex2D tc0[6] = {{0.0, 1.0}, {1.0, 1.0}, {1.0, 0.0}, {0.0, 1.0},{1.0, 0.0}, {0.0, 0.0}};
    
    RMvertex2D tc1[6] = {{-1.0, 1.0}, {1.0, 1.0}, {1.0, -1.0}, {-1.0, 1.0},{1.0, -1.0}, {-1.0, -1.0}};

    /* populate the RMprimitive  */
    rmPrimitiveSetVertex2D(prim, 6, v, RM_COPY_DATA, NULL);
    rmPrimitiveSetMultiTexcoord2D(prim, 6, tc0, RM_COPY_DATA, NULL, 0);
    rmPrimitiveSetMultiTexcoord2D(prim, 6, tc1, RM_COPY_DATA, NULL, 1);

#if 0
    {
	/* color not needed. it was used to test per vertex/face colors. */
	/* to have the color actually appear, uncomment this line, and
	   comment out the lines where the RMtexture is set at textureNode.
	   With texturing active, the concept of "current color" becomes
	   slippery. */
	RMcolor4D c[2] = {{0.7, 0.5, 0.0, 0.5}, {0.0, 0.5, 0.7, 0.5}};
	rmPrimitiveSetColor4D(prim, 2, c, RM_COPY_DATA, NULL);
    }
#endif

    return prim;
}

void
myInitFunc(RMpipe *p,
	   RMnode *n,
	   void *usrData)
{
    RMtexture *baseTexture, *topTexture;
    RMnode *textureNode;
    RMnode *myRoot;
    RMcolor4D bgColor={0.1F, 0.1F, 0.2F, 1.0F};
    RMprimitive *prim;
    int imgWidth, imgHeight;

    /*
     * SG organization:
     * rmRootNode()
     *    myRoot() - either 2D or 3D depending upon geomDims, has camera.
     *       textureNode - contains textures, prim
     */

    /* read in texture images */
    RMimage *baseImage = rmiReadJPEG(baseImageFilename);
    RMimage *topImage = rmiReadJPEG(topImageFilename);

    /* check for errors reading in texture images */
    if ((baseImage == NULL) || (topImage == NULL))
    {
	char msg[256];
	if (baseImage == NULL)
	{
	    sprintf(msg, "unable to open base image file named %s", baseImageFilename);
	    rmError(msg);
	}
	if (topImage == NULL)
	{
	    sprintf(msg, "unable to open top image file named %s", topImageFilename);
	    rmError(msg);
	}
    }

    /* make the two textures */
    baseTexture = rmTextureNew(2);
    topTexture = rmTextureNew(2);

    rmTextureSetEnv(baseTexture, GL_DECAL, NULL);
    rmTextureSetFilterMode(baseTexture, GL_NEAREST, GL_LINEAR);
    rmTextureSetImages(baseTexture, &baseImage, 1, 0);

    rmTextureSetEnv(topTexture, GL_MODULATE, NULL);
    rmTextureSetWrapMode(topTexture, GL_REPEAT);
    rmTextureSetFilterMode(topTexture, GL_NEAREST, GL_LINEAR);
    rmTextureSetImages(topTexture, &topImage, 1, 0);

    textureNode = rmNodeNew("textureNode", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL);
    /* add the two RMtexture objests as texture 0 and 1 to texturenode */
    rmNodeSetSceneMultiTexture(textureNode, baseTexture, 0);
    rmNodeSetSceneMultiTexture(textureNode, topTexture, 1);

    /* build myRoot, add textureNode to it. */
    if (geomDims == 2)
	myRoot = rmNodeNew("myRoot-2D", RM_RENDERPASS_2D, RM_RENDERPASS_ALL);
    else
	myRoot = rmNodeNew("myRoot-3D", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE);
    rmNodeAddChild(myRoot, textureNode);
    rmNodeAddChild(n, myRoot);

    /* invoke the appropriate build-prim routine */
    switch (primType)
    {
    case RM_QUADS:
	prim = buildQuadPrimitive();
	break;
    case RM_QUADMESH:
	prim = buildQuadmeshPrimitive();
	break;
    case RM_TRIANGLE_STRIP:
	prim = buildTStripPrimitive();
	break;
    case RM_TRIANGLES:
	prim = buildTrianglesPrimitive();
	break;
    case RM_INDEXED_TRIANGLES:
	prim = buildIndexedTrianglesPrimitive();
	break;
    default:
	rmError(" unrecognized primitive type. ");
	exit(-1);
	break;
    }

    rmNodeAddPrimitive(textureNode, prim);
    rmNodeComputeBoundingBox(textureNode);

    rmNodeUnionAllBoxes(myRoot);
    rmPipeGetWindowSize(p, &imgWidth, &imgHeight);
    computeSceneCamera(myRoot, imgWidth, imgHeight, geomDims);


    rmNodeSetSceneBackgroundColor(myRoot, &bgColor);
    rmauxSetGeomTransform(myRoot, p);

    /*    rmauxSetCamera3DTransform(rmRootNode(), p); */

    rmFrame(myPipe, rmRootNode());
}


#if 0
/* when OpenRM supports multitexture transformations, update this
routine to make changes there rather than directly accessing the stack. */
int
myIdleFunc(RMpipe *p,
	   RMnode *n,
	   void *usrData,
	   int ix,
	   int iy)
{
    float tx, ty;
    RMmatrix t;

    tx = -0.125F/(float)(DEFAULT_IMAGE_WIDTH*3);
    ty = 0.06125F/(float)(DEFAULT_IMAGE_HEIGHT*3); 
    /*    ty = 0.125F/(float)(DEFAULT_IMAGE_HEIGHT*3);  */

    rmMatrixIdentity(&t);

    t.m[3][0] = tx;
    t.m[3][1] = ty;

    glActiveTextureARB(GL_TEXTURE1_ARB);
    glMatrixMode(GL_TEXTURE);
    
    /*    glMultMatrixf((GLfloat *)(t.m)); */
    t.m[3][0] *= rmPipeGetFrameNumber(p);
    t.m[3][1] *= rmPipeGetFrameNumber(p);
    glLoadMatrixf((GLfloat *)(t.m));
    
    glMatrixMode(GL_MODELVIEW);

    rmFrame(p, rmRootNode()); 

    return 1;
}
#endif


void
usage(char *s)
{
    printf("usage: %s [-w imgWidthPixels ] [-h imgHeightPixels] [-p quad | qmesh | tstrip | tris | itris] [-3d] \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 if (strcmp(av[i],"-p") == 0)
	{
	    i++;
	    if (strcmp(av[i],"qmesh") == 0)
		primType = RM_QUADMESH;
	    else if (strcmp(av[i],"quad") == 0)
		primType = RM_QUADS;
	    else if (strcmp(av[i],"tstrip") == 0)
		primType = RM_TRIANGLE_STRIP;
	    else if (strcmp(av[i],"tris") == 0)
		primType = RM_TRIANGLES;
	    else if (strcmp(av[i],"itris") == 0)
		primType = RM_INDEXED_TRIANGLES;
	    else
	    {
		rmError(" unrecognized input primitive type. ");
		exit(-1);
	    }
	}
	else if (strcmp(av[i],"-3d") == 0)
	{
	    geomDims = 3;
	    rmWarning(" warning - 3d geometry stuff not fully implemented yet in this demo program. ");
	}
	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;
    
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_WGL;
    int imgWidth, imgHeight;

    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 */
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_GLX;
    int imgWidth, imgHeight;

    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);
    
    /*    rmauxSetIdleFunc(myPipe,myIdleFunc); */
    
    /*
     * set key handler function so this prog will exit on "q" key.
     */
    rmauxSetKeypressCallbackFunc(rmauxDefaultKeyFunc, NULL);
    
    rmauxEventLoop(myPipe,rmRootNode(), &msg);

    rmPipeDelete(myPipe);
    rmFinish();

    return(1);
}
   
/* EOF */ 
