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

Revision 12625, 82.0 kB (checked in by robert, 3 years ago)

Warning fixes for:

OpenSceneGraph/src/osgPlugins/Inventor/ConvertFromInventor.cpp: In member function ?virtual SbBool? SoVRMLImageTextureOsg::readInstance(SoInput?*, short unsigned int)?:
OpenSceneGraph/src/osgPlugins/Inventor/ConvertFromInventor.cpp:1264:16: warning: variable ?retval? set but not used [-Wunused-but-set-variable]
OpenSceneGraph/src/osgPlugins/ac/Geode.cpp: In member function ?void ac3d::Geode::ProcessGeometry?(std::ostream&, unsigned int)?:
OpenSceneGraph/src/osgPlugins/ac/Geode.cpp:806:35: warning: variable ?fRep_s? set but not used [-Wunused-but-set-variable]
OpenSceneGraph/src/osgPlugins/ac/Geode.cpp:806:43: warning: variable ?fRep_t? set but not used [-Wunused-but-set-variable]
OpenSceneGraph/src/osgPlugins/ac/Geode.cpp:807:35: warning: variable ?fOffset_s? set but not used [-Wunused-but-set-variable]
OpenSceneGraph/src/osgPlugins/ac/Geode.cpp:807:46: warning: variable ?fOffset_t? set but not used [-Wunused-but-set-variable]
OpenSceneGraph/src/osgViewer/GraphicsWindowX11.cpp: In member function ?virtual void osgViewer::GraphicsWindowX11::checkEvents()?:
OpenSceneGraph/src/osgViewer/GraphicsWindowX11.cpp:1181:10: warning: variable ?destroyWindowRequested? set but not used [-Wunused-but-set-variable]

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