Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.h
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.h (revision 9053)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.h (revision 11032)
@@ -11,4 +11,5 @@
 #include <vector>
 #include <stack>
+#include <assert.h>
 
 class ConvertFromInventor
@@ -18,40 +19,51 @@
         ~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,
-                                 SoCallbackAction* action, const SoNode* node);
-        static SoCallbackAction::Response postGroup(void* data,
-                                 SoCallbackAction* action, const SoNode* node);
-        static SoCallbackAction::Response preTexture(void* data, 
-                                 SoCallbackAction* action, const SoNode* node);
-        static SoCallbackAction::Response preLight(void* data, 
-                                 SoCallbackAction* action, const SoNode* node);
-        static SoCallbackAction::Response preRotor(void* data, 
-                                 SoCallbackAction* action, const SoNode* node);
-        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 postTexture(void* data,
+                                 SoCallbackAction* action, const SoNode* node);
+        static SoCallbackAction::Response preLight(void* data,
+                                 SoCallbackAction* action, const SoNode* node);
+        static SoCallbackAction::Response preEnvironment(void* data,
+                                 SoCallbackAction* action, const SoNode* node);
+        static SoCallbackAction::Response preShaderProgram(void* data,
+                                 SoCallbackAction* action, const SoNode* node);
+        static SoCallbackAction::Response preRotor(void* data,
+                                 SoCallbackAction* action, const SoNode* node);
+        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 preInfo(void* data,
@@ -68,16 +80,21 @@
                                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<osg::StateSet> getStateSet(SoCallbackAction* action);
 
-        osg::Texture2D* convertIVTexToOSGTex(const SoNode* soNode, 
+        osg::Texture2D* convertIVTexToOSGTex(const SoNode* soNode,
                                              SoCallbackAction* action);
 
@@ -100,5 +117,5 @@
         std::vector<osg::Vec2> textureCoords;
 
-        // Num of primitive and primitive type 
+        // Num of primitive and primitive type
         int numPrimitives;
         osg::PrimitiveSet::Mode primitiveType;
@@ -108,22 +125,102 @@
         VertexOrder vertexOrder;
 
-        // Stack of group nodes (used to build the scene graph)
-        std::stack<osg::Group* > groupStack;
-        // Stack of texture nodes (used for attaching the right texture to the
-        // geosets). Supported types are SoTexture2 and SoVRMLImageTexture for now.
-        std::stack<const SoNode*> soTexStack;
-
         // Mapping from SoTexture2 and SoVRMLImageTexture to already converted
         // osg::Texture2D - avoids duplication of same texture objects
         std::map<const SoNode*, osg::Texture2D*> ivToOsgTexMap;
 
-        // Stack to maintain the list of lights at each level of the 
-        // scenegraph
-        typedef std::vector<osg::Light *> LightList;
-        std::stack<LightList> lightStack;
-
         osg::ref_ptr<osg::MatrixTransform> _root;///<The root node;
 
-        osg::ref_ptr<osg::Group> 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<osg::ref_ptr<osg::Light> > inheritedLights;
+            std::vector<osg::ref_ptr<osg::Light> > currentLights;
+
+            // Active OpenGL glProgram and associated shaders
+            osg::ref_ptr<osg::Program> inheritedGLProgram;
+            osg::ref_ptr<osg::Program> currentGLProgram;
+
+            // Ambient light (of SoEnvironment)
+            SbColor inheritedAmbientLight;
+            SbColor currentAmbientLight;
+
+            // Generated OSG graph
+            osg::ref_ptr<osg::Group> 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<IvStateItem> 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);
+
 };
 
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ReaderWriterIV.cpp
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ReaderWriterIV.cpp (revision 8578)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ReaderWriterIV.cpp (revision 11032)
@@ -1,4 +1,2 @@
-#include "ReaderWriterIV.h"
-
 // OSG headers
 #include <osg/Notify>
@@ -13,12 +11,15 @@
 #include <Inventor/actions/SoWriteAction.h>
 #include <Inventor/actions/SoCallbackAction.h>
-
 #ifdef __COIN__
