root/OpenSceneGraph/trunk/src/osgPlugins/Inventor/ConvertToInventor.cpp @ 13497

Revision 13497, 77.5 kB (checked in by robert, 3 days ago)

Fixed comment

  • Property svn:eol-style set to native
Line 
1//
2//  ConvertToInventor converts OSG scene graph to Inventor or VRML 1 scene graph
3//
4//  It requires OSG and Inventor compatible library, such as Coin,
5//  SGI Inventor , or TGS Inventor.
6//
7//
8//  Autor: PCJohn (peciva _at fit.vutbr.cz)
9//
10//  License: public domain
11//
12//
13//  THIS SOFTWARE IS NOT COPYRIGHTED
14//
15//  This source code is offered for use in the public domain.
16//  You may use, modify or distribute it freely.
17//
18//  This source code is distributed in the hope that it will be useful but
19//  WITHOUT ANY WARRANTY.  ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
20//  DISCLAIMED.  This includes but is not limited to warranties of
21//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22//
23//  If you find the source code useful, authors will kindly welcome
24//  if you give them credit and keep their names with their source code,
25//  but you are not forced to do so.
26//
27
28#include <osg/Config>
29#ifndef OSG_USE_DEPRECATED_GEOMETRY_METHODS 
30#define OSG_USE_DEPRECATED_GEOMETRY_METHODS 1
31#endif
32
33#include <osg/BlendFunc>
34#include <osg/Billboard>
35#include <osg/Geode>
36#include <osg/Geometry>
37#include <osg/Group>
38#include <osg/LightModel>
39#include <osg/LOD>
40#include <osg/Material>
41#include <osg/MatrixTransform>
42#include <osg/Node>
43#include <osg/NodeVisitor>
44#include <osg/PositionAttitudeTransform>
45#include <osg/PrimitiveSet>
46#include <osg/ShapeDrawable>
47#include <osg/TexEnv>
48#include <osg/TexGen>
49#include <Inventor/nodes/SoBaseColor.h>
50#include <Inventor/nodes/SoCoordinate3.h>
51#include <Inventor/nodes/SoCoordinate4.h>
52#include <Inventor/nodes/SoCube.h>
53#include <Inventor/nodes/SoCone.h>
54#include <Inventor/nodes/SoCylinder.h>
55#include <Inventor/nodes/SoFaceSet.h>
56#include <Inventor/nodes/SoIndexedFaceSet.h>
57#include <Inventor/nodes/SoIndexedLineSet.h>
58#include <Inventor/nodes/SoIndexedTriangleStripSet.h>
59#include <Inventor/nodes/SoLightModel.h>
60#include <Inventor/nodes/SoLineSet.h>
61#include <Inventor/nodes/SoLOD.h>
62#include <Inventor/nodes/SoLevelOfDetail.h>
63#include <Inventor/nodes/SoMaterial.h>
64#include <Inventor/nodes/SoMatrixTransform.h>
65#include <Inventor/nodes/SoNode.h>
66#include <Inventor/nodes/SoNormal.h>
67#include <Inventor/nodes/SoNormalBinding.h>
68#include <Inventor/nodes/SoPointSet.h>
69#include <Inventor/nodes/SoSeparator.h>
70#include <Inventor/nodes/SoShapeHints.h>
71#include <Inventor/nodes/SoSphere.h>
72#include <Inventor/nodes/SoTexture2.h>
73#include <Inventor/nodes/SoTextureCoordinate2.h>
74#include <Inventor/nodes/SoTextureCoordinateEnvironment.h>
75#include <Inventor/nodes/SoTransform.h>
76#include <Inventor/nodes/SoTranslation.h>
77#include <Inventor/nodes/SoTriangleStripSet.h>
78#include <Inventor/fields/SoFields.h>
79#ifdef __COIN__
80#include <Inventor/nodes/SoTextureCoordinate3.h>
81#include <Inventor/nodes/SoTransparencyType.h>
82#include <Inventor/VRMLnodes/SoVRMLBillboard.h>
83#endif
84#include "ConvertToInventor.h"
85
86#include <assert.h>
87
88
89#define DEBUG_IV_WRITER 1
90
91
92// Considerations for VRML1 support:
93//
94// SoSeparator:                supported (only renderCulling field)
95// SoCoordinate3:              supported
96// SoCoordinate4:              no -> conversion to Coordinate3 required <---- missing
97// SoTextureCoordinate2:       supported
98// SoTextureCoordinate3:       no - 3D textures not supported by VRML1
99// SoTextureCoordinateEnvironment no <- should texturing be disabled in this case?
100// SoTextureCoordinateBinding: no -> indexing is probably mandatory
101// SoNormal:                   supported
102// SoNormalBinding:            supported (all modes supported, including non-indexed modes)
103// SoMaterial:                 supported
104// SoMaterialBinding:          supported (all modes supported, including non-indexed modes)
105// SoBaseColor:                no -> using SoMaterial instead
106// SoPointSet:                 supported
107// SoLineSet:                  no -> using SoIndexedListSet instead
108// SoIndexedLineSet:           supported
109// SoIndexedTriangleStripSet:  no -> using SoIndexedFaceSet instead
110// SoTriangleStripSet:         no -> using SoIndexedFaceSet instead
111// SoMatrixTransform:          supported
112// SoTransform:                supported
113// SoTransparencyType:         no   <---- missing
114// SoShapeHints:               supported
115// SoCone,SoCube,SoCylinder,SoSphere supported
116// SoTranslation               supported
117// SoLightModel:               ?
118// SoLOD:                      supported
119// SoLevelOfDetail:            no
120
121
122// Node list from VRML1 documentation:
123// shape nodes: AsciiText, Cone, Cube, Cylinder, IndexedFaceSet, IndexedLineSet, PointSet, Sphere
124// properties nodes: Coordinate3, FontStyle, Info, Material, MaterialBinding, Normal, NormalBinding,
125//                   Texture2, Texture2Transform, TextureCoordinate2, ShapeHints
126// transformantion nodes: MatrixTransform, Rotation, Scale, Transform, Translation
127// group nodes: Separator, Switch, WWWAnchor, LOD
128// cameras: OrthographicCamera, PerspectiveCamera
129// lights: DirectionalLight, PointLight, SpotLight
130// others: WWWInline
131
132
133
134ConvertToInventor::ConvertToInventor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
135{
136  // Enable/disable using of some extensions introduced by TGS and Coin.
137  //
138  // For instance, GL_REPLACE texturing mode is extension
139  // and it will not be used if extensions are not enabled.
140#ifdef __COIN__
141  useIvExtensions = true;
142#else
143  useIvExtensions = false;
144#endif
145  vrml1Conversion = false;
146  uniqueIdGenerator = 1;
147
148  // Scene root
149  ivRoot = new SoSeparator;
150  ivRoot->ref();
151
152  // OSG -> Inventor coordinate system conversion
153  SoMatrixTransform *mt = new SoMatrixTransform;
154  mt->matrix.setValue(1.f, 0.f,  0.f, 0.f,
155                      0.f, 0.f, -1.f, 0.f,
156                      0.f, 1.f,  0.f, 0.f,
157                      0.f, 0.f,  0.f, 1.f);
158  ivRoot->addChild(mt);
159
160#if 0 // Setting everything to defaults may not be correct option
161      // because the user may want to have model whose material,etc.
162      // will be specified up in the scene graph and he may, for instance,
163      // reuse the model with different materials.
164      // However, I am not sure anyway. PCJohn-2007-08-27
165
166  //              OSG defaults to:    Inventor defaults to:
167  // GL_LIGHTING   enabled             enabled
168  SoLightModel *lm = new SoLightModel;
169  lm->model = SoLightModel::PHONG;
170  ivRoot->addChild(lm);
171
172  //              OSG defaults to:    Inventor defaults to:
173  // lighting:     one side            one side lighting
174  // frontFace:    COUNTER_CLOCKWISE   COUNTER_CLOCKWISE
175  // culling:      DISABLED            DISABLED
176  // faceCulling:  GL_BACK             GL_BACK
177  SoShapeHints *sh = new SoShapeHints;
178  sh->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING;
179  ivRoot->addChild(sh);
180
181  // Make sure texturing is OFF
182  ivRoot->addChild(new SoTexture2D);
183
184  // Put default material
185  ivRoot->addChild(new SoMaterial);
186#endif
187
188  // initialize Inventor state stack
189  ivStack.push(InventorState::createTopLevelState(ivRoot));
190}
191
192
193ConvertToInventor::~ConvertToInventor()
194{
195  assert(ivStack.size() == 1 && "Not all InventorStates were popped from ivStack.");
196  if (ivRoot)  ivRoot->unref();
197}
198
199
200SoNode* ConvertToInventor::getIvSceneGraph() const
201{
202  return ivRoot;
203}
204
205
206void ConvertToInventor::apply(osg::Node &node)
207{
208#ifdef DEBUG_IV_WRITER
209  OSG_INFO << "IvWriter: node traversed" << std::endl;
210#endif
211
212  traverse(node);
213}
214
215
216template<typename fieldClass, typename ivType, typename osgType>
217void osgArray2ivMField_template(const osg::Array *array, fieldClass &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0)
218{
219  int i,num = array->getNumElements();
220  if (startIndex!=0 || stopIndex!=0) {
221    num = stopIndex - startIndex;
222    assert(stopIndex >= startIndex);
223    assert(startIndex >= 0 && stopIndex >= 0);
224    assert(stopIndex <= int(array->getNumElements()));
225  }
226
227  // update num if numItemsUntilMinusOne > 0
228  if (numItemsUntilMinusOne > 0 && num >= 1)
229    num += (num-1) / numItemsUntilMinusOne;
230
231  field.setNum(num);
232  ivType *a = field.startEditing();
233
234  osgType *ptr = (osgType*)array->getDataPointer() + startIndex;
235
236  if (numItemsUntilMinusOne <= 0)
237    for (i=0; i<num; i++, ptr++)
238      a[i] = ivType(*ptr);
239  else {
240    int z;
241    for (i=0, z=0; i<num; i++)
242      if (z == numItemsUntilMinusOne) {
243        a[i] = ivType(-1);
244        z = 0;
245      } else {
246        a[i] = ivType(*ptr);
247        ptr++;
248        z++;
249      }
250  }
251
252  field.finishEditing();
253}
254
255
256template<typename ivType, typename osgType, int shift>
257void osgArray2ivMField_composite_template_worker(ivType *dest, osgType *src, int num, int numItemsUntilMinusOne = 0)
258{
259  for (int i=0; i<num; i++, src+=shift)
260    dest[i] = ivType(src);
261}
262
263
264template<>
265void osgArray2ivMField_composite_template_worker<SbColor, GLubyte, 4>(SbColor *dest, GLubyte *src, int num, int numItemsUntilMinusOne)
266{
267  for (int i=0; i<num; i++, src+=4)
268    dest[i].setValue(src[0]/255.f, src[1]/255.f, src[2]/255.f);
269}
270
271
272template<>
273void osgArray2ivMField_composite_template_worker<SbVec3f, float, 2>(SbVec3f *dest, float *src, int num, int numItemsUntilMinusOne)
274{
275  for (int i=0; i<num; i++, src+=2)
276    dest[i].setValue(src[0], src[1], 0.f);
277}
278
279
280template<typename fieldClass, typename ivType, typename osgType, int shift>
281void osgArray2ivMField_composite_template(const osg::Array *array, fieldClass &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0)
282{
283  int num = array->getNumElements();
284  if (startIndex!=0 || stopIndex!=0) {
285    num = stopIndex - startIndex;
286    assert(stopIndex >= startIndex);
287    assert(startIndex >= 0 && stopIndex >= 0);
288    assert(stopIndex <= int(array->getNumElements()));
289  }
290  assert(numItemsUntilMinusOne <= 0 && "Composite template must have numItemsUntilMinusOne set to 0.");
291  field.setNum(num);
292  ivType *a = field.startEditing();
293
294  osgType *ptr = (osgType*)array->getDataPointer() + startIndex;
295  osgArray2ivMField_composite_template_worker<ivType, osgType, shift>(a, ptr, num, 0);
296
297  field.finishEditing();
298}
299
300
301template<typename fieldClass, typename ivType, typename osgType, int numComponents>
302void osgArray2ivMField_pack_template(const osg::Array *array, fieldClass &field,
303                                            osgType mul, osgType max, osgType min,
304                                            int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0)
305{
306  int i,num = array->getNumElements();
307  if (startIndex!=0 || stopIndex!=0) {
308    num = stopIndex - startIndex;
309    assert(stopIndex >= startIndex);
310    assert(startIndex >= 0 && stopIndex >= 0);
311    assert(stopIndex <= int(array->getNumElements()));
312  }
313  assert(numItemsUntilMinusOne <= 0 && "Pack template must have numItemsUntilMinusOne set to 0.");
314  field.setNum(num);
315  ivType *a = field.startEditing();
316
317  osgType *ptr = (osgType*)array->getDataPointer() + startIndex;
318  for (i=0; i<num; i++, ptr++) {
319    a[i] = ivType(0);
320    for (int j=0; j<numComponents; j++) {
321      osgType tmp = ptr[j]*mul;
322      if (tmp > max)  tmp = max;
323      if (tmp < min)  tmp = min;
324      a[i] |= ivType(tmp) << (((numComponents-1)*8)-(j*8));
325    }
326  }
327
328  field.finishEditing();
329}
330
331
332template<typename fieldClass, typename fieldItemType>
333bool ivApplicateIntType(const osg::Array *array, fieldClass &field, int startIndex, int stopIndex, int numItemsUntilMinusOne)
334{
335  if (field.isOfType(fieldClass::getClassTypeId()))
336  {
337    switch (array->getType())
338    {
339      case osg::Array::ByteArrayType:   osgArray2ivMField_template<fieldClass, fieldItemType, int8_t>
340                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
341      case osg::Array::UByteArrayType:  osgArray2ivMField_template<fieldClass, fieldItemType, uint8_t>
342                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
343      case osg::Array::ShortArrayType:  osgArray2ivMField_template<fieldClass, fieldItemType, int16_t>
344                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
345      case osg::Array::UShortArrayType: osgArray2ivMField_template<fieldClass, fieldItemType, uint16_t>
346                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
347      case osg::Array::IntArrayType:    osgArray2ivMField_template<fieldClass, fieldItemType, int32_t>
348                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
349      case osg::Array::UIntArrayType:   osgArray2ivMField_template<fieldClass, fieldItemType, uint32_t>
350                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
351      case osg::Array::FloatArrayType:  osgArray2ivMField_template<fieldClass, fieldItemType, float>
352                                          (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true;
353      case osg::Array::Vec4bArrayType:  // FIXME: should signed values be handled differently? Like -128..127?
354      case osg::Array::Vec4ubArrayType: osgArray2ivMField_pack_template<fieldClass, fieldItemType, GLubyte, 4>
355                                          (array, field, 1, 255, 0, startIndex, stopIndex, numItemsUntilMinusOne); return true;
356      case osg::Array::Vec4ArrayType:   osgArray2ivMField_pack_template<fieldClass, fieldItemType, float, 4>
357                                          (array, field, 255.f, 255.f, 0.f, startIndex, stopIndex, numItemsUntilMinusOne); return true;
358      default: break;
359    }
360  }
361  return false;
362}
363
364
365static void osgArray2ivMField(const osg::Array *array, SoMField &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0)
366{
367  if (field.isOfType(SoMFFloat::getClassTypeId()))
368  {
369    switch (array->getType())
370    {
371      case osg::Array::FloatArrayType:  osgArray2ivMField_template<SoMFFloat, float, float>
372                                          (array, (SoMFFloat&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
373      default: break;
374    }
375  }
376  else if (ivApplicateIntType<SoMFInt32,  int32_t>(array, (SoMFInt32&) field, startIndex, stopIndex, numItemsUntilMinusOne)) return;
377  else if (ivApplicateIntType<SoMFUInt32,uint32_t>(array, (SoMFUInt32&)field, startIndex, stopIndex, numItemsUntilMinusOne)) return;
378  else if (ivApplicateIntType<SoMFShort,  int16_t>(array, (SoMFShort&) field, startIndex, stopIndex, numItemsUntilMinusOne)) return;
379  else if (ivApplicateIntType<SoMFUShort,uint16_t>(array, (SoMFUShort&)field, startIndex, stopIndex, numItemsUntilMinusOne)) return;
380  else if (field.isOfType(SoMFVec2f::getClassTypeId()))
381  {
382    switch (array->getType())
383    {
384      case osg::Array::Vec2ArrayType:   osgArray2ivMField_composite_template<SoMFVec2f, SbVec2f, float, 2>
385                                          (array, (SoMFVec2f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
386      default: break;
387    }
388  }
389  else if (field.isOfType(SoMFVec3f::getClassTypeId()))
390  {
391    switch (array->getType())
392    {
393      case osg::Array::Vec3ArrayType:   osgArray2ivMField_composite_template<SoMFVec3f, SbVec3f, float, 3>
394                                          (array, (SoMFVec3f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
395      case osg::Array::Vec2ArrayType:   osgArray2ivMField_composite_template<SoMFVec3f, SbVec3f, float, 2>
396                                          (array, (SoMFVec3f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
397      default: break;
398    }
399  }
400  else if (field.isOfType(SoMFVec4f::getClassTypeId()))
401  {
402    switch (array->getType()) {
403      case osg::Array::Vec4ArrayType:   osgArray2ivMField_composite_template<SoMFVec4f, SbVec4f, float, 4>
404                                          (array, (SoMFVec4f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
405      default: break;
406    }
407  }
408  else if (field.isOfType(SoMFColor::getClassTypeId()))
409  {
410    switch (array->getType())
411    {
412      case osg::Array::Vec3ArrayType:   osgArray2ivMField_composite_template<SoMFColor, SbColor, float, 3>
413                                          (array, (SoMFColor&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
414      case osg::Array::Vec4ArrayType:   osgArray2ivMField_composite_template<SoMFColor, SbColor, float, 4>
415                                          (array, (SoMFColor&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
416      case osg::Array::Vec4ubArrayType: osgArray2ivMField_composite_template<SoMFColor, SbColor, GLubyte, 4>
417                                          (array, (SoMFColor&)field, startIndex, stopIndex, numItemsUntilMinusOne); return;
418      default: break;
419    }
420  };
421
422  OSG_WARN << "IvWriter: No direct conversion for array. "
423    << "The file may be broken." << std::endl;
424}
425
426
427template<typename variableType, typename indexType>
428bool ivDeindex(variableType *dest, const variableType *src, const int srcNum,
429                    const indexType *indices, const int numToProcess)
430{
431  for (int i=0; i<numToProcess; i++) {
432    int index = indices[i];
433    if (index<0 || index>=srcNum)  return false;
434    dest[i] = src[index];
435  }
436  return true;
437}
438
439
440template<typename variableType>
441bool ivDeindex(variableType *dest, const variableType *src, const int srcNum,
442                    const osg::Array *indices, const int numToProcess)
443{
444  if (int(indices->getNumElements()) < numToProcess) {
445    assert(0 && "Something is wrong: indices array is shorter than numToProcess.");
446    return false;
447  }
448
449  switch (indices->getType()) {
450  case osg::Array::ByteArrayType:
451  case osg::Array::UByteArrayType:
452      return ivDeindex<variableType, GLbyte>(dest, src, srcNum,
453                                           (GLbyte*)indices->getDataPointer(), numToProcess);
454      break;
455  case osg::Array::ShortArrayType:
456  case osg::Array::UShortArrayType:
457      return ivDeindex<variableType, GLshort>(dest, src, srcNum,
458                                            (GLshort*)indices->getDataPointer(), numToProcess);
459      break;
460  case osg::Array::IntArrayType:
461  case osg::Array::UIntArrayType:
462      return ivDeindex<variableType, GLint>(dest, src, srcNum,
463                                          (GLint*)indices->getDataPointer(), numToProcess);
464      break;
465  default:
466      assert(0 && "Index of strange type.");
467      return false;
468  }
469}
470
471
472template<typename variableType, typename fieldType>
473bool ivProcessArray(const osg::Array *indices, const osg::Array *drawElemIndices,
474                         fieldType *destField, const fieldType *srcField,
475                         int startIndex, int numToProcess)
476{
477  bool ok = true;
478
479  if (indices || drawElemIndices) {
480
481    // "deindex" original data
482    if (indices && !drawElemIndices)
483      ok = ivDeindex<variableType>(destField->startEditing(),
484                                 srcField->getValues(startIndex),
485                                 srcField->getNum(), indices, numToProcess); else
486    if (!indices && drawElemIndices)
487      ok = ivDeindex<variableType>(destField->startEditing(),
488                                 srcField->getValues(startIndex),
489                                 srcField->getNum(), drawElemIndices, numToProcess);
490    else {
491      OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
492      assert(0); // FIXME:
493    }
494
495    destField->finishEditing();
496    if (!ok)
497      OSG_WARN << "IvWriter: Can not deindex - bug in model: index out of range." << std::endl;
498
499  } else {
500
501    // copy required part of original data
502    const variableType *src = srcField->getValues(startIndex);
503    assert(startIndex+numToProcess <= srcField->getNum() && "Index out of bounds.");
504    variableType *dest = destField->startEditing();
505    for (int i=0; i<numToProcess; i++)
506      *(dest++) = *(src++);
507    destField->finishEditing();
508  }
509
510  return ok;
511}
512
513
514static void processIndices(const osg::Array *indices, const osg::Array *drawElemIndices,
515                           SoMFInt32 &ivIndices,
516                           int startIndex, int stopIndex, int numItemsUntilMinusOne)
517{
518  if (indices || drawElemIndices) {
519    if (indices && !drawElemIndices)
520      osgArray2ivMField(indices, ivIndices, startIndex, stopIndex, numItemsUntilMinusOne); else
521    if (!indices && drawElemIndices)
522      osgArray2ivMField(drawElemIndices, ivIndices, startIndex, stopIndex, numItemsUntilMinusOne);
523    else {
524      OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
525      assert(0); // FIXME:
526    }
527
528  } else {
529    int num = stopIndex-startIndex;
530    if (numItemsUntilMinusOne != 0 && num >= 1)
531      num += (num-1)/numItemsUntilMinusOne;
532    ivIndices.setNum(num);
533    int32_t *a = ivIndices.startEditing();
534    if (numItemsUntilMinusOne <= 0)
535      for (int i=0,j=startIndex; j<stopIndex; i++,j++)
536        a[i] = j;
537    else
538      for (int i=0,j=startIndex,k=0; j<stopIndex; i++)
539        if (k==numItemsUntilMinusOne) {
540          a[i]=-1;
541          k=0;
542        } else {
543          a[i] = j;
544          j++;
545          k++;
546        }
547    ivIndices.finishEditing();
548  }
549}
550
551
552static void postProcessDrawArrayLengths(const osg::DrawArrayLengths *drawArrayLengths, SoMFInt32 *field)
553{
554  int origNum = field->getNum();
555  int newNum = origNum + drawArrayLengths->size()-1;
556  field->setNum(newNum);
557  int32_t *a = field->startEditing();
558  int32_t *src = a + origNum;
559  int32_t *dst = a + newNum;
560  for (osg::DrawArrayLengths::const_reverse_iterator primItr = drawArrayLengths->rbegin();
561      primItr!=drawArrayLengths->rend()-1;
562      ++primItr) {
563    int c = *primItr;
564    src -= c;
565    dst -= c;
566    memmove(dst, src, sizeof(int32_t)*c);
567    dst--;
568    *dst = -1;
569  }
570  field->finishEditing();
571}
572
573
574static void postProcessField(const SbIntList &runLengths, osg::PrimitiveSet::Mode primType,
575                             SoMFInt32 *field, osg::Geometry::AttributeBinding binding)
576{
577  if (binding==osg::Geometry::BIND_OFF || binding==osg::Geometry::BIND_OVERALL ||
578      binding==osg::Geometry::BIND_PER_PRIMITIVE_SET)
579    return;
580
581  // make copy of array
582  const int32_t *fieldArray = field->getValues(0);
583  int origNum = field->getNum();
584  int32_t *tmpArray = new int32_t[origNum];
585  memcpy(tmpArray, fieldArray, origNum*sizeof(int32_t));
586
587  // compute new number of indices
588  int newNum = origNum;
589  const int l = runLengths.getLength();
590  switch (binding) {
591    case osg::Geometry::BIND_PER_VERTEX:
592      for (int i=0; i<l; i++)
593        newNum += (runLengths[i]-3)*3;
594      break;
595    case osg::Geometry::BIND_PER_PRIMITIVE:
596      for (int i=0; i<l; i++)
597        newNum += runLengths[i]-3;
598      break;
599    default:
600      assert(0);
601  }
602
603  // process indices
604  field->setNum(newNum);
605  int32_t *src = tmpArray;
606  int32_t *dst = field->startEditing();
607  // int32_t *dst2 = dst;
608  switch (binding) {
609    case osg::Geometry::BIND_PER_VERTEX:
610      for (int i=0; i<l; i++) {
611        int c = runLengths[i];
612        *(dst++) = *(src++);
613        *(dst++) = *(src++);
614        *(dst++) = *(src++);
615        bool even = true;
616        int32_t first = *(src-3);
617        for (int j=3; j<c; j++) {
618          *(dst++) = -1;
619          if (primType==osg::PrimitiveSet::TRIANGLE_STRIP) {
620            if (even) {
621              *(dst++) = *(src-1);
622              *(dst++) = *(src-2);
623            } else {
624              *(dst++) = *(src-2);
625              *(dst++) = *(src-1);
626            }
627            even = !even;
628          } else
629          if (primType==osg::PrimitiveSet::TRIANGLE_FAN) {
630            *(dst++) = first;
631            *(dst++) = *(src-1);
632          } // FIXME: are QUADS, QUAD_STRIP, and POLYGON requiring some handling here as well? PCJohn-2007-08-25
633          else {
634            *(dst++) = *(src-2);
635            *(dst++) = *(src-1);
636          }
637          *(dst++) = *(src++);
638        }
639        src++; // skip -1
640        if (i != l-1)
641          *(dst++) = -1;
642      }
643      break;
644
645    case osg::Geometry::BIND_PER_PRIMITIVE:
646      for (int i=0; i<l; i++,src++) {
647        int c = runLengths[i];
648        *(dst++) = *(src);
649        for (int j=3; j<c; j++)
650          *(dst++) = *(src);
651      }
652      break;
653
654    default:
655      assert(0);
656  }
657  field->finishEditing();
658
659  // free resources
660  delete [] tmpArray;
661}
662
663
664static void postProcessTriangleSeparation(SoIndexedShape *shape, osg::PrimitiveSet::Mode primType,
665                                          osg::Geometry::AttributeBinding normalBinding,
666                                          osg::Geometry::AttributeBinding colorBinding)
667{
668  // compute runLengths
669  SbIntList runLengths;
670  const int32_t *a = shape->coordIndex.getValues(0);
671  int origNum = shape->coordIndex.getNum();
672  int l = 0;
673  for (int i=0; i<origNum; i++,a++) {
674    if (*a == -1) {
675      runLengths.append(l);
676      l = 0;
677    } else
678      l++;
679  }
680  if (l != 0) // append final l if field is not finished by -1
681    runLengths.append(l);
682
683  postProcessField(runLengths, primType, &shape->coordIndex,        osg::Geometry::BIND_PER_VERTEX);
684  postProcessField(runLengths, primType, &shape->normalIndex,       normalBinding);
685  postProcessField(runLengths, primType, &shape->materialIndex,     colorBinding);
686  bool notUseTexCoords = shape->textureCoordIndex.getNum()==0 ||
687                         (shape->textureCoordIndex.getNum()==1 && shape->textureCoordIndex[0] == -1);
688  if (!notUseTexCoords)
689    postProcessField(runLengths, primType, &shape->textureCoordIndex, osg::Geometry::BIND_PER_VERTEX);
690
691}
692
693
694static SoMaterialBinding* createMaterialBinding(const osg::Geometry *g, bool isMaterialIndexed)
695{
696  SoMaterialBinding *materialBinding = new SoMaterialBinding;
697  switch (g->getColorBinding()) {
698  case osg::Geometry::BIND_OFF: // OFF means use material from state set (if any) for whole geometry
699  case osg::Geometry::BIND_OVERALL:
700  case osg::Geometry::BIND_PER_PRIMITIVE_SET: materialBinding->value = SoMaterialBinding::OVERALL; break;
701  case osg::Geometry::BIND_PER_PRIMITIVE:     materialBinding->value = (isMaterialIndexed) ? SoMaterialBinding::PER_PART_INDEXED   : SoMaterialBinding::PER_PART; break;
702  case osg::Geometry::BIND_PER_VERTEX:        materialBinding->value = (isMaterialIndexed) ? SoMaterialBinding::PER_VERTEX_INDEXED : SoMaterialBinding::PER_VERTEX; break;
703  default: assert(0);
704  }
705  return materialBinding;
706}
707
708
709static SoNormalBinding* createNormalBinding(const osg::Geometry *g, bool areNormalsIndexed)
710{
711  // Convert normal binding
712  SoNormalBinding *normalBinding = new SoNormalBinding;
713  switch (g->getNormalBinding()) {
714  case osg::Geometry::BIND_OFF: // FIXME: what to do with BIND_OFF value?
715  case osg::Geometry::BIND_OVERALL:
716  case osg::Geometry::BIND_PER_PRIMITIVE_SET: normalBinding->value = SoNormalBinding::OVERALL; break;
717  case osg::Geometry::BIND_PER_PRIMITIVE:     normalBinding->value = (areNormalsIndexed) ? SoNormalBinding::PER_PART_INDEXED   : SoNormalBinding::PER_PART; break;
718  case osg::Geometry::BIND_PER_VERTEX:        normalBinding->value = (areNormalsIndexed) ? SoNormalBinding::PER_VERTEX_INDEXED : SoNormalBinding::PER_VERTEX; break;
719  default: assert(0);
720  }
721  return normalBinding;
722}
723
724
725static SoTextureCoordinateBinding* createTexCoordBinding(SbBool useIndexing)
726{
727  SoTextureCoordinateBinding *b = new SoTextureCoordinateBinding;
728  b->value.setValue(useIndexing ? SoTextureCoordinateBinding::PER_VERTEX_INDEXED :
729                    SoTextureCoordinateBinding::PER_VERTEX);
730  return b;
731}
732
733
734static SoTexture2::Model convertTexEnvMode(osg::TexEnv::Mode osgMode, bool useIvExtensions)
735{
736  switch (osgMode) {
737  case GL_MODULATE: return SoTexture2::MODULATE;
738  case GL_REPLACEreturn (SoTexture2::Model)(useIvExtensions ? GL_REPLACE : GL_MODULATE);
739  case GL_BLEND:    return SoTexture2::BLEND;
740  case GL_DECAL:    return SoTexture2::DECAL;
741  default: assert(0); return SoTexture2::MODULATE;
742  }
743}
744
745
746
747static SoTexture2::Wrap convertTextureWrap(osg::Texture::WrapMode osgWrap)
748{
749  // notes on support of CLAMP_TO_BORDER, CLAMP_TO_EDGE, and MIRRORED_REPEAT:
750  // original SGI Inventor: no
751  // Coin: no (until current version Coin 2.5.0b3)
752  // TGS Inventor: introduced in TGS Inventor 5.0 (available in SoTexture class)
753
754  // note: Coin (since 2.0) uses CLAMP_TO_EDGE for rendering if SoTexture2::CLAMP is specified.
755
756  switch (osgWrap) {
757    case osg::Texture::CLAMP:
758    case osg::Texture::CLAMP_TO_BORDER:
759    case osg::Texture::CLAMP_TO_EDGE: return SoTexture2::CLAMP;
760    case osg::Texture::REPEAT:
761    case osg::Texture::MIRROR: return SoTexture2::REPEAT;
762    default: assert(0); return SoTexture2::REPEAT;
763  }
764}
765
766
767static void setSoTransform(SoTransform *tr, const osg::Vec3 &translation, const osg::Quat &rotation,
768                           const osg::Vec3 &scale = osg::Vec3(1.,1.,1.))
769{
770  tr->translation.setValue(translation.ptr());
771  tr->rotation.setValue(rotation.x(), rotation.y(), rotation.z(), rotation.w());
772  tr->scaleFactor.setValue(scale.ptr());
773  //tr->scaleCenter.setValue(osg::Vec3f(node.getPivotPoint())); <- testing required on this line
774}
775
776
777static bool updateMode(bool &flag, const osg::StateAttribute::GLModeValue value)
778{
779  if (value & osg::StateAttribute::INHERIT)  return flag;
780  else  return (flag = (value & osg::StateAttribute::ON));
781}
782
783
784ConvertToInventor::InventorState* ConvertToInventor::createInventorState(const osg::StateSet *ss)
785{
786  // Push on stack
787  const InventorState *ivPrevState = &ivStack.top();
788  ivStack.push(*ivPrevState);
789  InventorState *ivState = &ivStack.top();
790
791  // Inventor graph
792  ivState->ivHead = new SoSeparator;
793  ivPrevState->ivHead->addChild(ivState->ivHead);
794
795  if (ss) {
796
797    //
798    // Lighting
799    //
800
801    // enable/disable lighting
802    updateMode(ivState->osgLighting, ss->getMode(GL_LIGHTING));
803    if (ivState->osgLighting != ivPrevState->osgLighting) {
804      SoLightModel *lm = new SoLightModel;
805      lm->model = (ivState->osgLighting) ? SoLightModel::PHONG : SoLightModel::BASE_COLOR;
806      ivState->ivHead->addChild(lm);
807    }
808
809    // two-sided lighting
810    const osg::LightModel *osgLM = dynamic_cast<const osg::LightModel*>(ss->getAttribute(osg::StateAttribute::LIGHTMODEL));
811    if (osgLM)
812      ivState->osgTwoSided = osgLM->getTwoSided();
813
814    // front face
815    const osg::FrontFace *osgFF = dynamic_cast<const osg::FrontFace*>(ss->getAttribute(osg::StateAttribute::FRONTFACE));
816    if (osgFF)
817      ivState->osgFrontFace = osgFF->getMode();
818
819    // face culling
820    updateMode(ivState->osgCullFaceEnabled, ss->getMode(GL_CULL_FACE));
821    const osg::CullFace *osgCF = dynamic_cast<const osg::CullFace*>(ss->getAttribute(osg::StateAttribute::CULLFACE));
822    if (osgCF)
823      ivState->osgCullFace = osgCF->getMode();
824
825    // detect state change
826    if (ivState->osgTwoSided != ivPrevState->osgTwoSided ||
827        ivState->osgFrontFace != ivPrevState->osgFrontFace ||
828        ivState->osgCullFaceEnabled != ivPrevState->osgCullFaceEnabled ||
829        ivState->osgCullFace != ivPrevState->osgCullFace) {
830
831      // new SoShapeHints
832      SoShapeHints *sh = new SoShapeHints;
833      if (ivState->osgTwoSided) {
834        // warn if face culling is on
835        if (ivState->osgCullFaceEnabled)
836          OSG_WARN << "IvWriter: Using face culling and two-sided lighting together! "
837                                    "Ignoring face culling." << std::endl;
838
839        // set two-sided lighting and backface culling off
840        sh->vertexOrdering = ivState->osgFrontFace==osg::FrontFace::COUNTER_CLOCKWISE ?
841                             SoShapeHints::COUNTERCLOCKWISE : SoShapeHints::CLOCKWISE;
842        sh->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;
843      }
844      else {
845        // set one-sided lighting and backface optionally
846        if (ivState->osgCullFaceEnabled) {
847
848          // determine vertex ordering
849          bool ccw = ivState->osgFrontFace==osg::FrontFace::COUNTER_CLOCKWISE;
850          if (ivState->osgCullFace != osg::CullFace::BACK)  ccw = !ccw;
851
852          if (ccw)
853            // Warn if culling the lit faces while rendering unlit faces.
854            // Inventor does not support this setup and it lits the unculled faces only.
855            OSG_WARN << "IvWriter: Culling was set in a way that one-sided lighting will lit the culled sides of faces. "
856                                      "Using lighting on correct faces." << std::endl;
857
858          // face culling on
859          sh->vertexOrdering = ccw ? SoShapeHints::COUNTERCLOCKWISE : SoShapeHints::CLOCKWISE;
860          sh->shapeType = SoShapeHints::SOLID;
861        }
862        else
863          // no face culling
864          sh->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;
865      }
866      ivState->ivHead->addChild(sh);
867    }
868
869    //
870    // Texturing
871    //
872    // FIXME: handle 1D and 3D textures
873
874    // get OSG state
875    ivState->osgTexture = dynamic_cast<const osg::Texture*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
876    ivState->osgTexEnv = dynamic_cast<const osg::TexEnv*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXENV));
877    updateMode(ivState->osgTexture2Enabled, ss->getTextureMode(0, GL_TEXTURE_2D));
878
879    // detect state changes
880    if (ivState->osgTexture2Enabled != ivPrevState->osgTexture2Enabled ||
881        ivState->osgTexture != ivPrevState->osgTexture ||
882        ivState->osgTexEnv != ivPrevState->osgTexEnv) {
883
884      if (!ivState->osgTexture2Enabled ||
885          ivState->osgTexture==NULL || ivState->osgTexture->getImage(0)==NULL)
886
887        // empty texture disables texturing
888        ivState->ivTexture = new SoTexture2;
889
890      else {
891
892        // reuse texture if possible
893        ivState->ivTexture = ivTexturesMap[ivState->osgTexture][ivState->osgTexEnv];
894
895        // if nothing for reusing, create new
896        if (!ivState->ivTexture) {
897
898          // create texture
899          ivState->ivTexture = new SoTexture2;
900          ivTexturesMap[ivState->osgTexture][ivState->osgTexEnv] = ivState->ivTexture;
901
902          // texture file name
903          const std::string &textureName = ivState->osgTexture->getImage(0)->getFileName();
904          ivState->ivTexture->filename.setValue(textureName.c_str()); // FIXME: handle inlined texture data in the files
905
906          // wrap
907          ivState->ivTexture->wrapS.setValue(convertTextureWrap(
908                                             ivState->osgTexture->getWrap(osg::Texture::WRAP_S)));
909          ivState->ivTexture->wrapT.setValue(convertTextureWrap(
910                                             ivState->osgTexture->getWrap(osg::Texture::WRAP_T)));
911
912          // texture environment
913          if (ivState->osgTexEnv) {
914            ivState->ivTexture->model.setValue(convertTexEnvMode(
915                                               ivState->osgTexEnv->getMode(), useIvExtensions));
916            osg::Vec4 color = ivState->osgTexEnv->getColor();
917            ivState->ivTexture->blendColor.setValue(color.r(), color.g(), color.b());
918          }
919
920          // notes on support of borderColor and borderWidth:
921          // original SGI Inventor: no
922          // Coin: no (until current version 2.5.0b3)
923          // TGS Inventor: introduced in version 5.0 (as SoTexture::borderColor)
924
925          // FIXME: implement support for texture filtering
926        }
927      }
928    }
929
930    // Texture coordinate generation
931    updateMode(ivState->osgTexGenS, ss->getTextureMode(0, GL_TEXTURE_GEN_S));
932    updateMode(ivState->osgTexGenT, ss->getTextureMode(0, GL_TEXTURE_GEN_T));
933    ivState->osgTexGen = dynamic_cast<const osg::TexGen*>(ss->getTextureAttribute(0, osg::StateAttribute::TEXGEN));
934
935    // Material parameters
936    const osg::Material *osgMaterial = dynamic_cast<const osg::Material*>(ss->getAttribute(osg::StateAttribute::MATERIAL));
937    if (osgMaterial)
938      ivState->osgMaterial = osgMaterial;
939
940    if (ivState->osgMaterial != ivPrevState->osgMaterial) {
941
942      ivState->ivMaterial = new SoMaterial;
943      assert(ivState->osgMaterial);
944
945      // Warn if using two side materials
946      // FIXME: The geometry can be probably doubled, or some estimation can be made
947      // whether only front or back faces are used.
948      if (ivState->osgMaterial->getAmbientFrontAndBack() == false ||
949          ivState->osgMaterial->getDiffuseFrontAndBack() == false ||
950          ivState->osgMaterial->getSpecularFrontAndBack() == false ||
951          ivState->osgMaterial->getEmissionFrontAndBack() == false ||
952          ivState->osgMaterial->getShininessFrontAndBack() == false)
953      {
954        OSG_WARN << "IvWriter: Model contains different materials for front and "
955                                  "back faces. This is not handled properly. Using front material only." << std::endl;
956      }
957
958      // Convert colors
959      // OSG represents colors by: Vec3, Vec4,Vec4ub
960      // Inventor by: uint32 (RGBA, by SoPackedColor), SbColor (Vec3f, by SoMaterial and SoBaseColor)
961      // note: Inventor can use DIFFUSE component inside a shape only. Material is set for whole shape.
962      // Although SoMaterial is used only, SoPackedColor may bring some possibilities on per-vertex
963      // alpha and SoBaseColor may be useful on pre-lit scene.
964      if (ivState->osgMaterial->getColorMode() != osg::Material::DIFFUSE &&
965          ivState->osgMaterial->getColorMode() != osg::Material::OFF)
966      {
967
968        if (ivState->osgMaterial->getColorMode() == osg::Material::AMBIENT_AND_DIFFUSE)
969        {
970            OSG_WARN << "IvWriter: The model is using AMBIENT_AND_DIFFUSE material "
971                                    "mode while Inventor supports DIFFUSE mode only. "
972                                    "The model colors may not much exactly." << std::endl;
973        }
974        else
975        {
976            OSG_WARN << "IvWriter: The model is not using DIFFUSE material mode and "
977                                    "Inventor supports DIFFUSE mode only. "
978                                    "The model colors may not be correct." << std::endl;
979        }
980      }
981
982      // Convert material components
983      // FIXME: Transparency can be specified for each component in OSG
984      // and just globally in Inventor.
985      // Solutions? It can be averaged or just diffuse can be used.
986      ((SoMaterial*)ivState->ivMaterial)->ambientColor.setValue(osgMaterial->getAmbient(
987        osgMaterial->getAmbientFrontAndBack()   ? osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr());
988      ((SoMaterial*)ivState->ivMaterial)->diffuseColor.setValue(osgMaterial->getDiffuse(
989        osgMaterial->getDiffuseFrontAndBack()   ? osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr());
990      ((SoMaterial*)ivState->ivMaterial)->specularColor.setValue(osgMaterial->getSpecular(
991        osgMaterial->getSpecularFrontAndBack()  ? osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr());
992      ((SoMaterial*)ivState->ivMaterial)->emissiveColor.setValue(osgMaterial->getEmission(
993        osgMaterial->getEmissionFrontAndBack()  ? osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr());
994      ((SoMaterial*)ivState->ivMaterial)->shininess.setValue(osgMaterial->getShininess(
995        osgMaterial->getShininessFrontAndBack() ? osg::Material::FRONT_AND_BACK : osg::Material::FRONT));
996      ((SoMaterial*)ivState->ivMaterial)->transparency.setValue(1.f - osgMaterial->getDiffuse(
997        osgMaterial->getDiffuseFrontAndBack()   ? osg::Material::FRONT_AND_BACK : osg::Material::FRONT).a());
998    }
999
1000    // note on "headlight":
1001    // OSG is using HEADLIGHT or SKY_LIGHT. In both cases it sets these defaults:
1002    // osg::LightModel::ambientIntensity(0.1, 0.1, 0.1, 1.0);
1003    // osg::Light::num(0)
1004    // osg::Light::ambient(0,0,0,1)
1005    // osg::Light::diffuse(0.8,0.8,0.8, 1)
1006    // osg::Light::specular(1,1,1,1)
1007    //
1008    // Inventor uses different settings:
1009    // SoEnvironment::ambientIntensity(0.2)
1010    // SoEnvironment::ambientColor(1,1,1)
1011    // SoDirectionalLight::intensity(1)
1012    // SoDirectionalLight::color(1,1,1)
1013    //
1014    // note on Inventor light handling:
1015    // ambient is set to 0,0,0,1
1016    // diffuse to color*intensity
1017    // specular to color*intensity
1018
1019
1020  #ifdef __COIN__
1021    //
1022    // Handle transparency
1023    //
1024    // OSG supports GL_BLEND and GL_ALPHA_TEST
1025    // Related classes: BlendFunc, BlendEquation, BlendColor, AlphaFunc
1026    //
1027    // Inventor is more difficult and not so robust. According to Inventor 2.1 standard,
1028    // just SoMaterial::transparency, SoTexture2 with alpha channel carry transparency information
1029    // that is controlled by SoGLRenderAction::transparency type that is set to SCREEN_DOOR by default
1030    // (dither pattern). So, if the user does not select better transparency type, there is no
1031    // possiblity to control transparency type from file format.
1032    //
1033    // However, SoTransparencyType node was introduced to overcome this historical limitation
1034    // because transparency was performance expensive long time ago.
1035    // Support by different Inventor branches:
1036    //
1037    // SGI Inventor: no
1038    // Coin: since 2.0
1039    // TGS Inventor: since 5.0
1040    //
1041    // Alpha test was introduced in TGS 4.0, but as SoGLRenderArea::setAlphaTest. So, no direct
1042    // control in the file format.
1043    //
1044    // Conclusion:
1045    // - Alpha test is not supported and all pixels will be drawn
1046    // - Blending - current strategy is following:
1047    //     ADD x BLEND - ADD is used if destination-blending-factor is GL_ONE
1048    //     DELAYED rendering is used if transparency bin is used by OSG
1049    //     SORTED_OBJECT is used if ...
1050    //
1051
1052    updateMode(ivState->osgBlendEnabled, ss->getMode(GL_BLEND));
1053    ivState->osgBlendFunc = dynamic_cast<const osg::BlendFunc*>(ss->getAttribute(osg::StateAttribute::BLENDFUNC));
1054
1055    if (useIvExtensions)
1056      if (ivState->osgBlendEnabled != ivPrevState->osgBlendEnabled ||
1057          ivState->osgBlendFunc != ivPrevState->osgBlendFunc ||
1058          (ivState->osgBlendFunc && ivPrevState->osgBlendFunc &&
1059          ivState->osgBlendFunc->getDestinationRGB() != ivPrevState->osgBlendFunc->getDestinationRGB())) {
1060
1061        const SoTransparencyType::Type transparencyTable[8] = {
1062          SoTransparencyType::BLEND,         SoTransparencyType::ADD,
1063          SoTransparencyType::DELAYED_BLEND, SoTransparencyType::DELAYED_ADD,
1064          // FIXME: add sorted modes and test previous four
1065        };
1066
1067        int index = 0;
1068        if (!ivState->osgBlendFunc)  index |= 0;
1069        else  index = (ivState->osgBlendFunc->getDestinationRGB() == osg::BlendFunc::ONE) ? 1 : 0;
1070        index |= (ss->getRenderingHint() == osg::StateSet::TRANSPARENT_BIN) ? 2 : 0;
1071
1072        SoTransparencyType *ivTransparencyType = new SoTransparencyType;
1073        ivTransparencyType->value = transparencyTable[index];
1074        ivState->ivHead->addChild(ivTransparencyType);
1075      }
1076  #endif
1077
1078  }
1079
1080  // ref Inventor nodes that are required when processing Drawables
1081  if (ivState->ivTexture)
1082    ivState->ivTexture->ref();
1083  if (ivState->ivMaterial)
1084    ivState->ivMaterial->ref();
1085
1086  return ivState;
1087}
1088
1089
1090void ConvertToInventor::popInventorState()
1091{
1092  InventorState *ivState = &ivStack.top();
1093
1094  // unref Inventor nodes
1095  if (ivState->ivTexture)
1096    ivState->ivTexture->unref();
1097  if (ivState->ivMaterial)
1098    ivState->ivMaterial->unref();
1099
1100  ivStack.pop();
1101}
1102
1103
1104static bool processPrimitiveSet(const osg::Geometry *g, const osg::PrimitiveSet *pset,
1105                                osg::UIntArray *drawElemIndices, bool needSeparateTriangles,
1106                                int elementsCount, int primSize, const int startIndex, int stopIndex,
1107                                int &normalIndex, int &colorIndex,
1108                                SoNode *ivCoords, SoNormal *ivNormals, SoNode *ivMaterial,
1109                                SoNode *ivTexCoords, SoNode *ivTexture, SoShape *shape,
1110                                SoSeparator *&indexedRoot, SoSeparator *&nonIndexedRoot)
1111{
1112  bool ok = true;
1113  const osg::DrawArrayLengths *drawArrayLengths =
1114    (elementsCount == -1) ? dynamic_cast<const osg::DrawArrayLengths*>(pset) : NULL;
1115
1116  int drawArrayLengthsElems = 0;
1117
1118  if (drawArrayLengths) {
1119
1120    // compute elementsCount
1121    elementsCount = 0;
1122    drawArrayLengthsElems = 0;
1123    for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin();
1124        primItr!=drawArrayLengths->end();
1125        ++primItr, drawArrayLengthsElems++)
1126      elementsCount += *primItr;
1127
1128    // update stopIndex
1129    stopIndex = startIndex + elementsCount;
1130  }
1131
1132  // NonIndexed data for nonIndexed shapes
1133  SoNode *nonIndexedCoords = NULL;
1134  SoNode *nonIndexedTexCoords = NULL;
1135  SoNormal *nonIndexedNormals = NULL;
1136  SoNode *nonIndexedMaterial = NULL;
1137
1138  // Normal indexing
1139  int normalStart = g->getNormalBinding() == osg::Geometry::BIND_PER_VERTEX ? startIndex : normalIndex;
1140  int numNormalsUsed = 0;
1141  switch (g->getNormalBinding()) {
1142  case osg::Geometry::BIND_OFF: // FIXME: what is meaning of OFF value?
1143  case osg::Geometry::BIND_OVERALL:           numNormalsUsed = 0; break;
1144  case osg::Geometry::BIND_PER_PRIMITIVE_SET: numNormalsUsed = 1; break;
1145  case osg::Geometry::BIND_PER_PRIMITIVE:     numNormalsUsed = primSize!=0 ? (stopIndex-startIndex)/primSize :
1146                                                (drawArrayLengths ? drawArrayLengths->size() : 1); break;
1147  case osg::Geometry::BIND_PER_VERTEX:        numNormalsUsed = stopIndex-startIndex; break;
1148  }
1149  normalIndex += numNormalsUsed;
1150
1151  // Color indexing
1152  int colorStart = g->getColorBinding() == osg::Geometry::BIND_PER_VERTEX ? startIndex : colorIndex;
1153  int numColorsUsed = 0;
1154  switch (g->getColorBinding()) {
1155  case osg::Geometry::BIND_OFF:
1156  case osg::Geometry::BIND_OVERALL:           numColorsUsed = 0; break;
1157  case osg::Geometry::BIND_PER_PRIMITIVE_SET: numColorsUsed = 1; break;
1158  case osg::Geometry::BIND_PER_PRIMITIVE:     numColorsUsed = primSize!=0 ? (stopIndex-startIndex)/primSize :
1159                                                (drawArrayLengths ? drawArrayLengths->size() : 1); break;
1160  case osg::Geometry::BIND_PER_VERTEX:        numColorsUsed = stopIndex-startIndex; break;
1161  }
1162  colorIndex += numColorsUsed;
1163
1164  if (shape->isOfType(SoIndexedShape::getClassTypeId())) {
1165
1166    // Convert to SoIndexedShape
1167    processIndices(g->getVertexIndices(), drawElemIndices, ((SoIndexedShape*)shape)->coordIndex,
1168                   startIndex, stopIndex, primSize);
1169
1170    if (ivNormals)
1171      processIndices(g->getNormalIndices(), drawElemIndices, ((SoIndexedShape*)shape)->normalIndex,
1172                     normalStart, normalStart+(numNormalsUsed==0 ? 1 : numNormalsUsed),
1173                     g->getNormalBinding()==osg::Geometry::BIND_PER_VERTEX ? primSize : 0);
1174
1175    if (ivMaterial)
1176      processIndices(g->getColorIndices(), drawElemIndices, ((SoIndexedShape*)shape)->materialIndex,
1177                     colorStart, colorStart+(numColorsUsed==0 ? 1 : numColorsUsed),
1178                     g->getColorBinding()==osg::Geometry::BIND_PER_VERTEX ? primSize : 0);
1179
1180    if (ivTexCoords && !ivTexCoords->isOfType(SoTextureCoordinateFunction::getClassTypeId()))
1181      processIndices(g->getTexCoordIndices(0), drawElemIndices, ((SoIndexedShape*)shape)->textureCoordIndex,
1182                     startIndex, stopIndex, primSize);
1183
1184    // Post-processing for DrawArrayLengths
1185    if (drawArrayLengths && primSize==0 && drawArrayLengths->size()>=2) {
1186
1187      postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->coordIndex);
1188
1189      if (ivNormals && g->getNormalBinding()==osg::Geometry::BIND_PER_VERTEX)
1190        postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->normalIndex);
1191
1192      if (ivMaterial && g->getColorBinding()==osg::Geometry::BIND_PER_VERTEX)
1193        postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->materialIndex);
1194
1195      if (ivTexCoords && !ivTexCoords->isOfType(SoTextureCoordinateFunction::getClassTypeId()))
1196        postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->textureCoordIndex);
1197    }
1198
1199    if (needSeparateTriangles)
1200      postProcessTriangleSeparation((SoIndexedShape*)shape, (osg::PrimitiveSet::Mode)pset->getMode(),
1201                                    g->getNormalBinding(), g->getColorBinding());
1202
1203  } else {
1204
1205    // Convert to SoNonIndexedShape
1206
1207    assert(shape->isOfType(SoNonIndexedShape::getClassTypeId()) && "Shape must be non-indexed.");
1208
1209    int i,n = stopIndex-startIndex;
1210
1211    // create alternate coordinates
1212    if (ivCoords->isOfType(SoCoordinate4::getClassTypeId()))
1213    {
1214      nonIndexedCoords = new SoCoordinate4;
1215      if (ok) {
1216        ((SoCoordinate4*)nonIndexedCoords)->point.setNum(n);
1217        ok = ivProcessArray<SbVec4f,SoMFVec4f>(g->getVertexIndices(),
1218                                             drawElemIndices,
1219                                             &((SoCoordinate4*)nonIndexedCoords)->point,
1220                                             &((SoCoordinate4*)ivCoords)->point,
1221                                             startIndex, n);
1222      }
1223    } else {
1224      nonIndexedCoords = new SoCoordinate3;
1225      if (ok) {
1226        ((SoCoordinate3*)nonIndexedCoords)->point.setNum(n);
1227        ok = ivProcessArray<SbVec3f,SoMFVec3f>(g->getVertexIndices(),
1228                                             drawElemIndices,
1229                                             &((SoCoordinate3*)nonIndexedCoords)->point,
1230                                             &((SoCoordinate3*)ivCoords)->point,
1231                                             startIndex, n);
1232      }
1233    }
1234
1235    // create alternate texture coordinates
1236    if (ivTexCoords)
1237    {
1238      if (ivTexCoords->isOfType(SoTextureCoordinate2::getClassTypeId()))
1239      {
1240        nonIndexedTexCoords = new SoTextureCoordinate2;
1241        if (ok)
1242        {
1243          ((SoTextureCoordinate2*)nonIndexedTexCoords)->point.setNum(n);
1244          ok = ivProcessArray<SbVec2f,SoMFVec2f>(g->getTexCoordIndices(0),
1245                                               drawElemIndices,
1246                                               &((SoTextureCoordinate2*)nonIndexedTexCoords)->point,
1247                                               &((SoTextureCoordinate2*)ivTexCoords)->point,
1248                                               startIndex, n);
1249        }
1250      } else
1251#ifdef __COIN__
1252      if (ivTexCoords->isOfType(SoTextureCoordinate3::getClassTypeId()))
1253      {
1254        nonIndexedTexCoords = new SoTextureCoordinate3;
1255        if (ok)
1256        {
1257          ((SoTextureCoordinate3*)nonIndexedTexCoords)->point.setNum(n);
1258          ok = ivProcessArray<SbVec3f,SoMFVec3f>(g->getTexCoordIndices(0),
1259                                               drawElemIndices,
1260                                               &((SoTextureCoordinate3*)nonIndexedTexCoords)->point,
1261                                               &((SoTextureCoordinate3*)ivCoords)->point,
1262                                               startIndex, n);
1263        }
1264      }
1265      else
1266#endif  // __COIN__
1267        nonIndexedTexCoords = ivTexCoords;
1268    }
1269
1270    // create alternate normals
1271    if (ivNormals) {
1272      nonIndexedNormals = new SoNormal;
1273      if (ok) {
1274        nonIndexedNormals->vector.setNum(numNormalsUsed==0 ? 1 : numNormalsUsed);
1275        ok = ivProcessArray<SbVec3f,SoMFVec3f>(g->getNormalIndices(),
1276                                             g->getNormalBinding()==osg::Geometry::BIND_PER_VERTEX ? drawElemIndices : NULL,
1277                                             &nonIndexedNormals->vector, &ivNormals->vector,
1278                                             normalStart, numNormalsUsed==0 ? 1 : numNormalsUsed);
1279      }
1280    }
1281
1282    // create alternate material
1283    if (ivMaterial) {
1284      SoMFColor *dstColorField;
1285      if (ivMaterial->isOfType(SoMaterial::getClassTypeId())) {
1286        nonIndexedMaterial = new SoMaterial;
1287        dstColorField = &((SoMaterial*)nonIndexedMaterial)->diffuseColor;
1288      } else {
1289        nonIndexedMaterial = new SoBaseColor;
1290        dstColorField = &((SoBaseColor*)nonIndexedMaterial)->rgb;
1291      }
1292      if (ok) {
1293        // FIXME: diffuse only?
1294        SoMFColor *srcColorField = (ivMaterial->isOfType(SoMaterial::getClassTypeId())) ?
1295                                   &((SoMaterial*)ivMaterial)->diffuseColor :
1296                                   &((SoBaseColor*)ivMaterial)->rgb;
1297        dstColorField->setNum(numColorsUsed==0 ? 1 : numColorsUsed);
1298        ok = ivProcessArray<SbColor,SoMFColor>(g->getColorIndices(),
1299                                            g->getColorBinding()==osg::Geometry::BIND_PER_VERTEX ? drawElemIndices : NULL,
1300                                            dstColorField, srcColorField,
1301                                            colorStart, numColorsUsed==0 ? 1 : numColorsUsed);
1302      }
1303    }
1304
1305    if (shape->isOfType(SoPointSet::getClassTypeId()))
1306      ((SoPointSet*)shape)->numPoints.setValue(elementsCount); else
1307
1308    if (shape->isOfType(SoLineSet::getClassTypeId())) {
1309      switch (pset->getMode()) {
1310      case GL_LINES:
1311          assert(elementsCount % 2 == 0 && "elementsCount is not multiple of 2.");
1312          n = elementsCount/2;
1313          ((SoLineSet*)shape)->numVertices.setNum(n);
1314          for (i=0; i<n; i++)
1315            ((SoLineSet*)shape)->numVertices.set1Value(i, 2);
1316          break;
1317      case GL_LINE_STRIP:
1318          if (drawArrayLengths) {
1319            ((SoLineSet*)shape)->numVertices.setNum(drawArrayLengthsElems);
1320            int i=0;
1321            for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin();
1322                primItr!=drawArrayLengths->end();
1323                ++primItr, i++)
1324              ((SoLineSet*)shape)->numVertices.set1Value(i, *primItr);
1325          } else {
1326            ((SoLineSet*)shape)->numVertices.setNum(1);
1327            ((SoLineSet*)shape)->numVertices.set1Value(0, elementsCount);
1328          }
1329          break;
1330      default:
1331          OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
1332          assert(0);
1333      }
1334    } else
1335
1336    if (shape->isOfType(SoTriangleStripSet::getClassTypeId())) {
1337      switch (pset->getMode()) {
1338      case GL_TRIANGLES:
1339          n = elementsCount/3;
1340          assert(n*3 == elementsCount && "elementsCount is not multiple of 3.");
1341          ((SoTriangleStripSet*)shape)->numVertices.setNum(n);
1342          for (i=0; i<n; i++)
1343            ((SoTriangleStripSet*)shape)->numVertices.set1Value(i, 3);
1344          break;
1345      case GL_TRIANGLE_STRIP:
1346          if (drawArrayLengths) {
1347            ((SoTriangleStripSet*)shape)->numVertices.setNum(drawArrayLengthsElems);
1348            int i=0;
1349            for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin();
1350                primItr!=drawArrayLengths->end();
1351                ++primItr, i++)
1352              ((SoTriangleStripSet*)shape)->numVertices.set1Value(i, *primItr);
1353          } else {
1354            ((SoTriangleStripSet*)shape)->numVertices.setNum(1);
1355            ((SoTriangleStripSet*)shape)->numVertices.set1Value(0, elementsCount);
1356          }
1357          break;
1358      case GL_TRIANGLE_FAN:
1359          OSG_WARN << "IvWriter: GL_TRIANGLE_FAN NOT IMPLEMENTED" << std::endl;
1360          ((SoTriangleStripSet*)shape)->numVertices.setNum(1);
1361          ((SoTriangleStripSet*)shape)->numVertices.set1Value(0, elementsCount);
1362          break;
1363      case GL_QUAD_STRIP:
1364          assert(elementsCount % 2 == 0 && "elementsCount is not multiple of 2.");
1365          if (drawArrayLengths) {
1366            ((SoTriangleStripSet*)shape)->numVertices.setNum(drawArrayLengthsElems);
1367            int i=0;
1368            for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin();
1369                primItr!=drawArrayLengths->end();
1370                ++primItr, i++)
1371              ((SoTriangleStripSet*)shape)->numVertices.set1Value(i, *primItr);
1372          } else {
1373            ((SoTriangleStripSet*)shape)->numVertices.setNum(1);
1374            ((SoTriangleStripSet*)shape)->numVertices.set1Value(0, elementsCount);
1375          }
1376          break;
1377      default:
1378          OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
1379          assert(0);
1380      }
1381    } else
1382
1383    if (shape->isOfType(SoFaceSet::getClassTypeId())) {
1384      switch (pset->getMode()) {
1385      case GL_QUADS:
1386          n = elementsCount/4;
1387          assert(n*4 == elementsCount && "elementsCount is not multiple of 4.");
1388          ((SoFaceSet*)shape)->numVertices.setNum(n);
1389          for (i=0; i<n; i++)
1390            ((SoFaceSet*)shape)->numVertices.set1Value(i, 4);
1391          break;
1392      case GL_POLYGON:
1393          if (drawArrayLengths) {
1394            ((SoFaceSet*)shape)->numVertices.setNum(drawArrayLengthsElems);
1395            int i=0;
1396            for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin();
1397                primItr!=drawArrayLengths->end();
1398                ++primItr, i++)
1399              ((SoFaceSet*)shape)->numVertices.set1Value(i, *primItr);
1400          } else {
1401            ((SoFaceSet*)shape)->numVertices.setNum(1);
1402            ((SoFaceSet*)shape)->numVertices.set1Value(0, elementsCount);
1403          }
1404          break;
1405      default:
1406          OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
1407          assert(0);
1408      }
1409    } else {
1410      OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
1411      assert(0 && "Unknown non-indexed shape type.");
1412    }
1413  }
1414
1415  // Construct graph
1416
1417  // Each osg::Drawable will have its own SoSeparator (render caching, culling, etc.)
1418  SoSeparator *sep = new SoSeparator;
1419  if (nonIndexedCoords) {
1420    assert(shape->isOfType(SoNonIndexedShape::getClassTypeId()) && "Not nonIndexed shape.");
1421
1422    if (!ok) {
1423
1424      // handle errors
1425      nonIndexedCoords->ref();
1426      nonIndexedCoords->unref();
1427      if (nonIndexedTexCoords) { nonIndexedTexCoords->ref(); nonIndexedTexCoords->unref(); }
1428      if (nonIndexedNormals) { nonIndexedNormals->ref(); nonIndexedNormals->unref(); }
1429      nonIndexedMaterial->ref();
1430      nonIndexedMaterial->unref();
1431      shape->ref();
1432      shape->unref();
1433      sep->ref();
1434      sep->unref();
1435    } else {
1436
1437      // make scene graph
1438      if (nonIndexedRoot == NULL) {
1439        nonIndexedRoot = new SoSeparator;
1440        if (ivTexture)  nonIndexedRoot->addChild(ivTexture);
1441        if (ivTexture)  nonIndexedRoot->addChild(createTexCoordBinding(FALSE));
1442        nonIndexedRoot->addChild(createMaterialBinding(g, FALSE));
1443        if (ivNormals)  nonIndexedRoot->addChild(createNormalBinding(g, FALSE));
1444      }
1445      if (nonIndexedMaterial)  sep->addChild(nonIndexedMaterial);
1446      sep->addChild(nonIndexedCoords);
1447      if (nonIndexedNormals)  sep->addChild(nonIndexedNormals);
1448      if (nonIndexedTexCoords)  sep->addChild(nonIndexedTexCoords);
1449      sep->addChild(shape);
1450      nonIndexedRoot->addChild(sep);
1451    }
1452  } else {
1453    assert(shape->isOfType(SoIndexedShape::getClassTypeId()) && "Not indexed shape.");
1454    assert(nonIndexedCoords==NULL && nonIndexedNormals==NULL && nonIndexedMaterial==NULL);
1455    if (indexedRoot == NULL) {
1456      indexedRoot = new SoSeparator;
1457      if (ivTexture)  indexedRoot->addChild(ivTexture);
1458      if (ivTexture)  indexedRoot->addChild(createTexCoordBinding(TRUE));
1459      if (ivMaterial)  indexedRoot->addChild(ivMaterial);
1460      indexedRoot->addChild(createMaterialBinding(g, TRUE));
1461      indexedRoot->addChild(ivCoords);
1462      if (ivNormals)  indexedRoot->addChild(ivNormals);
1463      if (ivNormals)  indexedRoot->addChild(createNormalBinding(g, TRUE));
1464      if (ivTexCoords)  indexedRoot->addChild(ivTexCoords);
1465    }
1466    sep->addChild(shape);
1467    indexedRoot->addChild(sep);
1468  }
1469
1470  return ok;
1471}
1472
1473
1474void ConvertToInventor::processGeometry(const osg::Geometry *g, InventorState *ivState)
1475{
1476  int normalIndex = 0;
1477  int colorIndex = 0;
1478
1479  // Inventor scene graph roots
1480  SoSeparator *indexedRoot = NULL;
1481  SoSeparator *nonIndexedRoot = NULL;
1482
1483  // Active material
1484  SoMaterial *ivStateMaterial = ivState->ivMaterial;
1485  SoNode *ivMaterial = NULL;
1486  if (ivState->osgLighting || vrml1Conversion)
1487    // SoMaterial
1488    if (g->getColorArray())
1489      if (ivStateMaterial)  ivMaterial = ivStateMaterial->copy();
1490      else  ivMaterial = new SoMaterial; // FIXME: check default values of SoMaterial and OSG lighting
1491    else
1492      if (ivStateMaterial)  ivMaterial = ivStateMaterial;
1493      else  ivMaterial = NULL;
1494  else
1495    // SoBaseColor
1496    if (g->getColorArray())
1497      if (ivStateMaterial) {
1498        ivMaterial = new SoBaseColor;
1499        ((SoBaseColor*)ivMaterial)->rgb.setValue(ivStateMaterial->diffuseColor[0]); // copy first value
1500      } else
1501        ivMaterial = new SoBaseColor; // FIXME: check default values of SoBaseColor and OSG pre-lit scene
1502    else
1503      if (ivStateMaterial) {
1504        ivMaterial = new SoBaseColor;
1505        ((SoBaseColor*)ivMaterial)->rgb.setValue(ivStateMaterial->diffuseColor[0]); // copy first value
1506      } else
1507        ivMaterial = NULL;
1508
1509  // Convert color array into the SoMaterial
1510  if (g->getColorArray()) {
1511    assert(ivMaterial);
1512
1513    // Choose correct color field
1514    SoMFColor *colorField;
1515    if (ivMaterial->isOfType(SoMaterial::getClassTypeId())) {
1516      if (vrml1Conversion && ivState->osgLighting==false) {
1517
1518        // special case of pre-lit VRML1 scene
1519        ((SoMaterial*)ivMaterial)->ambientColor.setValue(0.f,0.f,0.f);
1520        ((SoMaterial*)ivMaterial)->diffuseColor.setValue(0.f,0.f,0.f);
1521        ((SoMaterial*)ivMaterial)->specularColor.setValue(0.f,0.f,0.f);
1522        colorField = &((SoMaterial*)ivMaterial)->emissiveColor;
1523      } else
1524        // regular diffuse color
1525        colorField = &((SoMaterial*)ivMaterial)->diffuseColor;
1526    } else
1527      // Using of SoBaseColor
1528      colorField = &((SoBaseColor*)ivMaterial)->rgb;
1529
1530
1531    // Color array with material
1532    if (ivState->osgMaterial == NULL ||
1533        ivState->osgMaterial->getColorMode() == osg::Material::DIFFUSE ||
1534        ivState->osgMaterial->getColorMode() == osg::Material::AMBIENT_AND_DIFFUSE)
1535      osgArray2ivMField(g->getColorArray(), *colorField);
1536    else; // FIXME: implement some workaround for non-diffuse cases?
1537          // note: Warning was already shown in createInventorState().
1538          // note2: There is no effect to convert SoMaterial::[ambient|specular|emissive]color
1539          // here because Inventor does not set them per-vertex (performance reasons). See
1540          // Inventor documentation for more details.
1541  }
1542
1543
1544  // Convert coordinates
1545  // OSG represents coordinates by: Vec2, Vec3, Vec4
1546  // Inventor by: SbVec3f, SbVec4f
1547  SoNode *coords;
1548  if (g->getVertexArray()->getDataSize() == 4) {
1549    coords = new SoCoordinate4;
1550    osgArray2ivMField(g->getVertexArray(), ((SoCoordinate4*)coords)->point);
1551  } else {
1552    coords = new SoCoordinate3;
1553    osgArray2ivMField(g->getVertexArray(), ((SoCoordinate3*)coords)->point);
1554  }
1555  coords->ref();
1556
1557  // Convert texture coordinates
1558  SoNode *texCoords = NULL;
1559  if (ivState->ivTexture) {
1560    if (ivState->osgTexGenS && ivState->osgTexGenT &&
1561        ivState->osgTexGen && ivState->osgTexGen->getMode()==osg::TexGen::SPHERE_MAP)
1562      texCoords = new SoTextureCoordinateEnvironment;
1563    else
1564    if (g->getTexCoordArray(0)) {
1565      if (g->getTexCoordArray(0)->getDataSize() <= 2) {
1566        texCoords = new SoTextureCoordinate2;
1567        osgArray2ivMField(g->getTexCoordArray(0), ((SoTextureCoordinate2*)texCoords)->point);
1568      }
1569#ifdef __COIN__
1570      else {
1571        texCoords = new SoTextureCoordinate3;
1572        osgArray2ivMField(g->getTexCoordArray(0), ((SoTextureCoordinate3*)texCoords)->point);
1573      }
1574#endif   // __COIN__
1575    }
1576    if (texCoords)
1577      texCoords->ref();
1578  }
1579
1580  // Convert normals
1581  // OSG represents normals by: Vec3,Vec3s,Vec3b
1582  // and can handle: Vec4s,Vec4b by truncating them to three components
1583  // Inventor by: SbVec3f
1584  SoNormal *normals = NULL;
1585  if (g->getNormalArray()) {
1586    normals = new SoNormal;
1587    osgArray2ivMField(g->getNormalArray(), normals->vector);
1588    normals->ref();
1589  }
1590
1591  // Convert osg::PrimitiveSets to Inventor's SoShapes
1592  int psetIndex,numPsets = g->getNumPrimitiveSets();
1593  for (psetIndex=0; psetIndex<numPsets; psetIndex++) {
1594
1595    // Get PrimitiveSet
1596    const osg::PrimitiveSet *pset = g->getPrimitiveSet(psetIndex);
1597    osg::PrimitiveSet::Type type = pset->getType();
1598    GLenum mode = pset->getMode();
1599
1600    // Create appropriate SoShape
1601    bool useIndices = g->getVertexIndices() != NULL || vrml1Conversion;
1602    bool needSeparateTriangles = false;
1603    SoVertexShape *shape = NULL;
1604    switch (mode) {
1605      case GL_POINTS:         shape = new SoPointSet; break;
1606      case GL_LINES:
1607      case GL_LINE_STRIP:
1608      case GL_LINE_LOOP:      if (useIndices) shape = new SoIndexedLineSet;
1609                              else shape = new SoLineSet;
1610                              break;
1611      case GL_TRIANGLES:
1612      case GL_TRIANGLE_STRIP:
1613      case GL_QUAD_STRIP:     if (useIndices)
1614                                if (vrml1Conversion) {
1615                                  shape = new SoIndexedFaceSet;
1616                                  needSeparateTriangles = true;
1617                                } else
1618                                  shape = new SoIndexedTriangleStripSet;
1619                              else
1620                                shape = new SoTriangleStripSet;
1621                              break;
1622      case GL_TRIANGLE_FAN:   needSeparateTriangles = true;
1623                              shape = (vrml1Conversion) ? (SoVertexShape*)new SoIndexedFaceSet :
1624                                                          new SoIndexedTriangleStripSet;
1625                              break;
1626      case GL_QUADS:
1627      case GL_POLYGON:        if (useIndices) shape = new SoIndexedFaceSet;
1628                              else shape = new SoFaceSet;
1629                              break;
1630      default: assert(0);
1631    }
1632
1633    // Size of single geometric primitive
1634    int primSize;
1635    switch (mode) {
1636    case GL_LINES:          primSize = 2; break;
1637    case GL_TRIANGLES:      primSize = 3; break;
1638    case GL_QUADS:          primSize = 4; break;
1639    default: primSize = 0;
1640    };
1641
1642
1643    bool ok = true;
1644
1645    switch (type) {
1646
1647      case osg::PrimitiveSet::DrawArraysPrimitiveType:
1648      {
1649        const osg::DrawArrays *drawArrays = dynamic_cast<const osg::DrawArrays*>(pset);
1650
1651        int startIndex = drawArrays->getFirst();
1652        int stopIndex = startIndex + drawArrays->getCount();
1653
1654        // FIXME: Am I using startIndex for all bundles that are PER_VERTEX?
1655        ok = processPrimitiveSet(g, pset, NULL, needSeparateTriangles,
1656                                  drawArrays->getCount(), primSize,
1657                                  startIndex, stopIndex, normalIndex, colorIndex,
1658                                  coords, normals, ivMaterial, texCoords,
1659                                  ivState->ivTexture, shape, indexedRoot, nonIndexedRoot);
1660        if (!ok)
1661        {
1662            OSG_WARN<<"Inventor plugin, ConvertToInventor processPrimitiveSet() failed."<<std::endl;
1663        }
1664        break;
1665      }
1666
1667      case osg::PrimitiveSet::DrawArrayLengthsPrimitiveType:
1668      {
1669        const osg::DrawArrayLengths *drawArrayLengths =
1670          dynamic_cast<const osg::DrawArrayLengths*>(pset);
1671
1672        int startIndex = drawArrayLengths->getFirst();
1673
1674        ok = processPrimitiveSet(g, pset, NULL, needSeparateTriangles,
1675                                  -1, primSize, startIndex, -1, normalIndex, colorIndex,
1676                                  coords, normals, ivMaterial, texCoords,
1677                                  ivState->ivTexture, shape, indexedRoot, nonIndexedRoot);
1678        if (!ok)
1679        {
1680            OSG_WARN<<"Inventor plugin, ConvertToInventor processPrimitiveSet() failed."<<std::endl;
1681        }
1682
1683        break;
1684      }
1685
1686      case osg::PrimitiveSet::DrawElementsUBytePrimitiveType:
1687      case osg::PrimitiveSet::DrawElementsUShortPrimitiveType:
1688      case osg::PrimitiveSet::DrawElementsUIntPrimitiveType:
1689      {
1690        osg::ref_ptr<osg::UIntArray> drawElemIndices = new osg::UIntArray;
1691
1692        switch (type) {
1693        case osg::PrimitiveSet::DrawElementsUBytePrimitiveType:
1694          {
1695            const osg::DrawElementsUByte *drawElements =
1696              dynamic_cast<const osg::DrawElementsUByte*>(pset);
1697            for(osg::DrawElementsUByte::const_iterator primItr = drawElements->begin();
1698                primItr!=drawElements->end();
1699                ++primItr)
1700              drawElemIndices->push_back(*primItr);
1701            break;
1702          }
1703        case osg::PrimitiveSet::DrawElementsUShortPrimitiveType:
1704          {
1705            const osg::DrawElementsUShort *drawElements =
1706              dynamic_cast<const osg::DrawElementsUShort*>(pset);
1707            for(osg::DrawElementsUShort::const_iterator primItr = drawElements->begin();
1708                primItr!=drawElements->end();
1709                ++primItr)
1710              drawElemIndices->push_back(*primItr);
1711            break;
1712          }
1713        case osg::PrimitiveSet::DrawElementsUIntPrimitiveType:
1714          {
1715            const osg::DrawElementsUInt *drawElements =
1716              dynamic_cast<const osg::DrawElementsUInt*>(pset);
1717            for(osg::DrawElementsUInt::const_iterator primItr = drawElements->begin();
1718                primItr!=drawElements->end();
1719                ++primItr)
1720              drawElemIndices->push_back(*primItr);
1721            break;
1722          }
1723        default: assert(0);
1724        }
1725
1726        ok = processPrimitiveSet(g, pset, drawElemIndices.get(), needSeparateTriangles,
1727                                  drawElemIndices->getNumElements(), primSize,
1728                                  0, drawElemIndices->getNumElements(), normalIndex, colorIndex,
1729                                  coords, normals, ivMaterial, texCoords,
1730                                  ivState->ivTexture, shape, indexedRoot, nonIndexedRoot);
1731        if (!ok)
1732        {
1733            OSG_WARN<<"Inventor plugin, ConvertToInventor processPrimitiveSet() failed."<<std::endl;
1734        }
1735        break;
1736      }
1737
1738      default:
1739        OSG_WARN << "IvWriter: NOT IMPLEMENTED" << std::endl;
1740    }
1741  }
1742
1743  if (indexedRoot)  ivState->ivHead->addChild(indexedRoot);
1744  if (nonIndexedRoot)  ivState->ivHead->addChild(nonIndexedRoot);
1745
1746  coords->unref();
1747  if (texCoords)  texCoords->unref();
1748  if (normals)  normals->unref();
1749}
1750
1751
1752void ConvertToInventor::processShapeDrawable(const osg::ShapeDrawable *d, InventorState *ivState)
1753{
1754  // visitor for converting ShapeDrawables
1755  class MyShapeVisitor : public osg::ConstShapeVisitor {
1756  public:
1757    void processNode(SoNode *ivNode, const osg::Vec3& center, osg::Quat rotation,
1758                      SoGroup *root) {
1759      // convert rotation
1760      rotation = osg::Quat(-M_PI_2, osg::Vec3(0.,1.,0.)) * osg::Quat(M_PI_2, osg::Vec3(1.,0.,0.)) * rotation;
1761
1762      if (center.length2()==0. && rotation.zeroRotation() && ivState->ivTexture==NULL)
1763
1764        // optimized handling of single node
1765        root->addChild(ivNode);
1766
1767      else {
1768        SoSeparator *root2 = new SoSeparator;
1769
1770        // handle transformation
1771        if (center.length2()!=0. || !rotation.zeroRotation()) {
1772          SoTransform *ivTransform = new SoTransform;
1773          setSoTransform(ivTransform, center, rotation);
1774          root2->addChild(ivTransform);
1775        }
1776
1777        // handle texture
1778        if (ivState->ivTexture)
1779          root2->addChild(ivState->ivTexture);
1780
1781        // build graph
1782        root2->addChild(ivNode);
1783        root->addChild(root2);
1784      }
1785    }
1786
1787    virtual void apply(const osg::Sphere &s) {
1788      SoSphere *ivSphere = new SoSphere;
1789      ivSphere->radius.setValue(s.getRadius());
1790      processNode(ivSphere, s.getCenter(), osg::Quat(0., osg::Vec3(1.,0.,0.)), ivState->ivHead);
1791    }
1792    virtual void apply(const osg::Box &b) {
1793      SoCube *ivCube = new SoCube;
1794      ivCube->width  = 2 * b.getHalfLengths().y();
1795      ivCube->height = 2 * b.getHalfLengths().z();
1796      ivCube->depth  = 2 * b.getHalfLengths().x();
1797      processNode(ivCube, b.getCenter(), b.getRotation(), ivState->ivHead);
1798    }
1799    virtual void apply(const osg::Cone &c) {
1800      SoCone *ivCone = new SoCone;
1801      ivCone->bottomRadius = c.getRadius();
1802      ivCone->height = c.getHeight();
1803      osg::Vec3 newCenter(c.getCenter());
1804      newCenter.ptr()[2] -= c.getBaseOffset();
1805      processNode(ivCone, newCenter, c.getRotation(), ivState->ivHead);
1806    }
1807    virtual void apply(const osg::Cylinder &c) {
1808      SoCylinder *ivCylinder = new SoCylinder;
1809      ivCylinder->radius = c.getRadius();
1810      ivCylinder->height = c.getHeight();
1811      processNode(ivCylinder, c.getCenter(), c.getRotation(), ivState->ivHead);
1812    }
1813
1814    void warnNonSupported() {
1815      OSG_WARN << "IvWriter: Not supported ShapeDrawable found. Skipping it." << std::endl;
1816    }
1817    virtual void apply(const osg::Capsule&)        { warnNonSupported(); }
1818    virtual void apply(const osg::InfinitePlane&)  { warnNonSupported(); }
1819    virtual void apply(const osg::TriangleMesh&)   { warnNonSupported(); }
1820    virtual void apply(const osg::ConvexHull&)     { warnNonSupported(); }
1821    virtual void apply(const osg::HeightField&)    { warnNonSupported(); }
1822    virtual void apply(const osg::CompositeShape&) { warnNonSupported(); }
1823
1824    InventorState *ivState;
1825    MyShapeVisitor(InventorState *ivState) { this->ivState = ivState; }
1826  } shapeVisitor(ivState);
1827
1828  // convert ShapeDrawable
1829  const osg::Shape *shape = d->getShape();
1830  if (shape)
1831    shape->accept(shapeVisitor);
1832}
1833
1834
1835void ConvertToInventor::processDrawable(osg::Drawable *d)
1836{
1837  osg::Geometry *g = d->asGeometry(); // FIXME: other drawables have to be handled also
1838  osg::ShapeDrawable *sd;
1839
1840  // Create SoSeparator and convert StateSet for Drawable
1841  InventorState *ivDrawableState = createInventorState(d->getStateSet());
1842
1843  if (g != NULL)
1844    processGeometry(g, ivDrawableState);
1845  else
1846
1847  if ((sd = dynamic_cast<osg::ShapeDrawable*>(d)) != NULL) {
1848    processShapeDrawable(sd, ivDrawableState);
1849  }
1850  else
1851    OSG_WARN << "IvWriter: Unsupported drawable found: \"" << d->className() <<
1852                              "\". Skipping it." << std::endl;
1853
1854  // Restore state
1855  popInventorState();
1856}
1857
1858
1859void ConvertToInventor::apply(osg::Geode &node)
1860{
1861#ifdef DEBUG_IV_WRITER
1862  OSG_INFO << "IvWriter: Geode traversed" << std::endl;
1863#endif
1864
1865  // Create SoSeparator and convert StateSet for Geode
1866  /*InventorState *ivGeodeState = */createInventorState(node.getStateSet());
1867
1868  // Convert drawables
1869  const int numDrawables = node.getNumDrawables();
1870  for (int i=0; i<numDrawables; i++)
1871    processDrawable(node.getDrawable(i));
1872
1873  traverse(node);
1874
1875  // Restore state
1876  popInventorState();
1877}
1878
1879
1880void ConvertToInventor::apply(osg::Group &node)
1881{
1882#ifdef DEBUG_IV_WRITER
1883  OSG_INFO << "IvWriter: Group traversed" << std::endl;
1884#endif
1885
1886  // Create SoSeparator and convert StateSet
1887  /*InventorState *ivState = */createInventorState(node.getStateSet());
1888
1889  traverse(node);
1890
1891  popInventorState();
1892}
1893
1894
1895void ConvertToInventor::apply(osg::Billboard& node)
1896{
1897#ifdef DEBUG_IV_WRITER
1898  OSG_INFO << "IvWriter: Billboard traversed" << std::endl;
1899#endif
1900
1901#ifdef __COIN__
1902
1903  if (useIvExtensions) {
1904
1905    // Create SoSeparator and convert StateSet
1906    InventorState *ivState = createInventorState(node.getStateSet());
1907    SoGroup *root = ivState->ivHead;
1908
1909    // Process drawables
1910    const int numDrawables = node.getNumDrawables();
1911    for (int i=0; i<numDrawables; i++) {
1912
1913      SoVRMLBillboard *billboard = new SoVRMLBillboard;
1914
1915      // SoVRMLBillboard is VRML 2.0 node supported by Coin (?since 2.0?)
1916      // However, I am seeing bug in my Coin 2.4.5 so that if
1917      // SoVRMLBillboard::axisOfRotation is not 0,0,0, the billboard behaviour is strange.
1918      // As long as it is set to 0,0,0, POINT_ROT_EYE-style billboard works perfectly.
1919      // AXIAL_ROT seems not possible with the bug. And POINT_ROT_WORLD was not
1920      // investigated by me until now.
1921      // There is also billboard culling bug in Coin, so the billboards may not be
1922      // rendered properly from time to time. PCJohn-2007-09-08
1923    #if 0
1924      SbVec3f axis;
1925      switch (node.getMode()) {
1926        case osg::Billboard::POINT_ROT_EYE:   axis = SbVec3f(0.f,0.f,0.f); break;
1927        case osg::Billboard::POINT_ROT_WORLD: axis = SbVec3f(0.f,0.f,0.f); break;
1928        case osg::Billboard::AXIAL_ROT:       axis = node.getAxis().ptr(); break;
1929        default:
1930          axis = SbVec3f(0.f,0.f,0.f);
1931      };
1932      billboard->axisOfRotation.setValue(axis);
1933    #else
1934
1935      billboard->axisOfRotation.setValue(SbVec3f(0.f,0.f,0.f));
1936
1937    #endif
1938
1939      SoTranslation *translation = new SoTranslation;
1940      translation->translation.setValue(node.getPosition(i).ptr());
1941
1942      // Rotate billboard correctly (OSG->IV conversion)
1943      // Note: use SoTransform instead of SoRotation because SoRotation is not supported by VRML1.
1944      SoTransform *transform = new SoTransform;
1945      transform->rotation = SbRotation(SbVec3f(1.f,0.f,0.f), float(-M_PI_2));
1946
1947      SoSeparator *separator = new SoSeparator;
1948      separator->addChild(translation);
1949      separator->addChild(billboard);
1950      billboard->addChild(transform);
1951
1952      root->addChild(separator);
1953      ivState->ivHead = billboard;
1954
1955      processDrawable(node.getDrawable(i));
1956
1957      traverse((osg::Node&)node);
1958    }
1959
1960    popInventorState();
1961
1962  } else
1963    apply((osg::Geode&)node);
1964
1965#else
1966
1967  apply((osg::Geode&)node);
1968
1969#endif
1970}
1971
1972
1973void ConvertToInventor::apply(osg::MatrixTransform& node)
1974{
1975#ifdef DEBUG_IV_WRITER
1976  OSG_INFO << "IvWriter: MatrixTransform traversed" << std::endl;
1977#endif
1978
1979  // Convert matrix
1980  SoMatrixTransform *ivTransform = new SoMatrixTransform;
1981  SbMatrix ivMatrix;
1982  const osg::Matrix::value_type *src = node.getMatrix().ptr();
1983  float *dest = ivMatrix[0];
1984  for (int i=0; i<16; i++,dest++,src++)
1985    *dest = *src;
1986  ivTransform->matrix.setValue(ivMatrix);
1987
1988  // Create SoSeparator and convert StateSet
1989  InventorState *ivState = createInventorState(node.getStateSet());
1990  ivState->ivHead->addChild(ivTransform);
1991
1992  traverse((osg::Node&)node);
1993
1994  popInventorState();
1995}
1996
1997
1998void ConvertToInventor::apply(osg::PositionAttitudeTransform& node)
1999{
2000#ifdef DEBUG_IV_WRITER
2001  OSG_INFO << "IvWriter: PositionAttitudeTransform traversed" << std::endl;
2002#endif
2003
2004  // Convert matrix
2005  SoTransform *ivTransform = new SoTransform;
2006  setSoTransform(ivTransform, node.getPosition(), node.getAttitude(), node.getScale());
2007
2008  // Create SoSeparator and convert StateSet
2009  InventorState *ivState = createInventorState(node.getStateSet());
2010  ivState->ivHead->addChild(ivTransform);
2011
2012  traverse((osg::Node&)node);
2013
2014  popInventorState();
2015}
2016
2017
2018void ConvertToInventor::apply(osg::LOD& node)
2019{
2020#ifdef DEBUG_IV_WRITER
2021  OSG_INFO << "IvWriter: LOD traversed" << std::endl;
2022#endif
2023
2024  // Convert LOD
2025  SoGroup *ivLOD = NULL;
2026  osg::LOD::RangeMode rangeMode = node.getRangeMode();
2027  if (rangeMode == osg::LOD::DISTANCE_FROM_EYE_POINT) {
2028
2029    // use SoLOD for DISTANCE_FROM_EYE_POINT
2030    SoLOD *lod = new SoLOD;
2031
2032    // copy ranges
2033    int i,c=node.getNumRanges();
2034    for (i=0; i<c; i++)
2035       lod->range.set1Value(i, node.getMaxRange(i));
2036
2037    // set center
2038    osg::Vec3f center(node.getCenter());
2039    lod->center.setValue(center.ptr());
2040
2041    ivLOD = lod;
2042
2043  } else
2044  if (rangeMode == osg::LOD::PIXEL_SIZE_ON_SCREEN) {
2045
2046    // use SoLevelOfDetail for PIXEL_SIZE_ON_SCREEN
2047    SoLevelOfDetail *lod = new SoLevelOfDetail;
2048
2049    // copy ranges
2050    int i,c=node.getNumRanges();
2051    for (i=0; i<c; i++)
2052       lod->screenArea.set1Value(i, node.getMaxRange(i));
2053
2054    ivLOD = lod;
2055
2056  } else {
2057
2058    // undefined mode -> put warning
2059    OSG_WARN << "IvWriter: Undefined LOD::RangeMode value." << std::endl;
2060    ivLOD = new SoGroup;
2061  }
2062
2063  // Create SoSeparator and convert StateSet
2064  InventorState *ivState = createInventorState(node.getStateSet());
2065  ivState->ivHead->addChild(ivLOD);
2066  ivState->ivHead = ivLOD;
2067
2068  traverse((osg::Node&)node);
2069
2070  popInventorState();
2071}
Note: See TracBrowser for help on using the browser.