/*
 * Copyright (C) 2001-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: textureTest.c,v 1.11 2008/11/09 14:37:04 wes Exp $
 * $Revision: 1.11 $
 * $Name: v180-alpha-01 $
 * $Log: textureTest.c,v $
 * Revision 1.11  2008/11/09 14:37:04  wes
 * Update dates to 2008
 *
 * Revision 1.10  2006/08/08 03:49:21  wes
 * Updates to reflect ergonomic changes to rmaux
 *
 * Revision 1.9  2005/06/08 16:15:54  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.8  2003/11/22 00:54:35  wes
 * New command line argument and support code to permit testing of
 * texture instancing.
 *
 * 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/09/22 17:32:00  wes
 * Defaults for number of textures is now [32, 32].
 *
 * Revision 1.3  2002/09/17 14:27:16  wes
 * Added CLI that sets the number of textures to generate/draw.
 *
 * Revision 1.2  2002/09/05 15:22:35  wes
 * Increased number of quads/textures to exceed the default PAGE_SIZE.
 *
 * Revision 1.1  2002/09/05 15:09:01  wes
 * Initial entry. The spriteTest.c program will exercise the RMimage
 * realloc code, and the textureTest exercises the texture realloc code.
 *
 * Revision 1.3  2002/06/17 00:42:23  wes
 * Updated copyright line.
 *
 * Revision 1.2  2001/10/15 00:34:43  wes
 * Added rmPipeDelete() at the end of the program.
 *
 * Revision 1.1  2001/07/15 22:37:51  wes
 * Initial entry.
 *
 */

/*
 * usage: textureTest [-w imgWidthPixels] [-h imgHeightPixels]
 *                    [-n nTexturesPerSide] [-solo]
 *
 * This code is a skeleton application template. Use it as the basis
 * for your own OpenRM applications. 
 */

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

static RMpipe *myPipe=NULL;

#define DEFAULT_IMAGE_WIDTH 1024
#define DEFAULT_IMAGE_HEIGHT 960

#define DEFAULT_NTEXTURES 32
static int numTiles[2]={DEFAULT_NTEXTURES, DEFAULT_NTEXTURES};
static int numUnique = -1;

static RMtexture **theTextures=NULL;


RMtexture *
myMakeTexture(float a)
{
    int textureDims[2]={8, 4};
    int i, j;
    RMcolor4D sEven, sOdd, *d;

    RMtexture *t = rmTextureNew(2);
    RMimage *img = rmImageNew(2, textureDims[0], textureDims[1], 1,
			      RM_IMAGE_RGBA,RM_FLOAT,RM_COPY_DATA);

    d = (RMcolor4D *)rmImageGetPixelData(img);
    
    sEven.r = 0.0F;
    sEven.g = a;
    sEven.b = 1.0-a;
    sEven.a = 1.0;

    sOdd.r = 1.0-a;
    sOdd.g = 0.0;
    sOdd.b = a;
    sOdd.a = 1.0;

    for (j=0;j<textureDims[1];j++)
    {
	for (i=0;i<textureDims[0];i++)
	{
	    if (j & 1)
	    {
		if (i & 1)
		    *d = sOdd;
		else
		    *d = sEven;
	    }
	    else
	    {
		if (i & 1)
		    *d = sEven;
		else
		    *d = sOdd;
	    }
	    d++;
	}
    }

    rmTextureSetImages(t, &img, 1, 0);
    rmTextureSetEnv(t, GL_DECAL, NULL);
    return t;
}

void
setMyTexture(RMnode *n,
	     float a)
{
    int textureDims[2]={8, 4};
    int i, j;
    RMcolor4D sEven, sOdd, *d;

    RMtexture *t = rmTextureNew(2);
    RMimage *img = rmImageNew(2, textureDims[0], textureDims[1], 1,
			      RM_IMAGE_RGBA,RM_FLOAT,RM_COPY_DATA);

    d = (RMcolor4D *)rmImageGetPixelData(img);
    
    sEven.r = 0.0F;
    sEven.g = a;
    sEven.b = 1.0-a;
    sEven.a = 1.0;

    sOdd.r = 1.0-a;
    sOdd.g = 0.0;
    sOdd.b = a;
    sOdd.a = 1.0;

    for (j=0;j<textureDims[1];j++)
    {
	for (i=0;i<textureDims[0];i++)
	{
	    if (j & 1)
	    {
		if (i & 1)
		    *d = sOdd;
		else
		    *d = sEven;
	    }
	    else
	    {
		if (i & 1)
		    *d = sEven;
		else
		    *d = sOdd;
	    }
	    d++;
	}
    }

    rmTextureSetImages(t, &img, 1, 0);
    rmTextureSetEnv(t, GL_DECAL, NULL);
    rmNodeSetSceneTexture(n, t);

    /* delete the RMimage and the RMtexture because RM has a copy now */
    rmImageDelete(img);
    rmTextureDelete(t, RM_TRUE);
}