-#include <Inventor/VRMLnodes/SoVRMLImageTexture.h>
+# include <Inventor/VRMLnodes/SoVRMLImageTexture.h>
 #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);
 
 
@@ -26,32 +27,29 @@
 REGISTER_OSGPLUGIN(Inventor, ReaderWriterIV)
 
+
+/**
+ * Constructor.
+ * Initializes the ReaderWriterIV.
+ */
 ReaderWriterIV::ReaderWriterIV()
 {
+    // Set supported extensions and options
     supportsExtension("iv","Inventor format");
     supportsExtension("wrl","VRML world file");
-}
-
-// Read file and convert to OSG
-osgDB::ReaderWriter::ReadResult 
-ReaderWriterIV::readNode(const std::string& file,
-                         const osgDB::ReaderWriter::Options* options) 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
+    initInventor();
+}
+
+
+/**
+ * Initializes Open Inventor.
+ */
+void ReaderWriterIV::initInventor() const
+{
     // Initialize Inventor
     SoDB::init();
     SoNodeKit::init();
     SoInteraction::init();
-    
-
-    // Initial GroupSoLOD node
-    GroupSoLOD::initClass();
 
 #ifdef __COIN__
@@ -59,4 +57,84 @@
     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
@@ -69,21 +147,57 @@
     }
 
-    // Create the inventor scenegraph from the file
-    SoSeparator* rootIVNode = SoDB::readAll(&input);
-
-    // 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;
+    // Perform reading from SoInput
+    return readNodeFromSoInput(input, fileName, options);
+}
+
+
+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);
+        }
     }
-
-    return ReadResult::FILE_NOT_HANDLED;
+    input.setBuffer(buf, dataSize);
+    osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::readNode() "
+              "Stream size: " << dataSize << std::endl;
+
+    // Perform reading from SoInput
+    osgDB::ReaderWriter::ReadResult r;
+    std::string fileName("");
+    r = readNodeFromSoInput(input, fileName, options);
+
+    // clean up and return
+    free(buf);
+    return r;
 }
 
@@ -98,9 +212,6 @@
     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
@@ -131,2 +242,17 @@
     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(); i<c; i++)
+        SoInput::addDirectoryFirst(searchPaths->operator[](i).c_str());
+}
+
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ReaderWriterIV.h
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ReaderWriterIV.h (revision 8578)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ReaderWriterIV.h (revision 11032)
@@ -9,10 +9,10 @@
     public:
         ReaderWriterIV();
-        
+
         virtual const char* className() const
