/*
 * Copyright (C) 1997-2005, 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: trans2d.c,v 1.13 2005/06/08 15:58:08 wes Exp $
 * $Revision: 1.13 $
 * $Name: OpenRM-1-6-0-2-c $
 * $Log: trans2d.c,v $
 * Revision 1.13  2005/06/08 15:58:08  wes
 * Code cleanup to eliminate compiler warnings.
 *
 * Revision 1.12  2003/04/13 18:13:23  wes
 * Updated copyright dates.
 *
 * Revision 1.11  2003/01/27 05:07:07  wes
 * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL.
 *
 * Revision 1.10  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.9  2002/06/17 00:45:46  wes
 * Updated copyright line.
 *
 * Revision 1.8  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.7  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.6  2001/03/31 16:55:18  wes
 * Added procmode.h, which defines an RMpipe processing mode used in
 * most demonstration programs. The default processing mode is
 * RM_PIPE_MULTISTAGE_VIEW_PARALLEL.
 *
 * Revision 1.5  2000/12/02 17:24:32  wes
 * Version 1.4.0-alpha-1 checkin. See the RELEASENOTES file for
 * a summary of changes. With this checkin, these demo programs
 * are no longer compatible with versions of the OpenRM API that
 * are pre-1.4.0.
 *
 * Revision 1.4  2000/08/28 01:38:18  wes
 * Updated rmaux* interfaces - rmauxEventLoop now takes two additional
 * parameters (keypress and window resize app callbacks); replaced
 * rmauxUI with rmauxSetGeomTransform and, where appropriate,
 * rmauxSetCamera3DTransform.
 *
 * Revision 1.3  2000/04/27 03:12:05  wes
 * Minor code cleanup.
 *
 * Revision 1.2  2000/04/20 18:04:34  wes
 * Minor tweaks and reorg for OpenRM 1.2.1.
 *
 * Revision 1.1.1.1  2000/02/28 21:55:30  wes
 * OpenRM 1.2 Release
 *
 * Revision 1.11  2000/02/28 17:21:55  wes
 * RM 1.2, pre-OpenRM
 *
 */

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

/*
 * this program generates a bunch of 2D objects, lets you pick one
 * and translate it around in the (x,y) plane.
 *
 * since translation can be applied to RMnodes, and not RMprimitives, each
 * object is put into a different node (VRML style).
 *
 * the only command line args are those that let you set the
 * display image window size: "-w xxx -h yyy"
 */

static RMnode *MyRoot;
int img_width=400,img_height=300;
static char image_filename[128]={"data/doghead.x"}; 

#define DIVISOR 1.F/(float)(RAND_MAX)

#include <time.h>

void
initrand()
{
    srand(time(NULL));
}

float
RandToFloat(float fmin,
	    float fmax)
{
    /*
     * generate a random number, convert it to some floating point
     * range, and return it.
     */
    float mag,t;
    int r1;
    
    r1 = rand();

    /* input rand numbers in range 0..32767 */

    /* convert to range 0..1 */
    t = (float)r1 * DIVISOR;
    
    mag = fmax-fmin;
    t = t*mag + fmin;
    return(t);
}

void
my_set_scene(RMnode *camNode)
{
    RMcamera2D *c = rmCamera2DNew();
    float vp[4];

    /*
     * assume that all objects lie in -1..1 in x&y. all their vertices
     * will lie inside that area, however they may hang off the edges
     * due to radius sizes, etc.
     */

    rmCamera2DSetExtents(c,-1.0F, -1.0F, 1.0F, 1.0F);

    vp[0] = vp[1] = 0.0;
    vp[2] = vp[3] = 1.0;

    rmCamera2DResetAspectRatio(c,vp, img_width,img_height);  

    /* set the camera parm on MyRoot */
    rmNodeSetSceneCamera2D(camNode,c);

    rmCamera2DDelete(c);	/* don't need it any more..a copy is made */

}

