root/OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertFromInventor.cpp @ 13557

Revision 13557, 82.5 kB (checked in by robert, 99 minutes ago)

Improved handling of VolumeSettings?

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1#include "ConvertFromInventor.h"
2
3#include "PendulumCallback.h"
4#include "ShuttleCallback.h"
5
6// OSG headers
7#include <osg/MatrixTransform>
8#include <osg/Node>
9#include <osg/Geode>
10#include <osg/Notify>
11#include <osg/LineWidth>
12#include <osg/Point>
13#include <osg/TexEnv>
14#include <osg/Texture2D>
15#include <osg/PolygonMode>
16#include <osg/BlendFunc>
17#include <osg/Material>
18#include <osg/CullFace>
19#include <osg/Depth>
20#include <osg/LightModel>
21#include <osg/LightSource>
22#include <osg/ShadeModel>
23#include <osg/LOD>
24#include <osgDB/ReadFile>
25#include <osgUtil/TransformCallback>
26
27// Inventor headers
28#include <Inventor/SoDB.h>
29#include <Inventor/SoInteraction.h>
30#include <Inventor/nodes/SoSeparator.h>
31#include <Inventor/nodes/SoTransformSeparator.h>
32#include <Inventor/nodes/SoShape.h>
33#include <Inventor/nodes/SoVertexShape.h>
34#include <Inventor/nodes/SoLight.h>
35#include <Inventor/nodes/SoDirectionalLight.h>
36#include <Inventor/nodes/SoSpotLight.h>
37#include <Inventor/nodes/SoPointLight.h>
38#include <Inventor/nodes/SoRotor.h>
39#include <Inventor/nodes/SoPendulum.h>
40#include <Inventor/nodes/SoShuttle.h>
41#include <Inventor/nodes/SoLOD.h>
42#include <Inventor/nodes/SoTexture2.h>
43#include <Inventor/nodes/SoEnvironment.h>
44#include <Inventor/misc/SoChildList.h>
45#include <Inventor/SoPrimitiveVertex.h>
46#include <Inventor/SbLinear.h>
47#include <Inventor/nodes/SoTransform.h>
48#include <Inventor/nodes/SoInfo.h>
49#include <Inventor/actions/SoWriteAction.h>
50#include <Inventor/elements/SoModelMatrixElement.h>
51
52#ifdef __COIN__
53#include <Inventor/VRMLnodes/SoVRMLImageTexture.h>
54#include <Inventor/VRMLnodes/SoVRMLTransform.h>
55#include <Inventor/VRMLnodes/SoVRMLAppearance.h>
56#include <Inventor/VRMLnodes/SoVRMLMaterial.h>
57#include <Inventor/lists/SbStringList.h>
58#endif
59
60#if defined(__COIN__) && (COIN_MAJOR_VERSION >= 3 || \
61    (COIN_MAJOR_VERSION == 2 && COIN_MINOR_VERSION>=5))
62#define INVENTOR_SHADERS_AVAILABLE
63#endif
64
65#ifdef INVENTOR_SHADERS_AVAILABLE
66#include <Inventor/nodes/SoShaderProgram.h>
67#include <Inventor/nodes/SoVertexShader.h>
68#include <Inventor/nodes/SoGeometryShader.h>
69#include <Inventor/nodes/SoFragmentShader.h>
70#endif
71
72#include <map>
73#include <assert.h>
74#include <math.h>
75#include <string.h>
76#ifdef __linux
77#include <values.h>
78#endif
79#ifdef __APPLE__
80#include <float.h>
81#endif
82
83#define DEBUG_IV_PLUGIN
84#define NOTIFY_HEADER "Inventor Plugin (reader): "
85
86///////////////////////////////////////////
87ConvertFromInventor::ConvertFromInventor()
88{
89    numPrimitives = 0;
90}
91///////////////////////////////////////////
92ConvertFromInventor::~ConvertFromInventor()
93{
94}
95///////////////////////////////////////////////////////////////////
96static bool
97nodePreservesState(const SoNode *node)
98{
99    return node->isOfType(SoSeparator::getClassTypeId()) ||
100           (node->getChildren() != NULL && node->affectsState() == FALSE);
101}
102////////////////////////////////////////////////////////////////////
103SoCallbackAction::Response
104ConvertFromInventor::restructure(void* data, SoCallbackAction* action,
105                                 const SoNode* node)
106{
107#ifdef DEBUG_IV_PLUGIN
108    OSG_DEBUG << NOTIFY_HEADER << "restructure() "
109              << node->getTypeId().getName().getString();
110#endif
111
112    int childrenTotal = 0;
113    int numModifiedChildren = 0;
114    int numRemovedNodes = 0;
115    std::vector<std::vector<int> > &stack = *((std::vector<std::vector<int> >*)data);
116
117    if (node->isOfType(SoGroup::getClassTypeId())) {
118
119        SoGroup *group = (SoGroup*)node;
120        SoGroup *affectedScene = NULL;
121        childrenTotal = group->getNumChildren();
122
123        for (int i=0, c=group->getNumChildren(); i<c; i++) {
124            SoNode *child = group->getChild(i);
125            if (!child->isOfType(SoSeparator::getClassTypeId()) &&
126                child->affectsState()) {
127
128                // Put the node bellow separator
129                SoSeparator *s = new SoSeparator;
130                s->addChild(group->getChild(i));
131                group->replaceChild(i, s);
132                numModifiedChildren++;
133
134                // Create the scene that may be affected by the node
135                if (!affectedScene) {
136
137                    // Create the graph of nodes that may be influenced
138                    // by the node
139                    const SoFullPath *path = (const SoFullPath*)action->getCurPath();
140                    //assert(path->getLength() == 0 ||
141                    //       path->getNode(path->getLength()-1) == group &&
142                    //       "Group being restructured is not at the end of the path.");
143                    int stackLevel = stack.size()-2;
144                    for (int j=path->getLength()-2; j>=0; j--, stackLevel--) {
145
146                        // Get the appropriate stack level of nodesToRemove
147                        assert(stackLevel >=0);
148                        std::vector<int> &nodesToRemove = stack[stackLevel];
149
150                        // Get parent and index of the current group
151                        SoNode *parent = path->getNode(j);
152                        int childIndex = path->getIndex(j+1);
153                        const SoChildList *chl = parent->getChildren();
154                        assert(chl->operator[](childIndex) == path->getNode(j+1) &&
155                               "Wrong indexing.");
156
157                        // Create affected scene graph
158                        if (!affectedScene)
159                            affectedScene = new SoGroup;
160
161                        // Copy nodes to the graph
162                        for (int k=childIndex+1, n=chl->getLength(); k<n; k++) {
163                            affectedScene->addChild(chl->operator[](k));
164                            nodesToRemove.push_back(k);
165                            numRemovedNodes++;
166                        }
167
168                        // Stop recursion if we reached separator
169                        // or other state-preserving node.
170                        if (nodePreservesState(parent))
171                            break;
172                    }
173                }
174
175                // Append the affected graph to the separator
176                s->addChild(affectedScene);
177            }
178        }
179    }
180
181#ifdef DEBUG_IV_PLUGIN
182    if (numModifiedChildren == 0)
183    {
184        OSG_DEBUG << ": no changes necessary" << std::endl;
185    }
186    else
187    {
188        OSG_DEBUG << ": " << numModifiedChildren <<
189                  " nodes of " << childrenTotal << " restruc., " <<
190                  numRemovedNodes << " removed" << std::endl;
191    }
192#endif
193
194    return SoCallbackAction::CONTINUE;
195}
196///////////////////////////////////////////////////////////
197SoCallbackAction::Response
198ConvertFromInventor::restructurePreNode(void* data, SoCallbackAction* action,
199                             const SoNode* node)
200{
201    std::vector<std::vector<int> > &stack = *((std::vector<std::vector<int> >*)data);
202
203    stack.push_back(std::vector<int>());
204
205    return SoCallbackAction::CONTINUE;
206}
207////////////////////////////////////////////////////////////////////
208SoCallbackAction::Response
209ConvertFromInventor::restructurePostNode(void* data, SoCallbackAction* action,
210                              const SoNode* node)
211{
212    std::vector<std::vector<int> > &stack = *((std::vector<std::vector<int> >*)data);
213
214    assert(stack.size() > 0 && "Stack is empty");
215    std::vector<int> &nodesToRemove = stack.back();
216
217    if (nodesToRemove.size() > 0) {
218
219#ifdef DEBUG_IV_PLUGIN
220        OSG_DEBUG << NOTIFY_HEADER << "postNode()   "
221                  << node->getTypeId().getName().getString()
222                  << " (level " << stack.size() << ") removed "
223                  << nodesToRemove.size() << " node(s)" << std::endl;
224#endif
225
226        assert(node->getChildren());
227        for (int i=nodesToRemove.size()-1; i>=0; i--) {
228            //assert(i==0 || nodesToRemove[i-1] < nodesToRemove[i] &&
229            //       "Children to remove are not in order.");
230            node->getChildren()->remove(nodesToRemove[i]);
231        }
232    }
233
234    stack.pop_back();
235
236    return SoCallbackAction::CONTINUE;
237}
238///////////////////////////////////////////////////////////////////
239void
240ConvertFromInventor::preprocess(SoNode* root)
241{
242#ifdef DEBUG_IV_PLUGIN
243    OSG_DEBUG << NOTIFY_HEADER << "Preprocessing..." << std::endl;
244#endif
245
246    SoCallbackAction action;
247    std::vector<std::vector<int> > stackOfNodesToRemove;
248
249    // Callbacks for troublesome nodes
250    action.addPreCallback(SoNode::getClassTypeId(),
251                          restructurePreNode, &stackOfNodesToRemove);
252    action.addPostCallback(SoLOD::getClassTypeId(),
253                           restructure, &stackOfNodesToRemove);
254    action.addPostCallback(SoNode::getClassTypeId(),
255                           restructurePostNode, &stackOfNodesToRemove);
256
257    // Traverse the scene
258    action.apply(root);
259
260#if 0 // For debugging purposes: Write preprocessed scene to the file
261    SoOutput out;
262    out.openFile("preprocess.iv");
263    SoWriteAction wa(&out);
264    wa.apply(root);
265#endif
266}
267///////////////////////////////////////////////////////////
268osg::Node*
269ConvertFromInventor::convert(SoNode* ivRootNode)
270{
271#ifdef DEBUG_IV_PLUGIN
272    OSG_DEBUG << NOTIFY_HEADER << "Converting..." << std::endl;
273#endif
274
275    // Transformation matrix for converting Inventor coordinate system to OSG
276    // coordinate system
277    osg::Matrix ivToOSGMat(osg::Matrix(1.0, 0.0, 0.0, 0.0,
278                                       0.0, 0.0, 1.0, 0.0,
279                                       0.0,-1.0, 0.0, 0.0,
280                                       0.0, 0.0, 0.0, 1.0));
281
282    // Root of the scene
283    osg::ref_ptr<osg::Group> osgRootNode = new osg::MatrixTransform(ivToOSGMat);
284
285    // Propagate node name
286    osgRootNode->setName(ivRootNode->getName().getString());
287
288    // Initialize Inventor state stack
289    // (ivStateStack is used to track the state that is not accessible by
290    // SoCallbackAction functions)
291    ivStateStack.push(IvStateItem(ivRootNode, osgRootNode.get()));
292
293    // Create callback actions for the inventor nodes
294    // These callback functions perform the conversion
295    // note: if one class is derived from the other and both callbacks
296    // are registered, both functions will be called
297    SoCallbackAction cbAction;
298
299    // Node callbacks are used for detecting which node
300    // preserves state (like SoSeparator) and which not.
301    // There are few nodes that behave like SoSeparator although they
302    // are not derived from it.
303    // Note: postNode callback is moved down, because it must be
304    // called as the last callback.
305    cbAction.addPreCallback(SoNode::getClassTypeId(), preNode, this);
306
307    // SoTransformSeparator callbacks. Special handling of transformations.
308    cbAction.addPreCallback(SoTransformSeparator::getClassTypeId(), preTransformSeparator, this);
309    cbAction.addPostCallback(SoTransformSeparator::getClassTypeId(), postTransformSeparator, this);
310
311    // LOD (Level of Detail) callbacks. Handles SoLOD nodes.
312    // FIXME: SoLevelOfDetail needs to be implemented and tested.
313    cbAction.addPreCallback(SoLOD::getClassTypeId(), preLOD, this);
314    cbAction.addPostCallback(SoLOD::getClassTypeId(), postLOD, this);
315
316    // Shape callbacks collects all triangles and all the geometry data.
317    // Moreover, they handle transformations, ...
318    cbAction.addPreCallback(SoShape::getClassTypeId(), preShape, this);
319    cbAction.addPostCallback(SoShape::getClassTypeId(), postShape, this);
320
321    // Handling of textures
322    cbAction.addPostCallback(SoTexture2::getClassTypeId(),
323                            postTexture, this);
324#ifdef __COIN__
325    cbAction.addPostCallback(SoVRMLImageTexture::getClassTypeId(),
326                            postTexture, this);
327    cbAction.addPostCallback(SoVRMLAppearance::getClassTypeId(),
328                            postTexture, this);
329#endif
330
331#ifdef __COIN__
332    cbAction.addPreCallback(SoInfo::getClassTypeId(), preInfo, this);
333#endif
334
335    // Lights
336    cbAction.addPreCallback(SoLight::getClassTypeId(), preLight, this);
337
338    // Environment (ambient light,...)
339    cbAction.addPreCallback(SoEnvironment::getClassTypeId(), preEnvironment, this);
340
341    // Shaders
342#ifdef INVENTOR_SHADERS_AVAILABLE
343    cbAction.addPreCallback(SoShaderProgram::getClassTypeId(), preShaderProgram, this);
344#endif
345
346    // Motion callbacks
347    cbAction.addPreCallback(SoRotor::getClassTypeId(), preRotor, this);
348    cbAction.addPreCallback(SoPendulum::getClassTypeId(), prePendulum, this);
349    cbAction.addPreCallback(SoShuttle::getClassTypeId(), preShuttle, this);
350
351    // Geometry callbacks
352    cbAction.addTriangleCallback(SoShape::getClassTypeId(), addTriangleCB, this);
353    cbAction.addLineSegmentCallback(SoShape::getClassTypeId(), addLineSegmentCB,
354                                    this);
355    cbAction.addPointCallback(SoShape::getClassTypeId(), addPointCB, this);
356
357    // Post node callback
358    cbAction.addPostCallback(SoNode::getClassTypeId(), postNode, this);
359
360    // Traverse the inventor scene graph
361    cbAction.apply(ivRootNode);
362
363    // Remove superfluous group
364    if (osgRootNode->getNumChildren() == 1) {
365        osg::ref_ptr<osg::Group> toRemove = osgRootNode->getChild(0)->asGroup();
366        assert(toRemove.get() &&
367               strcmp(toRemove->className(), "Group") == 0 &&
368               "IvStateStack osg graph is expected to be "
369               "headed by osg::Group");
370        osgRootNode->removeChild(0u);
371        for (int i=0, c=toRemove->getNumChildren(); i<c; i++)
372            osgRootNode->addChild(toRemove->getChild(i));
373    }
374
375    return osgRootNode.get();
376}
377///////////////////////////////////////////////////////////////////
378static void
379notifyAboutMatrixContent(const osg::NotifySeverity level, const SbMatrix &m)
380{
381    SbVec3f t,s;
382    SbRotation r,so;
383    m.getTransform(t, r, s, so);
384    SbVec3f axis;
385    float angle;
386    r.getValue(axis, angle);
387    OSG_NOTIFY(level) << NOTIFY_HEADER << "  Translation: " <<
388              t[0] << "," << t[1] << "," << t[2] << std::endl;
389    OSG_NOTIFY(level) << NOTIFY_HEADER << "  Rotation: (" <<
390              axis[0] << "," << axis[1] << "," << axis[2] << ")," << angle << std::endl;
391}
392///////////////////////////////////////////////////////////////////
393void
394ConvertFromInventor::appendNode(osg::Node *n, const SoCallbackAction *action)
395{
396    IvStateItem &ivState = ivStateStack.top();
397    SbMatrix currentMatrix = action->getModelMatrix();
398    SbMatrix inheritedMatrix = ivState.inheritedTransformation;
399
400    // Keep children order - this must be done for some nodes like
401    // SoSwitch, SoLOD,...
402    // We will append dummy nodes if the child is expected to be on
403    // higher index.
404    if (ivState.flags & IvStateItem::KEEP_CHILDREN_ORDER) {
405
406        // Determine child index
407        int childIndex = -1;
408        const SoFullPath *path = (const SoFullPath*)(((SoCallbackAction*)action)->getCurPath());
409        for (int i=path->getLength()-2; i>=0; i--)
410            if (path->getNode(i) == ivState.keepChildrenOrderParent) {
411                childIndex = path->getIndex(i+1);
412                assert(ivState.keepChildrenOrderParent->getChildren());
413                assert((ivState.keepChildrenOrderParent->getChildren()->operator[](childIndex) == path->getNode(i+1)) && "Indexing is wrong.");
414                break;
415            }
416        assert(childIndex != -1 && "Node did not found.");
417
418        // Append dummy nodes to keep children order
419        assert(int(ivState.osgStateRoot->getNumChildren()) <= childIndex &&
420               "Number of children in ivState.osgStateRoot is too big.");
421        while (int(ivState.osgStateRoot->getNumChildren()) < childIndex)
422            ivState.osgStateRoot->addChild(new osg::Node);
423    }
424
425#ifdef DEBUG_IV_PLUGIN
426    OSG_DEBUG << NOTIFY_HEADER << "appendNode: "
427              << n->className();
428#endif
429
430    if (currentMatrix == inheritedMatrix) {
431
432        // just append node to the current group in osg scene graph
433        ivState.osgStateRoot->addChild(n);
434        ivState.lastUsedTransformation = inheritedMatrix;
435
436#ifdef DEBUG_IV_PLUGIN
437        if (osg::isNotifyEnabled(osg::DEBUG_INFO))
438            OSG_DEBUG <<
439                      " uses parent transformation" << std::endl;
440#endif
441
442    } else {
443
444        if (!(ivState.flags & IvStateItem::KEEP_CHILDREN_ORDER) &&
445            currentMatrix == ivState.lastUsedTransformation) {
446
447            // Previous node has the same transformation. Let's use it.
448            assert(ivState.osgStateRoot->getNumChildren() != 0 &&
449                   "This should never happen - there is no item on "
450                   "osgShapeGraphs list while want to use last one.");
451            osg::Transform *t = ivState.osgStateRoot->getChild(ivState.osgStateRoot->getNumChildren()-1)->asTransform();
452            assert(t && "This should never happen - want to use "
453                        "transformation of previous scene geometry "
454                        "and it does not have Transform node.");
455            t->addChild(n);
456
457#ifdef DEBUG_IV_PLUGIN
458            if (osg::isNotifyEnabled(osg::DEBUG_INFO))
459                OSG_DEBUG <<
460                          " reuses previous transformation" << std::endl;
461#endif
462
463        } else {
464
465            // We need a new transformation node
466            osg::Matrix m(osg::Matrix(currentMatrix.operator float*()));
467            osg::Matrix m2;
468            m2.invert(osg::Matrix(inheritedMatrix.operator float*()));
469            m.postMult(m2);
470            osg::MatrixTransform *mt = new osg::MatrixTransform(m);
471            mt->addChild(n);
472
473            ivState.osgStateRoot->addChild(mt);
474            ivState.lastUsedTransformation = currentMatrix;
475
476#ifdef DEBUG_IV_PLUGIN
477            if (osg::isNotifyEnabled(osg::DEBUG_INFO)) {
478                OSG_DEBUG <<
479                          " uses local transformation:" << std::endl;
480                notifyAboutMatrixContent(osg::DEBUG_INFO,
481                          SbMatrix((SbMat&)(*osg::Matrixf(m).ptr())));
482            }
483#endif
484        }
485    }
486}
487///////////////////////////////////////////////////////////////////
488void
489ConvertFromInventor::ivPushState(const SoCallbackAction *action,
490                                 const SoNode *initiator, const int flags,
491                                 osg::Group *root)
492{
493    assert(ivStateStack.size() >= 1 && "There must be at least one "
494           "value in the ivStateStack to use ivPushState function.");
495
496    // Propagate node name
497    root->setName(initiator->getName().getString());
498
499    // APPEND_AT_PUSH
500    if (flags & IvStateItem::APPEND_AT_PUSH)
501        appendNode(root, action);
502
503    // Push state
504    ivStateStack.push(IvStateItem(ivStateStack.top(), action, initiator, flags, root));
505
506}
507///////////////////////////////////////////////////////////////////
508void
509ConvertFromInventor::ivPopState(const SoCallbackAction *action,
510                                const SoNode *initiator)
511{
512    bool multipop;
513    do {
514        assert(ivStateStack.size() >= 2 && "There must be at least two "
515               "values in the ivStateStack to use ivPopState function.");
516
517        // Get multipop value
518        IvStateItem ivState = ivStateStack.top();
519        multipop = ivState.flags & IvStateItem::MULTI_POP;
520        //assert(multipop ||
521        //       ivState.pushInitiator == initiator &&
522        //       "ivStateStack push was initiated by different node.");
523
524        // Get osgStateRoot (note: we HAVE TO reference it)
525        osg::ref_ptr<osg::Group> r = ivState.osgStateRoot;
526
527        // Pop state
528        ivStateStack.pop();
529
530        // Update state from already popped values
531        if ((ivState.flags & (IvStateItem::UPDATE_STATE |
532            IvStateItem::UPDATE_STATE_EXCEPT_TRANSFORM)) != 0) {
533            IvStateItem &newTop = ivStateStack.top();
534            newTop.currentTexture = ivState.currentTexture;
535            newTop.currentLights = ivState.currentLights;
536            newTop.currentGLProgram = ivState.currentGLProgram;
537        }
538
539        // APPEND_AT_PUSH
540        if (!(ivState.flags & IvStateItem::APPEND_AT_PUSH))
541            appendNode(r.get(), action);
542
543    } while (multipop);
544
545}
546///////////////////////////////////////////////////////////////////
547SoCallbackAction::Response
548ConvertFromInventor::preNode(void* data, SoCallbackAction* action,
549                             const SoNode* node)
550{
551#ifdef DEBUG_IV_PLUGIN
552    OSG_DEBUG << NOTIFY_HEADER << "preNode()    "
553              << node->getTypeId().getName().getString() << std::endl;
554#endif
555
556    if (nodePreservesState(node)) {
557
558        // push state
559        ConvertFromInventor *thisPtr = (ConvertFromInventor *) data;
560        thisPtr->ivPushState(action, node);
561#ifdef DEBUG_IV_PLUGIN
562        if (osg::isNotifyEnabled(osg::DEBUG_INFO)) {
563            OSG_DEBUG << NOTIFY_HEADER << "push state, saved values: " << std::endl;
564            notifyAboutMatrixContent(osg::DEBUG_INFO, action->getModelMatrix());
565        }
566#endif
567    }
568
569    return SoCallbackAction::CONTINUE;
570}
571////////////////////////////////////////////////////////////////////
572SoCallbackAction::Response
573ConvertFromInventor::postNode(void* data, SoCallbackAction* action,
574                              const SoNode* node)
575{
576#ifdef DEBUG_IV_PLUGIN
577    OSG_DEBUG << NOTIFY_HEADER << "postNode()   "
578              << node->getTypeId().getName().getString() << std::endl;
579#endif
580
581    if (nodePreservesState(node)) {
582
583        // pop state
584        ConvertFromInventor *thisPtr = (ConvertFromInventor *) data;
585        assert(thisPtr->ivStateStack.size() > 0 && "ivStackState underflow");
586        thisPtr->ivPopState(action, node);
587
588#ifdef DEBUG_IV_PLUGIN
589        if (osg::isNotifyEnabled(osg::DEBUG_INFO)) {
590            OSG_DEBUG << NOTIFY_HEADER <<
591                      "pop state, restored transformation: " << std::endl;
592            notifyAboutMatrixContent(osg::DEBUG_INFO, action->getModelMatrix());
593        }
594#endif
595    }
596
597    return SoCallbackAction::CONTINUE;
598}
599///////////////////////////////////////////////////////////////////
600SoCallbackAction::Response
601ConvertFromInventor::preTransformSeparator(void* data, SoCallbackAction* action,
602                             const SoNode* node)
603{
604#ifdef DEBUG_IV_PLUGIN
605    OSG_DEBUG << NOTIFY_HEADER << "preTransformSeparator()    "
606              << node->getTypeId().getName().getString() << std::endl;
607#endif
608
609    // push state
610    ConvertFromInventor *thisPtr = (ConvertFromInventor *) data;
611    thisPtr->ivPushState(action, node, IvStateItem::UPDATE_STATE_EXCEPT_TRANSFORM,
612                         new osg::Group());
613
614    return SoCallbackAction::CONTINUE;
615}
616////////////////////////////////////////////////////////////////////
617SoCallbackAction::Response
618ConvertFromInventor::postTransformSeparator(void* data, SoCallbackAction* action,
619                              const SoNode* node)
620{
621#ifdef DEBUG_IV_PLUGIN
622    OSG_DEBUG << NOTIFY_HEADER << "postTransformSeparator()   "
623              << node->getTypeId().getName().getString() << std::endl;
624#endif
625
626    // pop state
627    ConvertFromInventor *thisPtr = (ConvertFromInventor *) data;
628    assert(thisPtr->ivStateStack.size() > 0 && "ivStackState underflow");
629    thisPtr->ivPopState(action, node);
630
631    return SoCallbackAction::CONTINUE;
632}
633///////////////////////////////////////////////////////////////////
634SoCallbackAction::Response
635ConvertFromInventor::preLOD(void* data, SoCallbackAction* action,
636                            const SoNode* node)
637{
638#ifdef DEBUG_IV_PLUGIN
639    OSG_DEBUG << NOTIFY_HEADER << "preLOD()   "
640              << node->getTypeId().getName().getString() << std::endl;
641#endif
642
643    // init values
644    ConvertFromInventor* thisPtr = (ConvertFromInventor*)data;
645
646    // SoLOD
647    // Note: It is not possible to convert SoLOD to osg:LOD
648    // in any non-complex algorithm, because SoLOD does not preserves
649    // traversal state (like SoSeparator). Thus, following example
650    // can not be easily converted:
651    //
652    // SoLOD {
653    //   range [...]
654    //   Complexity { value 0.1 }
655    //   Complexity { value 0.2 }
656    //   Complexity { value 0.3 }
657    // }
658    // Sphere {}
659    //
660    // It was decided that it is necessary to preprocess scene
661    // in a way to avoid any state to come out of SoLOD. For example:
662    //
663    // SoLOD {
664    //   range [...]
665    //   Separator {
666    //     Complexity { value 0.1 }
667    //     DEF mySphere Sphere {}
668    //   }
669    //   Separator {
670    //     Complexity { value 0.2 }
671    //     USE mySphere
672    //   }
673    //   Separator {
674    //     Complexity { value 0.3 }
675    //     USE mySphere
676    //   }
677    // }
678    //
679    // Such scene can be converted easily to OSG.
680    if (node->isOfType(SoLOD::getClassTypeId())) {
681
682        thisPtr->ivPushState(action, node, IvStateItem::KEEP_CHILDREN_ORDER,
683                             new osg::LOD);
684        thisPtr->ivStateStack.top().keepChildrenOrderParent = node;
685
686        return SoCallbackAction::CONTINUE;
687    }
688
689    return SoCallbackAction::CONTINUE;
690}
691//////////////////////////////////////////////////////////////
692SoCallbackAction::Response
693ConvertFromInventor::postLOD(void* data, SoCallbackAction* action,
694                             const SoNode* node)
695{
696#ifdef DEBUG_IV_PLUGIN
697    OSG_DEBUG << NOTIFY_HEADER << "postLOD()  "
698              << node->getTypeId().getName().getString() << std::endl;
699#endif
700
701    // SoGroup -> do nothing
702    if (node->getTypeId() == SoGroup::getClassTypeId())
703        return SoCallbackAction::CONTINUE;
704
705    // init values
706    ConvertFromInventor* thisPtr = (ConvertFromInventor*)data;
707    IvStateItem &ivState = thisPtr->ivStateStack.top();
708
709    // SoLOD
710    if (node->isOfType(SoLOD::getClassTypeId())) {
711
712        osg::LOD *lod = dynamic_cast<osg::LOD*>(ivState.osgStateRoot.get());
713        SoLOD *ivLOD = (SoLOD*)node;
714
715        // LOD center
716        SbVec3f ivCenter = ivLOD->center.getValue();
717        lod->setCenter(osg::Vec3(ivCenter[0], ivCenter[1], ivCenter[2]));
718
719        // Verify the number of children and range values
720        int num = lod->getNumChildren();
721        if (ivLOD->range.getNum()+1 != num &&
722            !(num == 0 && ivLOD->range.getNum() == 0)) {
723            OSG_WARN << NOTIFY_HEADER <<
724                      "Warning: SoLOD does not contain "
725                      "correct data in range field." << std::endl;
726            if (ivLOD->range.getNum()+1 < num) {
727                lod->removeChildren(ivLOD->range.getNum() + 1,
728                                    num - ivLOD->range.getNum() - 1);
729                num = ivLOD->range.getNum() + 1;
730            }
731        }
732
733        // Get the ranges and set it
734        if (num > 0) {
735            if (num == 1)
736                lod->setRange(0, 0.0, FLT_MAX);
737            else {
738                lod->setRange(0, 0.0, ivLOD->range[0]);
739                for (int i = 1; i < num-1; i++)
740                    lod->setRange(i, ivLOD->range[i-1], ivLOD->range[i]);
741                lod->setRange(num-1, ivLOD->range[num-2], FLT_MAX);
742            }
743        }
744
745#ifdef DEBUG_IV_PLUGIN
746        OSG_DEBUG << NOTIFY_HEADER <<
747                  "Appending osg::LOD with " << num << " children." << std::endl;
748#endif
749
750        assert(ivState.keepChildrenOrderParent == node &&
751               "Current node is not the root of keepChildrenOrder graph.");
752        thisPtr->ivPopState(action, node);
753
754        return SoCallbackAction::CONTINUE;
755    }
756
757    return SoCallbackAction::CONTINUE;
758}
759///////////////////////////////////////////////////////////////////
760SoCallbackAction::Response
761ConvertFromInventor::preShape(void* data, SoCallbackAction* action,
762                              const SoNode* node)
763{
764#ifdef DEBUG_IV_PLUGIN
765    OSG_DEBUG << NOTIFY_HEADER << "preShape()   "
766              << node->getTypeId().getName().getString() << std::endl;
767#endif
768
769    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
770
771    // Normal and color binding map from Inventor to OSG
772    static std::map<SoNormalBinding::Binding, deprecated_osg::Geometry::AttributeBinding>
773        normBindingMap;
774    static std::map<SoMaterialBinding::Binding, deprecated_osg::Geometry::AttributeBinding>
775        colBindingMap;
776    static bool firstTime = true;
777    if (firstTime)
778    {
779        normBindingMap[SoNormalBinding::OVERALL]
780                                        = deprecated_osg::Geometry::BIND_OVERALL;
781        normBindingMap[SoNormalBinding::PER_PART]
782                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
783        normBindingMap[SoNormalBinding::PER_PART_INDEXED]
784                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
785        normBindingMap[SoNormalBinding::PER_FACE]
786                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
787        normBindingMap[SoNormalBinding::PER_FACE_INDEXED]
788                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
789        normBindingMap[SoNormalBinding::PER_VERTEX]
790                                        = deprecated_osg::Geometry::BIND_PER_VERTEX;
791        normBindingMap[SoNormalBinding::PER_VERTEX_INDEXED]
792                                        = deprecated_osg::Geometry::BIND_PER_VERTEX;
793
794        colBindingMap[SoMaterialBinding::OVERALL]
795                                        = deprecated_osg::Geometry::BIND_OVERALL;
796        colBindingMap[SoMaterialBinding::PER_PART]
797                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
798        colBindingMap[SoMaterialBinding::PER_PART_INDEXED]
799                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
800        colBindingMap[SoMaterialBinding::PER_FACE]
801                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
802        colBindingMap[SoMaterialBinding::PER_FACE_INDEXED]
803                                        = deprecated_osg::Geometry::BIND_PER_PRIMITIVE;
804        colBindingMap[SoMaterialBinding::PER_VERTEX]
805                                        = deprecated_osg::Geometry::BIND_PER_VERTEX;
806        colBindingMap[SoMaterialBinding::PER_VERTEX_INDEXED]
807                                        = deprecated_osg::Geometry::BIND_PER_VERTEX;
808
809        firstTime = false;
810    }
811
812    // Get normal and color binding
813    if (node->isOfType(SoVertexShape::getClassTypeId()))
814    {
815        thisPtr->normalBinding = normBindingMap[action->getNormalBinding()];
816        thisPtr->colorBinding = colBindingMap[action->getMaterialBinding()];
817    }
818    else
819    {
820        thisPtr->normalBinding = deprecated_osg::Geometry::BIND_PER_VERTEX;
821        thisPtr->colorBinding = deprecated_osg::Geometry::BIND_PER_VERTEX;
822    }
823
824    // Check vertex ordering
825    if (action->getVertexOrdering() == SoShapeHints::CLOCKWISE)
826        thisPtr->vertexOrder = CLOCKWISE;
827    else
828        thisPtr->vertexOrder = COUNTER_CLOCKWISE;
829
830    // Clear the data from the previous shape callback
831    thisPtr->numPrimitives = 0;
832    thisPtr->vertices.clear();
833    thisPtr->normals.clear();
834    thisPtr->colors.clear();
835    thisPtr->textureCoords.clear();
836
837    return SoCallbackAction::CONTINUE;
838}
839///////////////////////////////////////////////////////////
840// OSG doesn't seem to have a transpose function         //
841//for matrices                                           //
842///////////////////////////////////////////////////////////
843void
844ConvertFromInventor::transposeMatrix(osg::Matrix& mat)
845{
846    float tmp;
847    for (int j = 0; j < 4; j++)
848    {
849        for (int i = j + 1; i < 4; i++)
850        {
851            tmp = mat.operator()(j,i);
852            mat.operator()(j,i) = mat.operator()(i,j);
853            mat.operator()(i,j) = tmp;
854        }
855    }
856
857}
858////////////////////////////////////////////////////////////////////
859SoCallbackAction::Response
860ConvertFromInventor::postShape(void* data, SoCallbackAction* action,
861                               const SoNode* node)
862{
863#ifdef DEBUG_IV_PLUGIN
864    OSG_DEBUG << NOTIFY_HEADER << "postShape()  "
865              << node->getTypeId().getName().getString() << std::endl;
866#endif
867
868    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
869
870
871    // Create a new Geometry
872    osg::ref_ptr<deprecated_osg::Geometry> geometry = new deprecated_osg::Geometry;
873
874
875    osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array(thisPtr->vertices.size());
876    for (unsigned int i = 0; i < thisPtr->vertices.size(); i++)
877        (*coords)[i] = thisPtr->vertices[i];
878    geometry->setVertexArray(coords.get());
879
880    osg::ref_ptr<osg::Vec3Array> norms = NULL;
881    if (thisPtr->normalBinding == deprecated_osg::Geometry::BIND_OVERALL)
882    {
883        norms = new osg::Vec3Array(1);
884        const SbVec3f &norm = action->getNormal(0);
885        (*norms)[0].set(norm[0], norm[1], norm[2]);
886    }
887    else
888    {
889        norms = new osg::Vec3Array(thisPtr->normals.size());
890        for (unsigned int i = 0; i < thisPtr->normals.size(); i++)
891        {
892            (*norms)[i] = thisPtr->normals[i];
893        }
894    }
895    geometry->setNormalArray(norms.get());
896    geometry->setNormalBinding(thisPtr->normalBinding);
897
898    // Set the colors
899    osg::ref_ptr<osg::Vec4Array> cols;
900    if (thisPtr->colorBinding == deprecated_osg::Geometry::BIND_OVERALL)
901    {
902        cols = new osg::Vec4Array(1);
903        SbColor ambient, diffuse, specular, emission;
904        float transparency, shininess;
905        action->getMaterial(ambient, diffuse, specular, emission, shininess,
906                            transparency, 0);
907        (*cols)[0].set(diffuse[0], diffuse[1], diffuse[2], 1.0 - transparency);
908    }
909    else
910    {
911        cols = new osg::Vec4Array(thisPtr->colors.size());
912        for (unsigned int i = 0; i < thisPtr->colors.size(); i++)
913            (*cols)[i] = thisPtr->colors[i];
914    }
915    geometry->setColorArray(cols.get());
916    geometry->setColorBinding(thisPtr->colorBinding);
917
918
919    if (thisPtr->textureCoords.empty())
920    {
921        OSG_DEBUG<<"tex coords not found"<<std::endl;
922    }
923    else
924    {
925
926        // report texture coordinate conditions
927        if (action->getNumTextureCoordinates()>0)
928        {
929            OSG_DEBUG<<"tex coords found"<<std::endl;
930        }
931        else
932        {
933           OSG_DEBUG<<"tex coords generated"<<std::endl;
934        }
935
936        // Get the texture transformation matrix
937        osg::Matrix textureMat;
938        textureMat.set((float *) action->getTextureMatrix().getValue());
939
940        // Transform texture coordinates if texture matrix is not an identity mat
941        osg::Matrix identityMat;
942        identityMat.makeIdentity();
943        osg::ref_ptr<osg::Vec2Array> texCoords
944            = new osg::Vec2Array(thisPtr->textureCoords.size());
945        if (textureMat == identityMat)
946        {
947            // Set the texture coordinates
948            for (unsigned int i = 0; i < thisPtr->textureCoords.size(); i++)
949                (*texCoords)[i] = thisPtr->textureCoords[i];
950        }
951        else
952        {
953            // Transform and set the texture coordinates
954            for (unsigned int i = 0; i < thisPtr->textureCoords.size(); i++)
955            {
956                osg::Vec3 transVec = textureMat.preMult(
957                        osg::Vec3(thisPtr->textureCoords[i][0],
958                                  thisPtr->textureCoords[i][1],
959                                  0.0));
960                (*texCoords)[i].set(transVec.x(), transVec.y());
961            }
962        }
963
964        geometry->setTexCoordArray(0, texCoords.get());
965    }
966
967    // Set the parameters for the geometry
968
969    geometry->addPrimitiveSet(new osg::DrawArrays(thisPtr->primitiveType,0,
970                                                  coords->size()));
971    // Get the StateSet for the geoset
972    osg::ref_ptr<osg::StateSet> stateSet = thisPtr->getStateSet(action);
973    geometry->setStateSet(stateSet.get());
974
975    // Add the geoset to a geode
976    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
977    geode->addDrawable(geometry.get());
978
979    // Set object names
980    const char* name = node->getName().getString();
981    geometry->setName(name);
982    geode->setName(strlen(name)>0 ? name : stateSet->getName());
983
984    // Transformation and scene graph building
985    thisPtr->appendNode(geode.get(), action);
986
987    return SoCallbackAction::CONTINUE;
988}
989///////////////////////////////////////////////////////////////
990
991#ifdef __COIN__
992
993//
994//  Following classes can be used for redirecting texture loading from Coin routines
995//  that use simage library to native OSG routines. This removes the dependency
996//  on simage and using the same routines by OSG and Coin may provide some
997//  advantages to programmer as well.
998//
999//  Classes that are loading textures: SoTexture2, SoTexture3 (since Coin 2.0),
1000//  SoVRMLImageTexture (since Coin 2.0), SoVRMLMovieTexture (not yet implemented in Coin)
1001//
1002//  Principle: SoType::overrideType() can be used for changing
1003//  of the class instantiation method. So let's change it and create our own
1004//  API compatible class that will just not load texture from file.
1005//
1006
1007#include <Inventor/nodes/SoTexture2.h>
1008#include <Inventor/nodes/SoTexture3.h>
1009#include <Inventor/VRMLnodes/SoVRMLImageTexture.h>
1010
1011
1012// This macro gives the common API for all overriding classes.
1013#define OVERRIDE_HEADER(_class_,_parent_) \
1014public: \
1015\
1016    static void overrideClass() \
1017    { \
1018        if (overrideCounter == 0) { \
1019            SoType t = _parent_::getClassTypeId(); \
1020            oldMethod = t.getInstantiationMethod(); \
1021            SoType::overrideType(t, _class_::createInstance); \
1022        } \
1023        overrideCounter++; \
1024    } \
1025\
1026    static SbBool cancelOverrideClass() \
1027    { \
1028        assert(overrideCounter > 0 && #_class_ "::cancelOverride called more times\n" \
1029               "than " #_class_ "::override"); \
1030        assert(_parent_::getClassTypeId().getInstantiationMethod() == _class_::createInstance && \
1031               "Somebody changed " #_parent_ " instantiation method\n" \
1032               "(probably through SoType::overrideType) and did not restored it."); \
1033\
1034        overrideCounter--; \
1035        if (overrideCounter == 0) \
1036            SoType::overrideType(_parent_::getClassTypeId(), oldMethod); \
1037\
1038        return overrideCounter==0; \
1039    } \
1040\
1041private: \
1042\
1043    static int overrideCounter; \
1044\
1045    static SoType::instantiationMethod oldMethod; \
1046\
1047    static void* createInstance() \
1048    { \
1049        return new _class_; \
1050    }
1051
1052#define OVERRIDE_SRC(_class_) \
1053\
1054int _class_::overrideCounter = 0; \
1055SoType::instantiationMethod _class_::oldMethod
1056
1057
1058class SoTexture2Osg : public SoTexture2 {
1059    OVERRIDE_HEADER(SoTexture2Osg, SoTexture2);
1060protected:
1061    virtual SbBool readInstance(SoInput *in, unsigned short flags);
1062};
1063
1064
1065class SoTexture3Osg : public SoTexture3 {
1066    OVERRIDE_HEADER(SoTexture3Osg, SoTexture3);
1067protected:
1068    virtual SbBool readInstance(SoInput *in, unsigned short flags);
1069};
1070
1071
1072class SoVRMLImageTextureOsg : public SoVRMLImageTexture {
1073    OVERRIDE_HEADER(SoVRMLImageTextureOsg, SoVRMLImageTexture);
1074protected:
1075    virtual SbBool readInstance(SoInput *in, unsigned short flags);
1076};
1077
1078OVERRIDE_SRC(SoTexture2Osg);
1079OVERRIDE_SRC(SoTexture3Osg);
1080OVERRIDE_SRC(SoVRMLImageTextureOsg);
1081
1082static osgDB::ReaderWriter::Options* createOptions()
1083{
1084    const SbStringList &soInputDirectories = SoInput::getDirectories();
1085    osgDB::ReaderWriter::Options *options = new osgDB::ReaderWriter::Options;
1086    osgDB::FilePathList &pathList = options->getDatabasePathList();
1087    int c = soInputDirectories.getLength();
1088    for (int i=0; i<c; i++)
1089        pathList.push_back(soInputDirectories[i]->getString());
1090
1091    return options;
1092}
1093
1094static osg::Image* loadImage(const char *fileName, osgDB::ReaderWriter::Options *options)
1095{
1096    osg::ref_ptr<osg::Image> osgImage = osgDB::readImageFile(fileName, options);
1097
1098    if (!osgImage)
1099    {
1100        OSG_WARN << NOTIFY_HEADER << "Could not read texture file '" << fileName << "'.";
1101        return 0;
1102    }
1103
1104    if (!osgImage->isDataContiguous())
1105    {
1106        OSG_WARN << NOTIFY_HEADER << "Inventor cannot handle non contiguous image data found in texture file '" << fileName << "'.";
1107        return 0;
1108    }
1109
1110    return osgImage.release();
1111}
1112
1113SbBool SoTexture2Osg::readInstance(SoInput *in, unsigned short flags)
1114{
1115    // disable notification and read inherited fields
1116    SbBool oldNotify = filename.enableNotify(FALSE);
1117    SbBool readOK = SoNode::readInstance(in, flags);
1118    this->setReadStatus((int) readOK);
1119
1120    // if file name given
1121    if (readOK && !filename.isDefault() && filename.getValue() != "")
1122    {
1123        // create options and read the file
1124        osgDB::ReaderWriter::Options *options = createOptions();
1125        osg::ref_ptr<osg::Image> image = loadImage(filename.getValue().getString(), options);
1126
1127        if (image.valid())
1128        {
1129            // get image dimensions and data
1130            int nc = osg::Image::computeNumComponents(image->getPixelFormat());
1131            SbVec2s size(image->s(), image->t());
1132            unsigned char *bytes = image->data();
1133
1134            // disable notification on image while setting data from filename
1135            // as a notify will cause a filename.setDefault(TRUE)
1136            SbBool oldnotify = this->image.enableNotify(FALSE);
1137            this->image.setValue(size, nc, bytes);
1138            this->image.enableNotify(oldnotify);
1139            // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender()
1140            // We can safely ignore this as we are not going to render the scene graph.
1141        }
1142        else
1143        {
1144            // image loading failed -> set readOK
1145            readOK = FALSE;
1146            this->setReadStatus(FALSE);
1147        }
1148
1149        // write filename, not image
1150        this->image.setDefault(TRUE);
1151    }
1152
1153    filename.enableNotify(oldNotify);
1154    return readOK;
1155}
1156
1157SbBool SoTexture3Osg::readInstance(SoInput *in, unsigned short flags)
1158{
1159    // disable notification and read inherited fields
1160    SbBool oldNotify = filenames.enableNotify(FALSE);
1161    SbBool readOK = SoNode::readInstance(in, flags);
1162    this->setReadStatus((int) readOK);
1163
1164    // if file name given
1165    int numImages = filenames.getNum();
1166    if (readOK && !filenames.isDefault() && numImages > 0)
1167    {
1168        // Fail on empty filenames
1169        SbBool sizeError = FALSE;
1170        SbBool retval = FALSE;
1171        SbVec3s volumeSize(0,0,0);
1172        int volumenc = -1;
1173        int i;
1174        for (i=0; i<numImages; i++)
1175            if (this->filenames[i].getLength()==0) break;
1176
1177        if (i==numImages)
1178        {
1179            // create options
1180            osgDB::ReaderWriter::Options *options = createOptions();
1181
1182            for (int n=0; n<numImages && !sizeError; n++)
1183            {
1184                // read the file
1185                osg::ref_ptr<osg::Image> image = loadImage(filenames[n].getString(), options);
1186
1187                if (!image.valid())
1188                {
1189                    OSG_WARN << NOTIFY_HEADER << "Could not read texture file #" << n << ": "
1190                             << filenames[n].getString() << "\n";
1191                    retval = FALSE;
1192                }
1193                else
1194                {
1195                    // get image dimensions and data
1196                    int nc = osg::Image::computeNumComponents(image->getPixelFormat());
1197                    SbVec3s size(image->s(), image->t(), image->r());
1198                    if (size[2]==0)
1199                        size[2]=1;
1200                    unsigned char *imgbytes = image->data();
1201
1202                    if (this->images.isDefault()) { // First time => allocate memory
1203                        volumeSize.setValue(size[0],
1204                                            size[1],
1205                                            size[2]*numImages);
1206                        volumenc = nc;
1207                        this->images.setValue(volumeSize, nc, NULL);
1208                    }
1209                    else { // Verify size & components
1210                        if (size[0] != volumeSize[0] ||
1211                            size[1] != volumeSize[1] ||
1212                            //FIXME: always 1 or what? (kintel 20020110)
1213                            size[2] != (volumeSize[2]/numImages) ||
1214                            nc != volumenc)
1215                        {
1216                            sizeError = TRUE;
1217                            retval = FALSE;
1218
1219                            OSG_WARN << NOTIFY_HEADER << "Texture file #" << n << " ("
1220                                     << filenames[n].getString() << ") has wrong size: "
1221                                     << "Expected (" << volumeSize[0] << "," << volumeSize[1] << ","
1222                                     << volumeSize[2] << "," << volumenc << ") got ("
1223                                     << size[0] << "," << size[1] << "," << size[2] << "," << nc << ")\n";
1224                        }
1225                    }
1226
1227                    if (!sizeError)
1228                    {
1229                        // disable notification on images while setting data from the
1230                        // filenames as a notify will cause a filenames.setDefault(TRUE).
1231                        SbBool oldnotify = this->images.enableNotify(FALSE);
1232                        unsigned char *volbytes = this->images.startEditing(volumeSize,
1233                                                                            volumenc);
1234                        memcpy(volbytes+int(size[0])*int(size[1])*int(size[2])*nc*n,
1235                               imgbytes, int(size[0])*int(size[1])*int(size[2])*nc);
1236                        this->images.finishEditing();
1237                        this->images.enableNotify(oldnotify);
1238                        // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender()
1239                        // We can safely ignore this as we are not going to render the scene graph.
1240                        retval = TRUE;
1241                    }
1242                }
1243            }
1244        }
1245
1246        if (!retval)
1247        {
1248            // if image loading failed, set read status,
1249            // but not set readOK to false (according to Coin source code)
1250            this->setReadStatus(FALSE);
1251        }
1252
1253        // write filename, not image
1254        this->images.setDefault(TRUE);
1255    }
1256
1257    filenames.enableNotify(oldNotify);
1258    return readOK;
1259}
1260
1261SbBool SoVRMLImageTextureOsg::readInstance(SoInput *in, unsigned short flags)
1262{
1263    // disable notification and read inherited fields
1264    SbBool oldNotify = url.enableNotify(FALSE);
1265    SbBool readOK = SoNode::readInstance(in, flags);
1266    this->setReadStatus((int) readOK);
1267
1268    if (readOK) {
1269
1270        // create options and read the file
1271        osgDB::ReaderWriter::Options *options = createOptions();
1272
1273        if (url.getNum() && url[0].getLength())
1274        {
1275            osg::ref_ptr<osg::Image> image = loadImage(url[0].getString(), options);
1276            if (!image->valid())
1277            {
1278                OSG_WARN << "Could not read texture file: " << url[0].getString() << std::endl;
1279                this->setReadStatus(FALSE);
1280            }
1281            else
1282            {
1283                // get image dimensions and data
1284                int nc = osg::Image::computeNumComponents(image->getPixelFormat());
1285                SbVec2s size(image->s(), image->t());
1286                unsigned char *bytes = image->data();
1287
1288                SbImage ivImage(bytes, size, nc);
1289
1290                // disable notification on image while setting data from filename
1291                // as a notify will cause a filename.setDefault(TRUE)
1292                //SbBool oldnotify = this->image.enableNotify(FALSE); <- difficult to implement for SoVRMLImageTexture
1293                this->setImage(ivImage);
1294                //this->image.enableNotify(oldnotify); <- difficult to implement for SoVRMLImageTexture
1295                // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender()
1296                // We can safely ignore this as we are not going to render the scene graph.
1297            }
1298        }
1299    }
1300
1301    url.enableNotify(oldNotify);
1302    return readOK;
1303}
1304
1305#endif /* __COIN__ */
1306
1307///////////////////////////////////////////////////////////////
1308SoCallbackAction::Response
1309ConvertFromInventor::postTexture(void* data, SoCallbackAction *,
1310                                 const SoNode* node)
1311{
1312#ifdef DEBUG_IV_PLUGIN
1313    OSG_DEBUG << NOTIFY_HEADER << "postTexture()  "
1314              << node->getTypeId().getName().getString();
1315    if (node->isOfType(SoTexture2::getClassTypeId())) {
1316        SoTexture2 *t = (SoTexture2*)node;
1317        if (t->filename.getValue().getLength())
1318            OSG_DEBUG << "  "  << t->filename.getValue().getString();
1319    }
1320    OSG_DEBUG << std::endl;
1321#endif
1322
1323    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1324    bool texturingEnabled = false;
1325
1326    // Texture2
1327    if (node->isOfType(SoTexture2::getClassTypeId())) {
1328
1329        // Check whether texturing was enabled by the texture node
1330        SoTexture2 *t = (SoTexture2*)node;
1331        SbVec2s size;
1332        int nc;
1333        const unsigned char *data = t->image.getValue(size, nc);
1334        texturingEnabled = t->filename.getValue().getLength() ||
1335                           (data && size != SbVec2s(0,0));
1336    }
1337
1338#ifdef __COIN__
1339
1340    // SoVRMLImageTexture
1341    if (node->isOfType(SoVRMLImageTexture::getClassTypeId())) {
1342
1343        // Check whether texturing was enabled by the texture node
1344        SoVRMLImageTexture *t = (SoVRMLImageTexture*)node;
1345        texturingEnabled = t->url.getNum() > 1 || (t->url.getNum() == 1 && t->url[0].getLength() > 0);
1346    }
1347
1348    // SoVRMLAppearance
1349    if (node->isOfType(SoVRMLAppearance::getClassTypeId())) {
1350
1351        // If SoVRMLAppearance is present and there is no texture
1352        // inside, disable texturing
1353        // FIXME: should SoVRMLAppearance really disable texturing
1354        // when not containing SoVRMLImageTexture? Coin is not doing that,
1355        // but it can be Coin bug.
1356        SoVRMLAppearance *a = (SoVRMLAppearance*)node;
1357        if (a->texture.getValue() == NULL)
1358            thisPtr->ivStateStack.top().currentTexture = NULL;
1359
1360        // Do not try to "optimize" this code by removing the return
1361    // and use the one at the end of the function.
1362    // It would break the case when there is texture inside
1363    // the appearance node.
1364        return SoCallbackAction::CONTINUE;
1365    }
1366
1367#endif /* __COIN__ */
1368
1369    // Set current texture
1370    if (texturingEnabled)
1371        thisPtr->ivStateStack.top().currentTexture = node;
1372    else
1373        thisPtr->ivStateStack.top().currentTexture = NULL;
1374
1375    return SoCallbackAction::CONTINUE;
1376}
1377//////////////////////////////////////////////////////////////////
1378void ConvertFromInventor::transformLight(SoCallbackAction* action,
1379                                         const SbVec3f& vec,
1380                                         osg::Vec3& transVec)
1381{
1382    osg::Matrix modelMat;
1383    modelMat.set((float *)action->getModelMatrix().getValue());
1384
1385    transVec.set(vec[0], vec[1], vec[2]);
1386    transVec = modelMat.preMult(transVec);
1387}
1388///////////////////////////////////////////////////////////////////
1389SoCallbackAction::Response
1390ConvertFromInventor::preLight(void* data, SoCallbackAction* action,
1391                              const SoNode* node)
1392{
1393#ifdef DEBUG_IV_PLUGIN
1394    OSG_DEBUG << NOTIFY_HEADER << "preLight()   "
1395              << node->getTypeId().getName().getString() << std::endl;
1396#endif
1397
1398    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1399
1400    // Return if the light is not on
1401    const SoLight* ivLight = (const SoLight*) node;
1402    if (!ivLight->on.getValue())
1403        return SoCallbackAction::CONTINUE;
1404
1405    // Create new OSG light
1406    IvStateItem &ivState = thisPtr->ivStateStack.top();
1407    osg::ref_ptr<osg::Light> osgLight = new osg::Light;
1408
1409    // Get color and intensity
1410    SbVec3f lightColor = ivLight->color.getValue();
1411    float intensity = ivLight->intensity.getValue();
1412
1413    // Set color and intensity
1414    osgLight->setAmbient(osg::Vec4(0.f, 0.f, 0.f, 1.f));
1415    osgLight->setDiffuse(osg::Vec4(lightColor[0] * intensity,
1416                                   lightColor[1] * intensity,
1417                                   lightColor[2] * intensity, 1));
1418    osgLight->setSpecular(osg::Vec4(lightColor[0] * intensity,
1419                                    lightColor[1] * intensity,
1420                                    lightColor[2] * intensity, 1));
1421
1422    // Light type
1423    if (node->isOfType(SoDirectionalLight::getClassTypeId()))
1424    {
1425        SoDirectionalLight *dirLight = (SoDirectionalLight *) node;
1426
1427#if 1 // Let's place the light to its place in scene graph instead of
1428      // old approach of global light group.
1429        SbVec3f l(dirLight->direction.getValue());
1430        osgLight->setPosition(osg::Vec4(-l[0], -l[1], -l[2] , 0.));
1431#else
1432        osg::Vec3 transVec;
1433        thisPtr->transformLight(action, dirLight->direction.getValue(), transVec);
1434        osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(),
1435                                        transVec.z(), 0.));
1436#endif
1437    }
1438    else if (node->isOfType(SoPointLight::getClassTypeId()))
1439    {
1440        SoPointLight* ptLight = (SoPointLight *) node;
1441
1442#if 1 // Let's place the light to its place in scene graph instead of
1443      // old approach of global light group.
1444        SbVec3f l(ptLight->location.getValue());
1445        osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 1.));
1446#else
1447        osg::Vec3 transVec;
1448        thisPtr->transformLight(action, ptLight->location.getValue(), transVec);
1449        osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(),
1450                                        transVec.z(), 1.));
1451#endif
1452    }
1453    else if (node->isOfType(SoSpotLight::getClassTypeId()))
1454    {
1455        SoSpotLight* spotLight = (SoSpotLight *) node;
1456
1457        osgLight->setSpotExponent(spotLight->dropOffRate.getValue() * 128.0);
1458        osgLight->setSpotCutoff(spotLight->cutOffAngle.getValue()*180.0/osg::PI);
1459
1460#if 1 // Let's place the light to its place in scene graph instead of
1461      // old approach of global light group.
1462        SbVec3f l(spotLight->location.getValue());
1463        osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 1.));
1464        l = spotLight->direction.getValue();
1465        osgLight->setDirection(osg::Vec3(l[0], l[1], l[2]));
1466#else
1467        osg::Vec3 transVec;
1468        thisPtr->transformLight(action, spotLight->location.getValue(), transVec);
1469        osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(),
1470                                        transVec.z(), 1.));
1471
1472        thisPtr->transformLight(action, spotLight->direction.getValue(),transVec);
1473        osgLight->setDirection(osg::Vec3(transVec.x(), transVec.y(),
1474                                         transVec.z()));
1475#endif
1476    }
1477
1478    // Attenuation
1479    if (!node->isOfType(SoDirectionalLight::getClassTypeId())) {
1480        SbVec3f att = action->getLightAttenuation();
1481        osgLight->setConstantAttenuation(att[2]);
1482        osgLight->setLinearAttenuation(att[1]);
1483        osgLight->setQuadraticAttenuation(att[0]);
1484    } else {
1485        // keep default light settings for directional light, e.g.
1486        // no attenuation
1487    }
1488
1489    // Append the light into the scene and onto the state stack
1490    osgLight->setLightNum(ivState.currentLights.size());
1491    ivState.currentLights.push_back(osgLight);
1492
1493    // Create LightSource
1494    osg::ref_ptr<osg::LightSource> ls = new osg::LightSource();
1495    ls->setLight(osgLight.get());
1496
1497    // Set object names
1498    const char* name = ivLight->getName().getString();
1499    osgLight->setName(name);
1500    //ls->setName(name); -> this will be handled bellow in ivPushState
1501
1502#if 1 // Let's place the light to its place in scene graph instead of
1503      // old approach of global light group.
1504    thisPtr->ivPushState(action, node,
1505              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
1506              IvStateItem::APPEND_AT_PUSH, ls.get());
1507#else
1508    if (!(thisPtr->lightGroup.get()))
1509        thisPtr->lightGroup = new osg::Group();
1510    thisPtr->lightGroup->addChild(ls);
1511#endif
1512
1513    return SoCallbackAction::CONTINUE;
1514}
1515///////////////////////////////////////////////////////////////////
1516SoCallbackAction::Response
1517ConvertFromInventor::preEnvironment(void* data, SoCallbackAction* action,
1518                                    const SoNode* node)
1519{
1520#ifdef DEBUG_IV_PLUGIN
1521    OSG_DEBUG << NOTIFY_HEADER << "preEnvironment()   "
1522              << node->getTypeId().getName().getString() << std::endl;
1523#endif
1524
1525    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1526    IvStateItem &ivState = thisPtr->ivStateStack.top();
1527
1528    ivState.currentAmbientLight = ((SoEnvironment*)node)->ambientColor.getValue() *
1529                                  ((SoEnvironment*)node)->ambientIntensity.getValue();
1530
1531    return SoCallbackAction::CONTINUE;
1532}
1533///////////////////////////////////////////////////////////////////
1534#ifdef INVENTOR_SHADERS_AVAILABLE
1535static bool
1536convertShader(osg::Shader::Type osgShaderType,
1537              const SoShaderObject *ivShader,
1538              osg::Program *osgProgram)
1539{
1540    // NULL shader is not converted while returning success
1541    if (ivShader == NULL)
1542        return true;
1543
1544    // Create shader
1545    osg::ref_ptr<osg::Shader> osgShader = new osg::Shader(osgShaderType);
1546    if (ivShader->sourceType.getValue() == SoShaderObject::FILENAME)
1547        osgShader->loadShaderSourceFromFile(ivShader->sourceProgram.getValue().getString());
1548    else
1549    if (ivShader->sourceType.getValue() == SoShaderObject::GLSL_PROGRAM)
1550        osgShader->setShaderSource(ivShader->sourceProgram.getValue().getString());
1551    else {
1552        OSG_WARN << NOTIFY_HEADER << "Can not convert "
1553                  << "shader. Unsupported shader language." << std::endl;
1554        return false;
1555    }
1556
1557    // Set shader name
1558    osgShader->setName(ivShader->getName().getString());
1559
1560    return osgProgram->addShader(osgShader.get());
1561}
1562#endif // INVENTOR_SHADERS_AVAILABLE
1563///////////////////////////////////////////////////////////////////
1564SoCallbackAction::Response
1565ConvertFromInventor::preShaderProgram(void* data, SoCallbackAction* action,
1566                              const SoNode* node)
1567{
1568#ifdef DEBUG_IV_PLUGIN
1569    OSG_DEBUG << NOTIFY_HEADER << "preShaderProgram()  "
1570              << node->getTypeId().getName().getString() << std::endl;
1571#endif
1572
1573#ifdef INVENTOR_SHADERS_AVAILABLE
1574
1575    ConvertFromInventor *thisPtr = (ConvertFromInventor*)data;
1576    IvStateItem &ivState = thisPtr->ivStateStack.top();
1577
1578    // Get Inventor nodes
1579    // Note: Shaders are available since Coin 2.5 (including
1580    // geometry shader)
1581    const SoShaderProgram *ivProgram = (const SoShaderProgram*)node;
1582    const SoVertexShader *ivVertexShader = NULL;
1583    const SoGeometryShader *ivGeometryShader = NULL;
1584    const SoFragmentShader *ivFragmentShader = NULL;
1585
1586    for (int i=0, c=ivProgram->shaderObject.getNum(); i<c; i++) {
1587
1588        const SoShaderObject *shader = (const SoShaderObject*)ivProgram->shaderObject[i];
1589        if (!shader->isOfType(SoShaderObject::getClassTypeId()))
1590            continue;
1591        if (shader->isActive.getValue() == FALSE)
1592            continue;
1593
1594        if (shader->isOfType(SoVertexShader::getClassTypeId()))
1595            ivVertexShader = (const SoVertexShader*)shader;
1596        if (shader->isOfType(SoGeometryShader::getClassTypeId()))
1597            ivGeometryShader = (const SoGeometryShader*)shader;
1598        if (shader->isOfType(SoFragmentShader::getClassTypeId()))
1599            ivFragmentShader = (const SoFragmentShader*)shader;
1600    }
1601
1602    // Create OSG shader program
1603    osg::Program *osgProgram = new osg::Program();
1604    if (!convertShader(osg::Shader::VERTEX, ivVertexShader, osgProgram))
1605        OSG_WARN << NOTIFY_HEADER
1606                  << "Failed to add vertex shader." << std::endl;
1607    if (!convertShader(osg::Shader::GEOMETRY, ivGeometryShader, osgProgram))
1608        OSG_WARN << NOTIFY_HEADER
1609                  << "Failed to add geometry shader." << std::endl;
1610    if (!convertShader(osg::Shader::FRAGMENT, ivFragmentShader, osgProgram))
1611        OSG_WARN << NOTIFY_HEADER
1612                  << "Failed to add fragment shader." << std::endl;
1613
1614    // Set program name
1615    osgProgram->setName(ivProgram->getName().getString());
1616
1617    // Put shader to the state stack
1618    ivState.currentGLProgram = osgProgram;
1619
1620#else
1621
1622    OSG_WARN << NOTIFY_HEADER << "Warning: The model "
1623              "contains shaders while your Inventor does not support "
1624              "them." << std::endl;
1625#endif
1626
1627    return SoCallbackAction::CONTINUE;
1628}
1629///////////////////////////////////////////////////////////////////////////////////////
1630osg::ref_ptr<osg::StateSet>
1631ConvertFromInventor::getStateSet(SoCallbackAction* action)
1632{
1633    osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet;
1634
1635    // Inherit modes from the global state
1636    stateSet->clear();
1637
1638    // Inventor State Stack
1639    IvStateItem &ivState = ivStateStack.top();
1640
1641    // Convert the IV texture to OSG texture if any
1642    osg::ref_ptr<osg::Texture2D> texture;
1643    const SoNode *ivTexture = ivState.currentTexture;
1644    if (ivTexture)
1645    {
1646        // Found a corresponding OSG texture object
1647        if (ivToOsgTexMap[ivTexture])
1648            texture = ivToOsgTexMap[ivTexture];
1649        else
1650        {
1651            // Create a new osg texture
1652            texture = convertIVTexToOSGTex(ivTexture, action);
1653
1654            // Add the new texture to the database
1655            ivToOsgTexMap[ivTexture] = texture.get();
1656        }
1657
1658        stateSet->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);
1659
1660        // propogate name
1661        if(texture.valid())
1662        {
1663            std::string name = texture->getName();
1664            if (name != "")
1665                stateSet->setName(name);
1666        }
1667        // Set the texture environment
1668        osg::ref_ptr<osg::TexEnv> texEnv = new osg::TexEnv;
1669        switch (action->getTextureModel())
1670        {
1671            case SoTexture2::MODULATE:
1672                texEnv->setMode(osg::TexEnv::MODULATE);
1673                break;
1674            case SoTexture2::DECAL:
1675                texEnv->setMode(osg::TexEnv::DECAL);
1676                break;
1677            case SoTexture2::BLEND: {
1678                texEnv->setMode(osg::TexEnv::BLEND);
1679                SbColor c(action->getTextureBlendColor());
1680                texEnv->setColor(osg::Vec4(c[0], c[1], c[2], 1.f));
1681                break;
1682            }
1683            // SGI's Inventor does not have REPLACE mode, but the Coin 3D library does.
1684            // Coin supports REPLACE since 2.2 release, TGS Inventor from 4.0.
1685            // Let's convert to the TexEnv anyway.
1686            case 0x1E01: //SoTexture2::REPLACE:
1687                texEnv->setMode(osg::TexEnv::REPLACE);
1688                break;
1689            default:
1690                OSG_WARN << "Unsupported TexEnv mode." << std::endl;
1691                break;
1692
1693        }
1694        stateSet->setTextureAttributeAndModes(0,texEnv.get(),osg::StateAttribute::ON);
1695    }
1696
1697    SbColor ambient, diffuse, specular, emission;
1698    float shininess, transparency;
1699
1700    // Get the material colors
1701    action->getMaterial(ambient, diffuse, specular, emission,
1702                shininess, transparency, 0);
1703
1704    // Set transparency
1705    SbBool hasTextureTransparency = FALSE;
1706    if (ivTexture) {
1707      SbVec2s size(0, 0);
1708      int bpp = 0;
1709      const unsigned char *data = NULL;
1710      if (ivTexture->isOfType(SoTexture2::getClassTypeId()))
1711        data = ((SoTexture2*)ivTexture)->image.getValue(size, bpp);
1712#ifdef __COIN__
1713      else
1714      if (ivTexture->isOfType(SoVRMLImageTexture::getClassTypeId())) {
1715        const SbImage *img = ((SoVRMLImageTexture*)ivTexture)->getImage();
1716        if (img)
1717          data = img->getValue(size, bpp);
1718      }
1719#endif
1720
1721      // look whether texture really contains transparency
1722      if ((bpp==4 || bpp==2) && data) {
1723        data += bpp - 1;
1724        for (int y=0; y<size[1]; y++)
1725          for (int x=0; x<size[0]; x++, data += bpp)
1726            if (*data != 255) {
1727              hasTextureTransparency = TRUE;
1728              goto finished;
1729            }
1730      finished:;
1731      }
1732    }
1733
1734    if (transparency > 0 || hasTextureTransparency)
1735    {
1736        // Blending to SRC_APLHA and ONE_MINUS_SRC_ALPHA
1737        stateSet->setAttributeAndModes(new osg::BlendFunc);
1738
1739        // Disable depth writes
1740        stateSet->setAttributeAndModes(new osg::Depth(osg::Depth::LEQUAL, 0., 1., false));
1741
1742        // Enable depth sorting for transparent objects
1743        stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1744        stateSet->setNestRenderBins(false);
1745    }
1746
1747    // Set linewidth
1748    if (action->getLineWidth())
1749    {
1750        osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth;
1751        lineWidth->setWidth(action->getLineWidth());
1752        stateSet->setAttributeAndModes(lineWidth.get(), osg::StateAttribute::ON);
1753    }
1754
1755    // Set pointsize
1756    if (action->getPointSize())
1757    {
1758        osg::ref_ptr<osg::Point> point = new osg::Point;
1759        point->setSize(action->getPointSize());
1760        stateSet->setAttributeAndModes(point.get(), osg::StateAttribute::ON);
1761    }
1762
1763    // Set draw mode
1764    switch (action->getDrawStyle())
1765    {
1766        case SoDrawStyle::FILLED:
1767        {
1768#if 0
1769// OSG defaults to filled draw style, so no need to set redundent state.
1770            osg::PolygonMode *polygonMode = new osg::PolygonMode;
1771            polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
1772                                 osg::PolygonMode::FILL);
1773            stateSet->setAttributeAndModes(polygonMode, osg::StateAttribute::ON);
1774#endif
1775            break;
1776        }
1777        case SoDrawStyle::LINES:
1778        {
1779            osg::ref_ptr<osg::PolygonMode> polygonMode = new osg::PolygonMode;
1780            polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
1781                                 osg::PolygonMode::LINE);
1782            stateSet->setAttributeAndModes(polygonMode.get(), osg::StateAttribute::ON);
1783            break;
1784        }
1785        case SoDrawStyle::POINTS:
1786        {
1787            osg::ref_ptr<osg::PolygonMode> polygonMode = new osg::PolygonMode;
1788            polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
1789                                 osg::PolygonMode::POINT);
1790            stateSet->setAttributeAndModes(polygonMode.get(), osg::StateAttribute::ON);
1791            break;
1792        }
1793        case SoDrawStyle::INVISIBLE:
1794            // check how to handle this in osg.
1795            break;
1796    }
1797
1798    // Set back face culling
1799    if (action->getShapeType() == SoShapeHints::SOLID)
1800    {
1801        osg::ref_ptr<osg::CullFace> cullFace = new osg::CullFace;
1802        cullFace->setMode(osg::CullFace::BACK);
1803        stateSet->setAttributeAndModes(cullFace.get(), osg::StateAttribute::ON);
1804    }
1805
1806    // Set lighting
1807    if (action->getLightModel() == SoLightModel::BASE_COLOR)
1808        stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
1809    else
1810    {
1811        // Set the material
1812        osg::ref_ptr<osg::Material> material = new osg::Material;
1813
1814        material->setAmbient(osg::Material::FRONT_AND_BACK,
1815                             osg::Vec4(ambient[0], ambient[1], ambient[2],
1816                                       1.0 - transparency));
1817        material->setDiffuse(osg::Material::FRONT_AND_BACK,
1818                             osg::Vec4(diffuse[0], diffuse[1], diffuse[2],
1819                                       1.0 - transparency));
1820        material->setSpecular(osg::Material::FRONT_AND_BACK,
1821                              osg::Vec4(specular[0], specular[1], specular[2],
1822                                        1.0 - transparency));
1823        material->setEmission(osg::Material::FRONT_AND_BACK,
1824                              osg::Vec4(emission[0], emission[1], emission[2],
1825                                        1.0 - transparency));
1826        material->setTransparency(osg::Material::FRONT_AND_BACK, transparency);
1827        if (specular[0] || specular[1] || specular[2])
1828            material->setShininess(osg::Material::FRONT_AND_BACK,
1829                                   shininess*128.0);
1830        else
1831            material->setShininess(osg::Material::FRONT_AND_BACK, 0.0);
1832
1833        material->setColorMode(osg::Material::DIFFUSE);
1834
1835        stateSet->setAttributeAndModes(material.get(), osg::StateAttribute::ON);
1836        stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
1837
1838        // Set global ambient light
1839        // note on osg::LightModel default values:
1840        //   colorControl: SINGLE_COLOR, localViewer: false, twoSided: false
1841        osg::LightModel *lightModel = new osg::LightModel();
1842        const SbColor &c = ivState.currentAmbientLight;
1843        lightModel->setAmbientIntensity(osg::Vec4(c[0], c[1], c[2], 1.0));
1844#if 0
1845// disable as two sided lighting causes problem under NVidia, and the above osg::Material settings are single sided anway..
1846update: The mentioned bug is probably just for very old NVidia drivers (commit time of the comment is 2005-03-18).
1847        The proper solution should be to set two sided lighting based on SoShapeHints node. Need to be developed. PCJohn-2010-01-20
1848        // Set two sided lighting
1849        lightModel->setTwoSided(true);
1850#endif
1851        stateSet->setAttributeAndModes(lightModel, osg::StateAttribute::ON);
1852
1853        // Set lights
1854        for (unsigned int i = 0; i < ivState.currentLights.size(); i++)
1855            stateSet->setAttributeAndModes(ivState.currentLights[i].get(),
1856                                           osg::StateAttribute::ON);
1857
1858    }
1859
1860    // Shader program setup
1861    if (ivState.currentGLProgram.get() != NULL) {
1862        stateSet->setAttributeAndModes(ivState.currentGLProgram.get(),
1863                                       osg::StateAttribute::ON);
1864    }
1865
1866    // Shader program uniforms
1867    if (ivState.currentGLProgram.get() != NULL) {
1868        for (int i=0, c=ivState.currentGLProgram->getNumShaders(); i<c; i++) {
1869             const std::string &shaderCode = ivState.currentGLProgram->getShader(i)->getShaderSource();
1870             if (shaderCode.find("coin_texunit0_model") != std::string::npos) {
1871                 int mode = (ivTexture!=NULL) ? action->getTextureModel() : 0;
1872                 stateSet->addUniform(new osg::Uniform("coin_texunit0_model", mode));
1873                 break;
1874             }
1875        }
1876    }
1877
1878    return stateSet;
1879}
1880////////////////////////////////////////////////////////////////////
1881osg::Texture2D*
1882ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode,
1883                                          SoCallbackAction* action)
1884{
1885#ifdef DEBUG_IV_PLUGIN
1886    OSG_DEBUG << NOTIFY_HEADER
1887              << "convertIVTexToOSGTex ("
1888              << soNode->getTypeId().getName().getString()
1889              << ")" << std::endl;
1890#endif
1891
1892    SbVec2s soSize;
1893    int soNC;
1894
1895    // Get the texture size and components
1896    const unsigned char* soImageData = action->getTextureImage(soSize, soNC);
1897    if (!soImageData) {
1898        OSG_WARN << NOTIFY_HEADER
1899                  << "Warning: Error while loading texture data." << std::endl;
1900        return NULL;
1901    }
1902
1903    // Allocate memory for image data
1904    unsigned char* osgImageData = new unsigned char[soSize[0] * soSize[1] * soNC];
1905
1906    // Copy the texture image data from the inventor texture
1907    memcpy(osgImageData, soImageData, soSize[0] * soSize[1] * soNC);
1908
1909    // File name
1910    std::string fileName;
1911    if (soNode->isOfType(SoTexture2::getClassTypeId()))
1912        fileName = ((SoTexture2*)soNode)->filename.getValue().getString();
1913#ifdef __COIN__
1914    else
1915    if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId()))
1916        fileName = ((SoVRMLImageTexture*)soNode)->url.getNum() >= 1 ?
1917                   ((SoVRMLImageTexture*)soNode)->url.getValues(0)[0].getString() : "";
1918#endif
1919    else
1920      OSG_WARN << NOTIFY_HEADER
1921                << " Warning: Unsupported texture type: "
1922                << soNode->getTypeId().getName().getString() << std::endl;
1923
1924#ifdef DEBUG_IV_PLUGIN
1925    OSG_DEBUG << NOTIFY_HEADER
1926              << "  Converting file name: " << fileName << " -> ";
1927#endif
1928    if (fileName[0]=='\"') fileName.erase(fileName.begin());
1929    if (fileName.size() > 0 && fileName[fileName.size()-1]=='\"')
1930        fileName.erase(fileName.begin()+fileName.size()-1);
1931#ifdef DEBUG_IV_PLUGIN
1932    OSG_DEBUG << fileName << std::endl;
1933#endif
1934
1935    // Create the osg::Image
1936    osg::ref_ptr<osg::Image> osgImage = new osg::Image;
1937    osgImage->setFileName(fileName);
1938    GLenum formats[] = {GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA};
1939    osgImage->setImage(soSize[0], soSize[1], 1, soNC, formats[soNC-1],
1940                       GL_UNSIGNED_BYTE, osgImageData, osg::Image::USE_NEW_DELETE);
1941
1942    // Create the osg::Texture2D
1943    osg::Texture2D *osgTex = new osg::Texture2D;
1944    osgTex->setImage(osgImage.get());
1945
1946    // Set name
1947    osgTex->setName(soNode->getName().getString());
1948
1949    static std::map<SoTexture2::Wrap, osg::Texture2D::WrapMode> texWrapMap;
1950    static bool firstTime = true;
1951    if (firstTime)
1952    {
1953        texWrapMap[SoTexture2::CLAMP] = osg::Texture2D::CLAMP;
1954        texWrapMap[SoTexture2::REPEAT] = osg::Texture2D::REPEAT;
1955        firstTime = false;
1956    }
1957
1958    // Set texture wrap mode
1959#ifdef __COIN__
1960    if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId())) {
1961        // It looks like there is a high probability of bug in Coin (investigated on version 2.4.6).
1962        // action->getTextureWrap() returns correct value on SoTexture2 (SoTexture2::CLAMP = 0x2900,
1963        // and REPEAT = 0x2901), but SoVRMLImageTexture returns incorrect value of
1964        // SoGLImage::REPEAT = 0, CLAMP = 1, CLAMP_TO_EDGE = 2).
1965        // So, let's not use action and try to get correct value directly from texture node.
1966        // PCJohn-2007-04-22
1967        osgTex->setWrap(osg::Texture2D::WRAP_S, ((SoVRMLImageTexture*)soNode)->repeatS.getValue() ?
1968            osg::Texture2D::REPEAT : osg::Texture2D::CLAMP_TO_EDGE);
1969        osgTex->setWrap(osg::Texture2D::WRAP_T, ((SoVRMLImageTexture*)soNode)->repeatT.getValue() ?
1970            osg::Texture2D::REPEAT : osg::Texture2D::CLAMP_TO_EDGE);
1971    }
1972    else
1973#endif
1974    {
1975        // Proper way to determine wrap mode
1976        osgTex->setWrap(osg::Texture2D::WRAP_S, texWrapMap[action->getTextureWrapS()]);
1977        osgTex->setWrap(osg::Texture2D::WRAP_T, texWrapMap[action->getTextureWrapT()]);
1978    }
1979
1980    return osgTex;
1981}
1982///////////////////////////////////////////////////////////////////
1983SoCallbackAction::Response
1984ConvertFromInventor::preInfo(void* data, SoCallbackAction* action,
1985                             const SoNode* node)
1986{
1987#ifdef DEBUG_IV_PLUGIN
1988    OSG_DEBUG << NOTIFY_HEADER << "preInfo()    "
1989              << node->getTypeId().getName().getString() << std::endl;
1990#endif
1991
1992#if 0 // FIXME: Not handled properly yet. There is no Info node in OSG.
1993      // Append probably empty Node and set its name to info->string.getValue();
1994    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1995    SoInfo* info = (SoInfo*)node;
1996#endif
1997
1998    return SoCallbackAction::CONTINUE;
1999}
2000/////////////////////////////////////////////////////////////
2001SoCallbackAction::Response
2002ConvertFromInventor::preRotor(void *data, SoCallbackAction *action,
2003                              const SoNode *node)
2004{
2005#ifdef DEBUG_IV_PLUGIN
2006    OSG_DEBUG << NOTIFY_HEADER << "preRotor()  "
2007              << node->getTypeId().getName().getString() << std::endl;
2008#endif
2009
2010    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2011
2012    // Get the parameters for the inventor Rotor
2013    SoRotor *ivRotor = (SoRotor *) node;
2014    SbVec3f ivAxis;
2015    float angle;
2016    ivRotor->rotation.getValue(ivAxis, angle);
2017
2018    // Create a new osg::MatrixTransform
2019    osg::ref_ptr<osg::MatrixTransform> rotorTransform = new osg::MatrixTransform;
2020
2021    // Create a Rotor Callback equivalent to the inventor Rotor
2022    osg::Vec3 pivot(0, 0, 0);
2023    osg::Vec3 axis(ivAxis[0], ivAxis[1], ivAxis[2]);
2024    osg::ref_ptr<osgUtil::TransformCallback> rotorCallback
2025        = new osgUtil::TransformCallback(pivot, axis,
2026                                         2 * osg::PI * ivRotor->speed.getValue());
2027
2028    // Set the app callback
2029    rotorTransform->setUpdateCallback(rotorCallback.get());
2030
2031    // Push the rotor onto the state stack
2032    thisPtr->ivPushState(action, node,
2033              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
2034              IvStateItem::APPEND_AT_PUSH, rotorTransform.get());
2035
2036    // Append initial rotation to the model matrix
2037    if (!ivRotor->rotation.isIgnored()) {
2038        SoModelMatrixElement::rotateBy(action->getState(), ivRotor,
2039                                       ivRotor->rotation.getValue());
2040    }
2041
2042    // Don't do the traversal of the SoShuttle
2043    // since it was seen on Coin that is does not append just
2044    // initial shuttle position, but some interpolated one,
2045    // resulting in incorrect animation.
2046    return SoCallbackAction::PRUNE;
2047}
2048////////////////////////////////////////////////////////////////
2049SoCallbackAction::Response
2050ConvertFromInventor::prePendulum(void* data, SoCallbackAction *action,
2051                                 const SoNode* node)
2052{
2053#ifdef DEBUG_IV_PLUGIN
2054    OSG_DEBUG << NOTIFY_HEADER << "prePendulum()  "
2055              << node->getTypeId().getName().getString() << std::endl;
2056#endif
2057
2058    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2059
2060    // Get the parameters for the inventor Pendulum
2061    SoPendulum *ivPendulum = (SoPendulum *) node;
2062    SbVec3f ivAxis0, ivAxis1;
2063    float startAngle, endAngle;
2064    ivPendulum->rotation0.getValue(ivAxis0, startAngle);
2065    ivPendulum->rotation1.getValue(ivAxis1, endAngle);
2066    ivAxis0.normalize();
2067    ivAxis1.normalize();
2068
2069    // Reverse axis and direction if required
2070    // Actually, this will produce correct results only when axis is
2071    // opposite to each other, and approximate results when nearly
2072    // opposite and garbage otherwise.
2073    if ((ivAxis0+ivAxis1).length() < 0.5 ) {
2074        ivAxis1 = -ivAxis1;
2075        endAngle = -endAngle;
2076    }
2077
2078    // Create a new osg::MatrixTransform
2079    osg::ref_ptr<osg::MatrixTransform> pendulumTransform = new osg::MatrixTransform;
2080
2081    // Create a Pendulum Callback equivalent to the inventor Rotor
2082    // Use axis from of the bigger angle (to avoid lost axis when
2083    // angle is zero - see SbRotation and quaternion theory).
2084    osg::Vec3 axis;
2085    if (fabs(startAngle) > fabs(endAngle))
2086        axis = osg::Vec3(ivAxis0[0], ivAxis0[1], ivAxis0[2]);
2087    else
2088        axis = osg::Vec3(ivAxis1[0], ivAxis1[1], ivAxis1[2]);
2089    PendulumCallback* pendulumCallback
2090        = new PendulumCallback(axis, startAngle, endAngle,
2091                               ivPendulum->speed.getValue());
2092
2093    // Set the app callback
2094    pendulumTransform->setUpdateCallback(pendulumCallback);
2095
2096    // Push the pendulum onto the state stack
2097    thisPtr->ivPushState(action, node,
2098              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
2099              IvStateItem::APPEND_AT_PUSH, pendulumTransform.get());
2100
2101    // Don't do the traversal of the SoShuttle
2102    // since it was seen on Coin that is does not append just
2103    // initial shuttle position, but some interpolated one,
2104    // resulting in incorrect animation.
2105    return SoCallbackAction::PRUNE;
2106}
2107////////////////////////////////////////////////////////////////
2108SoCallbackAction::Response
2109ConvertFromInventor::preShuttle(void* data, SoCallbackAction *action,
2110                                const SoNode* node)
2111{
2112#ifdef DEBUG_IV_PLUGIN
2113    OSG_DEBUG << NOTIFY_HEADER << "preShuttle()  "
2114              << node->getTypeId().getName().getString() << std::endl;
2115#endif
2116
2117    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2118
2119    // Get the parameters for the inventor Shuttle
2120    SoShuttle *ivShuttle = (SoShuttle *) node;
2121    SbVec3f ivStartPos, ivEndPos;
2122    ivStartPos = ivShuttle->translation0.getValue();
2123    ivEndPos = ivShuttle->translation1.getValue();
2124
2125    // Create a new osg::MatrixTransform
2126    osg::ref_ptr<osg::MatrixTransform> shuttleTransform = new osg::MatrixTransform;
2127
2128    // Create a shuttle Callback equivalent to the inventor Rotor
2129    osg::Vec3 startPos(ivStartPos[0], ivStartPos[1], ivStartPos[2]);
2130    osg::Vec3 endPos(ivEndPos[0], ivEndPos[1], ivEndPos[2]);
2131    ShuttleCallback* shuttleCallback
2132        = new ShuttleCallback(startPos, endPos, ivShuttle->speed.getValue());
2133
2134    // Set the app callback
2135    shuttleTransform->setUpdateCallback(shuttleCallback);
2136
2137    // Push the shuttle onto the state stack
2138    thisPtr->ivPushState(action, node,
2139              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
2140              IvStateItem::APPEND_AT_PUSH, shuttleTransform.get());
2141
2142    // Don't do the traversal of the SoShuttle
2143    // since it was seen on Coin that is does not append just
2144    // initial shuttle position, but some interpolated one,
2145    // resulting in incorrect animation.
2146    return SoCallbackAction::PRUNE;
2147}
2148////////////////////////////////////////////////////////////
2149void ConvertFromInventor::addVertex(SoCallbackAction* action,
2150                                    const SoPrimitiveVertex *v,
2151                                    int index)
2152{
2153    // Get the coordinates of the vertex
2154    SbVec3f pt = v->getPoint();
2155    vertices.push_back(osg::Vec3(pt[0], pt[1], pt[2]));
2156
2157    // Get the normal of the vertex
2158    SbVec3f norm = v->getNormal();
2159
2160    if ((normalBinding == deprecated_osg::Geometry::BIND_PER_VERTEX) ||
2161        (normalBinding == deprecated_osg::Geometry::BIND_PER_PRIMITIVE && index == 0))
2162    {
2163        // What is this? Why to invert normals at CLOCKWISE vertex ordering?
2164        // PCJohn 2009-12-13
2165        //if (vertexOrder == CLOCKWISE)
2166        //    normals.push_back(osg::Vec3(-norm[0], -norm[1], -norm[2]));
2167        //else
2168            normals.push_back(osg::Vec3(norm[0], norm[1], norm[2]));
2169    }
2170
2171    if (colorBinding == deprecated_osg::Geometry::BIND_PER_VERTEX ||
2172            colorBinding == deprecated_osg::Geometry::BIND_PER_PRIMITIVE)
2173    {
2174        // Get the material/color
2175        SbColor ambient, diffuse, specular, emission;
2176        float transparency, shininess;
2177        action->getMaterial(ambient, diffuse, specular, emission, shininess,
2178                            transparency, v->getMaterialIndex());
2179        if (colorBinding == deprecated_osg::Geometry::BIND_PER_VERTEX)
2180            colors.push_back(osg::Vec4(diffuse[0], diffuse[1], diffuse[2],
2181                                       1.0 - transparency));
2182        else if (colorBinding == deprecated_osg::Geometry::BIND_PER_PRIMITIVE && index == 0)
2183            colors.push_back(osg::Vec4(diffuse[0], diffuse[1], diffuse[2],
2184                                       1.0 - transparency));
2185    }
2186
2187    // Get the texture coordinates
2188    SbVec4f texCoord = v->getTextureCoords();
2189    textureCoords.push_back(osg::Vec2(texCoord[0], texCoord[1]));
2190}
2191////////////////////////////////////////////////////////////////////////////
2192void ConvertFromInventor::addTriangleCB(void* data, SoCallbackAction* action,
2193                                        const SoPrimitiveVertex* v0,
2194                                        const SoPrimitiveVertex* v1,
2195                                        const SoPrimitiveVertex* v2)
2196{
2197    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2198
2199    switch (thisPtr->vertexOrder)
2200    {
2201        case CLOCKWISE:
2202            thisPtr->addVertex(action, v0, 0);
2203            thisPtr->addVertex(action, v2, 1);
2204            thisPtr->addVertex(action, v1, 2);
2205            break;
2206        case COUNTER_CLOCKWISE:
2207            thisPtr->addVertex(action, v0, 0);
2208            thisPtr->addVertex(action, v1, 1);
2209            thisPtr->addVertex(action, v2, 2);
2210            break;
2211    }
2212
2213    thisPtr->numPrimitives++;
2214    thisPtr->primitiveType = osg::PrimitiveSet::TRIANGLES;
2215}
2216////////////////////////////////////////////////////////////////////////////////
2217void ConvertFromInventor::addLineSegmentCB(void* data, SoCallbackAction* action,
2218                                           const SoPrimitiveVertex* v0,
2219                                           const SoPrimitiveVertex* v1)
2220{
2221    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2222
2223    thisPtr->addVertex(action, v0, 0);
2224    thisPtr->addVertex(action, v1, 1);
2225
2226    thisPtr->numPrimitives++;
2227    thisPtr->primitiveType = osg::PrimitiveSet::LINES;
2228}
2229//////////////////////////////////////////////////////////////////////////
2230void ConvertFromInventor::addPointCB(void* data, SoCallbackAction* action,
2231                                     const SoPrimitiveVertex* v0)
2232{
2233    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2234
2235    thisPtr->addVertex(action, v0, 0);
2236
2237    thisPtr->numPrimitives++;
2238    thisPtr->primitiveType = osg::PrimitiveSet::POINTS;
2239}
2240//////////////////////////////////////////////////////////////////////////
2241void ConvertFromInventor::init()
2242{
2243#ifdef __COIN__
2244    SoTexture2Osg::overrideClass();
2245    SoTexture3Osg::overrideClass();
2246    SoVRMLImageTextureOsg::overrideClass();
2247#endif
2248}
Note: See TracBrowser for help on using the browser.