-        { 
-            return "Inventor reader/writer"; 
+        {
+            return "Inventor reader/writer";
         }
-        
+
         bool isInventorExtension(const std::string& extension) const
         {
@@ -20,10 +20,19 @@
         }
 
-        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
+
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ShuttleCallback.cpp
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ShuttleCallback.cpp (revision 6051)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ShuttleCallback.cpp (revision 11032)
@@ -3,5 +3,5 @@
 #include "ShuttleCallback.h"
 
-ShuttleCallback::ShuttleCallback(const osg::Vec3& startPos, 
+ShuttleCallback::ShuttleCallback(const osg::Vec3& startPos,
                                  const osg::Vec3& endPos,
                                  float frequency)
@@ -10,5 +10,5 @@
     _endPos = endPos;
     _frequency = frequency;
-    
+
     _previousTraversalNumber = -1;
     _previousTime = -1.0;
@@ -26,7 +26,7 @@
 
     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
@@ -35,7 +35,9 @@
     {
         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;
@@ -43,5 +45,5 @@
         // update the specified transform
         transform->setMatrix(osg::Matrix::translate(position));
-            
+
         _previousTraversalNumber = nv->getTraversalNumber();
         _previousTime = currentTime;
@@ -52,2 +54,3 @@
 
 }
+
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/PendulumCallback.cpp
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/PendulumCallback.cpp (revision 6051)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/PendulumCallback.cpp (revision 11032)
@@ -3,5 +3,5 @@
 #include "PendulumCallback.h"
 
-PendulumCallback::PendulumCallback(const osg::Vec3& axis, 
+PendulumCallback::PendulumCallback(const osg::Vec3& axis,
                                          float startAngle, float endAngle,
                                          float frequency)
@@ -11,5 +11,5 @@
     _endAngle = endAngle;
     _frequency = frequency;
-    
+
     _previousTraversalNumber = -1;
     _previousTime = -1.0;
@@ -27,7 +27,7 @@
 
     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
@@ -36,13 +36,15 @@
     {
         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;
@@ -53,2 +55,3 @@
 
 }
+
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertToInventor.cpp
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertToInventor.cpp (revision 9414)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertToInventor.cpp (revision 11032)
@@ -237,5 +237,5 @@
     for (i=0, z=0; i<num; i++)
       if (z == numItemsUntilMinusOne) {
-        a[i] = -1;
+        a[i] = ivType(-1);
         z = 0;
       } else {
@@ -1921,5 +1921,5 @@
       // 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;
@@ -2048,2 +2048,3 @@
   popInventorState();
 }
+
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.cpp
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.cpp (revision 9585)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.cpp (revision 11032)
@@ -27,4 +27,5 @@
 #include <Inventor/SoInteraction.h>
 #include <Inventor/nodes/SoSeparator.h>
+#include <Inventor/nodes/SoTransformSeparator.h>
 #include <Inventor/nodes/SoShape.h>
 #include <Inventor/nodes/SoVertexShape.h>
@@ -38,4 +39,5 @@
 #include <Inventor/nodes/SoLOD.h>
 #include <Inventor/nodes/SoTexture2.h>
+#include <Inventor/nodes/SoEnvironment.h>
 #include <Inventor/misc/SoChildList.h>
 #include <Inventor/SoPrimitiveVertex.h>
@@ -43,4 +45,6 @@
 #include <Inventor/nodes/SoTransform.h>
 #include <Inventor/nodes/SoInfo.h>
+#include <Inventor/actions/SoWriteAction.h>
+#include <Inventor/elements/SoModelMatrixElement.h>
 
 #ifdef __COIN__
@@ -51,5 +55,15 @@
 #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 <Inventor/nodes/SoShaderProgram.h>
+#include <Inventor/nodes/SoVertexShader.h>
+#include <Inventor/nodes/SoGeometryShader.h>
+#include <Inventor/nodes/SoFragmentShader.h>
+#endif
 
 #include <map>
@@ -65,4 +79,6 @@
 
 #define DEBUG_IV_PLUGIN
+#define NOTIFY_HEADER "Inventor Plugin (reader): "
+
 ///////////////////////////////////////////
 ConvertFromInventor::ConvertFromInventor()
@@ -71,6 +87,4 @@
     transformInfoName = "";
     appearanceName = "";
-    inAppearanceWithNoTexture = false;
-    lightGroup = NULL;
 }
 ///////////////////////////////////////////
@@ -78,8 +92,181 @@
 {
 }
+///////////////////////////////////////////////////////////////////
+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<std::vector<int> > &stack = *((std::vector<std::vector<int> >*)data);
+
+    if (node->isOfType(SoGroup::getClassTypeId())) {
+
+        SoGroup *group = (SoGroup*)node;
+        SoGroup *affectedScene = NULL;
+        childrenTotal = group->getNumChildren();
+
+        for (int i=0, c=group->getNumChildren(); i<c; i++) {
+            SoNode *child = group->getChild(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<int> &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(); k<n; k++) {
+                            affectedScene->addChild(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<std::vector<int> > &stack = *((std::vector<std::vector<int> >*)data);
+
+    stack.push_back(std::vector<int>());
+
+    return SoCallbackAction::CONTINUE;
+}
+////////////////////////////////////////////////////////////////////
+SoCallbackAction::Response
+ConvertFromInventor::restructurePostNode(void* data, SoCallbackAction* action,
+                              const SoNode* node)
+{
+    std::vector<std::vector<int> > &stack = *((std::vector<std::vector<int> >*)data);
+
+    assert(stack.size() > 0 && "Stack is empty");
+    std::vector<int> &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<std::vector<int> > 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,
@@ -88,34 +275,71 @@
                                        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());
-
-    // 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 
+    // Root of the scene
+    osg::ref_ptr<osg::Group> osgRootNode = new osg::MatrixTransform(ivToOSGMat);
+
+    // 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.addPreCallback(SoVRMLImageTexture::getClassTypeId(),
-                            preVRMLImageTexture, this);
-    cbAction.addPreCallback(SoVRMLAppearance::getClassTypeId(),
-                            preVRMLAppearance, this);
+    cbAction.addPostCallback(SoVRMLImageTexture::getClassTypeId(),
+                            postTexture, this);
+    cbAction.addPostCallback(SoVRMLAppearance::getClassTypeId(),
+                            postTexture, this);
+#endif
+
+#ifdef __COIN__
     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,
@@ -123,41 +347,428 @@
     cbAction.addPointCallback(SoShape::getClassTypeId(), addPointCB, this);
 
+    // Post node callback
+    cbAction.addPostCallback(SoNode::getClassTypeId(), postNode, this);
+
     // Traverse the inventor scene graph
-    cbAction.apply(rootIVNode);
-
-    // Pop all the groups that are Transforms
-    // Verify that the last transform is _root .
-    assert(groupStack.size() > 0 && "groupStack underflow.");
-    osg::ref_ptr<osg::Group> group = groupStack.top();
-    while (strcmp(group->className(), "MatrixTransform") == 0)
-    {
-        groupStack.pop();
-        if (groupStack.empty()) break;
-        group = groupStack.top();
-    }
-    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(); 
+    cbAction.apply(ivRootNode);
+
+    // Remove superfluous group
+    if (osgRootNode->getNumChildren() == 1) {
+        osg::ref_ptr<osg::Group> 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(); i<c; i++)
+            osgRootNode->addChild(toRemove->getChild(i));
+    }
+
+    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<osg::Group> 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<osg::LOD*>(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<SoNormalBinding::Binding, osg::Geometry::AttributeBinding> 
+    static std::map<SoNormalBinding::Binding, osg::Geometry::AttributeBinding>
         normBindingMap;
     static std::map<SoMaterialBinding::Binding, osg::Geometry::AttributeBinding>
@@ -166,32 +777,32 @@
     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;
 
@@ -223,5 +834,5 @@
     thisPtr->colors.clear();
     thisPtr->textureCoords.clear();
-    
+
     return SoCallbackAction::CONTINUE;
 }
@@ -230,10 +841,11 @@
 //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 i = j + 1; i < 4; i++) 
+    for (int j = 0; j < 4; j++)
+    {
+        for (int i = j + 1; i < 4; i++)
         {
             tmp = mat.operator()(j,i);
@@ -245,10 +857,10 @@
 }
 ////////////////////////////////////////////////////////////////////
-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
@@ -291,5 +903,5 @@
         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);
@@ -306,12 +918,12 @@
 
     if (thisPtr->textureCoords.empty())
-        osg::notify(osg::INFO)<<"tex coords not found"<<std::endl;
+        osg::notify(osg::DEBUG_INFO)<<"tex coords not found"<<std::endl;
     else {
-        
+
         // report texture coordinate conditions
         if (action->getNumTextureCoordinates()>0)
-            osg::notify(osg::INFO)<<"tex coords found"<<std::endl;
+            osg::notify(osg::DEBUG_INFO)<<"tex coords found"<<std::endl;
         else
-           osg::notify(osg::INFO)<<"tex coords generated"<<std::endl;
+           osg::notify(osg::DEBUG_INFO)<<"tex coords generated"<<std::endl;
 
         // Get the texture transformation matrix
@@ -322,5 +934,5 @@
         osg::Matrix identityMat;
         identityMat.makeIdentity();
-        osg::ref_ptr<osg::Vec2Array> texCoords 
+        osg::ref_ptr<osg::Vec2Array> texCoords
             = new osg::Vec2Array(thisPtr->textureCoords.size());
         if (textureMat == identityMat)
@@ -336,5 +948,5 @@
             {
                 osg::Vec3 transVec = textureMat.preMult(
-                        osg::Vec3(thisPtr->textureCoords[i][0], 
+                        osg::Vec3(thisPtr->textureCoords[i][0],
                                   thisPtr->textureCoords[i][1],
                                   0.0));
@@ -345,5 +957,5 @@
         geometry->setTexCoordArray(0, texCoords.get());
     }
-    
+
     // Set the parameters for the geometry
 
@@ -353,5 +965,5 @@
     osg::ref_ptr<osg::StateSet> stateSet = thisPtr->getStateSet(action);
     geometry->setStateSet(stateSet.get());
-    
+
     // Add the geoset to a geode
     osg::ref_ptr<osg::Geode> geode = new osg::Geode;
@@ -363,82 +975,83 @@
         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)
-{
-#ifdef DEBUG_IV_PLUGIN
-    osg::notify(osg::INFO) << "preTexture()  " 
-              << node->getTypeId().getName().getString() << std::endl;
-#endif
-    
+SoCallbackAction::Response
+ConvertFromInventor::postTexture(void* data, SoCallbackAction *,
+                                 const SoNode* node)
+{
+#ifdef DEBUG_IV_PLUGIN
+    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);
-        
+    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__
+
+    // 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);
+    }
+
+    // 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 /* __COIN__ */
+
+    // Set current texture
+    if (texturingEnabled)
+        thisPtr->ivStateStack.top().currentTexture = node;
+    else
+        thisPtr->ivStateStack.top().currentTexture = NULL;
+
     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
-
-#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; i<kids->getLength(); i++) {
-        SoNode* kid = (SoNode*)kids->get(i);
-        if (kid->isOfType(SoVRMLMaterial::getClassTypeId())) {
-            thisPtr->appearanceName = kid->getName();
-        }
-        if (kid->isOfType(SoVRMLTexture::getClassTypeId())) {
-            foundTex = true;
-        }
-    }
-    if (!foundTex) {
-        thisPtr->soTexStack.push(NULL);
-        thisPtr->inAppearanceWithNoTexture = true;
-    }
-#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
-
-    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
-
-    if (thisPtr->soTexStack.size())
-        thisPtr->soTexStack.pop();
-    thisPtr->soTexStack.push(node);
-
-    return SoCallbackAction::CONTINUE;
-}
 //////////////////////////////////////////////////////////////////
-void ConvertFromInventor::transformLight(SoCallbackAction* action, 
-                                         const SbVec3f& vec, 
+void ConvertFromInventor::transformLight(SoCallbackAction* action,
+                                         const SbVec3f& vec,
                                          osg::Vec3& transVec)
 {
@@ -450,16 +1063,15 @@
 }
 ///////////////////////////////////////////////////////////////////
-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;
@@ -467,10 +1079,12 @@
         return SoCallbackAction::CONTINUE;
 
+    // Create new OSG light
+    IvStateItem &ivState = thisPtr->ivStateStack.top();
     osg::ref_ptr<osg::Light> 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();
@@ -478,16 +1092,27 @@
 
     // 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()))
@@ -495,8 +1120,14 @@
         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()))
@@ -507,34 +1138,158 @@
         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);