void
create_boxes(RMnode *parent,
	     int nboxes)
{
    int i;
    RMprimitive *p;
    RMvertex2D *v;
    RMnode *n;
    RMcolor3D *c;

    v = rmVertex2DNew(nboxes*2);
    c = rmColor3DNew(nboxes);
    
    for (i=0;i<nboxes*2;i++)
    {
	v[i].x = RandToFloat(-1.0F, 1.0F);
	v[i].y = RandToFloat(-1.0F, 1.0F);

    }

    for (i=0;i<nboxes;i++)
    {
	c[i].r = c[i].g = 0.0;
	c[i].b = (float)i/(float)(nboxes-1);
    }

    n = rmNodeNew("boxes",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    
    p = rmPrimitiveNew(RM_BOX2D);

    rmPrimitiveSetVertex2D(p,nboxes*2,v,RM_COPY_DATA,NULL);
    
    rmPrimitiveSetColor3D(p,nboxes,c,
			  RM_COPY_DATA,
			  NULL);
    
    rmNodeAddPrimitive(n,p); 
    rmNodeAddChild(parent,n);
    rmVertex2DDelete(v);
    rmColor3DDelete(c);
}

void
create_circles(RMnode *parent,
	      int ncircles)
{
    int i;
    RMprimitive *p;
    RMvertex2D *v;
    RMnode *n;
    RMcolor3D *c;
    float *r;

    v = rmVertex2DNew(ncircles);
    r = rmFloatNew(ncircles); 
    c = rmColor3DNew(ncircles);
    
    for (i=0;i<ncircles;i++)
    {
	v[i].x = RandToFloat(-1.0F, 1.0F);
	v[i].y = RandToFloat(-1.0F, 1.0F);

	r[i] = RandToFloat(0.1,0.3);

	c[i].r = (float)i/(float)(ncircles-1); 
	c[i].g = 0.0;
	c[i].b = 0.0;
    }

    n = rmNodeNew("circles",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    
    p = rmPrimitiveNew(RM_CIRCLE2D);
    
    rmPrimitiveSetVertex2D(p,ncircles,v,RM_COPY_DATA,NULL);
    
    rmPrimitiveSetColor3D(p,ncircles,c,
			  RM_COPY_DATA,
			  NULL);
    
    rmPrimitiveSetRadii(p,ncircles,r,
			RM_COPY_DATA,
			NULL);

    rmNodeSetPolygonDrawMode(n,RM_FRONT_AND_BACK, RM_LINE);
    rmNodeSetLineStyle(n,RM_LINES_DASHED); 
    rmNodeSetLineWidth(n,RM_LINEWIDTH_HEAVY); 
    rmNodeAddPrimitive(n,p); 
    rmNodeAddChild(parent,n);
    
    rmVertex2DDelete(v);
    rmColor3DDelete(c);
    rmFloatDelete(r);
}

void
create_ellipses(RMnode *parent,
		int n)
{
    int i;
    RMprimitive *p;
    RMvertex2D *v;
    RMnode *node;
    RMcolor3D *c;
    float *r;
    float *rotatevals;

    v = rmVertex2DNew(n);
    r = rmFloatNew(n*2);
    c = rmColor3DNew(n);
    rotatevals = rmFloatNew(n);
    
    for (i=0;i<n;i++)
    {
	v[i].x = RandToFloat(-1.0F, 1.0F);
	v[i].y = RandToFloat(-1.0F, 1.0F);

	c[i].r = 0.0;
	c[i].g = (float)(n-i) /(float)(n);
	c[i].b = 0.0;

    }

    for (i=0;i<n;i++)
    {
	/*
	 * rotation values are specified in 10th's of degrees. therefore,
	 * a value of 3600 specifies 360 degrees of rotation. for this example,
	 * we constain rotation to +/- 180 degrees.
	 */
	rotatevals[i] = RandToFloat(-1800.0F, 1800.0F);
    }

    for (i=0;i<n*2;i++)
    {
	if (i & 1)
	    r[i] = RandToFloat(0.05F, 0.1F);
	else
	    r[i] = RandToFloat(0.2F, 0.3F);
    }

    node = rmNodeNew("ellipses",RM_RENDERPASS_2D,RM_RENDERPASS_OPAQUE);
    
    p = rmPrimitiveNew(RM_ELLIPSE2D);

    rmPrimitiveSetVertex2D(p,n,v,RM_COPY_DATA,NULL);
    
    rmPrimitiveSetColor3D(p,n,c,
			  RM_COPY_DATA,
			  NULL);
    rmPrimitiveSetRadii(p,n*2,r,
			RM_COPY_DATA,
			NULL);

    rmPrimitiveSetEllipse2DRotate(p,n,rotatevals,RM_COPY_DATA,NULL);

    rmNodeSetPolygonDrawMode(node,RM_FRONT_AND_BACK, RM_LINE);
    rmNodeAddPrimitive(node,p); 
    rmNodeAddChild(parent,node);
    
    rmVertex2DDelete(v);
    rmColor3DDelete(c);
    rmFloatDelete(r);
    rmFloatDelete(rotatevals);
}

void
create_sprite(RMnode *parent)
{
    RMimage *orig_img, *sprite_img;
    RMnode *sprite_node;
    RMvertex2D v;
    RMprimitive *sp;

    /*
     * first, read in an image.
     */

    orig_img = dioReadAVSImage(image_filename); 

#if 0
    /*
     * next,build a new image that is pretty small in size, and
     * resize the original image.
     */
    sprite_img = rmImageNew(2,100,100,1,rmImageGetFormat(orig_img),
			    RM_UNSIGNED_BYTE,
			    RM_COPY_DATA);
    rmImageResize(orig_img,sprite_img,RM_HARDWARE);

    /* throw away the first image because we don't need it anymore */
    rmImageDelete(orig_img); 

#endif
    /*
     * alternately, instead of resizing the image we could just set it's
     * pixel zoom parameters. take your pick.  you don't need both, so
     * if you uncomment the following, comment out the part above
     * where we resize the image.
     */

    rmImageSetPixelZoom(orig_img,0.25F, 0.25F);  
    sprite_img = orig_img;


    v.x = RandToFloat(-1.0F,1.0F);
    v.y = RandToFloat(-1.0F,1.0F);
    
    sprite_node = rmNodeNew("sprite",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    sp = rmPrimitiveNew(RM_SPRITE);

    rmPrimitiveSetVertex2D(sp,1,&v,RM_COPY_DATA,NULL);

    rmPrimitiveSetSprites(sp, 1, &sprite_img);

    rmNodeAddPrimitive(sprite_node,sp);

    rmNodeAddChild(parent,sprite_node);
}

#define ONE_OVER_255 0.0039215686

void
create_bitmap(RMnode *parent)
{
    RMimage *orig_img;
    RMbitmap *bmap;
    RMnode *bitmap_node;
    RMvertex2D v;
    RMprimitive *bp;
    int w,h;
    RMcolor3D c = {1.0F, 1.0F, 0.0F};

    /*
     * first, read in an image.
     */

    orig_img = dioReadAVSImage("data/wu.x");

    /*
     * next, create a bitmap, and turn on bits based upon the
     * luminance component within the AVS image. this chunk of
     * code makes use of a priori knowledge about:
     * 1. the format of bytes in an RMimage buffer.
     */

    rmImageGetImageSize(orig_img,NULL,&w,&h,NULL,NULL,NULL);
    bmap = rmBitmapNew(w,h);

    /*
     * the bitmap data starts out as all zeros. now, we'll go through and
     * turn bits on when the computed luminance of the source image
     * exceeds 0.5.
     */

    {
	unsigned char *s;
	int i,j,k;
	int w,h;

	/* 
	 * we're assuming the image is in RGBA format.
	 */

	k = rmImageGetFormat(orig_img);
	s = rmImageGetPixelData(orig_img);

	rmImageGetImageSize(orig_img,NULL,&w,&h,NULL,NULL,NULL);
	
	for (j=0;j<h;j++)
	{
	    for (i=0;i<w;i++)
	    {
		float r,g,b,a;
		/* luminance = 0.3*r,0.6*g,0.1*b, input is unsigned bytes */
		if (k == RM_IMAGE_RGBA)
		{
		    r = (float)(*s++) * ONE_OVER_255;
		    g = (float)(*s++) * ONE_OVER_255;
		    b = (float)(*s++) * ONE_OVER_255;
		    a = (float)(*s++) * ONE_OVER_255;

		    r = r * 0.3 + g*0.6 + b*0.1;
		    if (r > 0.5)
			rmBitmapSetBit(bmap,i,j);
		}
		else			 /* assume k==1 */
		{
		    /*
		     * the AVS images are always crunched to RGB format, so
		     * if we're here, there's a big error of some type.
		     */
		    if (*s++ > 0x80)
			rmBitmapSetBit(bmap,i,j);
		}
	    }
	}
    }
    
    /* throw away the first image because we don't need it anymore */
    rmImageDelete(orig_img);

    v.x = RandToFloat(-1.0F,1.0F);
    v.y = RandToFloat(-1.0F,1.0F);
    
    bitmap_node = rmNodeNew("bitmap",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    bp = rmPrimitiveNew(RM_BITMAP);

    rmPrimitiveSetVertex2D(bp,1,&v,RM_COPY_DATA,NULL);
    rmPrimitiveSetColor3D(bp,1,&c,RM_COPY_DATA,NULL);

    rmPrimitiveSetBitmaps(bp,1,&bmap);

    rmNodeAddPrimitive(bitmap_node,bp);

    rmNodeAddChild(parent,bitmap_node);
}


void
create_text(RMnode *parent)
{
    RMnode *textnode;
    RMprimitive *p;
    RMtextProps *tp;
    RMvertex2D v;
    RMcolor3D white={1.0,1.0,1.0};
    char string[32]={"woof!"};
    char *c[1];

    textnode = rmNodeNew("text",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);
    p = rmPrimitiveNew(RM_TEXT);

    v.x = RandToFloat(-0.75,0.75);
    v.y = RandToFloat(-0.75,0.75);
    
    rmPrimitiveSetVertex2D(p,1,&v,RM_COPY_DATA,NULL);

    tp = rmTextPropsNew();
    rmTextPropsSetAttribs(tp,
			  RM_FONT_SANS,
			  RM_FONT_L,
			  RM_FALSE,RM_FALSE,
			  RM_LEFT, RM_BOTTOM);
    
    rmNodeSetSceneTextProps(textnode,tp);
    rmTextPropsDelete(tp);

    c[0] = string;
    rmPrimitiveSetText(p,1,c);
    
    rmPrimitiveSetColor3D(p,1,&white,
			  RM_COPY_DATA,NULL);


    rmNodeAddPrimitive(textnode,p);

    rmNodeAddChild(parent,textnode);
    
}

void
my_build_objs()
{
    MyRoot = rmNodeNew("MyRoot",RM_RENDERPASS_2D, RM_RENDERPASS_OPAQUE);

    initrand();

    create_boxes(MyRoot,3); 

    create_circles(MyRoot, 3);

    create_ellipses(MyRoot, 3);

    create_sprite(MyRoot);

    create_bitmap(MyRoot);

    create_text(MyRoot);

    rmNodeAddChild(rmRootNode(),MyRoot);

    {
	RMcolor4D bgcolor={0.2,0.2,0.3,1.0};
	rmNodeSetSceneBackgroundColor(MyRoot, &bgcolor);
    }
}

void
usage(char *s)
{
    char buf[128];
    sprintf(buf," usage: %s [-w xxx (image width)] [-h yyy (image height)] \n",s);
    rmError(buf);
    exit(-1);
}

void
parse_args(int ac,
	   char *av[])
{
    int i;

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

RMnode *picked_node=NULL;
int start_x, start_y;
RMvertex3D save_translate_vector;
float sx,sy;
float camera_yscale,camera_xscale;

int
mypickfunc(RMpipe *p,
	   int ix,
	   int iy)
{
    RMpick *pick_results;
    
    pick_results = rmFramePick(p, rmRootNode(), ix,iy);

    if (pick_results != NULL)
    {
	fprintf(stderr," name of picked object is %s \n",rmPickedNodeName(pick_results));

	picked_node = rmPickedNode(pick_results);
	if (rmNodeGetTranslateVector(picked_node,&save_translate_vector) == RM_WHACKED)
	{
	    save_translate_vector.x = save_translate_vector.y = save_translate_vector.z = 0.;
	}

	start_x = ix;
	start_y = iy;


	/*
	 * the following code is needed to compute the camera_xscale and
	 * camera_yscale parms. these parms are used to attenuate the
	 * translations in the presence of non-square windows.
	 */
	{
	    RMcamera2D *c;
	    float xmin,ymin,xmax,ymax;
	    float aspect;
	    
	    rmNodeGetSceneCamera2D(MyRoot,&c);
	    
	    rmCamera2DGetExtents(c,&xmin,&ymin,&xmax,&ymax);
	    rmCamera2DGetAspectRatio(c, &aspect);

	    camera_yscale = ymax-ymin;  /* y-span of camera in world coords */
	    camera_xscale = xmax-xmin;  /* x-span of camera in world coords */
	    camera_xscale *= aspect;

	    rmPipeGetWindowSize(p, &img_width, &img_height);
	    camera_yscale /= img_height;
	    camera_xscale /= img_width;

	    rmCamera2DDelete(c);

	}

    }
    else
    {
	rmNotice("no objects picked. try again!");
	picked_node = NULL;
    }
    
    return(1);				 /* necessary, otherwise the
					  event loop will exit*/
}

int
mytranslatefunc(RMpipe *p,
		int ix,
		int iy)
{
    float tx,ty;
    float sx,sy;
    RMvertex3D new_translate_vector;

    if (picked_node == NULL)
	return(1);

    sx = camera_xscale;
    sy = camera_yscale;

    tx = (ix - start_x) * sx;
    ty = (start_y - iy) * sy;		 /* flip y */

    new_translate_vector.x = save_translate_vector.x + tx;
    new_translate_vector.y = save_translate_vector.y + ty;
    new_translate_vector.z = 0.F;
    
    rmNodeSetTranslateVector(picked_node,&new_translate_vector);

    /* redraw the frame */

    rmFrame(p, rmRootNode());

    return(1);				 /* necessary, otherwise the
					  event loop will exit*/
    
}

void
myrenderfunc(RMpipe *p,
	     RMnode *n)
{
    rmFrame(p, n);
}

int
myendpickfunc(RMpipe *p,
	      int ix,
	      int iy)
{
    /*
     * this routine is needed in multistage_*_parallel mode to push
     * the last accured transformation down the rendering pipeline.
     */
    rmFrame(p, rmRootNode());

    /* foil compiler warning */
    ix = iy = 0;
    
    return(1);
}

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

    /* enable only the 2D rendering pass */
    rmPipeSetRenderPassEnable(p, RM_FALSE, RM_FALSE, RM_TRUE);

    /*
     * set button functions to do pick & translation
     */
    rmauxSetButtonDownFunc (RM_BUTTON1,RM_NO_MODIFIER, mypickfunc);
    rmauxSetButtonMotionFunc (RM_BUTTON1,RM_NO_MODIFIER, mytranslatefunc);
    rmauxSetButtonUpFunc (RM_BUTTON1, RM_NO_MODIFIER, myendpickfunc);
    
    /*
     * set handler to reset aspect ratio when the window is resized.
     */
    rmauxSetResizeFunc(p, MyRoot, rmauxDefaultResizeFunc);
    
    if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE)
	rmFrame(p, n);
    
    rmFrame(p, n);
}


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

    parse_args(__argc,__argv);

#else  /* assume RM_X */
int 
main(int ac,
     char *av[])
{
    RMpipe *lone_pipe=NULL;
    RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */
    RMenum targetPlatform = RM_PIPE_GLX;
    void *msg;			/* needed for rmauxEventLoop
				 win32/unix API consistency */

    parse_args(ac,av);
#endif
    
    /* 
     * first stage of RM initialization.
     */
    rmInit();

    /* 
     * create the rendering pipe. this step is required in both
     * Win32 and X.
     */
    lone_pipe = rmPipeNew(targetPlatform);

    rmPipeSetProcessingMode(lone_pipe, 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(lone_pipe,
			       NULL, /* no parent window */
			       20,20,img_width,img_height,"RM for Windows",
			       hInstance,fptr);
	if (hWnd == 0)
	  exit(-1);

	/* 
	 * assign the new window handle to the rendering pipe.
	 */
	rmPipeSetWindow(lone_pipe,hWnd, img_width, img_height);
    }
#endif
#ifdef RM_X
    {
	Window w;
	w = rmauxCreateXWindow(lone_pipe,
			       (Window)NULL, /* parent window */
			       0,0,img_width,img_height,
			       "RM for X-Windows","RM",RM_TRUE);

	/* 
	 * assign the window to the rendering pipe.
	 */
	rmPipeSetWindow(lone_pipe,w,img_width,img_height);
    }
#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.
     */

    rmauxSetInitFunc(myinitfunc); 

    /* 
     * 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 we made the newly created context
     * current as part of the OpenGL initialization sequence.
     */
    rmPipeMakeCurrent(lone_pipe);	

    rmauxSetRenderFunc(myrenderfunc);

    /*
     * set key handler function so this prog will exit on "q" key.
     */
    rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc);
    
    rmauxEventLoop(lone_pipe,rmRootNode(), &msg);

    rmPipeDelete(lone_pipe);
    rmFinish();

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