// // Vrml 97 library // Copyright (C) 1998 Chris Morley // // VrmlNodeProto handles instances of nodes defined via PROTO // statements. // // Instances of PROTOs clone the implementation nodes stored // in a VrmlNodeType object. The only tricky parts are to // make sure ROUTEs are properly copied (the DEF name map is // not available) and that fields are copied properly (the // MF* guys currently share data & need to be made copy-on- // write for this to be correct). Flags are set as each node // is cloned so that USEd nodes are referenced rather than // duplicated. // // ROUTEs: Build a temp namespace as each (named) implementation // node is cloned, then traverse the implementation nodes again, // reproducing the routes in the cloned nodes using the temp ns. // I think that the addToScene() method is the right place to // download EXTERNPROTO implementations. Should also check that // the first node matches the expected type (need to store the // expected type if first node is null when one of the type // tests is run). // // Events between nodes in the PROTO implementation are handled // by the ROUTE copying described above. For eventIns coming into // the proto, when the implementation nodes are copied, a list // of eventIns/exposedFields along with their IS mappings should // be constructed. // EventOuts from an implementation node to a node outside the // PROTO can be directly replaced at copy time. // #include #include "VrmlNodeProto.h" #include "VrmlNamespace.h" #include "VrmlMFNode.h" #include #if defined AW_NEW_IOSTREAMS # include #else # include #endif VrmlNodeType *VrmlNodeProto::nodeType() const { return d_nodeType; } VrmlNodeProto::VrmlNodeProto(VrmlNodeType *nodeDef, VrmlScene *scene) : VrmlNode(scene), d_nodeType(nodeDef->reference()), d_instantiated(false), d_scope(0), d_nodes(0), d_viewerObject(0) { } VrmlNodeProto::VrmlNodeProto(const VrmlNodeProto &n) : VrmlNode(0), d_nodeType(n.nodeType()->reference()), d_instantiated(false), d_scope(0), d_nodes(0), d_viewerObject(0) { // Copy fields. list::const_iterator i; for (i = n.d_fields.begin(); i != n.d_fields.end(); i++) { VrmlField *value = (*i)->value; setField( (*i)->name, *value ); } } VrmlNodeProto::~VrmlNodeProto() { // Free strings & values list::iterator i; for (i = d_fields.begin(); i != d_fields.end(); i++) { NameValueRec *r = *i; free(r->name); delete r->value; delete r; } EventDispatchList::iterator e; for (e = d_eventDispatch.begin(); e != d_eventDispatch.end(); ++e) { EventDispatch *ed = *e; delete [] ed->name; VrmlNodeType::ISMap::iterator j; for (j = ed->ismap.begin(); j != ed->ismap.end(); ++j) { VrmlNodeType::NodeFieldRec *nf = (*j); delete [] nf->fieldName; delete nf; } delete ed; } delete d_nodes; delete d_scope; d_nodeType->dereference(); } // Note that the copy constructor doesn't copy the implementation // nodes, so they will need to be instantiated. VrmlNode *VrmlNodeProto::cloneMe() const { return new VrmlNodeProto(*this); } VrmlNodeProto* VrmlNodeProto::toProto() const { return (VrmlNodeProto*) this; } // Instantiate a local copy of the implementation nodes. // EXTERNPROTOs are actually loaded here. We don't want // *references* (DEF/USE) to the nodes in the PROTO definition, // we need to actually clone new nodes... void VrmlNodeProto::instantiate() { theSystem->debug("%s::%s instantiate\n", d_nodeType->getName(), name()); if (! d_nodes) { VrmlMFNode *protoNodes = d_nodeType->getImplementationNodes(); int nNodes = protoNodes ? protoNodes->size() : 0; int i; d_scope = new VrmlNamespace(); // Clear all flags - encountering a set flag during cloning // indicates a USEd node, which should be referenced rather // than cloned. for (i=0; iget(i)->clearFlags(); // Clone nodes // Those squeamish about broken encapsulations shouldn't look... d_nodes = new VrmlMFNode(nNodes, 0); VrmlNode **clone = d_nodes->get(); for (i=0; iget(i)->clone( d_scope )->reference(); // Copy internal (to the PROTO implementation) ROUTEs. for (i=0; iget(i)->copyRoutes( d_scope ); // Collect eventIns coming from outside the PROTO. // A list of eventIns along with their maps to local // nodes/eventIns is constructed for each instance. VrmlNodeType::FieldList &eventIns = d_nodeType->eventIns(); VrmlNodeType::FieldList::iterator ev; VrmlNodeType::ISMap *ismap; VrmlNodeType::ISMap::const_iterator j; for (ev = eventIns.begin(); ev != eventIns.end(); ++ev) { EventDispatch *ed = new EventDispatch; char *eventName = (*ev)->name; ed->name = new char[strlen(eventName)+1]; strcpy(ed->name, eventName); ismap = &(*ev)->thisIS; d_eventDispatch.push_front(ed); for (j = ismap->begin(); j != ismap->end(); ++j) { VrmlNodeType::NodeFieldRec *nf = new VrmlNodeType::NodeFieldRec; nf->node = d_scope->findNode((*j)->node->name()); nf->fieldName = new char[strlen((*j)->fieldName)+1]; strcpy(nf->fieldName, (*j)->fieldName); ed->ismap.push_front(nf); } } // Distribute eventOuts. Each eventOut ROUTE is added // directly to the local nodes that have IS'd the PROTO // eventOut. VrmlNodeType::FieldList &eventOuts = d_nodeType->eventOuts(); for (Route *r = d_routes; r; r = r->next()) for (ev = eventOuts.begin(); ev != eventOuts.end(); ++ev) if (strcmp((*ev)->name, r->fromEventOut()) == 0) { ismap = &(*ev)->thisIS; for (j = ismap->begin(); j != ismap->end(); ++j) { VrmlNode *n = d_scope->findNode((*j)->node->name()); if (n) n->addRoute((*j)->fieldName, r->toNode(), r->toEventIn() ); } } // Set IS'd field values in the implementation nodes to // the values specified in the instantiation. list::iterator ifld; for (ifld = d_fields.begin(); ifld != d_fields.end(); ++ifld) { VrmlField *value = (*ifld)->value; #if DEBUG cerr << d_nodeType->getName() << "::" << name() << " setting IS field " << (*ifld)->name; if (value) cerr << " to " << *value << endl; else cerr << " to null\n"; #endif if (! value) continue; if ((ismap = d_nodeType->getFieldISMap( (*ifld)->name )) != 0) { for (j = ismap->begin(); j != ismap->end(); ++j) { VrmlNode *n = d_scope->findNode((*j)->node->name()); #if DEBUG cerr << " on " << n->name() << "::" << (*j)->fieldName << endl; #endif if (n) n->setField( (*j)->fieldName, *value ); } } } } d_instantiated = true; } void VrmlNodeProto::addToScene(VrmlScene *s, const char *relUrl) { theSystem->debug("VrmlNodeProto::%s addToScene\n", name()); d_scene = s; // Make sure my nodes are here if (! d_instantiated) instantiate(); theSystem->debug("VrmlNodeProto::%s addToScene(%d nodes)\n", name(), d_nodes ? d_nodes->size() : 0); // ... and add the implementation nodes to the scene. if (d_nodes) { const char *rel = d_nodeType->url(); int j, n = d_nodes->size(); for (j=0; jget(j)->addToScene(s, rel ? rel : relUrl); } } void VrmlNodeProto::accumulateTransform( VrmlNode *n ) { // Make sure my nodes are here if (! d_instantiated) { theSystem->debug("VrmlNodeProto::%s accumTrans before instantiation\n", name()); instantiate(); } // ... and process the implementation nodes. if (d_nodes) { int i, j = d_nodes->size(); for (i=0; iget(i)->accumulateTransform(n); } } int VrmlNodeProto::size() { return d_nodes ? d_nodes->size() : 0; } VrmlNode *VrmlNodeProto::child(int index) { if (d_nodes && index >= 0 && index < d_nodes->size()) return (*d_nodes)[index]; return 0; } // Print the node type, instance vars ostream& VrmlNodeProto::printFields(ostream& os, int ) { os << "#VrmlNodeProto::printFields not implemented yet...\n"; return os; } // Use the first node to check the type VrmlNode *VrmlNodeProto::firstNode() const { return ((d_nodes && d_nodes->size()) ? d_nodes->get(0) : d_nodeType->firstNode()); } // These are passed along to the first implementation node of the proto. // If the first node is not present (EXTERNPROTO prior to retrieving the // the implementation), all tests fail. VrmlNodeAnchor* VrmlNodeProto::toAnchor() const { return firstNode() ? firstNode()->toAnchor() : 0; } VrmlNodeAppearance* VrmlNodeProto::toAppearance() const { return firstNode() ? firstNode()->toAppearance() : 0; } VrmlNodeAudioClip* VrmlNodeProto::toAudioClip() const { return firstNode() ? firstNode()->toAudioClip() : 0; } VrmlNodeChild* VrmlNodeProto::toChild() const { return firstNode() ? firstNode()->toChild() : 0; } VrmlNodeBackground* VrmlNodeProto::toBackground() const { return firstNode() ? firstNode()->toBackground() : 0; } VrmlNodeColor* VrmlNodeProto::toColor() const { return firstNode() ? firstNode()->toColor() : 0; } VrmlNodeCoordinate* VrmlNodeProto::toCoordinate() const { return firstNode() ? firstNode()->toCoordinate() : 0; } VrmlNodeFog* VrmlNodeProto::toFog() const { return firstNode() ? firstNode()->toFog() : 0; } VrmlNodeFontStyle* VrmlNodeProto::toFontStyle() const { return firstNode() ? firstNode()->toFontStyle() : 0; } VrmlNodeGeometry* VrmlNodeProto::toGeometry() const { return firstNode() ? firstNode()->toGeometry() : 0; } VrmlNodeGroup* VrmlNodeProto::toGroup() const { return firstNode() ? firstNode()->toGroup() : 0; } VrmlNodeInline* VrmlNodeProto::toInline() const { return firstNode() ? firstNode()->toInline() : 0; } VrmlNodeLight* VrmlNodeProto::toLight() const { return firstNode() ? firstNode()->toLight() : 0; } VrmlNodeMaterial* VrmlNodeProto::toMaterial() const { return firstNode() ? firstNode()->toMaterial() : 0; } VrmlNodeMovieTexture* VrmlNodeProto::toMovieTexture() const { return firstNode() ? firstNode()->toMovieTexture() : 0; } VrmlNodeNavigationInfo* VrmlNodeProto::toNavigationInfo() const { return firstNode() ? firstNode()->toNavigationInfo() : 0; } VrmlNodeNormal* VrmlNodeProto::toNormal() const { return firstNode() ? firstNode()->toNormal() : 0; } VrmlNodePlaneSensor* VrmlNodeProto::toPlaneSensor() const { return firstNode() ? firstNode()->toPlaneSensor() : 0; } VrmlNodePointLight* VrmlNodeProto::toPointLight() const { return firstNode() ? firstNode()->toPointLight() : 0; } VrmlNodeScript* VrmlNodeProto::toScript() const { return firstNode() ? firstNode()->toScript() : 0; } VrmlNodeSound* VrmlNodeProto::toSound() const { return firstNode() ? firstNode()->toSound() : 0; } VrmlNodeSpotLight* VrmlNodeProto::toSpotLight() const { return firstNode() ? firstNode()->toSpotLight() : 0; } VrmlNodeTexture* VrmlNodeProto::toTexture() const { return firstNode() ? firstNode()->toTexture() : 0; } VrmlNodeTextureCoordinate* VrmlNodeProto::toTextureCoordinate() const { return firstNode() ? firstNode()->toTextureCoordinate() : 0; } VrmlNodeTextureTransform* VrmlNodeProto::toTextureTransform() const { return firstNode() ? firstNode()->toTextureTransform() : 0; } VrmlNodeTimeSensor* VrmlNodeProto::toTimeSensor() const { return firstNode() ? firstNode()->toTimeSensor() : 0; } VrmlNodeTouchSensor* VrmlNodeProto::toTouchSensor() const { return firstNode() ? firstNode()->toTouchSensor() : 0; } VrmlNodeViewpoint* VrmlNodeProto::toViewpoint() const { return firstNode() ? firstNode()->toViewpoint() : 0; } // Larry VrmlNodeBox* VrmlNodeProto::toBox() const { return firstNode() ? firstNode()->toBox() : 0; } VrmlNodeCone* VrmlNodeProto::toCone() const { return firstNode() ? firstNode()->toCone() : 0; } VrmlNodeCylinder* VrmlNodeProto::toCylinder() const { return firstNode() ? firstNode()->toCylinder() : 0; } VrmlNodeDirLight* VrmlNodeProto::toDirLight() const { return firstNode() ? firstNode()->toDirLight() : 0; } VrmlNodeElevationGrid* VrmlNodeProto::toElevationGrid() const { return firstNode() ? firstNode()->toElevationGrid() : 0; } VrmlNodeExtrusion* VrmlNodeProto::toExtrusion() const { return firstNode() ? firstNode()->toExtrusion() : 0; } VrmlNodeIFaceSet* VrmlNodeProto::toIFaceSet() const { return firstNode() ? firstNode()->toIFaceSet() : 0; } VrmlNodeShape* VrmlNodeProto::toShape() const { return firstNode() ? firstNode()->toShape() : 0; } VrmlNodeSphere* VrmlNodeProto::toSphere() const { return firstNode() ? firstNode()->toSphere() : 0; } VrmlNodeSwitch* VrmlNodeProto::toSwitch() const { return firstNode() ? firstNode()->toSwitch() : 0; } VrmlNodeTransform* VrmlNodeProto::toTransform() const { return firstNode() ? firstNode()->toTransform() : 0; } VrmlNodeImageTexture* VrmlNodeProto::toImageTexture() const { return firstNode() ? firstNode()->toImageTexture() : 0; } VrmlNodePixelTexture* VrmlNodeProto::toPixelTexture() const { return firstNode() ? firstNode()->toPixelTexture() : 0; } // void VrmlNodeProto::render(Viewer *viewer) { if (! d_instantiated) { theSystem->debug("VrmlNodeProto::%s render before instantiation\n", name()); instantiate(); } if (d_viewerObject) viewer->insertReference(d_viewerObject); else if (d_nodes) { d_viewerObject = viewer->beginObject( name() ); // render the nodes with the new values int n = d_nodes->size(); for (int j = 0; jget(j)->render(viewer); viewer->endObject(); } clearModified(); } void VrmlNodeProto::eventIn(double timeStamp, const char *eventName, const VrmlField *fieldValue) { if (! d_instantiated) { theSystem->debug("VrmlNodeProto::%s eventIn before instantiation\n", name()); instantiate(); } const char *origEventName = eventName; if ( strncmp(eventName, "set_", 4) == 0 ) eventName += 4; #if 0 cerr << "eventIn " << nodeType()->getName() << "::" << name() << "." << origEventName << " " << (*fieldValue) << endl; #endif EventDispatchList::iterator i; for (i=d_eventDispatch.begin(); i != d_eventDispatch.end(); ++i) { if ( strcmp(eventName, (*i)->name) == 0 || strcmp(origEventName, (*i)->name) == 0 ) { VrmlNodeType::ISMap *ismap = & ((*i)->ismap); VrmlNodeType::ISMap::iterator j; for (j = ismap->begin(); j != ismap->end(); ++j) (*j)->node->eventIn( timeStamp, (*j)->fieldName, fieldValue ); return; } } // Let the generic code handle errors. VrmlNode::eventIn( timeStamp, origEventName, fieldValue ); } VrmlNodeProto::NameValueRec *VrmlNodeProto::findField(const char *fieldName) const { list::const_iterator i; for (i = d_fields.begin(); i != d_fields.end(); ++i) { NameValueRec *nv = *i; if (nv != NULL && strcmp(nv->name, fieldName) == 0) { return nv; } } return NULL; } // Set the value of one of the node fields (creates the field if // it doesn't exist - is that necessary?...) void VrmlNodeProto::setField(const char *fieldName, const VrmlField &fieldValue) { NameValueRec *nv = findField(fieldName); if (! nv) { nv = new NameValueRec; nv->name = strdup(fieldName); d_fields.push_front(nv); } else { delete nv->value; } nv->value = fieldValue.clone(); } const VrmlField *VrmlNodeProto::getField(const char *fieldName) const { NameValueRec *nv = findField(fieldName); if (nv) return nv->value; return 0; } VrmlNodeLOD* VrmlNodeProto::toLOD() const { return firstNode() ? firstNode()->toLOD() : 0; } VrmlNodeOrientationInt* VrmlNodeProto::toOrientationInt() const { return firstNode() ? firstNode()->toOrientationInt() : 0; } VrmlNodePositionInt* VrmlNodeProto::toPositionInt() const { return firstNode() ? firstNode()->toPositionInt() : 0; } VrmlNodeScalarInt* VrmlNodeProto::toScalarInt() const { return firstNode() ? firstNode()->toScalarInt() : 0; }