-    }
-
-    // add a light source node to the scene graph
+#endif
+    }
+
+    // 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<osg::LightSource> 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());
-    }
-    thisPtr->lightGroup->addChild(ls.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<osg::Shader> 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;
+    }
+
+    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(); i<c; i++) {
+
+        const SoShaderObject *shader = (const SoShaderObject*)ivProgram->shaderObject[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;
@@ -545,15 +1300,16 @@
 {
     osg::ref_ptr<osg::StateSet> 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<osg::Texture2D> texture;
-    const SoNode *ivTexture = soTexStack.top();
+    const SoNode *ivTexture = ivState.currentTexture;
     if (ivTexture)
     {
-        osg::notify(osg::INFO)<<"Have texture"<<std::endl;
-
         // Found a corresponding OSG texture object
         if (ivToOsgTexMap[ivTexture])
@@ -567,7 +1323,7 @@
             ivToOsgTexMap[ivTexture] = texture.get();
         }
-        
+
         stateSet->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);
-        
+
         // propogate name
         if(texture.valid())
@@ -587,16 +1343,18 @@
                 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;
 
@@ -611,10 +1369,10 @@
     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);
@@ -624,5 +1382,4 @@
         const SbImage *img = ((SoVRMLImageTexture*)ivTexture)->getImage();
         if (img) img->getValue(tmp, bpp);
-        else bpp = 0;
       }
 #endif
