/* * Copyright (C) 1997-2003, 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: tmap2d.c,v 1.13 2003/04/13 18:13:23 wes Exp $ * $Revision: 1.13 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: tmap2d.c,v $ * Revision 1.13 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.12 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.11 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.10 2002/06/17 00:44:52 wes * Replaced rmSubtreeFrame with rmFrame. * * Revision 1.9 2001/10/15 00:24:52 wes * Use of RM_SOFTWARE resize engine. * * 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 18:47:48 wes * Added intial transformation to object is visible at startup, changed * texture map resize from RM_HARDWARE to RM_SOFTWARE to guarantee * compatiblity with all forms of RMpipe multistage processing. * * Revision 1.5 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.4 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.3 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.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.9 2000/02/28 17:21:55 wes * RM 1.2, pre-OpenRM * */ #include <rm/rm.h> #include <rm/rmaux.h> #include <rm/rmi.h> #include "libdio.h" #include "procmode.h" static char MyRootName[]={"MyRoot"}; static RMnode *MyRoot; int img_width=600; #ifdef RM_X int img_height=512; #else /* assume RM_WIN */ int img_height = 548; #endif int do_mipmaps=0; int prim_type=0; static char image_filename[]={"data/doghead.jpg"}; void usage(char *av[]) { char buf[256]; sprintf(buf," usage: %s [-w img_width] [-h img_height] [-mips (compute all mipmaps)] [-t prim_type (0=single quad, 1=disjoint tris, 2=t-strip, 3=t-fan)] [-i fname (name of avs image file to use as a texture) \n",av[0]); rmError(buf); } 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 if (strcmp(av[i],"-mips") == 0) { do_mipmaps = 1; } else if (strcmp(av[i],"-t") == 0) { i++; sscanf(av[i],"%d",&prim_type); } else if (strcmp(av[i],"-i") == 0) { i++; strcpy(image_filename,av[i]); } else { usage(av); exit(-1); } i++; } } void my_set_scene(RMnode *camNode, int stereo_format) { RMcamera3D *c=rmCamera3DNew(); /* assign a default view */ rmDefaultCamera3D(c); /* adjust the view so all geom is visible */ rmCamera3DComputeViewFromGeometry(c,MyRoot,img_width,img_height); /* tweak stereo parms if necessary. */ if (stereo_format != RM_MONO_CHANNEL) { rmCamera3DSetStereo(c,RM_TRUE); rmCamera3DSetEyeSeparation(c,2.5F); rmCamera3DSetFocalDistance (c,0.707F); } /* then update the camera on the node */ rmNodeSetSceneCamera3D(camNode,c); /* free the copy we were working with. */ rmCamera3DDelete(c); /* use RM's default lighting model */ rmDefaultLighting(camNode); } void build_and_add_quad(RMnode *addto) { RMnode *b; RMprimitive *p; RMvertex3D v[4]; RMvertex2D tc[4]; RMvertex3D n[4]; b = rmNodeNew("outline box",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); p = rmPrimitiveNew(RM_QUADS); v[0].x = v[0].y = v[0].z = 0.0; VCOPY(v,v+1); v[1].x = 1.0; VCOPY(v+1,v+2); v[2].z = 1.0; VCOPY(v+2,v+3); v[3].x = 0.0; n[0].x = n[0].z = 0.0; n[0].y = -1.0; n[1] = n[0]; n[2] = n[0]; n[3] = n[0]; 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; rmPrimitiveSetVertex3D(p,4,v,RM_COPY_DATA,NULL); rmPrimitiveSetNormal3D(p,4,n,RM_COPY_DATA, NULL); rmPrimitiveSetTexcoord2D(p,4,tc, RM_COPY_DATA, NULL); rmNodeAddPrimitive(b,p); rmNodeComputeBoundingBox(b); rmNodeAddChild(MyRoot,b); } void build_and_add_distris(RMnode *addto) { RMnode *b; RMprimitive *p; RMvertex3D v[6]; RMvertex2D tc[6]; RMvertex3D n[6]; b = rmNodeNew("outline box",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); p = rmPrimitiveNew(RM_TRIANGLES); v[0].x = v[0].y = v[0].z = 0.0; VCOPY(v,v+1); v[1].x = 1.0; VCOPY(v+1,v+2); v[2].z = 1.0; VCOPY(v,v+3); VCOPY(v+3,v+4); v[4].x = v[4].z = 1.0; VCOPY(v,v+5); v[5].z = 1.0; n[0].x = n[0].z = 0.0; n[0].y = -1.0; VCOPY(n,n+1); VCOPY(n,n+2); VCOPY(n,n+3); VCOPY(n,n+4); VCOPY(n,n+5); 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 = tc[3].y = 0.0; tc[4].x = tc[4].y = 1.0; tc[5].x = 0.0; tc[5].y = 1.0; rmPrimitiveSetVertex3D(p,6,v,RM_COPY_DATA,NULL); rmPrimitiveSetNormal3D(p,6,n,RM_COPY_DATA, NULL); rmPrimitiveSetTexcoord2D(p,6,tc,RM_COPY_DATA, NULL); rmNodeAddPrimitive(b,p); rmNodeComputeBoundingBox(b); rmNodeAddChild(MyRoot,b); } void build_and_add_tstrip(RMnode *addto) { RMnode *b; RMprimitive *p; RMvertex3D v[4]; RMvertex2D tc[4]; RMvertex3D n[4]; b = rmNodeNew("outline box",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); p = rmPrimitiveNew(RM_TRIANGLE_STRIP); v[0].x = v[0].y = v[0].z = 0.0; v[1].x = 1.0; v[1].y = v[1].z = 0.0; v[2].x = v[2].y = 0.0; v[2].z = 1.0; v[3].x = v[3].z = 1.0; v[3].y = 0.0; n[0].x = n[0].z = 0.0; n[0].y = -1.0; VCOPY(n,n+1); VCOPY(n+1,n+2); VCOPY(n+2,n+3); tc[0].x = tc[0].y = 0.0; tc[1].x = 1.0; tc[1].y = 0.0; tc[2].x = 0.0; tc[2].y = 1.0; tc[3].x = tc[3].y = 1.0; rmPrimitiveSetVertex3D(p,4,v,RM_COPY_DATA,NULL); rmPrimitiveSetNormal3D(p,4,n,RM_COPY_DATA, NULL); rmPrimitiveSetTexcoord2D(p,4,tc,RM_COPY_DATA,NULL); rmNodeAddPrimitive(b,p); rmNodeComputeBoundingBox(b); rmNodeAddChild(MyRoot,b); } void build_and_add_tfan(RMnode *addto) { RMnode *b; RMprimitive *p; RMvertex3D v[4]; RMvertex2D tc[4]; RMvertex3D n[4]; b = rmNodeNew("outline box",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); p = rmPrimitiveNew(RM_TRIANGLE_FAN); v[0].x = v[0].y = v[0].z = 0.0; v[1].x = 1.0; v[1].y = v[1].z = 0.0; v[2].x = v[2].z = 1.0; v[2].y = 0.0; v[3].x = v[3].y = 0.0; v[3].z = 1.0; n[0].x = n[0].z = 0.0; n[0].y = -1.0; VCOPY(n,n+1); VCOPY(n+1,n+2); VCOPY(n+2,n+3); 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; rmPrimitiveSetVertex3D(p,4,v,RM_COPY_DATA,NULL); rmPrimitiveSetNormal3D(p,4,n,RM_COPY_DATA,NULL); rmPrimitiveSetTexcoord2D(p,4,tc,RM_COPY_DATA,NULL); rmNodeAddPrimitive(b,p); rmNodeComputeBoundingBox(b); rmNodeAddChild(MyRoot,b); } void my_build_objs(RMpipe *p) { RMimage *mytexture,*resizedtexture; #if 0 /* RM_HARDWARE does not function reliably when using multithreaded and multistage rendering - the OpenGL pipe isn't really initialized yet at this point. */ int resize_engine = RM_HARDWARE; /* or RM_SOFTWARE */ #endif int resize_engine = RM_SOFTWARE; MyRoot = rmNodeNew("MyRoot", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); rmNodeSetName(MyRoot,MyRootName); /* and has a name.. */ /* * read the texture image... */ /* mytexture = dioReadAVSImage(image_filename); */ mytexture = rmiReadJPEG(image_filename); rmImageMirror(mytexture, RM_IMAGE_MIRROR_HEIGHT); if (mytexture == NULL) { fprintf(stderr," problem reading the texture. exiting..\n"); exit(-1); } /* * it's probably not the right size...not an even power of two. * fix that. */ { int w,h,nw,nh; rmImageGetImageSize(mytexture,NULL,&w,&h,NULL,NULL,NULL); nw = rmNearestPowerOfTwo(w); nh = rmNearestPowerOfTwo(h); resizedtexture = rmImageNew(2,nw,nh,1,rmImageGetFormat(mytexture), rmImageGetType(mytexture), RM_COPY_DATA); rmImageResize(mytexture,resizedtexture,resize_engine,p); } /* * we now have an image of size 2**N,2**M. build an RMtexture * object for use as a scene parameter. populate the RMtexture * with the new resized image, or build mipmaps. */ { RMtexture *t; int mipmap_count = 1; RMimage **mipmaps; t = rmTextureNew(2); if (do_mipmaps == 1) { rmTextureSetFilterMode(t,GL_LINEAR_MIPMAP_LINEAR,GL_NEAREST); mipmap_count = rmImageBuildMipmaps(resizedtexture, &mipmaps,resize_engine,p); } else mipmaps = &resizedtexture; rmTextureSetImages(t,mipmaps,mipmap_count,RM_FALSE); rmNodeSetSceneTexture(MyRoot,t); /* * don't need to rmTextureDelete() the texture because RM will * manage that for us. */ } if (prim_type == 0) build_and_add_quad(MyRoot); else if (prim_type == 1) build_and_add_distris(MyRoot); else if (prim_type == 2) build_and_add_tstrip(MyRoot); else if (prim_type == 3) build_and_add_tfan(MyRoot); rmNodeAddChild(rmRootNode(),MyRoot); rmNodeUnionAllBoxes(rmRootNode()); /* this does union of bounding boxes in the entire tree, starting from the leaf nodes and going upwards.*/ rmNodeComputeCenterFromBoundingBox(MyRoot); rmNodeComputeCenterFromBoundingBox(rmRootNode()); { RMcolor4D bgcolor={0.2,0.2,0.3,1.0}; rmNodeSetSceneBackgroundColor(MyRoot,&bgcolor); } rmImageDelete(mytexture); /* don't need it any more */ } 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()); } void setInitialTransform(RMnode *n) { RMmatrix m; double c, s, r= RM_DEGREES_TO_RADIANS(-135.0); c = cos(r); s = sin(r); rmMatrixIdentity(&m); m.m[1][1] = m.m[2][2] = c; m.m[2][1] = -s; m.m[1][2] = s; rmNodeSetRotateMatrix(n, &m); } void myinitfunc(RMpipe *p, RMnode *n) { my_build_objs(p); my_set_scene(rmRootNode(), rmPipeGetChannelFormat(p)); rmauxSetGeomTransform(MyRoot,p); rmauxSetCamera3DTransform(rmRootNode(), p); setInitialTransform(MyRoot); /* * set handler to reset aspect ratio when the window is resized. */ rmauxSetResizeFunc(p, rmRootNode(), rmauxDefaultResizeFunc); if (rmPipeProcessingModeIsMultithreaded(p) == RM_TRUE) rmFrame(p, rmRootNode()); rmFrame(p, rmRootNode()); } void myRenderFunc(RMpipe *p, RMnode *n) { rmFrame(p, n); } #ifdef RM_WIN int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; HWND hWnd; void *fptr; RMenum channelFormat; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum targetPlatform = RM_PIPE_WGL; RMpipe *lone_pipe=NULL; int status; parse_args(__argc,__argv); #else /* assume RM_X */ int main(int ac, char *av[]) { RMpipe *lone_pipe=NULL; int status; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ RMenum channelFormat; RMenum targetPlatform = RM_PIPE_GLX; void *msg; /* needed for rmauxEventLoop win32/unix API consistency */ processingMode = RM_PIPE_SERIAL; /* tmp wes 10/17/02 */ parse_args(ac,av); #endif /* * 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. */ channelFormat = RM_MONO_CHANNEL; /* * 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 */ 0,0,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); /* uncomment this next line if you want the object to rotate while the user is idle. */ /* rmauxSetIdleFunc(lone_pipe,my_idle_func); */ /* * X-ism: once the window is created and assigned to the * rendering pipe, rmPipeInit 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(lone_pipe); /* * set key handler function so this prog will exit on "q" key. */ rmauxSetKeyFunc(lone_pipe, rmauxDefaultKeyFunc); rmauxSetRenderFunc(myRenderFunc); rmauxEventLoop(lone_pipe,rmRootNode(), &msg); rmPipeDelete(lone_pipe); rmFinish(); #ifdef RM_WIN return( msg.wParam ); #else return(1); #endif }