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

Revision 13502, 82.4 kB (checked in by robert, 33 hours ago)

Improved support for controlling the ShadingModel? via the VolumeSettings? object

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