@@ -633,11 +1390,11 @@
     {
         osg::ref_ptr<osg::BlendFunc> 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())
@@ -655,6 +1412,6 @@
         stateSet->setAttributeAndModes(point.get(), osg::StateAttribute::ON);
     }
-    
-    // Set draw mode 
+
+    // Set draw mode
     switch (action->getDrawStyle())
     {
@@ -664,5 +1421,5 @@
 // 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);
@@ -673,5 +1430,5 @@
         {
             osg::ref_ptr<osg::PolygonMode> 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);
@@ -681,5 +1438,5 @@
         {
             osg::ref_ptr<osg::PolygonMode> 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);
@@ -697,5 +1454,5 @@
         cullFace->setMode(osg::CullFace::BACK);
         stateSet->setAttributeAndModes(cullFace.get(), osg::StateAttribute::ON);
-    } 
+    }
 
     // Set lighting
@@ -707,19 +1464,19 @@
         osg::ref_ptr<osg::Material> 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
@@ -732,18 +1489,44 @@
         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);
+#endif
         stateSet->setAttributeAndModes(lightModel, osg::StateAttribute::ON);
-#endif
+
         // 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(); i<c; i++) {
+             const std::string &shaderCode = ivState.currentGLProgram->getShader(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;
 }
@@ -753,6 +1536,10 @@
                                           SoCallbackAction* action)
 {
-    osg::notify(osg::INFO)<<"convertIVTexToOSGTex of type "<<
-        soNode->getTypeId().getName().getString()<<std::endl;
+#ifdef DEBUG_IV_PLUGIN
+    osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER
+              << "convertIVTexToOSGTex ("
+              << soNode->getTypeId().getName().getString()
+              << ")" << std::endl;
+#endif
 
     SbVec2s soSize;
@@ -762,5 +1549,6 @@
     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;
     }
