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

Revision 12912, 82.2 kB (checked in by robert, 3 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • 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::ref_ptr<osg::Image> osgImage = osgDB::readImageFile(fileName, options);
1097
1098    if (!osgImage)
1099    {
1100        OSG_WARN << NOTIFY_HEADER << "Could not read texture file '" << fileName << "'.";
1101        return 0;
1102    }
1103
1104    if (!osgImage->isDataContiguous())
1105    {
1106        OSG_WARN << NOTIFY_HEADER << "Inventor cannot handle non contiguous image data found in texture file '" << fileName << "'.";
1107        return 0;
1108    }
1109
1110    return osgImage.release();
1111}
1112
1113SbBool SoTexture2Osg::readInstance(SoInput *in, unsigned short flags)
1114{
1115    // disable notification and read inherited fields
1116    SbBool oldNotify = filename.enableNotify(FALSE);
1117    SbBool readOK = SoNode::readInstance(in, flags);
1118    this->setReadStatus((int) readOK);
1119
1120    // if file name given
1121    if (readOK && !filename.isDefault() && filename.getValue() != "")
1122    {
1123        // create options and read the file
1124        osgDB::ReaderWriter::Options *options = createOptions();
1125        osg::ref_ptr<osg::Image> image = loadImage(filename.getValue().getString(), options);
1126
1127        if (image.valid())
1128        {
1129            // get image dimensions and data
1130            int nc = osg::Image::computeNumComponents(image->getPixelFormat());
1131            SbVec2s size(image->s(), image->t());
1132            unsigned char *bytes = image->data();
1133
1134            // disable notification on image while setting data from filename
1135            // as a notify will cause a filename.setDefault(TRUE)
1136            SbBool oldnotify = this->image.enableNotify(FALSE);
1137            this->image.setValue(size, nc, bytes);
1138            this->image.enableNotify(oldnotify);
1139            // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender()
1140            // We can safely ignore this as we are not going to render the scene graph.
1141        }
1142        else
1143        {
1144            // image loading failed -> set readOK
1145            readOK = FALSE;
1146            this->setReadStatus(FALSE);
1147        }
1148
1149        // write filename, not image
1150        this->image.setDefault(TRUE);
1151    }
1152
1153    filename.enableNotify(oldNotify);
1154    return readOK;
1155}
1156
1157SbBool SoTexture3Osg::readInstance(SoInput *in, unsigned short flags)
1158{
1159    // disable notification and read inherited fields
1160    SbBool oldNotify = filenames.enableNotify(FALSE);
1161    SbBool readOK = SoNode::readInstance(in, flags);
1162    this->setReadStatus((int) readOK);
1163
1164    // if file name given
1165    int numImages = filenames.getNum();
1166    if (readOK && !filenames.isDefault() && numImages > 0)
1167    {
1168        // Fail on empty filenames
1169        SbBool sizeError = FALSE;
1170        SbBool retval = FALSE;
1171        SbVec3s volumeSize(0,0,0);
1172        int volumenc = -1;
1173        int i;
1174        for (i=0; i<numImages; i++)
1175            if (this->filenames[i].getLength()==0) break;
1176
1177        if (i==numImages)
1178        {
1179            // create options
1180            osgDB::ReaderWriter::Options *options = createOptions();
1181
1182            for (int n=0; n<numImages && !sizeError; n++)
1183            {
1184                // read the file
1185                osg::ref_ptr<osg::Image> image = loadImage(filenames[n].getString(), options);
1186
1187                if (!image.valid())
1188                {
1189                    OSG_WARN << NOTIFY_HEADER << "Could not read texture file #" << n << ": "
1190                             << filenames[n].getString() << "\n";
1191                    retval = FALSE;
1192                }
1193                else
1194                {
1195                    // get image dimensions and data
1196                    int nc = osg::Image::computeNumComponents(image->getPixelFormat());
1197                    SbVec3s size(image->s(), image->t(), image->r());
1198                    if (size[2]==0)
1199                        size[2]=1;
1200                    unsigned char *imgbytes = image->data();
1201
1202                    if (this->images.isDefault()) { // First time => allocate memory
1203                        volumeSize.setValue(size[0],
1204                                            size[1],
1205                                            size[2]*numImages);
1206                        volumenc = nc;
1207                        this->images.setValue(volumeSize, nc, NULL);
1208                    }
1209                    else { // Verify size & components
1210                        if (size[0] != volumeSize[0] ||
1211                            size[1] != volumeSize[1] ||
1212                            //FIXME: always 1 or what? (kintel 20020110)
1213                            size[2] != (volumeSize[2]/numImages) ||
1214                            nc != volumenc)
1215                        {
1216                            sizeError = TRUE;
1217                            retval = FALSE;
1218
1219                            OSG_WARN << NOTIFY_HEADER << "Texture file #" << n << " ("
1220                                     << filenames[n].getString() << ") has wrong size: "
1221                                     << "Expected (" << volumeSize[0] << "," << volumeSize[1] << ","
1222                                     << volumeSize[2] << "," << volumenc << ") got ("
1223                                     << size[0] << "," << size[1] << "," << size[2] << "," << nc << ")\n";
1224                        }
1225                    }
1226
1227                    if (!sizeError)
1228                    {
1229                        // disable notification on images while setting data from the
1230                        // filenames as a notify will cause a filenames.setDefault(TRUE).
1231                        SbBool oldnotify = this->images.enableNotify(FALSE);
1232                        unsigned char *volbytes = this->images.startEditing(volumeSize,
1233                                                                            volumenc);
1234                        memcpy(volbytes+int(size[0])*int(size[1])*int(size[2])*nc*n,
1235                               imgbytes, int(size[0])*int(size[1])*int(size[2])*nc);
1236                        this->images.finishEditing();
1237                        this->images.enableNotify(oldnotify);
1238                        // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender()
1239                        // We can safely ignore this as we are not going to render the scene graph.
1240                        retval = TRUE;
1241                    }
1242                }
1243            }
1244        }
1245
1246        if (!retval)
1247        {
1248            // if image loading failed, set read status,
1249            // but not set readOK to false (according to Coin source code)
1250            this->setReadStatus(FALSE);
1251        }
1252
1253        // write filename, not image
1254        this->images.setDefault(TRUE);
1255    }
1256
1257    filenames.enableNotify(oldNotify);
1258    return readOK;
1259}
1260
1261SbBool SoVRMLImageTextureOsg::readInstance(SoInput *in, unsigned short flags)
1262{
1263    // disable notification and read inherited fields
1264    SbBool oldNotify = url.enableNotify(FALSE);
1265    SbBool readOK = SoNode::readInstance(in, flags);
1266    this->setReadStatus((int) readOK);
1267
1268    if (readOK) {
1269
1270        // create options and read the file
1271        osgDB::ReaderWriter::Options *options = createOptions();
1272
1273        if (url.getNum() && url[0].getLength())
1274        {
1275            osg::ref_ptr<osg::Image> image = loadImage(url[0].getString(), options);
1276            if (!image->valid())
1277            {
1278                OSG_WARN << "Could not read texture file: " << url[0].getString() << std::endl;
1279                this->setReadStatus(FALSE);
1280            }
1281            else
1282            {
1283                // get image dimensions and data
1284                int nc = osg::Image::computeNumComponents(image->getPixelFormat());
1285                SbVec2s size(image->s(), image->t());
1286                unsigned char *bytes = image->data();
1287
1288                SbImage ivImage(bytes, size, nc);
1289
1290                // disable notification on image while setting data from filename
1291                // as a notify will cause a filename.setDefault(TRUE)
1292                //SbBool oldnotify = this->image.enableNotify(FALSE); <- difficult to implement for SoVRMLImageTexture
1293                this->setImage(ivImage);
1294                //this->image.enableNotify(oldnotify); <- difficult to implement for SoVRMLImageTexture
1295                // PRIVATE(this)->glimagevalid = FALSE; -> recreate GL image in next GLRender()
1296                // We can safely ignore this as we are not going to render the scene graph.
1297            }
1298        }
1299    }
1300
1301    url.enableNotify(oldNotify);
1302    return readOK;
1303}
1304
1305#endif /* __COIN__ */
1306
1307///////////////////////////////////////////////////////////////
1308SoCallbackAction::Response
1309ConvertFromInventor::postTexture(void* data, SoCallbackAction *,
1310                                 const SoNode* node)
1311{
1312#ifdef DEBUG_IV_PLUGIN
1313    OSG_DEBUG << NOTIFY_HEADER << "postTexture()  "
1314              << node->getTypeId().getName().getString();
1315    if (node->isOfType(SoTexture2::getClassTypeId())) {
1316        SoTexture2 *t = (SoTexture2*)node;
1317        if (t->filename.getValue().getLength())
1318            OSG_DEBUG << "  "  << t->filename.getValue().getString();
1319    }
1320    OSG_DEBUG << std::endl;
1321#endif
1322
1323    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1324    bool texturingEnabled = false;
1325
1326    // Texture2
1327    if (node->isOfType(SoTexture2::getClassTypeId())) {
1328
1329        // Check whether texturing was enabled by the texture node
1330        SoTexture2 *t = (SoTexture2*)node;
1331        SbVec2s size;
1332        int nc;
1333        const unsigned char *data = t->image.getValue(size, nc);
1334        texturingEnabled = t->filename.getValue().getLength() ||
1335                           (data && size != SbVec2s(0,0));
1336    }
1337
1338#ifdef __COIN__
1339
1340    // SoVRMLImageTexture
1341    if (node->isOfType(SoVRMLImageTexture::getClassTypeId())) {
1342
1343        // Check whether texturing was enabled by the texture node
1344        SoVRMLImageTexture *t = (SoVRMLImageTexture*)node;
1345        texturingEnabled = t->url.getNum() > 1 || (t->url.getNum() == 1 && t->url[0].getLength() > 0);
1346    }
1347
1348    // SoVRMLAppearance
1349    if (node->isOfType(SoVRMLAppearance::getClassTypeId())) {
1350
1351        // If SoVRMLAppearance is present and there is no texture
1352        // inside, disable texturing
1353        // FIXME: should SoVRMLAppearance really disable texturing
1354        // when not containing SoVRMLImageTexture? Coin is not doing that,
1355        // but it can be Coin bug.
1356        SoVRMLAppearance *a = (SoVRMLAppearance*)node;
1357        if (a->texture.getValue() == NULL)
1358            thisPtr->ivStateStack.top().currentTexture = NULL;
1359
1360        // Do not try to "optimize" this code by removing the return
1361    // and use the one at the end of the function.
1362    // It would break the case when there is texture inside
1363    // the appearance node.
1364        return SoCallbackAction::CONTINUE;
1365    }
1366
1367#endif /* __COIN__ */
1368
1369    // Set current texture
1370    if (texturingEnabled)
1371        thisPtr->ivStateStack.top().currentTexture = node;
1372    else
1373        thisPtr->ivStateStack.top().currentTexture = NULL;
1374
1375    return SoCallbackAction::CONTINUE;
1376}
1377//////////////////////////////////////////////////////////////////
1378void ConvertFromInventor::transformLight(SoCallbackAction* action,
1379                                         const SbVec3f& vec,
1380                                         osg::Vec3& transVec)
1381{
1382    osg::Matrix modelMat;
1383    modelMat.set((float *)action->getModelMatrix().getValue());
1384
1385    transVec.set(vec[0], vec[1], vec[2]);
1386    transVec = modelMat.preMult(transVec);
1387}
1388///////////////////////////////////////////////////////////////////
1389SoCallbackAction::Response
1390ConvertFromInventor::preLight(void* data, SoCallbackAction* action,
1391                              const SoNode* node)
1392{
1393#ifdef DEBUG_IV_PLUGIN
1394    OSG_DEBUG << NOTIFY_HEADER << "preLight()   "
1395              << node->getTypeId().getName().getString() << std::endl;
1396#endif
1397
1398    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1399
1400    // Return if the light is not on
1401    const SoLight* ivLight = (const SoLight*) node;
1402    if (!ivLight->on.getValue())
1403        return SoCallbackAction::CONTINUE;
1404
1405    // Create new OSG light
1406    IvStateItem &ivState = thisPtr->ivStateStack.top();
1407    osg::ref_ptr<osg::Light> osgLight = new osg::Light;
1408
1409    // Get color and intensity
1410    SbVec3f lightColor = ivLight->color.getValue();
1411    float intensity = ivLight->intensity.getValue();
1412
1413    // Set color and intensity
1414    osgLight->setAmbient(osg::Vec4(0.f, 0.f, 0.f, 1.f));
1415    osgLight->setDiffuse(osg::Vec4(lightColor[0] * intensity,
1416                                   lightColor[1] * intensity,
1417                                   lightColor[2] * intensity, 1));
1418    osgLight->setSpecular(osg::Vec4(lightColor[0] * intensity,
1419                                    lightColor[1] * intensity,
1420                                    lightColor[2] * intensity, 1));
1421
1422    // Light type
1423    if (node->isOfType(SoDirectionalLight::getClassTypeId()))
1424    {
1425        SoDirectionalLight *dirLight = (SoDirectionalLight *) node;
1426
1427#if 1 // Let's place the light to its place in scene graph instead of
1428      // old approach of global light group.
1429        SbVec3f l(dirLight->direction.getValue());
1430        osgLight->setPosition(osg::Vec4(-l[0], -l[1], -l[2] , 0.));
1431#else
1432        osg::Vec3 transVec;
1433        thisPtr->transformLight(action, dirLight->direction.getValue(), transVec);
1434        osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(),
1435                                        transVec.z(), 0.));
1436#endif
1437    }
1438    else if (node->isOfType(SoPointLight::getClassTypeId()))
1439    {
1440        SoPointLight* ptLight = (SoPointLight *) node;
1441
1442#if 1 // Let's place the light to its place in scene graph instead of
1443      // old approach of global light group.
1444        SbVec3f l(ptLight->location.getValue());
1445        osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 1.));
1446#else
1447        osg::Vec3 transVec;
1448        thisPtr->transformLight(action, ptLight->location.getValue(), transVec);
1449        osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(),
1450                                        transVec.z(), 1.));
1451#endif
1452    }
1453    else if (node->isOfType(SoSpotLight::getClassTypeId()))
1454    {
1455        SoSpotLight* spotLight = (SoSpotLight *) node;
1456
1457        osgLight->setSpotExponent(spotLight->dropOffRate.getValue() * 128.0);
1458        osgLight->setSpotCutoff(spotLight->cutOffAngle.getValue()*180.0/osg::PI);
1459
1460#if 1 // Let's place the light to its place in scene graph instead of
1461      // old approach of global light group.
1462        SbVec3f l(spotLight->location.getValue());
1463        osgLight->setPosition(osg::Vec4(l[0], l[1], l[2] , 1.));
1464        l = spotLight->direction.getValue();
1465        osgLight->setDirection(osg::Vec3(l[0], l[1], l[2]));
1466#else
1467        osg::Vec3 transVec;
1468        thisPtr->transformLight(action, spotLight->location.getValue(), transVec);
1469        osgLight->setPosition(osg::Vec4(transVec.x(), transVec.y(),
1470                                        transVec.z(), 1.));
1471
1472        thisPtr->transformLight(action, spotLight->direction.getValue(),transVec);
1473        osgLight->setDirection(osg::Vec3(transVec.x(), transVec.y(),
1474                                         transVec.z()));
1475#endif
1476    }
1477
1478    // Attenuation
1479    if (!node->isOfType(SoDirectionalLight::getClassTypeId())) {
1480        SbVec3f att = action->getLightAttenuation();
1481        osgLight->setConstantAttenuation(att[2]);
1482        osgLight->setLinearAttenuation(att[1]);
1483        osgLight->setQuadraticAttenuation(att[0]);
1484    } else {
1485        // keep default light settings for directional light, e.g.
1486        // no attenuation
1487    }
1488
1489    // Append the light into the scene and onto the state stack
1490    osgLight->setLightNum(ivState.currentLights.size());
1491    ivState.currentLights.push_back(osgLight);
1492
1493    // Create LightSource
1494    osg::ref_ptr<osg::LightSource> ls = new osg::LightSource();
1495    ls->setLight(osgLight.get());
1496
1497    // Set object names
1498    const char* name = ivLight->getName().getString();
1499    osgLight->setName(name);
1500    //ls->setName(name); -> this will be handled bellow in ivPushState
1501
1502#if 1 // Let's place the light to its place in scene graph instead of
1503      // old approach of global light group.
1504    thisPtr->ivPushState(action, node,
1505              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
1506              IvStateItem::APPEND_AT_PUSH, ls.get());
1507#else
1508    if (!(thisPtr->lightGroup.get()))
1509        thisPtr->lightGroup = new osg::Group();
1510    thisPtr->lightGroup->addChild(ls);
1511#endif
1512
1513    return SoCallbackAction::CONTINUE;
1514}
1515///////////////////////////////////////////////////////////////////
1516SoCallbackAction::Response
1517ConvertFromInventor::preEnvironment(void* data, SoCallbackAction* action,
1518                                    const SoNode* node)
1519{
1520#ifdef DEBUG_IV_PLUGIN
1521    OSG_DEBUG << NOTIFY_HEADER << "preEnvironment()   "
1522              << node->getTypeId().getName().getString() << std::endl;
1523#endif
1524
1525    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1526    IvStateItem &ivState = thisPtr->ivStateStack.top();
1527
1528    ivState.currentAmbientLight = ((SoEnvironment*)node)->ambientColor.getValue() *
1529                                  ((SoEnvironment*)node)->ambientIntensity.getValue();
1530
1531    return SoCallbackAction::CONTINUE;
1532}
1533///////////////////////////////////////////////////////////////////
1534#ifdef INVENTOR_SHADERS_AVAILABLE
1535static bool
1536convertShader(osg::Shader::Type osgShaderType,
1537              const SoShaderObject *ivShader,
1538              osg::Program *osgProgram)
1539{
1540    // NULL shader is not converted while returning success
1541    if (ivShader == NULL)
1542        return true;
1543
1544    // Create shader
1545    osg::ref_ptr<osg::Shader> osgShader = new osg::Shader(osgShaderType);
1546    if (ivShader->sourceType.getValue() == SoShaderObject::FILENAME)
1547        osgShader->loadShaderSourceFromFile(ivShader->sourceProgram.getValue().getString());
1548    else
1549    if (ivShader->sourceType.getValue() == SoShaderObject::GLSL_PROGRAM)
1550        osgShader->setShaderSource(ivShader->sourceProgram.getValue().getString());
1551    else {
1552        OSG_WARN << NOTIFY_HEADER << "Can not convert "
1553                  << "shader. Unsupported shader language." << std::endl;
1554        return false;
1555    }
1556
1557    // Set shader name
1558    osgShader->setName(ivShader->getName().getString());
1559
1560    return osgProgram->addShader(osgShader.get());
1561}
1562#endif // INVENTOR_SHADERS_AVAILABLE
1563///////////////////////////////////////////////////////////////////
1564SoCallbackAction::Response
1565ConvertFromInventor::preShaderProgram(void* data, SoCallbackAction* action,
1566                              const SoNode* node)
1567{
1568#ifdef DEBUG_IV_PLUGIN
1569    OSG_DEBUG << NOTIFY_HEADER << "preShaderProgram()  "
1570              << node->getTypeId().getName().getString() << std::endl;
1571#endif
1572
1573#ifdef INVENTOR_SHADERS_AVAILABLE
1574
1575    ConvertFromInventor *thisPtr = (ConvertFromInventor*)data;
1576    IvStateItem &ivState = thisPtr->ivStateStack.top();
1577
1578    // Get Inventor nodes
1579    // Note: Shaders are available since Coin 2.5 (including
1580    // geometry shader)
1581    const SoShaderProgram *ivProgram = (const SoShaderProgram*)node;
1582    const SoVertexShader *ivVertexShader = NULL;
1583    const SoGeometryShader *ivGeometryShader = NULL;
1584    const SoFragmentShader *ivFragmentShader = NULL;
1585
1586    for (int i=0, c=ivProgram->shaderObject.getNum(); i<c; i++) {
1587
1588        const SoShaderObject *shader = (const SoShaderObject*)ivProgram->shaderObject[i];
1589        if (!shader->isOfType(SoShaderObject::getClassTypeId()))
1590            continue;
1591        if (shader->isActive.getValue() == FALSE)
1592            continue;
1593
1594        if (shader->isOfType(SoVertexShader::getClassTypeId()))
1595            ivVertexShader = (const SoVertexShader*)shader;
1596        if (shader->isOfType(SoGeometryShader::getClassTypeId()))
1597            ivGeometryShader = (const SoGeometryShader*)shader;
1598        if (shader->isOfType(SoFragmentShader::getClassTypeId()))
1599            ivFragmentShader = (const SoFragmentShader*)shader;
1600    }
1601
1602    // Create OSG shader program
1603    osg::Program *osgProgram = new osg::Program();
1604    if (!convertShader(osg::Shader::VERTEX, ivVertexShader, osgProgram))
1605        OSG_WARN << NOTIFY_HEADER
1606                  << "Failed to add vertex shader." << std::endl;
1607    if (!convertShader(osg::Shader::GEOMETRY, ivGeometryShader, osgProgram))
1608        OSG_WARN << NOTIFY_HEADER
1609                  << "Failed to add geometry shader." << std::endl;
1610    if (!convertShader(osg::Shader::FRAGMENT, ivFragmentShader, osgProgram))
1611        OSG_WARN << NOTIFY_HEADER
1612                  << "Failed to add fragment shader." << std::endl;
1613
1614    // Set program name
1615    osgProgram->setName(ivProgram->getName().getString());
1616
1617    // Put shader to the state stack
1618    ivState.currentGLProgram = osgProgram;
1619
1620#else
1621
1622    OSG_WARN << NOTIFY_HEADER << "Warning: The model "
1623              "contains shaders while your Inventor does not support "
1624              "them." << std::endl;
1625#endif
1626
1627    return SoCallbackAction::CONTINUE;
1628}
1629///////////////////////////////////////////////////////////////////////////////////////
1630osg::ref_ptr<osg::StateSet>
1631ConvertFromInventor::getStateSet(SoCallbackAction* action)
1632{
1633    osg::ref_ptr<osg::StateSet> stateSet = new osg::StateSet;
1634
1635    // Inherit modes from the global state
1636    stateSet->clear();
1637
1638    // Inventor State Stack
1639    IvStateItem &ivState = ivStateStack.top();
1640
1641    // Convert the IV texture to OSG texture if any
1642    osg::ref_ptr<osg::Texture2D> texture;
1643    const SoNode *ivTexture = ivState.currentTexture;
1644    if (ivTexture)
1645    {
1646        // Found a corresponding OSG texture object
1647        if (ivToOsgTexMap[ivTexture])
1648            texture = ivToOsgTexMap[ivTexture];
1649        else
1650        {
1651            // Create a new osg texture
1652            texture = convertIVTexToOSGTex(ivTexture, action);
1653
1654            // Add the new texture to the database
1655            ivToOsgTexMap[ivTexture] = texture.get();
1656        }
1657
1658        stateSet->setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON);
1659
1660        // propogate name
1661        if(texture.valid())
1662        {
1663            std::string name = texture->getName();
1664            if (name != "")
1665                stateSet->setName(name);
1666        }
1667        // Set the texture environment
1668        osg::ref_ptr<osg::TexEnv> texEnv = new osg::TexEnv;
1669        switch (action->getTextureModel())
1670        {
1671            case SoTexture2::MODULATE:
1672                texEnv->setMode(osg::TexEnv::MODULATE);
1673                break;
1674            case SoTexture2::DECAL:
1675                texEnv->setMode(osg::TexEnv::DECAL);
1676                break;
1677            case SoTexture2::BLEND: {
1678                texEnv->setMode(osg::TexEnv::BLEND);
1679                SbColor c(action->getTextureBlendColor());
1680                texEnv->setColor(osg::Vec4(c[0], c[1], c[2], 1.f));
1681                break;
1682            }
1683            // SGI's Inventor does not have REPLACE mode, but the Coin 3D library does.
1684            // Coin supports REPLACE since 2.2 release, TGS Inventor from 4.0.
1685            // Let's convert to the TexEnv anyway.
1686            case 0x1E01: //SoTexture2::REPLACE:
1687                texEnv->setMode(osg::TexEnv::REPLACE);
1688                break;
1689            default:
1690                OSG_WARN << "Unsupported TexEnv mode." << std::endl;
1691                break;
1692
1693        }
1694        stateSet->setTextureAttributeAndModes(0,texEnv.get(),osg::StateAttribute::ON);
1695    }
1696
1697    SbColor ambient, diffuse, specular, emission;
1698    float shininess, transparency;
1699
1700    // Get the material colors
1701    action->getMaterial(ambient, diffuse, specular, emission,
1702                shininess, transparency, 0);
1703
1704    // Set transparency
1705    SbBool hasTextureTransparency = FALSE;
1706    if (ivTexture) {
1707      SbVec2s size(0, 0);
1708      int bpp = 0;
1709      const unsigned char *data = NULL;
1710      if (ivTexture->isOfType(SoTexture2::getClassTypeId()))
1711        data = ((SoTexture2*)ivTexture)->image.getValue(size, bpp);
1712#ifdef __COIN__
1713      else
1714      if (ivTexture->isOfType(SoVRMLImageTexture::getClassTypeId())) {
1715        const SbImage *img = ((SoVRMLImageTexture*)ivTexture)->getImage();
1716        if (img)
1717          data = img->getValue(size, bpp);
1718      }
1719#endif
1720
1721      // look whether texture really contains transparency
1722      if ((bpp==4 || bpp==2) && data) {
1723        data += bpp - 1;
1724        for (int y=0; y<size[1]; y++)
1725          for (int x=0; x<size[0]; x++, data += bpp)
1726            if (*data != 255) {
1727              hasTextureTransparency = TRUE;
1728              goto finished;
1729            }
1730      finished:;
1731      }
1732    }
1733
1734    if (transparency > 0 || hasTextureTransparency)
1735    {
1736        // Blending to SRC_APLHA and ONE_MINUS_SRC_ALPHA
1737        stateSet->setAttributeAndModes(new osg::BlendFunc);
1738
1739        // Disable depth writes
1740        stateSet->setAttributeAndModes(new osg::Depth(osg::Depth::LEQUAL, 0., 1., false));
1741
1742        // Enable depth sorting for transparent objects
1743        stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
1744        stateSet->setNestRenderBins(false);
1745    }
1746
1747    // Set linewidth
1748    if (action->getLineWidth())
1749    {
1750        osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth;
1751        lineWidth->setWidth(action->getLineWidth());
1752        stateSet->setAttributeAndModes(lineWidth.get(), osg::StateAttribute::ON);
1753    }
1754
1755    // Set pointsize
1756    if (action->getPointSize())
1757    {
1758        osg::ref_ptr<osg::Point> point = new osg::Point;
1759        point->setSize(action->getPointSize());
1760        stateSet->setAttributeAndModes(point.get(), osg::StateAttribute::ON);
1761    }
1762
1763    // Set draw mode
1764    switch (action->getDrawStyle())
1765    {
1766        case SoDrawStyle::FILLED:
1767        {
1768#if 0
1769// OSG defaults to filled draw style, so no need to set redundent state.
1770            osg::PolygonMode *polygonMode = new osg::PolygonMode;
1771            polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
1772                                 osg::PolygonMode::FILL);
1773            stateSet->setAttributeAndModes(polygonMode, osg::StateAttribute::ON);
1774#endif
1775            break;
1776        }
1777        case SoDrawStyle::LINES:
1778        {
1779            osg::ref_ptr<osg::PolygonMode> polygonMode = new osg::PolygonMode;
1780            polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
1781                                 osg::PolygonMode::LINE);
1782            stateSet->setAttributeAndModes(polygonMode.get(), osg::StateAttribute::ON);
1783            break;
1784        }
1785        case SoDrawStyle::POINTS:
1786        {
1787            osg::ref_ptr<osg::PolygonMode> polygonMode = new osg::PolygonMode;
1788            polygonMode->setMode(osg::PolygonMode::FRONT_AND_BACK,
1789                                 osg::PolygonMode::POINT);
1790            stateSet->setAttributeAndModes(polygonMode.get(), osg::StateAttribute::ON);
1791            break;
1792        }
1793        case SoDrawStyle::INVISIBLE:
1794            // check how to handle this in osg.
1795            break;
1796    }
1797
1798    // Set back face culling
1799    if (action->getShapeType() == SoShapeHints::SOLID)
1800    {
1801        osg::ref_ptr<osg::CullFace> cullFace = new osg::CullFace;
1802        cullFace->setMode(osg::CullFace::BACK);
1803        stateSet->setAttributeAndModes(cullFace.get(), osg::StateAttribute::ON);
1804    }
1805
1806    // Set lighting
1807    if (action->getLightModel() == SoLightModel::BASE_COLOR)
1808        stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
1809    else
1810    {
1811        // Set the material
1812        osg::ref_ptr<osg::Material> material = new osg::Material;
1813
1814        material->setAmbient(osg::Material::FRONT_AND_BACK,
1815                             osg::Vec4(ambient[0], ambient[1], ambient[2],
1816                                       1.0 - transparency));
1817        material->setDiffuse(osg::Material::FRONT_AND_BACK,
1818                             osg::Vec4(diffuse[0], diffuse[1], diffuse[2],
1819                                       1.0 - transparency));
1820        material->setSpecular(osg::Material::FRONT_AND_BACK,
1821                              osg::Vec4(specular[0], specular[1], specular[2],
1822                                        1.0 - transparency));
1823        material->setEmission(osg::Material::FRONT_AND_BACK,
1824                              osg::Vec4(emission[0], emission[1], emission[2],
1825                                        1.0 - transparency));
1826        material->setTransparency(osg::Material::FRONT_AND_BACK, transparency);
1827        if (specular[0] || specular[1] || specular[2])
1828            material->setShininess(osg::Material::FRONT_AND_BACK,
1829                                   shininess*128.0);
1830        else
1831            material->setShininess(osg::Material::FRONT_AND_BACK, 0.0);
1832
1833        material->setColorMode(osg::Material::DIFFUSE);
1834
1835        stateSet->setAttributeAndModes(material.get(), osg::StateAttribute::ON);
1836        stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
1837
1838        // Set global ambient light
1839        // note on osg::LightModel default values:
1840        //   colorControl: SINGLE_COLOR, localViewer: false, twoSided: false
1841        osg::LightModel *lightModel = new osg::LightModel();
1842        const SbColor &c = ivState.currentAmbientLight;
1843        lightModel->setAmbientIntensity(osg::Vec4(c[0], c[1], c[2], 1.0));
1844#if 0
1845// disable as two sided lighting causes problem under NVidia, and the above osg::Material settings are single sided anway..
1846update: The mentioned bug is probably just for very old NVidia drivers (commit time of the comment is 2005-03-18).
1847        The proper solution should be to set two sided lighting based on SoShapeHints node. Need to be developed. PCJohn-2010-01-20
1848        // Set two sided lighting
1849        lightModel->setTwoSided(true);
1850#endif
1851        stateSet->setAttributeAndModes(lightModel, osg::StateAttribute::ON);
1852
1853        // Set lights
1854        for (unsigned int i = 0; i < ivState.currentLights.size(); i++)
1855            stateSet->setAttributeAndModes(ivState.currentLights[i].get(),
1856                                           osg::StateAttribute::ON);
1857
1858    }
1859
1860    // Shader program setup
1861    if (ivState.currentGLProgram.get() != NULL) {
1862        stateSet->setAttributeAndModes(ivState.currentGLProgram.get(),
1863                                       osg::StateAttribute::ON);
1864    }
1865
1866    // Shader program uniforms
1867    if (ivState.currentGLProgram.get() != NULL) {
1868        for (int i=0, c=ivState.currentGLProgram->getNumShaders(); i<c; i++) {
1869             const std::string &shaderCode = ivState.currentGLProgram->getShader(i)->getShaderSource();
1870             if (shaderCode.find("coin_texunit0_model") != std::string::npos) {
1871                 int mode = (ivTexture!=NULL) ? action->getTextureModel() : 0;
1872                 stateSet->addUniform(new osg::Uniform("coin_texunit0_model", mode));
1873                 break;
1874             }
1875        }
1876    }
1877
1878    return stateSet;
1879}
1880////////////////////////////////////////////////////////////////////
1881osg::Texture2D*
1882ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode,
1883                                          SoCallbackAction* action)
1884{
1885#ifdef DEBUG_IV_PLUGIN
1886    OSG_DEBUG << NOTIFY_HEADER
1887              << "convertIVTexToOSGTex ("
1888              << soNode->getTypeId().getName().getString()
1889              << ")" << std::endl;
1890#endif
1891
1892    SbVec2s soSize;
1893    int soNC;
1894
1895    // Get the texture size and components
1896    const unsigned char* soImageData = action->getTextureImage(soSize, soNC);
1897    if (!soImageData) {
1898        OSG_WARN << NOTIFY_HEADER
1899                  << "Warning: Error while loading texture data." << std::endl;
1900        return NULL;
1901    }
1902
1903    // Allocate memory for image data
1904    unsigned char* osgImageData = new unsigned char[soSize[0] * soSize[1] * soNC];
1905
1906    // Copy the texture image data from the inventor texture
1907    memcpy(osgImageData, soImageData, soSize[0] * soSize[1] * soNC);
1908
1909    // File name
1910    std::string fileName;
1911    if (soNode->isOfType(SoTexture2::getClassTypeId()))
1912        fileName = ((SoTexture2*)soNode)->filename.getValue().getString();
1913#ifdef __COIN__
1914    else
1915    if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId()))
1916        fileName = ((SoVRMLImageTexture*)soNode)->url.getNum() >= 1 ?
1917                   ((SoVRMLImageTexture*)soNode)->url.getValues(0)[0].getString() : "";
1918#endif
1919    else
1920      OSG_WARN << NOTIFY_HEADER
1921                << " Warning: Unsupported texture type: "
1922                << soNode->getTypeId().getName().getString() << std::endl;
1923
1924#ifdef DEBUG_IV_PLUGIN
1925    OSG_DEBUG << NOTIFY_HEADER
1926              << "  Converting file name: " << fileName << " -> ";
1927#endif
1928    if (fileName[0]=='\"') fileName.erase(fileName.begin());
1929    if (fileName.size() > 0 && fileName[fileName.size()-1]=='\"')
1930        fileName.erase(fileName.begin()+fileName.size()-1);
1931#ifdef DEBUG_IV_PLUGIN
1932    OSG_DEBUG << fileName << std::endl;
1933#endif
1934
1935    // Create the osg::Image
1936    osg::ref_ptr<osg::Image> osgImage = new osg::Image;
1937    osgImage->setFileName(fileName);
1938    GLenum formats[] = {GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA};
1939    osgImage->setImage(soSize[0], soSize[1], 1, soNC, formats[soNC-1],
1940                       GL_UNSIGNED_BYTE, osgImageData, osg::Image::USE_NEW_DELETE);
1941
1942    // Create the osg::Texture2D
1943    osg::Texture2D *osgTex = new osg::Texture2D;
1944    osgTex->setImage(osgImage.get());
1945
1946    // Set name
1947    osgTex->setName(soNode->getName().getString());
1948
1949    static std::map<SoTexture2::Wrap, osg::Texture2D::WrapMode> texWrapMap;
1950    static bool firstTime = true;
1951    if (firstTime)
1952    {
1953        texWrapMap[SoTexture2::CLAMP] = osg::Texture2D::CLAMP;
1954        texWrapMap[SoTexture2::REPEAT] = osg::Texture2D::REPEAT;
1955        firstTime = false;
1956    }
1957
1958    // Set texture wrap mode
1959#ifdef __COIN__
1960    if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId())) {
1961        // It looks like there is a high probability of bug in Coin (investigated on version 2.4.6).
1962        // action->getTextureWrap() returns correct value on SoTexture2 (SoTexture2::CLAMP = 0x2900,
1963        // and REPEAT = 0x2901), but SoVRMLImageTexture returns incorrect value of
1964        // SoGLImage::REPEAT = 0, CLAMP = 1, CLAMP_TO_EDGE = 2).
1965        // So, let's not use action and try to get correct value directly from texture node.
1966        // PCJohn-2007-04-22
1967        osgTex->setWrap(osg::Texture2D::WRAP_S, ((SoVRMLImageTexture*)soNode)->repeatS.getValue() ?
1968            osg::Texture2D::REPEAT : osg::Texture2D::CLAMP_TO_EDGE);
1969        osgTex->setWrap(osg::Texture2D::WRAP_T, ((SoVRMLImageTexture*)soNode)->repeatT.getValue() ?
1970            osg::Texture2D::REPEAT : osg::Texture2D::CLAMP_TO_EDGE);
1971    }
1972    else
1973#endif
1974    {
1975        // Proper way to determine wrap mode
1976        osgTex->setWrap(osg::Texture2D::WRAP_S, texWrapMap[action->getTextureWrapS()]);
1977        osgTex->setWrap(osg::Texture2D::WRAP_T, texWrapMap[action->getTextureWrapT()]);
1978    }
1979
1980    return osgTex;
1981}
1982///////////////////////////////////////////////////////////////////
1983SoCallbackAction::Response
1984ConvertFromInventor::preInfo(void* data, SoCallbackAction* action,
1985                             const SoNode* node)
1986{
1987#ifdef DEBUG_IV_PLUGIN
1988    OSG_DEBUG << NOTIFY_HEADER << "preInfo()    "
1989              << node->getTypeId().getName().getString() << std::endl;
1990#endif
1991
1992#if 0 // FIXME: Not handled properly yet. There is no Info node in OSG.
1993      // Append probably empty Node and set its name to info->string.getValue();
1994    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
1995    SoInfo* info = (SoInfo*)node;
1996#endif
1997
1998    return SoCallbackAction::CONTINUE;
1999}
2000/////////////////////////////////////////////////////////////
2001SoCallbackAction::Response
2002ConvertFromInventor::preRotor(void *data, SoCallbackAction *action,
2003                              const SoNode *node)
2004{
2005#ifdef DEBUG_IV_PLUGIN
2006    OSG_DEBUG << NOTIFY_HEADER << "preRotor()  "
2007              << node->getTypeId().getName().getString() << std::endl;
2008#endif
2009
2010    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2011
2012    // Get the parameters for the inventor Rotor
2013    SoRotor *ivRotor = (SoRotor *) node;
2014    SbVec3f ivAxis;
2015    float angle;
2016    ivRotor->rotation.getValue(ivAxis, angle);
2017
2018    // Create a new osg::MatrixTransform
2019    osg::ref_ptr<osg::MatrixTransform> rotorTransform = new osg::MatrixTransform;
2020
2021    // Create a Rotor Callback equivalent to the inventor Rotor
2022    osg::Vec3 pivot(0, 0, 0);
2023    osg::Vec3 axis(ivAxis[0], ivAxis[1], ivAxis[2]);
2024    osg::ref_ptr<osgUtil::TransformCallback> rotorCallback
2025        = new osgUtil::TransformCallback(pivot, axis,
2026                                         2 * osg::PI * ivRotor->speed.getValue());
2027
2028    // Set the app callback
2029    rotorTransform->setUpdateCallback(rotorCallback.get());
2030
2031    // Push the rotor onto the state stack
2032    thisPtr->ivPushState(action, node,
2033              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
2034              IvStateItem::APPEND_AT_PUSH, rotorTransform.get());
2035
2036    // Append initial rotation to the model matrix
2037    if (!ivRotor->rotation.isIgnored()) {
2038        SoModelMatrixElement::rotateBy(action->getState(), ivRotor,
2039                                       ivRotor->rotation.getValue());
2040    }
2041
2042    // Don't do the traversal of the SoShuttle
2043    // since it was seen on Coin that is does not append just
2044    // initial shuttle position, but some interpolated one,
2045    // resulting in incorrect animation.
2046    return SoCallbackAction::PRUNE;
2047}
2048////////////////////////////////////////////////////////////////
2049SoCallbackAction::Response
2050ConvertFromInventor::prePendulum(void* data, SoCallbackAction *action,
2051                                 const SoNode* node)
2052{
2053#ifdef DEBUG_IV_PLUGIN
2054    OSG_DEBUG << NOTIFY_HEADER << "prePendulum()  "
2055              << node->getTypeId().getName().getString() << std::endl;
2056#endif
2057
2058    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2059
2060    // Get the parameters for the inventor Pendulum
2061    SoPendulum *ivPendulum = (SoPendulum *) node;
2062    SbVec3f ivAxis0, ivAxis1;
2063    float startAngle, endAngle;
2064    ivPendulum->rotation0.getValue(ivAxis0, startAngle);
2065    ivPendulum->rotation1.getValue(ivAxis1, endAngle);
2066    ivAxis0.normalize();
2067    ivAxis1.normalize();
2068
2069    // Reverse axis and direction if required
2070    // Actually, this will produce correct results only when axis is
2071    // opposite to each other, and approximate results when nearly
2072    // opposite and garbage otherwise.
2073    if ((ivAxis0+ivAxis1).length() < 0.5 ) {
2074        ivAxis1 = -ivAxis1;
2075        endAngle = -endAngle;
2076    }
2077
2078    // Create a new osg::MatrixTransform
2079    osg::ref_ptr<osg::MatrixTransform> pendulumTransform = new osg::MatrixTransform;
2080
2081    // Create a Pendulum Callback equivalent to the inventor Rotor
2082    // Use axis from of the bigger angle (to avoid lost axis when
2083    // angle is zero - see SbRotation and quaternion theory).
2084    osg::Vec3 axis;
2085    if (fabs(startAngle) > fabs(endAngle))
2086        axis = osg::Vec3(ivAxis0[0], ivAxis0[1], ivAxis0[2]);
2087    else
2088        axis = osg::Vec3(ivAxis1[0], ivAxis1[1], ivAxis1[2]);
2089    PendulumCallback* pendulumCallback
2090        = new PendulumCallback(axis, startAngle, endAngle,
2091                               ivPendulum->speed.getValue());
2092
2093    // Set the app callback
2094    pendulumTransform->setUpdateCallback(pendulumCallback);
2095
2096    // Push the pendulum onto the state stack
2097    thisPtr->ivPushState(action, node,
2098              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
2099              IvStateItem::APPEND_AT_PUSH, pendulumTransform.get());
2100
2101    // Don't do the traversal of the SoShuttle
2102    // since it was seen on Coin that is does not append just
2103    // initial shuttle position, but some interpolated one,
2104    // resulting in incorrect animation.
2105    return SoCallbackAction::PRUNE;
2106}
2107////////////////////////////////////////////////////////////////
2108SoCallbackAction::Response
2109ConvertFromInventor::preShuttle(void* data, SoCallbackAction *action,
2110                                const SoNode* node)
2111{
2112#ifdef DEBUG_IV_PLUGIN
2113    OSG_DEBUG << NOTIFY_HEADER << "preShuttle()  "
2114              << node->getTypeId().getName().getString() << std::endl;
2115#endif
2116
2117    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2118
2119    // Get the parameters for the inventor Shuttle
2120    SoShuttle *ivShuttle = (SoShuttle *) node;
2121    SbVec3f ivStartPos, ivEndPos;
2122    ivStartPos = ivShuttle->translation0.getValue();
2123    ivEndPos = ivShuttle->translation1.getValue();
2124
2125    // Create a new osg::MatrixTransform
2126    osg::ref_ptr<osg::MatrixTransform> shuttleTransform = new osg::MatrixTransform;
2127
2128    // Create a shuttle Callback equivalent to the inventor Rotor
2129    osg::Vec3 startPos(ivStartPos[0], ivStartPos[1], ivStartPos[2]);
2130    osg::Vec3 endPos(ivEndPos[0], ivEndPos[1], ivEndPos[2]);
2131    ShuttleCallback* shuttleCallback
2132        = new ShuttleCallback(startPos, endPos, ivShuttle->speed.getValue());
2133
2134    // Set the app callback
2135    shuttleTransform->setUpdateCallback(shuttleCallback);
2136
2137    // Push the shuttle onto the state stack
2138    thisPtr->ivPushState(action, node,
2139              IvStateItem::MULTI_POP | IvStateItem::UPDATE_STATE |
2140              IvStateItem::APPEND_AT_PUSH, shuttleTransform.get());
2141
2142    // Don't do the traversal of the SoShuttle
2143    // since it was seen on Coin that is does not append just
2144    // initial shuttle position, but some interpolated one,
2145    // resulting in incorrect animation.
2146    return SoCallbackAction::PRUNE;
2147}
2148////////////////////////////////////////////////////////////
2149void ConvertFromInventor::addVertex(SoCallbackAction* action,
2150                                    const SoPrimitiveVertex *v,
2151                                    int index)
2152{
2153    // Get the coordinates of the vertex
2154    SbVec3f pt = v->getPoint();
2155    vertices.push_back(osg::Vec3(pt[0], pt[1], pt[2]));
2156
2157    // Get the normal of the vertex
2158    SbVec3f norm = v->getNormal();
2159
2160    if ((normalBinding == osg::Geometry::BIND_PER_VERTEX) ||
2161        (normalBinding == osg::Geometry::BIND_PER_PRIMITIVE && index == 0))
2162    {
2163        // What is this? Why to invert normals at CLOCKWISE vertex ordering?
2164        // PCJohn 2009-12-13
2165        //if (vertexOrder == CLOCKWISE)
2166        //    normals.push_back(osg::Vec3(-norm[0], -norm[1], -norm[2]));
2167        //else
2168            normals.push_back(osg::Vec3(norm[0], norm[1], norm[2]));
2169    }
2170
2171    if (colorBinding == osg::Geometry::BIND_PER_VERTEX ||
2172            colorBinding == osg::Geometry::BIND_PER_PRIMITIVE)
2173    {
2174        // Get the material/color
2175        SbColor ambient, diffuse, specular, emission;
2176        float transparency, shininess;
2177        action->getMaterial(ambient, diffuse, specular, emission, shininess,
2178                            transparency, v->getMaterialIndex());
2179        if (colorBinding == osg::Geometry::BIND_PER_VERTEX)
2180            colors.push_back(osg::Vec4(diffuse[0], diffuse[1], diffuse[2],
2181                                       1.0 - transparency));
2182        else if (colorBinding == osg::Geometry::BIND_PER_PRIMITIVE && index == 0)
2183            colors.push_back(osg::Vec4(diffuse[0], diffuse[1], diffuse[2],
2184                                       1.0 - transparency));
2185    }
2186
2187    // Get the texture coordinates
2188    SbVec4f texCoord = v->getTextureCoords();
2189    textureCoords.push_back(osg::Vec2(texCoord[0], texCoord[1]));
2190}
2191////////////////////////////////////////////////////////////////////////////
2192void ConvertFromInventor::addTriangleCB(void* data, SoCallbackAction* action,
2193                                        const SoPrimitiveVertex* v0,
2194                                        const SoPrimitiveVertex* v1,
2195                                        const SoPrimitiveVertex* v2)
2196{
2197    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2198
2199    switch (thisPtr->vertexOrder)
2200    {
2201        case CLOCKWISE:
2202            thisPtr->addVertex(action, v0, 0);
2203            thisPtr->addVertex(action, v2, 1);
2204            thisPtr->addVertex(action, v1, 2);
2205            break;
2206        case COUNTER_CLOCKWISE:
2207            thisPtr->addVertex(action, v0, 0);
2208            thisPtr->addVertex(action, v1, 1);
2209            thisPtr->addVertex(action, v2, 2);
2210            break;
2211    }
2212
2213    thisPtr->numPrimitives++;
2214    thisPtr->primitiveType = osg::PrimitiveSet::TRIANGLES;
2215}
2216////////////////////////////////////////////////////////////////////////////////
2217void ConvertFromInventor::addLineSegmentCB(void* data, SoCallbackAction* action,
2218                                           const SoPrimitiveVertex* v0,
2219                                           const SoPrimitiveVertex* v1)
2220{
2221    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2222
2223    thisPtr->addVertex(action, v0, 0);
2224    thisPtr->addVertex(action, v1, 1);
2225
2226    thisPtr->numPrimitives++;
2227    thisPtr->primitiveType = osg::PrimitiveSet::LINES;
2228}
2229//////////////////////////////////////////////////////////////////////////
2230void ConvertFromInventor::addPointCB(void* data, SoCallbackAction* action,
2231                                     const SoPrimitiveVertex* v0)
2232{
2233    ConvertFromInventor* thisPtr = (ConvertFromInventor *) (data);
2234
2235    thisPtr->addVertex(action, v0, 0);
2236
2237    thisPtr->numPrimitives++;
2238    thisPtr->primitiveType = osg::PrimitiveSet::POINTS;
2239}
2240//////////////////////////////////////////////////////////////////////////
2241void ConvertFromInventor::init()
2242{
2243#ifdef __COIN__
2244    SoTexture2Osg::overrideClass();
2245    SoTexture3Osg::overrideClass();
2246    SoVRMLImageTextureOsg::overrideClass();
2247#endif
2248}
Note: See TracBrowser for help on using the browser.