diff --git a/src/osgPlugins/Inventor/CMakeLists.txt b/src/osgPlugins/Inventor/CMakeLists.txt index 975a90d0d..01556c4e0 100644 --- a/src/osgPlugins/Inventor/CMakeLists.txt +++ b/src/osgPlugins/Inventor/CMakeLists.txt @@ -2,21 +2,25 @@ INCLUDE(OsgMacroUtils) INCLUDE_DIRECTORIES( ${INVENTOR_INCLUDE_DIR} ) +# Disable the build of a problem section in ConvertToInventor.cpp +ADD_DEFINITIONS(-DDISABLE_PROBLEM_COMPILE_SECTIONS) SET(TARGET_SRC + ConvertToInventor.cpp ConvertFromInventor.cpp GroupSoLOD.cpp PendulumCallback.cpp ReaderWriterIV.cpp ShuttleCallback.cpp - ) +) SET(TARGET_HDRS + ConvertToInventor.h ConvertFromInventor.h GroupSoLOD.h PendulumCallback.h ReaderWriterIV.h ShuttleCallback.h - ) +) SET(TARGET_EXTERNAL_LIBRARIES ${INVENTOR_LIBRARY} ) diff --git a/src/osgPlugins/Inventor/ConvertFromInventor.cpp b/src/osgPlugins/Inventor/ConvertFromInventor.cpp index 279a2abc1..58ba59556 100644 --- a/src/osgPlugins/Inventor/ConvertFromInventor.cpp +++ b/src/osgPlugins/Inventor/ConvertFromInventor.cpp @@ -40,7 +40,7 @@ #include #include -#ifdef COIN_BASIC_H +#ifdef __COIN__ #include #endif @@ -95,7 +95,7 @@ osg::Node* ConvertFromInventor::convert(SoNode* rootIVNode) cbAction.addPreCallback(SoGroup::getClassTypeId(), preGroup, this); cbAction.addPostCallback(SoGroup::getClassTypeId(), postGroup, this); cbAction.addPreCallback(SoTexture2::getClassTypeId(), preTexture, this); -#ifdef COIN_BASIC_H +#ifdef __COIN__ cbAction.addPreCallback(SoVRMLImageTexture::getClassTypeId(), preVRMLImageTexture, this); #endif @@ -498,6 +498,51 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) // Inherit modes from the global state stateSet->clear(); + // Convert the IV texture to OSG texture if any + osg::ref_ptr texture; + const SoNode *ivTexture = soTexStack.top(); + if (ivTexture) + { + osg::notify(osg::INFO)<<"Have texture"<setTextureAttributeAndModes(0, texture.get(), osg::StateAttribute::ON); + + // Set the texture environment + osg::ref_ptr texEnv = new osg::TexEnv; + switch (action->getTextureModel()) + { + case SoTexture2::MODULATE: + texEnv->setMode(osg::TexEnv::MODULATE); + break; + case SoTexture2::DECAL: + texEnv->setMode(osg::TexEnv::DECAL); + break; + case SoTexture2::BLEND: + texEnv->setMode(osg::TexEnv::BLEND); + break; + + // SGI's Inventor does not have REPLACE mode, but the Coin 3D library does. + // Coin supports REPLACE since 2.2 release, TGS Inventor from 4.0. + // Let's convert to the TexEnv anyway. + case (SoTexture2::Model)GL_REPLACE: // SoTexture2::REPLACE is the same as GL_REPLACE + texEnv->setMode(osg::TexEnv::REPLACE); + break; + } + stateSet->setTextureAttributeAndModes(0,texEnv.get(),osg::StateAttribute::ON); + } + SbColor ambient, diffuse, specular, emission; float shininess, transparency; @@ -506,7 +551,24 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) shininess, transparency, 0); // Set transparency - if (transparency > 0) + SbBool hasTextureTransparency = FALSE; + if (ivTexture) { + SbVec2s tmp; + int bpp; + if (ivTexture->isOfType(SoTexture2::getClassTypeId())) + ((SoTexture2*)ivTexture)->image.getValue(tmp, bpp); +#ifdef __COIN__ + else + if (ivTexture->isOfType(SoVRMLImageTexture::getClassTypeId())) { + const SbImage *img = ((SoVRMLImageTexture*)ivTexture)->getImage(); + if (img) img->getValue(tmp, bpp); + else bpp = 0; + } +#endif + hasTextureTransparency = bpp==4 || bpp==2; + } + + if (transparency > 0 || hasTextureTransparency) { osg::ref_ptr transparency = new osg::BlendFunc; stateSet->setAttributeAndModes(transparency.get(), @@ -621,51 +683,6 @@ ConvertFromInventor::getStateSet(SoCallbackAction* action) osg::StateAttribute::ON); } - // Convert the IV texture to OSG texture if any - osg::ref_ptr texture; - if (soTexStack.top()) - { - osg::notify(osg::INFO)<<"Have texture"<setTextureAttributeAndModes(0,texture.get(), osg::StateAttribute::ON); - - // Set the texture environment - osg::ref_ptr texEnv = new osg::TexEnv; - switch (action->getTextureModel()) - { - case SoTexture2::MODULATE: - texEnv->setMode(osg::TexEnv::MODULATE); - break; - case SoTexture2::DECAL: - texEnv->setMode(osg::TexEnv::DECAL); - break; - case SoTexture2::BLEND: - texEnv->setMode(osg::TexEnv::BLEND); - break; - -#ifdef __COIN__ -// SGI's Inventor does not have REPLACE mode, but the Coin 3D library does. -// Coin supports REPLACE since 2.2 release, TGS Inventor from 4.0. - case SoTexture2::REPLACE: - texEnv->setMode(osg::TexEnv::REPLACE); - break; -#endif - } - stateSet->setTextureAttributeAndModes(0,texEnv.get(),osg::StateAttribute::ON); - } - return stateSet; } //////////////////////////////////////////////////////////////////// @@ -696,7 +713,7 @@ ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, std::string fileName; if (soNode->isOfType(SoTexture2::getClassTypeId())) fileName = ((SoTexture2*)soNode)->filename.getValue().getString(); -#ifdef COIN_BASIC_H +#ifdef __COIN__ else if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId())) fileName = ((SoVRMLImageTexture*)soNode)->url.getNum() >= 1 ? @@ -733,7 +750,7 @@ ConvertFromInventor::convertIVTexToOSGTex(const SoNode* soNode, } // Set texture wrap mode -#ifdef COIN_BASIC_H +#ifdef __COIN__ if (soNode->isOfType(SoVRMLImageTexture::getClassTypeId())) { // It looks like there is a high probability of bug in Coin (investigated on version 2.4.6). // action->getTextureWrap() returns correct value on SoTexture2 (SoTexture2::CLAMP = 0x2900, diff --git a/src/osgPlugins/Inventor/ConvertToInventor.cpp b/src/osgPlugins/Inventor/ConvertToInventor.cpp new file mode 100644 index 000000000..1e13d8de8 --- /dev/null +++ b/src/osgPlugins/Inventor/ConvertToInventor.cpp @@ -0,0 +1,1857 @@ +// +// ConvertToInventor converts OSG scene graph to Inventor or VRML 1 scene graph +// +// It requires OSG and Inventor compatible library, such as Coin, +// SGI Inventor , or TGS Inventor. +// +// +// Autor: PCJohn (peciva _at fit.vutbr.cz) +// +// License: public domain +// +// +// THIS SOFTWARE IS NOT COPYRIGHTED +// +// This source code is offered for use in the public domain. +// You may use, modify or distribute it freely. +// +// This source code is distributed in the hope that it will be useful but +// WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY +// DISCLAIMED. This includes but is not limited to warranties of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// If you find the source code useful, authors will kindly welcome +// if you give them credit and keep their names with their source code, +// but you are not forced to do so. +// + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __COIN__ +#include +#endif +#include "ConvertToInventor.h" + + +#define DEBUG_IV_WRITER 1 + +ConvertToInventor::ConvertToInventor() : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) +{ + // Enable/disable using of some extensions introduced by TGS and Coin. + // + // For instance, GL_REPLACE texturing mode is extension + // and it will not be used if extensions are not enabled. +#ifdef __COIN__ + useIvExtensions = true; +#else + useIvExtensions = false; +#endif + vrml1Conversion = false; + uniqueIdGenerator = 1; + + // Scene root + ivRoot = new SoSeparator; + ivRoot->ref(); + + // OSG -> Inventor coordinate system conversion + SoMatrixTransform *mt = new SoMatrixTransform; + mt->matrix.setValue(1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, -1.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + ivRoot->addChild(mt); + +#if 0 // Setting everything to defaults may not be correct option + // because the user may want to have model whose material,etc. + // will be specified up in the scene graph and he may, for instance, + // reuse the model with different materials. + // However, I am not sure anyway. PCJohn-2007-08-27 + + // OSG defaults to: Inventor defaults to: + // GL_LIGHTING enabled enabled + SoLightModel *lm = new SoLightModel; + lm->model = SoLightModel::PHONG; + ivRoot->addChild(lm); + + // OSG defaults to: Inventor defaults to: + // lighting: one side one side lighting + // frontFace: COUNTER_CLOCKWISE COUNTER_CLOCKWISE + // culling: DISABLED DISABLED + // faceCulling: GL_BACK GL_BACK + SoShapeHints *sh = new SoShapeHints; + sh->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING; + ivRoot->addChild(sh); + + // Make sure texturing is OFF + ivRoot->addChild(new SoTexture2D); + + // Put default material + ivRoot->addChild(new SoMaterial); +#endif + + // initialize Inventor state stack + ivStack.push(InventorState::createTopLevelState(ivRoot)); +} + + +ConvertToInventor::~ConvertToInventor() +{ + assert(ivStack.size() == 1 && "Not all InventorStates were popped from ivStack."); + if (ivRoot) ivRoot->unref(); +} + + +SoNode* ConvertToInventor::getIvSceneGraph() const +{ + return ivRoot; +} + + +void ConvertToInventor::apply(osg::Node &node) +{ +#ifdef DEBUG_IV_WRITER + osg::notify(osg::INFO) << "IvWriter: node traversed" << std::endl; +#endif + + traverse(node); +} + + +template +static void osgArray2ivMField_template(const osg::Array *array, fieldClass &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0) +{ + int i,num = array->getNumElements(); + if (startIndex!=0 || stopIndex!=0) { + num = stopIndex - startIndex; + assert(stopIndex >= startIndex); + assert(startIndex >= 0 && stopIndex >= 0); + assert(stopIndex <= int(array->getNumElements())); + } + + // update num if numItemsUntilMinusOne > 0 + if (numItemsUntilMinusOne > 0 && num >= 1) + num += (num-1) / numItemsUntilMinusOne; + + field.setNum(num); + ivType *a = field.startEditing(); + + osgType *ptr = (osgType*)array->getDataPointer() + startIndex; + + if (numItemsUntilMinusOne <= 0) + for (i=0; i +static void osgArray2ivMField_composite_template_worker(ivType *dest, osgType *src, int num, int numItemsUntilMinusOne = 0) +{ + for (int i=0; i +static void osgArray2ivMField_composite_template_worker(SbColor *dest, GLubyte *src, int num, int numItemsUntilMinusOne) +{ + for (int i=0; i +static void osgArray2ivMField_composite_template_worker(SbVec3f *dest, float *src, int num, int numItemsUntilMinusOne) +{ + for (int i=0; i +static void osgArray2ivMField_composite_template(const osg::Array *array, fieldClass &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0) +{ + int num = array->getNumElements(); + if (startIndex!=0 || stopIndex!=0) { + num = stopIndex - startIndex; + assert(stopIndex >= startIndex); + assert(startIndex >= 0 && stopIndex >= 0); + assert(stopIndex <= int(array->getNumElements())); + } + assert(numItemsUntilMinusOne <= 0 && "Composite template must have numItemsUntilMinusOne set to 0."); + field.setNum(num); + ivType *a = field.startEditing(); + + osgType *ptr = (osgType*)array->getDataPointer() + startIndex; + osgArray2ivMField_composite_template_worker(a, ptr, num, 0); + + field.finishEditing(); +} + + +template +static void osgArray2ivMField_pack_template(const osg::Array *array, fieldClass &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0) +{ + int i,num = array->getNumElements(); + if (startIndex!=0 || stopIndex!=0) { + num = stopIndex - startIndex; + assert(stopIndex >= startIndex); + assert(startIndex >= 0 && stopIndex >= 0); + assert(stopIndex <= int(array->getNumElements())); + } + assert(numItemsUntilMinusOne <= 0 && "Pack template must have numItemsUntilMinusOne set to 0."); + field.setNum(num); + ivType *a = field.startEditing(); + + osgType *ptr = (osgType*)array->getDataPointer() + startIndex; + for (i=0; i max) tmp = max; + if (tmp < min) tmp = min; + a[i] |= ivType(tmp) << (((numComponents-1)*8)-(j*8)); + } + } + + field.finishEditing(); +} + + +template +static bool applicateIntType(const osg::Array *array, fieldClass &field, int startIndex, int stopIndex, int numItemsUntilMinusOne) +{ + if (field.isOfType(fieldClass::getClassTypeId())) + { + switch (array->getType()) + { + case osg::Array::ByteArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::UByteArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::ShortArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::UShortArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::IntArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::UIntArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::FloatArrayType: osgArray2ivMField_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; +#ifndef DISABLE_PROBLEM_COMPILE_SECTIONS +// note from Robert Osfield, could not compile the follow under Linux with g++ version 4.1.2 +// due to error: no matching function for call to ?osgArray2ivMField_pack_template(const osg::Array*&, SoMFInt32&, int&, int&, int&)? +// this need to be resolved before this section can be compiled by default + case osg::Array::Vec4bArrayType: // FIXME: should signed values be handled differently? Like -128..127? + case osg::Array::Vec4ubArrayType: osgArray2ivMField_pack_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; + case osg::Array::Vec4ArrayType: osgArray2ivMField_pack_template + (array, field, startIndex, stopIndex, numItemsUntilMinusOne); return true; +#endif + } + } + return false; +} + + +static void osgArray2ivMField(const osg::Array *array, SoMField &field, int startIndex = 0, int stopIndex = 0, int numItemsUntilMinusOne = 0) +{ + if (field.isOfType(SoMFFloat::getClassTypeId())) + { + switch (array->getType()) + { + case osg::Array::FloatArrayType: osgArray2ivMField_template + (array, (SoMFFloat&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + } + } + else if (applicateIntType(array, (SoMFInt32&) field, startIndex, stopIndex, numItemsUntilMinusOne)) return; + else if (applicateIntType(array, (SoMFUInt32&)field, startIndex, stopIndex, numItemsUntilMinusOne)) return; + else if (applicateIntType(array, (SoMFShort&) field, startIndex, stopIndex, numItemsUntilMinusOne)) return; + else if (applicateIntType(array, (SoMFUShort&)field, startIndex, stopIndex, numItemsUntilMinusOne)) return; + else if (field.isOfType(SoMFVec2f::getClassTypeId())) + { + switch (array->getType()) + { + case osg::Array::Vec2ArrayType: osgArray2ivMField_composite_template + (array, (SoMFVec2f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + } + } + else if (field.isOfType(SoMFVec3f::getClassTypeId())) + { + switch (array->getType()) + { + case osg::Array::Vec3ArrayType: osgArray2ivMField_composite_template + (array, (SoMFVec3f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + case osg::Array::Vec2ArrayType: osgArray2ivMField_composite_template + (array, (SoMFVec3f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + } + } + else if (field.isOfType(SoMFVec4f::getClassTypeId())) + { + switch (array->getType()) { + case osg::Array::Vec4ArrayType: osgArray2ivMField_composite_template + (array, (SoMFVec4f&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + } + } + else if (field.isOfType(SoMFColor::getClassTypeId())) + { + switch (array->getType()) + { + case osg::Array::Vec3ArrayType: osgArray2ivMField_composite_template + (array, (SoMFColor&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + case osg::Array::Vec4ArrayType: osgArray2ivMField_composite_template + (array, (SoMFColor&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + case osg::Array::Vec4ubArrayType: osgArray2ivMField_composite_template + (array, (SoMFColor&)field, startIndex, stopIndex, numItemsUntilMinusOne); return; + } + }; + + osg::notify(osg::WARN) << "IvWriter: No direct conversion for array. " + << "The file may be broken." << std::endl; +} + + +template +static bool deindex(variableType *dest, const variableType *src, const int srcNum, + const indexType *indices, const int numToProcess) +{ + for (int i=0; i=srcNum) return false; + dest[i] = src[index]; + } + return true; +} + + +template +static bool deindex(variableType *dest, const variableType *src, const int srcNum, + const osg::Array *indices, const int numToProcess) +{ + if (int(indices->getNumElements()) < numToProcess) { + assert(0 && "Something is wrong: indices array is shorter than numToProcess."); + return false; + } + + switch (indices->getType()) { + case osg::Array::ByteArrayType: + case osg::Array::UByteArrayType: + return deindex(dest, src, srcNum, + (GLbyte*)indices->getDataPointer(), numToProcess); + break; + case osg::Array::ShortArrayType: + case osg::Array::UShortArrayType: + return deindex(dest, src, srcNum, + (GLshort*)indices->getDataPointer(), numToProcess); + break; + case osg::Array::IntArrayType: + case osg::Array::UIntArrayType: + return deindex(dest, src, srcNum, + (GLint*)indices->getDataPointer(), numToProcess); + break; + default: + assert(0 && "Index of strange type."); + return false; + } +} + + +template +static bool processArray(const osg::Array *indices, const osg::Array *drawElemIndices, + fieldType *destField, const fieldType *srcField, + int startIndex, int numToProcess) +{ + bool ok = true; + + if (indices || drawElemIndices) { + + // "deindex" original data + if (indices && !drawElemIndices) + ok = deindex(destField->startEditing(), + srcField->getValues(startIndex), + srcField->getNum(), indices, numToProcess); else + if (!indices && drawElemIndices) + ok = deindex(destField->startEditing(), + srcField->getValues(startIndex), + srcField->getNum(), drawElemIndices, numToProcess); + else { + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + assert(0); // FIXME: + } + + destField->finishEditing(); + if (!ok) + osg::notify(osg::WARN) << "IvWriter: Can not deindex - bug in model: index out of range." << std::endl; + + } else { + + // copy required part of original data + const variableType *src = srcField->getValues(startIndex); + assert(startIndex+numToProcess <= srcField->getNum() && "Index out of bounds."); + variableType *dest = destField->startEditing(); + for (int i=0; ifinishEditing(); + } + + return ok; +} + + +static void processIndices(const osg::Array *indices, const osg::Array *drawElemIndices, + SoMFInt32 &ivIndices, + int startIndex, int stopIndex, int numItemsUntilMinusOne) +{ + if (indices || drawElemIndices) { + if (indices && !drawElemIndices) + osgArray2ivMField(indices, ivIndices, startIndex, stopIndex, numItemsUntilMinusOne); else + if (!indices && drawElemIndices) + osgArray2ivMField(drawElemIndices, ivIndices, startIndex, stopIndex, numItemsUntilMinusOne); + else { + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + assert(0); // FIXME: + } + + } else { + int num = stopIndex-startIndex; + if (numItemsUntilMinusOne != 0 && num >= 1) + num += (num-1)/numItemsUntilMinusOne; + ivIndices.setNum(num); + int32_t *a = ivIndices.startEditing(); + if (numItemsUntilMinusOne <= 0) + for (int i=0,j=startIndex; jgetNum(); + int newNum = origNum + drawArrayLengths->size()-1; + field->setNum(newNum); + int32_t *a = field->startEditing(); + int32_t *src = a + origNum; + int32_t *dst = a + newNum; + for (osg::DrawArrayLengths::const_reverse_iterator primItr = drawArrayLengths->rbegin(); + primItr!=drawArrayLengths->rend()-1; + ++primItr) { + int c = *primItr; + src -= c; + dst -= c; + memmove(dst, src, sizeof(int32_t)*c); + dst--; + *dst = -1; + } + field->finishEditing(); +} + + +static void postProcessField(const SbIntList &runLengths, osg::PrimitiveSet::Mode primType, + SoMFInt32 *field, osg::Geometry::AttributeBinding binding) +{ + if (binding==osg::Geometry::BIND_OFF || binding==osg::Geometry::BIND_OVERALL || + binding==osg::Geometry::BIND_PER_PRIMITIVE_SET) + return; + + // make copy of array + const int32_t *fieldArray = field->getValues(0); + int origNum = field->getNum(); + int32_t *tmpArray = new int32_t[origNum]; + memcpy(tmpArray, fieldArray, origNum*sizeof(int32_t)); + + // compute new number of indices + int newNum = origNum; + const int l = runLengths.getLength(); + switch (binding) { + case osg::Geometry::BIND_PER_VERTEX: + for (int i=0; isetNum(newNum); + int32_t *src = tmpArray; + int32_t *dst = field->startEditing(); + int32_t *dst2 = dst; + switch (binding) { + case osg::Geometry::BIND_PER_VERTEX: + for (int i=0; ifinishEditing(); + + // free resources + delete tmpArray; +} + + +static void postProcessTriangleSeparation(SoIndexedShape *shape, osg::PrimitiveSet::Mode primType, + osg::Geometry::AttributeBinding normalBinding, + osg::Geometry::AttributeBinding colorBinding) +{ + // compute runLengths + SbIntList runLengths; + const int32_t *a = shape->coordIndex.getValues(0); + int origNum = shape->coordIndex.getNum(); + int l = 0; + for (int i=0; icoordIndex, osg::Geometry::BIND_PER_VERTEX); + postProcessField(runLengths, primType, &shape->normalIndex, normalBinding); + postProcessField(runLengths, primType, &shape->materialIndex, colorBinding); + bool notUseTexCoords = shape->textureCoordIndex.getNum()==0 || + (shape->textureCoordIndex.getNum()==1 && shape->textureCoordIndex[0] == -1); + if (!notUseTexCoords) + postProcessField(runLengths, primType, &shape->textureCoordIndex, osg::Geometry::BIND_PER_VERTEX); + +} + + +static SoMaterialBinding* createMaterialBinding(const osg::Geometry *g, bool isMaterialIndexed) +{ + SoMaterialBinding *materialBinding = new SoMaterialBinding; + switch (g->getColorBinding()) { + case osg::Geometry::BIND_OFF: // OFF means use material from state set (if any) for whole geometry + case osg::Geometry::BIND_OVERALL: + case osg::Geometry::BIND_PER_PRIMITIVE_SET: materialBinding->value = SoMaterialBinding::OVERALL; break; + case osg::Geometry::BIND_PER_PRIMITIVE: materialBinding->value = (isMaterialIndexed) ? SoMaterialBinding::PER_PART_INDEXED : SoMaterialBinding::PER_PART; break; + case osg::Geometry::BIND_PER_VERTEX: materialBinding->value = (isMaterialIndexed) ? SoMaterialBinding::PER_VERTEX_INDEXED : SoMaterialBinding::PER_VERTEX; break; + default: assert(0); + } + return materialBinding; +} + + +static SoNormalBinding* createNormalBinding(const osg::Geometry *g, bool areNormalsIndexed) +{ + // Convert normal binding + SoNormalBinding *normalBinding = new SoNormalBinding; + switch (g->getNormalBinding()) { + case osg::Geometry::BIND_OFF: // FIXME: what to do with BIND_OFF value? + case osg::Geometry::BIND_OVERALL: + case osg::Geometry::BIND_PER_PRIMITIVE_SET: normalBinding->value = SoNormalBinding::OVERALL; break; + case osg::Geometry::BIND_PER_PRIMITIVE: normalBinding->value = (areNormalsIndexed) ? SoNormalBinding::PER_PART_INDEXED : SoNormalBinding::PER_PART; break; + case osg::Geometry::BIND_PER_VERTEX: normalBinding->value = (areNormalsIndexed) ? SoNormalBinding::PER_VERTEX_INDEXED : SoNormalBinding::PER_VERTEX; break; + default: assert(0); + } + return normalBinding; +} + + +static SoTextureCoordinateBinding* createTexCoordBinding(SbBool useIndexing) +{ + SoTextureCoordinateBinding *b = new SoTextureCoordinateBinding; + b->value.setValue(useIndexing ? SoTextureCoordinateBinding::PER_VERTEX_INDEXED : + SoTextureCoordinateBinding::PER_VERTEX); + return b; +} + + +static SoTexture2::Model convertTexEnvMode(osg::TexEnv::Mode osgMode, bool useIvExtensions) +{ + switch (osgMode) { + case GL_MODULATE: return SoTexture2::MODULATE; + case GL_REPLACE: return (SoTexture2::Model)(useIvExtensions ? GL_REPLACE : GL_MODULATE); + case GL_BLEND: return SoTexture2::BLEND; + case GL_DECAL: return SoTexture2::DECAL; + default: assert(0); return SoTexture2::MODULATE; + } +} + + + +static SoTexture2::Wrap convertTextureWrap(osg::Texture::WrapMode osgWrap) +{ + // notes on support of CLAMP_TO_BORDER, CLAMP_TO_EDGE, and MIRRORED_REPEAT: + // original SGI Inventor: no + // Coin: no (until current version Coin 2.5.0b3) + // TGS Inventor: introduced in TGS Inventor 5.0 (available in SoTexture class) + + // note: Coin (since 2.0) uses CLAMP_TO_EDGE for rendering if SoTexture2::CLAMP is specified. + + switch (osgWrap) { + case osg::Texture::CLAMP: + case osg::Texture::CLAMP_TO_BORDER: + case osg::Texture::CLAMP_TO_EDGE: return SoTexture2::CLAMP; + case osg::Texture::REPEAT: + case osg::Texture::MIRROR: return SoTexture2::REPEAT; + default: assert(0); return SoTexture2::REPEAT; + } +} + + +static void setSoTransform(SoTransform *tr, const osg::Vec3 &translation, const osg::Quat &rotation, + const osg::Vec3 &scale = osg::Vec3(1.,1.,1.)) +{ + tr->translation.setValue(translation.ptr()); + tr->rotation.setValue(rotation.x(), rotation.y(), rotation.z(), rotation.w()); + tr->scaleFactor.setValue(scale.ptr()); + //tr->scaleCenter.setValue(osg::Vec3f(node.getPivotPoint())); +} + + +static bool updateMode(bool &flag, const osg::StateAttribute::GLModeValue value) +{ + if (value & osg::StateAttribute::INHERIT) return flag; + else return (flag = (value & osg::StateAttribute::ON)); +} + + +ConvertToInventor::InventorState* ConvertToInventor::createInventorState(const osg::StateSet *ss) +{ + // Push on stack + const InventorState *ivPrevState = &ivStack.top(); + ivStack.push(*ivPrevState); + InventorState *ivState = &ivStack.top(); + + // Inventor graph + ivState->ivSeparator = new SoSeparator; + ivPrevState->ivSeparator->addChild(ivState->ivSeparator); + + if (ss) { + + // + // Lighting + // + + // enable/disable lighting + updateMode(ivState->osgLighting, ss->getMode(GL_LIGHTING)); + if (ivState->osgLighting != ivPrevState->osgLighting) { + SoLightModel *lm = new SoLightModel; + lm->model = (ivState->osgLighting) ? SoLightModel::PHONG : SoLightModel::BASE_COLOR; + ivState->ivSeparator->addChild(lm); + } + + // two-sided lighting + const osg::LightModel *osgLM = dynamic_cast(ss->getAttribute(osg::StateAttribute::LIGHTMODEL)); + if (osgLM) + ivState->osgTwoSided = osgLM->getTwoSided(); + + // front face + const osg::FrontFace *osgFF = dynamic_cast(ss->getAttribute(osg::StateAttribute::FRONTFACE)); + if (osgFF) + ivState->osgFrontFace = osgFF->getMode(); + + // face culling + updateMode(ivState->osgCullFaceEnabled, ss->getMode(GL_CULL_FACE)); + const osg::CullFace *osgCF = dynamic_cast(ss->getAttribute(osg::StateAttribute::CULLFACE)); + if (osgCF) + ivState->osgCullFace = osgCF->getMode(); + + // detect state change + if (ivState->osgTwoSided != ivPrevState->osgTwoSided || + ivState->osgFrontFace != ivPrevState->osgFrontFace || + ivState->osgCullFaceEnabled != ivPrevState->osgCullFaceEnabled || + ivState->osgCullFace != ivPrevState->osgCullFace) { + + // new SoShapeHints + SoShapeHints *sh = new SoShapeHints; + if (ivState->osgTwoSided) { + // warn if face culling is on + if (ivState->osgCullFaceEnabled) + osg::notify(osg::WARN) << "IvWriter: Using face culling and two-sided lighting together! " + "Ignoring face culling." << std::endl; + + // set two-sided lighting and backface culling off + sh->vertexOrdering = ivState->osgFrontFace==osg::FrontFace::COUNTER_CLOCKWISE ? + SoShapeHints::COUNTERCLOCKWISE : SoShapeHints::CLOCKWISE; + sh->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; + } + else { + // set one-sided lighting and backface optionally + if (ivState->osgCullFaceEnabled) { + + // determine vertex ordering + bool ccw = ivState->osgFrontFace==osg::FrontFace::COUNTER_CLOCKWISE; + if (ivState->osgCullFace != osg::CullFace::BACK) ccw = !ccw; + + if (ccw) + // Warn if culling the lit faces while rendering unlit faces. + // Inventor does not support this setup and it lits the unculled faces only. + osg::notify(osg::WARN) << "IvWriter: Culling was set in a way that one-sided lighting will lit the culled sides of faces. " + "Using lighting on correct faces." << std::endl; + + // face culling on + sh->vertexOrdering = ccw ? SoShapeHints::COUNTERCLOCKWISE : SoShapeHints::CLOCKWISE; + sh->shapeType = SoShapeHints::SOLID; + } + else + // no face culling + sh->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; + } + ivState->ivSeparator->addChild(sh); + } + + // + // Texturing + // + // FIXME: handle 1D and 3D textures + + // get OSG state + ivState->osgTexture = dynamic_cast(ss->getTextureAttribute(0, osg::StateAttribute::TEXTURE)); + ivState->osgTexEnv = dynamic_cast(ss->getTextureAttribute(0, osg::StateAttribute::TEXENV)); + updateMode(ivState->osgTexture2Enabled, ss->getTextureMode(0, GL_TEXTURE_2D)); + + // detect state changes + if (ivState->osgTexture2Enabled != ivPrevState->osgTexture2Enabled || + ivState->osgTexture != ivPrevState->osgTexture || + ivState->osgTexEnv != ivPrevState->osgTexEnv) { + + if (!ivState->osgTexture2Enabled || + ivState->osgTexture==NULL || ivState->osgTexture->getImage(0)==NULL) + + // empty texture disables texturing + ivState->ivTexture = new SoTexture2; + + else { + + // reuse texture if possible + ivState->ivTexture = ivTexturesMap[ivState->osgTexture][ivState->osgTexEnv]; + + // if nothing for reusing, create new + if (!ivState->ivTexture) { + + // create texture + ivState->ivTexture = new SoTexture2; + ivTexturesMap[ivState->osgTexture][ivState->osgTexEnv] = ivState->ivTexture; + + // texture file name + const std::string &textureName = ivState->osgTexture->getImage(0)->getFileName(); + ivState->ivTexture->filename.setValue(textureName.c_str()); // FIXME: handle inlined texture data in the files + + // wrap + ivState->ivTexture->wrapS.setValue(convertTextureWrap( + ivState->osgTexture->getWrap(osg::Texture::WRAP_S))); + ivState->ivTexture->wrapT.setValue(convertTextureWrap( + ivState->osgTexture->getWrap(osg::Texture::WRAP_T))); + + // texture environment + if (ivState->osgTexEnv) { + ivState->ivTexture->model.setValue(convertTexEnvMode( + ivState->osgTexEnv->getMode(), useIvExtensions)); + osg::Vec4 color = ivState->osgTexEnv->getColor(); + ivState->ivTexture->blendColor.setValue(color.r(), color.g(), color.b()); + } + + // notes on support of borderColor and borderWidth: + // original SGI Inventor: no + // Coin: no (until current version 2.5.0b3) + // TGS Inventor: introduced in version 5.0 (as SoTexture::borderColor) + + // FIXME: implement support for texture filtering + } + } + } + + // Texture coordinate generation + updateMode(ivState->osgTexGenS, ss->getTextureMode(0, GL_TEXTURE_GEN_S)); + updateMode(ivState->osgTexGenT, ss->getTextureMode(0, GL_TEXTURE_GEN_T)); + ivState->osgTexGen = dynamic_cast(ss->getTextureAttribute(0, osg::StateAttribute::TEXGEN)); + + // Material parameters + const osg::Material *osgMaterial = dynamic_cast(ss->getAttribute(osg::StateAttribute::MATERIAL)); + if (osgMaterial) { + + // Update ivState + ivState->osgMaterial = osgMaterial; + + if (ivState->osgLighting) { + + SoMaterial *ivMaterial = new SoMaterial; + ivState->ivMaterial = ivMaterial; + + // Warn if using two side materials + // FIXME: The geometry can be probably doubled, or some estimation can be made + // whether only front or back faces are used. + if (osgMaterial->getAmbientFrontAndBack() == false || + osgMaterial->getDiffuseFrontAndBack() == false || + osgMaterial->getSpecularFrontAndBack() == false || + osgMaterial->getEmissionFrontAndBack() == false || + osgMaterial->getShininessFrontAndBack() == false) + osg::notify(osg::WARN) << "IvWriter: Model contains different materials for front and " + "back faces. This is not handled properly. Using front material only." << std::endl; + + // Convert material components + // FIXME: Transparency can be specified for each component in OSG, + // in Inventor just globally. + // Solutions? It can be averaged or just diffuse can be used. + ivMaterial->ambientColor.setValue(osgMaterial->getAmbient(osgMaterial->getAmbientFrontAndBack() ? + osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr()); + ivMaterial->diffuseColor.setValue(osgMaterial->getDiffuse(osgMaterial->getDiffuseFrontAndBack() ? + osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr()); + ivMaterial->specularColor.setValue(osgMaterial->getSpecular(osgMaterial->getSpecularFrontAndBack() ? + osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr()); + ivMaterial->emissiveColor.setValue(osgMaterial->getEmission(osgMaterial->getEmissionFrontAndBack() ? + osg::Material::FRONT_AND_BACK : osg::Material::FRONT).ptr()); + ivMaterial->shininess.setValue(osgMaterial->getShininess(osgMaterial->getShininessFrontAndBack() ? + osg::Material::FRONT_AND_BACK : osg::Material::FRONT)); + ivMaterial->transparency.setValue(1.f - osgMaterial->getDiffuse(osgMaterial->getDiffuseFrontAndBack() ? + osg::Material::FRONT_AND_BACK : osg::Material::FRONT).a()); + + } else { + + if (vrml1Conversion) + ivState->ivMaterial = new SoMaterial; + else + ivState->ivMaterial = new SoBaseColor; + } + } + + // Convert colors + // OSG represents colors by: Vec3, Vec4,Vec4ub + // Inventor by: uint32 (RGBA, by SoPackedColor), SbColor (Vec3f, by SoMaterial and SoBaseColor) + // note: Inventor can use DIFFUSE component inside a shape only. Material is set for whole shape. + // Although SoMaterial is used only, SoPackedColor may bring some possibilities on per-vertex + // alpha and SoBaseColor may be useful on pre-lit scene. + + if (osgMaterial) + if (osgMaterial->getColorMode() != osg::Material::DIFFUSE && + osgMaterial->getColorMode() != osg::Material::OFF) { + + if (osgMaterial->getColorMode() == osg::Material::AMBIENT_AND_DIFFUSE) + osg::notify(osg::WARN) << "IvWriter: The model is using AMBIENT_AND_DIFFUSE material " + "mode while Inventor supports DIFFUSE mode only. " + "The model colors may not much exactly." << std::endl; + else + osg::notify(osg::WARN) << "IvWriter: The model is not using DIFFUSE material mode and " + "Inventor supports DIFFUSE mode only. " + "The model colors may not be correct." << std::endl; + } + + // note on "headlight": + // OSG is using HEADLIGHT or SKY_LIGHT. In both cases it sets these defaults: + // osg::LightModel::ambientIntensity(0.1, 0.1, 0.1, 1.0); + // osg::Light::num(0) + // osg::Light::ambient(0,0,0,1) + // osg::Light::diffuse(0.8,0.8,0.8, 1) + // osg::Light::specular(1,1,1,1) + // + // Inventor uses different settings: + // SoEnvironment::ambientIntensity(0.2) + // SoEnvironment::ambientColor(1,1,1) + // SoDirectionalLight::intensity(1) + // SoDirectionalLight::color(1,1,1) + // + // note on Inventor light handling: + // ambient is set to 0,0,0,1 + // diffuse to color*intensity + // specular to color*intensity + + + #ifdef __COIN__ + // + // Handle transparency + // + // OSG supports GL_BLEND and GL_ALPHA_TEST + // Related classes: BlendFunc, BlendEquation, BlendColor, AlphaFunc + // + // Inventor is more difficult and not so robust. According to Inventor 2.1 standard, + // just SoMaterial::transparency, SoTexture2 with alpha channel carry transparency information + // that is controlled by SoGLRenderAction::transparency type that is set to SCREEN_DOOR by default + // (dither pattern). So, if the user does not select better transparency type, there is no + // possiblity to control transparency type from file format. + // + // However, SoTransparencyType node was introduced to overcome this historical limitation + // when transparency was performance expensive. Support by different Inventor branches: + // + // SGI Inventor: no + // Coin: since 2.0 + // TGS Inventor: since 5.0 + // + // Alpha test was introduced in TGS 4.0, but as SoGLRenderArea::setAlphaTest. So, no direct + // control in the file format. + // + // Conclusion: + // - Alpha test is not supported and all pixels will be drawn + // - Blending - current strategy is following: + // ADD x BLEND - ADD is used if destination-blending-factor is GL_ONE + // DELAYED rendering is used if transparency bin is used by OSG + // SORTED_OBJECT is used if ... + // + + updateMode(ivState->osgBlendEnabled, ss->getMode(GL_BLEND)); + ivState->osgBlendFunc = dynamic_cast(ss->getAttribute(osg::StateAttribute::BLENDFUNC)); + + if (ivState->osgBlendEnabled != ivPrevState->osgBlendEnabled || + ivState->osgBlendFunc != ivPrevState->osgBlendFunc || + (ivState->osgBlendFunc && ivPrevState->osgBlendFunc && + ivState->osgBlendFunc->getDestinationRGB() != ivPrevState->osgBlendFunc->getDestinationRGB())) { + + const SoTransparencyType::Type transparencyTable[8] = { + SoTransparencyType::BLEND, SoTransparencyType::ADD, + SoTransparencyType::DELAYED_BLEND, SoTransparencyType::DELAYED_ADD, + // FIXME: add sorted modes and test previous four + }; + + int index = 0; + if (!ivState->osgBlendFunc) index |= 0; + else index = (ivState->osgBlendFunc->getDestinationRGB() == osg::BlendFunc::ONE) ? 1 : 0; + index |= (ss->getRenderingHint() == osg::StateSet::TRANSPARENT_BIN) ? 2 : 0; + + SoTransparencyType *ivTransparencyType = new SoTransparencyType; + ivTransparencyType->value = transparencyTable[index]; + ivState->ivSeparator->addChild(ivTransparencyType); + } + #endif + } + + // ref Inventor nodes that are required when processing Drawables + if (ivState->ivTexture) + ivState->ivTexture->ref(); + if (ivState->ivMaterial) + ivState->ivMaterial->ref(); + + return ivState; +} + + +void ConvertToInventor::popInventorState() +{ + InventorState *ivState = &ivStack.top(); + + // unref Inventor nodes + if (ivState->ivTexture) + ivState->ivTexture->unref(); + if (ivState->ivMaterial) + ivState->ivMaterial->unref(); + + ivStack.pop(); +} + + +static bool processPrimitiveSet(const osg::Geometry *g, const osg::PrimitiveSet *pset, + osg::UIntArray *drawElemIndices, bool needSeparateTriangles, + int elementsCount, int primSize, const int startIndex, int stopIndex, + int &normalIndex, int &colorIndex, + SoNode *ivCoords, SoNormal *ivNormals, SoNode *ivMaterial, + SoNode *ivTexCoords, SoNode *ivTexture, SoShape *shape, + SoSeparator *&indexedRoot, SoSeparator *&nonIndexedRoot) +{ + bool ok = true; + const osg::DrawArrayLengths *drawArrayLengths = + (elementsCount == -1) ? dynamic_cast(pset) : NULL; + int drawArrayLengthsElems; + + if (drawArrayLengths) { + + // compute elementsCount + elementsCount = 0; + drawArrayLengthsElems = 0; + for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin(); + primItr!=drawArrayLengths->end(); + ++primItr, drawArrayLengthsElems++) + elementsCount += *primItr; + + // update stopIndex + stopIndex = startIndex + elementsCount; + } + + // NonIndexed data for nonIndexed shapes + SoNode *nonIndexedCoords = NULL; + SoNode *nonIndexedTexCoords = NULL; + SoNormal *nonIndexedNormals = NULL; + SoMaterial *nonIndexedMaterial = NULL; + + // Normal indexing + int normalStart = g->getNormalBinding() == osg::Geometry::BIND_PER_VERTEX ? startIndex : normalIndex; + int numNormalsUsed; + switch (g->getNormalBinding()) { + case osg::Geometry::BIND_OFF: // FIXME: what is meaning of OFF value? + case osg::Geometry::BIND_OVERALL: numNormalsUsed = 0; break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: numNormalsUsed = 1; break; + case osg::Geometry::BIND_PER_PRIMITIVE: numNormalsUsed = primSize!=0 ? (stopIndex-startIndex)/primSize : + (drawArrayLengths ? drawArrayLengths->size() : 1); break; + case osg::Geometry::BIND_PER_VERTEX: numNormalsUsed = stopIndex-startIndex; break; + } + normalIndex += numNormalsUsed; + + // Color indexing + int colorStart = g->getColorBinding() == osg::Geometry::BIND_PER_VERTEX ? startIndex : colorIndex; + int numColorsUsed; + switch (g->getColorBinding()) { + case osg::Geometry::BIND_OFF: + case osg::Geometry::BIND_OVERALL: numColorsUsed = 0; break; + case osg::Geometry::BIND_PER_PRIMITIVE_SET: numColorsUsed = 1; break; + case osg::Geometry::BIND_PER_PRIMITIVE: numColorsUsed = primSize!=0 ? (stopIndex-startIndex)/primSize : + (drawArrayLengths ? drawArrayLengths->size() : 1); break; + case osg::Geometry::BIND_PER_VERTEX: numColorsUsed = stopIndex-startIndex; break; + } + colorIndex += numColorsUsed; + + if (shape->isOfType(SoIndexedShape::getClassTypeId())) { + + // Convert to SoIndexedShape + processIndices(g->getVertexIndices(), drawElemIndices, ((SoIndexedShape*)shape)->coordIndex, + startIndex, stopIndex, primSize); + + if (ivNormals) + processIndices(g->getNormalIndices(), drawElemIndices, ((SoIndexedShape*)shape)->normalIndex, + normalStart, normalStart+(numNormalsUsed==0 ? 1 : numNormalsUsed), + g->getNormalBinding()==osg::Geometry::BIND_PER_VERTEX ? primSize : 0); + + if (ivMaterial) + processIndices(g->getColorIndices(), drawElemIndices, ((SoIndexedShape*)shape)->materialIndex, + colorStart, colorStart+(numColorsUsed==0 ? 1 : numColorsUsed), + g->getColorBinding()==osg::Geometry::BIND_PER_VERTEX ? primSize : 0); + + if (ivTexCoords && !ivTexCoords->isOfType(SoTextureCoordinateFunction::getClassTypeId())) + processIndices(g->getTexCoordIndices(0), drawElemIndices, ((SoIndexedShape*)shape)->textureCoordIndex, + startIndex, stopIndex, primSize); + + // Post-processing for DrawArrayLengths + if (drawArrayLengths && primSize==0 && drawArrayLengths->size()>=2) { + + postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->coordIndex); + + if (ivNormals && g->getNormalBinding()==osg::Geometry::BIND_PER_VERTEX) + postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->normalIndex); + + if (ivMaterial && g->getColorBinding()==osg::Geometry::BIND_PER_VERTEX) + postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->materialIndex); + + if (ivTexCoords && !ivTexCoords->isOfType(SoTextureCoordinateFunction::getClassTypeId())) + postProcessDrawArrayLengths(drawArrayLengths, &((SoIndexedShape*)shape)->textureCoordIndex); + } + + if (needSeparateTriangles) + postProcessTriangleSeparation((SoIndexedShape*)shape, (osg::PrimitiveSet::Mode)pset->getMode(), + g->getNormalBinding(), g->getColorBinding()); + + } else { + + // Convert to SoNonIndexedShape + + assert(shape->isOfType(SoNonIndexedShape::getClassTypeId()) && "Shape must be non-indexed."); + + int i,n = stopIndex-startIndex; + + // create alternate coordinates + if (ivCoords->isOfType(SoCoordinate4::getClassTypeId())) { + nonIndexedCoords = new SoCoordinate4; + ((SoCoordinate4*)nonIndexedCoords)->point.setNum(n); + if (ok) + ok = processArray(g->getVertexIndices(), + drawElemIndices, + &((SoCoordinate4*)nonIndexedCoords)->point, + &((SoCoordinate4*)ivCoords)->point, + startIndex, n); + } else { + nonIndexedCoords = new SoCoordinate3; + ((SoCoordinate3*)nonIndexedCoords)->point.setNum(n); + if (ok) + ok = processArray(g->getVertexIndices(), + drawElemIndices, + &((SoCoordinate3*)nonIndexedCoords)->point, + &((SoCoordinate3*)ivCoords)->point, + startIndex, n); + } + + // create alternate texture coordinates + if (ivTexCoords) + if (ivTexCoords->isOfType(SoTextureCoordinate2::getClassTypeId())) { + nonIndexedTexCoords = new SoTextureCoordinate2; + ((SoTextureCoordinate2*)nonIndexedTexCoords)->point.setNum(n); + if (ok) + ok = processArray(g->getTexCoordIndices(0), + drawElemIndices, + &((SoTextureCoordinate2*)nonIndexedTexCoords)->point, + &((SoTextureCoordinate2*)ivTexCoords)->point, + startIndex, n); + } else + if (ivTexCoords->isOfType(SoTextureCoordinate3::getClassTypeId())) { + nonIndexedTexCoords = new SoTextureCoordinate3; + ((SoTextureCoordinate3*)nonIndexedTexCoords)->point.setNum(n); + if (ok) + ok = processArray(g->getTexCoordIndices(0), + drawElemIndices, + &((SoTextureCoordinate3*)nonIndexedTexCoords)->point, + &((SoTextureCoordinate3*)ivCoords)->point, + startIndex, n); + } else + nonIndexedTexCoords = ivTexCoords; + + // create alternate normals + if (ivNormals) { + nonIndexedNormals = new SoNormal; + nonIndexedNormals->vector.setNum(numNormalsUsed==0 ? 1 : numNormalsUsed); + if (ok) + ok = processArray(g->getNormalIndices(), + g->getNormalBinding()==osg::Geometry::BIND_PER_VERTEX ? drawElemIndices : NULL, + &nonIndexedNormals->vector, &ivNormals->vector, + normalStart, numNormalsUsed==0 ? 1 : numNormalsUsed); + } + + // create alternate material + if (ivMaterial) { + nonIndexedMaterial = new SoMaterial; + nonIndexedMaterial->diffuseColor.setNum(numColorsUsed==0 ? 1 : numColorsUsed); + if (ok) { + // FIXME: diffuse only? + SoMFColor *colorField = (ivMaterial->isOfType(SoMaterial::getClassTypeId())) ? + &((SoMaterial*)ivMaterial)->diffuseColor : + &((SoBaseColor*)ivMaterial)->rgb; + ok = processArray(g->getColorIndices(), + g->getColorBinding()==osg::Geometry::BIND_PER_VERTEX ? drawElemIndices : NULL, + &nonIndexedMaterial->diffuseColor, colorField, + colorStart, numColorsUsed==0 ? 1 : numColorsUsed); + } + } + + if (shape->isOfType(SoPointSet::getClassTypeId())) + ((SoPointSet*)shape)->numPoints.setValue(elementsCount); else + + if (shape->isOfType(SoLineSet::getClassTypeId())) { + switch (pset->getMode()) { + case GL_LINES: + assert(elementsCount % 2 == 0 && "elementsCount is not multiple of 2."); + n = elementsCount/2; + ((SoLineSet*)shape)->numVertices.setNum(n); + for (i=0; inumVertices.set1Value(i, 2); + break; + case GL_LINE_STRIP: + if (drawArrayLengths) { + ((SoLineSet*)shape)->numVertices.setNum(drawArrayLengthsElems); + int i=0; + for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin(); + primItr!=drawArrayLengths->end(); + ++primItr, i++) + ((SoLineSet*)shape)->numVertices.set1Value(i, *primItr); + } else { + ((SoLineSet*)shape)->numVertices.setNum(1); + ((SoLineSet*)shape)->numVertices.set1Value(0, elementsCount); + } + break; + default: + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + assert(0); + } + } else + + if (shape->isOfType(SoTriangleStripSet::getClassTypeId())) { + switch (pset->getMode()) { + case GL_TRIANGLES: + n = elementsCount/3; + assert(n*3 == elementsCount && "elementsCount is not multiple of 3."); + ((SoTriangleStripSet*)shape)->numVertices.setNum(n); + for (i=0; inumVertices.set1Value(i, 3); + break; + case GL_TRIANGLE_STRIP: + if (drawArrayLengths) { + ((SoTriangleStripSet*)shape)->numVertices.setNum(drawArrayLengthsElems); + int i=0; + for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin(); + primItr!=drawArrayLengths->end(); + ++primItr, i++) + ((SoTriangleStripSet*)shape)->numVertices.set1Value(i, *primItr); + } else { + ((SoTriangleStripSet*)shape)->numVertices.setNum(1); + ((SoTriangleStripSet*)shape)->numVertices.set1Value(0, elementsCount); + } + break; + case GL_TRIANGLE_FAN: + osg::notify(osg::WARN) << "IvWriter: GL_TRIANGLE_FAN NOT IMPLEMENTED" << std::endl; + ((SoTriangleStripSet*)shape)->numVertices.setNum(1); + ((SoTriangleStripSet*)shape)->numVertices.set1Value(0, elementsCount); + break; + case GL_QUAD_STRIP: + assert(elementsCount % 2 == 0 && "elementsCount is not multiple of 2."); + if (drawArrayLengths) { + ((SoTriangleStripSet*)shape)->numVertices.setNum(drawArrayLengthsElems); + int i=0; + for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin(); + primItr!=drawArrayLengths->end(); + ++primItr, i++) + ((SoTriangleStripSet*)shape)->numVertices.set1Value(i, *primItr); + } else { + ((SoTriangleStripSet*)shape)->numVertices.setNum(1); + ((SoTriangleStripSet*)shape)->numVertices.set1Value(0, elementsCount); + } + break; + default: + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + assert(0); + } + } else + + if (shape->isOfType(SoFaceSet::getClassTypeId())) { + switch (pset->getMode()) { + case GL_QUADS: + n = elementsCount/4; + assert(n*4 == elementsCount && "elementsCount is not multiple of 4."); + ((SoFaceSet*)shape)->numVertices.setNum(n); + for (i=0; inumVertices.set1Value(i, 4); + break; + case GL_POLYGON: + if (drawArrayLengths) { + ((SoFaceSet*)shape)->numVertices.setNum(drawArrayLengthsElems); + int i=0; + for (osg::DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin(); + primItr!=drawArrayLengths->end(); + ++primItr, i++) + ((SoFaceSet*)shape)->numVertices.set1Value(i, *primItr); + } else { + ((SoFaceSet*)shape)->numVertices.setNum(1); + ((SoFaceSet*)shape)->numVertices.set1Value(0, elementsCount); + } + break; + default: + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + assert(0); + } + } else { + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + assert(0 && "Unknown non-indexed shape type."); + } + } + + // Construct graph + + // Each osg::Drawable will have its own SoSeparator (render caching, culling, etc.) + SoSeparator *sep = new SoSeparator; + if (nonIndexedCoords) { + assert(shape->isOfType(SoNonIndexedShape::getClassTypeId()) && "Not nonIndexed shape."); + + if (!ok) { + + // handle errors + nonIndexedCoords->ref(); + nonIndexedCoords->unref(); + if (nonIndexedTexCoords) { nonIndexedTexCoords->ref(); nonIndexedTexCoords->unref(); } + if (nonIndexedNormals) { nonIndexedNormals->ref(); nonIndexedNormals->unref(); } + nonIndexedMaterial->ref(); + nonIndexedMaterial->unref(); + shape->ref(); + shape->unref(); + sep->ref(); + sep->unref(); + } else { + + // make scene graph + if (nonIndexedRoot == NULL) { + nonIndexedRoot = new SoSeparator; + if (ivTexture) nonIndexedRoot->addChild(ivTexture); + if (ivTexture) nonIndexedRoot->addChild(createTexCoordBinding(FALSE)); + nonIndexedRoot->addChild(createMaterialBinding(g, FALSE)); + if (ivNormals) nonIndexedRoot->addChild(createNormalBinding(g, FALSE)); + } + if (nonIndexedMaterial) sep->addChild(nonIndexedMaterial); + sep->addChild(nonIndexedCoords); + if (nonIndexedNormals) sep->addChild(nonIndexedNormals); + if (nonIndexedTexCoords) sep->addChild(nonIndexedTexCoords); + sep->addChild(shape); + nonIndexedRoot->addChild(sep); + } + } else { + assert(shape->isOfType(SoIndexedShape::getClassTypeId()) && "Not indexed shape."); + assert(nonIndexedCoords==NULL && nonIndexedNormals==NULL && nonIndexedMaterial==NULL); + if (indexedRoot == NULL) { + indexedRoot = new SoSeparator; + if (ivTexture) indexedRoot->addChild(ivTexture); + if (ivTexture) indexedRoot->addChild(createTexCoordBinding(TRUE)); + if (ivMaterial) indexedRoot->addChild(ivMaterial); + indexedRoot->addChild(createMaterialBinding(g, TRUE)); + indexedRoot->addChild(ivCoords); + if (ivNormals) indexedRoot->addChild(ivNormals); + if (ivNormals) indexedRoot->addChild(createNormalBinding(g, TRUE)); + if (ivTexCoords) indexedRoot->addChild(ivTexCoords); + } + sep->addChild(shape); + indexedRoot->addChild(sep); + } + + return ok; +} + + +void ConvertToInventor::processGeometry(const osg::Geometry *g, InventorState *ivState) +{ + int normalIndex = 0; + int colorIndex = 0; + + // Inventor scene graph roots + SoSeparator *indexedRoot = NULL; + SoSeparator *nonIndexedRoot = NULL; + + // Active material + SoNode *ivMaterial = ivState->ivMaterial; + + // Convert color array into the SoMaterial + if (g->getColorArray()) { + + // Make copy of SoMaterial (we need to modify it) + ivMaterial = (ivMaterial!=NULL) ? ivMaterial->copy() : + (ivState->osgLighting || vrml1Conversion) ? (SoNode*)new SoMaterial : new SoBaseColor; + + SoMFColor *colorField = (ivMaterial->isOfType(SoMaterial::getClassTypeId())) ? + &((SoMaterial*)ivMaterial)->diffuseColor : + &((SoBaseColor*)ivMaterial)->rgb; + + if (ivState->osgMaterial) { + + // Color array with material + if (ivState->osgMaterial->getColorMode() == osg::Material::DIFFUSE || + ivState->osgMaterial->getColorMode() == osg::Material::AMBIENT_AND_DIFFUSE) + osgArray2ivMField(g->getColorArray(), *colorField); + else; // FIXME: implement some workaround for non-diffuse cases? + // note: Warning was already shown in createInventorState(). + // note2: There is no effect to convert SoMaterial::[ambient|specular|emissive]color + // here because Inventor does not set them per-vertex (performance reasons). See + // Inventor documentation for more details. + + } else { + + // Color array without material + + // FIXME: Should some lighting model be set here? + // FIXME: What about ambientColor, specular and other fields of material? + // What values should go inside? + osgArray2ivMField(g->getColorArray(), *colorField); + } + } + + + // Convert coordinates + // OSG represents coordinates by: Vec2, Vec3, Vec4 + // Inventor by: SbVec3f, SbVec4f + SoNode *coords; + if (g->getVertexArray()->getDataSize() == 4) { + coords = new SoCoordinate4; + osgArray2ivMField(g->getVertexArray(), ((SoCoordinate4*)coords)->point); + } else { + coords = new SoCoordinate3; + osgArray2ivMField(g->getVertexArray(), ((SoCoordinate3*)coords)->point); + } + coords->ref(); + + // Convert texture coordinates + SoNode *texCoords = NULL; + if (ivState->ivTexture) { + if (ivState->osgTexGenS && ivState->osgTexGenT && + ivState->osgTexGen && ivState->osgTexGen->getMode()==osg::TexGen::SPHERE_MAP) + texCoords = new SoTextureCoordinateEnvironment; + else + if (g->getTexCoordArray(0)) { + if (g->getTexCoordArray(0)->getDataSize() <= 2) { + texCoords = new SoTextureCoordinate2; + osgArray2ivMField(g->getTexCoordArray(0), ((SoTextureCoordinate2*)texCoords)->point); + } else { + texCoords = new SoTextureCoordinate3; + osgArray2ivMField(g->getTexCoordArray(0), ((SoTextureCoordinate3*)texCoords)->point); + } + } + if (texCoords) + texCoords->ref(); + } + + // Convert normals + // OSG represents normals by: Vec3,Vec3s,Vec3b + // and can handle: Vec4s,Vec4b by truncating them to three components + // Inventor by: SbVec3f + SoNormal *normals = NULL; + if (g->getNormalArray()) { + normals = new SoNormal; + osgArray2ivMField(g->getNormalArray(), normals->vector); + normals->ref(); + } + + // Convert osg::PrimitiveSets to Inventor's SoShapes + int psetIndex,numPsets = g->getNumPrimitiveSets(); + for (psetIndex=0; psetIndexgetPrimitiveSet(psetIndex); + osg::PrimitiveSet::Type type = pset->getType(); + GLenum mode = pset->getMode(); + + // Create appropriate SoShape + bool useIndices = g->getVertexIndices() != NULL || vrml1Conversion; + bool needSeparateTriangles = false; + SoVertexShape *shape = NULL; + switch (mode) { + case GL_POINTS: shape = new SoPointSet; break; + case GL_LINES: + case GL_LINE_STRIP: + case GL_LINE_LOOP: if (useIndices) shape = new SoIndexedLineSet; + else shape = new SoLineSet; + break; + case GL_TRIANGLES: + case GL_TRIANGLE_STRIP: + case GL_QUAD_STRIP: if (useIndices) + if (vrml1Conversion) { + shape = new SoIndexedFaceSet; + needSeparateTriangles = true; + } else + shape = new SoIndexedTriangleStripSet; + else + shape = new SoTriangleStripSet; + break; + case GL_TRIANGLE_FAN: needSeparateTriangles = true; + shape = (vrml1Conversion) ? (SoVertexShape*)new SoIndexedFaceSet : + new SoIndexedTriangleStripSet; + break; + case GL_QUADS: + case GL_POLYGON: if (useIndices) shape = new SoIndexedFaceSet; + else shape = new SoFaceSet; + break; + default: assert(0); + } + + // Size of single geometric primitive + int primSize; + switch (mode) { + case GL_LINES: primSize = 2; break; + case GL_TRIANGLES: primSize = 3; break; + case GL_QUADS: primSize = 4; break; + default: primSize = 0; + }; + + + bool ok = true; + + switch (type) { + + case osg::PrimitiveSet::DrawArraysPrimitiveType: + { + const osg::DrawArrays *drawArrays = dynamic_cast(pset); + + int startIndex = drawArrays->getFirst(); + int stopIndex = startIndex + drawArrays->getCount(); + + // FIXME: Am I using startIndex for all bundles that are PER_VERTEX? + ok = processPrimitiveSet(g, pset, NULL, needSeparateTriangles, + drawArrays->getCount(), primSize, + startIndex, stopIndex, normalIndex, colorIndex, + coords, normals, ivMaterial, texCoords, + ivState->ivTexture, shape, indexedRoot, nonIndexedRoot); + break; + } + + case osg::PrimitiveSet::DrawArrayLengthsPrimitiveType: + { + const osg::DrawArrayLengths *drawArrayLengths = + dynamic_cast(pset); + + int startIndex = drawArrayLengths->getFirst(); + + ok = processPrimitiveSet(g, pset, NULL, needSeparateTriangles, + -1, primSize, startIndex, -1, normalIndex, colorIndex, + coords, normals, ivMaterial, texCoords, + ivState->ivTexture, shape, indexedRoot, nonIndexedRoot); + + break; + } + + case osg::PrimitiveSet::DrawElementsUBytePrimitiveType: + case osg::PrimitiveSet::DrawElementsUShortPrimitiveType: + case osg::PrimitiveSet::DrawElementsUIntPrimitiveType: + { + osg::ref_ptr drawElemIndices = new osg::UIntArray; + + switch (type) { + case osg::PrimitiveSet::DrawElementsUBytePrimitiveType: + { + const osg::DrawElementsUByte *drawElements = + dynamic_cast(pset); + for(osg::DrawElementsUByte::const_iterator primItr = drawElements->begin(); + primItr!=drawElements->end(); + ++primItr) + drawElemIndices->push_back(*primItr); + break; + } + case osg::PrimitiveSet::DrawElementsUShortPrimitiveType: + { + const osg::DrawElementsUShort *drawElements = + dynamic_cast(pset); + for(osg::DrawElementsUShort::const_iterator primItr = drawElements->begin(); + primItr!=drawElements->end(); + ++primItr) + drawElemIndices->push_back(*primItr); + break; + } + case osg::PrimitiveSet::DrawElementsUIntPrimitiveType: + { + const osg::DrawElementsUInt *drawElements = + dynamic_cast(pset); + for(osg::DrawElementsUInt::const_iterator primItr = drawElements->begin(); + primItr!=drawElements->end(); + ++primItr) + drawElemIndices->push_back(*primItr); + break; + } + default: assert(0); + } + + ok = processPrimitiveSet(g, pset, drawElemIndices.get(), needSeparateTriangles, + drawElemIndices->getNumElements(), primSize, + 0, drawElemIndices->getNumElements(), normalIndex, colorIndex, + coords, normals, ivMaterial, texCoords, + ivState->ivTexture, shape, indexedRoot, nonIndexedRoot); + break; + } + + default: + osg::notify(osg::WARN) << "IvWriter: NOT IMPLEMENTED" << std::endl; + } + } + + if (indexedRoot) ivState->ivSeparator->addChild(indexedRoot); + if (nonIndexedRoot) ivState->ivSeparator->addChild(nonIndexedRoot); + + coords->unref(); + if (texCoords) texCoords->unref(); + if (normals) normals->unref(); +} + + +void ConvertToInventor::processShapeDrawable(const osg::ShapeDrawable *d, InventorState *ivState) +{ + // visitor for converting ShapeDrawables + class MyShapeVisitor : public osg::ConstShapeVisitor { + public: + void processNode(SoNode *ivNode, const osg::Vec3& center, osg::Quat rotation, + SoSeparator *root) { + // convert rotation + rotation = osg::Quat(-M_PI_2, osg::Vec3(0.,1.,0.)) * osg::Quat(M_PI_2, osg::Vec3(1.,0.,0.)) * rotation; + + if (center.length2()==0. && rotation.zeroRotation() && ivState->ivTexture==NULL) + + // optimized handling of single node + root->addChild(ivNode); + + else { + SoSeparator *root2 = new SoSeparator; + + // handle transformation + if (center.length2()!=0. || !rotation.zeroRotation()) { + SoTransform *ivTransform = new SoTransform; + setSoTransform(ivTransform, center, rotation); + root2->addChild(ivTransform); + } + + // handle texture + if (ivState->ivTexture) + root2->addChild(ivState->ivTexture); + + // build graph + root2->addChild(ivNode); + root->addChild(root2); + } + } + + virtual void apply(const osg::Sphere &s) { + SoSphere *ivSphere = new SoSphere; + ivSphere->radius.setValue(s.getRadius()); + processNode(ivSphere, s.getCenter(), osg::Quat(0., osg::Vec3(1.,0.,0.)), ivState->ivSeparator); + } + virtual void apply(const osg::Box &b) { + SoCube *ivCube = new SoCube; + ivCube->width = 2 * b.getHalfLengths().y(); + ivCube->height = 2 * b.getHalfLengths().z(); + ivCube->depth = 2 * b.getHalfLengths().x(); + processNode(ivCube, b.getCenter(), b.getRotation(), ivState->ivSeparator); + } + virtual void apply(const osg::Cone &c) { + SoCone *ivCone = new SoCone; + ivCone->bottomRadius = c.getRadius(); + ivCone->height = c.getHeight(); + osg::Vec3 newCenter(c.getCenter()); + newCenter.ptr()[2] -= c.getBaseOffset(); + processNode(ivCone, newCenter, c.getRotation(), ivState->ivSeparator); + } + virtual void apply(const osg::Cylinder &c) { + SoCylinder *ivCylinder = new SoCylinder; + ivCylinder->radius = c.getRadius(); + ivCylinder->height = c.getHeight(); + processNode(ivCylinder, c.getCenter(), c.getRotation(), ivState->ivSeparator); + } + + void warnNonSupported() { + osg::notify(osg::WARN) << "IvWriter: Not supported ShapeDrawable found. Skipping it." << std::endl; + } + virtual void apply(const osg::Capsule&) { warnNonSupported(); } + virtual void apply(const osg::InfinitePlane&) { warnNonSupported(); } + virtual void apply(const osg::TriangleMesh&) { warnNonSupported(); } + virtual void apply(const osg::ConvexHull&) { warnNonSupported(); } + virtual void apply(const osg::HeightField&) { warnNonSupported(); } + virtual void apply(const osg::CompositeShape&) { warnNonSupported(); } + + InventorState *ivState; + MyShapeVisitor(InventorState *ivState) { this->ivState = ivState; } + } shapeVisitor(ivState); + + // convert ShapeDrawable + const osg::Shape *shape = d->getShape(); + if (shape) + shape->accept(shapeVisitor); +} + + +void ConvertToInventor::apply(osg::Geode &node) +{ +#ifdef DEBUG_IV_WRITER + osg::notify(osg::INFO) << "IvWriter: Geode traversed" << std::endl; +#endif + + // Considerations for VRML1 support: + // + // SoSeparator: supported (only renderCulling field) + // SoCoordinate3: supported + // SoCoordinate4: no -> conversion to Coordinate3 required <---- missing + // SoTextureCoordinate2: supported + // SoTextureCoordinate3: no - 3D textures not supported by VRML1 + // SoTextureCoordinateEnvironment no <- should texturing be disabled in this case? + // SoTextureCoordinateBinding: no -> indexing is probably mandatory + // SoNormal: supported + // SoNormalBinding: supported (all modes supported, including non-indexed modes) + // SoMaterial: supported + // SoMaterialBinding: supported (all modes supported, including non-indexed modes) + // SoPointSet: supported + // SoLineSet: no -> using SoIndexedListSet instead + // SoIndexedLineSet: supported + // SoIndexedTriangleStripSet: no -> using SoIndexedFaceSet instead + // SoTriangleStripSet: no -> using SoIndexedFaceSet instead + // SoMatrixTransform: supported + // SoTransform: supported + // SoTransparencyType: no <---- missing + // SoShapeHints: supported + + + + // Node list from VRML1 documentation: + // shape nodes: AsciiText, Cone, Cube, Cylinder, IndexedFaceSet, IndexedLineSet, PointSet, Sphere + // properties nodes: Coordinate3, FontStyle, Info, Material, MaterialBinding, Normal, NormalBinding, + // Texture2, Texture2Transform, TextureCoordinate2, ShapeHints + // transformantion nodes: MatrixTransform, Rotation, Scale, Transform, Translation + // group nodes: Separator, Switch, WWWAnchor, LOD + // cameras: OrthographicCamera, PerspectiveCamera + // lights: DirectionalLight, PointLight, SpotLight + // others: WWWInline + + + // Create SoSeparator and convert StateSet for Geode + InventorState *ivGeodeState = createInventorState(node.getStateSet()); + + + // Make loop throughout the drawables + const int numDrawables = node.getNumDrawables(); + for (int drawableIndex=0; drawableIndexasGeometry(); // FIXME: other drawables have to be handled also + osg::ShapeDrawable *sd; + + // Create SoSeparator and convert StateSet for Drawable + InventorState *ivDrawableState = createInventorState(d->getStateSet()); + + if (g != NULL) + processGeometry(g, ivDrawableState); + else + + if ((sd = dynamic_cast(d)) != NULL) { + processShapeDrawable(sd, ivDrawableState); + } + else + osg::notify(osg::WARN) << "IvWriter: Unsupported drawable found: \"" << d->className() << + "\". Skipping it." << std::endl; + + // Restore state + popInventorState(); + } + + traverse(node); + + // Restore state + popInventorState(); +} + + +void ConvertToInventor::apply(osg::Group &node) +{ +#ifdef DEBUG_IV_WRITER + osg::notify(osg::INFO) << "IvWriter: Group traversed" << std::endl; +#endif + + // Create SoSeparator and convert StateSet + InventorState *ivState = createInventorState(node.getStateSet()); + + traverse(node); + + popInventorState(); +} + + +void ConvertToInventor::apply(osg::MatrixTransform& node) +{ +#ifdef DEBUG_IV_WRITER + osg::notify(osg::INFO) << "IvWriter: MatrixTransform traversed" << std::endl; +#endif + + // Convert matrix + SoMatrixTransform *ivTransform = new SoMatrixTransform; + SbMatrix ivMatrix; + const double *src = node.getMatrix().ptr(); + float *dest = ivMatrix[0]; + for (int i=0; i<16; i++,dest++,src++) + *dest = *src; + ivTransform->matrix.setValue(ivMatrix); + + // Create SoSeparator and convert StateSet + InventorState *ivState = createInventorState(node.getStateSet()); + ivState->ivSeparator->addChild(ivTransform); + + traverse((osg::Node&)node); + + popInventorState(); +} + + +void ConvertToInventor::apply(osg::PositionAttitudeTransform& node) +{ +#ifdef DEBUG_IV_WRITER + osg::notify(osg::INFO) << "IvWriter: MatrixTransform traversed" << std::endl; +#endif + + // Convert matrix + SoTransform *ivTransform = new SoTransform; + setSoTransform(ivTransform, node.getPosition(), node.getAttitude(), node.getScale()); + + // Create SoSeparator and convert StateSet + InventorState *ivState = createInventorState(node.getStateSet()); + ivState->ivSeparator->addChild(ivTransform); + + traverse((osg::Node&)node); + + popInventorState(); +} diff --git a/src/osgPlugins/Inventor/ConvertToInventor.h b/src/osgPlugins/Inventor/ConvertToInventor.h new file mode 100644 index 000000000..3dc4a7e13 --- /dev/null +++ b/src/osgPlugins/Inventor/ConvertToInventor.h @@ -0,0 +1,138 @@ +#ifndef OSG_CONVERT_TO_INVENTOR_H +#define OSG_CONVERT_TO_INVENTOR_H +// +// ConvertToInventor converts OSG scene graph to Inventor or VRML 1 scene graph +// +// It requires OSG and Inventor compatible library, such as Coin, +// SGI Inventor , or TGS Inventor. +// +// +// Autor: PCJohn (peciva _at fit.vutbr.cz) +// +// License: public domain +// +// +// THIS SOFTWARE IS NOT COPYRIGHTED +// +// This source code is offered for use in the public domain. +// You may use, modify or distribute it freely. +// +// This source code is distributed in the hope that it will be useful but +// WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY +// DISCLAIMED. This includes but is not limited to warranties of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// +// If you find the source code useful, authors will kindly welcome +// if you give them credit and keep their names with their source code, +// but you are not forced to do so. +// + +#include +#include +#include + +class SoSeparator; + +namespace osg { + class BlendFunc; + class CullFace; + class FrontFace; + class Material; + class ShapeDrawable; + class TexEnv; + class TexGen; +}; + + + +class ConvertToInventor : public osg::NodeVisitor +{ +public: + ConvertToInventor(); + virtual ~ConvertToInventor(); + + SoNode* getIvSceneGraph() const; + void setVRML1Conversion(bool useVRML1) { vrml1Conversion = useVRML1; }; + + virtual void apply(osg::Node &node); + virtual void apply(osg::Geode &node); + virtual void apply(osg::Group &node); +#if 0 + virtual void apply(Billboard& node) { apply((Geode&)node); } + virtual void apply(ProxyNode& node) { apply((Group&)node); } + virtual void apply(Projection& node) { apply((Group&)node); } + virtual void apply(CoordinateSystemNode& node) { apply((Group&)node); } + + virtual void apply(ClipNode& node) { apply((Group&)node); } + virtual void apply(TexGenNode& node) { apply((Group&)node); } + virtual void apply(LightSource& node) { apply((Group&)node); } +#endif + //virtual void apply(Transform& node); + //virtual void apply(Camera& node) { apply((Transform&)node); } + //virtual void apply(CameraView& node) { apply((Transform&)node); } + virtual void apply(osg::MatrixTransform& node); + virtual void apply(osg::PositionAttitudeTransform& node); +#if 0 + virtual void apply(Switch& node) { apply((Group&)node); } + virtual void apply(Sequence& node) { apply((Group&)node); } + virtual void apply(LOD& node) { apply((Group&)node); } + virtual void apply(PagedLOD& node) { apply((LOD&)node); } + virtual void apply(ClearNode& node) { apply((Group&)node); } + virtual void apply(OccluderNode& node) { apply((Group&)node); } +#endif + +protected: + bool vrml1Conversion; + bool useIvExtensions; + SoSeparator *ivRoot; + + typedef struct InventorState { + class SoSeparator *ivSeparator; + class SoTexture2 *ivTexture; + class SoNode *ivMaterial; + const osg::Material *osgMaterial; + bool osgTexture2Enabled; + const osg::Texture *osgTexture; + const osg::TexEnv *osgTexEnv; + bool osgTexGenS, osgTexGenT; + const osg::TexGen *osgTexGen; + bool osgLighting; + bool osgTwoSided; + osg::FrontFace::Mode osgFrontFace; + bool osgCullFaceEnabled; + osg::CullFace::Mode osgCullFace; + bool osgBlendEnabled; + const osg::BlendFunc *osgBlendFunc; + + InventorState() {} + InventorState(SoSeparator *separator) : ivSeparator(separator), ivTexture(NULL), + ivMaterial(NULL), osgMaterial(NULL), + osgTexture2Enabled(false), osgTexture(NULL), osgTexEnv(NULL), + osgTexGenS(false), osgTexGenT(false), osgTexGen(NULL), + osgLighting(true), osgTwoSided(false), osgFrontFace(osg::FrontFace::COUNTER_CLOCKWISE), + osgCullFaceEnabled(false), osgCullFace(osg::CullFace::BACK), + osgBlendEnabled(false), osgBlendFunc(NULL) {} + InventorState(const InventorState &s) : ivSeparator(s.ivSeparator), ivTexture(s.ivTexture), + ivMaterial(s.ivMaterial), osgMaterial(s.osgMaterial), + osgTexture2Enabled(s.osgTexture2Enabled), osgTexture(s.osgTexture), osgTexEnv(s.osgTexEnv), + osgTexGenS(s.osgTexGenS), osgTexGenT(s.osgTexGenT), osgTexGen(osgTexGen), + osgLighting(s.osgLighting), osgTwoSided(s.osgTwoSided), osgFrontFace(s.osgFrontFace), + osgCullFaceEnabled(s.osgCullFaceEnabled), osgCullFace(s.osgCullFace), + osgBlendEnabled(s.osgBlendEnabled), osgBlendFunc(s.osgBlendFunc) {} + static InventorState createTopLevelState(SoSeparator *ivRoot) { return InventorState(ivRoot); } + }; + std::stack ivStack; + + typedef std::map Env2ivTexMap; + std::map ivTexturesMap; + int uniqueIdGenerator; + + void processGeometry(const osg::Geometry *g, InventorState *ivState); + void processShapeDrawable(const osg::ShapeDrawable *d, InventorState *ivState); + + virtual InventorState* createInventorState(const osg::StateSet *ss); + virtual void popInventorState(); +}; + + +#endif /* OSG_CONVERT_TO_INVENTOR_H */ diff --git a/src/osgPlugins/Inventor/README.txt b/src/osgPlugins/Inventor/README.txt index 9da4bf136..1086c8e8e 100644 --- a/src/osgPlugins/Inventor/README.txt +++ b/src/osgPlugins/Inventor/README.txt @@ -1,17 +1,27 @@ ######################################################## +# # # Inventor plugin # -# Supported file formats (import only): # +# # +# Supported import formats: # # - iv (ascii, binary) file format # -# - vrml 1.0 # -# - vrml 2.0 (when using Coin) # +# - VRML 1.0 # +# - VRML 2.0 (when using Coin) # +# # +# Supported export formats: # +# - iv format # +# - VRML 1.0 # +# # ######################################################## The plugin requires one of Inventor libraries: -- Coin (http://www.coin3d.org) - GPL, support of VRML 2.0 +- Coin - GPL, support of VRML 2.0 + (http://www.coin3d.org) - SGI Inventor - LGPL + (http://oss.sgi.com/projects/inventor/) - TGS Inventor - commercial + (http://www.tgs.com/) Contributors: diff --git a/src/osgPlugins/Inventor/ReaderWriterIV.cpp b/src/osgPlugins/Inventor/ReaderWriterIV.cpp index 0cdaa2b4f..2843ca37c 100644 --- a/src/osgPlugins/Inventor/ReaderWriterIV.cpp +++ b/src/osgPlugins/Inventor/ReaderWriterIV.cpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #ifdef COIN_BASIC_H #include @@ -17,6 +19,8 @@ #include "ConvertFromInventor.h" #include "GroupSoLOD.h" +#include "ConvertToInventor.h" + // Register with Registry to instantiate the inventor reader. REGISTER_OSGPLUGIN(Inventor, ReaderWriterIV) @@ -81,3 +85,46 @@ ReaderWriterIV::readNode(const std::string& file, return ReadResult::FILE_NOT_HANDLED; } + +osgDB::ReaderWriter::WriteResult +ReaderWriterIV::writeNode(const osg::Node& node, const std::string& fileName, + const osgDB::ReaderWriter::Options* options) const +{ + // accept extension + std::string ext = osgDB::getLowerCaseFileExtension(fileName); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + bool useVRML1 = !isInventorExtension(osgDB::getFileExtension(fileName)); + + osg::notify(osg::INFO) << "osgDB::ReaderWriterIV::writeNode() Writing file " + << fileName.data() << std::endl; + + // Initialize Inventor + SoInteraction::init(); + + // Convert OSG graph to Inventor graph + ConvertToInventor osg2iv; + osg2iv.setVRML1Conversion(useVRML1); + (const_cast(&node))->accept(osg2iv); + SoNode *ivRoot = osg2iv.getIvSceneGraph(); + if (ivRoot == NULL) + return WriteResult::ERROR_IN_WRITING_FILE; + ivRoot->ref(); + + // Change prefix according to VRML spec: + // Node names must not begin with a digit, and must not contain spaces or + // control characters, single or double quote characters, backslashes, curly braces, + // the sharp (#) character, the plus (+) character or the period character. + if (useVRML1) + SoBase::setInstancePrefix("_"); + + // Write Inventor graph to file + SoOutput out; + out.setHeaderString((useVRML1) ? "#VRML V1.0 ascii" : "#Inventor V2.1 ascii"); + if (!out.openFile(fileName.c_str())) + return WriteResult::ERROR_IN_WRITING_FILE; + SoWriteAction wa(&out); + wa.apply(ivRoot); + ivRoot->unref(); + + return WriteResult::FILE_SAVED; +} diff --git a/src/osgPlugins/Inventor/ReaderWriterIV.h b/src/osgPlugins/Inventor/ReaderWriterIV.h index 932fdcf7c..a8af72948 100644 --- a/src/osgPlugins/Inventor/ReaderWriterIV.h +++ b/src/osgPlugins/Inventor/ReaderWriterIV.h @@ -11,18 +11,25 @@ class ReaderWriterIV : public osgDB::ReaderWriter virtual const char* className() const { - return "Inventor Reader"; + return "Inventor reader/writer"; } + bool isInventorExtension(const std::string& extension) const + { + return osgDB::equalCaseInsensitive(extension, "iv") ? true : false; + } + virtual bool acceptsExtension(const std::string& extension) const { - return osgDB::equalCaseInsensitive(extension, "iv") ? true : + return isInventorExtension(extension) ? true : osgDB::equalCaseInsensitive(extension, "wrl") ? true : false; } virtual ReadResult readNode(const std::string& filename, const osgDB::ReaderWriter::Options *) const; + virtual WriteResult writeNode(const osg::Node& node, const std::string& filename, + const osgDB::ReaderWriter::Options* options = NULL) const; }; #endif