@@ -782,18 +1570,24 @@
     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::INFO) << fileName << " -> ";
+      osg::notify(osg::WARN) << NOTIFY_HEADER
+                << " Warning: Unsupported texture type: "
+                << soNode->getTypeId().getName().getString() << std::endl;
+
+#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;
-
-    // Create the osg::Image 
+#ifdef DEBUG_IV_PLUGIN
+    osg::notify(osg::DEBUG_INFO) << fileName << std::endl;
+#endif
+
+    // Create the osg::Image
     osg::ref_ptr<osg::Image> osgImage = new osg::Image;
     osgImage->setFileName(fileName);
@@ -817,5 +1611,5 @@
         firstTime = false;
     }
-         
+
     // Set texture wrap mode
 #ifdef __COIN__
@@ -840,13 +1634,13 @@
     }
 
-    return osgTex; 
+    return osgTex;
 }
 ///////////////////////////////////////////////////////////////////
-SoCallbackAction::Response 
-ConvertFromInventor::preInfo(void* data, SoCallbackAction* action, 
-                              const SoNode* node)
-{
-#ifdef DEBUG_IV_PLUGIN
-    osg::notify(osg::INFO) << "preInfo()    " 
+SoCallbackAction::Response
+ConvertFromInventor::preInfo(void* data, SoCallbackAction* action,
+                             const SoNode* node)
+{
+#ifdef DEBUG_IV_PLUGIN
+    osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preInfo()    "
               << node->getTypeId().getName().getString() << std::endl;
 #endif
@@ -857,179 +1651,11 @@
     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<osg::Group> 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; i<kids->getLength(); 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<osg::Group> 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<osg::LOD*>(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)
-{
-#ifdef DEBUG_IV_PLUGIN
-    osg::notify(osg::INFO) << "preRotor()  " 
+SoCallbackAction::Response
+ConvertFromInventor::preRotor(void *data, SoCallbackAction *action,
+                              const SoNode *node)
+{
+#ifdef DEBUG_IV_PLUGIN
+    osg::notify(osg::DEBUG_INFO) << NOTIFY_HEADER << "preRotor()  "
               << node->getTypeId().getName().getString() << std::endl;
 #endif
@@ -1045,28 +1671,39 @@
     // Create a new osg::MatrixTransform
     osg::ref_ptr<osg::MatrixTransform> 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<osgUtil::TransformCallback> rotorCallback 
-        = new osgUtil::TransformCallback(pivot, axis, 
+    osg::ref_ptr<osgUtil::TransformCallback> 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
@@ -1080,11 +1717,28 @@
     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<osg::MatrixTransform> 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());
@@ -1092,18 +1746,23 @@
     // 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
@@ -1119,23 +1778,28 @@
     // Create a new osg::MatrixTransform
     osg::ref_ptr<osg::MatrixTransform> 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)
 {
@@ -1150,7 +1814,9 @@
         (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]));
     }
@@ -1159,8 +1825,8 @@
             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)
