/* * Copyright (C) 1997-2005, R3vis Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; 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/lgpl.html. * * Original Contributor: * Wes Bethel, R3vis Corporation, Marin County, California * Additional Contributor(s): * * The OpenRM project is located at http://openrm.sourceforge.net/. */ /* * $Id: rmmtview.c,v 1.11 2005/06/26 18:53:48 wes Exp $ * Version: $Name: OpenRM-1-6-0-RC5 $ * $Revision: 1.11 $ * $Log: rmmtview.c,v $ * Revision 1.11 2005/06/26 18:53:48 wes * Consolidated opcode to capture both local state push/pop and OpenGL * attrib stack push/pop. This move helps streamline state tracking and * makes it possible to better track OpenGL state. * * Revision 1.10 2005/06/06 02:04:29 wes * Lots of small additions to clean up compiler warnings. * * Revision 1.9 2005/03/16 16:44:42 wes * Added node index to certain opcodes to support better debug output. * * Revision 1.8 2005/02/24 16:17:25 wes * Added support for fbClears to be set at the RMpipe level. Apps can * still set them at the RMnode level if desired for fine-grained control. * * Revision 1.7 2005/02/19 16:22:50 wes * Distro sync and consolidation. * * Revision 1.6 2005/01/23 17:04:03 wes * Copyright update to 2005. * * Revision 1.5 2004/01/16 16:46:09 wes * Updated copyright line for 2004. * * Revision 1.4 2003/04/12 21:02:00 wes * Undo of movement of RMpipe view-render buffer init back into rendering * path, and out of rmPipeNew. The init code can't be called from rmPipeNew * because the init code depends upon the channel format, which is usually * set by the app after rmPipeNew. * * Revision 1.3 2003/04/05 14:08:12 wes * Add code to remove inter-stage buffers, which is invoked vis a vis rmFinish(). * * Revision 1.2 2003/02/02 02:07:15 wes * Updated copyright to 2003. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.9 2003/01/16 22:21:17 wes * Updated all source files to reflect new organization of header files: * all header files formerly located in include/rmaux, include/rmi, include/rmv * are now located in include/rm. * * Revision 1.8 2002/09/05 15:06:18 wes * tweaks to how sizes for display list indices and opcodes are * computed during the init phase. these now use the default page * size, which is probably not the correct thing. these changes were * made during a frenzy of realloc code testing. * * Revision 1.7 2002/08/29 22:20:32 wes * * Massive upgrade to accommodate dynamic object reallocation within * the component manager, and within the context cache. Use the * debug #define DEBUG_LEVEL DEBUG_REALLOC_TRACE to get a printf * whenever a realloc occurs. With this upgrade, there are no * OpenRM limits on the size of the scene graph. There will be external * limits, such as the amount of RAM and the amount of space available * to your OpenGL implementation. * * Revision 1.6 2002/08/17 15:11:43 wes * Added machinery to invoke user-defined pre- and post-traversal callbacks * in the view traversal. * * Revision 1.5 2002/06/17 01:00:10 wes * Doubled the amount of space allocated for render stage opcodes. * This change was motivated by bug reports from a user who has extremely * large scene graphs where many nodes contains transformations. The * remaining problem is to accurately compute the amount of opcode * space required for a given SG configuration. The number of opcodes required * for each node varies as a function of the contents of its scene * parameters, transformations, etc. * * Revision 1.4 2002/04/30 19:32:47 wes * Updated copyright dates. * * Revision 1.3 2001/10/15 00:09:03 wes * Removed some dead code. * * Revision 1.2 2001/06/03 20:47:28 wes * Removed unused vars to get rid of compile warnings. * * Revision 1.1 2001/03/31 17:12:39 wes * v1.4.0-alpha-2 checkin. * */ #include #include "rmmultis.h" #include "rmprivat.h" #define MODELVIEW_MATRIX_CHANGED 1 #define PROJECTION_MATRIX_CHANGED 2 #define TEXTURE_MATRIX_CHANGED 4 static void private_rmViewRecurse(RMpipe *p, RMnode *n, int (*nodeRenderPassFunc)(const RMnode *), int (*channelTraversalFunc)(const RMnode *), RMdisplayList *dl, RMstate *rState); RMdisplayList * private_rmPipeDisplayListNew(int initNumOpCodes, int initNumIndices, int initNumMatrices) { RMdisplayList *t; t = (RMdisplayList *)malloc(sizeof(RMdisplayList)); memset(t,0,sizeof(RMdisplayList)); t->opCodes = (RMdisplayListOpcode *)malloc(sizeof(RMdisplayListOpcode) * initNumOpCodes); t->nOpCodes = 0; t->maxOpCodes = initNumOpCodes; t->indices = (int *)malloc(sizeof(int)*initNumIndices); t->nIndices = 0; t->maxIndices = initNumIndices; t->matrices = (RMmatrix *)malloc(sizeof(RMmatrix)*initNumMatrices); t->nMatrices = 0; t->maxMatrices = initNumMatrices; return(t); } void private_rmPipeDisplayListDelete(RMdisplayList *t) { free((void *)(t->opCodes)); free((void *)(t->indices)); free((void *)(t->matrices)); free((void *)t); } /* PRIVATE */ /* * Generate structures that are used to pass information between the * view-stage and render-stage traversals. These structures are NOT * OpenGL display lists. They are "real" display lists in the * traditional use of the term. */ void private_rmPipeDisplayListsNew(RMpipe *p) { int initNumNodes; int initNumMatrices = NUM_ITEMS_PER_PAGE; RMmultipassDisplayList *mpdl; /* * build initial render buffers. */ initNumNodes = initNumMatrices = NUM_ITEMS_PER_PAGE; /* right now, we're creating only a single multipass display list buffer. we'll need a second one if doing stereo. each multipass display list consists of double-buffers for results of multiple rendering passes: 3D opaque, 3d transparent, and 2d opaque */ mpdl = (RMmultipassDisplayList *)malloc(sizeof(RMmultipassDisplayList)); mpdl->globals[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->globals[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->opaque3D[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->opaque3D[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->transparent3D[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->transparent3D[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->opaque2D[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->opaque2D[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); if (p->channel_format != RM_MONO_CHANNEL) /* assume a stereo format */ { mpdl->rightGlobals[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightGlobals[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightOpaque3D[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightOpaque3D[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightTransparent3D[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightTransparent3D[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightOpaque2D[0] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); mpdl->rightOpaque2D[1] = private_rmPipeDisplayListNew(initNumNodes, initNumNodes, initNumMatrices); } p->displayLists = (void *)mpdl; } /* PRIVATE */ void private_rmPipeDisplayListsDelete(RMpipe *p) { RMmultipassDisplayList *mpdl; mpdl = (RMmultipassDisplayList *)(p->displayLists); private_rmPipeDisplayListDelete(mpdl->globals[0]); private_rmPipeDisplayListDelete(mpdl->globals[1]); private_rmPipeDisplayListDelete(mpdl->opaque3D[0]); private_rmPipeDisplayListDelete(mpdl->opaque3D[1]); private_rmPipeDisplayListDelete(mpdl->transparent3D[0]); private_rmPipeDisplayListDelete(mpdl->transparent3D[1]); private_rmPipeDisplayListDelete(mpdl->opaque2D[0]); private_rmPipeDisplayListDelete(mpdl->opaque2D[1]); if (p->channel_format != RM_MONO_CHANNEL) /* assume a stereo format */ { private_rmPipeDisplayListDelete(mpdl->rightGlobals[0]); private_rmPipeDisplayListDelete(mpdl->rightGlobals[1]); private_rmPipeDisplayListDelete(mpdl->rightOpaque3D[0]); private_rmPipeDisplayListDelete(mpdl->rightOpaque3D[1]); private_rmPipeDisplayListDelete(mpdl->rightTransparent3D[0]); private_rmPipeDisplayListDelete(mpdl->rightTransparent3D[1]); private_rmPipeDisplayListDelete(mpdl->rightOpaque2D[0]); private_rmPipeDisplayListDelete(mpdl->rightOpaque2D[1]); } free((void *)mpdl); p->displayLists = NULL; } /* PRIVATE */ void private_rmPipeDisplayListsInit(RMpipe *p, int i) /* even/odd buffer */ { /* reset all counters in pipe mt buffers to zero. */ RMdisplayList *t; RMmultipassDisplayList *mp; mp = (RMmultipassDisplayList *)(p->displayLists); t = mp->globals[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; t = mp->opaque3D[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; t = mp->transparent3D[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; t = mp->opaque2D[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; if (rmPipeGetChannelFormat(p) != RM_MONO_CHANNEL) { t = mp->rightGlobals[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; t = mp->rightOpaque3D[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; t = mp->rightTransparent3D[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; t = mp->rightOpaque2D[i]; t->nOpCodes = 0; t->nIndices = 0; t->nMatrices = 0; } } static int leftChannelTraversalFunc (const RMnode *r) { /* this filterfunc will return 1 if the node "r" is scheduled for traversal during the "left channel" OR "all channels" traversal */ RMenum stat = private_rmNodeGetTraversalMaskChannel(r); if ((stat == RM_ALL_CHANNELS) || (stat == RM_LEFT_CHANNEL)) return(1); else return(0); } static int rightChannelTraversalFunc (const RMnode *r) { /* this filterfunc will return 1 if the node "r" is scheduled for traversal during the "right channel" OR "all channels" traversal */ RMenum stat = private_rmNodeGetTraversalMaskChannel(r); if ((stat == RM_ALL_CHANNELS) || (stat == RM_RIGHT_CHANNEL)) return(1); else return(0); } static int opaque3DFilterFunc (const RMnode *r) { /* this filterfunc will return 1 if the node "r" has 3D locations and is op aque */ RMenum vdims, opacity; vdims = private_rmNodeGetTraversalMaskDims(r); opacity = private_rmNodeGetTraversalMaskOpacity(r); if (((vdims == RM_RENDERPASS_3D) || (vdims == RM_RENDERPASS_ALL)) && ((opacity == RM_RENDERPASS_OPAQUE) || (opacity == RM_RENDERPASS_ALL))) return(1); else return(0); } static int transparent3DFilterFunc (const RMnode *r) { /* this filterfunc will return 1 if the node "r" has 3D locations and is op aque */ RMenum vdims, opacity; vdims = private_rmNodeGetTraversalMaskDims(r); opacity = private_rmNodeGetTraversalMaskOpacity(r); if (((vdims == RM_RENDERPASS_3D) || (vdims == RM_RENDERPASS_ALL)) && ((opacity == RM_RENDERPASS_TRANSPARENT) || (opacity == RM_RENDERPASS_ALL))) return(1); else return(0); } static int opaque2DFilterFunc (const RMnode *r) { /* this filterfunc will return 1 if the node "r" has 2D locations and is op aque */ RMenum vdims, opacity; vdims = private_rmNodeGetTraversalMaskDims(r); opacity = private_rmNodeGetTraversalMaskOpacity(r); if (((vdims == RM_RENDERPASS_2D) || (vdims == RM_RENDERPASS_ALL)) && ((opacity == RM_RENDERPASS_OPAQUE) || (opacity == RM_RENDERPASS_ALL))) return(1); else return(0); } static void private_rmDLloadOpcode(RMdisplayListOpcode opcode, int inputIndx, RMdisplayList *mt) { int indx; indx = mt->nOpCodes; if (indx+1 >= mt->maxOpCodes) /* need to realloc */ { int oldSize, newSize; oldSize = mt->maxOpCodes; newSize = oldSize + NUM_ITEMS_PER_PAGE; mt->opCodes = (RMdisplayListOpcode *)realloc(mt->opCodes, newSize * sizeof(RMdisplayListOpcode)); mt->maxOpCodes += NUM_ITEMS_PER_PAGE; #if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE) printf("private_rmDLloadOpcode() realloc of opcode space. oldSize=%d, newSize = %d. \n", oldSize, newSize); #endif #if 0 rmError("private_rmDLloadOpcode() error: out of opcode space! Need to write realloc code! "); return; #endif } mt->opCodes[indx] = opcode; mt->nOpCodes += 1; /* note: t->nIndices should be the same as t->nOpCodes !! */ indx = mt->nIndices; if (indx+1 >= mt->maxIndices) /* need to realloc */ { int oldSize, newSize; oldSize = mt->maxIndices; newSize = oldSize + NUM_ITEMS_PER_PAGE; mt->indices = (int *)realloc(mt->indices, newSize * sizeof(int)); mt->maxIndices += NUM_ITEMS_PER_PAGE; #if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE) printf("private_rmDLloadOpcode() realloc of index space. oldSize=%d, newSize = %d. \n", oldSize, newSize); #endif } mt->indices[indx] = inputIndx; mt->nIndices += 1; } static int private_rmDLnextMatrix(RMdisplayList *mt) { if ((mt->nMatrices + 1) >= mt->maxMatrices) { int oldSize, newSize; oldSize = mt->maxMatrices; newSize = oldSize + NUM_ITEMS_PER_PAGE; mt->matrices = (RMmatrix *)realloc(mt->matrices, newSize * sizeof(RMmatrix)); mt->maxMatrices += NUM_ITEMS_PER_PAGE; #if (DEBUG_LEVEL & DEBUG_REALLOC_TRACE) printf("private_rmDLnextMatrix() - reallocating matrix space; oldValue = %d, newValue = %d \n", oldSize, newSize); #endif #if 0 /* old code */ rmError("private_rmDLnextMatrix() error: out of matrix space! Need to write realloc code! "); return(mt->maxMatrices - 1); #endif } return(mt->nMatrices); } static void private_rmDLloadMatrix(RMdisplayList *mt, int indx, RMmatrix *src) { rmMatrixCopy(mt->matrices + indx, src); mt->nMatrices += 1; } static int private_rmDLcollectTransformations(RMpipe *p, RMnode *n, RMstate *rState) { RMmatrix model, view, proj; RMmatrix oglModelView; int returnStatus; int viewMatrixChange=0, textureMatrixChange=0, modelMatrixChange=0; /* * this routine will determine what changes to the matrix stack, * if any, are dictated by parms at this node. * * note that RM maintains separate view, model & projection * matrices in the RMstate object. * * the return value from this routine is the bitwise OR of the following: * MODELVIEW_MATRIX_CHANGED,PROJECTION_MATRIX_CHANGED and * TEXTURE_MATRIX_CHANGED. each of these bits indicates which of * corresponding OpenGL matrix stacks was modified by this routine. a * return value of zero means that no matrix stacks were modified. * * the viewport is processed here, rather than later, because the * camera routines have occasion to use the current viewport. * */ if (n->scene_parms && n->scene_parms->viewport) /* do viewport */ /* viewport needed for pick matrix */ private_setViewport(n, rState, RM_FALSE, RM_FALSE); if (n->scene_parms && n->scene_parms->camera3d) { RMcamera3D tmp; /* stereo disabled for now */ if ((rState->which_channel != RM_ALL_CHANNELS) && (rmCamera3DGetStereo(n->scene_parms->camera3d) == RM_TRUE)) { /* * for binocular stereo, we compute a new camera model for * the left or right eye, then use that modified camera in * the derivation of a view matrix. */ private_computeStereoOffset(n->scene_parms->camera3d, rState->which_channel, &tmp); } else tmp = *(n->scene_parms->camera3d); rmCamera3DComputeViewMatrix(&tmp, &view, &proj); #if 0 /* picking disabled for now in multistage renderer */ if (renderMode == GL_SELECT) { RMmatrix pick; private_rmComputePickMatrix(rState, &pick); rState->pick = pick; private_drawCameraPickableQuad(perObjFunc, n); } #endif /* nested view matrices are permissible, but weird. transformations occur such that child transformations apply first. */ rmMatrixMultiply(&view, &(rState->view), &view); rState->view = view; /* nested projection matrices give me headaches to think about - they are not permitted */ /* rState->projection = proj; 2/15/2001 wes: llnl/vdl */ rmMatrixMultiply(&proj, &(rState->projection), &(rState->projection)); viewMatrixChange = 1; } else if (n->scene_parms && n->scene_parms->camera2d) { rmCamera2DComputeViewMatrix(n->scene_parms->camera2d, &view); rState->view = view; #if 0 /* selection (picking) in multistage disabled for now (1/2001) */ if (renderMode == GL_SELECT) { RMmatrix pick; private_rmComputePickMatrix(rState, &pick); rState->pick = pick; private_drawCameraPickableQuad(perObjFunc, n); } #endif rmMatrixIdentity(&proj); rmMatrixMultiply(&proj, &(rState->projection), &(rState->projection)); viewMatrixChange = 1; } if ((n->transforms != NULL) && (n->transforms->transform_mode != RM_TRANSFORM_IGNORE)) { rmNodeGetCompositeModelMatrix(n, &model); /* texture and model matrix transformations accumulate - child transformations are applied first */ if (n->transforms->transform_mode == RM_TRANSFORM_TEXTURE) { rmMatrixMultiply(&model, &(rState->textureMatrix), &model); rState->textureMatrix = model; textureMatrixChange = 1; returnStatus = TEXTURE_MATRIX_CHANGED; } else { rmMatrixMultiply(&model, &(rState->model), &model); rState->model = model; modelMatrixChange = 1; } } /* now apply the matrices to the RMstate object - load the matrices into OpenGL */ if (modelMatrixChange==1 || viewMatrixChange == 1) { RMmatrix m; returnStatus = MODELVIEW_MATRIX_CHANGED; rmMatrixMultiply(&(rState->model), &(rState->view), &oglModelView); if (viewMatrixChange == 1) { rmMatrixMultiply(&(rState->pick), &(rState->projection), &m); returnStatus |= PROJECTION_MATRIX_CHANGED; } rState->modelView = oglModelView; rmMatrixMultiply(&(rState->modelView), &(rState->projection), &(rState->composite)); } p = NULL; /* foil compiler warning */ return(returnStatus); } static void private_rmViewRecurse(RMpipe *p, RMnode *n, int (*nodeRenderPassFunc)(const RMnode *), int (*channelTraversalFunc)(const RMnode *), RMdisplayList *mt, RMstate *rState) { /* * outline: * * filterfunc test * pretraversal callback * channel traversal test * if !ok * return; * * cullmode: * if frustumcull * { * compute/accumulate xform, if any * frustum cull * } * other types of cull? * if !pass * return; * * compute opcode for this node (eg, pushattrib, loadmatrix, etc.) * load matrix and or node * into pipe mt buffer * * switch callback -OR- renderOrder callback * process child(ren) * * undo pushattrib, if set * * post traverse */ /* * June 2005. Consolidated two opcode push/pops into one: combined * the attrib and local state push/pop into a single call. The reason * is so that when we do a pop from the attrib stack, we can also * compare the attrib mask used during the push (now stored in a * stack in the RMpipe). If the GL_ENABLE_BIT is set, we then know * that the RMstateCache may be out of sync wrt the RMstate. Normally, * the RMstateCache has the most current settings for lighting, * colormaterial, etc. In some cases, some RMprimitives will * turn off lighting (rmText, for example), which is reflected * in the RMstateCache, but then when attrib/state is popped, the * old state might have had lighting enabled, which would then cause * OpenGL, the RMstate and the RMstateCache to be out of sync. * * By combining the two opcodes into one, we can address the sync problem * whereas we couldn't using two separate push/pops. */ #if 0 GLuint mask=0; #endif int i, descendChildren; int haveCameras, haveTransforms, haveTextProps, haveOGLStateChange; RMstate localState; int matrixStat=0; /* need refinement on traverse-enable flags? */ if (rmNodeGetTraverseEnable(n) == RM_FALSE) return; /* compare 3d/2d/transparent renderpass with values set in node */ if ((*nodeRenderPassFunc)(n) == 0) return; /* compare to stereo channel settings */ if ((channelTraversalFunc != NULL) && ((*channelTraversalFunc)(n) == 0)) return; localState = *rState; /* invoke user defined view-stage, pre-traversal callback */ if (n->viewPretraverseCallback != NULL) { int rstat; int (*afunc)(const RMnode *, const RMstate *); afunc = n->viewPretraverseCallback; rstat = (*afunc)((const RMnode *)n, (const RMstate *)rState); if (rstat <= 0) return; } /* 8/11/02 wes */ /* if there's a pre traversal callback for the render traversal, tell the render stage to invoke it */ if (n->renderPretraverseCallback != NULL) private_rmDLloadOpcode(MT_PRETRAVERSAL_FUNC, n->compListIndx, mt); /* anything in this node that will cause a draw state change? */ if ((n->scene_parms != NULL) && (n->scene_parms->textProps != NULL)) haveTextProps = 1; else haveTextProps = 0; haveTransforms = (n->transforms != NULL) ? 1 : 0; if ((n->scene_parms != NULL) && ((n->scene_parms->camera3d != NULL) || (n->scene_parms->camera2d != NULL))) haveCameras = 1; else haveCameras = 0; haveOGLStateChange = (private_rmNodeGetAttribMask(n) != 0) ? 1 : 0; #if 0 /* * June 2005 - want to combined OpenGL attrib push/pop along with * OpenRM state push/pop into a single call to help better sync * state tracking. */ if (haveOGLStateChange || haveCameras || haveTransforms || haveTextProps) private_rmDLloadOpcode(MT_PUSH_STATE, n->compListIndx, mt); #endif /* text props? */ if ((n->scene_parms != NULL) && (n->scene_parms->textProps != NULL)) private_rmDLloadOpcode(MT_PUSH_TEXTPROPS, n->compListIndx, mt); /* process transformations */ if (haveTransforms || haveCameras) { int indx; matrixStat = private_rmDLcollectTransformations(p, n, &localState); if (matrixStat & TEXTURE_MATRIX_CHANGED) { indx = private_rmDLnextMatrix(mt); private_rmDLloadMatrix(mt, indx, &(localState.textureMatrix)); private_rmDLloadOpcode(MT_PUSHLOAD_TEXTURE_MATRIX,indx, mt); } if (matrixStat & PROJECTION_MATRIX_CHANGED) { indx = private_rmDLnextMatrix(mt); private_rmDLloadMatrix(mt, indx, &(localState.projection)); private_rmDLloadOpcode(MT_PUSHLOAD_PROJ_MATRIX, indx, mt); } if (matrixStat & MODELVIEW_MATRIX_CHANGED) { indx = private_rmDLnextMatrix(mt); private_rmDLloadMatrix(mt, indx, &(localState.modelView)); private_rmDLloadOpcode(MT_PUSHLOAD_MODELVIEW_MATRIX, indx, mt); } } /* cull modes - not yet implemented */ #if 0 /* compute attribute opcode for this node */ /* * June 2005 - want to combined OpenGL attrib push/pop along with * OpenRM state push/pop into a single call to help better sync * state tracking. */ if ((mask = private_rmNodeGetAttribMask(n)) != 0) private_rmDLloadOpcode(MT_PUSHATTRIB, n->compListIndx, mt); #endif #if 1 /* * push OpenGL attrib and RM state */ /* if (haveOGLStateChange || haveCameras || haveTransforms || haveTextProps) */ if (haveOGLStateChange) private_rmDLloadOpcode(MT_PUSH_STATE_ATTRIB, n->compListIndx, mt); #endif /* fb clear? */ if (n->fbClear != 0) private_rmDLloadOpcode(MT_FBCLEAR, n->compListIndx, mt); /* process switch callback, and/or compute # of children. process children */ descendChildren = (n->nchildren > 0) ? 1 : 0; if (descendChildren != 0) { if (n->viewSwitchCallback != NULL) { int (*afunc)(const RMnode *, const RMstate *); int indx; afunc = n->viewSwitchCallback; indx = (*afunc)((const RMnode *)n, (const RMstate *)&localState); /* range check return value */ if ((indx >= 0) || (indx < n->nchildren)) private_rmViewRecurse(p, n->children[indx], nodeRenderPassFunc, channelTraversalFunc, mt, &localState); else { char buf[256]; sprintf(buf," view traversal error: the switch callback at node <%s> returned a value of %d which is out of range. The valid range is 0 <= n < %d.",n->object_info.name, indx, n->nchildren); rmError(buf); } } else if (n->viewRenderOrderCallback != NULL) /* render order callback */ { int i, nChildrenToRender; int (*appFunc)(const RMnode *, const RMstate *, int *orderIndices, int nChildren); int *childIndices = (int *)malloc(sizeof(int)*n->nchildren); /* default render or is 0..n-1 */ for (i=0;inchildren;i++) childIndices[i] = i; appFunc = n->viewRenderOrderCallback; nChildrenToRender = (*appFunc)((const RMnode *)n, (const RMstate *)&localState, childIndices, n->nchildren); for (i=0;i= n->nchildren)) { char buf[1024]; sprintf(buf,"private_rmViewRecurse() view stage traversal error: the index value returned by the app Render Order callback in position %d is equal to %d, which is out of range. Skipping this child index and processing the rest.",i, childIndices[i]); rmError(buf); } else private_rmViewRecurse(p, n->children[childIndices[i]], nodeRenderPassFunc, channelTraversalFunc, mt, &localState); } } else /* traverse all children */ { for (i=0;inchildren;i++) private_rmViewRecurse(p, n->children[i], nodeRenderPassFunc, channelTraversalFunc, mt, &localState); } } else { /* if no children, any prims or draw-process callbacks? if so, load this node for processing. */ if (n->nprims > 0) private_rmDLloadOpcode(MT_DRAW, n->compListIndx, mt); } /* 8/11/02 wes. */ /* invoke view-stage post traversal callback before attributes are popped */ if (n->viewPosttraverseCallback != NULL) { int (*afunc)(const RMnode *, const RMstate *); int rstat; afunc = n->viewPosttraverseCallback; rstat = (*afunc)((const RMnode *)n, (const RMstate *)rState); /* ignore the return status of the post traversal callback */ } /* 8/11/02 wes */ /* if there's a post traversal callback for the render traversal, tell the render stage to invoke it */ if (n->renderPosttraverseCallback != NULL) private_rmDLloadOpcode(MT_POSTTRAVERSAL_FUNC, n->compListIndx, mt); #if 0 /* if we did a pushattrib, undo it */ if (mask != 0) private_rmDLloadOpcode(MT_POPATTRIB, n->compListIndx, mt); /* private_rmDLloadOpcode(MT_POPATTRIB, -1, mt); */ #endif if (matrixStat != 0) { if (matrixStat & TEXTURE_MATRIX_CHANGED) private_rmDLloadOpcode(MT_POP_TEXTURE_MATRIX, -1, mt); if (matrixStat & PROJECTION_MATRIX_CHANGED) private_rmDLloadOpcode(MT_POP_PROJ_MATRIX, -1, mt); if (matrixStat & MODELVIEW_MATRIX_CHANGED) private_rmDLloadOpcode(MT_POP_MODELVIEW_MATRIX, -1, mt); } /* text props? */ if (haveTextProps != 0) private_rmDLloadOpcode(MT_POP_TEXTPROPS, n->compListIndx, mt); #if 0 if (haveOGLStateChange || haveCameras || haveTransforms || haveTextProps) private_rmDLloadOpcode(MT_POP_STATE, n->compListIndx, mt); #endif /* * pop OpenGL attrib and RM state */ /* if (haveOGLStateChange || haveCameras || haveTransforms || haveTextProps) */ if (haveOGLStateChange) private_rmDLloadOpcode(MT_POP_STATE_ATTRIB, n->compListIndx, mt); } void private_rmView (RMpipe *p, RMnode *n, int frameNumber, RMmatrix *initModelMatrix, RMmatrix *initViewMatrix, RMmatrix *initProjectionMatrix, RMmatrix *initTextureMatrix) { /* for each pass of multipass rendering on RMpipe p, perform view pass operations, preparing results for rendering */ RMstate rState, initState; RMenum channelFormat = rmPipeGetChannelFormat(p); RMmultipassDisplayList *mp=NULL; RMdisplayList *dl=NULL; int dlIndx; RMenum render3DOpaqueEnable; RMenum render3DTransparentEnable; RMenum render2DOpaqueEnable; frameNumber++; /* foil compiler warning */ #if 1 /* 4/3/03 wes - moved this initialization step to rmPipeNew. the result is to remove some initialization from the rendering path, even though the initialization is a one-time event) */ if (p->displayLists == NULL) private_rmPipeDisplayListsNew(p); #endif /* select even or odd multipass display list as a function of framenum */ dlIndx = private_rmSelectEvenOddBuffer(p->frameNumber); private_rmPipeDisplayListsInit(p, dlIndx); mp = (RMmultipassDisplayList *)(p->displayLists); private_rmStateInit(p, &initState, (RMenum) GL_RENDER, initModelMatrix, initViewMatrix, initProjectionMatrix, initTextureMatrix); rState = initState; rmPipeGetRenderPassEnable(p, &render3DOpaqueEnable, &render3DTransparentEnable, &render2DOpaqueEnable); /* RMpipe level fb clear? */ if ((p->fbClearNode != NULL) && (p->fbClearNode->fbClear != NULL)) { if (channelFormat == RM_MONO_CHANNEL) { dl = mp->globals[dlIndx]; private_rmDLloadOpcode(MT_FBCLEAR, p->fbClearNode->compListIndx, dl); } else { dl = mp->globals[dlIndx]; private_rmDLloadOpcode(MT_FBCLEAR, p->fbClearNode->compListIndx, dl); dl = mp->rightGlobals[dlIndx]; private_rmDLloadOpcode(MT_FBCLEAR, p->fbClearNode->compListIndx, dl); } } /* do the 3D, opaque rendering pass */ if (render3DOpaqueEnable == RM_TRUE) { rState.renderpass = RM_RENDERPASS_OPAQUE; if (channelFormat == RM_MONO_CHANNEL) { rState.which_channel = RM_ALL_CHANNELS; dl = mp->opaque3D[dlIndx]; private_rmViewRecurse(p, n, opaque3DFilterFunc, NULL, dl, &rState); } else { rState.which_channel = RM_LEFT_CHANNEL; dl = mp->opaque3D[dlIndx]; private_rmViewRecurse(p, n, opaque3DFilterFunc, leftChannelTraversalFunc, dl, &rState); rState.which_channel = RM_RIGHT_CHANNEL; dl = mp->rightOpaque3D[dlIndx]; private_rmViewRecurse(p, n, opaque3DFilterFunc, rightChannelTraversalFunc, dl, &rState); } } /* do the 3D, transparent rendering pass */ if (render3DTransparentEnable == RM_TRUE) { rState = initState; rState.renderpass = RM_RENDERPASS_TRANSPARENT; if (channelFormat == RM_MONO_CHANNEL) { rState.which_channel = RM_ALL_CHANNELS; dl = mp->transparent3D[dlIndx]; private_rmViewRecurse(p, n, transparent3DFilterFunc, NULL, dl, &rState); } else { rState.which_channel = RM_LEFT_CHANNEL; dl = mp->transparent3D[dlIndx]; private_rmViewRecurse(p, n, transparent3DFilterFunc, leftChannelTraversalFunc, dl, &rState); rState.which_channel = RM_RIGHT_CHANNEL; dl = mp->rightTransparent3D[dlIndx]; private_rmViewRecurse(p, n, transparent3DFilterFunc, rightChannelTraversalFunc, dl, &rState); } } /* do the 2d rendering pass */ if (render2DOpaqueEnable == RM_TRUE) { rState = initState; rState.renderpass = RM_RENDERPASS_OPAQUE; if (channelFormat == RM_MONO_CHANNEL) { rState.which_channel = RM_ALL_CHANNELS; dl = mp->opaque2D[dlIndx]; private_rmViewRecurse(p, n, opaque2DFilterFunc, NULL, dl, &rState); } else { rState.which_channel = RM_LEFT_CHANNEL; dl = mp->opaque2D[dlIndx]; private_rmViewRecurse(p, n, opaque2DFilterFunc, leftChannelTraversalFunc, dl, &rState); rState.which_channel = RM_RIGHT_CHANNEL; dl = mp->rightOpaque2D[dlIndx]; private_rmViewRecurse(p, n, opaque2DFilterFunc, rightChannelTraversalFunc, dl, &rState); } } } /* EOF */