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

Revision 8475, 77.2 kB (checked in by robert, 2 years ago)

Changed const double* to const Matrix::value_type* to ensure changes in Matrix type don't break the build

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