/*
 * 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: dyntmap.c,v 1.9 2008/11/09 14:37:04 wes Exp $
 * $Revision: 1.9 $
 * $Name: v180-alpha-01 $
 * $Log: dyntmap.c,v $
 * Revision 1.9  2008/11/09 14:37:04  wes
 * Update dates to 2008
 *
 * Revision 1.8  2006/08/08 03:49:21  wes
 * Updates to reflect ergonomic changes to rmaux
 *
 * Revision 1.7  2006/08/07 12:53:26  wes
 * Updates to reflect API changes in RMaux for button/key/idle callbacks.
 *
 * Revision 1.6  2005/06/08 18:18:53  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.5  2003/04/13 18:13:23  wes
 * Updated copyright dates.
 *
 * Revision 1.4  2003/01/27 05:07:07  wes
 * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
 *
 * Revision 1.3  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.2  2002/06/17 00:35:11  wes
 * Final 1.4.2 checkin.
 *
 * Revision 1.1  2002/04/30 19:43:30  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

/*
 * the "raw data file" is just raw byte information. a priori knowledge
 * about the size is required in order to use this program. the size of
 * the volume is compiled into this program.
 */
#define RAW_WIDTH 256
#define RAW_HEIGHT 64
#define RAW_DEPTH 256
char datafilename[256]={"data/volume.dat"};

typedef unsigned char dataptr;
dataptr *rawVolumeData=NULL;
RMvisMap *vmap = NULL;
RMnode *textureNode=NULL;
static int frameCounter = 0;
static int do_color = 0;

void
readData(char *datafilename)
{
    FILE *f;
    int npts=RAW_WIDTH*RAW_DEPTH*RAW_HEIGHT;

    vmap = rmDefaultVismap();	/* creates a hue ramp */
    rmVismapSetTfMin(vmap,0.0F);
    rmVismapSetTfMax(vmap,255.0F);
    
    if (do_color == 0)
    {
	/*
	 * rebuild the colormap/transfer function so that it's a
	 * gray ramp. this looks better than just using GL_ALPHA
	 * as the image data format.
	 *
	 * Ramp from 0..1 (gray) between indices 0..127. Then, ramp back
	 * down from 1 to 0 from 127..255.
	 * 
	 */
	int i;
	RMcolor4D c={0.F,0.F,0.F,0.F};
	double dc;

	dc = 1./127.;
	for (i=0;i<127;i++)
	{
	    rmVismapSetColor4D(vmap,i,&c);
	    c.r += dc;
	    c.g += dc;
	    c.b += dc;
	    c.a += dc;
	}
	for (i=i;i<256;i++)
	{
	    rmVismapSetColor4D(vmap,i,&c);
	    c.r -= dc;
	    c.g -= dc;
	    c.b -= dc;
	    c.a -= dc;
	}
    }

    printf(" loading raw volume data...");
    rawVolumeData = (unsigned char *)malloc(sizeof(unsigned char)*npts);
    f = fopen(datafilename,"r");
    fread((void *)rawVolumeData,sizeof(unsigned char),npts,f);
    fclose(f);
    printf("done \n");
}

void
setTexture(RMnode *n,		/* the RMnode containing an RMtexture */
	   int z,		/* which z-slice of data to load */
	   int frameCounter)	/* which frame */
{
    /*
     * this routine will read in the next slice of volume data, create an
     * RMimage from the data, and update the RMtexture at the RMnode n.
     * The RMtexture is created only on the first execution. Otherwise,
     * the RMtexture is obtained by querying the RMnode, and updating
     * the RMtexture's RMimage texture data.
     */
    dataptr *p;
    RMimage *i = rmImageNew(2, RAW_WIDTH, RAW_HEIGHT, 1, RM_IMAGE_LUMINANCE, RM_UNSIGNED_BYTE, RM_COPY_DATA);
    int initialOffset = RAW_WIDTH * RAW_HEIGHT * z;

    p = rawVolumeData + initialOffset;

    rmImageSetPixelData(i, p, RM_COPY_DATA, NULL);

    rmImageSetVismap(i, vmap);
    
    if (frameCounter == 0)
    {
	RMtexture *t = rmTextureNew(2);
	rmTextureSetFilterMode(t, GL_LINEAR, GL_LINEAR); 
	rmTextureSetImages(t, &i, 1, 0);
	rmNodeSetSceneTexture(n, t);
	rmTextureDelete(t, RM_TRUE);
    }
    else
    {
	RMtexture *t;
	rmNodeGetSceneTexture(n,&t);
	rmTextureSetImages(t, &i, 1, 0);
    }

    rmImageDelete(i);

}

