#include <rm/rm.h> #include <rm/rmaux.h> #include "barrier.h" #include "procmode.h" /* * Copyright (C) 2000-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: rm2screen.c,v 1.12 2004/01/17 03:18:20 wes Exp $ * $Revision: 1.12 $ * $Name: OpenRM-1-5-2-RC1 $ * $Log: rm2screen.c,v $ * Revision 1.12 2004/01/17 03:18:20 wes * Updated local SwapBuffers callback so that it matches the prototype * for user-assigned SwapBuffers callbacks. * * Revision 1.11 2003/10/15 05:50:42 wes * Replaced deprecated rmxPipeGetContext() with rmPipeGetContext(). * * Revision 1.10 2003/04/13 18:13:23 wes * Updated copyright dates. * * Revision 1.9 2003/01/27 05:07:07 wes * Changes to RMpipe initialization sequence APIs. Tested for GLX, but not WGL. * * Revision 1.8 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.7 2002/08/19 00:25:23 wes * Updated barrier_* calls and type references to local version so as to * avoid name collision with similar structs and calls inside OpenRM. * * Revision 1.6 2002/06/17 00:42:07 wes * Updated copyright line. * * Revision 1.5 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.4 2001/06/04 00:58:53 wes * Added call to new rmaux key handler callback. * * Revision 1.3 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.2 2000/12/03 23:30:23 wes * *** empty log message *** * * Revision 1.1 2000/12/02 17:23:02 wes * Initial entry. * */ /* * #define PARALLEL 0 means everything (X, glX, OpenGL) is serialized * #define PARALLEL 1 means everything happens in parallel (unreliable * on some platforms) */ #define PARALLEL 0 /* * use -DSOLARIS on the compile line, or uncomment the following line to * activate solaris-specific calls. you shouldn't have to uncomment * the following line on solaris boxes - the configure script in * rmdemo should generate a -DSOLARIS when run on a SunOS box. */ /*#define SOLARIS 1 */ #if 1 int window1[] = {0,0,500,500}; /* geom: x,y, w,h */ int window2[] = {500,0,500,500}; /* geom: x,y, w,h */ char display1[]={":0.0"}; char display2[]={":0.0"}; #else int window1[] = {0,0,1280,2048}; /* geom: x,y, w,h */ int window2[] = {0,0,1280,2048}; /* geom: x,y, w,h */ char display1[]={":2.1"}; char display2[]={":2.2"}; #endif double frust1[] = {-0.5, 0., -0.25, 0.25, 0.1, 10.0};/* frustum l, r, t, b, n, f */ double frust2[] = {0., 0.5, -0.25, 0.25, 0.1, 10.0};/* frustum l, r, t, b, n, f */ #if 0 double frust1[] = {-0.5, 0., -0.5, 0.5, 0.1, 10.0};/* frustum l, r, t, b, n, f */ double frust2[] = {0., 0.5, -0.5, 0.5, 0.1, 10.0};/* frustum l, r, t, b, n, f */ #endif pthread_mutex_t *globalMutex=NULL; int globalExit = 0; my_barrier_t serialRenderBarrier; typedef struct { int windowGeom[4]; /* input window geom */ int myID; /* thread "index" */ RMnode *modelRoot; /* the stuff to render */ pthread_mutex_t *mutex; double frustum[6]; char *display; } thrArgs; #if PARALLEL RMenum mySwapbuffersFunc(const RMpipe *p) { glXSwapBuffers(p->xdisplay, p->xdrawable); glFlush(); return RM_CHILL; } #endif void setFrustum(double *f) { #define FRUST_SCALE .3 glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(f[0]*FRUST_SCALE, f[1]*FRUST_SCALE, f[2]*FRUST_SCALE, f[3]*FRUST_SCALE, f[4], f[5]); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0., 0., 3., 0.0, 0.0, -1.0, 0.0, 1.0, 0.0); } void * threadFunc(void *args) { thrArgs *ta; RMpipe *myPipe=NULL; Window w; char windowTitle[32]; RMenum status; pthread_mutex_t *mutex; RMmatrix modelview; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ ta = (thrArgs *)args; mutex = ta->mutex; #if !PARALLEL pthread_mutex_lock(mutex); #endif myPipe = rmPipeNew(RM_PIPE_GLX); /* status = rmPipeInit(ta->display, RM_MONO_CHANNEL, &myPipe); */ /* * override default processing mode - always use MULTISTAGE serial * in the worker thread functions. */ rmPipeSetProcessingMode(myPipe, RM_PIPE_MULTISTAGE); /* rmPipeSetProcessingMode(myPipe, processingMode); */ #if PARALLEL rmPipeSetSwapBuffersFunc(myPipe, mySwapbuffersFunc); #endif sprintf(windowTitle,"Window%d", ta->myID); w = rmauxCreateXWindow(myPipe, (Window)NULL, /* parent window */ ta->windowGeom[0], ta->windowGeom[1], ta->windowGeom[2], ta->windowGeom[3], windowTitle, windowTitle, RM_FALSE); if (w == 0) { fprintf(stderr," error creating window \n"); exit(-1); } rmPipeSetWindow(myPipe,w,window1[2],window1[3]); XMapWindow(myPipe->xdisplay, myPipe->xdrawable); XSync(myPipe->xdisplay,False); rmPipeMakeCurrent(myPipe); rmPipeSetInitMatrixStackMode(myPipe, RM_FALSE); setFrustum(ta->frustum); rmFrame(myPipe,ta->modelRoot); #if !PARALLEL pthread_mutex_unlock(mutex); #endif sched_yield(); while (globalExit == 0) { #if !PARALLEL pthread_mutex_lock(mutex); glXMakeCurrent(rmxPipeGetDisplay(myPipe), rmPipeGetWindow(myPipe), rmPipeGetContext(myPipe)); #endif setFrustum(ta->frustum); rmFrame(myPipe, ta->modelRoot); glFlush(); #if !PARALLEL pthread_mutex_unlock(mutex); #endif my_barrier_wait(&serialRenderBarrier); sched_yield(); } rmPipeDelete(myPipe); return(NULL); } void buildCamera(RMnode **navView) { /* * create 3 RMnodes, each of which contains a 3D camera. */ RMnode *n, *l, *r; RMcamera3D *c; RMvertex3D eye = {0.0, 0.0, 3.0}; RMvertex3D up = {0.0, 1.0, 0.0}; RMvertex3D navAt= {0.0, 0.0, 0.0} ; float hither=0.1, yon=5.0; *navView = n = rmNodeNew("navView", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); c = rmCamera3DNew(); rmCamera3DSetEye(c,&eye); rmCamera3DSetAt(c,&navAt); rmCamera3DSetUpVector(c,&up); rmCamera3DSetHither(c,hither); rmCamera3DSetYon(c,yon); rmCamera3DSetAspectRatio(c,1.0F); rmCamera3DSetProjection(c,RM_PROJECTION_PERSPECTIVE); rmCamera3DSetFOV(c,60.0F); rmNodeSetSceneCamera3D(n,c); rmCamera3DDelete(c); } void myLighting(RMnode *addToNode) { /* set up two lights, one red and one blue */ RMlight *l0, *l1; RMcolor4D red = {1.0, 0.1, 0.1, 1.0}; RMcolor4D blue= {0.1, 0.1, 1.0, 1.0}; RMcolor4D specular = {0.5, 0.5, 0.5, 1.0}; RMvertex3D redDirection = {-3.0, 2.0, 1.0}; RMvertex3D blueDirection = {3.0, 2.0, 1.0}; l0 = rmLightNew(); if (l0 == NULL) return; rmLightSetType(l0, RM_LIGHT_DIRECTIONAL); rmLightSetColor(l0, NULL, &red, &specular); rmLightSetXYZ (l0, &redDirection); l1 = rmLightNew(); if (l1 == NULL) return; rmLightSetType(l1, RM_LIGHT_DIRECTIONAL); rmLightSetColor(l1, NULL, &blue, &specular); rmLightSetXYZ(l1, &blueDirection); rmNodeSetSceneLight(addToNode, RM_LIGHT0, l0); rmNodeSetSceneLight(addToNode, RM_LIGHT1, l1); /* * when the lights are added as scene parameters, RM makes a copy * of them, and we don't need our RMlight objects anymore, so we * need to delete them. */ rmLightDelete(l0); rmLightDelete(l1); /* set up the light model/lighting environment */ { RMcolor4D defAmbient = {0.5, 0.5, 0.5, 1.0}; RMlightModel *lm = rmLightModelNew(); if (lm == NULL) return; rmLightModelSetAmbient(lm, &defAmbient); rmLightModelSetTwoSided (lm, RM_FALSE); rmLightModelSetLocalViewer(lm, RM_FALSE); rmNodeSetSceneLightModel(addToNode, lm); rmLightModelDelete (lm); } } void buildInitialSceneGraph(RMnode **navView, RMnode **leftView, RMnode **rightView, RMnode **model) { RMnode *boxNode; RMprimitive *boxPrim; RMvertex3D boxCorners[2] = {{-1.0F, -1.0F, -1.0},{1.0F, 1.0F, 1.0F}}; RMcolor4D white={0.9, 0.9, 0.9, 1.0}; RMcolor4D gray={0.2, 0.2, 0.2, 1.0}; RMcolor4D gray1={0.2, 0.25, 0.2, 1.0}; RMcolor4D gray2={0.25, 0.2, 0.2, 1.0}; buildCamera(navView); *leftView = rmNodeNew("leftView", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); *rightView = rmNodeNew("rightView", RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); rmNodeSetSceneBackgroundColor(*navView, &gray); myLighting(*navView); rmNodeSetUnlitColor(*navView, &white); rmNodeSetSceneBackgroundColor(*leftView, &gray1); myLighting(*leftView); rmNodeSetUnlitColor(*leftView, &white); rmNodeSetSceneBackgroundColor(*rightView, &gray2); myLighting(*rightView); rmNodeSetUnlitColor(*rightView, &white); *model = boxNode = rmNodeNew("boxNode",RM_RENDERPASS_3D, RM_RENDERPASS_OPAQUE); boxPrim = rmPrimitiveNew(RM_BOX3D); rmPrimitiveSetVertex3D(boxPrim, 2, boxCorners, RM_COPY_DATA, NULL); rmNodeAddPrimitive(boxNode, boxPrim); rmNodeComputeBoundingBox(boxNode); rmNodeComputeCenterFromBoundingBox(boxNode); rmNodeAddChild(*navView, boxNode); rmNodeAddChild(*leftView, boxNode); rmNodeAddChild(*rightView, boxNode); } void myRenderFunc(RMpipe *p, RMnode *n) { rmFrame(p, n); /* render on the navPipe */ pthread_mutex_unlock(globalMutex); /* release slave rmPipe renderers */ my_barrier_wait(&serialRenderBarrier); /* global barrier */ pthread_mutex_lock(globalMutex); } void myIdleFunc(RMpipe *p, int ix, int iy) { #if !PARALLEL pthread_mutex_unlock(globalMutex); #endif sched_yield(); usleep(100); #if !PARALLEL pthread_mutex_lock(globalMutex); #endif } void myInitFunc(RMpipe *p, RMnode *toDraw) { rmFrame(p, toDraw); myRenderFunc(p, toDraw); } int main(int ac, char *av[]) { pthread_attr_t attr; pthread_t thrID1, thrID2; void *returnVal; thrArgs *threadArgs1, *threadArgs2; RMnode *leftView=NULL, *rightView=NULL, *navView=NULL, *model=NULL; int status; pthread_mutex_t *mutex; RMpipe *navPipe=NULL; Window navWindow; RMenum processingMode = DEFAULT_PROCESSING_MODE; /* in procmode.h */ status = XInitThreads(); #ifdef SOLARIS glXInitThreadsSUN(); #else pthread_setconcurrency(12); #endif rmInit(); buildInitialSceneGraph(&navView, &leftView, &rightView,&model); /* rmPipeInit(getenv("DISPLAY"), RM_MONO_CHANNEL, &navPipe); */ /* rmPipeInit(":0.0", RM_MONO_CHANNEL, &navPipe); */ navPipe = rmPipeNew(RM_PIPE_GLX); rmPipeSetProcessingMode(navPipe, processingMode); #if PARALLEL rmPipeSetSwapBuffersFunc(navPipe, mySwapbuffersFunc); #else my_barrier_init(&serialRenderBarrier, 3); #endif navWindow = rmauxCreateXWindow(navPipe, (Window)NULL, 500, 600, 200, 200, "Navigate Window", "navWindow", RM_TRUE); rmPipeSetWindow(navPipe, navWindow, 200, 200); rmPipeMakeCurrent(navPipe); rmauxSetGeomTransform(model, navPipe); globalMutex = mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); status = pthread_mutex_init(mutex,NULL); #if !PARALLEL pthread_mutex_lock(mutex); #endif threadArgs1 = (thrArgs *)malloc(sizeof(thrArgs)); memcpy(threadArgs1->windowGeom, window1, sizeof(int)*4); memcpy(threadArgs1->frustum, frust1, sizeof(double)*6); threadArgs1->myID = 0; threadArgs1->modelRoot = leftView; threadArgs1->mutex = mutex; threadArgs1->display = display1; pthread_attr_init(&attr); pthread_create(&thrID1, &attr, threadFunc, (void *)threadArgs1); threadArgs2 = (thrArgs *)malloc(sizeof(thrArgs)); memcpy(threadArgs2->windowGeom, window2, sizeof(int)*4); memcpy(threadArgs2->frustum, frust2, sizeof(double)*6); threadArgs2->myID = 1; threadArgs2->modelRoot = rightView; threadArgs2->mutex = mutex; threadArgs2->display = display2; pthread_create(&thrID2, &attr, threadFunc, (void *)threadArgs2); /* rmauxSetIdleFunc(navPipe, myIdleFunc); */ rmauxSetRenderFunc(myRenderFunc); rmauxSetInitFunc(myInitFunc); /* * set key handler function so this prog will exit on "q" key. */ rmauxSetKeyFunc(navPipe, rmauxDefaultKeyFunc); rmauxEventLoop(navPipe, navView, NULL); globalExit = 1; myRenderFunc(navPipe, navView); pthread_mutex_unlock(mutex); status = pthread_join(thrID2, &returnVal); status = pthread_join(thrID1, &returnVal); rmPipeDelete(navPipe); #if !PARALLEL my_barrier_destroy(&serialRenderBarrier); #endif rmFinish(); }