/* * 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: rmpipe.c,v 1.28 2005/09/12 04:03:51 wes Exp $ * Version: $Name: OpenRM-1-6-0-2-RC2 $ * $Revision: 1.28 $ * $Log: rmpipe.c,v $ * Revision 1.28 2005/09/12 04:03:51 wes * Minor documentation updates. * * Revision 1.27 2005/06/26 18:57:03 wes * Added initialization code to rmPipeNew that clears the new attrib mask stack. * * Revision 1.26 2005/06/12 21:18:23 wes * Minor language changes to in-code documentation. * * Revision 1.25 2005/06/06 02:04:29 wes * Lots of small additions to clean up compiler warnings. * * Revision 1.24 2005/05/16 01:02:02 wes * Streamlining rmPipeMakeCurrent * * Revision 1.23 2005/03/16 16:45:47 wes * Minor code changes, documentation update. * * Revision 1.22 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.21 2005/02/19 16:40:20 wes * Distro sync and consolidation. * Repairs to fix memory leak associated with repeated calls to rmPipeNew, * rmPipeMakeCurrent, rmPipeClose. * * Revision 1.20 2005/01/23 17:08:25 wes * Copyright updated to 2005. * Updates to support access and use of extensions; multitexturing on all * platforms, use of 3d texture extension to realize implementation of * volume rendering and related functions on Windows platforms. * * Revision 1.19 2004/09/26 21:47:09 wes * Modified rmPipeMakeCurrent to do more thorough checking before * making the OpenGL context current. In X, you must have a valid * Display, a valid Window and a pre-assigned context before OpenRM * will do a glXMakeCurrent call. * * Also, modified rmPipeClose to zero out the internal variables for * the contextCache and displayLists fields so that you can call rmPipeClose * on an RMpipe more than once and not get an error. * * Revision 1.18 2004/08/07 17:12:59 wes * Updated rmPipeMakeCurrent so that the return status accurately reflects * that both (1) the OpenGL context is active and usable, and (2) that * RM was able to initialize the context cache and component manager. * * Revision 1.17 2004/05/09 22:23:13 wes * Update to rmPipeMakeCurrent to accommodate component mgr, context cache * and quadrics display list construction when the app has specified * NULL as a drawable. The implication here is that they're using their * own OpenGL context. Added a routine to check for the presence of * an active context. The new routine contains arch-specific code. * * Revision 1.16 2004/03/27 16:46:56 wes * Tweak rmPipeMakeCurrent so that glXMakeCurrent is called only if we have * a valid window and context handle under X11 (those checks were already * done for Win32) * * Revision 1.15 2004/02/23 03:03:48 wes * New routine: rmPipeGetFrameNumber(). * * Revision 1.14 2004/01/16 16:57:03 wes * Added time synchronization function which is used to achieve * constant-rate rendering. * * Revision 1.13 2003/12/12 00:34:13 wes * Added documentation for rmPipeSetFrameRate and rmPipeGetFrameRate. * * Revision 1.12 2003/12/06 03:26:06 wes * Documentation tweaks to RMtime routines, updates to RMtime routines for * Windows. * * Revision 1.11 2003/12/01 02:13:05 wes * Additions to support constant frame-rate rendering on both Unix and Win32. * * Revision 1.10 2003/10/03 19:20:47 wes * Use platform-independent interface to access the OpenGL context. * * Revision 1.9 2003/07/23 13:32:28 wes * Win32: problems with offscreen rendering appeared with new context * initialization code sequence (1.5.0). Minor repairs needed to fix the bug. * * Revision 1.8 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.7 2003/04/12 19:48:30 wes * Set the default comm size to 1, default rank to zero. * * Revision 1.6 2003/04/05 14:12:06 wes * Add code to rmPipeClose() that calls routines freeing inter-stage resources. * * Revision 1.5 2003/03/16 21:56:16 wes * Documentation updates. * * Revision 1.4 2003/02/14 00:21:02 wes * *** empty log message *** * * Revision 1.3 2003/02/02 02:07:15 wes * Updated copyright to 2003. * * Revision 1.2 2003/02/01 17:56:15 wes * Win32 code work to reflect new RMpipe initialization sequence. * * Revision 1.1.1.1 2003/01/28 02:15:23 wes * Manual rebuild of rm150 repository. * * Revision 1.19 2003/01/27 05:04:42 wes * Changes to RMpipe API and initialization sequence to unify GLX, WGL and CR * platforms w/o too much disruption to existing apps. * * Revision 1.18 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.17 2003/01/11 18:44:22 wes * Added global control over whether or not display lists are used during * rendering by adding the new RMpipe controls rmPipeSetDisplayListEnable() * and rmPipeGetDisplayListEnable(). * * Revision 1.16 2002/12/31 00:55:22 wes * * Various enhancements to support Chromium - achitecture-specific sections * of RMpipe were cleaned up, etc. * * Revision 1.15 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.14 2002/06/30 21:51:22 wes * Added code by SF member barrett that checks for NULL when deleting * an RMpipe's context cache. * * Revision 1.13 2002/04/30 19:33:05 wes * Updated copyright dates. * * Revision 1.12 2001/10/15 00:11:31 wes * Minor tweak. * * Revision 1.11 2001/10/15 00:10:20 wes * Added code to close the window on when an RMpipe is closed. * * Revision 1.10 2001/07/15 17:17:59 wes * Added code to cleanse the context cache when the RMpipe is either * rmPipeClose()'d or rmPipeDelete()'d. * * Revision 1.9 2001/06/04 00:59:55 wes * Added documentation for RMpipe processing modes. * * Revision 1.8 2001/06/03 20:49:46 wes * Removed "rmPipeGetCurrent", added calls to query current processing * mode to be able to determine if it's an offscreen mode or a * multithreaded mode. * * Revision 1.7 2001/03/31 17:12:39 wes * v1.4.0-alpha-2 checkin. * * Revision 1.6 2000/12/03 22:33:55 wes * Mods for thread-safety. * * Revision 1.5 2000/08/23 23:27:45 wes * Minor edits for readability. * * Revision 1.4 2000/05/14 23:37:11 wes * Added control via RMpipe attribute to how OpenGL matrix stack * is initialized or used during rendering. * * Revision 1.3 2000/04/20 16:29:47 wes * Documentation additions/enhancements, some code rearragement. * * Revision 1.2 2000/02/29 23:43:53 wes * Compile warning cleanups. * * Revision 1.1.1.1 2000/02/28 21:29:40 wes * OpenRM 1.2 Checkin * * Revision 1.1.1.1 2000/02/28 17:18:48 wes * Initial entry - pre-RM120 release, source base for OpenRM 1.2. * */ /* * generic pipe utilities (window system independent) in this file. * specific pipe/window utilities are in rmx.c or rmw.c */ #include #include "rmprivat.h" #include "rmmultis.h" /* stuff for debugging constant-rate rendering */ #define DEBUG_RSYNC 0 #if DEBUG_RSYNC #define MAX_RSYNC_SAMPLES 1000 typedef struct { RMtime t; int frameNumber; } RMtimeSyncStruct; RMtimeSyncStruct timeSyncData[1000]; int timeSyncIndx = 0; #endif static int private_rmCheckForActiveContext(void); static void private_rmPipeGetCapabilities(RMpipeOGLCapabilities *); /* * ---------------------------------------------------- * @Name rmPipeCreateContext @pstart RMenum rmPipeCreateContext (RMpipe *toUse) @pend @astart RMpipe *toUse - a handle to an RMpipe (modified). @aend @dstart This routine will create a platform-specific OpenGL context that honors the display format attributes contained in the RMpipe. Use rmPipeSetChannelFormat() to set such parameters prior to creating the context. Changing the channel format parameters after the context has been created with this routine will have no effect. After the context has been created with this routine, be sure to call rmPipeMakeCurrent() to perform final intialization steps needed to ready the context for use in rendering. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmPipeCreateContext (RMpipe *p) { if (p->createContextFunc != NULL) return(p->createContextFunc(p)); else rmError(" rmPipeCreateContext() error - no context creation function assigned!! Ouch!"); return RM_WHACKED; } /* * ---------------------------------------------------- * @Name rmPipeNew @pstart RMpipe * rmPipeNew (RMenum targetPlatform) @pend @astart RMenum targetPlatform - select the appropriate platform. This parameter must be one of RM_PIPE_GLX, RM_PIPE_WGL or RM_PIPE_CR. @aend @dstart Use this routine to create a new RMpipe object for use with a specified display platform. Upon success, a handle to the new RMpipe object is returned to the caller, or NULL is returned upon failure. The input parameter must be one of RM_PIPE_GLX, RM_PIPE_WGL, RM_PIPE_CR or RM_PIPE_NOPLATFORM. RM_PIPE_GLX specifies use on any X-based system that has the GLX extension (supports OpenGL through the server). RM_PIPE_WGL specifies use on a Win32 platform. RM_PIPE_CR specifies use only on a Chromium-enabled system. RM_PIPE_NOPLATFORM may be be used on any platform. The difference between RM_PIPE_NOPLATFORM and others is that some RMpipe fields are not initialized (swapbuffers function, context creation function,... ). Note that RM_PIPE_GLX and RM_PIPE_WGL platforms can make use of Chromium as well for doing single application to one or more crservers. However, an RM_PIPE_CR platform is highly Chromium-specific. Refer to the RM Programming Guide for more details. During rmPipeNew(), the RMpipe object is initialized with the following platform-specific and platform-neutral settings: 1. The "swapbuffers" function is set to a platform-appropriate value. The application may later override this setting using rmPipeSetSwapBuffersFunc(). By assigning a default swapbuffers function, we are assuming that the OpenGL context (created later) will be double buffered. For the RM_PIPE_NOPLATFORM, no swapbuffers function is assigned. 2. The "context creation function" is set to a platform-appropriate value. The application may indirectly override the context creation function using rmPipeSetContext() to assign an OpenGL context to the RMpipe. Such an assignment must occur between rmPipeNew() and rmPipeMakeCurrent(). 3. The default channel format is RM_MONO_CHANNEL, which corresponds to an OpenGL visual that has RGBA color buffers, a depth buffer, and which is double-buffered. The channel format can be set using rmPipeSetChannelFormat() after rmPipeNew() returns, and before a window is created (or before drawing occurs in the case of RM_PIPE_CR). 4. The default processing mode is RM_PIPE_MULTISTAGE. This mode corresponds to serial rendering using a two-stage pipeline. You can set the processing mode after rmPipeNew() returns (see rmPipeSetProcessingMode), but before rmPipeMakeCurrent(). 5. The "post render barrier function" is set to NULL (see rmPipeSetPostRenderBarrierFunc). 6. The "post render function" is set to NULL (see rmPipeSetPostRenderFunc). 7. Each of the following three passes of the multipass rendering engine is enabled by default: opaque 3D, transparent 3D, opaque 2D. Applications may not change the order of these rendering passes, but may enable or disable a given rendering pass with rmPipeSetRenderPassEnable(). The basic sequence of steps needed to fully initialize RM for rendering are: 1. Use rmPipeNew(yourPlatform) to create the RMpipe object specific for your platform. 2. Set any optional parameters on the RMpipe: processing mode (see rmPipeSetProcessingMode), channel format (see rmPipeSetChannelFormat), XDisplay on X systems (see rmxPipeSetDisplay). X11 note: if you want to display somewhere other than getenv($DISPLAY), you will need to make an explicit call to rmxPipeSetDisplay() using an appropriate XDisplay structure. By default, rmPipeNew(RM_PIPE_GLX) will perform XOpenDisplay(getenv("$DISPLAY")), and assign the resulting XDisplay structure to the RMpipe. An opened X Display is necessary for access to bitmap fonts. 3. Create a window suitable for display. This can be done with either rmauxCreateXWindow() or rmauxCreateW32Window(), or a suitable drawable can be provided by an external source (for more details, see the RM Programming Guide). 4. Assign the window to the RMpipe (see rmPipeSetWindow) 5. Make the pipe current (rmPipeMakeCurrent). @dend * ---------------------------------------------------- */ RMpipe * rmPipeNew (RMenum targetPlatform) { RMpipe *t; extern RMenum RM_DEFAULT_PIPE_DISPLAY_LIST_ENABLE; if ((targetPlatform != RM_PIPE_GLX) && (targetPlatform != RM_PIPE_WGL) && (targetPlatform != RM_PIPE_CR) && (targetPlatform != RM_PIPE_NOPLATFORM)) { rmError("rmPipeNew() error - the input targetPlatform must be one of RM_PIPE_GLX, RM_PIPE_WGL, RM_PIPE_CR, RM_PIPE_NOPLATFORM"); return NULL; } t = (RMpipe *)calloc(1,sizeof(RMpipe)); if (t == NULL) return(NULL); private_rmPipeSetPlatform(t, targetPlatform); rmPipeSetSwapBuffersFunc(t, NULL); #ifdef RM_X if (targetPlatform == RM_PIPE_GLX) { /* 2/5/05 - want ability for selective XOpenDisplay */ /* 5/1/05 - why? wes */ const char *displayName = getenv("DISPLAY"); Display *theDisplay = XOpenDisplay(displayName); rmxPipeSetDisplay(t, theDisplay); rmPipeSetSwapBuffersFunc(t, rmPipeSwapBuffersX11); t->shutdownFunc = private_rmPipeCloseContextX11; t->createContextFunc = private_rmxPipeCreateContext; } #endif #ifdef RM_WIN if (targetPlatform == RM_PIPE_WGL) { rmPipeSetSwapBuffersFunc(t, rmPipeSwapBuffersWin32); t->shutdownFunc = private_rmPipeCloseContextW32; t->createContextFunc = rmwPipeCreateContext; } #endif #ifdef RM_CR if (targetPlatform == RM_PIPE_CR) { #ifdef RM_X /* 2/5/05 - want ability for selective XOpenDisplay */ /* 5/1/05 - why? wes */ /* 5/1/05 - when building the Chromium-enabled version of RM, you should also build with -DRM_X because the RMpipe needs the $DISPLAY to access bitmap fonts. */ const char *displayName = getenv("DISPLAY"); Display *theDisplay = XOpenDisplay(displayName); rmxPipeSetDisplay(t, theDisplay); #endif private_rmPipeInitCR(t); t->shutdownFunc = private_rmPipeCloseContextCR; t->createContextFunc = rmPipeCreateContextCR; t->contextCR = -1; } #endif if (targetPlatform == RM_PIPE_NOPLATFORM) { t->shutdownFunc = NULL; t->createContextFunc = NULL; } rmPipeSetCommSize(t, 1); /* default comm size is 1 */ rmPipeSetRank(t, 0); /* default rank is zero */ rmPipeSetPostRenderBarrierFunc(t, NULL); rmPipeSetPostRenderFunc(t, NULL); rmPipeSetInitMatrixStackMode(t, RM_TRUE); t->channel_format = RM_MONO_CHANNEL; t->processingMode = RM_PIPE_MULTISTAGE; t->offscreen = RM_FALSE; rmPipeSetChannelFormat(t, RM_MONO_CHANNEL); /* enable 3 stage, multipass rendering */ rmPipeSetRenderPassEnable(t, RM_TRUE, RM_TRUE, RM_TRUE); /* set default display list use policy */ rmPipeSetDisplayListEnable(t, RM_DEFAULT_PIPE_DISPLAY_LIST_ENABLE); /* set frame rate to -1. this means constant-rate rendering stuff is disabled by default, and the app must specifically request it. */ rmPipeSetFrameRate(t, -1); private_rmPipeSetTimeSyncFunc(t, NULL); /* initialize the local attrib mask stack */ t->localMaskStackTop = 0; memset((void *)(t->localMaskStack), 0, sizeof(GLuint)*MAX_MASK_STACK_DEPTH); return(t); } /* * ---------------------------------------------------- * @Name rmPipeDelete @pstart RMenum rmPipeDelete (RMpipe *toDelete) @pend @astart RMpipe *toDelete - a handle to an RMpipe to be deleted. @aend @dstart Releases resources associated with an RMpipe object. This is the opposite of rmPipeNew(). Returns RM_WHACKED if the input RMpipe is NULL. @dend * ---------------------------------------------------- */ RMenum rmPipeDelete (RMpipe *p) { if (RM_ASSERT(p, "rmPipeDelete() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); /* * we could potentially do more work, like close the window, close * the display, etc. but for now (Jan 2000) we'll just do the free(). */ rmPipeClose(p); /* new in 1.6.0, free the capabilities struct if present. */ if (p->caps != NULL) { free((void *)p->caps); p->caps = NULL; } free((void *)p); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeMakeCurrent @pstart RMenum rmPipeMakeCurrent (RMpipe *toUse) @pend @astart RMpipe *toUse - a handle to an RMpipe object (input). @aend @dstart Use this routine to make a named RMpipe "active." Making an RMpipe "active" involves three primary processing stages. First, we take steps to either verify that an OpenGL context is current already, or we make current the OpenGL context assigned to the RMpipe. Second, we initialize the RMpipe's context cache. Third, we initialize some OpenGL variables needed by RM for rendering. This routine will return a value of RM_CHILL to indicate that RM and OpenGL are ready for rendering. A value of RM_WHACKED is returned if either of the following is true: (1) RM was unable to initialize an OpenGL rendering context and/or make it current; (2) RM was unable to initialize its internal context cache and component manager. Both conditions (1) and (2) must be satisfied before rendering may occur. A return value of RM_WHACKED means that the RMpipe is not ready for use in rendering. All applications will use this routine to make an RMpipe "active." In the first stage of processing, we first check to see if there is an already "current" OpenGL context. If so, we do not disturb it, but just go ahead and use it. If no OpenGL context is already current, then we will attempt to make current the OpenGL context assigned to the RMpipe. This step requires the presence of a Window or HWND parameter in the X11 and Windows environments, respectively. In the second stage of processing, this routine will check for the existance of an RMpipe "context cache," and will create a new one if one does not exist. If a context cache exists, it will be flushed and replaced with a new one. The context cache is used internally by RM to store retained mode, OpenGL context-specific data, such as display list indices, texture object identifiers, and so forth. The reason for creating/replacing/activating the context cache in this routine, rather than earlier (when the OpenGL context is created) is due to the inconsistencies between X11 and Win32 in how OpenGL is initialized. In X11, the GLX context must be created prior to creating an X11 window (XCreateWindow needs an XVisualInfo structure, which reflects the pixel format chosen with glXChooseVisual). In Win32, the equivalent of an XVisualInfo structure is created using a window handle. In nearly all applications, this routine will be called only once. It is not an error call it multiple times, but developers must be aware that any existing retained mode structures will be flushed by rmPipeMakeCurrent, and rebuilt during subsequent renderings. This routine *must* be called by all applications between rmPipeNew() (RMpipe initialization) and rmFrame() (rendering). @dend * ---------------------------------------------------- */ RMenum rmPipeMakeCurrent (RMpipe *pipe) { RMenum haveActiveContext=RM_FALSE; if (RM_ASSERT(pipe, "rmPipeMakeCurrent() error: the input RMpipe is NULL") == RM_WHACKED) return RM_WHACKED; #ifdef RM_X if (private_rmPipeGetPlatform(pipe) == RM_PIPE_GLX) { /* first, check to see if the assigned context is active. */ if ((haveActiveContext = private_rmCheckForActiveContext()) != RM_TRUE) { /* don't have an active context. See if we have enough to make an active context. */ Window w = rmPipeGetWindow(pipe); GLXContext ctx = rmPipeGetContext(pipe); Display *d = rmxPipeGetDisplay(pipe); if ((w != 0) && (ctx != NULL) && (d != NULL)) { if (glXMakeCurrent(rmxPipeGetDisplay(pipe), rmPipeGetWindow(pipe), rmPipeGetContext(pipe)) == True) haveActiveContext = RM_TRUE; } else { rmWarning("rmPipeMakeCurrent (X11): we don't have an active OpenGL context, and don't have enough variables set in the RMpipe to make an active context. We need: (1) an open Display (see rmxPipeSetDisplay), (2) a window or drawable (see rmPipeSetWindow), and (3) an OpenGL context (see rmPipeSetContext)."); return RM_WHACKED; } } } #endif #ifdef RM_WIN if (private_rmPipeGetPlatform(pipe) == RM_PIPE_WGL) { /* first, check to see if the assigned context is active. */ if ((haveActiveContext = private_rmCheckForActiveContext()) != RM_TRUE) { /* don't have an active context. See if we have enough to make an active context. */ HWND hWnd = rmPipeGetWindow(pipe); HGLRC ctx = rmPipeGetContext(pipe); if ((hWnd != 0) && (ctx != NULL)) { if (wglMakeCurrent (pipe->hdc, ctx) == TRUE) haveActiveContext = RM_TRUE; else { private_rmwCheckAndDisplayLastError("rmPipeMakeCurrent: After wglMakeCurrent"); return RM_WHACKED; } } else { rmWarning("rmPipeMakeCurrent (Win32): we don't have an active OpenGL context, and at least one of an OpenGL context and/or window has not been assigned to the RMpipe."); return RM_WHACKED; } } } #endif #ifdef RM_CR /* special code for making the context current in Chromium */ if (private_rmPipeGetPlatform(pipe) == RM_PIPE_CR) haveActiveContext = rmPipeMakeCurrentCR(pipe); #endif if (private_rmPipeGetPlatform(pipe) == RM_PIPE_NOPLATFORM) { if ((haveActiveContext = private_rmCheckForActiveContext()) != RM_TRUE) { rmError("rmPipeMakeCurrent error -- for your RM_PIPE_NOPLATFORM RMpipe, it does not appear there is a current, active OpenGL context. Therefore, OpenGL is not initialized and ready for use by OpenRM Scene Graph. "); return RM_WHACKED; } } if (haveActiveContext == RM_TRUE) { #ifdef RM_WIN glPixelStorei(GL_PACK_ALIGNMENT, 4); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); /* say's that we're word aligned on each scanline */ #endif #ifdef RM_X glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* say's that we're byte aligned on each scanline */ #endif } /* at this point, wstat == 1 means we have a valid, active OpenGL context. if wstat == 0, OpenGL is not ready to go. */ /* * flush any existing context cache. */ if (pipe->contextCache != NULL && haveActiveContext == RM_TRUE) private_rmCacheFlush(pipe->contextCache); /* * then build a new one. */ if (private_rmCacheInit(&(pipe->contextCache)) != RM_CHILL) { rmError("rmPipeMakeCurrent - unable to initialize the RMpipe context cache. Please file a bug report."); return RM_WHACKED; } /* * load prebuilt display lists for quadrics objects into the * context cache of each pipe. */ if (pipe->contextCache != NULL && haveActiveContext == RM_TRUE) private_rmInitQuadrics(pipe->contextCache); /* * if we have a valide OpenGL context and the component manager/context * cache is ready to go, go ahead and obtain the capabilities for * the RMpipe object. */ if (haveActiveContext == RM_TRUE) { RMpipeOGLCapabilities *caps = (RMpipeOGLCapabilities *)calloc(1,sizeof(RMpipeOGLCapabilities)); private_rmPipeGetCapabilities(caps); pipe->caps = caps; } /* * return status of 1 indicates that we have both initialized the * context cache as well as the OpenGL context, and that RM is ready * to render stuff. A value of 0 means that either one of these two * init operations failed, and that RM cannot render anything. */ return( (haveActiveContext == RM_TRUE) ? RM_CHILL : RM_WHACKED ); } /* * ---------------------------------------------------- * @Name rmPipeClose @pstart RMenum rmPipeClose(RMpipe *toClose) @pend @astart RMpipe *toClose - a handle to an RMpipe (modified). @aend @dstart This routine will destroy the OpenGL rendering context associated with the input RMpipe object, and delete all resources associated with the RMpipe's context cache, including all OpenGL display lists and texture object id's. On X11, this routine will also close the X Display associated with the RMpipe. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmPipeClose (RMpipe *toClose) { /* deletes an OpenGL rendering context */ if (RM_ASSERT(toClose, "rmPipeClose() error: the input RMpipe is NULL.") == RM_WHACKED) return(RM_WHACKED); /* * we want to first shut down any rendering threads so that we can * reattach to the OpenGL context in order to delete display lists, etc. */ /* temp - assume that the rendering stuff is serial, no parallel stuff. TODO: we need to add a call that will close down the rendering threads, if any. this call will return control of the OpenGL rendering context to the caller (us) so that we can do stuff to the OpenGL rendering context. 7/4/01 w.bethel */ if (toClose->contextCache != NULL) { private_rmCacheDelete(toClose, toClose->contextCache); toClose->contextCache = NULL; } if (toClose->shutdownFunc != NULL) toClose->shutdownFunc(toClose); /* * free up resources associated with view-render buffers */ if (toClose->displayLists != NULL) { private_rmPipeDisplayListsDelete(toClose); toClose->displayLists = NULL; } #if DEBUG_RSYNC { FILE *f=fopen("timeSync.txt","w"); int i, n; double d; int thisUn, nextUn; /* need to weave in some code to test for start != 0, which would be caused by wrapping around in the ring buffer */ if (timeSyncIndx >= MAX_RSYNC_SAMPLES) { n = MAX_RSYNC_SAMPLES; thisUn = (timeSyncIndx+1) % MAX_RSYNC_SAMPLES; } else { n = timeSyncIndx; thisUn = 0; } nextUn = (thisUn + 1) % MAX_RSYNC_SAMPLES; for (i=0; iswapBuffersFunc, "rmPipeSwapBuffers() warning: there is no swapbuffers function assigned to the input RMpipe.") == RM_WHACKED) return RM_WHACKED; #endif if (p->swapBuffersFunc != NULL) p->swapBuffersFunc(p); return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeSetChannelFormat @pstart RMenum rmPipeSetChannelFormat (RMpipe *toModify, RMenum channelFormat) @pend @astart RMpipe *toModify - a handle to an RMpipe object (modified). RMenum channelFormat - an RMenum value specifying a display format. May be one of RM_MONO_CHANNEL, RM_REDBLUE_STEREO_CHANNEL, RM_BLUERED_STEREO_CHANNEL, or RM_MBUF_STEREO_CHANNEL. @aend @dstart Use this routine to set the "channel format" of an RMpipe object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The RMpipe channel format serves two purposes. First, when multibuffered stereo is requested, this information must be known prior to creating an OpenGL rendering context. So, the channel format provides hints about the type of OpenGL capabilities that are needed by the application. As such, it is likely that the channel format interface will evolve over time to include more access to additional OpenGL capabilities. Second, when a channel format is specified, internal to rmPipeSetChannelFormat(), an RM rendering callback is assigned to the pipe. The rendering callback knows about multipass rendering as well as how to instruct the underlying rendering engine in the details of managing stereo rendering. This routine must be called between the time you create the RMpipe with rmPipeNew(), and before an OpenGL rendering context is created with rmPipeCreateContext(). It is a logic error to change an RMpipe's channel format after the OpenGL context has been created. @dend * ---------------------------------------------------- */ RMenum rmPipeSetChannelFormat (RMpipe *p, RMenum channel_format) { RMenum rstat = RM_CHILL; if (RM_ASSERT(p, "rmPipeSetChannelFormat() error: the input RMpipe pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); switch (channel_format) { case RM_MONO_CHANNEL: case RM_REDBLUE_STEREO_CHANNEL: case RM_BLUERED_STEREO_CHANNEL: case RM_MBUF_STEREO_CHANNEL: p->channel_format = channel_format; rstat = RM_CHILL; break; case RM_OFFSCREEN_MONO_CHANNEL: case RM_OFFSCREEN_REDBLUE_STEREO_CHANNEL: case RM_OFFSCREEN_BLUERED_STEREO_CHANNEL: p->offscreen = RM_TRUE; p->channel_format = channel_format; rmPipeSetSwapBuffersFunc(p, NULL); /* turn off swapbuffers when rendering offscreen */ rstat = RM_CHILL; break; default: rmError("rmPipeSetChannelFormat() error: the input channel format is not recognized."); return (RM_WHACKED); } if (rstat == RM_CHILL) private_rmSetChannelRenderFunc(p); return(rstat); } /* * ---------------------------------------------------- * @Name rmPipeGetChannelFormat @pstart RMenum rmPipeGetChannelFormat (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (input). @aend @dstart Returns to the caller the current channel format of an RMpipe object. A successful return value will be one of RM_MONO_CHANNEL, RM_REDBLUE_STEREO_CHANNEL, RM_BLUERED_STEREO_CHANNEL or RM_MBUF_STEREO_CHANNEL. A return value of RM_WHACKED indicates an error of some type. @dend * ---------------------------------------------------- */ RMenum rmPipeGetChannelFormat (const RMpipe *p) { if (RM_ASSERT(p, "rmPipeGetChannelFormat() error: the input RMpipe pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); return(p->channel_format); } /* * ---------------------------------------------------- * @Name rmPipeSetInitMatrixStackMode @pstart RMenum rmPipeSetInitMatrixStackMode(RMpipe *toModify, RMenum newMode) @pend @astart RMpipe *toModify - a handle to an RMpipe object (modified). RMenum newMode - an RMenum value, either RM_TRUE or RM_FALSE, that controls how matrix transformations are applied to the OpenGL matrix stack. @aend @dstart By default, OpenRM will initialize the OpenGL matrix stack during a frame rendering operation by setting GL_MODELVIEW, GL_PROJECTION and GL_TEXTURE matrices to the Identity matrix. This behavior may be overridden, so that OpenRM will honor any existing matrices in the matrix stacks while accumulating nested transformations. This is helpful in some deployment environments, such as combining OpenRM with CAVElib (www.vrco.com). When the input enumerator is set to RM_TRUE (the default), OpenRM will initialize the OpenGL matrix stack to the identity matrix prior to accumulating transformations during rendering. When the input enum is set to RM_FALSE, OpenRM will NOT initialize the matrix stack, and any existing transformations within the scene graph will be accumulated with the contents of the matrix stack at render time. More precisely, if the GL_MODELVIEW matrix stack has the matrix M, and the scene graph defines a matrix transformation S, the combined transformation will be S*M (left multiplication), so that the scene graph transformation S is effectively applied prior to the outer transformation M when vertex data moves down the OpenGL transformation pipeline. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmPipeSetInitMatrixStackMode(RMpipe *t, RMenum newMode) { if (RM_ASSERT(t,"rmPipeSetInitMatrixStackMode() error: the input RMpipe is NULL. ") == RM_WHACKED) return(RM_WHACKED); if ((newMode != RM_TRUE) && (newMode != RM_FALSE)) { rmError("rmPipeSetInitMatrixStackMode() error: the input newMode RMenum is neither RM_TRUE nor RM_FALSE"); return(RM_WHACKED); } t->initMatrixStack = newMode; /* RM_TRUE or RM_FALSE */ return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetInitMatrixStackMode @pstart RMenum rmPipeGetInitMatrixStackMode(const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (queried). @aend @dstart This routine will return the "init matrix stack" attribute of an RMpipe object. A return value of RM_TRUE means the OpenGL matrix stack will be initialized by OpenRM during frame rendering, while a value of RM_FALSE means that any existing values in the OpenGL matrix stack will be honored during rendering. A return value of RM_WHACKED indicates an error condition. @dend * ---------------------------------------------------- */ RMenum rmPipeGetInitMatrixStackMode(const RMpipe *t) { if (RM_ASSERT(t,"rmPipeGetInitMatrixStackMode() error: the input RMpipe is NULL. ") == RM_WHACKED) return(RM_WHACKED); return(t->initMatrixStack); } /* * ---------------------------------------------------- * @Name rmPipeSetRenderPassEnable @pstart RMenum rmPipeSetRenderPassEnable(RMpipe *toModify, RMenum opaque3DEnable, RMenum transparent3DEnable, RMenum opaque2DEnable) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). RMenum opaque3DEnable,transparent3DEnable, opaque2DEnable - RMenum values, may be either RM_TRUE or RM_FALSE. @aend @dstart This routine is used to selectely enable or disable one of the rendering passes of the RM multipass rendering engine. Applications may not change the order of the rendering passes, but may selectively enable or disable a given pass. The order of the passes is: 1. 3D Opaque 2. 3D Transparent 3. 2D Opaque (There may be a 2D transparent pass in a later release.) During each of these passes, traversal filters are applied at each node of the RM scene graph. If, at any stage during the traversal, the scene graph node does not pass the traversal filter test, that node, and any descendents, are not processed. For this reason, developers should carefully consider scene graph design such that 3D opaque, 3D transparent and 2D objects are appropriately partitioned within the scene graph itself. Background scene operations (background clear color, background image tile, background depth value, background depth image) are performed during rendering passes 1 and 3. A common RM error is to place a background scene operation at a node that is processed during multiple rendering passes. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmPipeSetRenderPassEnable (RMpipe *t, RMenum opaque3DEnable, RMenum transparent3DEnable, RMenum opaque2DEnable) { if (RM_ASSERT(t, "rmPipeSetRenderPassEnable() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); t->opaque3DEnable = opaque3DEnable; t->transparent3DEnable = transparent3DEnable; t->opaque2DEnable = opaque2DEnable; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetRenderPassEnable @pstart RMenum rmPipeGetRenderPassEnable(const RMpipe *t, RMenum *opaque3DEnableReturn, RMenum *transparent3DEnableReturn, RMenum *opaque2DEnableReturn) @pend @astart const RMpipe *t - a handle to an RMpipe (input). RMenum *opaque3DEnableReturn, *transparent3DEnableReturn, *opaque2DEnableReturn - handles to RMenum values (result). @aend @dstart This routine is used to obtain the boolean values that indicate if a particular rendering pass is enabled in the RMpipe. Each of the return parameters is optional - a value of NULL will skip reporting of that particular rendering pass. Upon success, RM_CHILL is returned, and RM_TRUE or RM_FALSE is copied into non-NULL caller-supplied memory for each of the RMpipe rendering passes. Otherwise, RM_WHACKED is returned, and caller-supplied memory remains unmodified. @dend * ---------------------------------------------------- */ RMenum rmPipeGetRenderPassEnable (const RMpipe *t, RMenum *opaque3DEnableReturn, RMenum *transparent3DEnableReturn, RMenum *opaque2DEnableReturn) { if (RM_ASSERT(t, "rmPipeSetGenderPassEnable() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); if (opaque3DEnableReturn != NULL) *opaque3DEnableReturn = t->opaque3DEnable; if (transparent3DEnableReturn != NULL) *transparent3DEnableReturn = t->transparent3DEnable; if (opaque2DEnableReturn != NULL) *opaque2DEnableReturn = t->opaque2DEnable; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetWindowSize @pstart RMenum rmPipeSetWindowSize (RMpipe *toModify, int newWidth, int newHeight) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). int newWidth, newHeight - integer values specifing the pixel width and height of the window associated with an RMpipe (input). @aend @dstart This routine sets the RMpipe's notion of pixel width and height of it's associated display window. RM_CHILL is returned upon success, or RM_WHACKED upon failure. The most typical use of this routine will be when an application detects, through an event loop, that the window geometry has changed. The application is reponsible for notifying RM of such changes; RM doesn't manage events and doesn't keep track of the size of the display window. This routine is called by rmPipeSetWindow(). @dend * ---------------------------------------------------- */ RMenum rmPipeSetWindowSize (RMpipe *pipe, int width, int height) { if (RM_ASSERT(pipe, "rmPipeSetWindowSize() error: the input RMpipe object is NULL.") == RM_WHACKED) return(RM_WHACKED); pipe->xwindow_width = width; pipe->xwindow_height = height; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetWindowSize @pstart RMenum rmPipeGetWindowSize (const RMpipe *toQuery, int *widthReturn, int *heightReturn) @pend @astart const RMpipe *toQuery - a handle to an RMpipe (input). int *widthReturn, *heightReturn - pointers to integers (return). Values of NULL are acceptable. @aend @dstart This routine returns via caller-supplied memory the named RMpipe's notion of the current window width and height. RM_CHILL is returned upon success, or RM_WHACKED upon failure. Callers interested in only width or height may specify NULL for the parameter for which information is not requested. @dend * ---------------------------------------------------- */ RMenum rmPipeGetWindowSize (const RMpipe *pipe, int *width, int *height) { if (RM_ASSERT(pipe, "rmPipeGetWindowSize() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); if (width != NULL) *width = pipe->xwindow_width; if (height != NULL) *height = pipe->xwindow_height; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetSwapBuffersFunc @pstart RMenum rmPipeSetSwapBuffersFunc (RMpipe *toModify, void (newFunc)(RMpipe *)) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). void (newFunc)(RMpipe *) - a handle to an application callback (input). @aend @dstart Use this routine to set the "swapbuffers" callback function associated with an RMpipe object. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The swapbuffers function is invoked after rendering has completed, and after the post-render barrier function, and the post-render framebuffer and depthbuffer grab callbacks, if any. By default, rmSwapBuffers() is assigned to all RMpipe objects' swapbuffers callback functions at the time the RMpipe is created with RMpipe new. The function rmSwapBuffers() calls the native window-system procedure that causes front and back buffers to be swapped. If the application overrides the default swapbuffers function, the application callback will be provided a single input parameter: a handle to an RMpipe object. The RMpipe object contains enough information to enable application code to do the swapbuffers call. @dend * ---------------------------------------------------- */ RMenum rmPipeSetSwapBuffersFunc (RMpipe *p, RMenum (*sbfunc)(const RMpipe *)) { #if 0 if ((RM_ASSERT(p, "rmPipeSetSwapBuffersFunc() error: the input RMpipe is NULL") == RM_WHACKED) || (RM_ASSERT((void *)sbfunc, "rmPipeSetSwapBuffersFunc() error: the input function handle is NULL") == RM_WHACKED)) return(RM_WHACKED); #endif /* cave change: allow for setting a NULL swapbuff func */ if (RM_ASSERT(p, "rmPipeSetSwapBuffersFunc() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); p->swapBuffersFunc = sbfunc; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetPostRenderFunc @pstart RMenum rmPipeSetPostRenderFunc (RMpipe *toModify, void (*postRenderFunc)(const RMimage *, RMenum)) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). void (*postRenderFunc)(RMimage *, RMenum) - a handle to an application callback. @aend @dstart This routine assigns a "post render" application callback to the RMpipe. The post render callback is invoked after rendering has occured, and after the "post render barrier function" has been called (if any), but before the "post render depthbuffer function" (if any) and before the swapbuffers function. RM_CHILL is returned upon success, or RM_WHACKED upon failure. Use a value of NULL for the postRenderFunc to effectively disable post rendering callbacks. The purpose of the post render function is to allow applications to grab a copy of the color planes of the framebuffer after rendering has occured. If you want the raw image data produced by the rendering, this is the path to use. When the application callback is invoked, the callback is invoked with two parameters. The first is a handle to an RMimage object. Use rmImageGetPixelData() to obtain the raw pixel data from this image. At this time (Jan 2000) the image provided to the app callback is in GL_RGBA, GL_UNSIGNED_BYTE format. Applications are advised to use the rmImageGet*() series of routines to obtain RMimage configuration information. In particular, pay close attention to scanline-padding issues. The second parameter provided to the application callback is an RMenum value that is one of RM_ALL_CHANNELS, RM_LEFT_CHANNEL or RM_RIGHT_CHANNEL. Multibuffered stereo channels will invoke the application callback twice, once for each channel. Anaglyph stereo formats (RM_REDBLUE_STEREO_CHANNEL and RM_BLUERED_STEREO_CHANNEL) will trigger the callback just once. This may change in the future (Jan 2000). The RMimage object provided to applications is managed by RM. @dend * ---------------------------------------------------- */ RMenum rmPipeSetPostRenderFunc (RMpipe *p, void (*prfunc)(const RMimage *, RMenum)) { if (RM_ASSERT(p, "rmPipeSetPostRenderFunc() error: the input RMpipe object is NULL") == RM_WHACKED) return(RM_WHACKED); p->postrenderfunc = prfunc; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetPostRenderDepthFunc @pstart RMenum rmPipeSetPostRenderDepthFunc (RMpipe *toModify, void (*postRenderDepthFunc)(const RMimage *, RMenum)) @pend @astart RMpipe *toModify - a handle to an RMimage object (modified). void (*postRenderDepthFunc)(RMimage *, RMenum) - a handle to an application callback (input). @aend @dstart Use this routine to assign a "post render depth buffer" callback to an RMpipe object. RM_CHILL is returned upon success, or RM_WHACKED upon failure. Use a value of NULL for the postRenderDepthFunc to effectively disable this post rendering callback. Whereas the "post render callback" gives applications access to the color planes of the framebuffer after rendering, the purpose of this callback is to give applications access to the depth buffer after rendering has completed. Assigning a "post render depth buffer callback" will cause the application callback to be invoked after rendering, and after the post-render barrier function, after the post render callback (the color planes) but before the swapbuffers function. The pixel data of the RMimage object provided to the application, the first application callback parameter, contains the contents of the depth buffer of the scene just rendered. The pixel data in the RMimage object is in RM_FLOAT format. Applications should use the rmImageGet*() series of routines to query specific RMimage attributes. The second parameter provided to the application callback is an RMenum value that is one of RM_ALL_CHANNELS, RM_LEFT_CHANNEL or RM_RIGHT_CHANNEL. Multibuffered stereo channels will invoke the application callback twice, once for each channel. Anaglyph stereo formats (RM_REDBLUE_STEREO_CHANNEL and RM_BLUERED_STEREO_CHANNEL) will trigger the callback just once. This may change in the future (Jan 2000). The RMimage object provided to applications is managed by RM. @dend * ---------------------------------------------------- */ RMenum rmPipeSetPostRenderDepthFunc (RMpipe *p, void (*prfunc)(const RMimage *, RMenum)) { if (RM_ASSERT(p, "rmPipeSetPostRenderDepthFunc() error: the input RMpipe object is NULL") == RM_WHACKED) return(RM_WHACKED); p->postrender_depthbufferfunc = prfunc; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetPostRenderBarrierFunc @pstart RMenum rmPipeSetPostRenderBarrierFunc (RMpipe *toModify, void (*barrierFunc)(RMpipe *)) @pend @astart RMpipe *toModify - a handle to an RMpipe object (modified). void (*barrierFunc)(RMpipe *) - a handle to an application callback (input). @aend @dstart Use this routine to set the "post render barrier function" on an RMpipe object. Use a value of NULL for barrierFunc to disable this callback, or remove a previously installed callback. Returns RM_CHILL upon success, or RM_WHACKED upon failure. The "post render barrier" callback, if present, will be invoked immediately after rendering, but prior to any other callbacks (post render color planes callback, post render depth buffer callback or swapbuffers). This routine is intended for use by parallel rendering applications. The application callback will be provided a single parameter, an RMpipe. @dend * ---------------------------------------------------- */ RMenum rmPipeSetPostRenderBarrierFunc (RMpipe *t, void (*barrierFunc)(RMpipe *)) { if (RM_ASSERT(t, "rmPipeSetPostRenderBarrierFunc error: the input RMpipe * is NULL.") == RM_WHACKED) return(RM_WHACKED); t->postRenderBarrierFunc = barrierFunc; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeProcessingModeIsMultithreaded @pstart RMenum rmPipeProcessingModeIsMultithreaded (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe (input). @aend @dstart This routine can be used to determine if the processing mode of the RMpipe toQuery is a multithreaded format. If toQuery's processing mode is either RM_PIPE_MULTISTAGE_VIEW_PARALLEL or RM_PIPE_MULTISTAGE_PARALLEL, this routine will return RM_TRUE. Otherwise, RM_FALSE is returned. See also rmPipeGetProcessingMode(). @dend * ---------------------------------------------------- */ RMenum rmPipeProcessingModeIsMultithreaded(const RMpipe *toQuery) { RMenum pmode = rmPipeGetProcessingMode(toQuery); if ((pmode == RM_PIPE_MULTISTAGE_VIEW_PARALLEL) || (pmode == RM_PIPE_MULTISTAGE_PARALLEL)) return(RM_TRUE); else return(RM_FALSE); } /* * ---------------------------------------------------- * @Name rmPipeGetProcessingMode @pstart RMenum rmPipeGetProcessingMode (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (input). @aend @dstart This routine will return the processing mode of the RMpipe toQuery. Upon success, one of the following is returned to the caller: RM_PIPE_SERIAL, RM_PIPE_MULTISTAGE, RM_PIPE_MULTISTAGE_PARALLEL or RM_PIPE_MULTISTAGE_VIEW_PARALLEL. Upon failure, RM_WHACKED is returned to the caller. @dend * ---------------------------------------------------- */ RMenum rmPipeGetProcessingMode(const RMpipe *toQuery) { if (RM_ASSERT(toQuery,"rmPipeGetProcessingMode() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); return(toQuery->processingMode); } /* * ---------------------------------------------------- * @Name rmPipeSetProcessingMode @pstart RMenum rmPipeSetProcessingMode (RMpipe *toModify, RMenum newMode) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). RMenum newMode - a RMenum specifying a processing mode. This value must be one of: RM_PIPE_SERIAL, RM_PIPE_MULTISTAGE, RM_PIPE_MULTISTAGE_PARALLEL or RM_PIPE_MULTISTAGE_VIEW_PARALLEL. @aend @dstart This routine will select the "processing mode" for an RMpipe object. The processing mode assigns a rendering engine to the RMpipe for use in subsequent rendering operations. Upon success, RM_CHILL is returned to the caller; upon failure, RM_WHACKED is returned. Applications may set the processing mode on an RMpipe any time between when it is created (using either rmPipeNew or rmPipeInit) and the first time a frame is rendered. As of this time (June 2001), it is not possible to change the processing mode of an RMpipe, and this routine will not detect this error condition. Your application will likely crash if you attempt to alter the processing mode of an RMpipe after the first frame has been drawn. OpenRM uses a two-stage rendering pipeline: a view traversal of the scene graph prepares a list of things to be drawn during a rendering traversal. The following processing modes are supported: RM_PIPE_MULTISTAGE - uses a two-stage rendering traversal (view, render), and both stages are called sequentially within the same process/thread as the caller. There is no parallelization in this processing mode. RM_PIPE_MULTISTAGE_PARALLEL - each of the two rendering stages are called from detached Posix threads. The render thread will assume ownership of the OpenGL context (applications should NOT make any OpenGL calls except from within node callbacks invoked by the OpenRM renderer). This mode is fully parallelized. RM_PIPE_MULTISTAGE_VIEW_PARALLEL - the view traversal is assigned to a detached thread, while the render traversal remains in the same execution process/thread as the caller (and does not attempt to exert ownership of the OpenGL rendering context). This mode is also fully parallelized, and is quite useful when combining OpenRM with other toolkits that provide device and event management and which make assumptions about ownership of the OpenGL rendering context (e.g., CAVELibrary, VRJuggler, FLTK, etc.). @dend * ---------------------------------------------------- */ RMenum rmPipeSetProcessingMode(RMpipe *toModify, RMenum newMode) { if (RM_ASSERT(toModify,"rmPipeSetProcessingMode() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); if ((newMode != RM_PIPE_SERIAL) && (newMode != RM_PIPE_MULTISTAGE) && (newMode != RM_PIPE_MULTISTAGE_PARALLEL) && (newMode != RM_PIPE_MULTISTAGE_VIEW_PARALLEL)) { rmError("rmPipeSetProcessingMode() error: the input processing mode is not valid."); return(RM_WHACKED); } #ifdef _NO_PTHREADS /* * if we're not using or don't have access to pthreads, permit use of * only one of the single-threaded processing modes. */ if ((newMode == RM_PIPE_MULTISTAGE_PARALLEL) || (newMode == RM_PIPE_MULTISTAGE_VIEW_PARALLEL)) { rmWarning("You are using a version of RM Scene Graph that was built without threads support and have requested an RMpipe processing mode that requires threading. The RMpipe processing mode you will be using instead is RM_PIPE_MULTISTAGE."); newMode = RM_PIPE_MULTISTAGE; } #endif toModify->processingMode = newMode; /* need to update the renderfunc based on new processing mode */ rmPipeSetChannelFormat(toModify, rmPipeGetChannelFormat(toModify)); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetDisplayListEnable @pstart RMenum rmPipeGetDisplayListEnable (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (input). @aend @dstart This routine will return the RMpipe's notion of whether or not it will use OpenGL display lists when rendering primitives. The returned value will be either RM_TRUE, which indicates that display lists will be used when rendering primitives on the RMpipe, or RM_FALSE, which indicates that no RMprimitives will be drawn using display lists. See also: rmPipeSetDisplayListEnable, rmPrimitiveSetDisplayListEnable. @dend * ---------------------------------------------------- */ RMenum rmPipeGetDisplayListEnable(const RMpipe *toQuery) { if (RM_ASSERT(toQuery,"rmPipeGetDisplayListEnable() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); return(toQuery->displayListEnableBool); } /* * ---------------------------------------------------- * @Name rmPipeSetDisplayListEnable @pstart RMenum rmPipeSetDisplayListEnable (RMpipe *toModify, RMenum newMode) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). RMenum newMode - a RMenum specifying whether or not RMprimitives rendered on the RMpipe toModify will use display lists. The input value should be either RM_TRUE or RM_FALSE. @aend @dstart Set's the RMpipe's policy on the use of display lists. When the value of newMode is set to RM_TRUE, use of display lists on the RMpipe toModify is enabled. When set to RM_FALSE, use of display lists on the RMpipe is disabled. The default policy for use of display lists is RM_TRUE, and this value is set at the time the RMpipe is created. You can override the default behavior by calling rmPipeSetDisplayListEnable() with a value of RM_FALSE. Use of display lists can greatly accelerate rendering performance on many platforms. By default, RM will attempt to create display lists for RMprimitives during rendering, and reuse them in subsequent renderings. Applications can control use of display lists in two ways: at the RMpipe level, and at the RMprimitive level. At the RMpipe level, you can enable or disable use of display lists for all RMprimitives drawn on RMpipe using the routine rmPipeSetDisplayListEnable. At the RMprimitive level, you can enable or disable the use of display lists for a single primitive using rmPrimitiveSetDisplayListEnable(). The RMprimitive display list policy does not override the display list policy set at the RMpipe level. In other words, if the policy at the RMpipe level is set to RM_FALSE, then no display lists will be used, even if the policy at the RMprimitive level is set to RM_TRUE. On the other hand, if the policy at the RMpipe level is set to RM_TRUE, a policy at the RMprimitive level of RM_FALSE will result on no display lists being used for the one RMprimitive. In order for display lists to be used at any given RMprimitive, the logical AND of RMpipe and RMprimitive display list policies must be RM_TRUE. To obtain the current display list use policy at an RMpipe, use the routine rmPipeGetDisplayListEnable(). See also rmPrimitiveSetDisplayListEnable(). @dend * ---------------------------------------------------- */ RMenum rmPipeSetDisplayListEnable(RMpipe *toModify, RMenum newMode) { if (RM_ASSERT(toModify,"rmPipeSetDisplayListEnable() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); if ((newMode != RM_TRUE) && (newMode != RM_FALSE)) { rmError("rmPipeSetDisplayListEnable() error: the new display list use policy mode is not valid."); return(RM_WHACKED); } toModify->displayListEnableBool = newMode; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetCommSize @pstart RMenum rmPipeSetCommSize (RMpipe *toModify) @pend @astart RMpipe *toModify - a handle to an RMpipe object (modified). @aend @dstart Sets the global number of PEs in an MPI-parallel application. Docs need to be better written. @dend * ---------------------------------------------------- */ RMenum rmPipeSetCommSize (RMpipe *toModify, int globalNPE) { if (RM_ASSERT(toModify,"rmPipeSetCommSize() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->globalNPE = globalNPE; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeGetCommSize @pstart int rmPipeSetCommSize (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (input). @aend @dstart Returns the global number of PEs in an MPI-parallel application. Docs need to be better written. @dend * ---------------------------------------------------- */ int rmPipeGetCommSize (const RMpipe *toQuery) { if (RM_ASSERT(toQuery,"rmPipeGetCommSize() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); return toQuery->globalNPE; } /* * ---------------------------------------------------- * @Name rmPipeSetRank @pstart RMenum rmPipeSetRank (RMpipe *toModify) @pend @astart RMpipe *toModify - a handle to an RMpipe object (modified). @aend @dstart Sets the rank value of one RMpipe in an MPI-parallel application. Docs need to be better written. @dend * ---------------------------------------------------- */ RMenum rmPipeSetRank (RMpipe *toModify, int myRank) { if (RM_ASSERT(toModify,"rmPipeSetMyRank() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); toModify->myRank = myRank; return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeGetRank @pstart int rmPipeGetRank (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (input). @aend @dstart Obtains the rank value of one RMpipe in an MPI-parallel application. Docs need to be better written. @dend * ---------------------------------------------------- */ int rmPipeGetRank (const RMpipe *toQuery) { if (RM_ASSERT(toQuery,"rmPipeGetMyRank() error: the input RMpipe is NULL") == RM_WHACKED) return(RM_WHACKED); return toQuery->myRank; } #if 0 /* * ---------------------------------------------------- * @Name rmPipeGetDisplayName @pstart const char * rmPipeGetDisplayName (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object (input). @aend @dstart Returns to the caller a character string defining $DISPLAY, the display name. $DISPLAY has an effect only on GL_PIPE_GLX platforms. @dend * ---------------------------------------------------- */ const char * rmPipeGetDisplayName(const RMpipe *toQuery) { if (RM_ASSERT(toQuery,"rmPipeGetDisplayName() error: the input RMpipe is NULL") == RM_WHACKED) return NULL; return toQuery->displayName; } /* * ---------------------------------------------------- * @Name rmPipeSetDisplayName @pstart RMenum rmPipeSetDisplayName (RMpipe *toModify, const char *displayName) @pend @astart RMpipe *toModify - a handle to an RMpipe object (modified). const char *displayName - character string defining name of the $DISPLAY to use in subsequent window system operations. @aend @dstart Use this routine to set the $DISPLAY variable in an RMpipe. This variable is used only on RM_PIPE_GLX platforms, and only when a Returns to the caller a character string defining $DISPLAY, the display name. $DISPLAY has an effect only on GL_PIPE_GLX platforms. @dend * ---------------------------------------------------- */ RMenum rmPipeSetDisplayName(RMpipe *toModify, const char *displayName) { if (RM_ASSERT(toModify,"rmPipeSetDisplayName() error: the input RMpipe is NULL") == RM_WHACKED) return RM_WHACKED; if (toQuery->displayName != NULL) { free((void *)(toQuery->displayName)); toQuery->displayName = NULL; } if ((displayName != NULL) && (displayName[0] != '\0')) toQuery->displayName = strdup(displayName); return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeGetDisplay @pstart void * rmPipeGetDisplay (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - a handle to an RMpipe object that will be queried (input). @aend @dstart Returns to the caller the X Display handle, cast to void *, associated with an RMpipe. @dend * ---------------------------------------------------- */ const void * rmPipeGetDisplay (const RMpipe *toQuery) { } RMenum rmPipeSetDisplay (RMpipe *toModify, void *XDisplayStruct) { } #endif /* PRIVATE */ void private_rmPipeCopy (RMpipe *source_pipe, RMpipe *dest_pipe) { memcpy((void *)dest_pipe, (void *)source_pipe, sizeof(RMpipe)); } /* PRIVATE */ int private_rmComparePipes (RMpipe *p1, RMpipe *p2) { /* returns 1 if pipes are identical, 0 otherwise */ RM_ASSERT(p1, "NULL pipe (1) for comparison."); RM_ASSERT(p2, "NULL pipe (2) for comparison."); #ifdef RM_X /* do we actually need to compare the xcolormaps? */ if ((p1->xdisplay == p2->xdisplay) && (p1->xvisual == p2->xvisual) && (p1->xdrawable == p2->xdrawable) && (p1->glxcontext == p2->glxcontext) && (p1->xcolormap == p2->xcolormap) && (p1->channel_format == p2->channel_format) && (p1->xwindow_width == p2->xwindow_width) && (p1->xwindow_height == p2->xwindow_height)) return(1); else return(0); #endif #ifdef RM_WIN if ((p1->hwnd == p2->hwnd)) return(1); else return(0); #endif } /* PRIVATE */ RMenum private_rmPipeIsOffscreenFormat(const RMpipe *p) { RMenum format = rmPipeGetChannelFormat(p); if ((format == RM_OFFSCREEN_MONO_CHANNEL) || (format == RM_OFFSCREEN_REDBLUE_STEREO_CHANNEL) || (format == RM_OFFSCREEN_BLUERED_STEREO_CHANNEL)) return RM_TRUE; else return RM_FALSE; } /* constant frame rate stuff */ /* * ---------------------------------------------------- * @Name rmPipeSetFrameRate @pstart RMenum rmPipeSetFrameRate (RMpipe *toModify, int newFPS) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). int newFPS - an integer value indicating the target frames-per-second renderering rate when rendering on the RMpipe toModify. @aend @dstart This routine is used to specify the desired frame rate when rendering on the RMpipe toModify. The caller provides an integer value, newFPS, that indicates the desired number of frames per second. A value of 30 will result in 30 frames per second being rendered, etc. A value of -1 will disable constant-rate rendering: OpenRM will not "watch the clock" if you specify a value of -1 frames per second as the desired target rate. The value for newFPS should be a positive integer. Note that not all values of newFPS make sense on all platforms for reasons related to how OpenGL is implemented on your machine. Constant-rate rendering is really a misnomer : what the contstant-rate rendering capability in OpenRM really does is to use a high precision timer and sleep function to ensure that *no more than a given number of frames per second are rendered*. In this way, you can be assured that your application will not be rendering more than newFPS per second. If your application produces a heavy graphics load, the actual rendering rate may fall well short of newFPS frames per second. Rather than calling this capability "constant frame rate" rendering, it is more accurately called "bounded frame rate" rendering. Future work in this area will result in the ability for the application to query the rendering "load" or "stress" values. This way, you'll be able to obtain a quantitative value that indicates how far "over budget" your rendering is, and take action accordingly. One remedial action will be for the application to use switch nodes and render lower-resolution models as a way to reduce graphics rendering load. From the newFPS parameter, a value of milliseconds-per-frame is computed. When your application calls rmFrame() for the first time, OpenRM makes note of the current time. It then proceeds with rendering without delay, and then returns control to the application after rendering is complete. The second time your application calls rmFrame(), OpenRM will delay rendering so that the time taken by rendering during the previous frame along with the time taken by the application for processing inbetween frames does not exceed the occur before the milliseconds-per-frame amount of time has passed. In other words, the period of timing is from the start of rendering to the start of rendering. The bounded-frame-rendering capability works on both Unix/Linux and Win32 platforms subject to caveats that are described in the OpenRM Programming Guide. Additional caveats not specific to a platform are also discussed in the OpenRM Progamming Guide. Use the routine rmPipeGetFrameRate() to query the frames-per-second parameter of an RMpipe. Returns RM_CHILL upon success, or RM_WHACKED upon failure. @dend * ---------------------------------------------------- */ RMenum rmPipeSetFrameRate(RMpipe *p, int newFPS) { if (RM_ASSERT(p, "rmPipeSetFrameRate() error: the input RMpipe is NULL") == RM_WHACKED) return RM_WHACKED; /* do a sanity check on the new FPS value: want either -1 or a positive integer value */ if ((newFPS == 0) || (newFPS < -1)) { rmError("rmPipeSetFrameRate() error: the input newFramesPerSecond value must be either a positive integer indicating frame per second, or a value of -1 to disable constant-rate rendering. "); return RM_WHACKED; } p->targetFrameRate = newFPS; rmTimeSet(&(p->timePerFrame), 0, 0); rmTimeCurrent(&(p->lastTimeStart)); /* compute and actual RMtime version of newFPS. */ if (newFPS > 0) { double msPerFrame = 1000.0/(double)newFPS; long usPerFrame = (long)(msPerFrame * 1000.0); rmTimeSet(&(p->timePerFrame), 0, usPerFrame); p->timePerFrameMS = msPerFrame; private_rmPipeSetTimeSyncFunc(p, private_rmPipeTimeSyncFunc); } else private_rmPipeSetTimeSyncFunc(p, NULL); return RM_CHILL; } /* * ---------------------------------------------------- * @Name rmPipeGetFrameRate @pstart int rmPipeGetFrameRate (constRMpipe *toQuery) @pend @astart RMpipe *toQuery - a handle to an RMpipe (queried). @aend @dstart Use this routine to obtain the RMpipe's notion of how many frames per second it is supposed to render. For a more meaningful discussion of what the frames-per-second RMpipe attribute means, refer to the discussion for rmPipeSetFrameRate() as well as the OpenRM Programming Guide. Returns either -1 or a positive integer on success, or zero on failure. @dend * ---------------------------------------------------- */ int rmPipeGetFrameRate(const RMpipe *p) { if (RM_ASSERT(p, "rmPipeGetFrameRate() error: the input RMpipe is NULL") == RM_WHACKED) return 0; return p->targetFrameRate; } void private_rmPipeFPSStart(RMpipe *p) { #if 0 RMtime now, wait, drift; double ms, diffMS; rmTimeCurrent(&now); ms = rmTimeDifferenceMS(&(p->lastTimeStart), &now); /* * quick test to see if whether or not we need to wait at all. */ if (ms > p->timePerFrameMS) { /* fprintf(stderr," SKIP!! est ms per frame = %g, current delta = %g\n", p->timePerFrameMS, ms); */ rmTimeCurrent(&(p->lastTimeStart)); return; } /* * compute amount of time we need to wait */ diffMS = p->timePerFrameMS - ms; rmTimeEncodeMS(&wait, diffMS); /* * do the wait */ rmTimeSleep(&wait); /* rmTimeSleepDrift(&wait, &drift); to accumulate/mediate error */ /* * what time is it now? */ rmTimeCurrent(&(p->lastTimeStart)); /* rmTimeDifference(&(p->lastTimeStart),&drift,(&p->lastTimeStart)); */ #endif p = NULL; /* foil compiler warnings */ } void private_rmPipeFPSEnd(RMpipe *p) { RMtime now; /* * compute the amount of time required for rendering */ rmTimeCurrent(&now); rmTimeDifference(&(p->lastTimeStart), &now, &(p->lastRenderTime)); } void private_rmPipeSetTimeSyncFunc(RMpipe *t, void (*newFunc)(RMpipe *)) { t->timeSyncFunc = newFunc; } void private_rmPipeTimeSyncFunc(RMpipe *p) { RMtime now, wait, drift; double ms, diffMS; extern RMenum rmTimeSleepDrift(const RMtime *tSleep, RMtime *drift); rmTimeCurrent(&now); ms = rmTimeDifferenceMS(&(p->lastTimeStart), &now); /* * quick test to see if whether or not we need to wait at all. */ #define FUDGE 0.0 /* ms fudge factor */ if ((ms-FUDGE) > p->timePerFrameMS) { /* fprintf(stderr," SKIP!! est ms per frame = %g, current delta = %g\n", p->timePerFrameMS, ms); */ rmTimeCurrent(&(p->lastTimeStart)); return; } /* * compute amount of time we need to wait */ diffMS = p->timePerFrameMS - ms; rmTimeEncodeMS(&wait, diffMS); /* * do the wait */ #define USE_DRIFT 1 #if USE_DRIFT rmTimeSleepDrift(&wait, &drift); #else rmTimeSleep(&wait); #endif /* * what time is it now? */ rmTimeCurrent(&(p->lastTimeStart)); #if DEBUG_RSYNC { int indx = timeSyncIndx % MAX_RSYNC_SAMPLES; timeSyncData[indx].t = p->lastTimeStart; /* current time */ timeSyncData[indx].frameNumber = rmPipeGetFrameNumber(p); timeSyncIndx++; } #endif #if USE_DRIFT /* * but, the timer should've gone off at now-drift, rather than now. * so, adjust "now" backwards by "drift" so that the next * frame happens at the right time. */ { long nowSecs, nowUSecs, dUSecs; rmTimeGet(&(p->lastTimeStart), &nowSecs, &nowUSecs); /* the seconds portion of the drift is most likely zero, so we'll ignore it */ #define FUDGE_US 0 rmTimeGet(&drift, NULL, &dUSecs); if (nowUSecs < (dUSecs+FUDGE_US)) { nowUSecs -= (dUSecs + 1000000 + FUDGE_US); nowSecs -= 1; } else nowUSecs -= (dUSecs+FUDGE_US); rmTimeSet(&(p->lastTimeStart), nowSecs, nowUSecs); } #endif } /* * ---------------------------------------------------- * @Name rmPipeGetFrameNumber @pstart int rmPipeGetFrameNumber (const RMpipe *toQuery) @pend @astart const RMpipe *toQuery - (input) a handle to an RMpipe. @aend @dstart Use this routine to obtain the RMpipe's notion of the current frame number. Upon success, a non-negative integer is returned. Upon failure, a value of -1 is returned. The RMpipe's frame number is initialized to zero when the RMpipe is created with rmPipeNew(). Upon each call to rmFrame(), the the frame number is incremented by one. There is no mechanism for applications to set the frame number, or for applications to reset the frame number to zero. @dend * ---------------------------------------------------- */ int rmPipeGetFrameNumber(const RMpipe *toQuery) { return toQuery->frameNumber; } static RMenum private_rmCheckForActiveContext(void) { /* * checks to see if there's an active OpenGL context. Return 1 if * all is well, otherwise, return a zero. */ #ifdef RM_X if (glXGetCurrentContext() == NULL) return RM_FALSE; else return RM_TRUE; #elif RM_WIN if (wglGetCurrentContext() == NULL) return RM_FALSE; else return RM_TRUE; #else /* assume RM_CR */ return RM_TRUE; #endif } /* PRIVATE */ int private_rmBuildExtensionTable(const char *s, char ***dst) { int n = 0; int i; char **d; d = (char **)malloc(sizeof(char *)*1024); /* max of 1024 extensions */ for (i=0; i< (int)(strlen(s)); ) { int istart, iend; /* scan to the first non-blank character */ while (s[i] == ' ') i++; istart = i; /* beginning of string */ /* scan to the next character that isn't a blank or a NULL */ while ((s[i] != ' ') && (s[i] != '\0') && (s[i] != '\n')) i++; iend = i-1; d[n] = (char *)calloc(iend-istart+2, sizeof(char)); /* leave room for an extra NUL */ memcpy((void *)d[n], (void *)(s+istart), iend-istart+1); n++; i++; } *dst = d; return n; } /* PRIVATE */ int private_rmHaveExtension(char **t, int n, const char *s) { int i; for (i=0; inumTextureUnits)); #if (DEBUG_LEVEL & DEBUG_TRACE) printf(" Multitexturing is supported on this renderer. There are %d texture units available. \n", c->numTextureUnits); #endif c->haveMultiTexturing = RM_TRUE; c->activeTextureARB = private_rmGLGetProcAddr("glActiveTextureARB"); c->multiTexCoord1fvARB = private_rmGLGetProcAddr("glMultiTexCoord1fvARB"); c->multiTexCoord2fvARB = private_rmGLGetProcAddr("glMultiTexCoord2fvARB"); c->multiTexCoord3fvARB = private_rmGLGetProcAddr("glMultiTexCoord3fvARB"); } else { #if (DEBUG_LEVEL & DEBUG_TRACE) printf("Sorry, GL_ARB_multitexture not supported by this renderer.\n"); #endif c->haveMultiTexturing = RM_FALSE; c->numTextureUnits = 0; c->activeTextureARB = NULL; c->multiTexCoord1fvARB = NULL; c->multiTexCoord2fvARB = NULL; c->multiTexCoord3fvARB = NULL; } /* check for 3D texturing */ if (private_rmHaveExtension(extensionTable, nExtensions, "GL_EXT_texture3D") == 1) { c->have3DTextures = RM_TRUE; c->rm_glTexImage3D = private_rmGLGetProcAddr("glTexImage3DEXT"); c->rm_glTexSubImage3D = private_rmGLGetProcAddr("glTexSubImage3DEXT"); } else { c->have3DTextures = RM_FALSE; c->rm_glTexImage3D = NULL; c->rm_glTexSubImage3D = NULL; } /* clean up */ { int i; for (i=0; i0 (positive integer) is returned to indicate the number of usable OpenGL multitexture units. Note that the number of texture units is implementation dependent, but should be at least two when multitexturing is present. This routine should be called only after the input RMpipe has been subjected to successful call to rmPipeMakeCurrent. @dend * ---------------------------------------------------- */ int rmPipeGetNumMultitextureUnits(const RMpipe *toQuery) { if (RM_ASSERT(toQuery, "rmPipeGetNumMultitextureUnits error - the input RMpipe is NULL") == RM_WHACKED) return -1; if (RM_ASSERT(toQuery->caps, "rmPipeGetNumMultitextureUnits internal error - the capabilities field is NULL. Please call rmPipeMakeCurrent before calling this routine. ") == RM_WHACKED) return -1; if (toQuery->caps->haveMultiTexturing == RM_FALSE) return 0; return toQuery->caps->numTextureUnits; } /* * ---------------------------------------------------- * @Name rmPipeSetSceneBackgroundColor @pstart RMenum rmPipeSetSceneBackgroundColor (RMpipe *toModify, const RMcolor4D *newColor) @pend @astart RMnode *toModify - a handle to an RMpipe (input). const RMcolor4D *newColor - a handle to the desired background color (input). @aend @dstart In RM/OpenRM, you may set a "background color" scene parameter at the RMnode level, at the RMpipe level, or both. The background color may consist of a single color or a color background image. In addition, you may also set a depth value or a depth image that will be used to initialize the z-buffer. The main issue with assigning a background color/depth parameter at the RMnode vs. the RMpipe level is convenience vs. fine-grained control. If you assign the background color/depth value at the RMpipe level, the background will be cleared once each time your application calls rmFrame() to render a scene. In some cases, such as when multiple viewports are rendered within a single window with a single call to rmFrame(), then you must assign the background color/depth value at the RMnode level. Unlike when assigning such values at the RMpipe level, using them at the RMnode level requires special care due to the multiple rendering passes RM makes for a single call to rmFrame(). The RMnode containing the background color/depth values must have its traversal mask set such that it is invoked at most once per call to rmFrame(). Use rmPipeSetSceneBackgroundColor to set the background color scene parameter at the RMpipe level. Calling this routine with a color argument of NULL disables the background color scene parameter. During rendering, the color planes of the framebuffer are cleared to this background color (with a depth value of 1.0, see NOTE below). Upon success, RM_CHILL is returned and the specified background color scene parameter is set. Otherwise, RM_WHACKED is returned, and the background color scene parameter remains unmodified. Passing in a value of NULL for the RMcolor4D object will effectively remove any existing background image color parameter from the RMpipe toModify. NOTE: internal to this routine, a call to rmPipeSetSceneDepthValue is performed, effectively coupling framebuffer and depthbuffer clears. Because this routine makes a copy of the input scene parameter (an RMcolor4D object), callers do not need to manage the input object after a successful return from this routine. This has important ramificiations: 1. Since a copy is made, any changes made to the caller's object will have no effect upon the scene graph, unless this routine is called again to update the scene parameter inside the scene graph; 2. Callers may safely delete their copy of the input object after a successful return from this routine. Related routines: rmPipeGetSceneBackgroundColor, rmPipeSetSceneBackgroundImage, rmPipeSetSceneDepthValue. @dend * ---------------------------------------------------- */ RMenum rmPipeSetSceneBackgroundColor (RMpipe *r, const RMcolor4D *new_color) { if (RM_ASSERT(r, "rmPipeSetSceneBackgroundColor() error: the input RMpipe pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); if (r->fbClearNode == NULL) r->fbClearNode = rmNodeNew("RMpipe fbClear node", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL); if (r->fbClearNode->fbClear == NULL) r->fbClearNode->fbClear = private_rmFBClearNew(); if (r->fbClearNode->fbClear->bgColor != NULL) rmColor4DDelete(r->fbClearNode->fbClear->bgColor); if (new_color != NULL) { float junk; r->fbClearNode->fbClear->bgColor = rmColor4DNew(1); *(r->fbClearNode->fbClear->bgColor) = *new_color; if (rmPipeGetSceneDepthValue(r, &junk) == RM_WHACKED) { extern float RM_DEFAULT_DEPTH_VALUE; junk = RM_DEFAULT_DEPTH_VALUE; rmPipeSetSceneDepthValue(r, &junk); } } else r->fbClearNode->fbClear->bgColor = NULL; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetSceneBackgroundColor @pstart RMenum rmPipeGetSceneBackgroundColor (const RMpipe *toQuery, RMcolor4D *returnColor) @pend @astart const RMnode *toQuery - a handle to an RMpipe (input). RMcolor4D *returnColor - a handle to a caller-supplied RMcolor4D object (return). @aend @dstart Use this routine to obtain the background color scene parameter of an RMpipe, if such a scene parameter exists. Upon success, RM_CHILL is returned and the scene background color is copied into the caller-supplied RMcolor4D (effectively returning a copy of the background color scene parameter to the caller). If no such scene parameter exists, or if there is some error condition detected, RM_WHACKED is returned, and the caller-supplied colorReturn object remains unmodified. @dend * ---------------------------------------------------- */ RMenum rmPipeGetSceneBackgroundColor (const RMpipe *r, RMcolor4D *color_return) { if ((RM_ASSERT(r, "rmPipeGetSceneBackgroundColor() error: the input RMpipe pointer is NULL.") == RM_WHACKED) || (RM_ASSERT(color_return, "rmPipeGetSceneBackgroundColor() error: the return RMcolor4D pointer is NULL.") == RM_WHACKED)) return(RM_WHACKED); if ((r->fbClearNode == NULL) || (r->fbClearNode->fbClear == NULL) || (r->fbClearNode->fbClear->bgColor == NULL)) return(RM_WHACKED); /* we are returning the contents of the scene parameter, an RMcolor4D object, to the caller, not the handle to the RMcolor4D object stored in the scene graph itself. */ *color_return = *(r->fbClearNode->fbClear->bgColor); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetSceneBackgroundImage @pstart RMenum rmPipeSetSceneBackgroundImage (RMpipe *toModify, const RMimage *newImageTile) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). const RMimage *newImageTile - a handle to an RMimage (input). @aend @dstart In RM/OpenRM, you may set a "background color" scene parameter at the RMnode level, at the RMpipe level, or both. The background color may consist of a single color or a color background image. In addition, you may also set a depth value or a depth image that will be used to initialize the z-buffer. The main issue with assigning a background color/depth parameter at the RMnode vs. the RMpipe level is convenience vs. fine-grained control. If you assign the background color/depth value at the RMpipe level, the background will be cleared once each time your application calls rmFrame() to render a scene. In some cases, such as when multiple viewports are rendered within a single window with a single call to rmFrame(), then you must assign the background color/depth value at the RMnode level. Unlike when assigning such values at the RMpipe level, using them at the RMnode level requires special care due to the multiple rendering passes RM makes for a single call to rmFrame(). The RMnode containing the background color/depth values must have its traversal mask set such that it is invoked at most once per call to rmFrame(). rmPipeSetSceneBackgroundImage sets the background image scene parameter for the RMpipe. When rendered, this image is tiled into the viewport at the scene depth value (or a default of 1.0) with an orthogonal projection, creating a background image. If the image does not fit the display, it will be tiled from top to bottom and left to right, so ragged edges fall on the bottom and right edges of the viewport. Passing in a value of NULL for the newImageTile parameter will effectively remove the background image tile scene parameter, if it exists. Upon success, RM_CHILL is returned and the background image scene parameter has been modified. Otherwise, RM_WHACKED is returned. Because this routine makes a copy of the input scene parameter (an RMimage object), callers do not need to manage the input object after a successful return from this routine. This has important ramificiations: 1. Since a copy is made, any changes made to the caller's object will have no effect upon the scene graph, unless this routine is called again to update the scene parameter inside the scene graph; 2. Callers may safely delete their copy of the input object after a successful return from this routine. NOTE: internal to this routine, a call to rmPipSetSceneDepthValue() is performed, effectively coupling framebuffer clears (by image tiling) with depth buffer clears. @dend * ---------------------------------------------------- */ RMenum rmPipeSetSceneBackgroundImage (RMpipe *p, const RMimage *tile) { if (RM_ASSERT(p, "rmPipeSetSceneBackgroundImage() error: input RMpipe is NULL. \n") == RM_WHACKED) return(RM_WHACKED); if (p->fbClearNode == NULL) p->fbClearNode = rmNodeNew("RMpipe fbClear node", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL); if (p->fbClearNode->fbClear == NULL) p->fbClearNode->fbClear = private_rmFBClearNew(); /* if there's an existing background image tile parameter, remove it. */ if (p->fbClearNode->fbClear->bgImageTile != NULL) rmImageDelete(p->fbClearNode->fbClear->bgImageTile); /* if the input isn't null, make a copy and assign it as a scene parameter. */ if (tile != NULL) { float junk; p->fbClearNode->fbClear->bgImageTile = rmImageDup(tile); /* if there's no background depth value, create one. this policy may change in the future. */ if (rmPipeGetSceneDepthValue(p, &junk) == RM_WHACKED) { extern float RM_DEFAULT_DEPTH_VALUE; junk = RM_DEFAULT_DEPTH_VALUE; rmPipeSetSceneDepthValue(p, &junk); } } else p->fbClearNode->fbClear->bgImageTile = NULL; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetSceneBackgroundImage @pstart RMenum rmPipeGetSceneBackgroundImage (const RMpipe *toQuery, RMimage **returnImageTile) @pend @astart const RMpipe *toQuery - a handle to an RMpipe (input). RMimage **returnImageTile - a handle to an RMimage handle (return). @aend @dstart Use this routine to obtain the background image scene parameter of an RMpipe. Note that a handle to the RMimage is returned, not a copy of the actual pixel data. Upon success, RM_CHILL is returned and a handle to the scene background image is copied into the caller-supplied RMimage handle. Otherwise, RM_WHACKED is returned. Unlike most other rmPipeGetScene*() routines, this routine returns a handle to the actual object contained within the RMpipe, rather than returning a copy. Applications should exercise appropriate discretion when using this object. @dend * ---------------------------------------------------- */ RMenum rmPipeGetSceneBackgroundImage (const RMpipe *r, RMimage **tile_return) { if ((RM_ASSERT(r, "rmPipeGetSceneBackgroundImage() error: input RMpipe is NULL. \n") == RM_WHACKED) || (RM_ASSERT(r, "rmPipeGetSceneBackgroundImage() error: input pointer to RMimage pointer is NULL. ") == RM_WHACKED)) return(RM_WHACKED); /* detect the absence of any scene parms, or the absence of the background image tile scene parm. */ if ((r->fbClearNode == NULL) || (r->fbClearNode->fbClear == NULL) || (r->fbClearNode->fbClear->bgImageTile == NULL)) return(RM_WHACKED); *tile_return = r->fbClearNode->fbClear->bgImageTile; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetSceneDepthImage @pstart RMenum rmPipeSetSceneDepthImage (RMpipe *toModify, const RMimage *newDepthImage) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). const RMimage *newDepthImage - a handle to a depth image (input) @aend @dstart In RM/OpenRM, you may set a "background color" scene parameter at the RMnode level, at the RMpipe level, or both. The background color may consist of a single color or a color background image. In addition, you may also set a depth value or a depth image that will be used to initialize the z-buffer. The main issue with assigning a background color/depth parameter at the RMnode vs. the RMpipe level is convenience vs. fine-grained control. If you assign the background color/depth value at the RMpipe level, the background will be cleared once each time your application calls rmFrame() to render a scene. In some cases, such as when multiple viewports are rendered within a single window with a single call to rmFrame(), then you must assign the background color/depth value at the RMnode level. Unlike when assigning such values at the RMpipe level, using them at the RMnode level requires special care due to the multiple rendering passes RM makes for a single call to rmFrame(). The RMnode containing the background color/depth values must have its traversal mask set such that it is invoked at most once per call to rmFrame(). rmPipeSetSceneDepthImage assigns a depth image as a scene parameter for the input RMpipe toModify. The depth image is similar to the background image tile, but is applied to the depth buffer, rather than the color planes of the framebuffer. Like the background image tile, the depth image scene parameter is tiled to fill the framebuffer, starting from the upper left-hand corner of the viewport. If the size of the depth buffer image and viewport do not match exactly, the "ragged edges" or on the right and the bottom of the viewport. Passing in a value of NULL for the RMimage object will have the effect of removing the background depth image scene parameter, if one exists, from the RMnode. Upon success, a copy of the caller's RMimage object is made, and the copy is assigned as a scene parameter (or, if the input RMimage object is NULL, the NULL is assigned as a depth image scene parameter, effectively turning off that scene parameter), and RM_CHILL is returned; otherwise, RM_WHACKED is returned. Because this routine makes a copy of the input scene parameter (an RMimage), callers do not need to manage the input object after a successful return from this routine. This has important ramificiations: 1. Since a copy is made, any changes made to the caller's object will have no effect upon the scene graph, unless this routine is called again to update the scene parameter inside the scene graph; 2. Callers may safely delete their copy of the input object after a successful return from this routine. Note: as a practical matter, it is suggested that the pixel format of input RMimage objects should be of type RM_FLOAT, and in the range 0..1. By default, depth buffer Pixels in OpenGL range from 0..1 (or perhaps 0..0.9999, depending upon your interpretation), but this range may be manipulated with glPixelTransferf(). As of the time of this writing (May 2000), we have tested background depth images only with RMimage's consisting of RM_FLOAT pixels in the range 0..1 (and they work - refer to the demonstration program "pdb" included with the openrm-demo distribution). @dend * ---------------------------------------------------- */ RMenum rmPipeSetSceneDepthImage (RMpipe *p, const RMimage *new_depth_image) { if (RM_ASSERT(p, "rmPipeSetSceneDepthImage() error: the input RMpipe pointer is NULL.") == RM_WHACKED) return(RM_WHACKED); if (p->fbClearNode == NULL) p->fbClearNode = rmNodeNew("RMpipe fbClear node", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL); if (p->fbClearNode->fbClear == NULL) p->fbClearNode->fbClear = private_rmFBClearNew(); if (p->fbClearNode->fbClear->depthImage != NULL) { rmImageDelete(p->fbClearNode->fbClear->depthImage); p->fbClearNode->fbClear->depthImage = NULL; } if (new_depth_image != NULL) p->fbClearNode->fbClear->depthImage = rmImageDup(new_depth_image); return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetSceneDepthImage @pstart RMenum rmPipeGetSceneDepthImage (const RMpipe *toQuery, RMimage **returnDepthImage) @pend @astart const RMpipe *toQuery - a handle to an RMnode (input). RMimage **returnDepthImage - a handle to a depth image handle (return). @aend @dstart Use this routine to obtain the RMpipe's depth image scene parameter. If such a scene parameter exists, the handle of the RMimage (depth image) scene parameter is copied into caller-supplied memory, and RM_CHILL is returned. Otherwise, in the event of an error or the absence of the depth image scene parameter, RM_WHACKED is returned and caller supplied memory remains unmodified. Unlike most other rmPipeGetScene*() routines, this routine returns a handle to the actual RMimage object contained within the scene graph, rather than returning a copy. Applications should exercise appropriate discretion when using this object. @dend * ---------------------------------------------------- */ RMenum rmPipeGetSceneDepthImage (const RMpipe *p, RMimage **depth_image_return) { if ((RM_ASSERT(p, "rmPipeGetSceneDepthImage() error: input RMpipe is NULL. \n") == RM_WHACKED) || (RM_ASSERT(depth_image_return, "rmPipeGetSceneDepthImage() error: input pointer to RMimage pointer is NULL. ") == RM_WHACKED)) return(RM_WHACKED); if ((p->fbClearNode == NULL) || (p->fbClearNode->fbClear == NULL) || (p->fbClearNode->fbClear->depthImage == NULL)) return(RM_WHACKED); *depth_image_return = p->fbClearNode->fbClear->depthImage; return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeSetSceneDepthValue @pstart RMenum rmPipeSetSceneDepthValue (RMpipe *toModify, const float *newDepthValue) @pend @astart RMpipe *toModify - a handle to an RMpipe (modified). const float *newDepthValue - a handle to the new depth value for the node (input). @aend @dstart In RM/OpenRM, you may set a "background color" scene parameter at the RMnode level, at the RMpipe level, or both. The background color may consist of a single color or a color background image. In addition, you may also set a depth value or a depth image that will be used to initialize the z-buffer. The main issue with assigning a background color/depth parameter at the RMnode vs. the RMpipe level is convenience vs. fine-grained control. If you assign the background color/depth value at the RMpipe level, the background will be cleared once each time your application calls rmFrame() to render a scene. In some cases, such as when multiple viewports are rendered within a single window with a single call to rmFrame(), then you must assign the background color/depth value at the RMnode level. Unlike when assigning such values at the RMpipe level, using them at the RMnode level requires special care due to the multiple rendering passes RM makes for a single call to rmFrame(). The RMnode containing the background color/depth values must have its traversal mask set such that it is invoked at most once per call to rmFrame(). Use rmPipeSetSceneDepthValue to set the scene depth value parameter for an RMpipe. The presence of the scene depth value has the effect of clearing the depth buffer to the value of newDepthValue. Passing in a value of NULL for newDepthValue has the effect of disabling depth buffer clears. The input newDepthValue should have the magnitude specified by glPixelTransferf(GL_DEPTH_SCALE,X) and range specified by glPixelTransferf(GL_DEPTH_BIAS,X). In OpenGL, the default depth bias is 0.0 and range/scale is 1.0. Upon success, RM_CHILL is returned and the node's depth value is set. Otherwise, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmPipeSetSceneDepthValue (RMpipe *p, const float *newDepthValue) { if (RM_ASSERT(p, "rmPipeSetSceneDepthValue() error: the input RMpipe pointer is NULL") == RM_WHACKED) return(RM_WHACKED); if (p->fbClearNode == NULL) p->fbClearNode = rmNodeNew("RMpipe fbClear node", RM_RENDERPASS_ALL, RM_RENDERPASS_ALL); if (p->fbClearNode->fbClear == NULL) p->fbClearNode->fbClear = private_rmFBClearNew(); if (p->fbClearNode->fbClear->depthValue != NULL) { free((void *)(p->fbClearNode->fbClear->depthValue)); p->fbClearNode->fbClear->depthValue = NULL; } if (newDepthValue != NULL) { p->fbClearNode->fbClear->depthValue = rmFloatNew(1); *(p->fbClearNode->fbClear->depthValue) = *newDepthValue; } return(RM_CHILL); } /* * ---------------------------------------------------- * @Name rmPipeGetSceneDepthValue @pstart RMenum rmPipeGetSceneDepthValue (const RMpipe *toQuery, float *returnDepthValue) @pend @astart const RMpipe *toQuery - a handle to an RMpipe (input). float *returnDepthValue - a handle to a caller-supplied float for the queried depth value (return). @aend @dstart Use this routine to query the scene depth value for a given RMpipe. Upon success, RM_CHILL is returned and the node's depth value is copied into the caller-supplied float. If the specified pointers are NULL, or no valid scene depth parameter exists, RM_WHACKED is returned. @dend * ---------------------------------------------------- */ RMenum rmPipeGetSceneDepthValue (const RMpipe *p, float *dv) { if ((RM_ASSERT(p, "rmPipeGetSceneDepthValue() error: the input RMpipe pointer is NULL") == RM_WHACKED) || (RM_ASSERT(dv, "rmPipeGetSceneDepthValue() error: the return float pointer is NULL.") == RM_WHACKED)) return(RM_WHACKED); if ((p->fbClearNode == NULL) || (p->fbClearNode->fbClear == NULL) || (p->fbClearNode->fbClear->depthValue == NULL)) return(RM_WHACKED); *dv = *(p->fbClearNode->fbClear->depthValue); return(RM_CHILL); } /* EOF */