From b2270e7f38fad0df7989e25e82ba3c9919d3da2d Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Sun, 31 Jan 2010 12:55:29 +0000 Subject: [PATCH] From Jan Peciva, "I am sending improved version of Inventor plugin. Attaching just modified files, while GroupSoLOD.h and .cpp was deleted. Please, delete it from repository, it is not used any longer and I doubt if it is probably not used for anything meaningful for a while. In the new code, there is no GroupSoLOD. Please, delete it. I am using new plugin version for about 1.5 month so I consider it stable by myself. List of changes: - rewritten Inventor state stack - shaders support - light attenuation support - support for reading from stream (readNode(std::istream& fin, options)) - improved grouping node handling (SoSeparator, SoGroup,...) - fixed transformation bug when two SoShapes/Drawables with different transformations are placed bellow one grouping node - introduced preprocessing to handle more advanced usage schemes of SoLOD and SoSwitch nodes - unused code clean up - improved notify messages - animation callbacks fixes - FindInventor.cmake improved finding routines, support for Coin3 and Coin4" --- src/osgPlugins/Inventor/CMakeLists.txt | 4 +- .../Inventor/ConvertFromInventor.cpp | 1570 ++++++++++++----- src/osgPlugins/Inventor/ConvertFromInventor.h | 171 +- src/osgPlugins/Inventor/ConvertToInventor.cpp | 5 +- src/osgPlugins/Inventor/GroupSoLOD.cpp | 28 - src/osgPlugins/Inventor/GroupSoLOD.h | 22 - src/osgPlugins/Inventor/PendulumCallback.cpp | 21 +- src/osgPlugins/Inventor/README.txt | 7 +- src/osgPlugins/Inventor/ReaderWriterIV.cpp | 206 ++- src/osgPlugins/Inventor/ReaderWriterIV.h | 21 +- src/osgPlugins/Inventor/ShuttleCallback.cpp | 19 +- 11 files changed, 1435 insertions(+), 639 deletions(-) delete mode 100644 src/osgPlugins/Inventor/GroupSoLOD.cpp delete mode 100644 src/osgPlugins/Inventor/GroupSoLOD.h 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..1e9f57f10 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,732 @@ #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)); + + // 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::Group *stateRoot = ivState.osgStateRoot; + osg::ref_ptr r = stateRoot; +/* assert(strcmp(stateRoot->className(), "Group") == 0 && + "IvStateStack osg graph is expected to be " + "headed by osg::Group"); + if (stateRoot->getNumChildren() == 1) { + r = stateRoot->getChild(0)->asGroup(); + osg::notify(osg::FATAL) << stateRoot->className() << std::endl; + osg::notify(osg::FATAL) << stateRoot->getChild(0)->className() << std::endl; + exit(0); + assert(r != NULL && "Node must be group."); + }*/ + + // 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, 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 +833,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 +856,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 +902,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 +917,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 +947,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 +956,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 +964,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 +974,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 +1062,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 +1137,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); +} +#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 +1299,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 +1342,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 +1368,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 +1381,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 +1389,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 +1411,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 +1420,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 +1429,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 +1437,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 +1453,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 +1463,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 +1488,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 +1535,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 +1548,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 +1569,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 +1610,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 +1633,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 +1650,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 +1670,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 +1716,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 +1777,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 +1813,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 +1842,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); } +