@@ -1177,62 +1843,4 @@
 }
 ////////////////////////////////////////////////////////////////////////////
-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<osg::MatrixTransform> 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<osg::MatrixTransform> 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<osg::MatrixTransform> 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<osg::MatrixTransform>  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<osg::MatrixTransform>  smt = new osg::MatrixTransform(mat);
-        groupStack.top()->addChild(smt.get());
-        groupStack.push(smt.get());
-    }
-}
-////////////////////////////////////////////////////////////////////////////
 void ConvertFromInventor::addTriangleCB(void* data, SoCallbackAction* action,
                                         const SoPrimitiveVertex* v0,
@@ -1241,5 +1849,5 @@
 {
     ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
-    
+
     switch (thisPtr->vertexOrder)
     {
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/README.txt
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/README.txt (revision 7348)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/README.txt (revision 11032)
@@ -18,5 +18,5 @@
 
 - Coin - GPL, support of VRML 2.0
-  (http://www.coin3d.org) 
+  (http://www.coin3d.org)
 - SGI Inventor - LGPL
   (http://oss.sgi.com/projects/inventor/)
@@ -27,7 +27,8 @@
 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:
Index: /OpenSceneGraph/trunk/src/osgPlugins/Inventor/CMakeLists.txt
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/CMakeLists.txt (revision 9339)
+++ /OpenSceneGraph/trunk/src/osgPlugins/Inventor/CMakeLists.txt (revision 11032)
@@ -4,5 +4,4 @@
     ConvertToInventor.cpp
     ConvertFromInventor.cpp
-    GroupSoLOD.cpp
     PendulumCallback.cpp
     ReaderWriterIV.cpp
@@ -12,5 +11,4 @@
     ConvertToInventor.h
     ConvertFromInventor.h
-    GroupSoLOD.h
     PendulumCallback.h
     ReaderWriterIV.h
@@ -22,5 +20,5 @@
 INCLUDE_DIRECTORIES(${INVENTOR_INCLUDE_DIR})
 
-SET(TARGET_LIBRARIES_VARS INVENTOR_LIBRARY)
+SET(TARGET_ADDED_LIBRARIES ${INVENTOR_LIBRARY})
 
 SETUP_PLUGIN(iv iv)
Index: /enSceneGraph/trunk/src/osgPlugins/Inventor/GroupSoLOD.h
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/GroupSoLOD.h (revision 2225)
+++  (revision )
@@ -1,22 +1,0 @@
-#ifndef _GROUPSOLOD_H_
-#define _GROUPSOLOD_H_
-
-#include <Inventor/nodes/SoLOD.h>
-#include <Inventor/nodes/SoSubNode.h>
-
-class GroupSoLOD : public SoLOD 
-{
-    SO_NODE_HEADER(GroupSoLOD);
-
-public:
-    GroupSoLOD();
-    static void initClass();
-
-protected:
-    virtual void callback(SoCallbackAction *action);
-
-private:
-    virtual ~GroupSoLOD();
-};
-
-#endif
Index: /enSceneGraph/trunk/src/osgPlugins/Inventor/GroupSoLOD.cpp
===================================================================
--- /OpenSceneGraph/trunk/src/osgPlugins/Inventor/GroupSoLOD.cpp (revision 7375)
+++  (revision )
@@ -1,28 +1,0 @@
-#include <Inventor/nodes/SoGroup.h>
-#include <Inventor/actions/SoCallbackAction.h>
-
-#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);
-}
-