void
buildTiles(RMnode *myRoot,
	   int numWidth,
	   int numHeight,
	   int numUnique)
{
    /*
     * assumptions: visible extents range from (-1,-1) .. (1,1)
     * leave 0.05 in margin all the way around the viewport.
     */
    float x,dx, xwidth;
    float y,dy, yheight;
    RMvertex2D v[4];
    RMvertex2D tc[4];
    RMcolor4D unlitColor={0.0F, 1.0F, 0.0F, 1.0F};
    int i, j;
    RMnode *blockRoot = rmNodeNew("blocks", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL);
    float a, da;
    int tcount=0;

    rmNodeAddChild(myRoot, blockRoot);

    x = -1.0 + 0.05;
    dx = 1.9/(numWidth); 	/* increment to next block */
    xwidth = dx - (dx * 0.2);

    y = -1.0 + 0.05;
    dy = 1.9/(numHeight);
    yheight = dy - (dy * 0.2);

    a = 1.0;
    if (numUnique == 1)
	da = 0.0;
    else
	da = -1.0/(float)(numUnique - 1);

    /* build the source textures */
    theTextures = (RMtexture **)malloc(sizeof(RMtexture *)*numUnique);
    for (j=0;j<numUnique;j++, a+=da)
	theTextures[j] = myMakeTexture(a);

    rmNodeSetUnlitColor(blockRoot, &unlitColor);
    rmNodeSetShader(blockRoot, RM_SHADER_NOLIGHT);

    tc[0].x = tc[0].y = 0.0;
    tc[1].x = 1.0;
    tc[1].y = 0.0;
    tc[2].x = tc[2].y = 1.0;
    tc[3].x = 0.0;
    tc[3].y = 1.0;

    for (j=0;j<numHeight;j++)
    {
	for (i=0;i<numWidth;i++,a+=da) 
	{
	    char buf[32];
	    RMnode *n;
	    RMprimitive *p;

	    sprintf(buf,"node-%d-%d",j,i);
	    
	    n = rmNodeNew(buf,RM_RENDERPASS_ALL, RM_RENDERPASS_ALL);
	    p = rmPrimitiveNew(RM_QUADS);

	    v[0].x = x + i * dx;
	    v[0].y = y + j * dy;
	    v[1].x = v[0].x + xwidth;
	    v[1].y = v[0].y;
	    
	    v[2].x = v[0].x + xwidth;
	    v[2].y = v[0].y + yheight;

	    v[3].x = v[0].x;
	    v[3].y = v[2].y;

	    rmPrimitiveSetVertex2D(p, 4, v, RM_COPY_DATA, NULL);
	    rmPrimitiveSetTexcoord2D(p, 4, tc, RM_COPY_DATA, NULL);
	    
	    rmNodeAddPrimitive(n, p);
	    rmNodeAddChild(blockRoot, n);

	    rmNodeSetSceneTexture(n, theTextures[tcount % numUnique]); 
/*	    new_rmNodeSetSceneTexture(n, theTextures[tcount % numUnique], RM_TRUE);  */
/*	    new_rmNodeSetSceneTexture(n, theTextures[tcount % numUnique], RM_FALSE);  */
	    tcount++;
/*	    setMyTexture(n, a);  */
	}
    }
}

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" */

    RMcamera2D *c = rmCamera2DNew();
    RMnode *myRoot = rmNodeNew("myRoot", RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    RMcolor4D bgColor={0.3, 0.3, 0.3, 1.0};
	
    rmNodeAddChild(n, myRoot);

    rmDefaultCamera2D(c); 	/* extents set to [-1,-1]..[1,1] */
    rmNodeSetSceneCamera2D(myRoot, c);
    rmNodeSetSceneBackgroundColor(myRoot, &bgColor);

    rmCamera2DDelete(c);

    buildTiles(myRoot, numTiles[0], numTiles[1], numUnique); 

    rmFrame(p, n);
}

void
usage(char *s)
{
    printf("usage: %s [-w imgWidthPixels ] [-h imgHeightPixels] [-n NN (number of textures displayed 'per side' for a total of NN**2 textures, default is %d)] [-nt NN (defines the number of unique RMtexture objects, the default is %d*%d, or %d unique RMtexture objects) \n",s, DEFAULT_NTEXTURES, DEFAULT_NTEXTURES, DEFAULT_NTEXTURES, DEFAULT_NTEXTURES*DEFAULT_NTEXTURES);
}

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

    i = 1;
    while (ac > 1)
    {
	if (strcmp(av[i],"-w") == 0)
	{
	    i++;
	    ac--;
	    sscanf(av[i],"%d", imgWidth);
	}
	else if (strcmp(av[i],"-h") == 0)
	{
	    i++;
	    ac--;
	    sscanf(av[i],"%d", imgHeight);
	}
	else if (strcmp(av[i],"-n") == 0)
	{
	    i++;
	    ac--;
	    sscanf(av[i],"%d", numTiles);
	    numTiles[1] = numTiles[0];
	    fprintf(stderr," setting number of tiles to be %d by %d \n", numTiles[0], numTiles[1]);
	}
	else if (strcmp(av[i],"-nt") == 0)
	{
	    i++;
	    ac--;
	    sscanf(av[i],"%d", &numUnique);
	    fprintf(stderr," setting the number of unique textures to be %d \n", numUnique);
	}
	else
	{
	    usage(av[0]);
	    exit(-1);
	}
	i++;
	ac--;
    }
}

#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;

    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;

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

    if (numUnique == -1) /* user didn't specify -nt on command line, use a
			    default value that corresponds to the number
			    of tiles */
	numUnique = numTiles[0]*numTiles[1];

    /* 
     * 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.
     */
    rmauxSetKeypressCallbackFunc(rmauxDefaultKeyFunc, NULL);
    
    rmauxEventLoop(myPipe,rmRootNode(), &msg);

    rmPipeDelete(myPipe);
    rmFinish();

    return(1);
}
/* EOF */
