// // Vrml 97 library // Copyright (C) 1998 Chris Morley // // VrmlScene.cpp // #if defined AW_NEW_IOSTREAMS # include #else # include #endif #include "config.h" #include "VrmlScene.h" #include #include #include #include "Doc.h" #include "Viewer.h" #include "System.h" #include "VrmlNamespace.h" #include "VrmlNodeType.h" // Handle clicks on Anchor nodes #include "VrmlNodeAnchor.h" // Bindable children nodes #include "VrmlNodeBackground.h" #include "VrmlNodeFog.h" #include "VrmlNodeNavigationInfo.h" #include "VrmlNodeViewpoint.h" // List of scene-scoped lights in the scene #include "VrmlNodePointLight.h" #include "VrmlNodeSpotLight.h" // List of Movies in the scene #include "VrmlNodeMovieTexture.h" // List of AudioClips in the scene #include "VrmlNodeAudioClip.h" // List of Scripts in the scene #include "VrmlNodeScript.h" // List of TimeSensors in the scene #include "VrmlNodeTimeSensor.h" // Max time in seconds between updates. Make this user // setable to balance performance with cpu usage. #ifndef DEFAULT_DELTA # define DEFAULT_DELTA 0.5 #endif // // Create a VrmlScene from a URL (optionally loading from a local copy, // so I can run as a netscape helper but still retrieve embedded urls). // VrmlScene::VrmlScene( const char *sceneUrl, const char *localCopy ) : d_url(0), d_urlLocal(0), d_namespace(0), d_modified(false), d_newView(false), d_deltaTime(DEFAULT_DELTA), d_pendingUrl(0), d_pendingParameters(0), d_pendingNodes(0), d_pendingScope(0), d_frameRate(0.0), d_firstEvent(0), d_lastEvent(0) { d_nodes.addToScene(this, sceneUrl); d_backgrounds = new VrmlNodeList; d_backgroundStack = new VrmlNodeList; d_fogs = new VrmlNodeList; d_fogStack = new VrmlNodeList; d_navigationInfos = new VrmlNodeList; d_navigationInfoStack = new VrmlNodeList; d_viewpoints = new VrmlNodeList; d_viewpointStack = new VrmlNodeList; d_scopedLights = new VrmlNodeList; d_scripts = new VrmlNodeList; d_timers = new VrmlNodeList; d_movies = new VrmlNodeList; d_audioClips = new VrmlNodeList; if (sceneUrl) if (! load(sceneUrl, localCopy)) theSystem->error("Couldn't load '%s'.\n", sceneUrl); } VrmlScene::~VrmlScene() { d_nodes.addToScene( 0, 0 ); d_nodes.removeChildren(); bindableRemoveAll( d_backgroundStack ); delete d_backgroundStack; bindableRemoveAll( d_fogStack ); delete d_fogStack; bindableRemoveAll( d_navigationInfoStack ); delete d_navigationInfoStack; bindableRemoveAll( d_viewpointStack ); delete d_viewpointStack; delete d_backgrounds; delete d_fogs; delete d_navigationInfos; delete d_viewpoints; delete d_scopedLights; delete d_scripts; delete d_timers; delete d_movies; delete d_audioClips; delete d_url; delete d_urlLocal; delete d_pendingUrl; delete d_pendingParameters; delete d_pendingNodes; delete d_pendingScope; delete d_namespace; } // Load a (possibly non-VRML) file... bool VrmlScene::loadUrl( VrmlMFString *url, VrmlMFString *parameters ) { if (! url) return false; int np = parameters ? parameters->size() : 0; char **params = parameters ? parameters->get() : 0; // try each url until we find one we can handle int i, n = url->size(); char **urls = url->get(); for (i=0; i 4 && ( strncmp( mod-4, ".wrl", 4) == 0 || strncmp( mod-4, ".wrz", 4) == 0 || strncmp( mod-4, ".WRL", 4) == 0 || strncmp( mod-4, ".WRZ", 4) == 0 || (mod-tail > 7 && strncmp( mod-7, ".wrl.gz", 7) == 0 ))) { if (load( urls[i] )) break; } else { if (theSystem->loadUrl( urls[i], np, params )) break; } } } return i != n; // true if we found a url that loaded } // Called by viewer when a destroy request is received. The request // is just passed on to the client via the worldChanged CB. void VrmlScene::destroyWorld() { doCallbacks( DESTROY_WORLD ); } // Replace nodes void VrmlScene::replaceWorld( VrmlMFNode &nodes, VrmlNamespace *ns, Doc *url, Doc *urlLocal ) { theSystem->debug("replaceWorld( url %s )\n", url->url()); delete d_namespace; delete d_url; delete d_urlLocal; d_namespace = ns; d_url = url; d_urlLocal = urlLocal; // Clear bindable stacks. bindableRemoveAll( d_backgroundStack ); bindableRemoveAll( d_fogStack ); bindableRemoveAll( d_navigationInfoStack ); bindableRemoveAll( d_viewpointStack ); // Get rid of current world: pending events, nodes. flushEvents(); d_nodes.removeChildren(); // Do this to set the relative URL d_nodes.addToScene( (VrmlScene *) this, urlDoc()->url() ); // Add the nodes to a Group and put the group in the scene. // This will load EXTERNPROTOs and Inlines. d_nodes.addChildren( nodes ); // Send initial set_binds to bindable nodes double timeNow = theSystem->time(); VrmlSFBool flag(true); VrmlNode *bindable = 0; // compiler warning if (d_backgrounds->size() > 0 && (bindable = d_backgrounds->front()) != 0) bindable->eventIn( timeNow, "set_bind", &flag ); if (d_fogs->size() > 0 && (bindable = d_fogs->front()) != 0) bindable->eventIn( timeNow, "set_bind", &flag ); if (d_navigationInfos->size() > 0 && (bindable = d_navigationInfos->front()) != 0) bindable->eventIn( timeNow, "set_bind", &flag ); if (d_viewpoints->size() > 0 && (bindable = d_viewpoints->front()) != 0) bindable->eventIn( timeNow, "set_bind", &flag ); // Notify anyone interested that the world has changed doCallbacks( REPLACE_WORLD ); setModified(); } void VrmlScene::doCallbacks( int reason ) { SceneCBList::iterator cb, cbend = d_sceneCallbacks.end(); for (cb = d_sceneCallbacks.begin(); cb != cbend; ++cb) (*cb)( reason ); } void VrmlScene::addWorldChangedCallback( SceneCB cb ) { d_sceneCallbacks.push_front( cb ); } // Read a VRML97 file. // This is only for [*.wrl][#viewpoint] url loading (no parameters). bool VrmlScene::load(const char *url, const char *localCopy) { // Look for '#Viewpoint' syntax. There ought to be a current // scene if this format is used. VrmlSFBool flag(true); if (*url == '#') { VrmlNode *vp = d_namespace ? d_namespace->findNode(url+1) : 0; // spec: ignore if named viewpoint not found if (vp) { vp->eventIn( theSystem->time(), "set_bind", &flag ); setModified(); } return true; } // Try to load a file. Prefer a local copy if available. Doc *tryUrl; if (localCopy) tryUrl = new Doc( localCopy, 0 ); else tryUrl = new Doc( url, d_url); VrmlNamespace *newScope = new VrmlNamespace(); VrmlMFNode *newNodes = readWrl( tryUrl, newScope ); if ( newNodes ) { Doc *sourceUrl = tryUrl, *urlLocal = 0; if (localCopy) { sourceUrl = new Doc( url ); urlLocal = tryUrl; } replaceWorld( *newNodes, newScope, sourceUrl, urlLocal ); delete newNodes; // Look for '#Viewpoint' syntax if ( sourceUrl->urlModifier() ) { VrmlNode *vp = d_namespace->findNode( sourceUrl->urlModifier()+1 ); double timeNow = theSystem->time(); if (vp) vp->eventIn( timeNow, "set_bind", &flag ); } return true; // Success. } delete tryUrl; return false; } // Read a VRML file from one of the urls. VrmlMFNode* VrmlScene::readWrl( VrmlMFString *urls, Doc *relative, VrmlNamespace *ns ) { Doc url; int i, n = urls->size(); for (i=0; idebug("Trying to read url '%s'\n", urls->get(i)); url.seturl( urls->get(i), relative ); VrmlMFNode *kids = VrmlScene::readWrl( &url, ns ); if ( kids ) return kids; else if (i < n-1 && strncmp(urls->get(i),"urn:",4)) theSystem->warn("Couldn't read url '%s': %s\n", urls->get(i), strerror( errno)); } return 0; } // yacc globals extern void yystring(char *); extern void yyfunction( int (*)(char *, int) ); extern int yyparse(); extern FILE *yyin; extern VrmlNamespace *yyNodeTypes; extern VrmlMFNode *yyParsedNodes; extern Doc *yyDocument; #if HAVE_LIBPNG # include "zlib.h" # define YYIN yygz extern gzFile yygz; #else # define YYIN yyin #endif // Read a VRML file and return the (valid) nodes. VrmlMFNode* VrmlScene::readWrl( Doc *tryUrl, VrmlNamespace *ns ) { VrmlMFNode* result = 0; theSystem->debug("readWRL %s\n", tryUrl->url()); // Should verify MIME type... #if HAVE_LIBPNG if ((YYIN = tryUrl->gzopen("rb")) != 0) #else if ((YYIN = tryUrl->fopen("rb")) != 0) #endif { // If the caller is not interested in PROTO defs, use a local namespace VrmlNamespace nodeDefs; if (ns) yyNodeTypes = ns; else yyNodeTypes = &nodeDefs; yyDocument = tryUrl; yyParsedNodes = 0; yyparse(); yyNodeTypes = 0; yyDocument = 0; result = yyParsedNodes; yyParsedNodes = 0; #if HAVE_LIBPNG tryUrl->gzclose(); #else tryUrl->fclose(); #endif } return result; } // bool VrmlScene::loadFromString( const char *vrmlString ) { VrmlNamespace *newScope = new VrmlNamespace(); VrmlMFNode *newNodes = readString( vrmlString, newScope ); if ( newNodes ) { replaceWorld( *newNodes, newScope, 0, 0 ); delete newNodes; return true; } return false; } // Read VRML from a string and return the (valid) nodes. VrmlMFNode* VrmlScene::readString( const char *vrmlString, VrmlNamespace *ns ) { VrmlMFNode* result = 0; if (vrmlString != 0) { yyNodeTypes = ns; yyDocument = 0; yyParsedNodes = 0; // set input to be from string yyin = 0; yystring( (char *)vrmlString ); yyparse(); yyNodeTypes = 0; result = yyParsedNodes; yyParsedNodes = 0; } return result; } // Load VRML from an application-provided callback function bool VrmlScene::loadFromFunction( LoadCB cb, const char *url ) { Doc *doc = url ? new Doc( url, 0 ) : 0; VrmlNamespace *ns = new VrmlNamespace(); VrmlMFNode *newNodes = readFunction( cb, doc, ns ); if ( newNodes ) { replaceWorld( *newNodes, ns, doc, 0 ); delete newNodes; return true; } if (doc) delete doc; return false; } // Read VRML from a cb and return the (valid) nodes. VrmlMFNode* VrmlScene::readFunction( LoadCB cb, Doc *url, VrmlNamespace *ns ) { VrmlMFNode* result = 0; if (cb != 0) { yyNodeTypes = ns; yyParsedNodes = 0; yyDocument = url; // set input to be from cb yyfunction( cb ); yyparse(); yyDocument = 0; yyNodeTypes = 0; result = yyParsedNodes; yyParsedNodes = 0; } return result; } // Read a PROTO from a URL to get the implementation of an EXTERNPROTO. // This should read only PROTOs and return when the first/specified PROTO // is read... VrmlNodeType* VrmlScene::readPROTO( VrmlMFString *urls, Doc *relative ) { // This is a problem. The nodeType of the EXTERNPROTO has a namespace // that refers back to this namespace (protos), which will be invalid // after we exit this function. I guess it needs to be allocated and // ref counted too... VrmlNamespace protos; Doc urlDoc; VrmlNodeType* def = 0; int i, n = urls->size(); theSystem->debug("readPROTO\n"); for (i=0; idebug("Trying to read url '%s'\n", urls->get(i)); urlDoc.seturl( urls->get(i), relative ); VrmlMFNode *kids = VrmlScene::readWrl( &urlDoc, &protos ); if ( kids ) delete kids; // Grab the specified PROTO, or the first one. const char *whichProto = urlDoc.urlModifier(); if (*whichProto) def = (VrmlNodeType*) protos.findType( whichProto+1 ); else def = (VrmlNodeType*) protos.firstType(); if (def) { def->setActualUrl( urlDoc.url() ); break; } else if (i < n-1 && strncmp(urls->get(i),"urn:",4)) theSystem->warn("Couldn't read EXTERNPROTO url '%s': %s\n", urls->get(i), strerror( errno)); } return def; } // Write the current scene to a file. // Need to save the PROTOs/EXTERNPROTOs too... bool VrmlScene::save(const char *url) { bool success = false; Doc save(url); ostream &os = save.outputStream(); if (os) { os << "#VRML V2.0 utf8\n"; os << d_nodes; success = true; } return success; } // // Script node API functions // const char *VrmlScene::getName() { return "LibVRML97"; } const char *VrmlScene::getVersion() { static char vs[32]; sprintf(vs, "%d.%d.%d", LIBVRML_MAJOR_VERSION, LIBVRML_MINOR_VERSION, LIBVRML_MICRO_VERSION); return vs; } double VrmlScene::getFrameRate() { return d_frameRate; } // Queue an event to load URL/nodes (async so it can be called from a node) void VrmlScene::queueLoadUrl( VrmlMFString *url, VrmlMFString *parameters ) { if (! d_pendingNodes && ! d_pendingUrl) { d_pendingUrl = url->clone()->toMFString(); d_pendingParameters = parameters->clone()->toMFString(); } } void VrmlScene::queueReplaceNodes( VrmlMFNode *nodes, VrmlNamespace *ns ) { if (! d_pendingNodes && ! d_pendingUrl) { d_pendingNodes = nodes->clone()->toMFNode(); d_pendingScope = ns; } } // Event processing. Current events are in the array // d_eventMem[d_firstEvent,d_lastEvent). If d_firstEvent == d_lastEvent, // the queue is empty. There is a fixed maximum number of events. If we // are so far behind that the queue is filled, the oldest events get // overwritten. void VrmlScene::queueEvent(double timeStamp, VrmlField *value, VrmlNode *toNode, const char *toEventIn) { Event *e = &d_eventMem[d_lastEvent]; e->timeStamp = timeStamp; e->value = value; e->toNode = toNode; e->toEventIn = toEventIn; d_lastEvent = (d_lastEvent+1) % MAXEVENTS; // If the event queue is full, discard the oldest (in terms of when it // was put on the queue, not necessarily in terms of earliest timestamp). if (d_lastEvent == d_firstEvent) { e = &d_eventMem[d_lastEvent]; delete e->value; d_firstEvent = (d_firstEvent+1) % MAXEVENTS; } } // Any events waiting to be distributed? bool VrmlScene::eventsPending() { return d_firstEvent != d_lastEvent; } // Discard all pending events void VrmlScene::flushEvents() { while (d_firstEvent != d_lastEvent) { Event *e = &d_eventMem[d_firstEvent]; d_firstEvent = (d_firstEvent+1) % MAXEVENTS; delete e->value; } } // Called by the viewer when the cursor passes over, clicks, drags, or // releases a sensitive object (an Anchor or another grouping node with // an enabled TouchSensor child). void VrmlScene::sensitiveEvent( void *object, double timeStamp, bool isOver, bool isActive, double *point ) { VrmlNode *n = (VrmlNode *)object; if (n) { VrmlNodeAnchor *a = n->toAnchor(); if ( a ) { // This should really be (isOver && !isActive && n->wasActive) // (ie, button up over the anchor after button down over the anchor) if (isActive && isOver) { a->activate(); //theSystem->inform(""); } else if (isOver) { const char *description = a->description(); const char *url = a->url(); if (description && url) theSystem->inform("%s (%s)", description, url); else if (description || url) theSystem->inform("%s", description ? description : url); //else //theSystem->inform(""); } //else //theSystem->inform(""); } // The parent grouping node is registered for Touch/Drag Sensors else { VrmlNodeGroup *g = n->toGroup(); if (g) { //theSystem->inform(""); g->activate( timeStamp, isOver, isActive, point ); setModified(); } } } //else //theSystem->inform(""); } // // The update method is where the events are processed. It should be // called after each frame is rendered. // bool VrmlScene::update( double timeStamp ) { if (timeStamp <= 0.0) timeStamp = theSystem->time(); VrmlSFTime now( timeStamp ); d_deltaTime = DEFAULT_DELTA; // Update each of the timers. VrmlNodeList::iterator i, end = d_timers->end(); for (i = d_timers->begin(); i != end; ++i) { VrmlNodeTimeSensor *t = (*i)->toTimeSensor(); if (t) t->update( now ); } // Update each of the clips. end = d_audioClips->end(); for (i = d_audioClips->begin(); i != end; ++i) { VrmlNodeAudioClip *c = (*i)->toAudioClip(); if (c) c->update( now ); } // Update each of the scripts. end = d_scripts->end(); for (i = d_scripts->begin(); i != end; ++i) { VrmlNodeScript *s = (*i)->toScript(); if (s) s->update( now ); } // Update each of the movies. end = d_movies->end(); for (i = d_movies->begin(); i != end; ++i) { VrmlNodeMovieTexture *m = (*i)->toMovieTexture(); if (m) m->update( now ); } // Pass along events to their destinations while (d_firstEvent != d_lastEvent && ! d_pendingUrl && ! d_pendingNodes) { Event *e = &d_eventMem[d_firstEvent]; d_firstEvent = (d_firstEvent+1) % MAXEVENTS; // Ensure that the node is in the scene graph VrmlNode *n = e->toNode; if (this != n->scene()) { theSystem->debug("VrmlScene::update: %s::%s is not in the scene graph yet.\n", n->nodeType()->getName(), n->name()); n->addToScene((VrmlScene*)this, urlDoc()->url() ); } n->eventIn(e->timeStamp, e->toEventIn, e->value); // this needs to change if event values are shared... delete e->value; } if (d_pendingNodes) { replaceWorld( *d_pendingNodes, d_pendingScope ); delete d_pendingNodes; d_pendingNodes = 0; d_pendingScope = 0; } else if (d_pendingUrl) { (void) loadUrl( d_pendingUrl, d_pendingParameters ); delete d_pendingUrl; delete d_pendingParameters; d_pendingUrl = 0; d_pendingParameters = 0; } // Signal a redisplay if necessary return isModified(); } bool VrmlScene::headlightOn() { VrmlNodeNavigationInfo *navInfo = bindableNavigationInfoTop(); if (navInfo) return navInfo->headlightOn(); return true; } // Draw this scene into the specified viewer void VrmlScene::render(Viewer *viewer) { // if (d_newView) { viewer->resetUserNavigation(); d_newView = false; } // Default viewpoint parameters float position[3] = { 0.0, 0.0, 10.0 }; float orientation[4] = { 0.0, 0.0, 1.0, 0.0 }; float field = 0.785398; float avatarSize = 0.25; float visibilityLimit = 0.0; VrmlNodeViewpoint *vp = bindableViewpointTop(); if (vp) { position[0] = vp->positionX(); position[1] = vp->positionY(); position[2] = vp->positionZ(); orientation[0] = vp->orientationX(); orientation[1] = vp->orientationY(); orientation[2] = vp->orientationZ(); orientation[3] = vp->orientationR(); field = vp->fieldOfView(); vp->inverseTransform(viewer); } VrmlNodeNavigationInfo *ni = bindableNavigationInfoTop(); if (ni) { avatarSize = ni->avatarSize()[0]; visibilityLimit = ni->visibilityLimit(); } viewer->setViewpoint( position, orientation, field, avatarSize, visibilityLimit); // Set background. VrmlNodeBackground *bg = bindableBackgroundTop(); if (bg) { // Should be transformed by the accumulated rotations above ... bg->renderBindable(viewer); } else viewer->insertBackground(); // Default background // Fog VrmlNodeFog *f = bindableFogTop(); if (f) { viewer->setFog(f->color(), f->visibilityRange(), f->fogType()); } // Activate the headlight. // ambient is supposed to be 0 according to the spec... if ( headlightOn() ) { float rgb[3] = { 1.0, 1.0, 1.0 }; float xyz[3] = { 0.0, 0.0, -1.0 }; float ambient = 0.3; viewer->insertDirLight( ambient, 1.0, rgb, xyz ); } // Top level object viewer->beginObject(0); // Do the scene-level lights (Points and Spots) VrmlNodeList::iterator li, end = d_scopedLights->end(); for (li = d_scopedLights->begin(); li != end; ++li) { VrmlNodeLight* x = (*li)->toLight(); if (x) x->renderScoped( viewer ); } // Render the top level group d_nodes.render( viewer ); viewer->endObject(); // This is actually one frame late... d_frameRate = viewer->getFrameRate(); clearModified(); // If any events were generated during render (ugly...) do an update if (eventsPending()) setDelta( 0.0 ); } // // Bindable children node stacks. For the CS purists out there, these // aren't really stacks as they allow arbitrary elements to be removed // (not just the top). // VrmlNode *VrmlScene::bindableTop( BindStack stack ) { return (stack == 0 || stack->empty()) ? 0 : stack->front(); } void VrmlScene::bindablePush( BindStack stack, VrmlNode *node ) { bindableRemove( stack, node ); // Remove any existing reference stack->push_front( node->reference() ); setModified(); } void VrmlScene::bindableRemove( BindStack stack, VrmlNode *node ) { if (stack) { VrmlNodeList::iterator i; for (i = stack->begin(); i != stack->end(); ++i ) if ( *i == node ) { (*i)->dereference(); stack->erase( i ); setModified(); break; } } } // Remove all entries from the stack void VrmlScene::bindableRemoveAll( BindStack stack ) { VrmlNodeList::iterator i; for (i = stack->begin(); i != stack->end(); ++i ) (*i)->dereference(); stack->erase(stack->begin(), stack->end()); } // The nodes in the "set of all nodes of this type" lists are not // ref'd/deref'd because they are only added to and removed from // the lists in their constructors and destructors. // Bindable children nodes (stacks) // Define for each Type: // add/remove to complete list of nodes of this type // VrmlNodeType *bindableTypeTop(); // void bindablePush(VrmlNodeType *); // void bindableRemove(VrmlNodeType *); // Background void VrmlScene::addBackground( VrmlNodeBackground *n ) { d_backgrounds->push_back( n ); } void VrmlScene::removeBackground( VrmlNodeBackground *n ) { d_backgrounds->remove( n ); } VrmlNodeBackground *VrmlScene::bindableBackgroundTop() { VrmlNode *b = bindableTop( d_backgroundStack ); return b ? b->toBackground() : 0; } void VrmlScene::bindablePush( VrmlNodeBackground *n ) { bindablePush( d_backgroundStack, n ); } void VrmlScene::bindableRemove( VrmlNodeBackground *n ) { bindableRemove( d_backgroundStack, n ); } // Fog void VrmlScene::addFog( VrmlNodeFog *n ) { d_fogs->push_back( n ); } void VrmlScene::removeFog( VrmlNodeFog *n ) { d_fogs->remove( n ); } VrmlNodeFog *VrmlScene::bindableFogTop() { VrmlNode *f = bindableTop( d_fogStack ); return f ? f->toFog() : 0; } void VrmlScene::bindablePush( VrmlNodeFog *n ) { bindablePush( d_fogStack, n ); } void VrmlScene::bindableRemove( VrmlNodeFog *n ) { bindableRemove( d_fogStack, n ); } // NavigationInfo void VrmlScene::addNavigationInfo( VrmlNodeNavigationInfo *n ) { d_navigationInfos->push_back( n ); } void VrmlScene::removeNavigationInfo( VrmlNodeNavigationInfo *n ) { d_navigationInfos->remove( n ); } VrmlNodeNavigationInfo *VrmlScene::bindableNavigationInfoTop() { VrmlNode *n = bindableTop( d_navigationInfoStack ); return n ? n->toNavigationInfo() : 0; } void VrmlScene::bindablePush( VrmlNodeNavigationInfo *n ) { bindablePush( d_navigationInfoStack, n ); } void VrmlScene::bindableRemove( VrmlNodeNavigationInfo *n ) { bindableRemove( d_navigationInfoStack, n ); } // Viewpoint void VrmlScene::addViewpoint( VrmlNodeViewpoint *n ) { d_viewpoints->push_back( n ); } void VrmlScene::removeViewpoint( VrmlNodeViewpoint *n ) { d_viewpoints->remove( n ); } VrmlNodeViewpoint *VrmlScene::bindableViewpointTop() { VrmlNode *t = bindableTop( d_viewpointStack ); return t ? t->toViewpoint() : 0; } void VrmlScene::bindablePush( VrmlNodeViewpoint *n ) { bindablePush( d_viewpointStack, n ); d_newView = true; } void VrmlScene::bindableRemove( VrmlNodeViewpoint *n ) { bindableRemove( d_viewpointStack, n ); d_newView = true; } // Bind to the next viewpoint in the list void VrmlScene::nextViewpoint() { VrmlNodeViewpoint *vp = bindableViewpointTop(); VrmlNodeList::iterator i; for (i = d_viewpoints->begin(); i != d_viewpoints->end(); ++i ) if ((*i) == vp) { if (++i == d_viewpoints->end()) i = d_viewpoints->begin(); VrmlSFBool flag(true); if ((*i) && (vp = (*i)->toViewpoint()) != 0) vp->eventIn( theSystem->time(), "set_bind", &flag ); return; } } void VrmlScene::prevViewpoint() { VrmlNodeViewpoint *vp = bindableViewpointTop(); VrmlNodeList::iterator i; for (i = d_viewpoints->begin(); i != d_viewpoints->end(); ++i ) if ((*i) == vp) { if (i == d_viewpoints->begin()) i = d_viewpoints->end(); VrmlSFBool flag(true); if ( *(--i) && (vp = (*i)->toViewpoint()) != 0 ) vp->eventIn( theSystem->time(), "set_bind", &flag ); return; } } int VrmlScene::nViewpoints() { return (int)d_viewpoints->size(); } void VrmlScene::getViewpoint(int nvp, const char **namep, const char **descriptionp) { VrmlNodeList::iterator i; int n; *namep = *descriptionp = 0; for (i = d_viewpoints->begin(), n=0; i != d_viewpoints->end(); ++i, ++n ) if (n == nvp) { *namep = (*i)->name(); *descriptionp = ((VrmlNodeViewpoint*)(*i))->description(); return; } } void VrmlScene::setViewpoint(const char *name, const char *description) { VrmlNodeList::iterator i; for (i = d_viewpoints->begin(); i != d_viewpoints->end(); ++i) if (strcmp(name, (*i)->name()) == 0 && strcmp(description, ((VrmlNodeViewpoint*)(*i))->description()) == 0) { VrmlNodeViewpoint *vp; VrmlSFBool flag(true); if ((vp = (VrmlNodeViewpoint*) *i) != 0) vp->eventIn( theSystem->time(), "set_bind", &flag ); return; } } void VrmlScene::setViewpoint(int nvp) { VrmlNodeList::iterator i; int j = 0; for (i = d_viewpoints->begin(); i != d_viewpoints->end(); ++i) { if (j == nvp) { VrmlNodeViewpoint *vp; VrmlSFBool flag(true); if ((vp = (VrmlNodeViewpoint*) *i) != 0) vp->eventIn( theSystem->time(), "set_bind", &flag ); return; } ++j; } } // The nodes in these lists are not ref'd/deref'd because they // are only added to and removed from the lists in their constructors // and destructors. // Scene-level distance-scoped lights void VrmlScene::addScopedLight( VrmlNodeLight *light ) { d_scopedLights->push_back( light ); } void VrmlScene::removeScopedLight( VrmlNodeLight *light ) { d_scopedLights->remove( light ); } // Movies void VrmlScene::addMovie( VrmlNodeMovieTexture *movie ) { d_movies->push_back( movie ); } void VrmlScene::removeMovie( VrmlNodeMovieTexture *movie ) { d_movies->remove( movie ); } // Scripts void VrmlScene::addScript( VrmlNodeScript *script ) { d_scripts->push_back( script ); } void VrmlScene::removeScript( VrmlNodeScript *script ) { d_scripts->remove( script ); } // TimeSensors void VrmlScene::addTimeSensor( VrmlNodeTimeSensor *timer ) { d_timers->push_back( timer ); } void VrmlScene::removeTimeSensor( VrmlNodeTimeSensor *timer ) { d_timers->remove( timer ); } // AudioClips void VrmlScene::addAudioClip( VrmlNodeAudioClip *audio_clip ) { d_audioClips->push_back( audio_clip ); } void VrmlScene::removeAudioClip( VrmlNodeAudioClip *audio_clip ) { d_audioClips->remove( audio_clip ); }