/* * Scene.cpp * * Copyright (C) 1999 Stephen F. White * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include "stdafx.h" #ifndef WIN32 # include #endif #include "Util.h" #ifdef HAVE_LIBZ extern "C" { # include "zlib.h" } #endif #include "swt.h" #include "Matrix.h" #include "Scene.h" #include "SceneView.h" #include "FieldValue.h" #include "FieldCommand.h" #include "MFieldCommand.h" #include "CommandList.h" #include "RouteCommand.h" #include "UnRouteCommand.h" #include "MoveCommand.h" #include "Node.h" #include "SFNode.h" #include "SFTime.h" #include "MFNode.h" #include "Proto.h" #include "parser.h" #include "Path.h" #include "Field.h" #include "EventIn.h" #include "EventOut.h" #include "ExposedField.h" #include "URL.h" #include "FontInfo.h" #include "DuneApp.h" #include "NodeAnchor.h" #include "NodeAppearance.h" #include "NodeAudioClip.h" #include "NodeBackground.h" #include "NodeBillboard.h" #include "NodeBox.h" #include "NodeCollision.h" #include "NodeColor.h" #include "NodeColorInterpolator.h" #include "NodeContour2D.h" #include "NodeCone.h" #include "NodeCoordinate.h" #include "NodeCoordinateDeformer.h" #include "NodeCoordinateInterpolator.h" #include "NodeCylinder.h" #include "NodeCylinderSensor.h" #include "NodeDirectionalLight.h" #include "NodeElevationGrid.h" #include "NodeExtrusion.h" #include "NodeFog.h" #include "NodeFontStyle.h" #include "NodeGroup.h" #include "NodeImageTexture.h" #include "NodeIndexedFaceSet.h" #include "NodeIndexedLineSet.h" #include "NodeInline.h" #include "NodeInlineLoadControl.h" #include "NodeLOD.h" #include "NodeLoadSensor.h" #include "NodeMaterial.h" #include "NodeMovieTexture.h" #include "NodeNavigationInfo.h" #include "NodeNormal.h" #include "NodeNormalInterpolator.h" #include "NodeNurbsCurve.h" #include "NodeNurbsCurve2D.h" #include "NodeNurbsGroup.h" #include "NodeNurbsSurface.h" #include "NodeNurbsTextureSurface.h" #include "NodeNurbsPositionInterpolator.h" #include "NodeOrientationInterpolator.h" #include "NodePixelTexture.h" #include "NodePlaneSensor.h" #include "NodePointLight.h" #include "NodePointSet.h" #include "NodePolyline2D.h" #include "NodePositionInterpolator.h" #include "NodeProximitySensor.h" #include "NodeScalarInterpolator.h" #include "NodeShape.h" #include "NodeSphere.h" #include "NodeSphereSensor.h" #include "NodeSpotLight.h" #include "NodeSound.h" #include "NodeSuperEllipsoid.h" #include "NodeSuperExtrusion.h" #include "NodeSuperShape.h" #include "NodeSwitch.h" #include "NodeText.h" #include "NodeTextureCoordinate.h" #include "NodeTextureTransform.h" #include "NodeTimeSensor.h" #include "NodeTouchSensor.h" #include "NodeTrimmedSurface.h" #include "NodeTransform.h" #include "NodeViewpoint.h" #include "NodeVisibilitySensor.h" #include "NodeWorldInfo.h" #include "NodeComment.h" #define ARRAY_SIZE(v) ((int) (sizeof(v) / sizeof(v[0]))) #define PICK_BUFFER_SIZE 1024 #define PICK_REGION_SIZE 2.5 enum { PICKED_NODE, PICKED_HANDLE, PICKED_3DCURSOR }; Scene::Scene() { _numSymbols = 0; _headlight = true; _numLights = 0; _selection = NULL; _selectedHandle = -1; _oldSelectedHandle = _selectedHandle; _transformMode = new TransformMode( (TMode) TheApp->GetIntPreference("TransformMode",TM_HOVER), (TDimension) TheApp->GetIntPreference("TransformDimension",TM_3D), (T2axes) TheApp->GetIntPreference("Transform2Axes",TM_NEAR_FAR)); if (TheApp->getMaxNumberAxesInputDevices()<=4) if ((_transformMode->tmode == TM_6D) || (_transformMode->tmode == TM_6D)) _transformMode->tmode = TM_HOVER; if (TheApp->getMaxNumberAxesInputDevices()<3) if (_transformMode->tmode == TM_ROCKET) _transformMode->tmode = TM_HOVER; if (TheApp->getMaxNumberAxesInputDevices()<2) if (_transformMode->tmode >= TM_6D) _transformMode->tmode = TM_TRANSLATE; _unmodified = NULL; _pureVRML97 = false; _extraModifiedFlag = false; _protos["Anchor"] = new ProtoAnchor(this); _protos["Appearance"] = new ProtoAppearance(this); _protos["AudioClip"] = new ProtoAudioClip(this); _protos["Background"] = new ProtoBackground(this); _protos["Billboard"] = new ProtoBillboard(this); _protos["Box"] = new ProtoBox(this); _protos["Collision"] = new ProtoCollision(this); _protos["Color"] = new ProtoColor(this); _protos["ColorInterpolator"] = new ProtoColorInterpolator(this); _protos["Contour2D"] = new ProtoContour2D(this); _protos["Cone"] = new ProtoCone(this); _protos["Coordinate"] = new ProtoCoordinate(this); _protos["CoordinateDeformer"] = new ProtoCoordinateDeformer(this); _protos["CoordinateInterpolator"] = new ProtoCoordinateInterpolator(this); _protos["Cylinder"] = new ProtoCylinder(this); _protos["CylinderSensor"] = new ProtoCylinderSensor(this); _protos["DirectionalLight"] = new ProtoDirectionalLight(this); _protos["ElevationGrid"] = new ProtoElevationGrid(this); _protos["Extrusion"] = new ProtoExtrusion(this); _protos["Fog"] = new ProtoFog(this); _protos["FontStyle"] = new ProtoFontStyle(this); _protos["Group"] = new ProtoGroup(this); _protos["ImageTexture"] = new ProtoImageTexture(this); _protos["IndexedFaceSet"] = new ProtoIndexedFaceSet(this); _protos["IndexedLineSet"] = new ProtoIndexedLineSet(this); _protos["Inline"] = new ProtoInline(this); _protos["InlineLoadControl"] = new ProtoInlineLoadControl(this); _protos["LoadSensor"] = new ProtoLoadSensor(this); _protos["LOD"] = new ProtoLOD(this); _protos["Material"] = new ProtoMaterial(this); _protos["MovieTexture"] = new ProtoMovieTexture(this); _protos["NavigationInfo"] = new ProtoNavigationInfo(this); _protos["Normal"] = new ProtoNormal(this); _protos["NormalInterpolator"] = new ProtoNormalInterpolator(this); _protos["NurbsCurve"] = new ProtoNurbsCurve(this); _protos["NurbsCurve2D"] = new ProtoNurbsCurve2D(this); _protos["NurbsGroup"] = new ProtoNurbsGroup(this); _protos["NurbsPositionInterpolator"] = new ProtoNurbsPositionInterpolator(this); _protos["NurbsSurface"] = new ProtoNurbsSurface(this); _protos["NurbsTextureSurface"] = new ProtoNurbsTextureSurface(this); _protos["OrientationInterpolator"] = new ProtoOrientationInterpolator(this); _protos["PixelTexture"] = new ProtoPixelTexture(this); _protos["PlaneSensor"] = new ProtoPlaneSensor(this); _protos["PointLight"] = new ProtoPointLight(this); _protos["PointSet"] = new ProtoPointSet(this); _protos["Polyline2D"] = new ProtoPolyline2D(this); _protos["PositionInterpolator"] = new ProtoPositionInterpolator(this); _protos["ProximitySensor"] = new ProtoProximitySensor(this); _protos["ScalarInterpolator"] = new ProtoScalarInterpolator(this); _protos["Shape"] = new ProtoShape(this); _protos["Sound"] = new ProtoSound(this); _protos["Sphere"] = new ProtoSphere(this); _protos["SphereSensor"] = new ProtoSphereSensor(this); _protos["SpotLight"] = new ProtoSpotLight(this); _protos["SuperEllipsoid"] = new ProtoSuperEllipsoid(this); _protos["SuperExtrusion"] = new ProtoSuperExtrusion(this); _protos["SuperShape"] = new ProtoSuperShape(this); _protos["Switch"] = new ProtoSwitch(this); _protos["Text"] = new ProtoText(this); _protos["TextureCoordinate"] = new ProtoTextureCoordinate(this); _protos["TextureTransform"] = new ProtoTextureTransform(this); _protos["TimeSensor"] = new ProtoTimeSensor(this); _protos["TouchSensor"] = new ProtoTouchSensor(this); _protos["Transform"] = new ProtoTransform(this); _protos["TrimmedSurface"] = new ProtoTrimmedSurface(this); _protos["Viewpoint"] = new ProtoViewpoint(this); _protos["VisibilitySensor"] = new ProtoVisibilitySensor(this); _protos["WorldInfo"] = new ProtoWorldInfo(this); _protos["#"] = new ProtoComment(this); _numberBuildinProtos = getAllNodeNames()->size(); _root = createNode("Group"); _root->ref(); _rootIndex = ((NodeGroup *) _root)->children_Index(); _running = false; _recording = false; _tempSave = false; _defaultViewpoint = (NodeViewpoint *) createNode("Viewpoint"); _defaultViewpoint->ref(); _currentViewpoint = _defaultViewpoint; _currentViewpoint->ref(); _numProtoNames = 0; _numProtoDefinitions = 0; _navigationMode = true; _routeList.Init(); _selectlevel=1; _hasFocus=false; _viewOfLastSelection=NULL; _selection_is_in_scene=false; _URL=""; _errorLineNumber = -1; _obj3dCursor = gluNewQuadric(); _multipleUndoTop = -1; _backupCommandList = NULL; _use3dCursor = false; _nodesWithExternProto.append("Contour2D"); _nodesWithExternProto.append("CoordinateDeformer"); _nodesWithExternProto.append("InlineLoadControl"); _nodesWithExternProto.append("LoadSensor"); _nodesWithExternProto.append("NurbsCurve"); _nodesWithExternProto.append("NurbsCurve2D"); _nodesWithExternProto.append("NurbsGroup"); _nodesWithExternProto.append("NurbsPositionInterpolator"); _nodesWithExternProto.append("NurbsSurface"); _nodesWithExternProto.append("NurbsTextureSurface"); _nodesWithExternProto.append("Polyline2D"); _nodesWithExternProto.append("TrimmedSurface"); _nodesWithExternProto.append("SuperEllipsoid"); _nodesWithExternProto.append("SuperExtrusion"); _nodesWithExternProto.append("SuperShape"); _protoPrefix = NULL; _externProtoWarning = true; TheApp->readProtoLibrary(this); _isParsing = false; } Scene::~Scene() { int i; TheApp->SetIntPreference("TransformMode",_transformMode->tmode); TheApp->SetIntPreference("TransformDimension",_transformMode->tdimension); TheApp->SetIntPreference("Transform2Axes",_transformMode->t2axes); while (!_undoStack.empty()) delete _undoStack.pop(); while (!_redoStack.empty()) delete _redoStack.pop(); _defaultViewpoint->unref(); _currentViewpoint->unref(); delete _selection; _selection = NULL; _root->unref(); ProtoMap::Chain::Iterator *j; for ( i = 0; i < _protos.width(); i++) { for ( j = _protos.chain(i).first(); j != NULL; j = j->next()) { delete j->item()->getData(); } } for (i = 0; i < _fonts.size(); i++) { delete _fonts[i]; } gluDeleteQuadric(_obj3dCursor); } void Scene::def(const char *nodeName, Node *value) { if (value) { _nodeMap[nodeName] = value; value->setName(nodeName); } } void Scene::undef(MyString nodeName) { if (nodeName!="") _nodeMap[nodeName] = NULL; } Node *Scene::use(const char *nodeName) { return _nodeMap[nodeName]; } int Scene::addSymbol(MyString s) { int &id = _symbols[s]; if (id == 0) { id = _numSymbols++; _symbolList[id] = s; } return id; } // store begin of multiple undo commands // this can be used for later optimization void Scene::startMultipleUndo() { _multipleUndoTop = _undoStack.getTop(); } // optimize multiple undo commands // read stack and throw out all unneeded commands // remember: backup commands contain setField(node, field) information // a setField(node, field) is erased by a later setField(node, field) // WARNING ! void Scene::optimizeMultipleUndo() { int i; // Take care, that the optimization mechanism for the commandStack // do not try to optimize to handle already deleted nodes // prove if all commands on the undoStack till _multipleUndoTop // are not MOVE_COMMANDs (but FIELD_COMMANDs) if (_multipleUndoTop == -1) return; for (i = _undoStack.getTop(); i > _multipleUndoTop; i--) if (_undoStack.peek(i)->getType() != FIELD_COMMAND) { _multipleUndoTop = -1; return; } CommandStack tmpStack; while (_undoStack.getTop() > _multipleUndoTop) { if (_undoStack.empty()) { _multipleUndoTop = -1; return; } Command *stackCommand = _undoStack.pop(); bool storeStackCommandToTmpStack = true; for (i = tmpStack.getTop(); i > 0; i--) { Command *tmpCommand = tmpStack.peek(i); if ( (stackCommand->getType() == FIELD_COMMAND) && (tmpCommand->getType() == FIELD_COMMAND)) { FieldCommand *fieldCommand = (FieldCommand *)tmpCommand; FieldCommand *fstackCommand = (FieldCommand *)stackCommand; if ( (fstackCommand->getNode() == fieldCommand->getNode()) && (fstackCommand->getField() == fieldCommand->getField()) ){ // there is already a command for this node and field // in the tmpstack, ignore this command storeStackCommandToTmpStack = false; break; } } } if (storeStackCommandToTmpStack) tmpStack.push(stackCommand); } // store content of tmpStack back to _undoStack while (!tmpStack.empty()) _undoStack.push(tmpStack.pop()); _multipleUndoTop = -1; } const MyString & Scene::getSymbol(int id) const { return _symbolList[id]; } void Scene::setNodes(NodeList *nodes) { ((NodeGroup *)_root)->children(new MFNode(nodes)); } void Scene::addNodes(Node *target, NodeList *nodes) { if (target == NULL) { _root->addFieldNodeList(_rootIndex, nodes); } else target->addFieldNodeList(-1, nodes); scanForInlines(nodes); } void Scene::scanForInlines(NodeList *nodes) { if (nodes == NULL) return; for (int i=0; i < nodes->size(); i++) { Node *node = nodes->get(i); if ((node->getType() == NODE_INLINE) || (node->getType() == NODE_INLINE_LOAD_CONTROL)) { if (TheApp->loadNewInline()) readInline((NodeInline *)node); } else { for (int j = 0; j < node->getProto()->getNumFields(); j++) { FieldValue *field = node->getField(j); if (field->getType() == MFNODE) { NodeList *childList = ((MFNode *) field)->getValues(); scanForInlines(childList); } } } } } void Scene::readInline(NodeInline *node) { MFString *urls = node->url(); for (int j = 0; j < urls->getSize(); j++) { if (urls->getValue(j).length() == 0) continue; URL url(getURL(), urls->getValue(j)); MyString path; if (Download(url, &path)) { struct stat fileStat; const char *filename = path; if (stat(filename, &fileStat) == 0) { if (S_ISREG(fileStat.st_mode)) { URL importURL; importURL.FromPath(filename); TheApp->setImportURL(importURL); FILE *file = fopen(filename, "r"); parse(file, false, node); fclose(file); break; } } } } } int Scene::writeRouteStrings(int f) { if (_routeList.size()!=0) { for (List::Iterator* routepointer = _routeList.first(); routepointer != NULL; routepointer = routepointer->next() ) { RET_ONERROR( mywritestr(f ,(const char*) routepointer->item()) ) RET_ONERROR( mywritestr(f ,"\n") ) TheApp->incSelectionLinenumber(); } RET_ONERROR( mywritestr(f ,"\n") ) TheApp->incSelectionLinenumber(); _routeList.removeAll(); } return 0; } int Scene::writeExternProto(int f, char* protoName) { // search if EXTERNPROTO already exist int i; bool foundProto = false; for (i = 0;i < _numProtoNames;i++) if (strcmp((const char*)_protoNames[i], protoName)==0) foundProto = true; if (!foundProto) { // write EXTERNPROTO const NodeList *nodes = getNodes(); for (i = 0; i < nodes->size(); i++) { Node *node = nodes->get(i); if (node->isInScene(this)) { const char *nodeName = node->getProto()->getName(); if (strcmp(nodeName, protoName)==0) { RET_ONERROR( node->writeEXTERNPROTO(f) ) RET_ONERROR( mywritestr(f ,"\n\n") ) TheApp->incSelectionLinenumber(2); break; } } } } return 0; } bool avoidProto(MyString name) { if (stringncmp(name, "Nurbs") == 0) return true; if (stringncmp(name, "Super") == 0) return true; if (strcmp(name, "Contour2D") == 0) return true; if (strcmp(name, "CoordinateDeformer") == 0) return true; if (strcmp(name, "Polyline2D") == 0) return true; if (strcmp(name, "TrimmedSurface") == 0) return true; return false; } int Scene::write(int f, const char *url, bool tempSave, bool pureVRML97) { _newURL = url; _tempSave = tempSave; int i; _pureVRML97 = pureVRML97; getNodes()->clearFlag(NODE_FLAG_DEFED); getNodes()->clearFlag(NODE_FLAG_TOUCHED); // remove multiple identical ProtoDefinitions smuggled in by Inline nodes for (i = 0;i < _numProtoDefinitions;i++) { for (int j = 0;j < _numProtoDefinitions;j++) if (i != j) if (strcmp((const char*)_protoDefinitions[i], (const char*)_protoDefinitions[j]) == 0) _protoDefinitions[j] = ""; } RET_ONERROR( mywritestr(f ,"#VRML V2.0 utf8\n\n") ) TheApp->incSelectionLinenumber(2); for (i = 0;i < _numProtoDefinitions;i++) { if (pureVRML97 && avoidProto(_protoNames[i])) { bool found = false; for (int j = 0; j < _nodesWithExternProto.size(); j++) if (strcmp((const char *)_protoNames[i], _nodesWithExternProto[j]) == 0) found = true; if (found == true) continue; } if (getProtoPrefix() != NULL) { MyString prefix = getProtoPrefix(); bool isPrefixProto = false; for (int j = 0; j < _numProtoDefinitions; j++) { if (strncmp(_protoNames[j], prefix, prefix.length()) == 0) if (strcmp(_protoNames[j], _protoNames[i]) == 0) isPrefixProto = true; } if (!isPrefixProto) { for (int j = 0; j < _numProtoDefinitions; j++) { if (strncmp(_protoNames[j], prefix, prefix.length()) == 0) _protoDefinitions[i].gsubOnce( _protoNames[j] + prefix.length(), _protoNames[j]); } } } RET_ONERROR( _protoDefinitions[i].write(f) ) // count end of line characters in protodefinitions char* string=(char*) ((const char*) _protoDefinitions[i]); while ((string=strchr(string, '\n')) !=NULL) { TheApp->incSelectionLinenumber(); string++; } RET_ONERROR( mywritestr(f ,"\n\n") ) TheApp->incSelectionLinenumber(2); } for (int j = 0; j < _nodesWithExternProto.size(); j++) { // do not write EXTERN PROTOs for Nurbs Nodes when using pureVRML97 if ((!pureVRML97) || (!avoidProto(_nodesWithExternProto[j]))) RET_ONERROR( writeExternProto(f, _nodesWithExternProto[j]) ) } _nodes.clearFlag(NODE_FLAG_TOUCHED); NodeList *childList = ((NodeGroup *)getRoot())->children()->getValues(); for (i = 0; i < childList->size(); i++) { RET_ONERROR( childList->get(i)->write(f, 0) ) } if ((!tempSave) && (!pureVRML97)) { _unmodified = _undoStack.empty() ? (Command *) NULL : _undoStack.peek(); _extraModifiedFlag = false; _URL = url; } RET_ONERROR( writeRouteStrings(f) ) return(0); } void Scene::addProto(MyString name, Proto *value) { if (_protos[name] == NULL) { _protos[name] = value; } else { delete value; } } Proto * Scene::getProto(MyString name) { return _protos[name]; } // static bool Scene::validRoute(Node *src, int eventOut, Node *dst, int eventIn) { bool onlyOneConnectAnything = false; // "connect anything" route of ScriptNode (eventOut will be created) if (src->getType() == NODE_SCRIPT) if (eventOut == src->getProto()->getNumEventOuts()) onlyOneConnectAnything = true; // "connect anything" route of ScriptNode (eventIn will be created) if (dst->getType() == NODE_SCRIPT) if (eventIn == dst->getProto()->getNumEventIns()) if (onlyOneConnectAnything) onlyOneConnectAnything = false; else onlyOneConnectAnything = true; if (onlyOneConnectAnything) return true; if (eventOut < 0 || eventOut >= src->getProto()->getNumEventOuts()) return false; if (eventIn < 0 || eventIn >= dst->getProto()->getNumEventIns()) return false; if (src->getProto()->getEventOut(eventOut)->getType() != dst->getProto()->getEventIn(eventIn)->getType()) return false; return true; } bool Scene::addRoute(Node *src, int eventOut, Node *dst, int eventIn) { if (!validRoute(src, eventOut, dst, eventIn)) return false; RouteUpdate hint(src, eventOut, dst, eventIn); src->addOutput(eventOut, dst, eventIn); dst->addInput(eventIn, src, eventOut); // try to copy a exposedField value or field of a eventIn // to the first value of a Interpolator int field = -1; // is this dst field an ExposedField? ExposedField *e = dst->getProto()->getEventIn(eventIn)->getExposedField(); if (e) field = e->getField(); else { // is this dst field an EventIn connected to a Field ? field = dst->getProto()->getEventIn(eventIn)->getField(); } if (field != -1) { Interpolator *interp = findUpstreamInterpolator(dst, field); if (interp) { if (interp->getNumKeys() == 0) { FieldValue *value = dst->getField(field); if (value) interp->recordKey(value, true); } } } UpdateViews(NULL, UPDATE_ADD_ROUTE, (Hint *) &hint); return true; } void Scene::deleteRoute(Node *src, int eventOut, Node *dst, int eventIn) { if (eventOut < 0 || eventOut >= src->getProto()->getNumEventOuts()) return; if (eventIn < 0 || eventIn >= dst->getProto()->getNumEventIns()) return; RouteUpdate hint(src, eventOut, dst, eventIn); src->removeOutput(eventOut, dst, eventIn); dst->removeInput(eventIn, src, eventOut); UpdateViews(NULL, UPDATE_DELETE_ROUTE, (Hint *) &hint); } void Scene::errorf(const char *fmt, ...) { va_list ap; char buf[1024], buf2[1024]; va_start(ap, fmt); vsprintf(buf, fmt, ap); sprintf(buf2, "%d: %s", lineno, buf); _compileErrors += buf2; } void Scene::invalidNode(const char *name) { errorf("invalid DEF name \"%s\"\n", name); } void Scene::invalidField(const char *node, const char *field) { errorf("node \"%s\" has no field \"%s\", ignored", node, field); } const char * Scene::parse(FILE *f, bool protoLibrary, Node* target) { #ifdef HAVE_LIBZ inputFile = gzdopen(fileno(f),"rb"); #else inputFile = f; #endif isInProtoLibrary = protoLibrary ? 1 : 0; scene = this; targetNode = target; lineno = 1; _compileErrors = ""; _isParsing = true; yyparse(); _isParsing = false; return _compileErrors; } void Scene::add(Command *cmd) { _undoStack.push(cmd); while (!_redoStack.empty()) { delete _redoStack.pop(); } } void Scene::execute(Command *cmd) { cmd->execute(); add(cmd); } void Scene::backupFieldsStart(void) { // check for forbidden recurive usage or not missing backupFieldsDone() assert(_backupCommandList == NULL); _backupCommandList = new CommandList(); } void Scene::backupFieldsAppend(Node *node, int field) { if (isRecording()) { Interpolator *interp = findUpstreamInterpolator(node, field); if (interp) { interp->backup(_backupCommandList); } } _backupCommandList->append(new FieldCommand(node, field)); } void Scene::backupFieldsDone(void) { add(_backupCommandList); _backupCommandList = NULL; } void Scene::backupField(Node *node, int field) { backupFieldsStart(); backupFieldsAppend(node, field); backupFieldsDone(); } Interpolator * Scene::findUpstreamInterpolator(Node *node, int field) const { // is this field an ExposedField? int eventIn = -1; Field *f = node->getProto()->getField(field); ExposedField *e = f->getExposedField(); if (e) eventIn = e->getEventIn(); else if (f->getEventIn() != -1) eventIn = f->getEventIn(); if (eventIn != -1) { const SocketList::Iterator *i; // check for interpolator routed to the corresponding EventIn; for (i = node->getInput(eventIn).first(); i != NULL; i = i->next()) { if (i->item()._node->getNodeClass() & INTERPOLATOR_NODE) return ((Interpolator *) i->item()._node); } } return NULL; } void Scene::setField(Node *node, int field, FieldValue *value) { if (isRecording()) { Interpolator *interp = findUpstreamInterpolator(node, field); if (interp) { interp->recordKey(value,isRunning()); } } node->setField(field, value); if (node->getProto() == _protos["TimeSensor"]) { NodeTimeSensor *nodeTimeSensor = (NodeTimeSensor *) node; nodeTimeSensor->updateStart(field, value, swGetCurrentTime()); } OnFieldChange(node, field); } void Scene::undo() { if (_undoStack.empty()) { assert(false); return; } Command *change = _undoStack.pop(); change->undo(); _redoStack.push(change); } void Scene::redo() { if (_redoStack.empty()) { assert(false); return; } Command *change = _redoStack.pop(); change->execute(); _undoStack.push(change); } void Scene::drawScene(bool pick, int x, int y) { GLint v[4]; float aspect; int i; glGetIntegerv(GL_VIEWPORT, v); if (v[3]) aspect = (GLfloat)v[2]/v[3]; else // don't divide by zero, not that we should ever run into that... aspect = 1.0f; _numLights = 0; _headlight = true; glMatrixMode(GL_PROJECTION); glLoadIdentity(); float pickRegionSize = PICK_REGION_SIZE * TheApp->GetHandleSize(); if (pick) gluPickMatrix(x, y, pickRegionSize, pickRegionSize, v); float fieldOfView = RAD2DEG(getCamera()->fieldOfView()->getValue()); if (TheApp->hasFixFieldOfView()) fieldOfView = TheApp->getFixFieldOfView(); gluPerspective(fieldOfView, aspect, TheApp->GetNearClippingPlaneDist(), TheApp->GetFarClippingPlaneDist()); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.0F, 0.0F, 0.0F, 1.0F); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glEnable(GL_LIGHTING); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); if (TheApp->GetRenderFasterWorse()) { glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST); glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST); glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST); } // use later // glDepthFunc(GL_ALWAYS); // for a extra pass, which renders only transparent shapes // no scene ambient light, please GLint zero[4] = { 0, 0, 0, 0 }; glLightModeliv(GL_LIGHT_MODEL_AMBIENT, zero); // first pass: pre-draw traversal // enable PointLights and SpotLights; pick up ViewPoints, Fogs. // Backgrounds and TimeSensors glPushMatrix(); applyCamera(); _viewpoints.resize(0); _fogs.resize(0); _backgrounds.resize(0); _timeSensors.resize(0); _root->preDraw(); if (_fogs.size() > 0) ((NodeFog *) _fogs[0])->apply(); glPopMatrix(); // second pass: main drawing traversal glPushMatrix(); if (_headlight) enableHeadlight(); glPushName(PICKED_NODE); if (_backgrounds.size() > 0) { glPushName(0); // FIXME: can't pick backgrounds, yet ((NodeBackground *) _backgrounds[0])->apply(); glPopName(); } applyCamera(); _root->draw(); for (i = 0; i < _numLights; i++) { glDisable((GLenum) (GL_LIGHT0 + i)); } _numLights = 0; glDisable(GL_FOG); glPopMatrix(); // post-draw phase // draw handles for all nodes in the current selection path glPushMatrix(); enableHeadlight(); applyCamera(); glLoadName(PICKED_HANDLE); drawHandles(); glPopName(); glDisable(GL_LIGHT0); glPopMatrix(); } void Scene::draw3dCursor(int x, int y) { if (!use3dCursor()) return; float objX; float objY; float objZ; float eyeposition=0; float eyeangle=0; float nearPlane=TheApp->GetNearClippingPlaneDist(); glPushMatrix(); unProjectPoint(x, y, 0, &objX, &objY, &objZ); if (TheApp->useStereo()) { // inexact "toe in" stereo method if (TheApp->getEyeMode()==EM_RIGHT) { eyeposition= - TheApp->getEyeHalfDist(); eyeangle= - TheApp->getEyeAngle(); } else if (TheApp->getEyeMode()==EM_LEFT) { eyeposition= + TheApp->getEyeHalfDist(); eyeangle= + TheApp->getEyeAngle(); } } glTranslatef(-eyeposition, 0, 0); glRotatef(-eyeangle, 0,1,0); glPushAttrib(GL_ENABLE_BIT); glDisable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); glEnable(GL_LINE_SMOOTH); glDisable(GL_BLEND); glLineWidth(TheApp->Get3dCursorWidth()); glBegin(GL_LINE_STRIP); if (TheApp->isAnaglyphStereo()) Util::myGlColor3f(1, 1, 1); else Util::myGlColor3f(0, 1, 0); glVertex3f(objX * 1.0/nearPlane, -objY * 1.0/nearPlane, objZ - 1); glVertex3f(objX * 1.0/nearPlane, -objY * 1.0/nearPlane, objZ - 1 - TheApp->Get3dCursorLength()); glEnd(); glEnable(GL_LIGHTING); glLineWidth(1); glPopAttrib(); glPopMatrix(); } Path * Scene::pick(int x, int y) { GLuint pickBuffer[PICK_BUFFER_SIZE]; glSelectBuffer(PICK_BUFFER_SIZE, pickBuffer); glRenderMode(GL_SELECT); glInitNames(); TheApp->setEyeMode(EM_NONE); drawScene(true, x, y); int hits = glRenderMode(GL_RENDER); Path *path = NULL; if (hits) path = processHits(hits, pickBuffer); if (path != NULL) { return path; } else { return new Path(NULL, 0, this); } } Path * Scene::processHits(unsigned hits, GLuint *pickBuffer) { GLuint *path = NULL; int pathLen = 0; GLuint depth = UINT_MAX; int selectedLevel=0; _selectedHandle = -1; // _oldSelectedHandle = _selectedHandle; for (unsigned i = 0; i < hits; i++) { unsigned numNames = *pickBuffer++; unsigned minDepth = *pickBuffer++; /* unsigned maxDepth = * */ pickBuffer++; if (*pickBuffer == PICKED_NODE) { if (_selectedHandle == -1 || minDepth < depth) { path = pickBuffer + 1; pathLen = numNames - 1; depth = minDepth; // write here code to avoid the selection of the thing most away from user // printf("selectedLevel %d _selectlevel %d\n",selectedLevel+1,_selectlevel); // if (selectedLevel++==_selectlevel) // break; } } else if (*pickBuffer == PICKED_HANDLE) { path = pickBuffer + 1; pathLen = numNames - 2; _selectedHandle = pickBuffer[numNames-1]; _oldSelectedHandle = _selectedHandle; depth = minDepth; } else if (*pickBuffer == PICKED_3DCURSOR) { return NULL; } else { return NULL; // assert(false); } pickBuffer += numNames; } assert( path != NULL ); return new Path((int *) path, pathLen, this); } void Scene::transform(const Path *path) { assert(path != NULL); applyCamera(); const NodeList *nodes = path->getNodes(); int size = nodes->size() - 1; for (int i = 0; i < size; i++) { nodes->get(i)->transform(); } } // search for a Transform node in a path // return new path to Transform node or NULL if not found Path* Scene::searchTransform(void) { Path* transform=new Path(*_selection); if (transform != NULL) { if (transform->getNode()->getType() == NODE_TRANSFORM) return transform; while (transform->getNode() != _root) { Path* old_transform=transform; transform=newPath(transform->getNode()->getParent()); if (transform->getNode()->getType() == NODE_TRANSFORM) break; // delete old_transform; // bug: deleting a path cause a crash } if (transform->getNode()==_root) transform=NULL; } return transform; } void Scene::drawHandles() { glDisable(GL_DEPTH_TEST); switch (TheApp->GetHandleMode()) { case HM_NONE: break; case HM_TREE: if (_selection != NULL) { glPushMatrix(); int len = _selection->getPathLen(); const int *path = _selection->getPath(); Node *node = _root; for (int i = 0; i < len;) { int field = path[i++]; glPushName(field); FieldValue *value = node->getField(field); if (i >= len) { break; } glPushName(path[i]); if (value->getType() == SFNODE) { node = ((SFNode *) value)->getValue(); i++; } else if (value->getType() == MFNODE) { node = ((MFNode *) value)->getValue(path[i++]); } else { assert(false); } node->drawHandles(); node->transform(); } glPopMatrix(); } break; case HM_SELECTED: if (_selection != NULL) { glPushMatrix(); int len = _selection->getPathLen(); const int *path = _selection->getPath(); Node *node = _root; Node *handlenode = _root; Node *lastnode = _root; { for (int i = 0; i < len;) { int field = path[i++]; FieldValue *value = node->getField(field); if (i >= len) { break; } if (value->getType() == SFNODE) { node = ((SFNode *) value)->getValue(); i++; } else if (value->getType() == MFNODE) { node = ((MFNode *) value)->getValue(path[i++]); } else { assert(false); } /* search last transform node in path */ if (node->getType() == NODE_TRANSFORM) handlenode=node; } } lastnode=node; node = _root; { for (int i = 0; i < len;) { int field = path[i++]; glPushName(field); FieldValue *value = node->getField(field); if (i >= len) { break; } glPushName(path[i]); if (value->getType() == SFNODE) { node = ((SFNode *) value)->getValue(); i++; } else if (value->getType() == MFNODE) { node = ((MFNode *) value)->getValue(path[i++]); } else { assert(false); } /* display last transform node in path */ if ((node == handlenode) || (node==lastnode)) node->drawHandles(); node->transform(); } } glPopMatrix(); } break; case HM_ALL: drawHandlesRec(_root); break; } } void Scene::drawHandlesRec(Node *node) const { int numFields = node->getProto()->getNumFields(); glPushMatrix(); node->drawHandles(); node->transform(); for (int i = 0; i < numFields; i++) { FieldValue *value = node->getField(i); if (value->getType() == SFNODE) { Node *child = ((SFNode *) value)->getValue(); if (child) { glPushName(i); glPushName(0); drawHandlesRec(child); glPopName(); glPopName(); } } else if (value->getType() == MFNODE) { glPushName(i); glPushName(0); MFNode *v = (MFNode *) value; int n = v->getSize(); for (int j = 0; j < n; j++) { glLoadName(j); drawHandlesRec(v->getValue(j)); } glPopName(); glPopName(); } } glPopMatrix(); } void Scene::enableHeadlight() { GLenum light = (GLenum) allocateLight(); static float pos[4] = {0.0f, 0.0f, 1.0f, 0.0f}; static float ambientColor[4] = {0.0f, 0.0f, 0.0f, 1.0f}; static float diffuseColor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; glLightfv(light, GL_AMBIENT, ambientColor); glLightfv(light, GL_DIFFUSE, diffuseColor); glLightfv(light, GL_POSITION, pos); glLightfv(light, GL_SPECULAR, diffuseColor); glLightf(light, GL_SPOT_CUTOFF, 180.0f); glLightf(light, GL_SPOT_EXPONENT, 0.0f); glLightf(light, GL_CONSTANT_ATTENUATION, 1.0f); glLightf(light, GL_LINEAR_ATTENUATION, 0.0f); glLightf(light, GL_QUADRATIC_ATTENUATION, 0.0f); glEnable(light); } // allocateLight() // // reserve an openGL light int Scene::allocateLight() { GLint maxLights; glGetIntegerv(GL_MAX_LIGHTS, &maxLights); if (_numLights >= maxLights) { errorf("too many lights!"); return GL_LIGHT0; } return (GL_LIGHT0 + _numLights++); } int Scene::freeLight() { return GL_LIGHT0 + --_numLights; } void Scene::projectPoint(float x, float y, float z, float *wx, float *wy, float *wz) { GLdouble mmat[16], pmat[16]; GLdouble winx, winy, winz; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, mmat); glGetDoublev(GL_PROJECTION_MATRIX, pmat); glGetIntegerv(GL_VIEWPORT, viewport); gluProject(x, y, z, mmat, pmat, viewport, &winx, &winy, &winz); *wx = (float) winx; *wy = (float) winy; *wz = (float) winz; } void Scene::unProjectPoint(float wx, float wy, float wz, float *x, float *y, float *z) { GLdouble mmat[16], pmat[16]; GLdouble objx, objy, objz; GLint viewport[4]; glGetDoublev(GL_MODELVIEW_MATRIX, mmat); glGetDoublev(GL_PROJECTION_MATRIX, pmat); glGetIntegerv(GL_VIEWPORT, viewport); gluUnProject(wx, wy, wz, mmat, pmat, viewport, &objx, &objy, &objz); *x = (float) objx; *y = (float) objy; *z = (float) objz; } void Scene::addViewpoint(Node *viewpoint) { _viewpoints.append(viewpoint); } void Scene::addFog(Node *fog) { _fogs.append(fog); } void Scene::addBackground(Node *background) { _backgrounds.append(background); } void Scene::addTimeSensor(Node *timeSensor) { _timeSensors.append(timeSensor); } void Scene::moveCamera(float dx, float dy, float dz) { Vec3f pos = _currentViewpoint->getPosition(); Quaternion rot = _currentViewpoint->getOrientation(); /* if (!isRecording() && _currentViewpoint != _defaultViewpoint) { _currentViewpoint = _defaultViewpoint; _currentViewpoint->setOrientation(rot); } */ _currentViewpoint->setPosition(pos + rot * Vec3f(dx, dy, dz)); } void Scene::turnCamera(float x, float y, float z, float ang) { Quaternion rot = _currentViewpoint->getOrientation(); Quaternion r(Vec3f(x, y, z), ang); _currentViewpoint->setOrientation(r * rot); } void Scene::orbitCamera(float dtheta, float dphi) { /* if (!isRecording() && _currentViewpoint != _defaultViewpoint) { _currentViewpoint = _defaultViewpoint; } */ Vec3f pos(_currentViewpoint->getPosition()); Quaternion rot(_currentViewpoint->getOrientation()); Quaternion up(Vec3f(0.0f, 1.0f, 0.0f), dtheta); Quaternion around(Vec3f(1.0f, 0.0f, 0.0f), dphi); Quaternion newRot(up * around * rot); newRot.normalize(); Vec3f newPos(rot.conj() * newRot * pos); if (TheApp->GetMouseMode() == MOUSE_EXAMINE) { // fixme: with this line: a buggy examine mode, without a fly mode _currentViewpoint->setPosition(newPos); } _currentViewpoint->setOrientation(newRot); } Node * Scene::createNode(const char *nodeType) { Proto *def = _protos[nodeType]; return def ? def->create(this) : (Node *) NULL; } void Scene::addNode(Node *node) { _nodes.append(node); } MyString Scene::getUniqueNodeName(Node *node) { static char buf[512]; const char *name = node->getProto()->getName(); for (int i = 1; ; i++) { mysnprintf(buf, 512, "%s%d", name, i); if (use(buf) == NULL) break; } MyString ret = ""; ret += buf; return ret; } MyString Scene::generateUniqueNodeName(Node *node) { MyString name = strdup(getUniqueNodeName(node)); def(name, node); return name; } void Scene::removeNode(Node *node) { int index = _nodes.find(node); _nodes.remove(index); } void Scene::setSelection(const Path *path) { _selection_is_in_scene = true; _viewOfLastSelection = NULL; if (_selection != path) { delete _selection; _selection = path; _nodes.clearFlag(NODE_FLAG_SELECTED); _selection->getNodes()->setFlag(NODE_FLAG_SELECTED); Node *node = _selection->getNode(); if (node == NULL) { setSelection(getRoot()); return; } if (node->getType() == NODE_VIEWPOINT) { _currentViewpoint = (NodeViewpoint *)node; applyCamera(); } } } Path* Scene::newPath(Node *node) { Path* ret; int len = 0; Node *n; for (n = node; n->hasParent() ; n = n->getParent()) { len += 2; } if (len > 0) { int *list = new int[len]; int i = len-1; for (n = node; n->hasParent(); n = n->getParent()) { Node *parent = n->getParent(); int field = n->getParentField(); list[i--] = parent->findChild(n, field); list[i--] = field; } ret=new Path(list, len, this); delete [] list; } else { // select root node ret=new Path(NULL, 0, this); } return ret; } void Scene::setSelection(Node *node) { setSelection(newPath(node)); } bool Scene::isModified() const { if (_undoStack.empty()) { if (_unmodified != NULL) return TRUE; else return _extraModifiedFlag; } else { return _unmodified != _undoStack.peek(); } } void Scene::applyCamera() { _currentViewpoint->apply(); } void Scene::start() { _running = true; if (_viewpoints.size() > 0) { _currentViewpoint = (NodeViewpoint *) _viewpoints[0]; } else { _currentViewpoint = _defaultViewpoint; } double t = swGetCurrentTime(); for (int i = 0; i < _timeSensors.size(); i++) { ((NodeTimeSensor *) _timeSensors[i])->start(t); } } void Scene::stop() { _running = false; } void Scene::updateTime() { double t = swGetCurrentTime(); for (int i = 0; i < _timeSensors.size(); i++) { ((NodeTimeSensor *) _timeSensors[i])->setTime(t); } UpdateViews(NULL, UPDATE_TIME); } NodeViewpoint * Scene::getCamera() const { return _currentViewpoint; } void Scene::AddView(SceneView *view) { _views.append(view); } void Scene::RemoveView(SceneView *view) { _views.remove(_views.find(view)); } void Scene::OnFieldChange(Node *node, int field, int index) { FieldUpdate hint(node, field, index); UpdateViews(NULL, UPDATE_FIELD, (Hint *) &hint); } void Scene::OnAddNode(Node *node, Node *dest, int field) { NodeUpdate hint(node, dest, field); UpdateViews(NULL, UPDATE_ADD_NODE, (Hint *) &hint); } void Scene::OnRemoveNode(Node *node, Node *src, int field) { NodeUpdate hint(node, src, field); UpdateViews(NULL, UPDATE_REMOVE_NODE, (Hint *) &hint); } void Scene::UpdateViews(SceneView *sender, int type, Hint *hint) { for (List::Iterator *i = _views.first(); i != NULL; i = i->next()) { SceneView *view = i->item(); if (view != sender) { view->OnUpdate(sender, type, hint); } } } void BackupRoutesRec(Node *node, CommandList *list) { int i; SocketList::Iterator *j; if (!node) return; if (node->getNumParents() > 1) return; for (i = 0; i < node->getProto()->getNumEventIns(); i++) { for (j = node->getInput(i).first(); j != NULL; j = j->next()) { const Socket &s = j->item(); list->append(new UnRouteCommand(s._node, s._index, node, i)); } } for (i = 0; i < node->getProto()->getNumEventOuts(); i++) { for (j = node->getOutput(i).first(); j != NULL; j = j->next()) { const Socket &s = j->item(); list->append(new UnRouteCommand(node, i, s._node, s._index)); } } for (i = 0; i < node->getProto()->getNumFields(); i++) { FieldValue *v = node->getField(i); if (v->getType() == SFNODE) { BackupRoutesRec(((SFNode *) v)->getValue(), list); } else if (v->getType() == MFNODE) { int size = ((MFNode *) v)->getSize(); for (int k = 0; k < size; k++) { BackupRoutesRec(((MFNode *) v)->getValue(k), list); } } } } void Scene::DeleteSelected() { // Node *parent = _selection->getParent(); if ((_selection->getField() == -1 && _selection->getParentField() != -1)) { CommandList *list = new CommandList(); DeleteSelectedAppend(list); execute(list); // setSelection(parent); } } void Scene::DeleteSelectedAppend(CommandList* list) { if (_selection && (_selection->getNode() != _root)) { Node *node = _selection->getNode(); int field = _selection->getField(); Node *parent = _selection->getParent(); int parentField = _selection->getParentField(); if (field == -1 && parentField != -1) { if (node->getNumParents() == 1) { BackupRoutesRec(node, list); } list->append(new MoveCommand(node, parent, parentField, NULL, -1)); } } } int Scene::OnDragOver(Node *src, Node *srcParent, int srcField, Node *dest, int destField, int modifiers) { int rc = 0; if (src && dest) { if (destField == -1) destField = dest->findValidField(src); if (dest->validChildType(destField, src->getNodeClass()) || dest->validChildType(destField, src->getType())) { if ((modifiers & SW_CONTROL) && (modifiers & SW_SHIFT) && dest != src && !dest->hasAncestor(src)) { rc = SW_DRAG_LINK; } else if (modifiers & SW_CONTROL) { rc = SW_DRAG_COPY; } else if (dest != src && !dest->hasAncestor(src) && dest->findChild(src, destField) == -1) { rc = SW_DRAG_MOVE; } } } return rc; } int Scene::OnDrop(Node *src, Node *srcParent, int srcField, Node *dest, int destField, int modifiers) { int effect = OnDragOver(src, srcParent, srcField, dest, destField, modifiers); if (src && dest) { if (destField == -1) destField = dest->findValidField(src); switch(effect) { case SW_DRAG_COPY: execute(new MoveCommand(src->copy(), NULL, -1, dest, destField)); src->reInit(); break; case SW_DRAG_MOVE: execute(new MoveCommand(src, srcParent, srcField, dest, destField)); break; case SW_DRAG_LINK: execute(new MoveCommand(src, NULL, -1, dest, destField)); break; } return 1; } else { return 0; } } bool Scene::Download(const URL &url, MyString *path) { *path = url.ToPath(); return true; } FontInfo * Scene::LoadGLFont(const char *fontName, const char *style) { int i; int styleId; // handle "special" font names if (!strcmp(fontName, "SERIF")) { fontName = "Times New Roman"; } else if (!strcmp(fontName, "SANS")) { fontName = "Arial"; } else if (!strcmp(fontName, "TYPEWRITER")) { fontName = "Courier New"; } if (!strcmp(style, "BOLD")) { styleId = SW_BOLD; } else if (!strcmp(style, "ITALIC")) { styleId = SW_ITALIC; } else if (!strcmp(style, "BOLDITALIC")) { styleId = SW_BOLD | SW_ITALIC; } else { styleId = SW_PLAIN; } // look for font in cache for (i = 0; i < _fonts.size(); i++) { if (!strcmp(_fonts[i]->name, fontName) && _fonts[i]->style == styleId) { return _fonts[i]; } } // create some font outlines FontInfo *info = new FontInfo(); info->displayListBase = swLoadGLFont(fontName, styleId, info->kernX, info->kernY); if (info->displayListBase == 0) { delete info; return NULL; } else { info->name = fontName; info->style = styleId; _fonts.append(info); return info; } } bool Scene::addProtoName(MyString name) { for (int i=0;i<_numProtoNames;i++) if (name == _protoNames[i]) { for (int j = 0; j < _nodesWithExternProto.size(); j++) if (strcmp(name, _nodesWithExternProto[j]) == 0) return true; return false; } _protoNames[_numProtoNames++] = name; return true; } void Scene::addProtoDefinition(void) { _protoDefinitions[_numProtoDefinitions++] = ""; } void Scene::addToProtoDefinition(char* string) { _protoDefinitions[_numProtoDefinitions-1] += string; } void Scene::addRouteString(MyString string) { _routeList.append(string); } void Scene::setViewOfLastSelection(SceneView* view) { _viewOfLastSelection=view; _selection_is_in_scene=false; } SceneView* Scene::getViewOfLastSelection(void) { return _viewOfLastSelection; } void Scene::DeleteLastSelection(void) { if (_selection_is_in_scene) { // do delete in scene DeleteSelected(); } else { // do delete in View SceneView* view = getViewOfLastSelection(); if (view != NULL) view->DeleteLastSelection(); } } /* check if name of node is already used in scene */ bool Scene::hasAlreadyName(MyString name) { const NodeList *nodes = getNodes(); for (int i = 0; i < nodes->size(); i++) { if (name == nodes->get(i)->getName()) return true; } return false; } /* update URLs in Nodes of scene to new url */ void Scene::updateURLs(Node* node) { if (node==NULL) return; for (int i=0;igetProto()->getNumFields();i++) { Field *field = node->getProto()->getField(i); FieldValue *value = node->getField(i); if (value && !value->equals(field->getDefault())) { if ((field->getFlags() & FF_URL) && (!TheApp->GetKeepURLs())) { node->setField(i,node->rewriteField(value, TheApp->getImportURL(), getURL())); } } } } void Scene::saveProtoStatus(void) { _statusNumProtoNames=_numProtoNames; _statusNumProtoDefinitions=_numProtoDefinitions; } void Scene::restoreProtoStatus(void) { int i; for (i=_statusNumProtoNames+1;i<_numProtoNames;i++) _protoNames.remove(i); _numProtoNames=_statusNumProtoNames; for (i=_statusNumProtoDefinitions+1;i<_numProtoDefinitions;i++) _protoDefinitions.remove(i); _numProtoDefinitions=_statusNumProtoDefinitions; } StringArray * Scene::getAllNodeNames(void) { StringArray *ret = new StringArray(); ProtoMap::Chain::Iterator *j; for (int i = 0; i < _protos.width(); i++) { for ( j = _protos.chain(i).first(); j != NULL; j = j->next()) { ret->append(j->item()->getKey()); } } return ret; } bool Scene::use3dCursor(void) { switch (TheApp->Get3dCursorMode()) { case CM_3DCURSOR_ALLWAYS: return _use3dCursor; case CM_3DCURSOR_RECORDING: if (isRecording()) return _use3dCursor; break; case CM_3DCURSOR_NOT_RUN: if (!isRunning()) return _use3dCursor; break; } return false; } // the proto PREFIX is only needed for the illegal2vrml program // undefined nodes named "something" are renamed to "PREFIXsomething" // when the proto "PREFIXsomething" is defined bool Scene::setProtoPrefix(char* protoPrefix) { if (protoPrefix != NULL) _protoPrefix = protoPrefix; else { // compare all protonames to find out a common prefix bool prefixFound = false; if (getNumProtoNames() > 1) { MyString prefix = ""; for (int numChar = 0; numChar < _protoNames[0].length(); numChar++) { char character = _protoNames[0][numChar]; bool sameCharacter = true; for (int i = 1; i < getNumProtoNames(); i++) { if ((numChar >= _protoNames[i].length()) || (_protoNames[i][numChar] != character)) { sameCharacter = false; break; } } if (sameCharacter) { prefixFound = true; prefix += character; } else break; } if (prefixFound) _protoPrefix = strdup(prefix); } if (!prefixFound) { errorf("can not find out prefix from only one node\n"); errorf("prefix missing, use \"-prefix\" in commandline\n"); return false; } } return true; } MyString Scene::getNodeWithPrefix(const MyString &nodeType) { MyString newNodeType = ""; newNodeType += scene->getProtoPrefix(); newNodeType += nodeType; return newNodeType; } void Scene::setPathAllURL(const char *path) { const NodeList *nodes = getNodes(); for (int i = 0; i < nodes->size(); i++) { Node *node = nodes->get(i); if (node->isInScene(this)) for (int j = 0; j < node->getProto()->getNumFields(); j++) { Field *field = node->getProto()->getField(j); if ((field->getType() == MFSTRING) && ((field->getFlags() & FF_URL) != 0)) { MFString* urls = (MFString *)node->getField(j); for (int k = 0; k < urls->getSize(); k++) { const char *urlk = urls->getValue(k); if (notJavascript(urlk) && notURN(urlk)) { URL url(getURL(), urlk); MyString *newURL = new MyString(""); if (strlen(path) != 0) { *newURL += path; *newURL += "/"; } *newURL += url.GetFileName(); urls->setValue(k, *newURL); } } } } } } Node * Scene::convertConeToNurbs(Node* cone) { Node *parent = cone->getParent(); int field = cone->getParentField(); if ((parent != NULL) && (field != -1)) { NodeNurbsSurface *nurbsNode = (NodeNurbsSurface *) cone->toNurbs(2, 2, 3, 2, 1); SFRotation rot(0,1,0,M_PI/2.0); nurbsNode->rotate(rot); execute(new MoveCommand(cone, parent, field, NULL, -1)); execute(new MoveCommand(nurbsNode, NULL, -1, parent, field)); return nurbsNode; } return NULL; }