void
myBuildObjs(RMnode *addTo)
{
    RMvertex2D v[4] = {{-1.0,-1.0},{1.0,-1.0},{1.0,1.0},{-1.0,1.0}};
    RMvertex2D tc[4] = {{0.0,0.0},{1.0,0.0},{1.0,1.0},{0.0,1.0}};
    RMcolor4D bgColor={0.2, 0.1, 0.1, 1.0};
    RMnode *myRoot = rmNodeNew("MyRoot", RM_RENDERPASS_2D, RM_RENDERPASS_ALL);
    RMnode *quadNode = rmNodeNew("QuadNode", RM_RENDERPASS_2D, RM_RENDERPASS_ALL);
    RMcamera2D *c = rmCamera2DNew();
    RMprimitive *p = rmPrimitiveNew(RM_QUADS);

    rmPrimitiveSetVertex2D(p, 4, v, RM_COPY_DATA, NULL);
    rmPrimitiveSetTexcoord2D(p, 4, tc, RM_COPY_DATA, NULL);
    rmNodeAddPrimitive(quadNode, p);

    rmNodeAddChild(myRoot, quadNode);
    textureNode = quadNode;

    setTexture(textureNode, 0, frameCounter++);

    rmCamera2DSetExtents(c,-1.1,-1.1,1.1,1.1);
    rmNodeSetSceneCamera2D(myRoot, c);
    rmNodeSetSceneBackgroundColor(myRoot, &bgColor);

    rmCamera2DDelete(c);
    rmNodeAddChild(addTo, myRoot);
}

void
myInitFunc(RMpipe *p,
	   RMnode *n,
	   void *usrData)
{
    readData(datafilename);
    myBuildObjs(n);

    rmFrame(p, n);
}

int
myRenderFunc(RMpipe *myPipe,
	     RMnode *subTree,
	     void *usrData)
{
    rmFrame(myPipe, subTree);
    return 1;
}

void
usage(char *s)
{
    printf("usage: %s [-w imgWidthPixels ] [-h imgHeightPixels] [-c (use a HSV hue ramp in an RMvisMap to colorize the data, default is grayscale.) ]\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++;
	    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],"-c") == 0)
	{
	    do_color = 1;
	}
	else
	{
	    usage(av[0]);
	    exit(-1);
	}
	i++;
    }
}

int
myIdleFunc(RMpipe *p,
	   RMnode *n,
	   void *usrData,
	   int pointerX,
	   int pointerY)
{
    /*
     * rather than display all slices of volume data, we only show
     * a portion of the dataset - between slices 100 and 212. The
     * other slices are all zeros, so the display is not very
     * interesting to look at in those regions.
     */
#define START 100
#define END 212
    static int counter=START;
    int *localFrameCounter = (int *)usrData;
    
    if (counter >= END)
	counter = START;
#if 0
    printf(" frame %d \n", frameCounter);
    setTexture(textureNode, counter++, frameCounter++);
#endif
    printf(" frame %d \n", *localFrameCounter);
    setTexture(textureNode, counter++, *localFrameCounter);
    *localFrameCounter += 1;

    rmFrame(p, rmRootNode());

    /* foil compiler warning */
    pointerX = pointerY = 0;
    
    return 1;
}

#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

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

	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);
    rmauxSetRenderCallbackFunc(myRenderFunc, NULL);
    
    rmauxSetIdleCallbackFunc(myIdleFunc, &frameCounter);
    
    /*
     * 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 */ 
