diff --git a/src/osgPlugins/Inventor/CMakeLists.txt b/src/osgPlugins/Inventor/CMakeLists.txt index 34829fa6c..035cb36c4 100644 --- a/src/osgPlugins/Inventor/CMakeLists.txt +++ b/src/osgPlugins/Inventor/CMakeLists.txt @@ -3,7 +3,6 @@ INCLUDE(OsgMacroUtils) SET(TARGET_SRC ConvertToInventor.cpp ConvertFromInventor.cpp - GroupSoLOD.cpp PendulumCallback.cpp ReaderWriterIV.cpp ShuttleCallback.cpp @@ -11,7 +10,6 @@ SET(TARGET_SRC SET(TARGET_HDRS ConvertToInventor.h ConvertFromInventor.h - GroupSoLOD.h PendulumCallback.h ReaderWriterIV.h ShuttleCallback.h @@ -21,6 +19,6 @@ ADD_DEFINITIONS(-DCOIN_DLL) INCLUDE_DIRECTORIES(${INVENTOR_INCLUDE_DIR}) -SET(TARGET_LIBRARIES_VARS INVENTOR_LIBRARY) +SET(TARGET_ADDED_LIBRARIES ${INVENTOR_LIBRARY}) SETUP_PLUGIN(iv iv) diff --git a/src/osgPlugins/Inventor/ConvertFromInventor.cpp b/src/osgPlugins/Inventor/ConvertFromInventor.cpp index c33870235..93a97decc 100644 --- a/src/osgPlugins/Inventor/ConvertFromInventor.cpp +++ b/src/osgPlugins/Inventor/ConvertFromInventor.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -37,11 +38,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #ifdef __COIN__ #include @@ -50,7 +54,17 @@ #include #endif -#include "GroupSoLOD.h" +#if defined(__COIN__) && (COIN_MAJOR_VERSION >= 3 || \ + (COIN_MAJOR_VERSION == 2 && COIN_MINOR_VERSION>=5)) +#define INVENTOR_SHADERS_AVAILABLE +#endif + +#ifdef INVENTOR_SHADERS_AVAILABLE +#include +#include +#include +#include +#endif #include #include @@ -64,135 +78,721 @@ #endif #define DEBUG_IV_PLUGIN +#define NOTIFY_HEADER "Inventor Plugin (reader): " + /////////////////////////////////////////// ConvertFromInventor::ConvertFromInventor() { numPrimitives = 0; transformInfoName = ""; appearanceName = ""; - inAppearanceWithNoTexture = false; - lightGroup = NULL; } /////////////////////////////////////////// ConvertFromInventor::~ConvertFromInventor() { } +/////////////////////////////////////////////////////////////////// +static bool +nodePreservesState(const SoNode *node) +{ + return node->isOfType(SoSeparator::getClassTypeId()) || + (node->getChildren() != NULL && node->affectsState() == FALSE); +} +//////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::restructure(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "restructure() " + << node->getTypeId().getName().getString(); +#endif + + int childrenTotal = 0; + int numModifiedChildren = 0; + int numRemovedNodes = 0; + std::vector > &stack = *((std::vector >*)data); + + if (node->isOfType(SoGroup::getClassTypeId())) { + + SoGroup *group = (SoGroup*)node; + SoGroup *affectedScene = NULL; + childrenTotal = group->getNumChildren(); + + for (int i=0, c=group->getNumChildren(); igetChild(i); + if (!child->isOfType(SoSeparator::getClassTypeId()) && + child->affectsState()) { + + // Put the node bellow separator + SoSeparator *s = new SoSeparator; + s->addChild(group->getChild(i)); + group->replaceChild(i, s); + numModifiedChildren++; + + // Create the scene that may be affected by the node + if (!affectedScene) { + + // Create the graph of nodes that may be influenced + // by the node + const SoFullPath *path = (const SoFullPath*)action->getCurPath(); + assert(path->getLength() == 0 || + path->getNode(path->getLength()-1) == group && + "Group being restructured is not at the end of the path."); + int stackLevel = stack.size()-2; + for (int j=path->getLength()-2; j>=0; j--, stackLevel--) { + + // Get the appropriate stack level of nodesToRemove + assert(stackLevel >=0); + std::vector &nodesToRemove = stack[stackLevel]; + + // Get parent and index of the current group + SoNode *parent = path->getNode(j); + int childIndex = path->getIndex(j+1); + const SoChildList *chl = parent->getChildren(); + assert(chl->operator[](childIndex) == path->getNode(j+1) && + "Wrong indexing."); + + // Create affected scene graph + if (!affectedScene) + affectedScene = new SoGroup; + + // Copy nodes to the graph + for (int k=childIndex+1, n=chl->getLength(); kaddChild(chl->operator[](k)); + nodesToRemove.push_back(k); + numRemovedNodes++; + } + + // Stop recursion if we reached separator + // or other state-preserving node. + if (nodePreservesState(parent)) + break; + } + } + + // Append the affected graph to the separator + s->addChild(affectedScene); + } + } + } + +#ifdef DEBUG_IV_PLUGIN + if (numModifiedChildren == 0) + osg::notify(osg::DEBUG_INFO) << ": no changes necessary" << std::endl; + else + osg::notify(osg::DEBUG_INFO) << ": " << numModifiedChildren << + " nodes of " << childrenTotal << " restruc., " << + numRemovedNodes << " removed" << std::endl; +#endif + + return SoCallbackAction::CONTINUE; +} /////////////////////////////////////////////////////////// -osg::Node* ConvertFromInventor::convert(SoNode* rootIVNode) -{ - // Transformation matrix for converting Inventor coordinate system to OSG +SoCallbackAction::Response +ConvertFromInventor::restructurePreNode(void* data, SoCallbackAction* action, + const SoNode* node) +{ + std::vector > &stack = *((std::vector >*)data); + + stack.push_back(std::vector()); + + return SoCallbackAction::CONTINUE; +} +//////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::restructurePostNode(void* data, SoCallbackAction* action, + const SoNode* node) +{ + std::vector > &stack = *((std::vector >*)data); + + assert(stack.size() > 0 && "Stack is empty"); + std::vector &nodesToRemove = stack.back(); + + if (nodesToRemove.size() > 0) { + +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "postNode() " + << node->getTypeId().getName().getString() + << " (level " << stack.size() << ") removed " + << nodesToRemove.size() << " node(s)" << std::endl; +#endif + + assert(node->getChildren()); + for (int i=nodesToRemove.size()-1; i>=0; i--) { + assert(i==0 || nodesToRemove[i-1] < nodesToRemove[i] && + "Children to remove are not in order."); + node->getChildren()->remove(nodesToRemove[i]); + } + } + + stack.pop_back(); + + return SoCallbackAction::CONTINUE; +} +/////////////////////////////////////////////////////////////////// +void +ConvertFromInventor::preprocess(SoNode* root) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "Preprocessing..." << std::endl; +#endif + + SoCallbackAction action; + std::vector > stackOfNodesToRemove; + + // Callbacks for troublesome nodes + action.addPreCallback(SoNode::getClassTypeId(), + restructurePreNode, &stackOfNodesToRemove); + action.addPostCallback(SoLOD::getClassTypeId(), + restructure, &stackOfNodesToRemove); + action.addPostCallback(SoNode::getClassTypeId(), + restructurePostNode, &stackOfNodesToRemove); + + // Traverse the scene + action.apply(root); + +#if 0 // For debugging purposes: Write preprocessed scene to the file + SoOutput out; + out.openFile("preprocess.iv"); + SoWriteAction wa(&out); + wa.apply(root); +#endif +} +/////////////////////////////////////////////////////////// +osg::Node* +ConvertFromInventor::convert(SoNode* ivRootNode) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "Converting..." << std::endl; +#endif + + // Transformation matrix for converting Inventor coordinate system to OSG // coordinate system osg::Matrix ivToOSGMat(osg::Matrix(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,-1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0)); - // Create a root node and push it onto the stack - _root = new osg::MatrixTransform; - _root->setMatrix(ivToOSGMat); - groupStack.push(_root.get()); + // Root of the scene + osg::ref_ptr osgRootNode = new osg::MatrixTransform(ivToOSGMat); - // Push an empty list of light and push it onto the light stack - LightList lightList; - lightStack.push(lightList); - - // Create callback actions for the inventor nodes + // Initialize Inventor state stack + // (ivStateStack is used to track the state that is not accessible by + // SoCallbackAction functions) + ivStateStack.push(IvStateItem(ivRootNode, osgRootNode.get())); + + // Create callback actions for the inventor nodes // These callback functions perform the conversion // note: if one class is derived from the other and both callbacks // are registered, both functions will be called SoCallbackAction cbAction; + + // Node callbacks are used for detecting which node + // preserves state (like SoSeparator) and which not. + // There are few nodes that behave like SoSeparator although they + // are not derived from it. + // Note: postNode callback is moved down, because it must be + // called as the last callback. + cbAction.addPreCallback(SoNode::getClassTypeId(), preNode, this); + + // SoTransformSeparator callbacks. Special handling of transformations. + cbAction.addPreCallback(SoTransformSeparator::getClassTypeId(), preTransformSeparator, this); + cbAction.addPostCallback(SoTransformSeparator::getClassTypeId(), postTransformSeparator, this); + + // LOD (Level of Detail) callbacks. Handles SoLOD nodes. + // FIXME: SoLevelOfDetail needs to be implemented and tested. + cbAction.addPreCallback(SoLOD::getClassTypeId(), preLOD, this); + cbAction.addPostCallback(SoLOD::getClassTypeId(), postLOD, this); + + // Shape callbacks collects all triangles and all the geometry data. + // Moreover, they handle transformations, ... cbAction.addPreCallback(SoShape::getClassTypeId(), preShape, this); cbAction.addPostCallback(SoShape::getClassTypeId(), postShape, this); - cbAction.addPreCallback(SoGroup::getClassTypeId(), preGroup, this); - cbAction.addPostCallback(SoGroup::getClassTypeId(), postGroup, this); - cbAction.addPreCallback(SoTexture2::getClassTypeId(), preTexture, this); + + // Handling of textures + cbAction.addPostCallback(SoTexture2::getClassTypeId(), + postTexture, this); +#ifdef __COIN__ + cbAction.addPostCallback(SoVRMLImageTexture::getClassTypeId(), + postTexture, this); + cbAction.addPostCallback(SoVRMLAppearance::getClassTypeId(), + postTexture, this); +#endif + #ifdef __COIN__ - cbAction.addPreCallback(SoVRMLImageTexture::getClassTypeId(), - preVRMLImageTexture, this); - cbAction.addPreCallback(SoVRMLAppearance::getClassTypeId(), - preVRMLAppearance, this); cbAction.addPreCallback(SoInfo::getClassTypeId(), preInfo, this); #endif + + // Lights cbAction.addPreCallback(SoLight::getClassTypeId(), preLight, this); + + // Environment (ambient light,...) + cbAction.addPreCallback(SoEnvironment::getClassTypeId(), preEnvironment, this); + + // Shaders +#ifdef INVENTOR_SHADERS_AVAILABLE + cbAction.addPreCallback(SoShaderProgram::getClassTypeId(), preShaderProgram, this); +#endif + + // Motion callbacks cbAction.addPreCallback(SoRotor::getClassTypeId(), preRotor, this); cbAction.addPreCallback(SoPendulum::getClassTypeId(), prePendulum, this); cbAction.addPreCallback(SoShuttle::getClassTypeId(), preShuttle, this); + + // Geometry callbacks cbAction.addTriangleCallback(SoShape::getClassTypeId(), addTriangleCB, this); cbAction.addLineSegmentCallback(SoShape::getClassTypeId(), addLineSegmentCB, this); cbAction.addPointCallback(SoShape::getClassTypeId(), addPointCB, this); + // Post node callback + cbAction.addPostCallback(SoNode::getClassTypeId(), postNode, this); + // Traverse the inventor scene graph - cbAction.apply(rootIVNode); + cbAction.apply(ivRootNode); - // Pop all the groups that are Transforms - // Verify that the last transform is _root . - assert(groupStack.size() > 0 && "groupStack underflow."); - osg::ref_ptr group = groupStack.top(); - while (strcmp(group->className(), "MatrixTransform") == 0) - { - groupStack.pop(); - if (groupStack.empty()) break; - group = groupStack.top(); + // Remove superfluous group + if (osgRootNode->getNumChildren() == 1) { + osg::ref_ptr toRemove = osgRootNode->getChild(0)->asGroup(); + assert(toRemove.get() && + strcmp(toRemove->className(), "Group") == 0 && + "IvStateStack osg graph is expected to be " + "headed by osg::Group"); + osgRootNode->removeChild(0u); + for (int i=0, c=toRemove->getNumChildren(); iaddChild(toRemove->getChild(i)); } - assert(group.get() == _root.get() && "groupStack error"); - assert(groupStack.size() == 0 && "groupStack is not empty after traversal."); - assert(soTexStack.size() == 0 && "soTexStack was left at inconsistent state."); - - assert(lightStack.size() == 1 && "lightStack was left at inconsistent state."); - lightStack.pop(); - - return _root.get(); + return osgRootNode.get(); } /////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preShape(void* data, SoCallbackAction* action, +static void +notifyAboutMatrixContent(const osg::NotifySeverity level, const SbMatrix &m) +{ + SbVec3f t,s; + SbRotation r,so; + m.getTransform(t, r, s, so); + SbVec3f axis; + float angle; + r.getValue(axis, angle); + osg::notify(level) << NOTIFY_HEADER << " Translation: " << + t[0] << "," << t[1] << "," << t[2] << std::endl; + osg::notify(level) << NOTIFY_HEADER << " Rotation: (" << + axis[0] << "," << axis[1] << "," << axis[2] << ")," << angle << std::endl; +} +/////////////////////////////////////////////////////////////////// +void +ConvertFromInventor::appendNode(osg::Node *n, const SoCallbackAction *action) +{ + IvStateItem &ivState = ivStateStack.top(); + SbMatrix currentMatrix = action->getModelMatrix(); + SbMatrix inheritedMatrix = ivState.inheritedTransformation; + + // Keep children order - this must be done for some nodes like + // SoSwitch, SoLOD,... + // We will append dummy nodes if the child is expected to be on + // higher index. + if (ivState.flags & IvStateItem::KEEP_CHILDREN_ORDER) { + + // Determine child index + int childIndex = -1; + const SoFullPath *path = (const SoFullPath*)(((SoCallbackAction*)action)->getCurPath()); + for (int i=path->getLength()-2; i>=0; i--) + if (path->getNode(i) == ivState.keepChildrenOrderParent) { + childIndex = path->getIndex(i+1); + assert(ivState.keepChildrenOrderParent->getChildren()); + assert((ivState.keepChildrenOrderParent->getChildren()->operator[](childIndex) == path->getNode(i+1)) && "Indexing is wrong."); + break; + } + assert(childIndex != -1 && "Node did not found."); + + // Append dummy nodes to keep children order + assert(int(ivState.osgStateRoot->getNumChildren()) <= childIndex && + "Number of children in ivState.osgStateRoot is too big."); + while (int(ivState.osgStateRoot->getNumChildren()) < childIndex) + ivState.osgStateRoot->addChild(new osg::Node); + } + +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "appendNode: " + << n->className(); +#endif + + if (currentMatrix == inheritedMatrix) { + + // just append node to the current group in osg scene graph + ivState.osgStateRoot->addChild(n); + ivState.lastUsedTransformation = inheritedMatrix; + +#ifdef DEBUG_IV_PLUGIN + if (osg::isNotifyEnabled(osg::DEBUG_INFO)) + osg::notify(osg::DEBUG_INFO) << + " uses parent transformation" << std::endl; +#endif + + } else { + + if (!(ivState.flags & IvStateItem::KEEP_CHILDREN_ORDER) && + currentMatrix == ivState.lastUsedTransformation) { + + // Previous node has the same transformation. Let's use it. + assert(ivState.osgStateRoot->getNumChildren() != 0 && + "This should never happen - there is no item on " + "osgShapeGraphs list while want to use last one."); + osg::Transform *t = ivState.osgStateRoot->getChild(ivState.osgStateRoot->getNumChildren()-1)->asTransform(); + assert(t && "This should never happen - want to use " + "transformation of previous scene geometry " + "and it does not have Transform node."); + t->addChild(n); + +#ifdef DEBUG_IV_PLUGIN + if (osg::isNotifyEnabled(osg::DEBUG_INFO)) + osg::notify(osg::DEBUG_INFO) << + " reuses previous transformation" << std::endl; +#endif + + } else { + + // We need a new transformation node + osg::Matrix m(osg::Matrix(currentMatrix.operator float*())); + osg::Matrix m2; + m2.invert(osg::Matrix(inheritedMatrix.operator float*())); + m.postMult(m2); + osg::MatrixTransform *mt = new osg::MatrixTransform(m); + mt->addChild(n); + + ivState.osgStateRoot->addChild(mt); + ivState.lastUsedTransformation = currentMatrix; + +#ifdef DEBUG_IV_PLUGIN + if (osg::isNotifyEnabled(osg::DEBUG_INFO)) { + osg::notify(osg::DEBUG_INFO) << + " uses local transformation:" << std::endl; + notifyAboutMatrixContent(osg::DEBUG_INFO, + SbMatrix((SbMat&)(*osg::Matrixf(m).ptr()))); + } +#endif + } + } +} +/////////////////////////////////////////////////////////////////// +void +ConvertFromInventor::ivPushState(const SoCallbackAction *action, + const SoNode *initiator, const int flags, + osg::Group *root) +{ + assert(ivStateStack.size() >= 1 && "There must be at least one " + "value in the ivStateStack to use ivPushState function."); + + // APPEND_AT_PUSH + if (flags & IvStateItem::APPEND_AT_PUSH) + appendNode(root, action); + + // Push state + ivStateStack.push(IvStateItem(ivStateStack.top(), action, initiator, flags, root)); + +} +/////////////////////////////////////////////////////////////////// +void +ConvertFromInventor::ivPopState(const SoCallbackAction *action, + const SoNode *initiator) +{ + bool multipop; + do { + assert(ivStateStack.size() >= 2 && "There must be at least two " + "values in the ivStateStack to use ivPopState function."); + + // Get multipop value + IvStateItem ivState = ivStateStack.top(); + multipop = ivState.flags & IvStateItem::MULTI_POP; + assert(multipop || + ivState.pushInitiator == initiator && + "ivStateStack push was initiated by different node."); + + // Get osgStateRoot (note: we HAVE TO reference it) + osg::ref_ptr r = ivState.osgStateRoot; + + // Pop state + ivStateStack.pop(); + + // Update state from already popped values + if ((ivState.flags & (IvStateItem::UPDATE_STATE | + IvStateItem::UPDATE_STATE_EXCEPT_TRANSFORM)) != 0) { + IvStateItem &newTop = ivStateStack.top(); + newTop.currentTexture = ivState.currentTexture; + newTop.currentLights = ivState.currentLights; + newTop.currentGLProgram = ivState.currentGLProgram; + } + + // APPEND_AT_PUSH + if (!(ivState.flags & IvStateItem::APPEND_AT_PUSH)) + appendNode(r.get(), action); + + } while (multipop); + +} +/////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::preNode(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preNode() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + if (nodePreservesState(node)) { + + // push state + ConvertFromInventor *thisPtr = (ConvertFromInventor *) data; + thisPtr->ivPushState(action, node); +#ifdef DEBUG_IV_PLUGIN + if (osg::isNotifyEnabled(osg::DEBUG_INFO)) { + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "push state, saved values: " << std::endl; + notifyAboutMatrixContent(osg::DEBUG_INFO, action->getModelMatrix()); + } +#endif + } + + return SoCallbackAction::CONTINUE; +} +//////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::postNode(void* data, SoCallbackAction* action, const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preShape() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "postNode() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + if (nodePreservesState(node)) { + + // pop state + ConvertFromInventor *thisPtr = (ConvertFromInventor *) data; + assert(thisPtr->ivStateStack.size() > 0 && "ivStackState underflow"); + thisPtr->ivPopState(action, node); + +#ifdef DEBUG_IV_PLUGIN + if (osg::isNotifyEnabled(osg::DEBUG_INFO)) { + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << + "pop state, restored transformation: " << std::endl; + notifyAboutMatrixContent(osg::DEBUG_INFO, action->getModelMatrix()); + } +#endif + } + + return SoCallbackAction::CONTINUE; +} +/////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::preTransformSeparator(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preTransformSeparator() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + // push state + ConvertFromInventor *thisPtr = (ConvertFromInventor *) data; + thisPtr->ivPushState(action, node, IvStateItem::UPDATE_STATE_EXCEPT_TRANSFORM, + new osg::Group()); + + return SoCallbackAction::CONTINUE; +} +//////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::postTransformSeparator(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "postTransformSeparator() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + // pop state + ConvertFromInventor *thisPtr = (ConvertFromInventor *) data; + assert(thisPtr->ivStateStack.size() > 0 && "ivStackState underflow"); + thisPtr->ivPopState(action, node); + + return SoCallbackAction::CONTINUE; +} +/////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::preLOD(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preLOD() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + // init values + ConvertFromInventor* thisPtr = (ConvertFromInventor*)data; + + // SoLOD + // Note: It is not possible to convert SoLOD to osg:LOD + // in any non-complex algorithm, because SoLOD does not preserves + // traversal state (like SoSeparator). Thus, following example + // can not be easily converted: + // + // SoLOD { + // range [...] + // Complexity { value 0.1 } + // Complexity { value 0.2 } + // Complexity { value 0.3 } + // } + // Sphere {} + // + // It was decided that it is necessary to preprocess scene + // in a way to avoid any state to come out of SoLOD. For example: + // + // SoLOD { + // range [...] + // Separator { + // Complexity { value 0.1 } + // DEF mySphere Sphere {} + // } + // Separator { + // Complexity { value 0.2 } + // USE mySphere + // } + // Separator { + // Complexity { value 0.3 } + // USE mySphere + // } + // } + // + // Such scene can be converted easily to OSG. + if (node->isOfType(SoLOD::getClassTypeId())) { + + thisPtr->ivPushState(action, node, IvStateItem::KEEP_CHILDREN_ORDER, + new osg::LOD); + thisPtr->ivStateStack.top().keepChildrenOrderParent = node; + + return SoCallbackAction::CONTINUE; + } + + return SoCallbackAction::CONTINUE; +} +////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::postLOD(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "postLOD() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + // SoGroup -> do nothing + if (node->getTypeId() == SoGroup::getClassTypeId()) + return SoCallbackAction::CONTINUE; + + // init values + ConvertFromInventor* thisPtr = (ConvertFromInventor*)data; + IvStateItem &ivState = thisPtr->ivStateStack.top(); + + // SoLOD + if (node->isOfType(SoLOD::getClassTypeId())) { + + osg::LOD *lod = dynamic_cast(ivState.osgStateRoot.get()); + SoLOD *ivLOD = (SoLOD*)node; + + // LOD center + SbVec3f ivCenter = ivLOD->center.getValue(); + lod->setCenter(osg::Vec3(ivCenter[0], ivCenter[1], ivCenter[2])); + + // Verify the number of children and range values + int num = lod->getNumChildren(); + if (ivLOD->range.getNum()+1 != num && + !(num == 0 && ivLOD->range.getNum() == 0)) { + osg::notify(osg::WARN) << NOTIFY_HEADER << + "Warning: SoLOD does not contain " + "correct data in range field." << std::endl; + if (ivLOD->range.getNum()+1 < num) { + lod->removeChildren(ivLOD->range.getNum() + 1, + num - ivLOD->range.getNum() - 1); + num = ivLOD->range.getNum() + 1; + } + } + + // Get the ranges and set it + if (num > 0) { + if (num == 1) + lod->setRange(0, 0.0, FLT_MAX); + else { + lod->setRange(0, 0.0, ivLOD->range[0]); + for (int i = 1; i < num-1; i++) + lod->setRange(i, ivLOD->range[i-1], ivLOD->range[i]); + lod->setRange(num-1, ivLOD->range[num-2], FLT_MAX); + } + } + +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << + "Appending osg::LOD with " << num << " children." << std::endl; +#endif + + assert(ivState.keepChildrenOrderParent == node && + "Current node is not the root of keepChildrenOrder graph."); + thisPtr->ivPopState(action, node); + + return SoCallbackAction::CONTINUE; + } + + return SoCallbackAction::CONTINUE; +} +/////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::preShape(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preShape() " << node->getTypeId().getName().getString() << std::endl; #endif ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); // Normal and color binding map from Inventor to OSG - static std::map + static std::map normBindingMap; static std::map colBindingMap; static bool firstTime = true; if (firstTime) { - normBindingMap[SoNormalBinding::OVERALL] + normBindingMap[SoNormalBinding::OVERALL] = osg::Geometry::BIND_OVERALL; - normBindingMap[SoNormalBinding::PER_PART] + normBindingMap[SoNormalBinding::PER_PART] = osg::Geometry::BIND_PER_PRIMITIVE; - normBindingMap[SoNormalBinding::PER_PART_INDEXED] + normBindingMap[SoNormalBinding::PER_PART_INDEXED] = osg::Geometry::BIND_PER_PRIMITIVE; - normBindingMap[SoNormalBinding::PER_FACE] + normBindingMap[SoNormalBinding::PER_FACE] = osg::Geometry::BIND_PER_PRIMITIVE; - normBindingMap[SoNormalBinding::PER_FACE_INDEXED] + normBindingMap[SoNormalBinding::PER_FACE_INDEXED] = osg::Geometry::BIND_PER_PRIMITIVE; - normBindingMap[SoNormalBinding::PER_VERTEX] + normBindingMap[SoNormalBinding::PER_VERTEX] = osg::Geometry::BIND_PER_VERTEX; - normBindingMap[SoNormalBinding::PER_VERTEX_INDEXED] + normBindingMap[SoNormalBinding::PER_VERTEX_INDEXED] = osg::Geometry::BIND_PER_VERTEX; - colBindingMap[SoMaterialBinding::OVERALL] + colBindingMap[SoMaterialBinding::OVERALL] = osg::Geometry::BIND_OVERALL; colBindingMap[SoMaterialBinding::PER_PART] = osg::Geometry::BIND_PER_PRIMITIVE; - colBindingMap[SoMaterialBinding::PER_PART_INDEXED] + colBindingMap[SoMaterialBinding::PER_PART_INDEXED] = osg::Geometry::BIND_PER_PRIMITIVE; colBindingMap[SoMaterialBinding::PER_FACE] = osg::Geometry::BIND_PER_PRIMITIVE; - colBindingMap[SoMaterialBinding::PER_FACE_INDEXED] + colBindingMap[SoMaterialBinding::PER_FACE_INDEXED] = osg::Geometry::BIND_PER_PRIMITIVE; - colBindingMap[SoMaterialBinding::PER_VERTEX] + colBindingMap[SoMaterialBinding::PER_VERTEX] = osg::Geometry::BIND_PER_VERTEX; - colBindingMap[SoMaterialBinding::PER_VERTEX_INDEXED] + colBindingMap[SoMaterialBinding::PER_VERTEX_INDEXED] = osg::Geometry::BIND_PER_VERTEX; firstTime = false; @@ -222,19 +822,20 @@ ConvertFromInventor::preShape(void* data, SoCallbackAction* action, thisPtr->normals.clear(); thisPtr->colors.clear(); thisPtr->textureCoords.clear(); - + return SoCallbackAction::CONTINUE; } /////////////////////////////////////////////////////////// // OSG doesn't seem to have a transpose function // //for matrices // /////////////////////////////////////////////////////////// -void ConvertFromInventor::transposeMatrix(osg::Matrix& mat) +void +ConvertFromInventor::transposeMatrix(osg::Matrix& mat) { float tmp; - for (int j = 0; j < 4; j++) + for (int j = 0; j < 4; j++) { - for (int i = j + 1; i < 4; i++) + for (int i = j + 1; i < 4; i++) { tmp = mat.operator()(j,i); mat.operator()(j,i) = mat.operator()(i,j); @@ -244,12 +845,12 @@ void ConvertFromInventor::transposeMatrix(osg::Matrix& mat) } //////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::postShape(void* data, SoCallbackAction* action, +SoCallbackAction::Response +ConvertFromInventor::postShape(void* data, SoCallbackAction* action, const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "postShape() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "postShape() " << node->getTypeId().getName().getString() << std::endl; #endif @@ -290,7 +891,7 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, cols = new osg::Vec4Array(1); SbColor ambient, diffuse, specular, emission; float transparency, shininess; - action->getMaterial(ambient, diffuse, specular, emission, shininess, + action->getMaterial(ambient, diffuse, specular, emission, shininess, transparency, 0); (*cols)[0].set(diffuse[0], diffuse[1], diffuse[2], 1.0 - transparency); } @@ -305,14 +906,14 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, if (thisPtr->textureCoords.empty()) - osg::notify(osg::INFO)<<"tex coords not found"<getNumTextureCoordinates()>0) - osg::notify(osg::INFO)<<"tex coords found"< texCoords + osg::ref_ptr texCoords = new osg::Vec2Array(thisPtr->textureCoords.size()); if (textureMat == identityMat) { @@ -335,7 +936,7 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, for (unsigned int i = 0; i < thisPtr->textureCoords.size(); i++) { osg::Vec3 transVec = textureMat.preMult( - osg::Vec3(thisPtr->textureCoords[i][0], + osg::Vec3(thisPtr->textureCoords[i][0], thisPtr->textureCoords[i][1], 0.0)); (*texCoords)[i].set(transVec.x(), transVec.y()); @@ -344,7 +945,7 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, geometry->setTexCoordArray(0, texCoords.get()); } - + // Set the parameters for the geometry geometry->addPrimitiveSet(new osg::DrawArrays(thisPtr->primitiveType,0, @@ -352,7 +953,7 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, // Get the StateSet for the geoset osg::ref_ptr stateSet = thisPtr->getStateSet(action); geometry->setStateSet(stateSet.get()); - + // Add the geoset to a geode osg::ref_ptr geode = new osg::Geode; geode->addDrawable(geometry.get()); @@ -362,84 +963,85 @@ ConvertFromInventor::postShape(void* data, SoCallbackAction* action, if (name != "") { geode->setName(name); } - // Add geode to scenegraph - thisPtr->groupStack.top()->addChild(geode.get()); + + // Transformation and scene graph building + thisPtr->appendNode(geode.get(), action); return SoCallbackAction::CONTINUE; } /////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preTexture(void* data, SoCallbackAction *, - const SoNode* node) +SoCallbackAction::Response +ConvertFromInventor::postTexture(void* data, SoCallbackAction *, + const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preTexture() " - << node->getTypeId().getName().getString() << std::endl; + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "postTexture() " + << node->getTypeId().getName().getString(); + if (node->isOfType(SoTexture2::getClassTypeId())) { + SoTexture2 *t = (SoTexture2*)node; + if (t->filename.getValue().getLength()) + osg::notify(osg::DEBUG_INFO) << " " << t->filename.getValue().getString(); + } + osg::notify(osg::DEBUG_INFO) << std::endl; #endif - + ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - - if (thisPtr->soTexStack.size()) - thisPtr->soTexStack.pop(); - thisPtr->soTexStack.push(node); - - return SoCallbackAction::CONTINUE; -} -////////////////////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preVRMLAppearance(void* data, SoCallbackAction* action, - const SoNode* node) -{ -#ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preVRMLAppearance() " - << node->getTypeId().getName().getString() << std::endl; -#endif + bool texturingEnabled = false; + + // Texture2 + if (node->isOfType(SoTexture2::getClassTypeId())) { + + // Check whether texturing was enabled by the texture node + SoTexture2 *t = (SoTexture2*)node; + SbVec2s size; + int nc; + const unsigned char *data = t->image.getValue(size, nc); + texturingEnabled = t->filename.getValue().getLength() || + (data && size != SbVec2s(0,0)); + } #ifdef __COIN__ - ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - // If there is a VRML appearance node without a texture node, then - // we push a NULL texture onto the stack - bool foundTex = false; - SoChildList *kids = node->getChildren(); - for (int i=0; igetLength(); i++) { - SoNode* kid = (SoNode*)kids->get(i); - if (kid->isOfType(SoVRMLMaterial::getClassTypeId())) { - thisPtr->appearanceName = kid->getName(); - } - if (kid->isOfType(SoVRMLTexture::getClassTypeId())) { - foundTex = true; - } + // SoVRMLImageTexture + if (node->isOfType(SoVRMLImageTexture::getClassTypeId())) { + + // Check whether texturing was enabled by the texture node + SoVRMLImageTexture *t = (SoVRMLImageTexture*)node; + texturingEnabled = t->url.getNum() > 1 || (t->url.getNum() == 1 && t->url[0].getLength() > 0); } - if (!foundTex) { - thisPtr->soTexStack.push(NULL); - thisPtr->inAppearanceWithNoTexture = true; + + // SoVRMLAppearance + if (node->isOfType(SoVRMLAppearance::getClassTypeId())) { + + // If SoVRMLAppearance is present and there is no texture + // inside, disable texturing + // FIXME: should SoVRMLAppearance really disable texturing + // when not containing SoVRMLImageTexture? Coin is not doing that, + // but it can be Coin bug. + SoVRMLAppearance *a = (SoVRMLAppearance*)node; + if (a->texture.getValue() == NULL) + thisPtr->ivStateStack.top().currentTexture = NULL; + + // Do not try to "optimize" this code by removing the return + // and use the one at the end of the function. + // It would break the case when there is texture inside + // the appearance node. + return SoCallbackAction::CONTINUE; } -#endif - return SoCallbackAction::CONTINUE; -} -////////////////////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preVRMLImageTexture(void* data, SoCallbackAction* action, - const SoNode* node) -{ -#ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preVRMLImageTexture() " - << node->getTypeId().getName().getString() << std::endl; -#endif +#endif /* __COIN__ */ - ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - - if (thisPtr->soTexStack.size()) - thisPtr->soTexStack.pop(); - thisPtr->soTexStack.push(node); + // Set current texture + if (texturingEnabled) + thisPtr->ivStateStack.top().currentTexture = node; + else + thisPtr->ivStateStack.top().currentTexture = NULL; return SoCallbackAction::CONTINUE; } ////////////////////////////////////////////////////////////////// -void ConvertFromInventor::transformLight(SoCallbackAction* action, - const SbVec3f& vec, +void ConvertFromInventor::transformLight(SoCallbackAction* action, + const SbVec3f& vec, osg::Vec3& transVec) { osg::Matrix modelMat; @@ -449,55 +1051,73 @@ void ConvertFromInventor::transformLight(SoCallbackAction* action, transVec = modelMat.preMult(transVec); } /////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preLight(void* data, SoCallbackAction* action, +SoCallbackAction::Response +ConvertFromInventor::preLight(void* data, SoCallbackAction* action, const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preLight() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preLight() " << node->getTypeId().getName().getString() << std::endl; #endif ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - static int lightNum = 1; - + // Return if the light is not on const SoLight* ivLight = (const SoLight*) node; if (!ivLight->on.getValue()) return SoCallbackAction::CONTINUE; + // Create new OSG light + IvStateItem &ivState = thisPtr->ivStateStack.top(); osg::ref_ptr osgLight = new osg::Light; - osgLight->setLightNum(lightNum++); + // Light name const char* name = ivLight->getName().getString(); osgLight->setName(name); - + // Get color and intensity SbVec3f lightColor = ivLight->color.getValue(); float intensity = ivLight->intensity.getValue(); // Set color and intensity + osgLight->setAmbient(osg::Vec4(0.f, 0.f, 0.f, 1.f)); osgLight->setDiffuse(osg::Vec4(lightColor[0] * intensity, lightColor[1] * intensity, lightColor[2] * intensity, 1)); + osgLight->setSpecular(osg::Vec4(lightColor[0] * intensity, + lightColor[1] * intensity, + lightColor[2] * intensity, 1)); + // Light type if (node->isOfType(SoDirectionalLight::getClassTypeId())) { SoDirectionalLight *dirLight = (SoDirectionalLight *) node; - + +#if 1 // Let's place the light to its place in scene graph instead of + // old approach of global light group. + SbVec3f l(dirLight->direction.getValue()); + osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 0.)); +#else osg::Vec3 transVec; thisPtr->transformLight(action, dirLight->direction.getValue(), transVec); - osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(), - transVec.z(), 0)); + osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(), + transVec.z(), 0.)); +#endif } else if (node->isOfType(SoPointLight::getClassTypeId())) { SoPointLight* ptLight = (SoPointLight *) node; +#if 1 // Let's place the light to its place in scene graph instead of + // old approach of global light group. + SbVec3f l(ptLight->location.getValue()); + osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 1.)); +#else osg::Vec3 transVec; thisPtr->transformLight(action, ptLight->location.getValue(), transVec); - osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(), - transVec.z(), 0)); + osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(), + transVec.z(), 1.)); +#endif } else if (node->isOfType(SoSpotLight::getClassTypeId())) { @@ -506,36 +1126,160 @@ ConvertFromInventor::preLight(void* data, SoCallbackAction* action, osgLight->setSpotExponent(spotLight->dropOffRate.getValue() * 128.0); osgLight->setSpotCutoff(spotLight->cutOffAngle.getValue()*180.0/osg::PI); +#if 1 // Let's place the light to its place in scene graph instead of + // old approach of global light group. + SbVec3f l(spotLight->location.getValue()); + osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 1.)); + l = spotLight->direction.getValue(); + osgLight->setDirection(osg::Vec3(l[0], l[1], l[2])); +#else osg::Vec3 transVec; thisPtr->transformLight(action, spotLight->location.getValue(), transVec); - osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(), - transVec.z(), 0)); + osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(), + transVec.z(), 1.)); thisPtr->transformLight(action, spotLight->direction.getValue(),transVec); - osgLight->setDirection(osg::Vec3(transVec.x(), transVec.y(), + osgLight->setDirection(osg::Vec3(transVec.x(), transVec.y(), transVec.z())); - } - - // Add light to list in the current level - if (thisPtr->lightStack.size()) - { - LightList lightList; - lightList = thisPtr->lightStack.top(); - lightList.push_back(osgLight.get()); - thisPtr->lightStack.pop(); - thisPtr->lightStack.push(lightList); +#endif } - // add a light source node to the scene graph + // Attenuation + if (!node->isOfType(SoDirectionalLight::getClassTypeId())) { + SbVec3f att = action->getLightAttenuation(); + osgLight->setConstantAttenuation(att[2]); + osgLight->setLinearAttenuation(att[1]); + osgLight->setQuadraticAttenuation(att[0]); + } else { + // keep default light settings for directional light, e.g. + // no attenuation + } + + // Append the light into the scene and onto the state stack + osgLight->setLightNum(ivState.currentLights.size()); + ivState.currentLights.push_back(osgLight); + osg::ref_ptr ls = new osg::LightSource(); ls->setLight(osgLight.get()); ls->setName(ivLight->getName().getString()); - if (thisPtr->lightGroup == NULL) { - thisPtr->lightGroup = new osg::Group; - thisPtr->lightGroup->setName("IvLightGroup"); - thisPtr->_root->addChild(thisPtr->lightGroup.get()); +#if 1 // Let's place the light to its place in scene graph instead of + // old approach of global light group. + thisPtr->ivPushState(action, node, + IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE | + IvStateItem::APPEND_AT_PUSH, ls.get()); +#else + if (!(thisPtr->lightGroup.get())) + thisPtr->lightGroup = new osg::Group(); + thisPtr->lightGroup->addChild(ls); +#endif + + return SoCallbackAction::CONTINUE; +} +/////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::preEnvironment(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preLight() " + << node->getTypeId().getName().getString() << std::endl; +#endif + + ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); + IvStateItem &ivState = thisPtr->ivStateStack.top(); + + ivState.currentAmbientLight = ((SoEnvironment*)node)->ambientColor.getValue() * + ((SoEnvironment*)node)->ambientIntensity.getValue(); + + return SoCallbackAction::CONTINUE; +} +/////////////////////////////////////////////////////////////////// +#ifdef INVENTOR_SHADERS_AVAILABLE +static bool +convertShader(osg::Shader::Type osgShaderType, + const SoShaderObject *ivShader, + osg::Program *osgProgram) +{ + // NULL shader is not converted while returning success + if (ivShader == NULL) + return true; + + // Create shader + osg::ref_ptr osgShader = new osg::Shader(osgShaderType); + if (ivShader->sourceType.getValue() == SoShaderObject::FILENAME) + osgShader->loadShaderSourceFromFile(ivShader->sourceProgram.getValue().getString()); + else + if (ivShader->sourceType.getValue() == SoShaderObject::GLSL_PROGRAM) + osgShader->setShaderSource(ivShader->sourceProgram.getValue().getString()); + else { + osg::notify(osg::WARN) << NOTIFY_HEADER << "Can not convert " + << "shader. Unsupported shader language." << std::endl; + return false; } - thisPtr->lightGroup->addChild(ls.get()); + + return osgProgram->addShader(osgShader.get()); +} +#endif // INVENTOR_SHADERS_AVAILABLE +/////////////////////////////////////////////////////////////////// +SoCallbackAction::Response +ConvertFromInventor::preShaderProgram(void* data, SoCallbackAction* action, + const SoNode* node) +{ +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preShaderProgram() " + << node->getTypeId().getName().getString() << std::endl; +#endif + +#ifdef INVENTOR_SHADERS_AVAILABLE + + ConvertFromInventor *thisPtr = (ConvertFromInventor*)data; + IvStateItem &ivState = thisPtr->ivStateStack.top(); + + // Get Inventor nodes + // Note: Shaders are available since Coin 2.5 (including + // geometry shader) + const SoShaderProgram *ivProgram = (const SoShaderProgram*)node; + const SoVertexShader *ivVertexShader = NULL; + const SoGeometryShader *ivGeometryShader = NULL; + const SoFragmentShader *ivFragmentShader = NULL; + + for (int i=0, c=ivProgram->shaderObject.getNum(); ishaderObject[i]; + if (!shader->isOfType(SoShaderObject::getClassTypeId())) + continue; + if (shader->isActive.getValue() == FALSE) + continue; + + if (shader->isOfType(SoVertexShader::getClassTypeId())) + ivVertexShader = (const SoVertexShader*)shader; + if (shader->isOfType(SoGeometryShader::getClassTypeId())) + ivGeometryShader = (const SoGeometryShader*)shader; + if (shader->isOfType(SoFragmentShader::getClassTypeId())) + ivFragmentShader = (const SoFragmentShader*)shader; + } + + // Create OSG shader + osg::Program *osgProgram = new osg::Program(); + if (!convertShader(osg::Shader::VERTEX, ivVertexShader, osgProgram)) + osg::notify(osg::WARN) << NOTIFY_HEADER + << "Failed to add vertex shader." << std::endl; + if (!convertShader(osg::Shader::GEOMETRY, ivGeometryShader, osgProgram)) + osg::notify(osg::WARN) << NOTIFY_HEADER + << "Failed to add geometry shader." << std::endl; + if (!convertShader(osg::Shader::FRAGMENT, ivFragmentShader, osgProgram)) + osg::notify(osg::WARN) << NOTIFY_HEADER + << "Failed to add fragment shader." << std::endl; + + // Put shader to the state stack + ivState.currentGLProgram = osgProgram; + +#else + + osg::notify(osg::WARN) << NOTIFY_HEADER << "Warning: The model " + "contains shaders while your Inventor does not support " + "them." << std::endl; +#endif return SoCallbackAction::CONTINUE; } @@ -544,17 +1288,18 @@ osg::ref_ptr ConvertFromInventor::getStateSet(SoCallbackAction* action) { osg::ref_ptr stateSet = new osg::StateSet; - + // Inherit modes from the global state stateSet->clear(); + // Inventor State Stack + IvStateItem &ivState = ivStateStack.top(); + // Convert the IV texture to OSG texture if any osg::ref_ptr texture; - const SoNode *ivTexture = soTexStack.top(); + const SoNode *ivTexture = ivState.currentTexture; if (ivTexture) { - osg::notify(osg::INFO)<<"Have texture"<setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON); - + // propogate name if(texture.valid()) { @@ -586,18 +1331,20 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) case SoTexture2::DECAL: texEnv->setMode(osg::TexEnv::DECAL); break; - case SoTexture2::BLEND: + case SoTexture2::BLEND: { texEnv->setMode(osg::TexEnv::BLEND); + SbColor c(action->getTextureBlendColor()); + texEnv->setColor(osg::Vec4(c[0], c[1], c[2], 1.f)); break; -#if defined(__COIN__) && ((COIN_MAJOR_VERSION==2 && COIN_MINOR_VERSION>=2) || (COIN_MAJOR_VERSION>2)) + } // SGI's Inventor does not have REPLACE mode, but the Coin 3D library does. // Coin supports REPLACE since 2.2 release, TGS Inventor from 4.0. // Let's convert to the TexEnv anyway. - case SoTexture2::REPLACE: + case 0x1E01: //SoTexture2::REPLACE: texEnv->setMode(osg::TexEnv::REPLACE); break; -#endif default: + osg::notify(osg::WARN) << "Unsupported TexEnv mode." << std::endl; break; } @@ -610,12 +1357,12 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) // Get the material colors action->getMaterial(ambient, diffuse, specular, emission, shininess, transparency, 0); - + // Set transparency SbBool hasTextureTransparency = FALSE; if (ivTexture) { SbVec2s tmp; - int bpp; + int bpp = 0; if (ivTexture->isOfType(SoTexture2::getClassTypeId())) ((SoTexture2*)ivTexture)->image.getValue(tmp, bpp); #ifdef __COIN__ @@ -623,7 +1370,6 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) if (ivTexture->isOfType(SoVRMLImageTexture::getClassTypeId())) { const SbImage *img = ((SoVRMLImageTexture*)ivTexture)->getImage(); if (img) img->getValue(tmp, bpp); - else bpp = 0; } #endif hasTextureTransparency = bpp==4 || bpp==2; @@ -632,13 +1378,13 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) if (transparency > 0 || hasTextureTransparency) { osg::ref_ptr transparency = new osg::BlendFunc; - stateSet->setAttributeAndModes(transparency.get(), + stateSet->setAttributeAndModes(transparency.get(), osg::StateAttribute::ON); - + // Enable depth sorting for transparent objects stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); } - + // Set linewidth if (action->getLineWidth()) { @@ -654,8 +1400,8 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) point->setSize(action->getPointSize()); stateSet->setAttributeAndModes(point.get(), osg::StateAttribute::ON); } - - // Set draw mode + + // Set draw mode switch (action->getDrawStyle()) { case SoDrawStyle::FILLED: @@ -663,7 +1409,7 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) #if 0 // OSG defaults to filled draw style, so no need to set redundent state. osg::PolygonMode *polygonMode = new osg::PolygonMode; - polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, + polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL); stateSet->setAttributeAndModes(polygonMode, osg::StateAttribute::ON); #endif @@ -672,7 +1418,7 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) case SoDrawStyle::LINES: { osg::ref_ptr polygonMode = new osg::PolygonMode; - polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, + polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE); stateSet->setAttributeAndModes(polygonMode.get(), osg::StateAttribute::ON); break; @@ -680,7 +1426,7 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) case SoDrawStyle::POINTS: { osg::ref_ptr polygonMode = new osg::PolygonMode; - polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, + polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::POINT); stateSet->setAttributeAndModes(polygonMode.get(), osg::StateAttribute::ON); break; @@ -696,7 +1442,7 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) osg::ref_ptr cullFace = new osg::CullFace; cullFace->setMode(osg::CullFace::BACK); stateSet->setAttributeAndModes(cullFace.get(), osg::StateAttribute::ON); - } + } // Set lighting if (action->getLightModel() == SoLightModel::BASE_COLOR) @@ -706,21 +1452,21 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) // Set the material osg::ref_ptr material = new osg::Material; - material->setAmbient(osg::Material::FRONT_AND_BACK, - osg::Vec4(ambient[0], ambient[1], ambient[2], + material->setAmbient(osg::Material::FRONT_AND_BACK, + osg::Vec4(ambient[0], ambient[1], ambient[2], 1.0 - transparency)); - material->setDiffuse(osg::Material::FRONT_AND_BACK, - osg::Vec4(diffuse[0], diffuse[1], diffuse[2], + material->setDiffuse(osg::Material::FRONT_AND_BACK, + osg::Vec4(diffuse[0], diffuse[1], diffuse[2], 1.0 - transparency)); - material->setSpecular(osg::Material::FRONT_AND_BACK, - osg::Vec4(specular[0], specular[1], specular[2], + material->setSpecular(osg::Material::FRONT_AND_BACK, + osg::Vec4(specular[0], specular[1], specular[2], 1.0 - transparency)); - material->setEmission(osg::Material::FRONT_AND_BACK, - osg::Vec4(emission[0], emission[1], emission[2], + material->setEmission(osg::Material::FRONT_AND_BACK, + osg::Vec4(emission[0], emission[1], emission[2], 1.0 - transparency)); material->setTransparency(osg::Material::FRONT_AND_BACK, transparency); if (specular[0] || specular[1] || specular[2]) - material->setShininess(osg::Material::FRONT_AND_BACK, + material->setShininess(osg::Material::FRONT_AND_BACK, shininess*128.0); else material->setShininess(osg::Material::FRONT_AND_BACK, 0.0); @@ -731,20 +1477,46 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) stateSet->setName(appearanceName.getString()); stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); + // Set global ambient light + // note on osg::LightModel default values: + // colorControl: SINGLE_COLOR, localViewer: false, twoSided: false + osg::LightModel *lightModel = new osg::LightModel(); + const SbColor &c = ivState.currentAmbientLight; + lightModel->setAmbientIntensity(osg::Vec4(c[0], c[1], c[2], 1.0)); #if 0 // disable as two sided lighting causes problem under NVidia, and the above osg::Material settings are single sided anway.. +update: The mentioned bug is probably just for very old NVidia drivers (commit time of the comment is 2005-03-18). + The proper solution should be to set two sided lighting based on SoShapeHints node. Need to be developed. PCJohn-2010-01-20 // Set two sided lighting - osg::LightModel* lightModel = new osg::LightModel; lightModel->setTwoSided(true); - stateSet->setAttributeAndModes(lightModel, osg::StateAttribute::ON); #endif + stateSet->setAttributeAndModes(lightModel, osg::StateAttribute::ON); + // Set lights - LightList lightList = lightStack.top(); - for (unsigned int i = 0; i < lightList.size(); i++) - stateSet->setAttributeAndModes(lightList[i], + for (unsigned int i = 0; i < ivState.currentLights.size(); i++) + stateSet->setAttributeAndModes(ivState.currentLights[i].get(), osg::StateAttribute::ON); + } - + + // Shader program setup + if (ivState.currentGLProgram.get() != NULL) { + stateSet->setAttributeAndModes(ivState.currentGLProgram.get(), + osg::StateAttribute::ON); + } + + // Shader program uniforms + if (ivState.currentGLProgram.get() != NULL) { + for (int i=0, c=ivState.currentGLProgram->getNumShaders(); igetShader(i)->getShaderSource(); + if (shaderCode.find("coin_texunit0_model") != std::string::npos) { + int mode = (ivTexture!=NULL) ? action->getTextureModel() : 0; + stateSet->addUniform(new osg::Uniform("coin_texunit0_model", mode)); + break; + } + } + } + return stateSet; } //////////////////////////////////////////////////////////////////// @@ -752,8 +1524,12 @@ osg::Texture2D* ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, SoCallbackAction* action) { - osg::notify(osg::INFO)<<"convertIVTexToOSGTex of type "<< - soNode->getTypeId().getName().getString()<getTypeId().getName().getString() + << ")" << std::endl; +#endif SbVec2s soSize; int soNC; @@ -761,7 +1537,8 @@ ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, // Get the texture size and components const unsigned char* soImageData = action->getTextureImage(soSize, soNC); if (!soImageData) { - osg::notify(osg::WARN) << "IV import warning: Error while loading texture data." << std::endl; + osg::notify(osg::WARN) << NOTIFY_HEADER + << "Warning: Error while loading texture data." << std::endl; return NULL; } @@ -781,20 +1558,26 @@ ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, #ifdef __COIN__ else if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId())) - fileName = ((SoVRMLImageTexture*)soNode)->url.getNum() >= 1 ? + fileName = ((SoVRMLImageTexture*)soNode)->url.getNum() >= 1 ? ((SoVRMLImageTexture*)soNode)->url.getValues(0)[0].getString() : ""; #endif else - osg::notify(osg::WARN) << "IV import warning: Unsupported texture type: " - << soNode->getTypeId().getName().getString() << std::endl; + osg::notify(osg::WARN) << NOTIFY_HEADER + << " Warning: Unsupported texture type: " + << soNode->getTypeId().getName().getString() << std::endl; - osg::notify(osg::INFO) << fileName << " -> "; +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER + << " Converting file name: " << fileName << " -> "; +#endif if (fileName[0]=='\"') fileName.erase(fileName.begin()); - if (fileName.size() > 0 && fileName[fileName.size()-1]=='\"') + if (fileName.size() > 0 && fileName[fileName.size()-1]=='\"') fileName.erase(fileName.begin()+fileName.size()-1); - osg::notify(osg::INFO) << fileName << std::endl; +#ifdef DEBUG_IV_PLUGIN + osg::notify(osg::DEBUG_INFO) << fileName << std::endl; +#endif - // Create the osg::Image + // Create the osg::Image osg::ref_ptr osgImage = new osg::Image; osgImage->setFileName(fileName); GLenum formats[] = {GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA}; @@ -816,7 +1599,7 @@ ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, texWrapMap[SoTexture2::REPEAT] = osg::Texture2D::REPEAT; firstTime = false; } - + // Set texture wrap mode #ifdef __COIN__ if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId())) { @@ -839,15 +1622,15 @@ ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, osgTex->setWrap(osg::Texture2D::WRAP_T, texWrapMap[action->getTextureWrapT()]); } - return osgTex; + return osgTex; } /////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preInfo(void* data, SoCallbackAction* action, - const SoNode* node) +SoCallbackAction::Response +ConvertFromInventor::preInfo(void* data, SoCallbackAction* action, + const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preInfo() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preInfo() " << node->getTypeId().getName().getString() << std::endl; #endif ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); @@ -856,181 +1639,13 @@ ConvertFromInventor::preInfo(void* data, SoCallbackAction* action, return SoCallbackAction::CONTINUE; } - -/////////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preGroup(void* data, SoCallbackAction* action, - const SoNode* node) -{ -#ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preGroup() " - << node->getTypeId().getName().getString() << std::endl; -#endif - - ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - - // Create a new Group or LOD and add it to the stack - osg::ref_ptr group; - if (node->isOfType(SoLOD::getClassTypeId())) { - group = new osg::LOD; - } - else { - group = new osg::Group; - } - - - thisPtr->groupStack.top()->addChild(group.get()); - thisPtr->groupStack.push(group.get()); - - // SoTransform nodes are not the parent of the nodes they apply to - // But are in the same separator as them - SoChildList *kids = node->getChildren(); - for (int i=0; igetLength(); i++) { - SoNode* kid = (SoNode*)kids->get(i); - if (kid->isOfType(SoTransform::getClassTypeId())) { - SoTransform* t = (SoTransform*)kid; - SbVec3f axis, center, trans, scale; - float angle; - - center = t->center.getValue(); - t->rotation.getValue(axis, angle); - trans = t->translation.getValue(); - scale = t->scaleFactor.getValue(); - std::string name = t->getName().getString(); - - thisPtr->addMatrixTransform(name, axis, angle, center, trans, scale); - } - } -#ifdef __COIN__ - if (node->isOfType(SoVRMLTransform::getClassTypeId())) { - std::string name; - if (thisPtr->transformInfoName != "") { - name = std::string("INFO_"); - name += thisPtr->transformInfoName.getString(); - name += "_trans"; - } - else { - name = node->getName(); - } - - SoVRMLTransform* vt = (SoVRMLTransform*)node; - SbVec3f axis, center, trans, scale; - float angle; - - center = vt->center.getValue(); - vt->rotation.getValue(axis, angle); - trans = vt->translation.getValue(); - scale = vt->scale.getValue(); - - thisPtr->addMatrixTransform(name, axis, angle, center, trans, scale); - } -#endif - if (node->isOfType(SoSeparator::getClassTypeId())) - { - if (thisPtr->soTexStack.size()) - thisPtr->soTexStack.push(thisPtr->soTexStack.top()); - else - thisPtr->soTexStack.push(NULL); - if (thisPtr->lightStack.size()) - { - LightList lightList = thisPtr->lightStack.top(); - thisPtr->lightStack.push(lightList); - } - } - - return SoCallbackAction::CONTINUE; -} -////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::postGroup(void* data, SoCallbackAction* action, - const SoNode* node) -{ - // Handle SoLOD nodes specially - if (node->isOfType(SoLOD::getClassTypeId())) - return postLOD(data, action, node); - -#ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "postGroup() " - << node->getTypeId().getName().getString() << std::endl; -#endif - ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - - // Pop all the groups that are Transforms - osg::ref_ptr group = thisPtr->groupStack.top(); - while (strcmp(group->className(), "MatrixTransform") == 0) - { - thisPtr->groupStack.pop(); - group = thisPtr->groupStack.top(); - } - - // Pop the group from the stack - thisPtr->groupStack.pop(); - - // Pop the state if the group is a Separator - if (node->isOfType(SoSeparator::getClassTypeId())) - { - thisPtr->soTexStack.pop(); - thisPtr->lightStack.pop(); - } - - return SoCallbackAction::CONTINUE; -} -//////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::postLOD(void* data, SoCallbackAction *, - const SoNode* node) -{ -#ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "postLOD() " - << node->getTypeId().getName().getString() << std::endl; -#endif - - ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - - // Inventor and OSG LOD node - SoLOD *ivLOD = (SoLOD *) node; - osg::LOD *lod = dynamic_cast(thisPtr->groupStack.top()); - - // Get the center of LOD and set it - SbVec3f ivCenter = ivLOD->center.getValue(); - lod->setCenter(osg::Vec3(ivCenter[0], ivCenter[1], ivCenter[2])); - - // Verify the number of children and range values - int num = thisPtr->groupStack.top()->getNumChildren(); - if (ivLOD->range.getNum()+1 != num && !(num == 0 && ivLOD->range.getNum() == 0)) { - osg::notify(osg::WARN) << "IV import warning: SoLOD does not " - << "contain correct data in range field." << std::endl; - if (ivLOD->range.getNum()+1 < num) { - thisPtr->groupStack.top()->removeChildren(ivLOD->range.getNum() + 1, - num - ivLOD->range.getNum() - 1); - num = ivLOD->range.getNum() + 1; - } - } - - // Get the ranges and set it - if (num > 0) { - if (num == 1) - lod->setRange(0, 0.0, FLT_MAX); - else { - lod->setRange(0, 0.0, ivLOD->range[0]); - for (int i = 1; i < num-2; i++) - lod->setRange(i, ivLOD->range[i-1], ivLOD->range[i]); - lod->setRange(num-1, ivLOD->range[num-2], FLT_MAX); - } - } - - // Pop the group from the stack - thisPtr->groupStack.pop(); - - return SoCallbackAction::CONTINUE; -} ///////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preRotor(void* data, SoCallbackAction *, - const SoNode* node) +SoCallbackAction::Response +ConvertFromInventor::preRotor(void *data, SoCallbackAction *action, + const SoNode *node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preRotor() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preRotor() " << node->getTypeId().getName().getString() << std::endl; #endif @@ -1044,30 +1659,41 @@ ConvertFromInventor::preRotor(void* data, SoCallbackAction *, // Create a new osg::MatrixTransform osg::ref_ptr rotorTransform = new osg::MatrixTransform; - + // Create a Rotor Callback equivalent to the inventor Rotor osg::Vec3 pivot(0, 0, 0); osg::Vec3 axis(ivAxis[0], ivAxis[1], ivAxis[2]); - osg::ref_ptr rotorCallback - = new osgUtil::TransformCallback(pivot, axis, + osg::ref_ptr rotorCallback + = new osgUtil::TransformCallback(pivot, axis, 2 * osg::PI * ivRotor->speed.getValue()); // Set the app callback rotorTransform->setUpdateCallback(rotorCallback.get()); - - // Push the rotor transform onto the group stack - thisPtr->groupStack.top()->addChild(rotorTransform.get()); - thisPtr->groupStack.push(rotorTransform.get()); - return SoCallbackAction::CONTINUE; + // Push the rotor onto the state stack + thisPtr->ivPushState(action, node, + IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE | + IvStateItem::APPEND_AT_PUSH, rotorTransform.get()); + + // Append initial rotation to the model matrix + if (!ivRotor->rotation.isIgnored()) { + SoModelMatrixElement::rotateBy(action->getState(), ivRotor, + ivRotor->rotation.getValue()); + } + + // Don't do the traversal of the SoShuttle + // since it was seen on Coin that is does not append just + // initial shuttle position, but some interpolated one, + // resulting in incorrect animation. + return SoCallbackAction::PRUNE; } //////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::prePendulum(void* data, SoCallbackAction *, +SoCallbackAction::Response +ConvertFromInventor::prePendulum(void* data, SoCallbackAction *action, const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "prePendulum() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "prePendulum() " << node->getTypeId().getName().getString() << std::endl; #endif @@ -1079,32 +1705,54 @@ ConvertFromInventor::prePendulum(void* data, SoCallbackAction *, float startAngle, endAngle; ivPendulum->rotation0.getValue(ivAxis0, startAngle); ivPendulum->rotation1.getValue(ivAxis1, endAngle); + ivAxis0.normalize(); + ivAxis1.normalize(); + + // Reverse axis and direction if required + // Actually, this will produce correct results only when axis is + // opposite to each other, and approximate results when nearly + // opposite and garbage otherwise. + if ((ivAxis0+ivAxis1).length() < 0.5 ) { + ivAxis1 = -ivAxis1; + endAngle = -endAngle; + } // Create a new osg::MatrixTransform osg::ref_ptr pendulumTransform = new osg::MatrixTransform; - + // Create a Pendulum Callback equivalent to the inventor Rotor - osg::Vec3 axis(ivAxis0[0], ivAxis0[1], ivAxis0[2]); - PendulumCallback* pendulumCallback + // Use axis from of the bigger angle (to avoid lost axis when + // angle is zero - see SbRotation and quaternion theory). + osg::Vec3 axis; + if (fabs(startAngle) > fabs(endAngle)) + axis = osg::Vec3(ivAxis0[0], ivAxis0[1], ivAxis0[2]); + else + axis = osg::Vec3(ivAxis1[0], ivAxis1[1], ivAxis1[2]); + PendulumCallback* pendulumCallback = new PendulumCallback(axis, startAngle, endAngle, ivPendulum->speed.getValue()); // Set the app callback pendulumTransform->setUpdateCallback(pendulumCallback); - - // Push the pendulum transform onto the group stack - thisPtr->groupStack.top()->addChild(pendulumTransform.get()); - thisPtr->groupStack.push(pendulumTransform.get()); - return SoCallbackAction::CONTINUE; + // Push the pendulum onto the state stack + thisPtr->ivPushState(action, node, + IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE | + IvStateItem::APPEND_AT_PUSH, pendulumTransform.get()); + + // Don't do the traversal of the SoShuttle + // since it was seen on Coin that is does not append just + // initial shuttle position, but some interpolated one, + // resulting in incorrect animation. + return SoCallbackAction::PRUNE; } //////////////////////////////////////////////////////////////// -SoCallbackAction::Response -ConvertFromInventor::preShuttle(void* data, SoCallbackAction *, +SoCallbackAction::Response +ConvertFromInventor::preShuttle(void* data, SoCallbackAction *action, const SoNode* node) { #ifdef DEBUG_IV_PLUGIN - osg::notify(osg::INFO) << "preShuttle() " + osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preShuttle() " << node->getTypeId().getName().getString() << std::endl; #endif @@ -1118,25 +1766,30 @@ ConvertFromInventor::preShuttle(void* data, SoCallbackAction *, // Create a new osg::MatrixTransform osg::ref_ptr shuttleTransform = new osg::MatrixTransform; - + // Create a shuttle Callback equivalent to the inventor Rotor osg::Vec3 startPos(ivStartPos[0], ivStartPos[1], ivStartPos[2]); osg::Vec3 endPos(ivEndPos[0], ivEndPos[1], ivEndPos[2]); - ShuttleCallback* shuttleCallback + ShuttleCallback* shuttleCallback = new ShuttleCallback(startPos, endPos, ivShuttle->speed.getValue()); // Set the app callback shuttleTransform->setUpdateCallback(shuttleCallback); - - // Push the shuttle transform onto the group stack - thisPtr->groupStack.top()->addChild(shuttleTransform.get()); - thisPtr->groupStack.push(shuttleTransform.get()); - return SoCallbackAction::CONTINUE; + // Push the shuttle onto the state stack + thisPtr->ivPushState(action, node, + IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE | + IvStateItem::APPEND_AT_PUSH, shuttleTransform.get()); + + // Don't do the traversal of the SoShuttle + // since it was seen on Coin that is does not append just + // initial shuttle position, but some interpolated one, + // resulting in incorrect animation. + return SoCallbackAction::PRUNE; } //////////////////////////////////////////////////////////// void ConvertFromInventor::addVertex(SoCallbackAction* action, - const SoPrimitiveVertex *v, + const SoPrimitiveVertex *v, int index) { // Get the coordinates of the vertex @@ -1149,19 +1802,21 @@ void ConvertFromInventor::addVertex(SoCallbackAction* action, if ((normalBinding == osg::Geometry::BIND_PER_VERTEX) || (normalBinding == osg::Geometry::BIND_PER_PRIMITIVE && index == 0)) { - if (vertexOrder == CLOCKWISE) - normals.push_back(osg::Vec3(-norm[0], -norm[1], -norm[2])); - else + // What is this? Why to invert normals at CLOCKWISE vertex ordering? + // PCJohn 2009-12-13 + //if (vertexOrder == CLOCKWISE) + // normals.push_back(osg::Vec3(-norm[0], -norm[1], -norm[2])); + //else normals.push_back(osg::Vec3(norm[0], norm[1], norm[2])); } if (colorBinding == osg::Geometry::BIND_PER_VERTEX || colorBinding == osg::Geometry::BIND_PER_PRIMITIVE) { - // Get the material/color + // Get the material/color SbColor ambient, diffuse, specular, emission; float transparency, shininess; - action->getMaterial(ambient, diffuse, specular, emission, shininess, + action->getMaterial(ambient, diffuse, specular, emission, shininess, transparency, v->getMaterialIndex()); if (colorBinding == osg::Geometry::BIND_PER_VERTEX) colors.push_back(osg::Vec4(diffuse[0], diffuse[1], diffuse[2], @@ -1176,71 +1831,13 @@ void ConvertFromInventor::addVertex(SoCallbackAction* action, textureCoords.push_back(osg::Vec2(texCoord[0], texCoord[1])); } //////////////////////////////////////////////////////////////////////////// -void ConvertFromInventor::addMatrixTransform(const std::string& name, SbVec3f axis, float angle, SbVec3f center, SbVec3f trans, SbVec3f scale) -{ - osg::Matrix mat; - if (trans.length() != 0.0 || name != "") - { - mat.makeIdentity(); - mat.setTrans(trans[0], trans[1], trans[2]); - osg::ref_ptr mt = new osg::MatrixTransform(mat); - if (name != "") { - std::string name2 = name; - name2 += "_t"; - mt->setName(name2); - } - groupStack.top()->addChild(mt.get()); - groupStack.push(mt.get()); - } - - if (center.length() != 0.0) { - mat.makeIdentity(); - mat.setTrans(center[0], center[1], center[2]); - osg::ref_ptr mt = new osg::MatrixTransform(mat); - groupStack.top()->addChild(mt.get()); - groupStack.push(mt.get()); - } - - if (angle != 0.0 || name != "") - { - osg::Quat q(angle, osg::Vec3f(axis[0], axis[1], axis[2])); - mat.makeIdentity(); - mat.setRotate(q); - osg::ref_ptr mt = new osg::MatrixTransform(mat); - if (name != "") { - std::string name2 = name; - name2 += "_r"; - mt->setName(name2); - } - groupStack.top()->addChild(mt.get()); - groupStack.push(mt.get()); - } - - if (center.length() != 0.0) { - center.negate(); - mat.makeIdentity(); - mat.setTrans(center[0], center[1], center[2]); - osg::ref_ptr mt = new osg::MatrixTransform(mat); - groupStack.top()->addChild(mt.get()); - groupStack.push(mt.get()); - } - - if (scale[0] != 1.0 || scale[1] != 1.0 || scale[2] != 1.0) { - mat.makeIdentity(); - mat.makeScale(scale[0], scale[1], scale[2]); - osg::ref_ptr smt = new osg::MatrixTransform(mat); - groupStack.top()->addChild(smt.get()); - groupStack.push(smt.get()); - } -} -//////////////////////////////////////////////////////////////////////////// void ConvertFromInventor::addTriangleCB(void* data, SoCallbackAction* action, const SoPrimitiveVertex* v0, const SoPrimitiveVertex* v1, const SoPrimitiveVertex* v2) { ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data); - + switch (thisPtr->vertexOrder) { case CLOCKWISE: diff --git a/src/osgPlugins/Inventor/ConvertFromInventor.h b/src/osgPlugins/Inventor/ConvertFromInventor.h index 8eba8a7ab..a885cff86 100644 --- a/src/osgPlugins/Inventor/ConvertFromInventor.h +++ b/src/osgPlugins/Inventor/ConvertFromInventor.h @@ -10,6 +10,7 @@ #include #include #include +#include class ConvertFromInventor { @@ -17,42 +18,53 @@ class ConvertFromInventor ConvertFromInventor(); ~ConvertFromInventor(); + /// Conversts from IV to OSG scene graph osg::Node* convert(SoNode* rootIVNode); + /** + * Preprocessing restructure the scene for the convertor + * to be able to convert some peculiar scene constructions. + * Resulting scene is geometrically equivalent to the source + * scene. The preprocessing is related to grouping nodes only + * (SoSeparator, SoGroup, SoLOD, SoSwitch,...) and their + * treatment with traversal state. + */ + void preprocess(SoNode *root); + private: - // Callback functions for converting inventor scene graph to osg + // Callback functions for converting inventor scene graph to osg // scene graph + static SoCallbackAction::Response preNode(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response postNode(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response preTransformSeparator(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response postTransformSeparator(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response preLOD(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response postLOD(void* data, + SoCallbackAction* action, const SoNode* node); static SoCallbackAction::Response preShape(void* data, SoCallbackAction* action, const SoNode* node); static SoCallbackAction::Response postShape(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response preGroup(void* data, + static SoCallbackAction::Response postTexture(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response postGroup(void* data, + static SoCallbackAction::Response preLight(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response preTexture(void* data, + static SoCallbackAction::Response preEnvironment(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response preLight(void* data, + static SoCallbackAction::Response preShaderProgram(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response preRotor(void* data, + static SoCallbackAction::Response preRotor(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response prePendulum(void* data, + static SoCallbackAction::Response prePendulum(void* data, SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response preShuttle(void* data, - SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response postLOD(void* data, - SoCallbackAction* action, const SoNode* node); - ///Callback for VRMLImageTexture - ///\param data The pointer to ConvertFromInventor object - ///\param action The action handling class - ///\param node The current VRMLImageTexture node - static SoCallbackAction::Response preVRMLImageTexture(void* data, - SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response preVRMLAppearance(void* data, - SoCallbackAction* action, const SoNode* node); - static SoCallbackAction::Response postVRMLAppearance(void* data, + static SoCallbackAction::Response preShuttle(void* data, SoCallbackAction* action, const SoNode* node); static SoCallbackAction::Response preInfo(void* data, SoCallbackAction* action, const SoNode* node); @@ -67,18 +79,23 @@ class ConvertFromInventor static void addPointCB(void* data, SoCallbackAction* action, const SoPrimitiveVertex *v0); + static SoCallbackAction::Response restructure(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response restructurePreNode(void* data, + SoCallbackAction* action, const SoNode* node); + static SoCallbackAction::Response restructurePostNode(void* data, + SoCallbackAction* action, const SoNode* node); + private: SbString transformInfoName; SbName appearanceName; - bool inAppearanceWithNoTexture; - void addMatrixTransform(const std::string& name, SbVec3f axis, float angle, SbVec3f center, SbVec3f trans, SbVec3f scale); - void addVertex(SoCallbackAction* action, const SoPrimitiveVertex* v, + void addVertex(SoCallbackAction* action, const SoPrimitiveVertex* v, int index); osg::ref_ptr getStateSet(SoCallbackAction* action); - osg::Texture2D* convertIVTexToOSGTex(const SoNode* soNode, + osg::Texture2D* convertIVTexToOSGTex(const SoNode* soNode, SoCallbackAction* action); void transformLight(SoCallbackAction* action, const SbVec3f& vec, @@ -99,7 +116,7 @@ class ConvertFromInventor std::vector colors; std::vector textureCoords; - // Num of primitive and primitive type + // Num of primitive and primitive type int numPrimitives; osg::PrimitiveSet::Mode primitiveType; @@ -107,24 +124,104 @@ class ConvertFromInventor enum VertexOrder { CLOCKWISE, COUNTER_CLOCKWISE }; VertexOrder vertexOrder; - // Stack of group nodes (used to build the scene graph) - std::stack groupStack; - // Stack of texture nodes (used for attaching the right texture to the - // geosets). Supported types are SoTexture2 and SoVRMLImageTexture for now. - std::stack soTexStack; - // Mapping from SoTexture2 and SoVRMLImageTexture to already converted // osg::Texture2D - avoids duplication of same texture objects std::map ivToOsgTexMap; - // Stack to maintain the list of lights at each level of the - // scenegraph - typedef std::vector LightList; - std::stack lightStack; - osg::ref_ptr _root;/// lightGroup; + /** + * IvStateItem aids lack of some state retrieval methods + * of SoCallbackAction. State is maintained in stack + * manner separately from Open Inventor. + */ + class IvStateItem { + public: + + // Pop flags and node caused the push + enum Flags { + DEFAULT_FLAGS = 0, + MULTI_POP = 1, + KEEP_CHILDREN_ORDER = 2, + APPEND_AT_PUSH = 4, + UPDATE_STATE = 8, + UPDATE_STATE_EXCEPT_TRANSFORM = 0x10 // this has the same + // effect as UPDATE_STATE at the present time + }; + int flags; + const SoNode *pushInitiator; + + // Tracking of model transformation + SbMatrix inheritedTransformation; + SbMatrix lastUsedTransformation; + + // Active texture node (used for attaching the right texture to the + // geosets). Supported types are SoTexture2 and SoVRMLImageTexture for now. + // No multitexturing yet. + const SoNode* inheritedTexture; + const SoNode* currentTexture; + + // List of active lights + std::vector > inheritedLights; + std::vector > currentLights; + + // Active OpenGL glProgram and associated shaders + osg::ref_ptr inheritedGLProgram; + osg::ref_ptr currentGLProgram; + + // Ambient light (of SoEnvironment) + SbColor inheritedAmbientLight; + SbColor currentAmbientLight; + + // Generated OSG graph + osg::ref_ptr osgStateRoot; + + // Extra variables + const SoNode *keepChildrenOrderParent; + + IvStateItem(const SoNode *initiator, osg::Group *root = NULL) : + flags(IvStateItem::DEFAULT_FLAGS), + pushInitiator(initiator), + inheritedTransformation(SbMatrix::identity()), + lastUsedTransformation(SbMatrix::identity()), + inheritedTexture(NULL), + currentTexture(NULL), + inheritedLights(), + currentLights(), + inheritedGLProgram(NULL), + currentGLProgram(NULL), + inheritedAmbientLight(SbColor(0.2f,0.2f,0.2f)), + currentAmbientLight(SbColor(0.2f,0.2f,0.2f)), + osgStateRoot(root ? root : new osg::Group) {} + + IvStateItem(const IvStateItem& i, const SoCallbackAction *action, + const SoNode *initiator, const int f, + osg::Group *root) : + flags(f), + pushInitiator(initiator), + inheritedTransformation(action->getModelMatrix()), + lastUsedTransformation(action->getModelMatrix()), + inheritedTexture(i.currentTexture), + currentTexture(i.currentTexture), + inheritedLights(i.currentLights), + currentLights(i.currentLights), + inheritedGLProgram(i.currentGLProgram), + currentGLProgram(i.currentGLProgram), + inheritedAmbientLight(i.inheritedAmbientLight), + currentAmbientLight(i.currentAmbientLight), + osgStateRoot(root) {} + }; + + /// State stack for Inventor scene traversal + std::stack ivStateStack; + + void ivPushState(const SoCallbackAction *action, + const SoNode *initiator, const int flags = IvStateItem::DEFAULT_FLAGS, + osg::Group *root = new osg::Group); + void ivPopState(const SoCallbackAction *action, const SoNode *initator); + + void appendNode(osg::Node *n, const SoCallbackAction *action); + }; #endif diff --git a/src/osgPlugins/Inventor/ConvertToInventor.cpp b/src/osgPlugins/Inventor/ConvertToInventor.cpp index 0bb941340..140ebacd1 100644 --- a/src/osgPlugins/Inventor/ConvertToInventor.cpp +++ b/src/osgPlugins/Inventor/ConvertToInventor.cpp @@ -236,7 +236,7 @@ void osgArray2ivMField_template(const osg::Array *array, fieldClass &field, int int z; for (i=0, z=0; iIV conversion) // Note: use SoTransform instead of SoRotation because SoRotation is not supported by VRML1. SoTransform *transform = new SoTransform; - transform->rotation = SbRotation(SbVec3f(1.f,0.f,0.f), -M_PI_2); + transform->rotation = SbRotation(SbVec3f(1.f,0.f,0.f), float(-M_PI_2)); SoSeparator *separator = new SoSeparator; separator->addChild(translation); @@ -2047,3 +2047,4 @@ void ConvertToInventor::apply(osg::LOD& node) popInventorState(); } + diff --git a/src/osgPlugins/Inventor/GroupSoLOD.cpp b/src/osgPlugins/Inventor/GroupSoLOD.cpp deleted file mode 100644 index a2f7f5c34..000000000 --- a/src/osgPlugins/Inventor/GroupSoLOD.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -#include "GroupSoLOD.h" - -SO_NODE_SOURCE(GroupSoLOD) - -void GroupSoLOD::initClass() -{ - classTypeId = SoType::overrideType(SoLOD::getClassTypeId(), - createInstance); - parentFieldData = SoLOD::getFieldDataPtr(); -} - -GroupSoLOD::GroupSoLOD() -{ - SO_NODE_CONSTRUCTOR(GroupSoLOD); -} - -GroupSoLOD::~GroupSoLOD() -{ -} - -void GroupSoLOD::callback(SoCallbackAction *action) -{ - SoGroup::doAction(action); -} - diff --git a/src/osgPlugins/Inventor/GroupSoLOD.h b/src/osgPlugins/Inventor/GroupSoLOD.h deleted file mode 100644 index ce2174d6d..000000000 --- a/src/osgPlugins/Inventor/GroupSoLOD.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _GROUPSOLOD_H_ -#define _GROUPSOLOD_H_ - -#include -#include - -class GroupSoLOD : public SoLOD -{ - SO_NODE_HEADER(GroupSoLOD); - -public: - GroupSoLOD(); - static void initClass(); - -protected: - virtual void callback(SoCallbackAction *action); - -private: - virtual ~GroupSoLOD(); -}; - -#endif diff --git a/src/osgPlugins/Inventor/PendulumCallback.cpp b/src/osgPlugins/Inventor/PendulumCallback.cpp index f2459fdd0..a36926f6a 100644 --- a/src/osgPlugins/Inventor/PendulumCallback.cpp +++ b/src/osgPlugins/Inventor/PendulumCallback.cpp @@ -2,7 +2,7 @@ #include "PendulumCallback.h" -PendulumCallback::PendulumCallback(const osg::Vec3& axis, +PendulumCallback::PendulumCallback(const osg::Vec3& axis, float startAngle, float endAngle, float frequency) { @@ -10,7 +10,7 @@ PendulumCallback::PendulumCallback(const osg::Vec3& axis, _startAngle = startAngle; _endAngle = endAngle; _frequency = frequency; - + _previousTraversalNumber = -1; _previousTime = -1.0; _angle = 0.0; @@ -26,24 +26,26 @@ void PendulumCallback::operator() (osg::Node* node, osg::NodeVisitor* nv) return; const osg::FrameStamp* fs = nv->getFrameStamp(); - if (!fs) - return; - + if (!fs) + return; + // ensure that we do not operate on this node more than // once during this traversal. This is an issue since node // can be shared between multiple parents. if (nv->getTraversalNumber()!=_previousTraversalNumber) { double currentTime = fs->getSimulationTime(); + if (_previousTime == -1.) + _previousTime = currentTime; _angle += (currentTime - _previousTime) * 2 * osg::PI * _frequency; - - double frac = 0.5 + 0.5 * sin(_angle); - double rotAngle = _endAngle - _startAngle - osg::PI + + double frac = 0.5 - 0.5 * cos(_angle); + double rotAngle = //_endAngle - _startAngle - osg::PI + (1.0 - frac) * _startAngle + frac * _endAngle; // update the specified transform transform->setMatrix(osg::Matrix::rotate(rotAngle, _axis)); - + _previousTraversalNumber = nv->getTraversalNumber(); _previousTime = currentTime; } @@ -52,3 +54,4 @@ void PendulumCallback::operator() (osg::Node* node, osg::NodeVisitor* nv) traverse(node,nv); } + diff --git a/src/osgPlugins/Inventor/README.txt b/src/osgPlugins/Inventor/README.txt index 1086c8e8e..5a126099d 100644 --- a/src/osgPlugins/Inventor/README.txt +++ b/src/osgPlugins/Inventor/README.txt @@ -17,7 +17,7 @@ The plugin requires one of Inventor libraries: - Coin - GPL, support of VRML 2.0 - (http://www.coin3d.org) + (http://www.coin3d.org) - SGI Inventor - LGPL (http://oss.sgi.com/projects/inventor/) - TGS Inventor - commercial @@ -26,9 +26,10 @@ The plugin requires one of Inventor libraries: Contributors: -Sean Spicer - Vivek (c) Magic-Earth - Original author of the plugin +Sean Spicer - Vivek (c) Magic-Earth - Original author of the Inventor reader Gerrick Bivins -PCJohn - Jan Peciva, Cadwork (c) +PCJohn - Jan Peciva, Cadwork (c) - author of Inventor writer, number of + contributions and improvements to the reader Minor fixes: Ruben diff --git a/src/osgPlugins/Inventor/ReaderWriterIV.cpp b/src/osgPlugins/Inventor/ReaderWriterIV.cpp index fa8e63754..3de780fdc 100644 --- a/src/osgPlugins/Inventor/ReaderWriterIV.cpp +++ b/src/osgPlugins/Inventor/ReaderWriterIV.cpp @@ -1,5 +1,3 @@ -#include "ReaderWriterIV.h" - // OSG headers #include #include @@ -12,52 +10,132 @@ #include #include #include - #ifdef __COIN__ -#include +# include #endif +#include "ReaderWriterIV.h" #include "ConvertFromInventor.h" -#include "GroupSoLOD.h" #include "ConvertToInventor.h" +// forward declarations of static functions +static void addSearchPaths(const osgDB::FilePathList *searchPaths); +static void removeSearchPaths(const osgDB::FilePathList *searchPaths); + // Register with Registry to instantiate the inventor reader. REGISTER_OSGPLUGIN(Inventor, ReaderWriterIV) + +/** + * Constructor. + * Initializes the ReaderWriterIV. + */ ReaderWriterIV::ReaderWriterIV() { + // Set supported extensions and options supportsExtension("iv","Inventor format"); supportsExtension("wrl","VRML world file"); + + // Initialize Inventor + initInventor(); } -// Read file and convert to OSG -osgDB::ReaderWriter::ReadResult -ReaderWriterIV::readNode(const std::string& file, - const osgDB::ReaderWriter::Options* options) const + +/** + * Initializes Open Inventor. + */ +void ReaderWriterIV::initInventor() const { - std::string ext = osgDB::getLowerCaseFileExtension(file); - if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; - - std::string fileName = osgDB::findDataFile( file, options ); - if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; - - osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::readNode() Reading file " - << fileName.data() << std::endl; - // Initialize Inventor SoDB::init(); SoNodeKit::init(); SoInteraction::init(); - - - // Initial GroupSoLOD node - GroupSoLOD::initClass(); #ifdef __COIN__ // Disable delayed loading of VRML textures SoVRMLImageTexture::setDelayFetchURL(FALSE); #endif +} + + +/** + * Read from SoInput and convert to OSG. + * This is a method used by readNode(string,options) and readNode(istream,options). + */ +osgDB::ReaderWriter::ReadResult +ReaderWriterIV::readNodeFromSoInput(SoInput &input, + std::string &fileName, const osgDB::ReaderWriter::Options *options) const +{ + // Parse options and add search paths to SoInput + const osgDB::FilePathList *searchPaths = options ? &options->getDatabasePathList() : NULL; + if (options) + addSearchPaths(searchPaths); + + // Create the inventor scenegraph by reading from SoInput + SoSeparator* rootIVNode = SoDB::readAll(&input); + + // Remove recently appened search paths + if (options) + removeSearchPaths(searchPaths); + + // Close the file + input.closeFile(); + + // Perform conversion + ReadResult result; + if (rootIVNode) + { + rootIVNode->ref(); + // Convert the inventor scenegraph to an osg scenegraph + ConvertFromInventor convertIV; + convertIV.preprocess(rootIVNode); + result = convertIV.convert(rootIVNode); + rootIVNode->unref(); + } else + result = ReadResult::FILE_NOT_HANDLED; + + // Notify + if (result.success()) { + if (fileName.length()) + osg::notify(osg::NOTICE) << "osgDB::ReaderWriterIV::readNode() " + << "File " << fileName.data() + << " loaded successfully." << std::endl; + else + osg::notify(osg::NOTICE) << "osgDB::ReaderWriterIV::readNode() " + << "Stream loaded successfully." << std::endl; + } else { + if (fileName.length()) + osg::notify(osg::WARN) << "osgDB::ReaderWriterIV::readNode() " + << "Failed to load file " << fileName.data() + << "." << std::endl; + else + osg::notify(osg::WARN) << "osgDB::ReaderWriterIV::readNode() " + << "Failed to load stream." << std::endl; + } + + return result; +} + + +// Read file and convert to OSG +osgDB::ReaderWriter::ReadResult +ReaderWriterIV::readNode(const std::string& file, + const osgDB::ReaderWriter::Options* options) const +{ + // Accept extension + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + + // Find file + std::string fileName = osgDB::findDataFile( file, options ); + if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; + + // Notify + osg::notify(osg::NOTICE) << "osgDB::ReaderWriterIV::readNode() Reading file " + << fileName.data() << std::endl; + osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::readNode() Inventor version: " + << SoDB::getVersion() << std::endl; // Open the file SoInput input; @@ -68,23 +146,59 @@ ReaderWriterIV::readNode(const std::string& file, return ReadResult::ERROR_IN_READING_FILE; } - // Create the inventor scenegraph from the file - SoSeparator* rootIVNode = SoDB::readAll(&input); + // Perform reading from SoInput + return readNodeFromSoInput(input, fileName, options); +} - // Close the file - input.closeFile(); - if (rootIVNode) - { - rootIVNode->ref(); - // Convert the inventor scenegraph to an osg scenegraph and return it - ConvertFromInventor convertIV; - ReadResult result = convertIV.convert(rootIVNode); - rootIVNode->unref(); - return result; +osgDB::ReaderWriter::ReadResult +ReaderWriterIV::readNode(std::istream& fin, + const osgDB::ReaderWriter::Options* options) const +{ + // Notify + osg::notify(osg::NOTICE) << "osgDB::ReaderWriterIV::readNode() " + "Reading from stream." << std::endl; + osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::readNode() " + "Inventor version: " << SoDB::getVersion() << std::endl; + + // Open the file + SoInput input; + + // Assign istream to SoInput + // note: It seems there is no straightforward way to do that. + // SoInput accepts only FILE by setFilePointer or memory buffer + // by setBuffer. The FILE is dangerous on Windows, since it forces + // the plugin and Inventor DLL to use the same runtime library + // (otherwise there are app crashes). + // The memory buffer seems much better option here, even although + // there will not be a real streaming. However, the model data + // are usually much smaller than textures, so we should not worry + // about it and think how to stream textures instead. + + // Get the data to the buffer + size_t bufSize = 126*1024; // let's make it something bellow 128KB + char *buf = (char*)malloc(bufSize); + size_t dataSize = 0; + while (!fin.eof() && fin.good()) { + fin.read(buf+dataSize, bufSize-dataSize); + dataSize += fin.gcount(); + if (bufSize == dataSize) { + bufSize *= 2; + buf = (char*)realloc(buf, bufSize); + } } + input.setBuffer(buf, dataSize); + osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::readNode() " + "Stream size: " << dataSize << std::endl; - return ReadResult::FILE_NOT_HANDLED; + // Perform reading from SoInput + osgDB::ReaderWriter::ReadResult r; + std::string fileName(""); + r = readNodeFromSoInput(input, fileName, options); + + // clean up and return + free(buf); + return r; } @@ -97,11 +211,8 @@ ReaderWriterIV::writeNode(const osg::Node& node, const std::string& fileName, if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; bool useVRML1 = !isInventorExtension(osgDB::getFileExtension(fileName)); - osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::writeNode() Writing file " - << fileName.data() << std::endl; - - // Initialize Inventor - SoInteraction::init(); + osg::notify(osg::NOTICE) << "osgDB::ReaderWriterIV::writeNode() Writing file " + << fileName.data() << std::endl; // Convert OSG graph to Inventor graph ConvertToInventor osg2iv; @@ -130,3 +241,18 @@ ReaderWriterIV::writeNode(const osg::Node& node, const std::string& fileName, return WriteResult::FILE_SAVED; } + + +static void addSearchPaths(const osgDB::FilePathList *searchPaths) +{ + for (int i=searchPaths->size()-1; i>=0; i--) + SoInput::addDirectoryFirst(searchPaths->operator[](i).c_str()); +} + + +static void removeSearchPaths(const osgDB::FilePathList *searchPaths) +{ + for (int i=0, c=searchPaths->size(); ioperator[](i).c_str()); +} + diff --git a/src/osgPlugins/Inventor/ReaderWriterIV.h b/src/osgPlugins/Inventor/ReaderWriterIV.h index 84c83db2a..7b3b10d16 100644 --- a/src/osgPlugins/Inventor/ReaderWriterIV.h +++ b/src/osgPlugins/Inventor/ReaderWriterIV.h @@ -8,22 +8,31 @@ class ReaderWriterIV : public osgDB::ReaderWriter { public: ReaderWriterIV(); - + virtual const char* className() const - { - return "Inventor reader/writer"; + { + return "Inventor reader/writer"; } - + bool isInventorExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive(extension, "iv") ? true : false; } - virtual ReadResult readNode(const std::string& filename, - const osgDB::ReaderWriter::Options *) const; + virtual ReadResult readNode(const std::string& filename, + const osgDB::ReaderWriter::Options*) const; + virtual ReadResult readNode(std::istream& fin, + const osgDB::ReaderWriter::Options* = NULL) const; + virtual WriteResult writeNode(const osg::Node& node, const std::string& filename, const osgDB::ReaderWriter::Options* options = NULL) const; + + protected: + void initInventor() const; + ReadResult readNodeFromSoInput(class SoInput&, + std::string &fileName, const osgDB::ReaderWriter::Options*) const; }; #endif + diff --git a/src/osgPlugins/Inventor/ShuttleCallback.cpp b/src/osgPlugins/Inventor/ShuttleCallback.cpp index a14794552..98be155a5 100644 --- a/src/osgPlugins/Inventor/ShuttleCallback.cpp +++ b/src/osgPlugins/Inventor/ShuttleCallback.cpp @@ -2,14 +2,14 @@ #include "ShuttleCallback.h" -ShuttleCallback::ShuttleCallback(const osg::Vec3& startPos, +ShuttleCallback::ShuttleCallback(const osg::Vec3& startPos, const osg::Vec3& endPos, float frequency) { _startPos = startPos; _endPos = endPos; _frequency = frequency; - + _previousTraversalNumber = -1; _previousTime = -1.0; _angle = 0.0; @@ -25,24 +25,26 @@ void ShuttleCallback::operator() (osg::Node* node, osg::NodeVisitor* nv) return; const osg::FrameStamp* fs = nv->getFrameStamp(); - if (!fs) - return; - + if (!fs) + return; + // ensure that we do not operate on this node more than // once during this traversal. This is an issue since node // can be shared between multiple parents. if (nv->getTraversalNumber()!=_previousTraversalNumber) { double currentTime = fs->getSimulationTime(); + if (_previousTime == -1.) + _previousTime = currentTime; _angle += (currentTime - _previousTime) * 2 * osg::PI * _frequency; - - double frac = 0.5 + 0.5 * sin(_angle); + + double frac = 0.5 - 0.5 * cos(_angle); osg::Vec3 position = _startPos * (1.0 - frac) + _endPos * frac; // update the specified transform transform->setMatrix(osg::Matrix::translate(position)); - + _previousTraversalNumber = nv->getTraversalNumber(); _previousTime = currentTime; } @@ -51,3 +53,4 @@ void ShuttleCallback::operator() (osg::Node* node, osg::NodeVisitor* nv) traverse(node,nv); } +