diff --git a/include/osg/Array b/include/osg/Array index f5c08b3ba..151edbdb1 100644 --- a/include/osg/Array +++ b/include/osg/Array @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -71,7 +72,8 @@ class OSG_EXPORT Array : public BufferData DoubleArrayType = 18, Vec2dArrayType = 19, Vec3dArrayType = 20, - Vec4dArrayType = 21 + Vec4dArrayType = 21, + MatrixArrayType = 22 }; Array(Type arrayType=ArrayType,GLint dataSize=0,GLenum dataType=0): @@ -309,6 +311,8 @@ typedef TemplateArray typedef TemplateArray Vec3dArray; typedef TemplateArray Vec4dArray; +typedef TemplateArray MatrixfArray; + class ArrayVisitor { @@ -343,6 +347,8 @@ class ArrayVisitor virtual void apply(Vec2dArray&) {} virtual void apply(Vec3dArray&) {} virtual void apply(Vec4dArray&) {} + + virtual void apply(MatrixfArray&) {} }; class ConstArrayVisitor @@ -378,6 +384,8 @@ class ConstArrayVisitor virtual void apply(const Vec2dArray&) {} virtual void apply(const Vec3dArray&) {} virtual void apply(const Vec4dArray&) {} + + virtual void apply(const MatrixfArray&) {} }; @@ -414,6 +422,8 @@ class ValueVisitor virtual void apply(Vec2d&) {} virtual void apply(Vec3d&) {} virtual void apply(Vec4d&) {} + + virtual void apply(Matrixf&) {} }; class ConstValueVisitor @@ -448,6 +458,8 @@ class ConstValueVisitor virtual void apply(const Vec2d&) {} virtual void apply(const Vec3d&) {} virtual void apply(const Vec4d&) {} + + virtual void apply(const Matrixf&) {} }; template diff --git a/include/osg/Matrixf b/include/osg/Matrixf index 178d46b6e..8a4d3cd7a 100644 --- a/include/osg/Matrixf +++ b/include/osg/Matrixf @@ -383,6 +383,116 @@ class OSG_EXPORT Matrixf return r; } + /** Multiply by scalar. */ + inline Matrixf operator * (value_type rhs) const + { + return Matrixf( + _mat[0][0]*rhs, _mat[0][1]*rhs, _mat[0][2]*rhs, _mat[0][3]*rhs, + _mat[1][0]*rhs, _mat[1][1]*rhs, _mat[1][2]*rhs, _mat[1][3]*rhs, + _mat[2][0]*rhs, _mat[2][1]*rhs, _mat[2][2]*rhs, _mat[2][3]*rhs, + _mat[3][0]*rhs, _mat[3][1]*rhs, _mat[3][2]*rhs, _mat[3][3]*rhs); + } + + /** Unary multiply by scalar. */ + inline Matrixf& operator *= (value_type rhs) + { + _mat[0][0]*=rhs; + _mat[0][1]*=rhs; + _mat[0][2]*=rhs; + _mat[0][3]*=rhs; + _mat[1][0]*=rhs; + _mat[1][1]*=rhs; + _mat[1][2]*=rhs; + _mat[1][3]*=rhs; + _mat[2][0]*=rhs; + _mat[2][1]*=rhs; + _mat[2][2]*=rhs; + _mat[2][3]*=rhs; + _mat[3][0]*=rhs; + _mat[3][1]*=rhs; + _mat[3][2]*=rhs; + _mat[3][3]*=rhs; + return *this; + } + + /** Divide by scalar. */ + inline Matrixf operator / (value_type rhs) const + { + return Matrixf( + _mat[0][0]/rhs, _mat[0][1]/rhs, _mat[0][2]/rhs, _mat[0][3]/rhs, + _mat[1][0]/rhs, _mat[1][1]/rhs, _mat[1][2]/rhs, _mat[1][3]/rhs, + _mat[2][0]/rhs, _mat[2][1]/rhs, _mat[2][2]/rhs, _mat[2][3]/rhs, + _mat[3][0]/rhs, _mat[3][1]/rhs, _mat[3][2]/rhs, _mat[3][3]/rhs); + } + + /** Unary divide by scalar. */ + inline Matrixf& operator /= (value_type rhs) + { + _mat[0][0]/=rhs; + _mat[0][1]/=rhs; + _mat[0][2]/=rhs; + _mat[0][3]/=rhs; + _mat[1][0]/=rhs; + _mat[1][1]/=rhs; + _mat[1][2]/=rhs; + _mat[1][3]/=rhs; + _mat[2][0]/=rhs; + _mat[2][1]/=rhs; + _mat[2][2]/=rhs; + _mat[2][3]/=rhs; + _mat[3][0]/=rhs; + _mat[3][1]/=rhs; + _mat[3][2]/=rhs; + _mat[3][3]/=rhs; + return *this; + } + + /** Binary vector add. */ + inline Matrixf operator + (const Matrixf& rhs) const + { + return Matrixf( + _mat[0][0] + rhs._mat[0][0], + _mat[0][1] + rhs._mat[0][1], + _mat[0][2] + rhs._mat[0][2], + _mat[0][3] + rhs._mat[0][3], + _mat[1][0] + rhs._mat[1][0], + _mat[1][1] + rhs._mat[1][1], + _mat[1][2] + rhs._mat[1][2], + _mat[1][3] + rhs._mat[1][3], + _mat[2][0] + rhs._mat[2][0], + _mat[2][1] + rhs._mat[2][1], + _mat[2][2] + rhs._mat[2][2], + _mat[2][3] + rhs._mat[2][3], + _mat[3][0] + rhs._mat[3][0], + _mat[3][1] + rhs._mat[3][1], + _mat[3][2] + rhs._mat[3][2], + _mat[3][3] + rhs._mat[3][3]); + } + + /** Unary vector add. Slightly more efficient because no temporary + * intermediate object. + */ + inline Matrixf& operator += (const Matrixf& rhs) + { + _mat[0][0] += rhs._mat[0][0]; + _mat[0][1] += rhs._mat[0][1]; + _mat[0][2] += rhs._mat[0][2]; + _mat[0][3] += rhs._mat[0][3]; + _mat[1][0] += rhs._mat[1][0]; + _mat[1][1] += rhs._mat[1][1]; + _mat[1][2] += rhs._mat[1][2]; + _mat[1][3] += rhs._mat[1][3]; + _mat[2][0] += rhs._mat[2][0]; + _mat[2][1] += rhs._mat[2][1]; + _mat[2][2] += rhs._mat[2][2]; + _mat[2][3] += rhs._mat[2][3]; + _mat[3][0] += rhs._mat[3][0]; + _mat[3][1] += rhs._mat[3][1]; + _mat[3][2] += rhs._mat[3][2]; + _mat[3][3] += rhs._mat[3][3]; + return *this; + } + protected: value_type _mat[4][4]; diff --git a/src/osgPlugins/dae/CMakeLists.txt b/src/osgPlugins/dae/CMakeLists.txt index 4e7b5e4ac..8ecd7af56 100644 --- a/src/osgPlugins/dae/CMakeLists.txt +++ b/src/osgPlugins/dae/CMakeLists.txt @@ -2,10 +2,13 @@ INCLUDE_DIRECTORIES( ${COLLADA_INCLUDE_DIR} ${COLLADA_INCLUDE_DIR}/1.4) SET(TARGET_SRC daeReader.cpp + daeRAnimations.cpp daeRGeometry.cpp daeRMaterials.cpp daeRSceneObjects.cpp + daeRSkinning.cpp daeRTransforms.cpp + daeWAnimations.cpp daeWGeometry.cpp daeWMaterials.cpp daeWriter.cpp @@ -66,7 +69,7 @@ ELSE() ENDIF() -SET(TARGET_ADDED_LIBRARIES osgSim ) +SET(TARGET_ADDED_LIBRARIES osgSim osgAnimation) #### end var setup ### SETUP_PLUGIN(dae dae) diff --git a/src/osgPlugins/dae/ReaderWriterDAE.cpp b/src/osgPlugins/dae/ReaderWriterDAE.cpp index 31405c41d..03da70091 100644 --- a/src/osgPlugins/dae/ReaderWriterDAE.cpp +++ b/src/osgPlugins/dae/ReaderWriterDAE.cpp @@ -57,7 +57,7 @@ ReaderWriterDAE::readNode(const std::string& fname, pDAE = new DAE; } - osgdae::daeReader daeReader(pDAE, options && options->getOptionString().find("StrictTransparency") != std::string::npos ) ; + osgDAE::daeReader daeReader(pDAE, options && options->getOptionString().find("StrictTransparency") != std::string::npos ) ; // Convert file name to URI std::string fileURI = ConvertFilePathToColladaCompatibleURI(fileName); @@ -75,11 +75,11 @@ ReaderWriterDAE::readNode(const std::string& fname, *(std::string*)options->getPluginData("DAE-DocumentURI") = fileURI; // Return some additional information about the document if (options->getPluginData("DAE-AssetUnitName")) - *(std::string*)options->getPluginData("DAE-AssetUnitName") = daeReader.m_AssetUnitName; + *(std::string*)options->getPluginData("DAE-AssetUnitName") = daeReader.getAssetUnitName(); if (options->getPluginData("DAE-AssetUnitMeter")) - *(float*)options->getPluginData("DAE-AssetUnitMeter") = daeReader.m_AssetUnitMeter; + *(float*)options->getPluginData("DAE-AssetUnitMeter") = daeReader.getAssetUnitMeter(); if (options->getPluginData("DAE-AssetUp_axis")) - *(domUpAxisType*)options->getPluginData("DAE-AssetUp_axis") = daeReader.m_AssetUp_axis; + *(domUpAxisType*)options->getPluginData("DAE-AssetUp_axis") = daeReader.getAssetUpAxis(); } if (bOwnDAE) @@ -141,14 +141,14 @@ ReaderWriterDAE::writeNode( const osg::Node& node, osg::NodeVisitor::TraversalMode traversalMode = writeExtras ? osg::NodeVisitor::TRAVERSE_ALL_CHILDREN : osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN; - osgdae::daeWriter daeWriter(pDAE, fileURI, usePolygon, GoogleMode, traversalMode, writeExtras); + osgDAE::daeWriter daeWriter(pDAE, fileURI, usePolygon, GoogleMode, traversalMode, writeExtras); daeWriter.setRootNode( node ); const_cast(&node)->accept( daeWriter ); osgDB::ReaderWriter::WriteResult retVal( WriteResult::ERROR_IN_WRITING_FILE ); if ( daeWriter.isSuccess() ) { - if ( daeWriter.writeFile() ) + if (pDAE->write(fileURI)) retVal = WriteResult::FILE_SAVED; } diff --git a/src/osgPlugins/dae/ReaderWriterDAE.h b/src/osgPlugins/dae/ReaderWriterDAE.h index bf9884b7a..ce1a10251 100644 --- a/src/osgPlugins/dae/ReaderWriterDAE.h +++ b/src/osgPlugins/dae/ReaderWriterDAE.h @@ -8,14 +8,15 @@ // OSG reader/writer plugin for the COLLADA 1.4.x ".dae" format. // See http://collada.org/ and http://khronos.org/collada/ -#define EXTENSION_NAME "dae" - class ReaderWriterDAE : public osgDB::ReaderWriter { public: ReaderWriterDAE() { - supportsExtension(EXTENSION_NAME,"COLLADA 1.4.x DAE format"); + // Collada document + supportsExtension("dae","COLLADA 1.4.x DAE format"); + // Collada zip archive (contains one or more dae files and a manifest.xml) + supportsExtension("zae","COLLADA 1.4.x ZAE format"); } const char* className() const { return "COLLADA 1.4.x DAE reader/writer"; } diff --git a/src/osgPlugins/dae/daeRAnimations.cpp b/src/osgPlugins/dae/daeRAnimations.cpp new file mode 100644 index 000000000..968815209 --- /dev/null +++ b/src/osgPlugins/dae/daeRAnimations.cpp @@ -0,0 +1,891 @@ +#include "daeReader.h" +#include "domSourceReader.h" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace osgDAE; + + +// Mapping Collada animations to Osg animations +// domAnimation -> osgAnimation::Animation +// domSampler -> osgAnimation::Channel +// domSource -> osgAnimation::Channel.Sampler +// domChannel -> osgAnimation::Channel.TargetName +osgAnimation::BasicAnimationManager* daeReader::processAnimationLibraries(domCOLLADA* document) +{ + domLibrary_animation_clips_Array domAnimationClipsLibraries = document->getLibrary_animation_clips_array(); + + domLibrary_animations_Array domAnimationsLibraries = document->getLibrary_animations_array(); + osgAnimation::BasicAnimationManager* pOsgAnimationManager = NULL; + + // Only create an animationmanager if we have animation clip libraries or animation libraries + if ((domAnimationClipsLibraries.getCount() > 0) || (domAnimationsLibraries.getCount() > 0)) + { + pOsgAnimationManager = new osgAnimation::BasicAnimationManager(); + + // Process all animation clip libraries + for (size_t i=0; i < domAnimationClipsLibraries.getCount(); i++) + { + domAnimation_clip_Array domAnimationClips = domAnimationClipsLibraries[i]->getAnimation_clip_array(); + // Process all animation clips in this library + for (size_t j=0; j < domAnimationClips.getCount(); j++) + { + processAnimationClip(pOsgAnimationManager, domAnimationClips[j]); + } + } + + // If there are no clips then all animations are part of the same clip + if (domAnimationClipsLibraries.getCount() == 0 && domAnimationsLibraries.getCount()) + { + osgAnimation::Animation* pOsgAnimation = new osgAnimation::Animation; + pOsgAnimation->setName("Default"); + pOsgAnimationManager->registerAnimation(pOsgAnimation); + + // Process all animation libraries + for (size_t i=0; i < domAnimationsLibraries.getCount(); i++) + { + domAnimation_Array domAnimations = domAnimationsLibraries[i]->getAnimation_array(); + // Process all animations in this library + for (size_t j=0; j < domAnimations.getCount(); j++) + { + processAnimation(domAnimations[j], pOsgAnimation); + } + } + } + } + return pOsgAnimationManager; +} + +// +// 0..1 +// 1..* +// 0..* +void daeReader::processAnimationClip(osgAnimation::BasicAnimationManager* pOsgAnimationManager, domAnimation_clip* pDomAnimationClip) +{ + // an groups animations + osgAnimation::Animation* pOsgAnimation = new osgAnimation::Animation; + std::string name = pDomAnimationClip->getId() ? pDomAnimationClip->getId() : "AnimationClip"; + pOsgAnimation->setName(name); + + // We register the animation inside the scheduler + pOsgAnimationManager->registerAnimation(pOsgAnimation); + + double start = pDomAnimationClip->getStart(); + double end = pDomAnimationClip->getEnd(); + + pOsgAnimation->setStartTime(start); + double duration = end - start; + if (duration > 0) + { + pOsgAnimation->setDuration(duration); + } + + // 1..* + domInstanceWithExtra_Array domInstanceArray = pDomAnimationClip->getInstance_animation_array(); + for (size_t i=0; i < domInstanceArray.getCount(); i++) + { + domAnimation *pDomAnimation = daeSafeCast(getElementFromURI(domInstanceArray[i]->getUrl())); + if (pDomAnimation) + { + // getorcreateanimation + processAnimation(pDomAnimation, pOsgAnimation); + } + else + { + osg::notify( osg::WARN ) << "Failed to locate animation " << domInstanceArray[i]->getUrl().getURI() << std::endl; + } + } +} + +struct KeyFrameComparator +{ + bool operator () (const osgAnimation::Keyframe& a, const osgAnimation::Keyframe& b) const + { + return a.getTime() < b.getTime(); + } + bool operator () (const osgAnimation::Keyframe& a, float t) const + { + return a.getTime() < t; + } + bool operator () (float t, const osgAnimation::Keyframe& b) const + { + return t < b.getTime(); + } +}; + +template +void deCasteljau(osgAnimation::TemplateCubicBezier& l, osgAnimation::TemplateCubicBezier& n, osgAnimation::TemplateCubicBezier& r, float t) +{ + T q1 = l.getPosition() + t * (l.getControlPointOut() - l.getPosition()); + T q2 = l.getControlPointOut() + t * (r.getControlPointIn() - l.getControlPointOut()); + T q3 = r.getControlPointIn() + t * (r.getPosition() - r.getControlPointIn()); + + T r1 = q1 + t * (q2 - q1); + T r2 = q2 + t * (q3 - q2); + + T s = r1 + t * (r2 - r1); + + n.setControlPointIn(r1); + + n.setPosition(s); + + n.setControlPointOut(r2); + + l.setControlPointOut(q1); + + r.setControlPointIn(q3); +} + +void mergeKeyframeContainers(osgAnimation::Vec3CubicBezierKeyframeContainer* to, + osgAnimation::FloatCubicBezierKeyframeContainer** from, + daeReader::InterpolationType interpolationType) +{ + assert(to->empty()); + + typedef std::set TimeSet; + TimeSet times; + + for (int i = 0; i < 3; ++i) + { + if (!from[i] || from[i]->empty()) + { + continue; + } + + for (osgAnimation::FloatCubicBezierKeyframeContainer::const_iterator + it = from[i]->begin(), end = from[i]->end(); it != end; ++it) + { + times.insert(it->getTime()); + } + } + + for (TimeSet::const_iterator it = times.begin(), end = times.end(); it != end; ++it) + { + const float time = *it; + + osgAnimation::Vec3CubicBezier value; + + for (int i = 0; i < 3; ++i) + { + if (!from[i] || from[i]->empty()) + { + continue; + } + + osgAnimation::FloatCubicBezierKeyframeContainer::iterator next = + std::lower_bound(from[i]->begin(), from[i]->end(), time, KeyFrameComparator()); + if (next == from[i]->end()) + { + --next; + value.getPosition().ptr()[i] = next->getValue().getPosition(); + value.getControlPointIn().ptr()[i] = next->getValue().getControlPointIn(); + value.getControlPointOut().ptr()[i] = next->getValue().getControlPointOut(); + } + else if (next == from[i]->begin() || next->getTime() == time) + { + value.getPosition().ptr()[i] = next->getValue().getPosition(); + value.getControlPointIn().ptr()[i] = next->getValue().getControlPointIn(); + value.getControlPointOut().ptr()[i] = next->getValue().getControlPointOut(); + } + else + { + osgAnimation::FloatCubicBezierKeyframeContainer::iterator prev = next; + --prev; + + switch (interpolationType) + { + case daeReader::INTERPOLATION_STEP: + value.getPosition().ptr()[i] = prev->getValue().getPosition(); + break; + case daeReader::INTERPOLATION_LINEAR: + { + float xp = prev->getTime(), xn = next->getTime(); + float yp = prev->getValue().getPosition(), yn = next->getValue().getPosition(); + value.getPosition().ptr()[i] = yp + (yn - yp) * (time - xp) / (xn - xp); + } + break; + case daeReader::INTERPOLATION_BEZIER: + { + float xp = prev->getTime(), xn = next->getTime(); + + osgAnimation::FloatCubicBezier l(prev->getValue()), n, r(next->getValue()); + deCasteljau(l, n, r, (time - xp) / (xn - xp)); + + value.getPosition().ptr()[i] = n.getPosition(); + value.getControlPointIn().ptr()[i] = n.getControlPointIn(); + value.getControlPointOut().ptr()[i] = n.getControlPointOut(); + + osgAnimation::Vec3CubicBezier prevValue = to->back().getValue(); + prevValue.getControlPointOut().ptr()[i] = l.getControlPointOut(); + to->back().setValue(prevValue); + + prev->setValue(l); + next->setValue(r); + from[i]->insert(next, osgAnimation::FloatCubicBezierKeyframe(time, n)); + } + break; + default: + osg::notify(osg::WARN) << "Unsupported interpolation type." << std::endl; + break; + } + + //todo - different types of interpolation + } + } + + to->push_back(osgAnimation::Vec3CubicBezierKeyframe(time, value)); + } +} + +void daeReader::processAnimationChannels( + domAnimation* pDomAnimation, TargetChannelPartMap& tcm) +{ + // 1..* + SourceMap sources; + domSource_Array domSources = pDomAnimation->getSource_array(); + for (size_t i=0; i < domSources.getCount(); i++) + { + sources.insert(std::make_pair((daeElement*)domSources[i], domSourceReader(domSources[i]))); + } + + domChannel_Array domChannels = pDomAnimation->getChannel_array(); + for (size_t i=0; i < domChannels.getCount(); i++) + { + processChannel(domChannels[i], sources, tcm); + } + + domAnimation_Array domAnimations = pDomAnimation->getAnimation_array(); + for (size_t i=0; i < domAnimations.getCount(); i++) + { + // recursively call + processAnimationChannels(domAnimations[i], tcm); + } +} + +osgAnimation::Vec3KeyframeContainer* convertKeyframeContainerToLinear(osgAnimation::Vec3CubicBezierKeyframeContainer& from) +{ + osgAnimation::Vec3KeyframeContainer* linearKeyframes = new osgAnimation::Vec3KeyframeContainer; + for (size_t i = 0; i < from.size(); ++i) + { + linearKeyframes->push_back(osgAnimation::Vec3Keyframe( + from[i].getTime(), from[i].getValue().getPosition())); + } + return linearKeyframes; +} + +template +void convertHermiteToBezier(osgAnimation::TemplateKeyframeContainer& keyframes) +{ + for (size_t i = 0; i < keyframes.size(); ++i) + { + T val = keyframes[i].getValue(); + val.setControlPointIn(val.getControlPointIn() / 3.0f + val.getPosition()); + val.setControlPointOut(val.getControlPointOut() / -3.0f + val.getPosition()); + keyframes[i].setValue(val); + } +} + +// osgAnimation requires control points to be in a weird order. This function +// reorders them from the conventional order to osgAnimation order. +template +void reorderControlPoints(osgAnimation::TemplateKeyframeContainer >& vkfCont) +{ + if (vkfCont.size() <= 1) + { + if (vkfCont.size() == 1) + { + osgAnimation::TemplateCubicBezier tcb = vkfCont.front().getValue(); + T inCP = tcb.getControlPointIn(); + tcb.setControlPointIn(tcb.getControlPointOut()); + tcb.setControlPointOut(inCP); + vkfCont.front().setValue(tcb); + } + return; + } + + osgAnimation::TemplateCubicBezier first = vkfCont.front().getValue(); + + for (unsigned i = 0; i < vkfCont.size() - 1; ++i) + { + osgAnimation::TemplateCubicBezier tcb = vkfCont[i].getValue(); + tcb.setControlPointIn(tcb.getControlPointOut()); + tcb.setControlPointOut(vkfCont[i + 1].getValue().getControlPointIn()); + vkfCont[i].setValue(tcb); + } + + osgAnimation::TemplateCubicBezier last = vkfCont.back().getValue(); + last.setControlPointIn(last.getControlPointOut()); + last.setControlPointOut(first.getControlPointIn()); + vkfCont.back().setValue(last); +} + +// +// 0..1 +// option 1 +// 1..* +// one of (, , ) or (see below) +// option 2 +// 1..* +// 1..* +// 0..* +// option 3 +// 1..* +// 0..* +void daeReader::processAnimation(domAnimation* pDomAnimation, osgAnimation::Animation* pOsgAnimation) +{ + // in an , you can have either a child and/or a and + domSampler_Array domSamplers = pDomAnimation->getSampler_array(); + domAnimation_Array domAnimations = pDomAnimation->getAnimation_array(); + + TargetChannelPartMap tcm; + processAnimationChannels(pDomAnimation, tcm); + + for (TargetChannelPartMap::iterator lb = tcm.begin(), end = tcm.end(); lb != end;) + { + TargetChannelPartMap::iterator ub = tcm.upper_bound(lb->first); + + osgAnimation::Channel* pOsgAnimationChannel = NULL; + std::string channelName, targetName, componentName; + + if (dynamic_cast(lb->first)) + { + osgAnimation::FloatCubicBezierKeyframeContainer* fkfConts[3] = {NULL, NULL, NULL}; + osgAnimation::Vec3CubicBezierKeyframeContainer* vkfCont = NULL; + InterpolationType interpolationType = INTERPOLATION_DEFAULT; + + for (TargetChannelPartMap::iterator it = lb; it != ub; ++it) + { + ChannelPart* channelPart = it->second.get(); + extractTargetName(channelPart->name, channelName, targetName, componentName); + interpolationType = channelPart->interpolation; + + if (osgAnimation::Vec3CubicBezierKeyframeContainer* v3cnt = dynamic_cast(channelPart->keyframes.get())) + { + vkfCont = v3cnt; + break; + } + else if (osgAnimation::FloatCubicBezierKeyframeContainer* fcnt = dynamic_cast(channelPart->keyframes.get())) + { + if (strchr("xusr0", tolower(componentName[0]))) + { + fkfConts[0] = fcnt; + } + else if (strchr("yvtg1", tolower(componentName[0]))) + { + fkfConts[1] = fcnt; + } + else if (strchr("zpb2", tolower(componentName[0]))) + { + fkfConts[2] = fcnt; + } + else + { + osg::notify(osg::WARN) << "Unrecognised vector component \"" << componentName << "\"" << std::endl; + } + } + else + { + osg::notify(osg::WARN) << "Unrecognised keyframe container \"" << channelPart->name << "\"" << std::endl; + } + } + + if (!pOsgAnimationChannel && (fkfConts[0] || fkfConts[1] || fkfConts[2])) + { + vkfCont = new osgAnimation::Vec3CubicBezierKeyframeContainer; + mergeKeyframeContainers(vkfCont, fkfConts, interpolationType); + } + + if (vkfCont) + { + if (interpolationType == INTERPOLATION_STEP) + { + osgAnimation::Vec3StepChannel* channel = new osgAnimation::Vec3StepChannel; + pOsgAnimationChannel = channel; + channel->getOrCreateSampler()->setKeyframeContainer(convertKeyframeContainerToLinear(*vkfCont)); + } + else if (interpolationType == INTERPOLATION_LINEAR) + { + osgAnimation::Vec3LinearChannel* channel = new osgAnimation::Vec3LinearChannel; + pOsgAnimationChannel = channel; + channel->getOrCreateSampler()->setKeyframeContainer(convertKeyframeContainerToLinear(*vkfCont)); + } + else if (interpolationType == INTERPOLATION_BEZIER) + { + osgAnimation::Vec3CubicBezierChannel* channel = new osgAnimation::Vec3CubicBezierChannel; + pOsgAnimationChannel = channel; + reorderControlPoints(*vkfCont); + channel->getOrCreateSampler()->setKeyframeContainer(vkfCont); + } + else if (interpolationType == INTERPOLATION_HERMITE) + { + osgAnimation::Vec3CubicBezierChannel* channel = new osgAnimation::Vec3CubicBezierChannel; + pOsgAnimationChannel = channel; + convertHermiteToBezier(*vkfCont); + reorderControlPoints(*vkfCont); + channel->getOrCreateSampler()->setKeyframeContainer(vkfCont); + } + else + { + osg::notify(osg::WARN) << "Unsupported interpolation type" << std::endl; + } + } + } + else + { + ChannelPart* channelPart = lb->second.get(); + extractTargetName(channelPart->name, channelName, targetName, componentName); + + typedef osgAnimation::TemplateKeyframe > MatrixCubicBezierKeyframe; + typedef osgAnimation::TemplateKeyframeContainer > MatrixCubicBezierKeyframeContainer; + + if (osgAnimation::FloatCubicBezierKeyframeContainer* kfCntr = + dynamic_cast(channelPart->keyframes.get())) + { + if (dynamic_cast(lb->first)) + { + osg::notify(osg::WARN) << "Animating elements of matrices not supported." << std::endl; + } + else + { + osgAnimation::FloatCubicBezierChannel* channel = new osgAnimation::FloatCubicBezierChannel; + reorderControlPoints(*kfCntr); + channel->getOrCreateSampler()->setKeyframeContainer(kfCntr); + pOsgAnimationChannel = channel; + } + } + else if (MatrixCubicBezierKeyframeContainer* cbkfCntr = + dynamic_cast(channelPart->keyframes.get())) + { + osgAnimation::MatrixKeyframeContainer* kfCntr = new osgAnimation::MatrixKeyframeContainer; + for (size_t i = 0; i < cbkfCntr->size(); ++i) + { + const MatrixCubicBezierKeyframe& cbkf = cbkfCntr->at(i); + kfCntr->push_back(osgAnimation::MatrixKeyframe(cbkf.getTime(), cbkf.getValue().getPosition())); + } + osgAnimation::MatrixLinearChannel* channel = new osgAnimation::MatrixLinearChannel; + channel->getOrCreateSampler()->setKeyframeContainer(kfCntr); + pOsgAnimationChannel = channel; + } + } + + if (pOsgAnimationChannel) + { + pOsgAnimationChannel->setTargetName(targetName); + pOsgAnimationChannel->setName(channelName); + pOsgAnimation->addChannel(pOsgAnimationChannel); + } + lb = ub; + } + + pOsgAnimation->computeDuration(); +} + +template +osgAnimation::KeyframeContainer* makeKeyframes( + const osg::FloatArray* pOsgTimesArray, + TArray* pOsgPointArray, + TArray* pOsgInTanArray, + TArray* pOsgOutTanArray, + daeReader::InterpolationType& interpolationType) +{ + osgAnimation::TemplateKeyframeContainer >* keyframes = + new osgAnimation::TemplateKeyframeContainer >; + + for (size_t i = 0; i < pOsgTimesArray->size(); i++) + { + T pt = (*pOsgPointArray)[i]; + T cpIn = pt, cpOut = pt; + if (pOsgInTanArray) + { + if (interpolationType == daeReader::INTERPOLATION_HERMITE) + //convert from hermite to bezier + cpIn += (*pOsgInTanArray)[i] / 3; + else if (interpolationType == daeReader::INTERPOLATION_BEZIER) + cpIn = (*pOsgInTanArray)[i]; + } + if (pOsgOutTanArray) + { + if (interpolationType == daeReader::INTERPOLATION_HERMITE) + //convert from hermite to bezier + cpOut += (*pOsgOutTanArray)[i] / 3; + else if (interpolationType == daeReader::INTERPOLATION_BEZIER) + cpOut = (*pOsgOutTanArray)[i]; + } + + keyframes->push_back( + osgAnimation::TemplateKeyframe >( + (*pOsgTimesArray)[i], + osgAnimation::TemplateCubicBezier(pt, cpIn, cpOut))); + } + + if (interpolationType == daeReader::INTERPOLATION_HERMITE) + { + interpolationType = daeReader::INTERPOLATION_BEZIER; + } + + return keyframes; +} + +// Sampler tells how to use the sources to generate an animated value. +// +// 1..* +// 1 semantic +// 1 source +daeReader::ChannelPart* daeReader::processSampler(domChannel* pDomChannel, SourceMap &sources) +{ + // And we finally define our channel + // Note osg can only create time based animations + + //from the channel you know the target, from the target you know the type + domSampler *pDomSampler = daeSafeCast(getElementFromURI(pDomChannel->getSource())); + if (!pDomSampler) + { + return NULL; + } + + domInputLocal_Array domInputArray = pDomSampler->getInput_array(); + + daeElement* input_source = NULL; + daeElement* output_source = NULL; + daeElement* output_intangent_source = NULL; + daeElement* output_outtangent_source = NULL; + domInputLocal *tmp; + + osg::FloatArray* pOsgTimesArray = NULL; + if (findInputSourceBySemantic(domInputArray, COMMON_PROFILE_INPUT_INPUT, input_source, &tmp)) + { + domSource* pDomSource = daeSafeCast(input_source); + if (pDomSource) + { + domSource::domTechnique_common* pDomTechnique = pDomSource->getTechnique_common(); + if (pDomTechnique) + { + domAccessor* pDomAccessor = pDomTechnique->getAccessor(); + domParam_Array domParams = pDomAccessor->getParam_array(); + if (domParams.getCount() > 0) + { + if (!strcmp("TIME", domParams[0]->getName())) + { + pOsgTimesArray = sources[input_source].getFloatArray(); + } + else + { + osg::notify(osg::WARN) << "Only TIME based animations are supported" < in " << pDomSource->getName() <(input_source); + if (pDomSource) + { + domName_array* pDomNames = pDomSource->getName_array(); + if (pDomNames) + { + daeStringArray* stringArray = &(pDomNames->getValue()); + + // Take a look at the first element in the array to see what kind of interpolation is needed + // multiple interpolation types inside an animation is not supported + if (stringArray->getCount() > 0) + { + // Collada interpolation types + for (int i = 0; i < interpTypeCount; ++i) + { + if (!strcmp(interpTypeNames[i].str, (*stringArray)[0])) + { + interpolationType = interpTypeNames[i].interp; + break; + } + } + } + else + { + osg::notify(osg::WARN) << "No names in " < in " << pDomSource->getName() <(pOsgTimesArray, + sources[output_source].getFloatArray(), + sources[output_intangent_source].getFloatArray(), + sources[output_outtangent_source].getFloatArray(), + interpolationType); + break; + case domSourceReader::Vec2: + keyframes = makeKeyframes(pOsgTimesArray, + sources[output_source].getVec2Array(), + sources[output_intangent_source].getVec2Array(), + sources[output_outtangent_source].getVec2Array(), + interpolationType); + break; + case domSourceReader::Vec3: + keyframes = makeKeyframes(pOsgTimesArray, + sources[output_source].getVec3Array(), + sources[output_intangent_source].getVec3Array(), + sources[output_outtangent_source].getVec3Array(), + interpolationType); + break; + case domSourceReader::Vec4: + keyframes = makeKeyframes(pOsgTimesArray, + sources[output_source].getVec4Array(), + sources[output_intangent_source].getVec4Array(), + sources[output_outtangent_source].getVec4Array(), + interpolationType); + break; + case domSourceReader::Matrix: + keyframes = makeKeyframes(pOsgTimesArray, + sources[output_source].getMatrixArray(), + sources[output_intangent_source].getMatrixArray(), + sources[output_outtangent_source].getMatrixArray(), + interpolationType); + break; + } + + if (keyframes) + { + ChannelPart* chanPart = new ChannelPart; + chanPart->keyframes = keyframes; + chanPart->interpolation = interpolationType; + chanPart->name = pDomChannel->getTarget(); + return chanPart; + } + + return NULL; +} + +osgAnimation::Target* findChannelTarget(osg::NodeCallback* nc, const std::string& targetName, bool& rotation) +{ + if (osgAnimation::UpdateMatrixTransform* umt = dynamic_cast(nc)) + { + for (osgAnimation::StackedTransform::const_iterator + it = umt->getStackedTransforms().begin(), end = umt->getStackedTransforms().end(); it != end; ++it) + { + osgAnimation::StackedTransformElement* te = it->get(); + if (te->getName() == targetName) + { + rotation = dynamic_cast(te) != NULL; + return te->getOrCreateTarget(); + } + } + } + else if (!dynamic_cast(nc)) + { + osg::notify(osg::WARN) << "Unrecognised AnimationUpdateCallback" << std::endl; + } + + return NULL; +} + +void convertDegreesToRadians(osgAnimation::KeyframeContainer* pKeyframeContainer) +{ + if (osgAnimation::FloatKeyframeContainer* fkc = + dynamic_cast(pKeyframeContainer)) + { + for (size_t i = 0; i < fkc->size(); ++i) + { + osgAnimation::FloatKeyframe& fk = (*fkc)[i]; + fk.setValue(osg::DegreesToRadians(fk.getValue())); + } + } + else if (osgAnimation::FloatCubicBezierKeyframeContainer* fcbkc = + dynamic_cast(pKeyframeContainer)) + { + for (size_t i = 0; i < fcbkc->size(); ++i) + { + osgAnimation::FloatCubicBezierKeyframe& fcbk = (*fcbkc)[i]; + osgAnimation::FloatCubicBezier fcb = fcbk.getValue(); + fcb.setPosition(osg::DegreesToRadians(fcb.getPosition())); + fcb.setControlPointIn(osg::DegreesToRadians(fcb.getControlPointIn())); + fcb.setControlPointOut(osg::DegreesToRadians(fcb.getControlPointOut())); + fcbk.setValue(fcb); + } + } + else + { + osg::notify(osg::WARN) << "Warning: rotation keyframes not converted to radians." << std::endl; + } +} + +// Channel connects animation output to parameter to animate +// +// 1 source +// 1 target +void daeReader::processChannel(domChannel* pDomChannel, SourceMap& sources, TargetChannelPartMap& tcm) +{ + domSampler *pDomSampler = daeSafeCast(getElementFromURI(pDomChannel->getSource())); + if (pDomSampler) + { + ChannelPart* pChannelPart = processSampler(pDomChannel, sources); + + if (pChannelPart) + { + domChannelOsgAnimationUpdateCallbackMap::iterator iter = _domChannelOsgAnimationUpdateCallbackMap.find(pDomChannel); + if (iter != _domChannelOsgAnimationUpdateCallbackMap.end()) + { + osg::NodeCallback* nc = iter->second.get(); + + std::string channelName, targetName, componentName; + extractTargetName(pDomChannel->getTarget(), channelName, targetName, componentName); + //assert(targetName == nc->getName()); + bool bRotationChannel = false; + if (osgAnimation::Target* pTarget = findChannelTarget(nc, channelName, bRotationChannel)) + { + if (bRotationChannel) + { + convertDegreesToRadians(pChannelPart->keyframes.get()); + } + tcm.insert(TargetChannelPartMap::value_type(pTarget, pChannelPart)); + } + else + { + osg::notify(osg::WARN) << "Target \"" << channelName << "\" not found." << std::endl; + } + } + else + { + osg::notify(osg::WARN) << "Could not locate UpdateCallback for target " << pDomChannel->getTarget()<< std::endl; + } + } + else + { + osg::notify( osg::WARN ) << " source " << pDomChannel->getSource().getURI() << " has no corresponding osgAnimation::Channel" << std::endl; + } + } + else + { + osg::notify( osg::WARN ) << "Could not locate source " << pDomChannel->getSource().getURI() << std::endl; + } +} + +void daeReader::extractTargetName(const std::string& daeTarget, std::string& channelName, std::string& targetName, std::string& component) +{ + size_t slash = daeTarget.find_last_of("/"); + if (slash != std::string::npos) + { + // Handle /translation + targetName = daeTarget.substr(0, slash); + channelName = daeTarget.substr(slash+1, std::string::npos); + } + else + { + size_t parenthesis = daeTarget.find_last_of("("); + size_t endpos = daeTarget.find_last_of(")"); + if (parenthesis != std::string::npos && endpos != std::string::npos) + { + // Handle (1) + targetName = daeTarget.substr(0, parenthesis); + channelName = daeTarget.substr(parenthesis+1, endpos - parenthesis - 1); + } + else + { + osg::notify(osg::WARN) << "Couldn't extract a proper name for target " << daeTarget << std::endl; + } + } + + size_t period = channelName.find_last_of("."); + if (period != std::string::npos) + { + component = channelName.substr(period+1, std::string::npos); + channelName = channelName.substr(0, period); + } + else + { + component.clear(); + + size_t first_parenthesis = channelName.find_first_of("("); + if (first_parenthesis != std::string::npos) + { + size_t open_parenthesis = first_parenthesis; + + do + { + if (open_parenthesis != first_parenthesis) component += ","; + + size_t close_parenthesis = channelName.find_first_of(")", open_parenthesis); + component += channelName.substr(open_parenthesis+1, close_parenthesis-open_parenthesis-1); + open_parenthesis = channelName.find_first_of("(", close_parenthesis); + } + while (open_parenthesis != std::string::npos); + + channelName = channelName.substr(0, first_parenthesis); + } + } +} diff --git a/src/osgPlugins/dae/daeRGeometry.cpp b/src/osgPlugins/dae/daeRGeometry.cpp index 67ef96da2..a5dd650ed 100644 --- a/src/osgPlugins/dae/daeRGeometry.cpp +++ b/src/osgPlugins/dae/daeRGeometry.cpp @@ -1,14 +1,14 @@ /* * Copyright 2006 Sony Computer Entertainment Inc. * - * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * http://research.scea.com/scea_shared_source_license.html * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - * implied. See the License for the specific language governing permissions and limitations under the - * License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. */ #include "daeReader.h" @@ -18,513 +18,857 @@ #include #include #include +#include #include - +#include #include -using namespace osgdae; +#include +#include +#include -osg::Geode* daeReader::processInstanceGeometry( domInstance_geometry *ig ) +using namespace osgDAE; + +osg::Geode* daeReader::getOrCreateGeometry(domGeometry *pDomGeometry, domBind_material* pDomBindMaterial, const osg::Geode** ppOriginalGeode) { - daeElement *el = getElementFromURI( ig->getUrl() ); - domGeometry *geom = daeSafeCast< domGeometry >( el ); - if ( geom == NULL ) - { - osg::notify( osg::WARN ) << "Failed to locate geometry " << ig->getUrl().getURI() << std::endl; - return NULL; - } - // Check cache if geometry already exists - osg::Geode* cachedGeode; + osg::Geode* pOsgGeode; - domGeometryGeodeMap::iterator iter = geometryMap.find( geom ); - if ( iter != geometryMap.end() ) + domGeometryGeodeMap::iterator iter = _geometryMap.find( pDomGeometry ); + if ( iter != _geometryMap.end() ) { - cachedGeode = iter->second.get(); + pOsgGeode = iter->second.get(); } else { - cachedGeode = processGeometry( geom ); - geometryMap.insert( std::make_pair( geom, cachedGeode ) ); + pOsgGeode = processGeometry( pDomGeometry ); + _geometryMap.insert( std::make_pair( pDomGeometry, pOsgGeode ) ); } + if (ppOriginalGeode) + { + *ppOriginalGeode = pOsgGeode; + } + + if (!pOsgGeode) + return NULL; + // Create a copy of the cached Geode with a copy of the drawables, // because we may be using a different material or texture unit bindings. - osg::Geode *geode = static_cast(cachedGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); - if ( geode == NULL ) + osg::Geode *pCopiedOsgGeode = static_cast(pOsgGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); + if ( pCopiedOsgGeode == NULL ) { - osg::notify( osg::WARN ) << "Failed to load geometry " << ig->getUrl().getURI() << std::endl; + osg::notify( osg::WARN ) << "Failed to load geometry " << pDomGeometry->getName() << std::endl; return NULL; } - // process material bindings - if ( ig->getBind_material() != NULL ) + // Compute optimized geometry by expanding all indexed arrays so we are no longer rendering with the slow path + for(unsigned int i=0;i < pCopiedOsgGeode->getNumDrawables();++i) { - processBindMaterial( ig->getBind_material(), geom, geode, cachedGeode ); + osg::Geometry* geom = pCopiedOsgGeode->getDrawable(i)->asGeometry(); + if (geom) + { + if (!geom->areFastPathsUsed() && !geom->getInternalOptimizedGeometry()) + { + //geom->computeInternalOptimizedGeometry(); + } + } } - return geode; + if (pDomBindMaterial) + { + processBindMaterial( pDomBindMaterial, pDomGeometry, pCopiedOsgGeode, pOsgGeode ); + } + + return pCopiedOsgGeode; } -// -// attributes: -// id, name -// elements: +osgAnimation::Bone* daeReader::getOrCreateBone(domNode *pDomNode) +{ + // Check cache if bone already exists + osgAnimation::Bone *pOsgBone = NULL; + + domNodeOsgBoneMap::iterator iterBone = _jointMap.find( pDomNode ); + if ( iterBone != _jointMap.end() ) + return iterBone->second.get(); + + std::string name; + if (pDomNode->getId()) + name = pDomNode->getId(); + if (name.empty() && pDomNode->getSid()) + name = pDomNode->getSid(); + if (name.empty() && pDomNode->getName()) + name = pDomNode->getName(); + pOsgBone = new osgAnimation::Bone(name); + pOsgBone->setDataVariance(osg::Object::DYNAMIC); + + pOsgBone->setUpdateCallback(new osgAnimation::UpdateBone(name)); + + _jointMap.insert( std::make_pair( pDomNode, pOsgBone ) ); + + return pOsgBone; +} + +osgAnimation::Skeleton* daeReader::getOrCreateSkeleton(domNode *pDomNode) +{ + // Check cache if skeleton already exists + osgAnimation::Skeleton *pOsgSkeleton = NULL; + + domNodeOsgSkeletonMap::iterator iter = _skeletonMap.find( pDomNode ); + if ( iter != _skeletonMap.end() ) + return iter->second.get(); + + pOsgSkeleton = new osgAnimation::Skeleton; + pOsgSkeleton->setDefaultUpdateCallback(); + pOsgSkeleton->setDataVariance(osg::Object::DYNAMIC); + + _skeletonMap.insert( std::make_pair( pDomNode, pOsgSkeleton ) ); + + return pOsgSkeleton; +} + + + +osg::Geode* daeReader::processInstanceGeometry( domInstance_geometry *pDomInstanceGeometry ) +{ + domGeometry *pDomGeometry = daeSafeCast< domGeometry >(getElementFromURI(pDomInstanceGeometry->getUrl())); + if (!pDomGeometry) + { + osg::notify( osg::WARN ) << "Failed to locate geometry " << pDomInstanceGeometry->getUrl().getURI() << std::endl; + return NULL; + } + + return getOrCreateGeometry(pDomGeometry, pDomInstanceGeometry->getBind_material()); +} + +// +// 2..* +// 1 +// 2..* +// 0..* +// 0..* +osg::Node* daeReader::processMorph(domMorph* pDomMorph, domBind_material* pDomBindMaterial) +{ + domGeometry* pDomGeometry = daeSafeCast< domGeometry >(getElementFromURI( pDomMorph->getSource())); + + if (!pDomGeometry) + { + osg::notify( osg::WARN ) << "Failed to locate geometry " << pDomMorph->getSource().getURI() << std::endl; + return NULL; + } + + // Base mesh + osg::Geode* pOsgGeode = getOrCreateGeometry(pDomGeometry, pDomBindMaterial); + if (!pOsgGeode) + return NULL; + + // Expects a single geometry inside the geode, should change this + osg::Geometry* pOsgGeometry = dynamic_cast(pOsgGeode->getDrawable(0)); + if (!pOsgGeometry) + return NULL; + + osgAnimation::MorphGeometry* pOsgMorphGeometry = new osgAnimation::MorphGeometry(*pOsgGeometry); + pOsgGeode->removeDrawables(0); + pOsgGeode->addDrawable(pOsgMorphGeometry); + + domMorphMethodType morphMethod = pDomMorph->getMethod(); + + //Files exported by the FBX converter always seem to say they're relative + //when in fact they should be normalized. + if (_authoringTool == FBX_CONVERTER) + { + morphMethod = MORPHMETHODTYPE_NORMALIZED; + } + + switch (morphMethod) + { + case MORPHMETHODTYPE_RELATIVE: + pOsgMorphGeometry->setMethod(osgAnimation::MorphGeometry::RELATIVE); + break; + case MORPHMETHODTYPE_NORMALIZED: + pOsgMorphGeometry->setMethod(osgAnimation::MorphGeometry::NORMALIZED); + break; + default: + osg::notify( osg::WARN ) << "Unknown morph method method type " << std::endl; + } + + // 1 + domMorph::domTargets* pDomMorhpTargets = pDomMorph->getTargets(); + domInputLocal_Array domInputs = pDomMorhpTargets->getInput_array(); + + // TODO how to handle multiple pairs of morph inputs? + if (domInputs.getCount() > 2) + { + osg::notify( osg::WARN ) << "Only a single pair of morph inputs is supported." << std::endl; + } + + for (size_t i=0; i < 2; i++) + { + if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_MORPH_TARGET)) + { + domSource* pDomSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (pDomSource) + { + if (const domName_array* pDomNames = pDomSource->getName_array()) + { + const domListOfNames& names = pDomNames->getValue(); + for (size_t j=0; j < names.getCount(); j++) + { + daeSIDResolver resolver(_visualScene, names.get(j)); + pDomGeometry = daeSafeCast< domGeometry >(resolver.getElement()); + + if (pDomGeometry) + { + osg::Geode* targetgeode = getOrCreateGeometry(pDomGeometry, NULL); + + // Expects a single geometry inside the geode, should change this + osg::Geometry* pOsgGeometry = dynamic_cast(targetgeode->getDrawable(0)); + if (pOsgGeometry) + { + pOsgMorphGeometry->addMorphTarget(pOsgGeometry); + } + } + else + { + osg::notify( osg::WARN ) << "Failed to locate morph geometry '" << names.get(j) << "'" << std::endl; + } + } + } + else if (domIDREF_array* pDomIDREFs = pDomSource->getIDREF_array()) + { + xsIDREFS* pIDREFS = &(pDomIDREFs->getValue()); + for (size_t j=0; j < pIDREFS->getCount(); j++) + { + pDomGeometry = daeSafeCast< domGeometry >(getElementFromIDRef(pIDREFS->get(j))); + + if (pDomGeometry) + { + osg::Geode* targetgeode = getOrCreateGeometry(pDomGeometry, NULL); + + // Expects a single geometry inside the geode, should change this + osg::Geometry* pOsgGeometry = dynamic_cast(targetgeode->getDrawable(0)); + if (pOsgGeometry) + { + pOsgMorphGeometry->addMorphTarget(pOsgGeometry); + } + } + else + { + osg::notify( osg::WARN ) << "Failed to locate morph geometry '" << pIDREFS->get(j).getID() << "'" << std::endl; + } + } + } + } + else + { + osg::notify( osg::WARN ) << "Could not find morph source '" << domInputs[i]->getSource().getURI() << "'" <getSemantic(), COMMON_PROFILE_INPUT_MORPH_WEIGHT)) + { + domSource* pDomSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (pDomSource) + { + domFloat_array* pDomFloatArray = pDomSource->getFloat_array(); + domListOfFloats weights = pDomFloatArray->getValue(); + for (size_t j=0; j < pDomFloatArray->getCount(); j++) + { + pOsgMorphGeometry->setWeight(j, weights.get(j)); + } + + // See if morph weights are targetted by animations + daeElementDomChannelMap::iterator iter = _daeElementDomChannelMap.find(pDomSource); + if (iter != _daeElementDomChannelMap.end()) + { + std::string name = pDomSource->getId() ? pDomSource->getId() : ""; + osgAnimation::UpdateMorph* pUpdateCallback = new osgAnimation::UpdateMorph(name); + pOsgGeode->setUpdateCallback(pUpdateCallback); + pOsgGeode->setDataVariance(osg::Object::DYNAMIC); + + // Associate all animation channels with this update callback + do + { + _domChannelOsgAnimationUpdateCallbackMap[iter->second] = pUpdateCallback; + ++iter; + } + while (iter != _daeElementDomChannelMap.upper_bound(pDomSource)); + } + } + else + { + osg::notify( osg::WARN ) << "Could not find morph source '" << domInputs[i]->getSource().getURI() << "'" < // 0..1 // 1 , // 0..* -osg::Geode* daeReader::processInstanceController( domInstance_controller *ictrl ) +osg::Node* daeReader::processInstanceController( domInstance_controller *pDomInstanceController ) { - //TODO: support skinning - daeElement *el = getElementFromURI( ictrl->getUrl()); - domController *ctrl = daeSafeCast< domController >( el ); - if ( ctrl == NULL ) + domController *pDomController = daeSafeCast< domController >(getElementFromURI(pDomInstanceController->getUrl())); + if (!pDomController) { - osg::notify( osg::WARN ) << "Failed to locate conroller " << ictrl->getUrl().getURI() << std::endl; + osg::notify( osg::WARN ) << "Failed to locate controller " << pDomInstanceController->getUrl().getURI() << std::endl; return NULL; } - osg::notify( osg::WARN ) << "Processing . There is not skinning support but will display the base mesh." << std::endl; - - el = NULL; - //## non init - daeURI *src=NULL; - if ( ctrl->getSkin() != NULL ) + if (pDomController->getSkin()) { - src = &ctrl->getSkin()->getSource(); - el = getElementFromURI( ctrl->getSkin()->getSource() ); - } - else if ( ctrl->getMorph() != NULL ) - { - src = &ctrl->getSkin()->getSource(); - el = getElementFromURI( ctrl->getMorph()->getSource() ); - } - - //non init case - if ( !src ) - { - osg::notify( osg::WARN ) << "Failed to locate geometry : URI is NULL" << std::endl; + _skinInstanceControllers.push_back(pDomInstanceController); return NULL; } - - domGeometry *geom = daeSafeCast< domGeometry >( el ); - if ( geom == NULL ) + else if (pDomController->getMorph()) { - osg::notify( osg::WARN ) << "Failed to locate geometry " << src->getURI() << std::endl; - return NULL; + return processMorph(pDomController->getMorph(), pDomInstanceController->getBind_material()); } - // Check cache if geometry already exists - osg::Geode* cachedGeode; - domGeometryGeodeMap::iterator iter = geometryMap.find( geom ); - if ( iter != geometryMap.end() ) - { - cachedGeode = iter->second.get(); - } - else - { - cachedGeode = processGeometry( geom ); - geometryMap.insert( std::make_pair( geom, cachedGeode ) ); - } + osg::notify( osg::WARN ) << "Expected skin or morph element in controller '" << pDomController->getName() << "'" << std::endl; - // Create a copy of the cached Geode with a copy of the drawables, - // because we may be using a different material or texture unit bindings. - osg::Geode *geode = static_cast(cachedGeode->clone(osg::CopyOp::DEEP_COPY_DRAWABLES)); - if ( geode == NULL ) - { - osg::notify( osg::WARN ) << "Failed to load geometry " << src->getURI() << std::endl; - return NULL; - } - //process material bindings - if ( ictrl->getBind_material() != NULL ) - { - processBindMaterial( ictrl->getBind_material(), geom, geode, cachedGeode ); - } - - return geode; + return NULL; } -// -// attributes: -// id, name -// elements: -// 0..1 -// 1 , , +// +// 1..* +// 1 +// 0..* , , , , , , // 0..* -osg::Geode *daeReader::processGeometry( domGeometry *geo ) +osg::Geode *daeReader::processMesh(domMesh* pDomMesh) { - domMesh *mesh = geo->getMesh(); - if ( mesh == NULL ) + osg::Geode* pOsgGeode = new osg::Geode; +// if (pDomMesh->getId() != NULL ) { - osg::notify( osg::WARN ) << "Unsupported Geometry type loading " << geo->getId() << std::endl; - return NULL; +// pOsgGeode->setName( pDomMesh->getId() ); } - osg::Geode* geode = new osg::Geode; - if (geo->getId() != NULL ) - { - geode->setName( geo->getId() ); - } - - // - // elements: - // 1..* - // 1 - // 0..* , , , , , , - // 0..* - // size_t count = mesh->getContents().getCount(); - + // 1..* SourceMap sources; - domSource_Array sourceArray = mesh->getSource_array(); + domSource_Array sourceArray = pDomMesh->getSource_array(); for ( size_t i = 0; i < sourceArray.getCount(); i++) { - sources.insert( std::make_pair((daeElement*)sourceArray[i], domSourceReader( sourceArray[i] ) ) ); + sources.insert(std::make_pair((daeElement*)sourceArray[i], domSourceReader(sourceArray[i]))); } // 0..* - domLines_Array linesArray = mesh->getLines_array(); + domLines_Array linesArray = pDomMesh->getLines_array(); for ( size_t i = 0; i < linesArray.getCount(); i++) { - processSinglePPrimitive(geode, linesArray[i], sources, GL_LINES ); + processSinglePPrimitive(pOsgGeode, pDomMesh, linesArray[i], sources, GL_LINES); } // 0..* - domLinestrips_Array linestripsArray = mesh->getLinestrips_array(); + domLinestrips_Array linestripsArray = pDomMesh->getLinestrips_array(); for ( size_t i = 0; i < linestripsArray.getCount(); i++) { - processMultiPPrimitive(geode, linestripsArray[i], sources, GL_LINE_STRIP ); + processMultiPPrimitive(pOsgGeode, pDomMesh, linestripsArray[i], sources, GL_LINE_STRIP); } // 0..* - domPolygons_Array polygonsArray = mesh->getPolygons_array(); + domPolygons_Array polygonsArray = pDomMesh->getPolygons_array(); for ( size_t i = 0; i < polygonsArray.getCount(); i++) { - processMultiPPrimitive(geode, polygonsArray[i], sources, GL_POLYGON ); + processPolygons(pOsgGeode, pDomMesh, polygonsArray[i], sources); } // 0..* - domPolylist_Array polylistArray = mesh->getPolylist_array(); + domPolylist_Array polylistArray = pDomMesh->getPolylist_array(); for ( size_t i = 0; i < polylistArray.getCount(); i++) { - processPolylist(geode, polylistArray[i], sources ); + processPolylist(pOsgGeode, pDomMesh, polylistArray[i], sources); } // 0..* - domTriangles_Array trianglesArray = mesh->getTriangles_array(); + domTriangles_Array trianglesArray = pDomMesh->getTriangles_array(); for ( size_t i = 0; i < trianglesArray.getCount(); i++) { - processSinglePPrimitive(geode, trianglesArray[i], sources, GL_TRIANGLES ); + processSinglePPrimitive(pOsgGeode, pDomMesh, trianglesArray[i], sources, GL_TRIANGLES); } // 0..* - domTrifans_Array trifansArray = mesh->getTrifans_array(); + domTrifans_Array trifansArray = pDomMesh->getTrifans_array(); for ( size_t i = 0; i < trifansArray.getCount(); i++) { - processMultiPPrimitive(geode, trifansArray[i], sources, GL_TRIANGLE_FAN ); + processPolygons(pOsgGeode, pDomMesh, trifansArray[i], sources); } // 0..* - domTristrips_Array tristripsArray = mesh->getTristrips_array(); + domTristrips_Array tristripsArray = pDomMesh->getTristrips_array(); for ( size_t i = 0; i < tristripsArray.getCount(); i++) { - processMultiPPrimitive(geode, tristripsArray[i], sources, GL_TRIANGLE_STRIP ); + processMultiPPrimitive(pOsgGeode, pDomMesh, tristripsArray[i], sources, GL_TRIANGLE_STRIP); } - return geode; + return pOsgGeode; +} + +// +osg::Geode *daeReader::processConvexMesh(domConvex_mesh* pDomConvexMesh) +{ +// osg::notify( osg::WARN ) << "Unsupported geometry convex mesh '" << pDomConvexMesh->getId() << "'" << std::endl; + return NULL; +} + +// +osg::Geode *daeReader::processSpline(domSpline* pDomSpline) +{ +// osg::notify( osg::WARN ) << "Unsupported geometry type spline '" << pDomSpline->getId() << "'" << std::endl; + return NULL; +} + +// +// 0..1 +// 1 , , +// 0..* +osg::Geode *daeReader::processGeometry(domGeometry *pDomGeometry) +{ + if (pDomGeometry->getMesh()) + { + return processMesh(pDomGeometry->getMesh()); + } + else if (pDomGeometry->getConvex_mesh()) + { + return processConvexMesh(pDomGeometry->getConvex_mesh()); + } + else if (pDomGeometry->getSpline()) + { + return processSpline(pDomGeometry->getSpline()); + } +#ifdef COLLADA15 + else if (pDomGeometry->getBRep()) + { + return processBRep(pDomGeometry->getBRep()); + } +#endif + + osg::notify( osg::WARN ) << "Unexpected geometry type in geometry '" << pDomGeometry->getId() << "'" << std::endl; + return NULL; } template< typename T > -void daeReader::processSinglePPrimitive(osg::Geode* geode, T *group, SourceMap &sources, GLenum mode ) +void daeReader::processSinglePPrimitive(osg::Geode* geode, + const domMesh* pDomMesh, const T* group, SourceMap& sources, GLenum mode) { - osg::Geometry *geometry = new ReaderGeometry(); - if (group->getMaterial() != NULL ) - { - geometry->setName(group->getMaterial()); - } - - IndexMap index_map; - resolveArrays( group->getInput_array(), geometry, sources, index_map ); - - osg::DrawArrayLengths* dal = new osg::DrawArrayLengths( mode ); - processP( group->getP(), geometry, index_map, dal/*mode*/ ); - geometry->addPrimitiveSet( dal ); - + osg::Geometry *geometry = new osg::Geometry(); + geometry->setName(group->getMaterial()); geode->addDrawable( geometry ); + + osg::DrawElementsUInt* pDrawElements = new osg::DrawElementsUInt(mode); + geometry->addPrimitiveSet(pDrawElements); + + domP_Array domPArray; + domPArray.append(group->getP()); + std::vector > indexLists; + resolveMeshArrays(domPArray, group->getInput_array(), pDomMesh, + geometry, sources, indexLists); + pDrawElements->asVector().swap(indexLists.front()); } template< typename T > -void daeReader::processMultiPPrimitive(osg::Geode* geode, T *group, SourceMap &sources, GLenum mode ) +void daeReader::processMultiPPrimitive(osg::Geode* geode, + const domMesh* pDomMesh, const T* group, SourceMap &sources, GLenum mode) { - osg::Geometry *geometry = new ReaderGeometry(); - if (group->getMaterial() != NULL ) - { - geometry->setName(group->getMaterial()); - } - - IndexMap index_map; - resolveArrays( group->getInput_array(), geometry, sources, index_map ); - - osg::DrawArrayLengths* dal = new osg::DrawArrayLengths( mode ); - - for ( size_t i = 0; i < group->getP_array().getCount(); i++ ) - { - processP( group->getP_array()[i], geometry, index_map, dal/*mode*/ ); - } - geometry->addPrimitiveSet( dal ); - + osg::Geometry *geometry = new osg::Geometry(); + geometry->setName(group->getMaterial()); geode->addDrawable( geometry ); -} -void daeReader::processPolylist(osg::Geode* geode, domPolylist *group, SourceMap &sources ) -{ - osg::Geometry *geometry = new ReaderGeometry(); - if (group->getMaterial() != NULL ) + std::vector > indexLists; + resolveMeshArrays(group->getP_array(), group->getInput_array(), pDomMesh, + geometry, sources, indexLists); + + for (size_t i = 0; i < indexLists.size(); ++i) { - geometry->setName(group->getMaterial()); - } - - IndexMap index_map; - resolveArrays( group->getInput_array(), geometry, sources, index_map ); - - osg::DrawArrayLengths* dal = new osg::DrawArrayLengths( GL_POLYGON ); - - //domPRef p = (domP*)(daeElement*)domP::_Meta->create(); //I don't condone creating elements like this but I don't care - domPRef p = (domP*)domP::registerElement(*dae)->create().cast(); - //if it created properly because I never want it as part of the document. Its just a temporary - //element to trick the importer into loading polylists easier. - unsigned int maxOffset = 0; - for ( unsigned int i = 0; i < group->getInput_array().getCount(); i++ ) - { - if ( group->getInput_array()[i]->getOffset() > maxOffset ) - { - maxOffset = group->getInput_array()[i]->getOffset(); - } - } - maxOffset++; - unsigned int pOffset = 0; - for ( unsigned int i = 0; i < group->getCount(); i++ ) - { - p->getValue().clear(); - for ( unsigned int x = 0; x < group->getVcount()->getValue()[i]; x++ ) - { - for ( unsigned int y = 0; y < maxOffset; y++ ) - { - p->getValue().append( group->getP()->getValue()[ pOffset + x*maxOffset + y ] ); - } - } - pOffset += group->getVcount()->getValue()[i] * maxOffset; - processP( p, geometry, index_map, dal/*mode*/ ); - } - - geometry->addPrimitiveSet( dal ); - - geode->addDrawable( geometry ); -} - -void daeReader::processP( domP *p, osg::Geometry *&/*geom*/, IndexMap &index_map, osg::DrawArrayLengths* dal /*GLenum mode*/ ) -{ - int idxcount = index_map.size(); - int count = p->getValue().getCount(); - count = (count/idxcount)*idxcount; - dal->push_back(count/idxcount); - - int j = 0; - while ( j < count ) { - for ( IndexMap::iterator k = index_map.begin(); k != index_map.end(); k++,j++ ) { - int tmp = p->getValue()[j]; - k->second->push_back(tmp); - } + osg::DrawElementsUInt* pDrawElements = new osg::DrawElementsUInt(mode); + geometry->addPrimitiveSet(pDrawElements); + pDrawElements->asVector().swap(indexLists[i]); } } -void daeReader::resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry *geom, - SourceMap &sources, IndexMap &index_map ) +void daeReader::processPolylist(osg::Geode* geode, const domMesh* pDomMesh, const domPolylist *group, SourceMap &sources) { - domVertices* vertices = NULL; - daeElement* position_source = NULL; - daeElement* color_source = NULL; - daeElement* normal_source = NULL; - daeElement* texcoord_source = NULL; - - daeElement *tmp_el; - domInputLocalOffset *tmp_input; - ReaderGeometry* GeometryWrapper = dynamic_cast(geom); - - int TexCoordSetsUsed = 0; - - if ( findInputSourceBySemantic( inputs, "VERTEX", tmp_el, &tmp_input ) ) + const domPolylist::domVcount* pDomVcount = group->getVcount(); + if (!pDomVcount) { - vertices = daeSafeCast< domVertices >( tmp_el ); - if ( vertices == NULL ) - { - osg::notify( osg::WARN )<<"Could not get vertices"<getOffset(); - if ( index_map[offset] == NULL ) - index_map[offset] = new osg::IntArray(); - geom->setVertexIndices( index_map[offset] ); - - // Process input elements within the vertices element. These are of the unshared type - // and therefore cannot have set and offset attributes - - // The vertices POSITION semantic input element is mandatory - domInputLocal *tmp; - findInputSourceBySemantic( vertices->getInput_array(), "POSITION", position_source, &tmp ); - if ( position_source != NULL ) - { - geom->setVertexArray( sources[position_source].getVec3Array() ); - } - else - { - osg::notify( osg::FATAL )<<"Mandatory POSITION semantic missing"<getInput_array(), "COLOR", color_source, &tmp ); - findInputSourceBySemantic( vertices->getInput_array(), "NORMAL", normal_source, &tmp ); - findInputSourceBySemantic( vertices->getInput_array(), "TEXCOORD", texcoord_source, &tmp ); - - - int VertexCount = sources[position_source].getCount(); - if ( color_source != NULL ) - { - // Check matching arrays - if ( sources[color_source].getCount() >= VertexCount ) - { - geom->setColorArray( sources[color_source].getVec4Array() ); - geom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); - geom->setColorIndices( index_map[offset] ); // Use the vertex indices - } - else - { - osg::notify( osg::WARN )<<"Not enough entries in color array"<= VertexCount ) - { - geom->setNormalArray( sources[normal_source].getVec3Array() ); - geom->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); - geom->setNormalIndices( index_map[offset] ); // Use the vertex indices - } - else - { - osg::notify( osg::WARN )<<"Not enough entries in normal array"<getCount() >= VertexCount ) - { - if (NULL != GeometryWrapper) - GeometryWrapper->_TexcoordSetMap[0] = TexCoordSetsUsed; - switch( sc->getArrayType() ) - { - case domSourceReader::Vec2: - geom->setTexCoordArray( TexCoordSetsUsed, sc->getVec2Array() ); - break; - case domSourceReader::Vec3: - geom->setTexCoordArray( TexCoordSetsUsed, sc->getVec3Array() ); - break; - default: - osg::notify( osg::WARN )<<"Unsupported array type: "<< sc->getArrayType() <setName(group->getMaterial()); + geode->addDrawable(geometry); + + std::vector > vertexLists; + domP_Array domPArray; + domPArray.append(group->getP()); + resolveMeshArrays(domPArray, group->getInput_array(), pDomMesh, geometry, sources, vertexLists); + + const std::vector& vertexList = vertexLists.front(); + + osg::DrawElementsUInt* pDrawTriangles = new osg::DrawElementsUInt(GL_TRIANGLES); + geometry->addPrimitiveSet(pDrawTriangles); + + const domListOfUInts& vCount = pDomVcount->getValue(); + for (size_t i = 0, j = 0; i < vCount.getCount(); ++i) { - if (color_source != NULL) - osg::notify( osg::WARN )<<"Overwriting vertices input(COLOR) with input from primitive"<setColorBinding( osg::Geometry::BIND_PER_VERTEX ); - - int offset = tmp_input->getOffset(); - if ( index_map[offset] == NULL ) - index_map[offset] = new osg::IntArray(); - geom->setColorIndices( index_map[offset] ); - geom->setColorArray( sources[tmp_el].getVec4Array() ); - } - - if ( findInputSourceBySemantic( inputs, "NORMAL", tmp_el, &tmp_input ) ) - { - if (normal_source != NULL) - osg::notify( osg::WARN )<<"Overwriting vertices input(NORMAL) with input from primitive"<setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); - - int offset = tmp_input->getOffset(); - if ( index_map[offset] == NULL ) - index_map[offset] = new osg::IntArray(); - geom->setNormalIndices( index_map[offset] ); - geom->setNormalArray( sources[tmp_el].getVec3Array() ); - } - - int inputNumber = 0; - while ( findInputSourceBySemantic( inputs, "TEXCOORD", texcoord_source, &tmp_input, inputNumber ) ) - { - int offset = tmp_input->getOffset(); - int set = tmp_input->getSet(); - if (NULL != GeometryWrapper) + size_t primitiveLength = vCount[i]; + if (j + primitiveLength > vertexList.size()) { - if (GeometryWrapper->_TexcoordSetMap.find(set) != GeometryWrapper->_TexcoordSetMap.end()) - osg::notify( osg::WARN )<<"Duplicate texcoord set: "<< set <_TexcoordSetMap[set] = TexCoordSetsUsed; + osg::notify(osg::WARN) << "Error: vertex counts are greater than the number of indices." << std::endl; + return; } - - if ( index_map[offset] == NULL ) + for (size_t k = 2; k < primitiveLength; ++k) { - index_map[offset] = new osg::IntArray(); + pDrawTriangles->push_back(vertexList[j]); + pDrawTriangles->push_back(vertexList[j+k-1]); + pDrawTriangles->push_back(vertexList[j+k]); } - geom->setTexCoordIndices( TexCoordSetsUsed, index_map[offset] ); - - if ( texcoord_source != NULL ) - { - domSourceReader &sc = sources[texcoord_source]; - switch( sc.getArrayType() ) { - case domSourceReader::Vec2: - geom->setTexCoordArray( TexCoordSetsUsed, sc.getVec2Array() ); - break; - case domSourceReader::Vec3: - geom->setTexCoordArray( TexCoordSetsUsed, sc.getVec3Array() ); - break; - default: - osg::notify( osg::WARN )<<"Unsupported array type: "<< sc.getArrayType() < +void daeReader::processPolygons(osg::Geode* geode, + const domMesh* pDomMesh, const T *group, SourceMap& sources) +{ + osg::Geometry *geometry = new osg::Geometry(); + geometry->setName(group->getMaterial()); + geode->addDrawable(geometry); + + osg::DrawElementsUInt* pDrawElements = new osg::DrawElementsUInt(GL_TRIANGLES); + geometry->addPrimitiveSet(pDrawElements); + + std::vector > indexLists; + resolveMeshArrays(group->getP_array(), group->getInput_array(), pDomMesh, + geometry, sources, indexLists); + + for ( size_t i = 0; i < indexLists.size(); ++i) + { + const std::vector& indices = indexLists[i]; + + for (size_t j = 2; j < indices.size(); ++j) + { + pDrawElements->push_back(indices.front()); + pDrawElements->push_back(indices[j - 1]); + pDrawElements->push_back(indices[j]); + } + } +} + +void processVertices( + domVertices* vertices, + daeElement*& position_source, + daeElement*& color_source, + daeElement*& normal_source, + daeElement*& texcoord_source) +{ + const domInputLocal_Array& inputs = vertices->getInput_array(); + + // Process input elements within the vertices element. These are of the unshared type + // and therefore cannot have set and offset attributes + + for (size_t i = 0; i < inputs.getCount(); ++i) + { + xsNMTOKEN semantic = inputs[i]->getSemantic(); + daeElement* pDaeElement = getElementFromURI(inputs[i]->getSource()); + if (strcmp(COMMON_PROFILE_INPUT_POSITION, semantic) == 0) + { + position_source = pDaeElement; + } + else if (strcmp(COMMON_PROFILE_INPUT_COLOR, semantic) == 0) + { + color_source = pDaeElement; + } + else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, semantic) == 0) + { + normal_source = pDaeElement; + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, semantic) == 0) + { + texcoord_source = pDaeElement; + } + } +} + +// I've never seen more than 2 used so this should be enough. If you find that +// a greater number is needed then increase it accordingly and submit the change +// to OpenSceneGraph. +// Why not use a vector? Because a large map of VertexIndices is used and +// allocating vectors for each element would make it a lot slower. +const int MAX_TEXTURE_COORDINATE_SETS = 4; + +void resolveMeshInputs( + const domInputLocalOffset_Array &inputs, + daeElement*& position_source, + daeElement*& color_source, + daeElement*& normal_source, + daeElement* texcoord_sources[MAX_TEXTURE_COORDINATE_SETS], + int& position_offset, + int& color_offset, + int& normal_offset, + int texcoord_offsets[MAX_TEXTURE_COORDINATE_SETS]) +{ + position_source = color_source = normal_source = NULL; + position_offset = color_offset = normal_offset = 0; + for (int i = 0; i < MAX_TEXTURE_COORDINATE_SETS; ++i) + { + texcoord_sources[i] = NULL; + texcoord_offsets[i] = NULL; + } + + for ( size_t i = 0; i < inputs.getCount(); i++ ) + { + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0) + { + daeElement* pDaeElement = getElementFromURI(inputs[i]->getSource()); + if (domVertices* vertices = daeSafeCast(pDaeElement)) + { + processVertices(vertices, position_source, color_source, normal_source, texcoord_sources[0]); + position_offset = inputs[i]->getOffset(); + + if (color_source) color_offset = position_offset; + if (normal_source) normal_offset = position_offset; + if (texcoord_sources[0]) texcoord_offsets[0] = position_offset; + } + break; + } + } + + for ( size_t i = 0; i < inputs.getCount(); i++ ) + { + xsNMTOKEN semantic = inputs[i]->getSemantic(); + daeElement* pDaeElement = getElementFromURI(inputs[i]->getSource()); + int offset = inputs[i]->getOffset(); + + if (strcmp(COMMON_PROFILE_INPUT_COLOR, semantic) == 0) + { + if (color_source != NULL) + osg::notify( osg::WARN )<<"Overwriting vertices input(COLOR) with input from primitive"<getSet(); + if (set >= MAX_TEXTURE_COORDINATE_SETS) + { + osg::notify( osg::WARN )<<"Texture coordinate set "<< set << + "was requested, the maximum allowed is " << MAX_TEXTURE_COORDINATE_SETS - 1 << "." << std::endl; + continue; + } + if (texcoord_sources[set]) + osg::notify( osg::WARN )<<"Overwriting vertices input(TEXCOORD) with input from primitive"< >& vertexLists) +{ + daeElement* position_source = NULL; + daeElement* color_source = NULL; + daeElement* normal_source = NULL; + daeElement* texcoord_sources[MAX_TEXTURE_COORDINATE_SETS] = {NULL}; + int position_offset = 0; + int color_offset = 0; + int normal_offset = 0; + int texcoord_offsets[MAX_TEXTURE_COORDINATE_SETS] = {0}; + + resolveMeshInputs(inputs, + position_source, + color_source, + normal_source, + texcoord_sources, + position_offset, + color_offset, + normal_offset, + texcoord_offsets); + + unsigned stride = 0; + for (size_t i = 0; i < inputs.getCount(); ++i) + { + stride = osg::maximum(stride, inputs[i]->getOffset()); + } + ++stride; + + typedef std::map VertexIndicesIndexMap; + VertexIndicesIndexMap vertexIndicesIndexMap; + + for (size_t j = 0; j < domPArray.getCount(); ++j) + { + const domListOfUInts& p = domPArray[j]->getValue(); + + for (size_t i = 0; i < p.getCount(); i += stride) + { + int texcoord_indices[MAX_TEXTURE_COORDINATE_SETS]; + for (int t = 0; t < MAX_TEXTURE_COORDINATE_SETS; ++t) + { + texcoord_indices[t] = p.get(i + texcoord_offsets[t]); + } + VertexIndices v( + p.get(i + position_offset), + p.get(i + color_offset), + p.get(i + normal_offset), + texcoord_indices); + vertexIndicesIndexMap.insert(VertexIndicesIndexMap::value_type(v, 0)); + } + } + + { + VertexIndicesIndexMap::iterator it = vertexIndicesIndexMap.begin(), end = vertexIndicesIndexMap.end(); + for (GLuint i = 0; it != end; ++it, ++i) + { + it->second = i; + } + } + + vertexLists.resize(domPArray.getCount()); + + for (size_t j = 0; j < domPArray.getCount(); ++j) + { + const domListOfUInts& p = domPArray[j]->getValue(); + + for (size_t i = 0; i < p.getCount(); i += stride) + { + int texcoord_indices[MAX_TEXTURE_COORDINATE_SETS]; + for (int t = 0; t < MAX_TEXTURE_COORDINATE_SETS; ++t) + { + texcoord_indices[t] = p.get(i + texcoord_offsets[t]); + } + VertexIndices v( + p.get(i + position_offset), + p.get(i + color_offset), + p.get(i + normal_offset), + texcoord_indices); + + GLuint index = vertexIndicesIndexMap.find(v)->second; + + _oldToNewIndexMap.insert(OldToNewIndexMap::value_type( + OldToNewIndexMap::key_type(pDomMesh, v.position_index), + OldToNewIndexMap::mapped_type(geometry, index))); + vertexLists[j].push_back(index); + } + } + + if (const osg::Vec3Array* source = sources[position_source].getVec3Array()) + { + osg::Vec3Array* pArray = new osg::Vec3Array; + + for (VertexIndicesIndexMap::const_iterator it = vertexIndicesIndexMap.begin(), + end = vertexIndicesIndexMap.end(); it != end; ++it) + { + pArray->push_back(source->at(it->first.position_index)); + } + + geometry->setVertexData(osg::Geometry::ArrayData(pArray, osg::Geometry::BIND_PER_VERTEX)); + } + + if (color_source) + { + if (const osg::Vec4Array* source = sources[color_source].getVec4Array()) + { + osg::Vec4Array* pArray = new osg::Vec4Array; + + for (VertexIndicesIndexMap::const_iterator it = vertexIndicesIndexMap.begin(), + end = vertexIndicesIndexMap.end(); it != end; ++it) + { + pArray->push_back(source->at(it->first.color_index)); + } + + geometry->setColorData(osg::Geometry::ArrayData(pArray, osg::Geometry::BIND_PER_VERTEX)); + } + } + + if (normal_source) + { + if (const osg::Vec3Array* source = sources[normal_source].getVec3Array()) + { + osg::Vec3Array* pArray = new osg::Vec3Array; + + for (VertexIndicesIndexMap::const_iterator it = vertexIndicesIndexMap.begin(), + end = vertexIndicesIndexMap.end(); it != end; ++it) + { + pArray->push_back(source->at(it->first.normal_index)); + } + + geometry->setNormalData(osg::Geometry::ArrayData(pArray, osg::Geometry::BIND_PER_VERTEX)); + } + } + + for (int texcoord_set = 0; texcoord_set < MAX_TEXTURE_COORDINATE_SETS; ++texcoord_set) + { + if (daeElement* texcoord_source = texcoord_sources[texcoord_set]) + { + osg::Array* pArray = NULL; + + if (const osg::Vec2Array* source = sources[texcoord_source].getVec2Array()) + { + osg::Vec2Array* pVec2Array = new osg::Vec2Array; + pArray = pVec2Array; + + for (VertexIndicesIndexMap::const_iterator it = vertexIndicesIndexMap.begin(), + end = vertexIndicesIndexMap.end(); it != end; ++it) + { + pVec2Array->push_back(source->at(it->first.texcoord_indices[texcoord_set])); + } + } + else if (const osg::Vec3Array* source = sources[texcoord_source].getVec3Array()) + { + osg::Vec3Array* pVec3Array = new osg::Vec3Array; + pArray = pVec3Array; + + for (VertexIndicesIndexMap::const_iterator it = vertexIndicesIndexMap.begin(), + end = vertexIndicesIndexMap.end(); it != end; ++it) + { + pVec3Array->push_back(source->at(it->first.texcoord_indices[texcoord_set])); + } + } + + if (pArray) + { + geometry->setTexCoordData(texcoord_set, osg::Geometry::ArrayData(pArray, osg::Geometry::BIND_PER_VERTEX)); + } + } + } +} diff --git a/src/osgPlugins/dae/daeRMaterials.cpp b/src/osgPlugins/dae/daeRMaterials.cpp index daf07c1fb..225a723c0 100644 --- a/src/osgPlugins/dae/daeRMaterials.cpp +++ b/src/osgPlugins/dae/daeRMaterials.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -29,8 +30,69 @@ #include -using namespace osgdae; +using namespace osgDAE; +template +void daeReader::getTransparencyCounts(daeDatabase* database, int& zero, int& one) const +{ + std::vector constantVec; + database->typeLookup(constantVec); + + for (size_t i = 0; i < constantVec.size(); ++i) + { + if (const domCommon_transparent_type* pTransparent = constantVec[i]->getTransparent()) + { + domFx_opaque_enum opaque = pTransparent->getOpaque(); + if (opaque == FX_OPAQUE_ENUM_RGB_ZERO) + { + ++one; + continue; + } + } + + if (const domCommon_float_or_param_type* pTransparency = constantVec[i]->getTransparency()) + { + float transparency; + + domFloat transparencyParam = 1.0; + if (pTransparency->getFloat()) + { + transparency = pTransparency->getFloat()->getValue(); + } + else if (pTransparency->getParam() && + GetFloatParam(pTransparency->getParam()->getRef(), transparencyParam)) + { + transparency = transparencyParam; + } + else + { + continue; + } + + if (transparency < 0.01f) + { + ++zero; + } + else if (transparency > 0.99f) + { + ++one; + } + } + + } + +} + +bool daeReader::findInvertTransparency(daeDatabase* database) const +{ + int zero = 0, one = 0; + getTransparencyCounts(database, zero, one); + getTransparencyCounts(database, zero, one); + getTransparencyCounts(database, zero, one); + getTransparencyCounts(database, zero, one); + + return zero > one; +} // // elements: @@ -63,7 +125,7 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os { osg::Drawable* drawable = geode->getDrawable(i); std::string materialName = drawable->getName(); - ReaderGeometry *cachedGeometry = dynamic_cast(cachedGeode->getDrawable(i)->asGeometry()); + osg::Geometry *cachedGeometry = dynamic_cast(cachedGeode->getDrawable(i)->asGeometry()); domInstance_material_Array &ima = bm->getTechnique_common()->getInstance_material_array(); std::string symbol; @@ -79,8 +141,8 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os { // Check material cache if this material already exists osg::StateSet* ss; - domMaterialStateSetMap::iterator iter = materialMap.find( mat ); - if ( iter != materialMap.end() ) + domMaterialStateSetMap::iterator iter = _materialMap.find( mat ); + if (iter != _materialMap.end() ) { // Reuse material ss = iter->second.get(); @@ -90,12 +152,10 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os // Create new material ss = new osg::StateSet; processMaterial(ss, mat); - materialMap.insert(std::make_pair(mat, ss)); + _materialMap.insert(std::make_pair(mat, ss)); } drawable->setStateSet(ss); // Need to process bind_vertex_inputs here - // This all feels like a horrible kludge to me - // I wish somebody with a better knowledge of Collada and OSG than me would have a go at it! // 1. Clear the texcoord arrays and associated texcoord vertex indices // from the current (cloned) drawable. osg::Geometry *clonedGeometry = drawable->asGeometry(); @@ -105,102 +165,13 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os break; } clonedGeometry->getTexCoordArrayList().clear(); + // 2. For each possible texture unit find the correct texcoord array and - // indices from the original (uncloned) drawable and place in the cloned drawable + // indices from the cached drawable and place in the cloned drawable // in the correct texture unit slot - std::string TransparencyMapTexcoordName; - osg::Texture2D *Texture; - if (NULL != (Texture = dynamic_cast(ss->getTextureAttribute(AMBIENT_OCCLUSION_UNIT, osg::StateAttribute::TEXTURE)))) - { - std::string AmbientOcclusionTexcoordName = Texture->getName(); - if (!AmbientOcclusionTexcoordName.empty()) - { - domInstance_material::domBind_vertex_input_Array &bvia = ima[j]->getBind_vertex_input_array(); - size_t k; - for ( k = 0; k < bvia.getCount(); k++) - { - if (!strcmp(bvia[k]->getSemantic(), AmbientOcclusionTexcoordName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), "TEXCOORD")) - { - // OK - found the effect name, now see if I can find a matching set in the cachedGeometry - if (NULL != cachedGeometry) - { - std::map::iterator iTr; - if (cachedGeometry->_TexcoordSetMap.end() != (iTr = cachedGeometry->_TexcoordSetMap.find(bvia[k]->getInput_set()))) - { - // Copy the texture cordinates and indices (if any) into the cloned geometry - clonedGeometry->setTexCoordData(AMBIENT_OCCLUSION_UNIT, cachedGeometry->getTexCoordData(iTr->second)); - } - } - break; - } - } - if (k == bvia.getCount()) - osg::notify( osg::WARN ) << "Failed to find matching for " << AmbientOcclusionTexcoordName << std::endl; - } - } - if (NULL != (Texture = dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE)))) - { - std::string MainTextureTexcoordName = Texture->getName(); - if (!MainTextureTexcoordName.empty()) - { - domInstance_material::domBind_vertex_input_Array &bvia = ima[j]->getBind_vertex_input_array(); - size_t k; - for ( k = 0; k < bvia.getCount(); k++) - { - if (!strcmp(bvia[k]->getSemantic(), MainTextureTexcoordName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), "TEXCOORD")) - { - // OK - found the effect name, now see if I can find a matching set in the cachedGeometry - if (NULL != cachedGeometry) - { - std::map::iterator iTr; - if (cachedGeometry->_TexcoordSetMap.end() != (iTr = cachedGeometry->_TexcoordSetMap.find(bvia[k]->getInput_set()))) - { - // Copy the texture cordinates and indices (if any) into the cloned geometry - clonedGeometry->setTexCoordData(MAIN_TEXTURE_UNIT, cachedGeometry->getTexCoordData(iTr->second)); - } - } - break; - } - } - if (k == bvia.getCount()) - { - osg::notify( osg::WARN ) << "Failed to find matching for " << MainTextureTexcoordName << std::endl; - // This may be a departure from the spec. For the time being I am only going to do this - // for the MAIN_TEXTURE_UNIT. - // Not found so just use the first TEXCOORD we have if any. - if (cachedGeometry->_TexcoordSetMap.size() > 0) - clonedGeometry->setTexCoordData(MAIN_TEXTURE_UNIT, cachedGeometry->getTexCoordData(cachedGeometry->_TexcoordSetMap.begin()->second)); - } - } - } - if (NULL != (Texture = dynamic_cast(ss->getTextureAttribute(TRANSPARENCY_MAP_UNIT, osg::StateAttribute::TEXTURE)))) - { - std::string TransparencyMapTexcoordName = Texture->getName(); - if (!TransparencyMapTexcoordName.empty()) - { - domInstance_material::domBind_vertex_input_Array &bvia = ima[j]->getBind_vertex_input_array(); - size_t k; - for ( k = 0; k < bvia.getCount(); k++) - { - if (!strcmp(bvia[k]->getSemantic(), TransparencyMapTexcoordName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), "TEXCOORD")) - { - // OK - found the effect name, now see if I can find a matching set in the cachedGeometry - if (NULL != cachedGeometry) - { - std::map::iterator iTr; - if (cachedGeometry->_TexcoordSetMap.end() != (iTr = cachedGeometry->_TexcoordSetMap.find(bvia[k]->getInput_set()))) - { - // Copy the texture cordinates and indices (if any) into the cloned geometry - clonedGeometry->setTexCoordData(TRANSPARENCY_MAP_UNIT, cachedGeometry->getTexCoordData(iTr->second)); - } - } - break; - } - } - if (k == bvia.getCount()) - osg::notify( osg::WARN ) << "Failed to find matching for " << TransparencyMapTexcoordName << std::endl; - } - } + copyTextureCoordinateSet(ss, cachedGeometry, clonedGeometry, ima[j], AMBIENT_OCCLUSION_UNIT); + copyTextureCoordinateSet(ss, cachedGeometry, clonedGeometry, ima[j], MAIN_TEXTURE_UNIT); + copyTextureCoordinateSet(ss, cachedGeometry, clonedGeometry, ima[j], TRANSPARENCY_MAP_UNIT); } else { @@ -227,8 +198,8 @@ void daeReader::processBindMaterial( domBind_material *bm, domGeometry *geom, os // 0..* void daeReader::processMaterial(osg::StateSet *ss, domMaterial *mat ) { - currentInstance_effect = mat->getInstance_effect(); - domEffect *effect = daeSafeCast< domEffect >( getElementFromURI( currentInstance_effect->getUrl() ) ); + _currentInstance_effect = mat->getInstance_effect(); + domEffect *effect = daeSafeCast< domEffect >( getElementFromURI( _currentInstance_effect->getUrl() ) ); if (effect) { processEffect(ss, effect); @@ -259,14 +230,14 @@ void daeReader::processEffect(osg::StateSet *ss, domEffect *effect ) for ( size_t i = 0; i < effect->getFx_profile_abstract_array().getCount(); i++ ) { domProfile_COMMON *pc = daeSafeCast< domProfile_COMMON >( effect->getFx_profile_abstract_array()[i] ); - if ( pc != NULL ) + if (pc != NULL ) { - if ( hasCOMMON ) + if (hasCOMMON ) { osg::notify( osg::WARN ) << "Effect already has a profile_COMMON. Skipping this one" << std::endl; continue; } - currentEffect = effect; + _currentEffect = effect; processProfileCOMMON(ss, pc); hasCOMMON = true; continue; @@ -345,21 +316,21 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 // 0..1 // 0..1 - if ( b != NULL ) + if (b != NULL ) { - osg::StateAttribute *EmissionStateAttribute = NULL; - osg::StateAttribute *AmbientStateAttribute = NULL; - osg::StateAttribute *DiffuseStateAttribute = NULL; - processColorOrTextureType( b->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); + osg::Texture2D *EmissionStateAttribute = NULL; + osg::Texture2D *AmbientStateAttribute = NULL; + osg::Texture2D *DiffuseStateAttribute = NULL; + processColorOrTextureType(ss, b->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); if (NULL != EmissionStateAttribute) osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; - processColorOrTextureType( b->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute ); + processColorOrTextureType(ss, b->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute ); - processColorOrTextureType( b->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); - if ( DiffuseStateAttribute != NULL ) + processColorOrTextureType(ss, b->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); + if (DiffuseStateAttribute != NULL ) { - if ( AmbientStateAttribute != NULL ) + if (AmbientStateAttribute != NULL ) { // Set the ambient and diffuse colour white so that the incoming fragment colour ends up as a // lit white colour. I modulate both textures onto this to approximate the lighting equation. @@ -391,11 +362,11 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) } else { - if ( NULL != AmbientStateAttribute ) + if (NULL != AmbientStateAttribute ) osg::notify( osg::WARN ) << "Ambient occlusion map only supported when diffuse texture also specified" << std::endl; } - if(processColorOrTextureType( b->getSpecular(), osg::Material::SPECULAR, mat.get(), b->getShininess() ) && (NULL != DiffuseStateAttribute) ) + if (processColorOrTextureType(ss, b->getSpecular(), osg::Material::SPECULAR, mat.get(), b->getShininess() ) && (NULL != DiffuseStateAttribute) ) { // Diffuse texture will defeat specular highlighting // So postpone specular - Not sure if I should do this here @@ -419,21 +390,21 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 // 0..1 // 0..1 - else if ( p != NULL ) + else if (p != NULL ) { - osg::StateAttribute *EmissionStateAttribute = NULL; - osg::StateAttribute *AmbientStateAttribute = NULL; - osg::StateAttribute *DiffuseStateAttribute = NULL; - processColorOrTextureType( p->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); + osg::Texture2D *EmissionStateAttribute = NULL; + osg::Texture2D *AmbientStateAttribute = NULL; + osg::Texture2D *DiffuseStateAttribute = NULL; + processColorOrTextureType(ss, p->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); if (NULL != EmissionStateAttribute) osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; - processColorOrTextureType( p->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute ); + processColorOrTextureType(ss, p->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute ); - processColorOrTextureType( p->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); - if ( DiffuseStateAttribute != NULL ) + processColorOrTextureType(ss, p->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); + if (DiffuseStateAttribute != NULL ) { - if ( AmbientStateAttribute != NULL ) + if (AmbientStateAttribute != NULL ) { // Set the ambient and diffuse colour white so that the incoming fragment colour ends up as a // lit white colour. I modulate both textures onto this to approximate the lighting equation. @@ -465,11 +436,11 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) } else { - if ( NULL != AmbientStateAttribute ) + if (NULL != AmbientStateAttribute ) osg::notify( osg::WARN ) << "Ambient occlusion map only supported when diffuse texture also specified" << std::endl; } - if(processColorOrTextureType( p->getSpecular(), osg::Material::SPECULAR, mat.get(), p->getShininess() ) && (NULL != DiffuseStateAttribute) ) + if (processColorOrTextureType(ss, p->getSpecular(), osg::Material::SPECULAR, mat.get(), p->getShininess() ) && (NULL != DiffuseStateAttribute) ) { // Diffuse texture will defeat specular highlighting // So postpone specular - Not sure if I should do this here @@ -491,21 +462,21 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 // 0..1 // 0..1 - else if ( l != NULL ) + else if (l != NULL ) { - osg::StateAttribute *EmissionStateAttribute = NULL; - osg::StateAttribute *AmbientStateAttribute = NULL; - osg::StateAttribute *DiffuseStateAttribute = NULL; - processColorOrTextureType( l->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); + osg::Texture2D *EmissionStateAttribute = NULL; + osg::Texture2D *AmbientStateAttribute = NULL; + osg::Texture2D *DiffuseStateAttribute = NULL; + processColorOrTextureType(ss, l->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &EmissionStateAttribute ); if (NULL != EmissionStateAttribute) osg::notify( osg::WARN ) << "Currently no support for in Emission channel " << std::endl; - processColorOrTextureType( l->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute); + processColorOrTextureType(ss, l->getAmbient(), osg::Material::AMBIENT, mat.get(), NULL, &AmbientStateAttribute); - processColorOrTextureType( l->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); - if ( DiffuseStateAttribute != NULL ) + processColorOrTextureType(ss, l->getDiffuse(), osg::Material::DIFFUSE, mat.get(), NULL, &DiffuseStateAttribute ); + if (DiffuseStateAttribute != NULL ) { - if ( AmbientStateAttribute != NULL ) + if (AmbientStateAttribute != NULL ) { // Set the ambient and diffuse colour white so that the incoming fragment colour ends up as a // lit white colour. I modulate both textures onto this to approximate the lighting equation. @@ -537,7 +508,7 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) } else { - if ( NULL != AmbientStateAttribute ) + if (NULL != AmbientStateAttribute ) osg::notify( osg::WARN ) << "Ambient occlusion map only supported when diffuse texture also specified" << std::endl; } @@ -551,11 +522,11 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 0..1 // 0..1 // 0..1 - else if ( c != NULL ) + else if (c != NULL ) { - osg::StateAttribute *sa = NULL; - processColorOrTextureType( c->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &sa ); - if ( sa != NULL ) + osg::Texture2D *sa = NULL; + processColorOrTextureType(ss, c->getEmission(), osg::Material::EMISSION, mat.get(), NULL, &sa ); + if (sa != NULL ) { ss->setTextureMode( MAIN_TEXTURE_UNIT, GL_TEXTURE_2D, GL_TRUE ); ss->setTextureAttribute(MAIN_TEXTURE_UNIT, new osg::TexEnv(osg::TexEnv::REPLACE) ); @@ -585,24 +556,27 @@ void daeReader::processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ) // 1 texture // 1 texcoord // 0..* extra -bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *cot, - osg::Material::ColorMode channel, - osg::Material *mat, - domCommon_float_or_param_type *fop, - osg::StateAttribute **sa, - bool blinn) +bool daeReader::processColorOrTextureType(const osg::StateSet* ss, + domCommon_color_or_texture_type *cot, + osg::Material::ColorMode channel, + osg::Material *mat, + domCommon_float_or_param_type *fop, + osg::Texture2D **sa, + bool blinn) { - if ( cot == NULL ) + if (cot == NULL ) { return false; } bool retVal = false; + std::string texCoordSet; + //osg::StateAttribute *sa = NULL; //TODO: Make all channels process type of value - if ( channel == osg::Material::EMISSION ) + if (channel == osg::Material::EMISSION ) { - if ( cot->getColor() != NULL ) + if (cot->getColor() != NULL ) { domFloat4 &f4 = cot->getColor()->getValue(); mat->setEmission( osg::Material::FRONT_AND_BACK, osg::Vec4( f4[0], f4[1], f4[2], f4[3] ) ); @@ -621,7 +595,7 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c { if (sa != NULL) { - *sa = processTexture( cot->getTexture() ); + *sa = processTexture( cot->getTexture(), ss, MAIN_TEXTURE_UNIT); retVal = true; } else @@ -632,9 +606,9 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c osg::notify( osg::WARN ) << "Missing , or in Emission channel " << std::endl; } } - else if ( channel == osg::Material::AMBIENT ) + else if (channel == osg::Material::AMBIENT ) { - if ( cot->getColor() != NULL ) + if (cot->getColor() != NULL ) { domFloat4 &f4 = cot->getColor()->getValue(); mat->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4( f4[0], f4[1], f4[2], f4[3] ) ); @@ -652,7 +626,7 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c else if (cot->getTexture() != NULL) { if (sa != NULL) - *sa = processTexture( cot->getTexture() ); + *sa = processTexture( cot->getTexture(), ss, AMBIENT_OCCLUSION_UNIT); else { osg::notify( osg::WARN ) << "Currently no support for in Ambient channel " << std::endl; @@ -665,31 +639,31 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c osg::notify( osg::WARN ) << "Missing , or in Ambient channel " << std::endl; } } - else if ( channel == osg::Material::DIFFUSE ) + else if (channel == osg::Material::DIFFUSE ) { - if ( cot->getColor() != NULL ) + if (cot->getColor() != NULL ) { domFloat4 &f4 = cot->getColor()->getValue(); mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( f4[0], f4[1], f4[2], f4[3] ) ); retVal = true; } - else if ( cot->getTexture() != NULL) + else if (cot->getTexture() != NULL) { if (sa != NULL) - *sa = processTexture( cot->getTexture() ); + *sa = processTexture( cot->getTexture(), ss, MAIN_TEXTURE_UNIT); else { osg::notify( osg::WARN ) << "Currently no support for in Diffuse channel " << std::endl; mat->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4( 0.8f, 0.8f, 0.8f, 1.0f ) ); } domExtra *extra = cot->getTexture()->getExtra(); - if ( extra != NULL && extra->getType() != NULL && strcmp( extra->getType(), "color" ) == 0 ) + if (extra != NULL && extra->getType() != NULL && strcmp( extra->getType(), "color" ) == 0 ) { //the extra data for osg. Diffuse color can happen with a texture. for ( unsigned int i = 0; i < extra->getTechnique_array().getCount(); i++ ) { domTechnique *teq = extra->getTechnique_array()[i]; - if ( strcmp( teq->getProfile(), "SCEI" ) == 0 ) + if (strcmp( teq->getProfile(), "SCEI" ) == 0 ) { osg::Vec4 col; domAny *dcol = (domAny*)(daeElement*)teq->getContents()[0]; @@ -716,9 +690,9 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c osg::notify( osg::WARN ) << "Missing , or in Diffuse channel " << std::endl; } } - else if ( channel == osg::Material::SPECULAR ) + else if (channel == osg::Material::SPECULAR ) { - if ( cot->getColor() != NULL ) + if (cot->getColor() != NULL ) { domFloat4 &f4 = cot->getColor()->getValue(); mat->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4( f4[0], f4[1], f4[2], f4[3] ) ); @@ -742,7 +716,7 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c osg::notify( osg::WARN ) << "Missing , or in Specular channel " << std::endl; } - if ( fop != NULL && fop->getFloat() != NULL ) + if (fop != NULL && fop->getFloat() != NULL ) { float shininess = fop->getFloat()->getValue(); if (blinn) @@ -758,22 +732,22 @@ bool daeReader::processColorOrTextureType( domCommon_color_or_texture_type *c return retVal; } -bool daeReader::GetFloat4Param(xsNCName Reference, domFloat4 &f4) +bool daeReader::GetFloat4Param(xsNCName Reference, domFloat4 &f4) const { std::string MyReference = Reference; MyReference.insert(0, "./"); - daeSIDResolver Resolver(currentEffect, MyReference.c_str()); + daeSIDResolver Resolver(_currentEffect, MyReference.c_str()); daeElement *el = Resolver.getElement(); if (NULL == el) return false; - if (NULL != currentInstance_effect) + if (NULL != _currentInstance_effect) { // look here first for setparams // I am sure there must be a better way of doing this // Maybe the Collada DAE guys can give us a parameter management mechanism ! - const domInstance_effect::domSetparam_Array& SetParamArray = currentInstance_effect->getSetparam_array(); + const domInstance_effect::domSetparam_Array& SetParamArray = _currentInstance_effect->getSetparam_array(); size_t NumberOfSetParams = SetParamArray.getCount(); for (size_t i = 0; i < NumberOfSetParams; i++) { @@ -805,22 +779,22 @@ bool daeReader::GetFloat4Param(xsNCName Reference, domFloat4 &f4) return false; } -bool daeReader::GetFloatParam(xsNCName Reference, domFloat &f) +bool daeReader::GetFloatParam(xsNCName Reference, domFloat &f) const { std::string MyReference = Reference; MyReference.insert(0, "./"); - daeSIDResolver Resolver(currentEffect, MyReference.c_str()); + daeSIDResolver Resolver(_currentEffect, MyReference.c_str()); daeElement *el = Resolver.getElement(); if (NULL == el) return false; - if (NULL != currentInstance_effect) + if (NULL != _currentInstance_effect) { // look here first for setparams // I am sure there must be a better way of doing this // Maybe the Collada DAE guys can give us a parameter management mechanism ! - const domInstance_effect::domSetparam_Array& SetParamArray = currentInstance_effect->getSetparam_array(); + const domInstance_effect::domSetparam_Array& SetParamArray = _currentInstance_effect->getSetparam_array(); size_t NumberOfSetParams = SetParamArray.getCount(); for (size_t i = 0; i < NumberOfSetParams; i++) { @@ -852,8 +826,179 @@ bool daeReader::GetFloatParam(xsNCName Reference, domFloat &f) return false; } -osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_complexType::domTexture *tex ) +osg::Texture::WrapMode getWrapMode(domFx_sampler_wrap_common domWrap) { + switch (domWrap) + { + case FX_SAMPLER_WRAP_COMMON_WRAP: + return osg::Texture::REPEAT; + case FX_SAMPLER_WRAP_COMMON_MIRROR: + return osg::Texture::MIRROR; + case FX_SAMPLER_WRAP_COMMON_CLAMP: + return osg::Texture::CLAMP_TO_EDGE; + case FX_SAMPLER_WRAP_COMMON_NONE: + case FX_SAMPLER_WRAP_COMMON_BORDER: + return osg::Texture::CLAMP_TO_BORDER; + } + + return osg::Texture::CLAMP; +} + +osg::Texture::FilterMode getFilterMode(domFx_sampler_filter_common domFilter, bool allowMipMap) +{ + switch (domFilter) + { + case FX_SAMPLER_FILTER_COMMON_NEAREST: + return osg::Texture::NEAREST; + case FX_SAMPLER_FILTER_COMMON_LINEAR: + return osg::Texture::LINEAR; + } + + if (allowMipMap) + { + switch (domFilter) + { + case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_NEAREST: + return osg::Texture::NEAREST_MIPMAP_NEAREST; + case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_NEAREST: + return osg::Texture::LINEAR_MIPMAP_NEAREST; + case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_LINEAR: + return osg::Texture::NEAREST_MIPMAP_LINEAR; + case FX_SAMPLER_FILTER_COMMON_NONE: + case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_LINEAR: + return osg::Texture::LINEAR_MIPMAP_LINEAR; + } + } + + return osg::Texture::LINEAR; +} + +std::string daeReader::processImagePath(const domImage* pDomImage) const +{ + if (pDomImage == NULL) + { + osg::notify( osg::WARN ) << "Could not locate image for texture" << std::endl; + return std::string(); + } + + //Got a sampler and a surface and an imaged. Time to create the texture stuff for osg + if (pDomImage->getInit_from()) + { + pDomImage->getInit_from()->getValue().validate(); + if (strcmp(pDomImage->getInit_from()->getValue().getProtocol(), "file") == 0) + { + std::string path = pDomImage->getInit_from()->getValue().pathDir() + + pDomImage->getInit_from()->getValue().pathFile(); + path = cdom::uriToNativePath(path); + if (path.empty()) + { + osg::notify( osg::WARN ) << "Unable to get path from URI." << std::endl; + return std::string(); + } +#ifdef WIN32 + // If the path has a drive specifier or a UNC name then strip the leading / + const char* szFilename = path.c_str(); + if (path.size() > 2 && (path[2] == ':' || (path[1] == '/' && path[2] == '/'))) + return szFilename + 1; +#endif + return path; + } + else + { + osg::notify( osg::WARN ) << "Only images with a \"file\" scheme URI are supported in this version." << std::endl; + } + } + else + { + osg::notify( osg::WARN ) << "Embedded image data is not supported in this version." << std::endl; + } + return std::string(); +} + +float luminance(const osg::Vec4& color) +{ + return + color.r() * 0.212671f + + color.g() * 0.715160f + + color.b() * 0.072169f; +} + +osg::Image* daeReader::processImageTransparency(const osg::Image* srcImg, domFx_opaque_enum opaque, float transparency) const +{ + int s = srcImg->s(); + int t = srcImg->t(); + unsigned char* pixels = new unsigned char [s * t]; + + if (opaque == FX_OPAQUE_ENUM_RGB_ZERO) + { + for (int i = 0; i < t; ++i) + { + for (int j = 0; j < s; ++j) + { + osg::Vec4 color(srcImg->getColor(j, i)); + + pixels[i * s + j] = static_cast( + (1.0f - luminance(color) * transparency) * 255.0f); + } + } + } + else + { + bool texHasAlpha = false; + switch (srcImg->getPixelFormat()) + { + case GL_ALPHA: + case GL_LUMINANCE_ALPHA: + case GL_RGBA: + case GL_BGRA: + texHasAlpha = true; + } + + if (texHasAlpha) + { + for (int i = 0; i < t; ++i) + { + for (int j = 0; j < s; ++j) + { + osg::Vec4 color(srcImg->getColor(j, i)); + + pixels[i * s + j] = static_cast( + color.a() * transparency * 255.0f); + } + } + } + else + { + for (int i = 0; i < t; ++i) + { + for (int j = 0; j < s; ++j) + { + osg::Vec4 color(srcImg->getColor(j, i)); + + pixels[i * s + j] = static_cast( + luminance(color) * transparency * 255.0f); + } + } + } + } + + osg::Image* transparentImage = new osg::Image; + transparentImage->setWriteHint(osg::Image::STORE_INLINE); + transparentImage->setImage(s, t, 1, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, pixels, osg::Image::USE_NEW_DELETE); + + return transparentImage; +} + +osg::Texture2D* daeReader::processTexture( + domCommon_color_or_texture_type_complexType::domTexture *tex, + const osg::StateSet* ss, TextureUnitUsage tuu, + domFx_opaque_enum opaque, float transparency) +{ + TextureParameters parameters; + parameters.transparent = tuu == TRANSPARENCY_MAP_UNIT; + parameters.opaque = opaque; + parameters.transparency = transparency; + //find the newparam for the sampler based on the texture attribute domFx_sampler2D_common *sampler = NULL; domFx_surface_common *surface = NULL; @@ -862,15 +1007,15 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ std::string target = std::string("./") + std::string(tex->getTexture()); osg::notify(osg::NOTICE)<<"processTexture("<getTexture() << std::endl; - osg::notify( osg::WARN ) << "Checking if data does incorrect linking straight to the image" << std::endl; - dae->getDatabase()->getElement( (daeElement**)&dImg, 0, tex->getTexture(), "image" ); - if ( dImg != NULL ) + osg::notify( osg::WARN ) << "Could not locate newparam for texture sampler2D \"" << tex->getTexture() << + "\". Checking if data does incorrect linking straight to the image" << std::endl; + _dae->getDatabase()->getElement( (daeElement**)&dImg, 0, tex->getTexture(), "image" ); + if (dImg != NULL ) { osg::notify( osg::WARN ) << "Direct image link found. Data is incorrect but will continue to load texture" << std::endl; } @@ -880,16 +1025,16 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ domCommon_newparam_type *cnp = daeSafeCast< domCommon_newparam_type >( el ); domFx_newparam_common *npc = daeSafeCast< domFx_newparam_common >( el ); - if ( cnp != NULL ) + if (cnp != NULL ) { sampler = cnp->getSampler2D(); } - else if ( npc != NULL ) + else if (npc != NULL ) { sampler = npc->getFx_basic_type_common()->getSampler2D(); } - if ( sampler == NULL ) + if (sampler == NULL ) { osg::notify( osg::WARN ) << "Wrong newparam type. Expected sampler2D" << std::endl; return NULL; @@ -897,9 +1042,9 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ //find the newparam for the surface based on the sampler2D->source value target = std::string("./") + std::string( sampler->getSource()->getValue() ); - daeSIDResolver res2( currentEffect, target.c_str() ); + daeSIDResolver res2( _currentEffect, target.c_str() ); el = res2.getElement(); - if ( el == NULL ) + if (el == NULL ) { osg::notify( osg::WARN ) << "Could not locate newparam for source " << sampler->getSource()->getValue() << std::endl; return NULL; @@ -907,16 +1052,16 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ cnp = daeSafeCast< domCommon_newparam_type >( el ); npc = daeSafeCast< domFx_newparam_common >( el ); - if ( cnp != NULL ) + if (cnp != NULL ) { surface = cnp->getSurface(); } - else if ( npc != NULL ) + else if (npc != NULL ) { surface = npc->getFx_basic_type_common()->getSurface(); } - if ( surface == NULL ) + if (surface == NULL ) { osg::notify( osg::WARN ) << "Wrong newparam type. Expected surface" << std::endl; return NULL; @@ -926,207 +1071,77 @@ osg::StateAttribute *daeReader::processTexture( domCommon_color_or_texture_type_ daeIDRef &ref = surface->getFx_surface_init_common()->getInit_from_array()[0]->getValue(); dImg = daeSafeCast< domImage >( getElementFromIDRef( ref ) ); } - if ( dImg == NULL ) + + parameters.filename = processImagePath(dImg); + if (parameters.filename.empty()) { - osg::notify( osg::WARN ) << "Could not locate image for texture" << std::endl; return NULL; } - //Got a sampler and a surface and an imaged. Time to create the texture stuff for osg - osg::ref_ptr img = NULL; - if ( dImg->getInit_from() != NULL ) + + //set texture parameters + if (sampler) { - // daeURI uri = dImg->getInit_from()->getValue(); - dImg->getInit_from()->getValue().validate(); - if ( std::string( dImg->getInit_from()->getValue().getProtocol() ) == std::string( "file" ) ) + if (sampler->getWrap_s()) { - //unsigned int bufSize = 1; //for the null char - //bufSize += dImg->getInit_from()->getValue().pathDir().size(); - //bufSize += dImg->getInit_from()->getValue().pathFile().size(); - std::string path = dImg->getInit_from()->getValue().pathDir()+ - dImg->getInit_from()->getValue().pathFile(); - // remove space encodings - // - path = cdom::uriToNativePath(path); - if(path.empty()) - { - osg::notify( osg::WARN ) << "Unable to get path from URI." << std::endl; - return NULL; - } -#ifdef WIN32 - // If the path has a drive specifier or a UNC name then strip the leading / - const char* filename =path.c_str(); - if ((path[2] == ':') || ((path[1] == '/') && (path[2] == '/'))) - ++filename;// = path+1; -// else -// filename = path; -#else - const char* filename = path.c_str(); -#endif - img = osgDB::readRefImageFile( filename ); - - osg::notify(osg::INFO)<<" processTexture(..) - readImage("<getWrap_s()->getValue()); } - else + if (sampler->getWrap_t()) { - osg::notify( osg::WARN ) << "Only images with a \"file\" scheme URI are supported in this version." << std::endl; - return NULL; + parameters.wrap_t = getWrapMode(sampler->getWrap_s()->getValue()); } + + if (sampler->getMinfilter()) + { + parameters.filter_min = getFilterMode(sampler->getMinfilter()->getValue(), true); + } + if (sampler->getMagfilter()) + { + parameters.filter_min = getFilterMode(sampler->getMagfilter()->getValue(), false); + } + + if (sampler->getBorder_color() != NULL ) + { + const domFloat4& col = sampler->getBorder_color()->getValue(); + parameters.border.set(col[0], col[1], col[2], col[3]); + } + } + + osg::Texture2D* t2D = NULL; + TextureParametersMap::const_iterator mapIt = _textureParamMap.find(parameters); + if (mapIt != _textureParamMap.end()) + { + t2D = mapIt->second.get(); } else { - osg::notify( osg::WARN ) << "Embedded image data is not supported in this version." << std::endl; + osg::ref_ptr img = osgDB::readRefImageFile(parameters.filename); + + if (!img.valid()) + { + _textureParamMap[parameters] = NULL; return NULL; + } + + osg::notify(osg::INFO)<<" processTexture(..) - readImage("<setWrap( osg::Texture::WRAP_S, parameters.wrap_s); + t2D->setWrap( osg::Texture::WRAP_T, parameters.wrap_t); + t2D->setFilter( osg::Texture::MIN_FILTER, parameters.filter_min); + t2D->setFilter( osg::Texture::MAG_FILTER, parameters.filter_mag); + t2D->setBorderColor(parameters.border); + + _textureParamMap[parameters] = t2D; } - osg::Texture2D *t2D = new osg::Texture2D( img.get() ); - //set texture parameters - if ( sampler != NULL ) - { - if ( sampler->getWrap_s() != NULL ) - { - osg::Texture::WrapMode wrap; - switch( sampler->getWrap_s()->getValue() ) - { - case FX_SAMPLER_WRAP_COMMON_WRAP: - wrap = osg::Texture::REPEAT; - break; - case FX_SAMPLER_WRAP_COMMON_MIRROR: - wrap = osg::Texture::MIRROR; - break; - case FX_SAMPLER_WRAP_COMMON_CLAMP: - wrap = osg::Texture::CLAMP_TO_EDGE; - break; - case FX_SAMPLER_WRAP_COMMON_NONE: - case FX_SAMPLER_WRAP_COMMON_BORDER: - wrap = osg::Texture::CLAMP_TO_BORDER; - break; - default: - wrap = osg::Texture::CLAMP; - break; - } - t2D->setWrap( osg::Texture::WRAP_S, wrap ); - } - else - { - t2D->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); - } - if ( sampler->getWrap_t() != NULL ) - { - osg::Texture::WrapMode wrap; - switch( sampler->getWrap_t()->getValue() ) - { - case FX_SAMPLER_WRAP_COMMON_WRAP: - wrap = osg::Texture::REPEAT; - break; - case FX_SAMPLER_WRAP_COMMON_MIRROR: - wrap = osg::Texture::MIRROR; - break; - case FX_SAMPLER_WRAP_COMMON_CLAMP: - wrap = osg::Texture::CLAMP_TO_EDGE; - break; - case FX_SAMPLER_WRAP_COMMON_NONE: - case FX_SAMPLER_WRAP_COMMON_BORDER: - wrap = osg::Texture::CLAMP_TO_BORDER; - break; - default: - wrap = osg::Texture::CLAMP; - break; - } - t2D->setWrap( osg::Texture::WRAP_T, wrap ); - } - else - { - t2D->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT ); - } - if ( sampler->getMinfilter() != NULL ) - { - osg::Texture::FilterMode mode; - switch( sampler->getMinfilter()->getValue() ) - { - case FX_SAMPLER_FILTER_COMMON_NEAREST: - mode = osg::Texture::NEAREST; - break; - case FX_SAMPLER_FILTER_COMMON_LINEAR: - mode = osg::Texture::LINEAR; - break; - case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_NEAREST: - mode = osg::Texture::NEAREST_MIPMAP_NEAREST; - break; - case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_NEAREST: - mode = osg::Texture::LINEAR_MIPMAP_NEAREST; - break; - case FX_SAMPLER_FILTER_COMMON_NONE: - case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_LINEAR: - mode = osg::Texture::NEAREST_MIPMAP_LINEAR; - break; - case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_LINEAR: - mode = osg::Texture::LINEAR_MIPMAP_LINEAR; - break; - default: - mode = osg::Texture::LINEAR; - break; - } - t2D->setFilter( osg::Texture::MIN_FILTER, mode ); - } - else - { - t2D->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST_MIPMAP_LINEAR ); - } - if ( sampler->getMagfilter() != NULL ) - { - osg::Texture::FilterMode mode; - switch( sampler->getMagfilter()->getValue() ) - { - case FX_SAMPLER_FILTER_COMMON_NEAREST: - mode = osg::Texture::NEAREST; - break; - case FX_SAMPLER_FILTER_COMMON_NONE: - case FX_SAMPLER_FILTER_COMMON_LINEAR: - mode = osg::Texture::LINEAR; - break; - case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_NEAREST: - mode = osg::Texture::NEAREST_MIPMAP_NEAREST; - break; - case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_NEAREST: - mode = osg::Texture::LINEAR_MIPMAP_NEAREST; - break; - case FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_LINEAR: - mode = osg::Texture::NEAREST_MIPMAP_LINEAR; - break; - case FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_LINEAR: - mode = osg::Texture::LINEAR_MIPMAP_LINEAR; - break; - default: - mode = osg::Texture::LINEAR; - break; - } - t2D->setFilter( osg::Texture::MAG_FILTER, mode ); - } - else - { - t2D->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); - } - if ( sampler->getBorder_color() != NULL ) - { - const domFloat4 &col = sampler->getBorder_color()->getValue(); - t2D->setBorderColor( osg::Vec4( col[0], col[1], col[2], col[3] ) ); - } - } - else - { - t2D->setWrap( osg::Texture::WRAP_S, osg::Texture::REPEAT ); - t2D->setWrap( osg::Texture::WRAP_T, osg::Texture::REPEAT ); - t2D->setFilter( osg::Texture::MIN_FILTER, osg::Texture::NEAREST_MIPMAP_LINEAR ); - t2D->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); - } + _texCoordSetMap[TextureToCoordSetMap::key_type(ss, tuu)] = tex->getTexcoord(); - // Store the texcoord name in the texture object - t2D->setName(tex->getTexcoord()); return t2D; } @@ -1159,240 +1174,141 @@ void daeReader::processTransparencySettings( domCommon_transparent_type *ctt, osg::Material *material, xsNCName diffuseTextureName ) { + if (ss == NULL) + return; + if (NULL == ctt && NULL == pTransparency) return; - if (ctt && ctt->getTexture() != NULL) + float transparency = 1.0f; + if (pTransparency) { - if (!diffuseTextureName || (strcmp( ctt->getTexture()->getTexture(), diffuseTextureName))) + if (pTransparency->getFloat()) { - osg::notify( osg::WARN ) << "Currently no support for different textures in diffuse and transparent channels." << std::endl; - return; + transparency = pTransparency->getFloat()->getValue(); + } + else if (pTransparency->getParam()) + { + domFloat transparencyParam; + if (GetFloatParam(pTransparency->getParam()->getRef(), transparencyParam)) + { + transparency = transparencyParam; + } + } + + if (_invertTransparency) + { + transparency = 1.0f - transparency; } } - - // Fix up defaults according to 1.4.1 release notes - domFloat4 f4; - domFx_opaque_enum Opaque = FX_OPAQUE_ENUM_A_ONE; - if (NULL == ctt) + + osg::Texture2D* pTransparentTexture = NULL; + osg::Vec4 transparentColor(transparency, transparency, transparency, transparency); + + // Fix up defaults according to "Determining Transparency" chapter of 1.4.1 spec + domFx_opaque_enum opaque = FX_OPAQUE_ENUM_A_ONE; + if (ctt) { - f4.append(0.0f); - f4.append(0.0f); - f4.append(0.0f); - f4.append(1.0f); + opaque = ctt->getOpaque(); + if (ctt->getColor()) + { + const domFx_color_common& domColorValue = ctt->getColor()->getValue(); + transparentColor.set( + domColorValue.get(0), + domColorValue.get(1), + domColorValue.get(2), + domColorValue.get(3)); + + if (opaque == FX_OPAQUE_ENUM_RGB_ZERO) + { + transparentColor.set( + 1.0f - transparentColor.r() * transparency, + 1.0f - transparentColor.g() * transparency, + 1.0f - transparentColor.b() * transparency, + 1.0f - luminance(transparentColor) * transparency); + } + else + { + float a = transparentColor.a() * transparency; + transparentColor.set(a, a, a, a); + } + } + else if (ctt->getTexture()) + { + pTransparentTexture = processTexture(ctt->getTexture(), ss, TRANSPARENCY_MAP_UNIT, opaque, transparency); + } + } + + if (pTransparentTexture) + { + ss->setTextureAttributeAndModes(TRANSPARENCY_MAP_UNIT, pTransparentTexture); + ss->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); } else { - Opaque = ctt->getOpaque(); - if (NULL != ctt->getColor()) + bool strictTransparency = _strictTransparency; + if (!strictTransparency) { - f4 = ctt->getColor()->getValue(); + const osg::Texture* pMainTexture = dynamic_cast( + ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE)); + bool haveTranslucentTexture = pMainTexture && + pMainTexture->getImage(0) && pMainTexture->getImage(0)->isImageTranslucent(); + strictTransparency = !haveTranslucentTexture; } - else if ((NULL == ctt->getParam()) || !GetFloat4Param(ctt->getParam()->getRef(), f4)) - { - f4.append(0.0f); - f4.append(0.0f); - f4.append(0.0f); - f4.append(1.0f); - } - } - domFloat Transparency = 1.0f; - if (NULL != pTransparency) - { - if (NULL != pTransparency->getFloat()) + if (strictTransparency) { - Transparency = pTransparency->getFloat()->getValue(); - if (m_AuthoringTool == GOOGLE_SKETCHUP) // Google back to front support - Transparency = 1.0f - Transparency; - } - else if (NULL != pTransparency->getParam()) - { - if (GetFloatParam(pTransparency->getParam()->getRef(), Transparency)) + if (transparentColor == osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)) { - if (m_AuthoringTool == GOOGLE_SKETCHUP) // Google back to front support - Transparency = 1.0f - Transparency; + return; } - else - Transparency = 1.0f; - } - } - if (NULL != ctt || NULL != pTransparency) - { - int SourceBlendFactor; - int DestBlendFactor; - bool SwitchOnTheBlender = false; - if (m_StrictTransparency) - { - // Process transparent and transparency settings accroding to a strict interpretation of the spec - // See https://collada.org/public_forum/viewtopic.php?f=12&t=1210 - SwitchOnTheBlender = true; - switch(Opaque) - { - /* - case FX_OPAQUE_ENUM_RGB_ONE: - if (ctt->getTexture() != NULL) - { - SourceBlendFactor = GL_SRC_COLOR; - DestBlendFactor = GL_ONE_MINUS_SRC_COLOR; - } - else - { - SourceBlendFactor = GL_CONSTANT_COLOR; - DestBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; - } - break; - case FX_OPAQUE_ALPHA_ZERO: - if (ctt->getTexture() != NULL) - { - SourceBlendFactor = GL_ONE_MINUS_SRC_ALPHA; - DestBlendFactor = GL_SRC_ALPHA; - } - else - { - SourceBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; - DestBlendFactor = GL_CONSTANT_ALPHA; - } - break; - */ - case FX_OPAQUE_ENUM_RGB_ZERO: - if (ctt->getTexture() != NULL) - { - SourceBlendFactor = GL_ONE_MINUS_SRC_COLOR; - DestBlendFactor = GL_SRC_COLOR; - } - else - { - SourceBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; - DestBlendFactor = GL_CONSTANT_COLOR; - } - break; - default: - if (ctt->getTexture() != NULL) - { - SourceBlendFactor = GL_SRC_ALPHA; - DestBlendFactor = GL_ONE_MINUS_SRC_ALPHA; - } - else - { - SourceBlendFactor = GL_CONSTANT_ALPHA; - DestBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; - } - break; - } + ss->setAttributeAndModes(new osg::BlendColor(transparentColor)); + ss->setAttributeAndModes(new osg::BlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR)); } else { - // Jump through various hoops to accomodate the multiplicity of different ways - // that various people have interpreted the specification - // I assume that the presence of either a or a element - // means that the user may want some kind of alpha blending - bool HaveDiffuseTexture = false; - bool HaveTranslucentDiffuseTexture = false; - // Unfortunately isImageTransparent only works for A_ONE_OPAQUE - if ((NULL != ss) && - (HaveDiffuseTexture = (GL_TRUE == ss->getTextureMode(MAIN_TEXTURE_UNIT, GL_TEXTURE_2D))) && - (NULL != dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE))) && - (NULL != dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE))->getImage()) && - (dynamic_cast(ss->getTextureAttribute(MAIN_TEXTURE_UNIT, osg::StateAttribute::TEXTURE))->getImage()->isImageTranslucent())) - HaveTranslucentDiffuseTexture = true; - osg::Vec4 Diffuse; - if (material) - Diffuse = material->getDiffuse(osg::Material::FRONT_AND_BACK); + ss->setAttributeAndModes(new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + } + } - // Determine whether or not to switch on the blender and which blending factors to use. - // I switch the blender on if the supplied (or default) and elements work out as non opaque, - // or if they work out opaque and I have a translucent texture in the MAIN_TEXTURE_UNIT or a non opaque value in the diffuse colour - switch(Opaque) + ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); +} + +void daeReader::copyTextureCoordinateSet(const osg::StateSet* ss, const osg::Geometry* cachedGeometry, osg::Geometry* clonedGeometry, const domInstance_material* im, TextureUnitUsage tuu) +{ + if (!ss->getTextureAttribute(tuu, osg::StateAttribute::TEXTURE)) + return; + + const std::string& texCoordSetName = _texCoordSetMap + [TextureToCoordSetMap::key_type(ss, tuu)]; + if (!texCoordSetName.empty()) + { + const domInstance_material::domBind_vertex_input_Array &bvia = im->getBind_vertex_input_array(); + size_t k; + for (k = 0; k < bvia.getCount(); k++) + { + if (!strcmp(bvia[k]->getSemantic(), texCoordSetName.c_str()) && !strcmp(bvia[k]->getInput_semantic(), COMMON_PROFILE_INPUT_TEXCOORD)) { - /* - case FX_OPAQUE_ENUM_RGB_ONE: - if ((Transparency * f4[0] > 0.99f) && - (Transparency * f4[1] > 0.99f) && - (Transparency * f4[2] > 0.99f)) - { - SourceBlendFactor = GL_SRC_COLOR; - DestBlendFactor = GL_ONE_MINUS_SRC_COLOR; - // It would be nice to check for a translucent texture here as well - if (!HaveDiffuseTexture && (Diffuse.r() < 0.99f) && (Diffuse.g() < 0.99f) && (Diffuse.b() < 0.99f)) - SwitchOnTheBlender = true; - } - else - { - SourceBlendFactor = GL_CONSTANT_COLOR; - DestBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; - SwitchOnTheBlender = true; - } - break; - case FX_OPAQUE_ALPHA_ZERO: - if (Transparency * f4[3] < 0.01f) - { - SourceBlendFactor = GL_ONE_MINUS_SRC_ALPHA; - DestBlendFactor = GL_SRC_ALPHA; - // It would be nice to check for a translucent texture here as well - if (Diffuse.a() > 0.01f) - SwitchOnTheBlender = true; - } - else - { - SourceBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; - DestBlendFactor = GL_CONSTANT_ALPHA; - SwitchOnTheBlender = true; - } - break; - */ - case FX_OPAQUE_ENUM_RGB_ZERO: - if ((Transparency * f4[0] < 0.01f) && - (Transparency * f4[1] < 0.01f) && - (Transparency * f4[2] < 0.01f)) - { - SourceBlendFactor = GL_ONE_MINUS_SRC_COLOR; - DestBlendFactor = GL_SRC_COLOR; - // It would be nice to check for a translucent texture here as well - // if (!HaveDiffuseTexture && (Diffuse.r() > 0.01f) && (Diffuse.g() > 0.01f) && (Diffuse.b() > 0.01f)) - // SwitchOnTheBlender = true; - } - else - { - SourceBlendFactor = GL_ONE_MINUS_CONSTANT_COLOR; - DestBlendFactor = GL_CONSTANT_COLOR; - SwitchOnTheBlender = true; - } - break; - default: - if (Transparency * f4[3] > 0.99f) - { - SourceBlendFactor = GL_SRC_ALPHA; - DestBlendFactor = GL_ONE_MINUS_SRC_ALPHA; - if (HaveTranslucentDiffuseTexture || (Diffuse.a() < 0.99f)) - SwitchOnTheBlender = true; - } - else - { - SourceBlendFactor = GL_CONSTANT_ALPHA; - DestBlendFactor = GL_ONE_MINUS_CONSTANT_ALPHA; - SwitchOnTheBlender = true; - } - break; + unsigned set = bvia[k]->getInput_set(); + if (set < cachedGeometry->getNumTexCoordArrays()) + { + clonedGeometry->setTexCoordData(tuu, cachedGeometry->getTexCoordData(set)); + } + else + { + osg::notify(osg::WARN) << "Texture coordinate set " << set << " not found." << std::endl; + } + break; } } - if (SwitchOnTheBlender) + if (k == bvia.getCount()) { - if ((SourceBlendFactor == GL_CONSTANT_COLOR) || - (SourceBlendFactor == GL_ONE_MINUS_CONSTANT_COLOR) || - (SourceBlendFactor == GL_CONSTANT_ALPHA) || - (SourceBlendFactor == GL_ONE_MINUS_CONSTANT_ALPHA)) + osg::notify( osg::WARN ) << "Failed to find matching for " << texCoordSetName << std::endl; + if (cachedGeometry->getNumTexCoordArrays()) { - osg::BlendColor *bc = new osg::BlendColor(); - bc->setConstantColor(osg::Vec4( f4[0] * Transparency, f4[1] * Transparency, f4[2] * Transparency, f4[3] * Transparency )); - ss->setAttribute( bc ); + clonedGeometry->setTexCoordData(tuu, cachedGeometry->getTexCoordData(0)); } - osg::BlendFunc *bf = new osg::BlendFunc(SourceBlendFactor, DestBlendFactor); - ss->setAttribute( bf ); - ss->setMode( GL_BLEND, osg::StateAttribute::ON ); - - ss->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); } } } diff --git a/src/osgPlugins/dae/daeRSceneObjects.cpp b/src/osgPlugins/dae/daeRSceneObjects.cpp index 7f9d6820a..47f2fcba5 100644 --- a/src/osgPlugins/dae/daeRSceneObjects.cpp +++ b/src/osgPlugins/dae/daeRSceneObjects.cpp @@ -27,9 +27,9 @@ #include #include -using namespace osgdae; +using namespace osgDAE; -osg::Node* daeReader::processOsgMultiSwitch(domTechnique* teq) +osg::Group* daeReader::processOsgMultiSwitch(domTechnique* teq) { osgSim::MultiSwitch* msw = new osgSim::MultiSwitch; @@ -86,7 +86,7 @@ osg::Node* daeReader::processOsgMultiSwitch(domTechnique* teq) return msw; } -osg::Node* daeReader::processOsgSwitch(domTechnique* teq) +osg::Group* daeReader::processOsgSwitch(domTechnique* teq) { osg::Switch* sw = new osg::Switch; @@ -112,7 +112,7 @@ osg::Node* daeReader::processOsgSwitch(domTechnique* teq) return sw; } -osg::Node* daeReader::processOsgSequence(domTechnique* teq) +osg::Group* daeReader::processOsgSequence(domTechnique* teq) { osg::Sequence* sq = new osg::Sequence; @@ -219,7 +219,7 @@ osg::Node* daeReader::processOsgSequence(domTechnique* teq) } -osg::Node* daeReader::processOsgLOD(domTechnique* teq) +osg::Group* daeReader::processOsgLOD(domTechnique* teq) { osg::LOD* lod = new osg::LOD; @@ -323,7 +323,7 @@ osg::Node* daeReader::processOsgLOD(domTechnique* teq) // 0..* osg::Node* daeReader::processLight( domLight *dlight ) { - if (m_numlights >= 7) + if (_numlights >= 7) { osg::notify( osg::WARN ) << "More than 8 lights may not be supported by OpenGL driver." << std::endl; } @@ -343,12 +343,12 @@ osg::Node* daeReader::processLight( domLight *dlight ) osg::Light* light = new osg::Light(); light->setPosition(osg::Vec4(0,0,0,1)); - light->setLightNum(m_numlights); + light->setLightNum(_numlights); // Enable OpenGL lighting _rootStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); // Enable this OpenGL light - _rootStateSet->setMode(GL_LIGHT0 + m_numlights++, osg::StateAttribute::ON); + _rootStateSet->setMode(GL_LIGHT0 + _numlights++, osg::StateAttribute::ON); // Set ambient of lightmodel to zero // Ambient lights are added as separate lights with only an ambient term diff --git a/src/osgPlugins/dae/daeRSkinning.cpp b/src/osgPlugins/dae/daeRSkinning.cpp new file mode 100644 index 000000000..fe61906f2 --- /dev/null +++ b/src/osgPlugins/dae/daeRSkinning.cpp @@ -0,0 +1,533 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include "daeReader.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace osgDAE; + +domNode* daeReader::getRootJoint(domNode* joint) const +{ + int depth = 0; + while (domNode* parent = daeSafeCast(joint->getParent())) + { + if (isJoint(parent)) + { + joint = parent; + ++depth; + } + else + { + break; + } + } + return joint; +} + +domNode* daeReader::findJointNode(daeElement* searchFrom, domInstance_controller* pDomInstanceController) const +{ + domController *pDomController = daeSafeCast(getElementFromURI(pDomInstanceController->getUrl())); + domSkin::domJoints* pDomSkinJoints = pDomController->getSkin()->getJoints(); + + domInputLocal_Array domInputs = pDomSkinJoints->getInput_array(); + + domSource* pDomJointsSource = NULL; + for (size_t i=0; i < domInputs.getCount(); i++) + { + if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT)) + { + pDomJointsSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (!pDomJointsSource) + { + osg::notify( osg::WARN ) << "Could not find skin joints source '" << domInputs[i]->getSource().getURI() << "'" <getIDREF_array()) + { + if (pDomIDREFs->getCount()) + { + return daeSafeCast< domNode >(getElementFromIDRef(pDomIDREFs->getValue().get(0))); + } + } + else if (domName_array* pDomNames = pDomJointsSource->getName_array()) + { + if (pDomNames->getCount()) + { + daeString target = pDomNames->getValue().get(0); + daeSIDResolver resolver(searchFrom, target); + return daeSafeCast(resolver.getElement()); + } + } + + osg::notify( osg::WARN ) << "No valid names or IDREFS array in " < SkelSkinMap; + SkelSkinMap skelSkinMap; + + //group the skins according to which group of joints they're attached to. + for (size_t i = 0; i < _skinInstanceControllers.size(); ++i) + { + domInstance_controller* pDomInstanceController = _skinInstanceControllers[i]; + + const domInstance_controller::domSkeleton_Array& pDomSkeletons = + pDomInstanceController->getSkeleton_array(); + + if (pDomSkeletons.getCount() == 0) + { + domNode* skelNode = _skeletonMap.begin()->first; + if (skelNode = findSkeletonNode(skelNode, pDomInstanceController)) + { + skelSkinMap[skelNode].push_back(pDomInstanceController); + } + } + else + { + if (daeElement* pDaeElement = pDomSkeletons.get(0)->getValue().getElement()) + { + if (domNode* skelNode = findSkeletonNode(pDaeElement, pDomInstanceController)) + { + skelSkinMap[skelNode].push_back(pDomInstanceController); + } + } + } + } + + for (SkelSkinMap::iterator it = skelSkinMap.begin(); it != skelSkinMap.end(); ++it) + { + processSkeletonSkins(it->first, it->second); + } +} + +void getJointsAndInverseObjectspaceBindMatrices(domInstance_controller* pDomInstanceController, + domNode* pDomSkeletonNode, + std::vector >& jointsAndBindMatrices) +{ + domController* pDomController = daeSafeCast< domController >(getElementFromURI(pDomInstanceController->getUrl())); + + domSkin* pDomSkin = pDomController->getSkin(); + + domSkin::domJoints* pDomSkinJoints = pDomSkin->getJoints(); + domInputLocal_Array domInputs = pDomSkinJoints->getInput_array(); + + if (domInputs.getCount() > 2) + { + osg::notify( osg::WARN ) << "Only a single pair of skin joints inputs is supported." << std::endl; + } + + domSource* pDomJointsSource = NULL; + domSource* pDomInvBindMatricesSource = NULL; + for (size_t i=0; i < domInputs.getCount(); i++) + { + if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT)) + { + pDomJointsSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (!pDomJointsSource) + { + osg::notify( osg::WARN ) << "Could not find skin joints source '" << domInputs[i]->getSource().getURI() << "'" <getSemantic(), COMMON_PROFILE_INPUT_INV_BIND_MATRIX)) + { + pDomInvBindMatricesSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (!pDomInvBindMatricesSource) + { + osg::notify( osg::WARN ) << "Could not find skin inverse bind matrices source '" << domInputs[i]->getSource().getURI() << "'" <getFloat_array(); + domListOfFloats matrices = pDomFloatArray->getValue(); + + osg::Matrix parentInverseSkeletonBindMatrix; + + if (domIDREF_array* pDomIDREFs = pDomJointsSource->getIDREF_array()) + { + // IDREFS refer to an absolute joint and therefore do not allow a different skeleton + xsIDREFS* pIDREFS = &(pDomIDREFs->getValue()); + for (size_t i=0; i < pIDREFS->getCount(); i++) + { + domNode* pDomNode = daeSafeCast< domNode >(getElementFromIDRef(pIDREFS->get(i))); + + if (pDomNode) + { + jointsAndBindMatrices.push_back(std::pair(pDomNode, osg::Matrix())); + } + else + { + osg::notify( osg::WARN ) << "Failed to locate joint '" << pIDREFS->get(i).getID() << "'" << std::endl; + } + } + } + else if (domName_array* pDomNames = pDomJointsSource->getName_array()) + { + // Using a list of names is the preferred way of referring to joints, because + // this refers to a joint relative to the given skeletons + domListOfNames* pNames = &(pDomNames->getValue()); + for (size_t i=0; i < pNames->getCount(); i++) + { + daeSIDResolver resolver(pDomSkeletonNode, pNames->get(i)); + domNode* pDomNode = daeSafeCast< domNode >(resolver.getElement()); + + if (pDomNode) + { + jointsAndBindMatrices.push_back(std::pair(pDomNode, osg::Matrix())); + } + else + { + osg::notify( osg::WARN ) << "Failed to locate joint '" << pNames->get(i) << "'" << std::endl; + } + } + } + else + { + osg::notify( osg::WARN ) << "No valid names or IDREFS array in " < > jointsAndInverseBindMatrices; + getJointsAndInverseObjectspaceBindMatrices(instanceController, skeletonRoot, jointsAndInverseBindMatrices); + + for (size_t j = 0; j < jointsAndInverseBindMatrices.size(); ++j) + { + osgAnimation::Bone* pOsgBone = getOrCreateBone(jointsAndInverseBindMatrices[j].first); + pOsgBone->setInvBindMatrixInSkeletonSpace(jointsAndInverseBindMatrices[j].second); + } + } + + osgAnimation::Skeleton* skeleton = getOrCreateSkeleton(skeletonRoot); + + for (size_t i = 0; i < instanceControllers.size(); ++i) + { + domInstance_controller *pDomInstanceController = instanceControllers[i]; + domController *pDomController = daeSafeCast< domController >(getElementFromURI(pDomInstanceController->getUrl())); + processSkin(pDomController->getSkin(), skeletonRoot, skeleton, pDomInstanceController->getBind_material()); + } +} + +osgAnimation::VertexInfluence& getVertexInfluence( + osgAnimation::VertexInfluenceMap& vim, const std::string& name) +{ + osgAnimation::VertexInfluenceMap::iterator it = vim.lower_bound(name); + if (it == vim.end() || name != it->first) + { + it = vim.insert(it, osgAnimation::VertexInfluenceMap::value_type( + name, osgAnimation::VertexInfluence())); + it->second.setName(name); + } + return it->second; +} + +// +// 0..1 +// 3..* +// 1 +// 2..* +// 0..* +// 1 +// 2..* +// 0..1 +// 0..1 +// 0.* +// 0..* +void daeReader::processSkin(domSkin* pDomSkin, domNode* skeletonRoot, osgAnimation::Skeleton* pOsgSkeleton, domBind_material* pDomBindMaterial) +{ + daeElement* pDaeSkinSource = getElementFromURI( pDomSkin->getSource()); + + if (!pDaeSkinSource) + { + osg::notify( osg::WARN ) << "Failed to locate geometry " << pDomSkin->getSource().getURI() << std::endl; + return; + } + + domGeometry* pDomGeometry = daeSafeCast< domGeometry >(pDaeSkinSource); + + if (!pDomGeometry) + { + osg::notify( osg::WARN ) << "Skin source is of type " << pDaeSkinSource->getTypeName() << " which is not supported." << std::endl; + return; + } + + // Base mesh + const osg::Geode* pOriginalGeode = NULL; + osg::Geode* pOsgGeode = getOrCreateGeometry(pDomGeometry, pDomBindMaterial, &pOriginalGeode); + if (!pOsgGeode) + return; + + domMesh* pDomMesh = pDomGeometry->getMesh(); + + osg::Geode* pOsgRigGeode = new osg::Geode; + pOsgRigGeode->setDataVariance(osg::Object::DYNAMIC); + + typedef std::map GeometryRigGeometryMap; + GeometryRigGeometryMap old2newGeometryMap; + + for (unsigned i = 0; i < pOsgGeode->getNumDrawables(); ++i) + { + if (osg::Geometry* pOsgGeometry = dynamic_cast(pOsgGeode->getDrawable(i))) + { + const osg::Geometry* pOriginalGeometry = dynamic_cast(pOriginalGeode->getDrawable(i)); + + osgAnimation::RigGeometry* pOsgRigGeometry = new osgAnimation::RigGeometry(); + pOsgRigGeometry->setSourceGeometry(pOsgGeometry); + pOsgRigGeometry->copyFrom(*pOsgGeometry); + old2newGeometryMap.insert(GeometryRigGeometryMap::value_type(pOriginalGeometry, pOsgRigGeometry)); + pOsgRigGeometry->setDataVariance(osg::Object::DYNAMIC); + pOsgRigGeometry->setUseDisplayList( false ); + pOsgRigGeode->addDrawable(pOsgRigGeometry); + } + else + { + pOsgRigGeode->addDrawable(pOsgGeode->getDrawable(i)); + } + } + + pOsgSkeleton->addChild(pOsgRigGeode); + + // + if (domSkin::domBind_shape_matrix* pDomBindShapeMatrix = pDomSkin->getBind_shape_matrix()) + { + domFloat4x4 matrix = pDomBindShapeMatrix->getValue(); + osg::Matrix bindMatrix( + matrix.get(0), matrix.get(4), matrix.get(8), matrix.get(12), + matrix.get(1), matrix.get(5), matrix.get(9), matrix.get(13), + matrix.get(2), matrix.get(6), matrix.get(10), matrix.get(14), + matrix.get(3), matrix.get(7), matrix.get(11), matrix.get(15)); + + for (unsigned d = 0; d < pOsgRigGeode->getNumDrawables(); ++d) + { + osgAnimation::RigGeometry* pOsgRigGeometry = dynamic_cast(pOsgRigGeode->getDrawable(d)); + if (!pOsgRigGeometry) + continue; + + osg::Vec3Array& vertices = *static_cast(pOsgRigGeometry->getVertexArray()); + + for (size_t i = 0; i < vertices.size(); ++i) + { + vertices[i] = vertices[i] * bindMatrix; + } + + if (osg::Vec3Array* normals = static_cast(pOsgRigGeometry->getNormalArray())) + { + for (size_t i = 0; i < normals->size(); ++i) + { + (*normals)[i] = osg::Matrix::transform3x3((*normals)[i], bindMatrix); + } + } + + } + } + + // 1 + + domSkin::domVertex_weights* pDomVertexWeights = pDomSkin->getVertex_weights(); + domInputLocalOffset_Array domInputs = pDomVertexWeights->getInput_array(); + + if (domInputs.getCount() > 2) + { + osg::notify( osg::WARN ) << "Only a single pair of skin vertex weights inputs is supported." << std::endl; + } + + domSource* pDomJointsSource = NULL; + domSource* pDomWeightsSource = NULL; + for (size_t i=0; i < 2; i++) + { + if (!strcmp(domInputs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT)) + { + pDomJointsSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (!pDomJointsSource) + { + osg::notify( osg::WARN ) << "Could not find skin joints source '" << domInputs[i]->getSource().getURI() << "'" <getSemantic(), COMMON_PROFILE_INPUT_WEIGHT)) + { + pDomWeightsSource = daeSafeCast(getElementFromURI(domInputs[i]->getSource())); + if (!pDomWeightsSource) + { + osg::notify( osg::WARN ) << "Could not find skin weights source '" << domInputs[i]->getSource().getURI() << "'" <getFloat_array(); + domListOfFloats weights = pDomFloatArray->getValue(); + + domSkin::domVertex_weights::domVcount* pDomVcount = pDomVertexWeights->getVcount(); + domListOfUInts influenceCounts = pDomVcount->getValue(); + + domSkin::domVertex_weights::domV* pDomV= pDomVertexWeights->getV(); + domListOfInts jointWeightIndices = pDomV->getValue(); + + std::vector jointNames; + + if (domName_array* pDomNames = pDomJointsSource->getName_array()) + { + domListOfNames* pNames = &(pDomNames->getValue()); + + jointNames.reserve(pNames->getCount()); + + for (size_t i = 0; i < pNames->getCount(); ++i) + { + const char* szName = pNames->get(i); + daeSIDResolver resolver(skeletonRoot, szName); + osgAnimation::Bone* pOsgBone = _jointMap[daeSafeCast(resolver.getElement())].get(); + if (pOsgBone) + { + jointNames.push_back(pOsgBone->getName()); + } + else + { + jointNames.push_back(szName); + osg::notify(osg::WARN) << "Cannot find bone " << szName << std::endl; + } + } + } + else if (domIDREF_array* pDomIDREFs = pDomJointsSource->getIDREF_array()) + { + xsIDREFS* pIDREFs = &(pDomIDREFs->getValue()); + + jointNames.reserve(pIDREFs->getCount()); + + for (size_t i = 0; i < pIDREFs->getCount(); ++i) + { + osgAnimation::Bone* pOsgBone = _jointMap[daeSafeCast(pIDREFs->get(i).getElement())].get(); + if (pOsgBone) + { + jointNames.push_back(pOsgBone->getName()); + } + else + { + jointNames.push_back(pIDREFs->get(i).getID()); + osg::notify(osg::WARN) << "Cannot find bone " << pIDREFs->get(i).getID() << std::endl; + } + } + } + else + { + osg::notify( osg::WARN ) << "No valid names or IDREFS array in " < jointWeightIndices.getCount()) + { + osg::notify( osg::WARN ) << "vIndex is larger than number of v values" <= jointNames.size()) + { + osg::notify( osg::WARN ) << "Joint index is larger the number of joints" <= weights.getCount()) + { + osg::notify( osg::WARN ) << "Weight index is larger the number of weights" < 0.0f) + { + const std::string& name = jointNames[jointIndex]; + + for (OldToNewIndexMap::const_iterator it = start; it != end; ++it) + { + osgAnimation::RigGeometry* pRigGeometry = old2newGeometryMap[it->second.first.get()]; + + osgAnimation::VertexInfluenceMap* vim = pRigGeometry->getInfluenceMap(); + if (!vim) + { + pRigGeometry->setInfluenceMap(vim = new osgAnimation::VertexInfluenceMap); + } + + getVertexInfluence(*vim, name).push_back( + osgAnimation::VertexIndexWeight(it->second.second, weight)); + } + } + } + } +} diff --git a/src/osgPlugins/dae/daeRTransforms.cpp b/src/osgPlugins/dae/daeRTransforms.cpp index 6a03ab1e3..39a448ffe 100644 --- a/src/osgPlugins/dae/daeRTransforms.cpp +++ b/src/osgPlugins/dae/daeRTransforms.cpp @@ -1,14 +1,14 @@ /* * Copyright 2006 Sony Computer Entertainment Inc. * - * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * http://research.scea.com/scea_shared_source_license.html * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - * implied. See the License for the specific language governing permissions and limitations under the - * License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. */ #include "daeReader.h" @@ -16,131 +16,115 @@ #include #include +#include +#include +#include +#include +#include #include #include -using namespace osgdae; +using namespace osgDAE; // Note , , , , and may appear in any order // These transformations can be combined in any number and ordering to produce the desired -// coordinate systemfor the parent element. The COLLADA specificatin requires that the -// transformation elements are processed in order and accumulate the result as if they were +// coordinate system for the parent element. The COLLADA specificatin requires that the +// transformation elements are processed in order and accumulate the result as if they were // converted to column-order matrices and concatenated using matrix post-multiplication. -osg::Node* daeReader::processOsgMatrixTransform( domNode *node ) +osg::Transform* daeReader::processOsgMatrixTransform(domNode *node, bool isBone) { - osg::MatrixTransform* matNode = new osg::MatrixTransform; - osg::Matrix matrix; + osg::MatrixTransform* resultNode = NULL; + + if (isBone) + { + resultNode = getOrCreateBone(node); + } + else + { + resultNode = new osg::MatrixTransform; + } + + osg::NodeCallback* pNodeCallback = resultNode->getUpdateCallback(); + std::vector > transformElements; + osg::ref_ptr pLastStaticTransformElement; // Process all coordinate system contributing elements in order! size_t count = node->getContents().getCount(); - for (size_t i = 0; i < count; i++ ) + for (size_t i = 0; i < count; i++ ) { - domRotate * rot = daeSafeCast< domRotate >( node->getContents()[i] ); - if (rot) + daeElement* pDaeElement = node->getContents()[i]; + osg::ref_ptr pTransformElement = NULL; + + if (domRotate * pDomRotate = daeSafeCast< domRotate >( pDaeElement )) { - domFloat4& r = rot->getValue(); - if (r.getCount() != 4 ) + const domFloat4& r = pDomRotate->getValue(); + if (r.getCount() != 4 ) { - osg::notify(osg::WARN)<<"Data is wrong size for rotate"<getSid() ? pDomRotate->getSid() : "", osg::Vec3(r[0], r[1], r[2]), osg::DegreesToRadians(r[3])); } - - domTranslate * trans = daeSafeCast< domTranslate >( node->getContents()[i] ); - if (trans != NULL) + else if (domTranslate * pDomTranslate = daeSafeCast< domTranslate >( pDaeElement )) { - domFloat3& t = trans->getValue(); - if (t.getCount() != 3 ) + const domFloat3& t = pDomTranslate->getValue(); + if (t.getCount() != 3 ) { osg::notify(osg::WARN)<<"Data is wrong size for translate"<getSid() ? pDomTranslate->getSid() : "", osg::Vec3(t[0], t[1], t[2])); } - - domScale * scale = daeSafeCast< domScale >( node->getContents()[i] ); - if (scale != NULL) + else if (domScale * pDomScale = daeSafeCast< domScale >( pDaeElement )) { - domFloat3& s = scale->getValue(); - if (s.getCount() != 3 ) + const domFloat3& s = pDomScale->getValue(); + if (s.getCount() != 3 ) { osg::notify(osg::WARN)<<"Data is wrong size for scale"<getSid() ? pDomScale->getSid() : "", osg::Vec3(s[0], s[1], s[2])); } - - domMatrix * mat = daeSafeCast< domMatrix >( node->getContents()[i] ); - if (mat != NULL) + else if (domMatrix * pDomMatrix = daeSafeCast< domMatrix >( pDaeElement )) { - if (mat->getValue().getCount() != 16 ) + if (pDomMatrix->getValue().getCount() != 16 ) { osg::notify(osg::WARN)<<"Data is wrong size for matrix"<getValue()[0], mat->getValue()[4], mat->getValue()[8], mat->getValue()[12], - mat->getValue()[1], mat->getValue()[5], mat->getValue()[9], mat->getValue()[13], - mat->getValue()[2], mat->getValue()[6], mat->getValue()[10], mat->getValue()[14], - mat->getValue()[3], mat->getValue()[7], mat->getValue()[11], mat->getValue()[15] ); - - matrix = mMat * matrix; - continue; + pTransformElement = new osgAnimation::StackedMatrixElement(pDomMatrix->getSid() ? pDomMatrix->getSid() : "", + osg::Matrix( pDomMatrix->getValue()[0], pDomMatrix->getValue()[4], pDomMatrix->getValue()[8], pDomMatrix->getValue()[12], + pDomMatrix->getValue()[1], pDomMatrix->getValue()[5], pDomMatrix->getValue()[9], pDomMatrix->getValue()[13], + pDomMatrix->getValue()[2], pDomMatrix->getValue()[6], pDomMatrix->getValue()[10], pDomMatrix->getValue()[14], + pDomMatrix->getValue()[3], pDomMatrix->getValue()[7], pDomMatrix->getValue()[11], pDomMatrix->getValue()[15])); } - - domLookat * la = daeSafeCast< domLookat >( node->getContents()[i] ); - if (la != NULL) + else if (domLookat * pDomLookat = daeSafeCast< domLookat >( pDaeElement )) { - if (la->getValue().getCount() != 9 ) + if (pDomLookat->getValue().getCount() != 9 ) { osg::notify(osg::WARN)<<"Data is wrong size for lookat"<getValue()[0], la->getValue()[1], la->getValue()[2]); - osg::Vec3 center(la->getValue()[3], la->getValue()[4], la->getValue()[5] ); - osg::Vec3 up( la->getValue()[6], la->getValue()[7], la->getValue()[8] ); - lookatMat.makeLookAt( eye, center, up ); - - matrix = lookatMat * matrix; - continue; + pTransformElement = new osgAnimation::StackedMatrixElement(pDomLookat->getSid() ? pDomLookat->getSid() : "", + osg::Matrix::lookAt( + osg::Vec3(pDomLookat->getValue()[0], pDomLookat->getValue()[1], pDomLookat->getValue()[2]), + osg::Vec3(pDomLookat->getValue()[3], pDomLookat->getValue()[4], pDomLookat->getValue()[5]), + osg::Vec3(pDomLookat->getValue()[6], pDomLookat->getValue()[7], pDomLookat->getValue()[8]))); } - - domSkew * skew = daeSafeCast< domSkew >( node->getContents()[i] ); - if (skew != NULL) + else if (domSkew * pDomSkew = daeSafeCast< domSkew >( pDaeElement )) { - if (skew->getValue().getCount() != 7 ) + if (pDomSkew->getValue().getCount() != 7 ) { osg::notify(osg::WARN)<<"Data is wrong size for skew"<getValue(); + const domFloat7& s = pDomSkew->getValue(); float shear = sin(osg::DegreesToRadians(s[0])); // axis of rotation @@ -148,58 +132,104 @@ osg::Node* daeReader::processOsgMatrixTransform( domNode *node ) // axis of translation osg::Vec3f along(s[4],s[5],s[6]); - along.normalize(); - osg::Vec3f a = around - (along * (around * along)); - a.normalize(); - - float an1 = around * a; - float an2 = around * along; - - float rx = an1 * cos(shear) - an2 * sin(shear); - float ry = an1 * sin(shear) + an2 * cos(shear); + //This maths is untested so may be transposed or negated or just completely wrong. + osg::Vec3f normal = along ^ around; + normal.normalize(); + around.normalize(); + along *= shear / along.length(); - if (rx <= 0.0) + pTransformElement = new osgAnimation::StackedMatrixElement(pDomLookat->getSid() ? pDomLookat->getSid() : "", + osg::Matrix( + normal.x() * along.x() + 1.0f, normal.x() * along.y(), normal.x() * along.z(), 0.0f, + normal.y() * along.x(), normal.y() * along.y() + 1.0f, normal.y() * along.z(), 0.0f, + normal.z() * along.x(), normal.z() * along.y(), normal.z() * along.z() + 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f)); + } + + if (pTransformElement) + { + daeElementDomChannelMap::iterator iter = _daeElementDomChannelMap.find(pDaeElement); + if (iter != _daeElementDomChannelMap.end()) { - osg::notify(osg::WARN)<<"skew angle too large"<getId() ? node->getId() : node->getSid() ? node->getSid() : ""; + resultNode->setDataVariance(osg::Object::DYNAMIC); + + pNodeCallback = new osgAnimation::UpdateMatrixTransform(name); + resultNode->setUpdateCallback(pNodeCallback); + } + + do + { + _domChannelOsgAnimationUpdateCallbackMap[iter->second] = pNodeCallback; + ++iter; + } while (iter != _daeElementDomChannelMap.end() && iter->first == pDaeElement); } - - float alpha; - // A parallel to B?? - if (an1==0) + else if (pLastStaticTransformElement) { - alpha=0; - } - else - { - alpha=ry/rx-an2/an1; + // Add transform element only if not identity + if (!pTransformElement->isIdentity()) + { + // Collapse static transform elements + osg::Matrix matrix = pLastStaticTransformElement->getAsMatrix(); + pTransformElement->applyToMatrix(matrix); + pLastStaticTransformElement = new osgAnimation::StackedMatrixElement("collapsed", matrix); + } + } + else if (!pTransformElement->isIdentity()) + { + // Store single static transform element only if not identity + pLastStaticTransformElement = pTransformElement; } - - - osg::Matrix skewMat(a.x()*along.x()*alpha+1.0, a.x()*along.y()*alpha, a.x()*along.z()*alpha, 0, - a.y()*along.x()*alpha, a.y()*along.y()*alpha+1.0, a.y()*along.z()*alpha, 0, - a.z()*along.x()*alpha, a.z()*along.y()*alpha, a.z()*along.z()*alpha+1.0, 0, - 0, 0, 0, 1); - - - matrix = skewMat * matrix; - continue; } } - matNode->setMatrix(matrix); + // Add final collapsed element (if any) + if (pLastStaticTransformElement) + { + transformElements.push_back(pLastStaticTransformElement); + } + + // Build a matrix for the MatrixTransform and add the elements to the updateCallback + osg::Matrix matrix; + + osgAnimation::UpdateMatrixTransform* pUpdateStackedTransform = + dynamic_cast(pNodeCallback); + + for (size_t i=0; i < transformElements.size(); i++) + { + transformElements[i]->applyToMatrix(matrix); + if (pUpdateStackedTransform) + { + pUpdateStackedTransform->getStackedTransforms().push_back(transformElements[i].get()); + } + } + + resultNode->setMatrix(matrix); osg::Vec3 scale = matrix.getScale(); if ((scale.x() != 1) || (scale.y() != 1) || (scale.z() != 1)) { - osg::StateSet* ss = matNode->getOrCreateStateSet(); + osg::StateSet* ss = resultNode->getOrCreateStateSet(); ss->setMode(GL_RESCALE_NORMAL, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE); } - return matNode; + return resultNode; } -osg::Node* daeReader::processOsgDOFTransform(domTechnique* teq) +osg::Group* daeReader::processOsgDOFTransform(domTechnique* teq) { osgSim::DOFTransform* dof = new osgSim::DOFTransform; diff --git a/src/osgPlugins/dae/daeReader.cpp b/src/osgPlugins/dae/daeReader.cpp index 34d6cc935..d40ddf361 100644 --- a/src/osgPlugins/dae/daeReader.cpp +++ b/src/osgPlugins/dae/daeReader.cpp @@ -18,20 +18,23 @@ #include #include #include +#include -using namespace osgdae; +using namespace osgDAE; -daeReader::daeReader(DAE *dae_, bool strictTransparency) : - m_AssetUnitName("meter"), - m_AssetUnitMeter(1.0), - m_AssetUp_axis(UPAXISTYPE_Y_UP), - dae(dae_), - rootNode(NULL), - m_numlights(0), - currentInstance_effect(NULL), - currentEffect(NULL), - m_AuthoringTool(UNKNOWN), - m_StrictTransparency(strictTransparency) +daeReader::daeReader(DAE *dae_, bool strictTransparency) : + _dae(dae_), + _rootNode(NULL), + _visualScene(NULL), + _numlights(0), + _currentInstance_effect(NULL), + _currentEffect(NULL), + _authoringTool(UNKNOWN), + _strictTransparency(strictTransparency), + _invertTransparency(false), + _assetUnitName("meter"), + _assetUnitMeter(1.0), + _assetUp_axis(UPAXISTYPE_Y_UP) { } @@ -41,69 +44,97 @@ daeReader::~daeReader() bool daeReader::convert( const std::string &fileURI ) { + // Clear caches + _geometryMap.clear(); + _materialMap.clear(); + _materialMap2.clear(); + daeElement *colladaElement; - domInstance_rigid_body *irb; + daeInt count, result; - daeInt res = dae->load( fileURI.c_str() ); + _document = _dae->open(fileURI); - if( res != DAE_OK && res != DAE_ERR_COLLECTION_ALREADY_EXISTS) + if (!_document) { osg::notify( osg::WARN ) << "Load failed in COLLADA DOM" << std::endl; return false; } osg::notify( osg::INFO ) << "URI loaded: " << fileURI << std::endl; - domCOLLADA* document = dae->getDom( fileURI.c_str() ); - - if ( !document->getScene() || !document->getScene()->getInstance_visual_scene() ) + if ( !_document->getScene() || !_document->getScene()->getInstance_visual_scene() ) { osg::notify( osg::WARN ) << "No scene found!" << std::endl; return false; } - if (document->getAsset()) + if (_document->getAsset()) { - const domAsset::domContributor_Array& ContributorArray = document->getAsset()->getContributor_array(); + const domAsset::domContributor_Array& ContributorArray = _document->getAsset()->getContributor_array(); size_t NumberOfContributors = ContributorArray.getCount(); size_t CurrentContributor; for (CurrentContributor = 0; CurrentContributor < NumberOfContributors; CurrentContributor++) { if (ContributorArray[CurrentContributor]->getAuthoring_tool()) { + const char szBlender[] = "Blender"; + const char szDazStudio[] = "DAZ|Studio"; + const char szSketchup[] = "Google SketchUp"; + const char szFbx[] = "FBX"; + const char szMaya[] = "Maya"; + xsString Tool = ContributorArray[CurrentContributor]->getAuthoring_tool()->getValue(); - if (strncmp(Tool, "Google SketchUp", 15) == 0) - m_AuthoringTool = GOOGLE_SKETCHUP; + + if (strncmp(Tool, szBlender, strlen(szBlender)) == 0) + _authoringTool = BLENDER; + else if (strncmp(Tool, szDazStudio, strlen(szDazStudio)) == 0) + _authoringTool = DAZ_STUDIO; + else if (strncmp(Tool, szFbx, strlen(szFbx)) == 0) + _authoringTool = FBX_CONVERTER; + else if (strncmp(Tool, szSketchup, strlen(szSketchup)) == 0) + _authoringTool = GOOGLE_SKETCHUP; + else if (strncmp(Tool, szMaya, strlen(szMaya)) == 0) + _authoringTool = MAYA; } } - if (document->getAsset()->getUnit()) + if (_document->getAsset()->getUnit()) { - if (NULL != document->getAsset()->getUnit()->getName()) - m_AssetUnitName = std::string(document->getAsset()->getUnit()->getName()); - if (0 != document->getAsset()->getUnit()->getMeter()) - m_AssetUnitMeter = document->getAsset()->getUnit()->getMeter(); + if (NULL != _document->getAsset()->getUnit()->getName()) + _assetUnitName = std::string(_document->getAsset()->getUnit()->getName()); + if (0 != _document->getAsset()->getUnit()->getMeter()) + _assetUnitMeter = _document->getAsset()->getUnit()->getMeter(); } - if (document->getAsset()->getUp_axis()) - m_AssetUp_axis = document->getAsset()->getUp_axis()->getValue(); + if (_document->getAsset()->getUp_axis()) + _assetUp_axis = _document->getAsset()->getUp_axis()->getValue(); } - if (dae->getDatabase()) + domInstanceWithExtra *ivs = _document->getScene()->getInstance_visual_scene(); + _visualScene = daeSafeCast< domVisual_scene >( getElementFromURI( ivs->getUrl() ) ); + if ( _visualScene == NULL ) { - count = dae->getDatabase()->getElementCount(NULL, COLLADA_TYPE_INSTANCE_RIGID_BODY, NULL); + osg::notify( osg::WARN ) << "Unable to locate visual scene!" << std::endl; + return false; + } + + if (daeDatabase* database = _dae->getDatabase()) + { + _invertTransparency = findInvertTransparency(database); // build a std::map for lookup if Group or PositionAttitudeTransform should be created, // i.e, make it easy to check if a instance_rigid_body targets a visual node + domInstance_rigid_body *pDomInstanceRigidBody; + count = database->getElementCount(NULL, COLLADA_TYPE_INSTANCE_RIGID_BODY, NULL); for (int i=0; igetDatabase()->getElement(&colladaElement, i, NULL, COLLADA_TYPE_INSTANCE_RIGID_BODY); + result = database->getElement(&colladaElement, i, NULL, COLLADA_TYPE_INSTANCE_RIGID_BODY); if (result == DAE_OK) { - irb = daeSafeCast(colladaElement); - if (irb) + pDomInstanceRigidBody = daeSafeCast(colladaElement); + if (pDomInstanceRigidBody) { - domNode *node = daeSafeCast(irb->getTarget().getElement()); + domNode *node = daeSafeCast(pDomInstanceRigidBody->getTarget().getElement()); if (node && node->getId()) { _targetMap[ std::string(node->getId()) ] = true; @@ -111,23 +142,176 @@ bool daeReader::convert( const std::string &fileURI ) } } } + + // Build a map of elements that are targetted by animations + count = database->getElementCount(NULL, COLLADA_TYPE_CHANNEL, NULL); + for (int i=0; igetElement(&colladaElement, i, NULL, COLLADA_TYPE_CHANNEL); + + if (result == DAE_OK) + { + domChannel* pDomChannel = daeSafeCast(colladaElement); + if (pDomChannel) + { + std::string target = pDomChannel->getTarget(); + size_t openparenthesis = target.find_first_of('('); + if (openparenthesis != std::string::npos) target.erase(openparenthesis); + daeSIDResolver resolver(pDomChannel, target.c_str()); + daeElement *pDaeElement = resolver.getElement(); + if (pDaeElement) + { + _daeElementDomChannelMap.insert(daeElementDomChannelMap::value_type(pDaeElement, pDomChannel)); + } + else + { + osg::notify( osg::WARN ) << "Could not locate target " << pDomChannel->getTarget()<< std::endl; + } + } + } + } + + // Find all nodes that are used as bones. Note that while many files + // identify nodes with type="JOINT", some don't do this, while others + // identify every node as a joint, making it meaningless. + std::vector instanceControllers; + database->typeLookup(instanceControllers); + for (size_t i = 0; i < instanceControllers.size(); ++i) + { + domInstance_controller* pInstanceController = instanceControllers[i]; + + domController *pDomController = daeSafeCast(getElementFromURI(pInstanceController->getUrl())); + if (!pDomController) + { + osg::notify( osg::WARN ) << "Failed to locate controller " << pInstanceController->getUrl().getURI() << std::endl; + continue; + } + + const domInstance_controller::domSkeleton_Array& domSkeletonURIs = pInstanceController->getSkeleton_array(); + std::vector searchIn; + + for (size_t i = 0; i < domSkeletonURIs.getCount(); ++i) + { + if (daeElement* el = getElementFromURI(domSkeletonURIs[i]->getValue())) + { + searchIn.push_back(el); + if (domNode* pJoint = daeSafeCast(el)) + { + _jointSet.insert(pJoint); + } + } + } + + if (searchIn.empty()) + { + searchIn.push_back(_visualScene); + } + + const domSkin* pSkin = pDomController->getSkin(); + if (!pSkin) continue; + const domSkin::domJoints* pJoints = pSkin->getJoints(); + if (!pJoints) continue; + const domInputLocal_Array& inputURIs = pJoints->getInput_array(); + + domSource* pDomJointsSource = NULL; + for (size_t i=0; i < inputURIs.getCount(); i++) + { + if (!strcmp(inputURIs[i]->getSemantic(), COMMON_PROFILE_INPUT_JOINT)) + { + pDomJointsSource = daeSafeCast(getElementFromURI(inputURIs[i]->getSource())); + if (!pDomJointsSource) + { + osg::notify( osg::WARN ) << "Could not find skin joints source '" << inputURIs[i]->getSource().getURI() << "'" <getIDREF_array()) + { + for (size_t i = 0; i < pDomIDREFs->getCount(); ++i) + { + if (domNode* pJoint = daeSafeCast(getElementFromIDRef(pDomIDREFs->getValue().get(i)))) + { + _jointSet.insert(pJoint); + } + } + } + else if (domName_array* pDomNames = pDomJointsSource->getName_array()) + { + for (size_t i = 0; i < pDomNames->getCount(); ++i) + { + daeString target = pDomNames->getValue().get(i); + for (size_t j = 0; j < searchIn.size(); ++j) + { + daeSIDResolver resolver(searchIn[j], target); + if (domNode* pJoint = daeSafeCast(resolver.getElement())) + { + _jointSet.insert(pJoint); + } + } + } + } + } } - domInstanceWithExtra *ivs = document->getScene()->getInstance_visual_scene(); - domVisual_scene *vs = daeSafeCast< domVisual_scene >( getElementFromURI( ivs->getUrl() ) ); - if ( vs == NULL ) + // Build the actual scene graph based on the visual scene + _rootNode = processVisualScene( _visualScene ); + + osgAnimation::BasicAnimationManager* pOsgAnimationManager = processAnimationLibraries(_document); + if (pOsgAnimationManager) { - osg::notify( osg::WARN ) << "Unable to locate visual scene!" << std::endl; - return false; + _rootNode->addUpdateCallback(pOsgAnimationManager); } - rootNode = processVisualScene( vs ); - + return true; } -osg::Node* daeReader::processVisualScene( domVisual_scene *scene ) +void daeReader::addChild(osg::Group* group, osg::Node* node) { - osg::Node *retVal; + if (dynamic_cast(node)) + { + unsigned index = 0; + while (index < group->getNumChildren() && + dynamic_cast(group->getChild(index))) + { + ++index; + } + group->insertChild(index, node); + } + else + { + group->addChild(node); + } +} + +osg::Group* daeReader::turnZUp() +{ + osg::PositionAttitudeTransform* pat = NULL; + + // If not Z axis up we need to rotate scene to bring the Z axis up + if (_assetUp_axis != UPAXISTYPE_Z_UP) + { + pat = new osg::PositionAttitudeTransform(); + if (_assetUp_axis == UPAXISTYPE_Y_UP) + { + pat->setAttitude(osg::Quat(osg::inDegrees(90.0f), osg::Vec3(1.0f,0.0f,0.0f))); + } + else //(m_AssetUp_axis == UPAXISTYPE_X_UP) + { + pat->setAttitude(osg::Quat(osg::inDegrees(90.0f), osg::Vec3(0.0f,1.0f,0.0f))); + } + } + + _assetUp_axis = UPAXISTYPE_Z_UP; + return pat; +} + +osg::Group* daeReader::processVisualScene( domVisual_scene *scene ) +{ + osg::Group *retVal; _rootStateSet = new osg::StateSet(); unsigned int nbVisualSceneGroup=scene->getNode_array().getCount(); @@ -137,36 +321,48 @@ osg::Node* daeReader::processVisualScene( domVisual_scene *scene ) retVal = new osg::Group(); retVal->setName("Empty Collada scene"); } - else if (nbVisualSceneGroup==1) - { - osg::Node *node = processNode( scene->getNode_array()[0] ); - if ( node != NULL ) - retVal = node; - else - { - retVal = new osg::Group(); - retVal->setName("Empty Collada scene (import failure)"); - } - } else - { - retVal = new osg::Group(); - retVal->setName("Collada visual scene group"); - for ( size_t i = 0; i < scene->getNode_array().getCount(); i++ ) - { - osg::Node *node = processNode( scene->getNode_array()[i] ); - if ( node != NULL ) - { - retVal->asGroup()->addChild( node ); - } - } + { + retVal = turnZUp(); + + if (!retVal) + { + retVal = new osg::Group; + } + + _skinInstanceControllers.clear(); + + const domNode_Array& node_array = scene->getNode_array(); + for (size_t i = 0; i < node_array.getCount(); i++) + { + if (osg::Node* node = processNode(node_array[i], false)) + { + addChild(retVal, node); + } + } + + processSkins(); + + if (retVal->getName().empty()) + { + if (retVal->getNumChildren()) + { + retVal->setName("Collada visual scene group"); + } + else + { + retVal->setName("Empty Collada scene (import failure)"); + } + } } retVal->setStateSet(_rootStateSet.get()); return retVal; } -osg::Node* daeReader::processExtras(domNode *node) + + +osg::Group* daeReader::processExtras(domNode *node) { // See if one of the extras contains OpenSceneGraph specific information unsigned int numExtras = node->getExtra_array().getCount(); @@ -303,7 +499,7 @@ domTechnique* daeReader::getOpenSceneGraphProfile(domExtra* extra) // 0..* // 0..* // 0..* -osg::Node* daeReader::processNode( domNode *node ) +osg::Node* daeReader::processNode( domNode *node, bool skeleton) { // First we need to determine what kind of OSG node we need // If there exist any of the , , , , , elements @@ -315,17 +511,26 @@ osg::Node* daeReader::processNode( domNode *node ) node->getMatrix_array().getCount() + node->getSkew_array().getCount(); - // See if it is targeted + // See if it is targeted by an animation bool targeted = false; if (node->getId()) { targeted = _targetMap[std::string(node->getId())]; } - osg::Node *resultNode; - if (coordcount > 0 || targeted ) + + osg::Group *resultNode = NULL; + + bool isBone = skeleton || isJoint(node); + + if (coordcount > 0 || targeted || isBone) { - resultNode = processOsgMatrixTransform(node); + // TODO + // single matrix -> MatrixTransform + // scale, euler, translate -> PositionAttitudeTransform + // if targeted -> StackedTransform + // otherwise a flattened -> MatrixTransform + resultNode = processOsgMatrixTransform(node, isBone); } else { @@ -336,73 +541,89 @@ osg::Node* daeReader::processNode( domNode *node ) // See if there is generic node info attached as extra processNodeExtra(resultNode, node); - resultNode->setName( node->getId() ? node->getId() : "" ); - - osg::Group* groupNode = resultNode->asGroup(); - - if (groupNode) + if (resultNode->getName().empty()) { - // 0..* - domInstance_camera_Array cameraInstanceArray = node->getInstance_camera_array(); - for ( size_t i = 0; i < cameraInstanceArray.getCount(); i++ ) - { - daeElement *el = getElementFromURI( cameraInstanceArray[i]->getUrl()); - domCamera *c = daeSafeCast< domCamera >( el ); + resultNode->setName( node->getId() ? node->getId() : "" ); + } - if (c) - groupNode->addChild( processCamera( c )); - else - osg::notify( osg::WARN ) << "Failed to locate camera " << cameraInstanceArray[i]->getUrl().getURI() << std::endl; + osg::Group* attachTo = resultNode; + + if (!skeleton && isJoint(node)) + { + skeleton = true; + osgAnimation::Skeleton* pOsgSkeleton = getOrCreateSkeleton(node); + pOsgSkeleton->addChild(resultNode); + attachTo = resultNode; + resultNode = pOsgSkeleton; + } + + // 0..* + const domInstance_camera_Array& cameraInstanceArray = node->getInstance_camera_array(); + for ( size_t i = 0; i < cameraInstanceArray.getCount(); i++ ) + { + daeElement *el = getElementFromURI( cameraInstanceArray[i]->getUrl()); + domCamera *c = daeSafeCast< domCamera >( el ); + + if (c) + addChild(attachTo, processCamera( c )); + else + osg::notify( osg::WARN ) << "Failed to locate camera " << cameraInstanceArray[i]->getUrl().getURI() << std::endl; + } + + // 0..* + const domInstance_controller_Array& controllerInstanceArray = node->getInstance_controller_array(); + for ( size_t i = 0; i < controllerInstanceArray.getCount(); i++ ) + { + osg::Node* pOsgNode = processInstanceController( controllerInstanceArray[i]); + + // A skin controller may return NULL, since the RigGeometry is added as + // child of the skeleton and the skeleton already is added to the scenegraph + if (pOsgNode) + { + addChild(attachTo, pOsgNode); } + } - // 0..* - domInstance_controller_Array controllerInstanceArray = node->getInstance_controller_array(); - for ( size_t i = 0; i < controllerInstanceArray.getCount(); i++ ) - { - groupNode->addChild( processInstanceController( controllerInstanceArray[i] )); - } + // 0..* + const domInstance_geometry_Array& geometryInstanceArray = node->getInstance_geometry_array(); + for ( size_t i = 0; i < geometryInstanceArray.getCount(); i++ ) + { + addChild(attachTo, processInstanceGeometry( geometryInstanceArray[i] )); + } - // 0..* - domInstance_geometry_Array geometryInstanceArray = node->getInstance_geometry_array(); - for ( size_t i = 0; i < geometryInstanceArray.getCount(); i++ ) - { - groupNode->addChild( processInstanceGeometry( geometryInstanceArray[i] )); - } + // 0..* + const domInstance_light_Array& lightInstanceArray = node->getInstance_light_array(); + for ( size_t i = 0; i < lightInstanceArray.getCount(); i++ ) + { + daeElement *el = getElementFromURI( lightInstanceArray[i]->getUrl()); + domLight *pDomLight = daeSafeCast< domLight >( el ); + + if (pDomLight) + addChild(attachTo, processLight(pDomLight)); + else + osg::notify( osg::WARN ) << "Failed to locate light " << lightInstanceArray[i]->getUrl().getURI() << std::endl; + } - // 0..* - domInstance_light_Array lightInstanceArray = node->getInstance_light_array(); - for ( size_t i = 0; i < lightInstanceArray.getCount(); i++ ) - { - daeElement *el = getElementFromURI( lightInstanceArray[i]->getUrl()); - domLight *l = daeSafeCast< domLight >( el ); - - if (l) - groupNode->addChild( processLight( l )); - else - osg::notify( osg::WARN ) << "Failed to locate light " << lightInstanceArray[i]->getUrl().getURI() << std::endl; - } + // 0..* + const domInstance_node_Array& nodeInstanceArray = node->getInstance_node_array(); + for ( size_t i = 0; i < nodeInstanceArray.getCount(); i++ ) + { + daeElement *el = getElementFromURI( nodeInstanceArray[i]->getUrl()); + domNode *n = daeSafeCast< domNode >( el ); - // 0..* - domInstance_node_Array nodeInstanceArray = node->getInstance_node_array(); - for ( size_t i = 0; i < nodeInstanceArray.getCount(); i++ ) - { - daeElement *el = getElementFromURI( nodeInstanceArray[i]->getUrl()); - domNode *n = daeSafeCast< domNode >( el ); - - if (n) - // Recursive call - groupNode->addChild( processNode( n )); - else - osg::notify( osg::WARN ) << "Failed to locate node " << nodeInstanceArray[i]->getUrl().getURI() << std::endl; - } - - // 0..* - domNode_Array nodeArray = node->getNode_array(); - for ( size_t i = 0; i < nodeArray.getCount(); i++ ) - { + if (n) // Recursive call - groupNode->addChild( processNode( nodeArray[i] )); - } + addChild(attachTo, processNode( n, skeleton )); + else + osg::notify( osg::WARN ) << "Failed to locate node " << nodeInstanceArray[i]->getUrl().getURI() << std::endl; + } + + // 0..* + const domNode_Array& nodeArray = node->getNode_array(); + for ( size_t i = 0; i < nodeArray.getCount(); i++ ) + { + // Recursive call + addChild(attachTo, processNode( nodeArray[i], skeleton )); } return resultNode; diff --git a/src/osgPlugins/dae/daeReader.h b/src/osgPlugins/dae/daeReader.h index ed5eac077..8f2a2abd4 100644 --- a/src/osgPlugins/dae/daeReader.h +++ b/src/osgPlugins/dae/daeReader.h @@ -20,17 +20,20 @@ #include #include #include +#include +#include #include -#include #include -#include #include #include #include #include #include - +#include +#include +#include +#include class domBind_material; class domCamera; @@ -52,9 +55,8 @@ class domTranslate; class domRotate; class domVisual_scene; -#include - -namespace osgdae { +namespace osgDAE +{ class domSourceReader; @@ -136,121 +138,266 @@ public: bool convert( const std::string &fileURI ); - osg::Node* getRootNode() { return rootNode; } + osg::Node* getRootNode() { return _rootNode; } - // Additional Information - std::string m_AssetUnitName; - float m_AssetUnitMeter; - domUpAxisType m_AssetUp_axis; + const std::string& getAssetUnitName() const {return _assetUnitName;} + float getAssetUnitMeter() const {return _assetUnitMeter;} + domUpAxisType getAssetUpAxis() const {return _assetUp_axis;} - // Texture unit useage - enum + enum TextureUnitUsage { AMBIENT_OCCLUSION_UNIT = 0, MAIN_TEXTURE_UNIT, TRANSPARENCY_MAP_UNIT }; -protected: - //scene processing - osg::Node* processVisualScene( domVisual_scene *scene ); - osg::Node* processNode( domNode *node ); - osg::Node* processOsgMatrixTransform( domNode *node ); - //osg::Node* processInstance( domInstanceWithExtra *iwe ); + enum InterpolationType + { + INTERPOLATION_UNKNOWN, + INTERPOLATION_STEP, + INTERPOLATION_LINEAR, + INTERPOLATION_BEZIER, + INTERPOLATION_HERMITE, + INTERPOLATION_CARDINAL, + INTERPOLATION_BSPLINE, + + //COLLADA spec states that if interpolation is not specified then + //interpolation is application defined. Linear is a sensible default. + INTERPOLATION_DEFAULT = INTERPOLATION_LINEAR + }; + + enum AuthoringTool + { + UNKNOWN, + BLENDER, + DAZ_STUDIO, + FBX_CONVERTER, + AUTODESK_3DS_MAX = FBX_CONVERTER,//3ds Max exports to DAE via Autodesk's FBX converter + GOOGLE_SKETCHUP, + MAYA + }; + + class TextureParameters + { + public: + TextureParameters() + : wrap_s(osg::Texture::REPEAT), wrap_t(osg::Texture::REPEAT), + filter_min(osg::Texture::LINEAR_MIPMAP_LINEAR), filter_mag(osg::Texture::LINEAR), + transparent(false), opaque(FX_OPAQUE_ENUM_A_ONE), transparency(1.0f) + {} + + bool operator < (const TextureParameters& rhs) const + { + int diffStr = filename.compare(rhs.filename); + if (diffStr) return diffStr < 0; + if (wrap_s != rhs.wrap_s) return wrap_s < rhs.wrap_s; + if (wrap_t != rhs.wrap_t) return wrap_t < rhs.wrap_t; + if (filter_min != rhs.filter_min) return filter_min < rhs.filter_min; + if (filter_mag != rhs.filter_mag) return filter_mag < rhs.filter_mag; + if (transparency != rhs.transparency) return transparency < rhs.transparency; + if (opaque != rhs.opaque) return opaque < rhs.opaque; + if (transparent != rhs.transparent) return transparent < rhs.transparent; + return border < rhs.border; + } + + std::string filename; + osg::Texture::WrapMode wrap_s, wrap_t; + osg::Texture::FilterMode filter_min, filter_mag; + osg::Vec4 border; + + //The following parameters are for transparency textures, to handle + //COLLADA's horrible transparency spec. + bool transparent; + domFx_opaque_enum opaque; + float transparency; + }; + + class ChannelPart : public osg::Referenced + { + public: + std::string name; + osg::ref_ptr keyframes; + InterpolationType interpolation; + }; + + typedef std::map > domGeometryGeodeMap; + typedef std::map > domMaterialStateSetMap; + typedef std::map > MaterialStateSetMap; + typedef std::multimap< daeElement*, domChannel*> daeElementDomChannelMap; + typedef std::map > domChannelOsgAnimationUpdateCallbackMap; + typedef std::map > domNodeOsgBoneMap; + typedef std::map > domNodeOsgSkeletonMap; + typedef std::map > TextureParametersMap; + typedef std::map, std::string> TextureToCoordSetMap; + + typedef std::map< daeElement*, domSourceReader > SourceMap; + typedef std::map< int, osg::IntArray*, std::less > IndexMap; + typedef std::map< int, osg::Array*, std::less > ArrayMap; + + typedef std::multimap< osgAnimation::Target*, osg::ref_ptr > TargetChannelPartMap; + typedef std::multimap, std::pair, GLuint> > OldToNewIndexMap; + +private: + // If the node is a bone then it should be added before any other types of + // node, this function makes that happen. + static void addChild(osg::Group*, osg::Node*); + + //scene processing + osg::Group* turnZUp(); + osg::Group* processVisualScene( domVisual_scene *scene ); + osg::Node* processNode( domNode *node, bool skeleton ); + osg::Transform* processOsgMatrixTransform( domNode *node, bool isBone); + + template + inline void getTransparencyCounts(daeDatabase*, int& zero, int& one) const; + + /** Earlier versions of the COLLADA 1.4 spec state that transparency values + of 0 mean 100% opacity, but this has been changed in later versions to state + that transparency values of 1 mean 100% opacity. Documents created by + different tools at different times adhere to different versions of the + standard. This function looks at all transparency values in the database and + heuristically decides which way the values should be interpreted.*/ + bool findInvertTransparency(daeDatabase*) const; + + osgAnimation::BasicAnimationManager* processAnimationLibraries(domCOLLADA* document); + void processAnimationClip(osgAnimation::BasicAnimationManager* pOsgAnimationManager, domAnimation_clip* pDomAnimationClip); + void processAnimation(domAnimation* pDomAnimation, osgAnimation::Animation* pOsgAnimation); + ChannelPart* processSampler(domChannel* pDomChannel, SourceMap &sources); + void processAnimationChannels(domAnimation* pDomAnimation, TargetChannelPartMap& tcm); + void processChannel(domChannel* pDomChannel, SourceMap &sources, TargetChannelPartMap& tcm); + void extractTargetName(const std::string&, std::string&, std::string&, std::string&); // Processing of OSG specific info stored in node extras - osg::Node* processExtras(domNode *node); + osg::Group* processExtras(domNode *node); void processNodeExtra(osg::Node* osgNode, domNode *node); domTechnique* getOpenSceneGraphProfile(domExtra* extra); void processAsset( domAsset *node ); - osg::Node* processOsgSwitch(domTechnique* teq); - osg::Node* processOsgMultiSwitch(domTechnique* teq); - osg::Node* processOsgLOD(domTechnique* teq); - osg::Node* processOsgDOFTransform(domTechnique* teq); - osg::Node* processOsgSequence(domTechnique* teq); + osg::Group* processOsgSwitch(domTechnique* teq); + osg::Group* processOsgMultiSwitch(domTechnique* teq); + osg::Group* processOsgLOD(domTechnique* teq); + osg::Group* processOsgDOFTransform(domTechnique* teq); + osg::Group* processOsgSequence(domTechnique* teq); - //geometry processing - class ReaderGeometry : public osg::Geometry - { - public: - std::map _TexcoordSetMap; - }; + // geometry processing + osg::Geode* getOrCreateGeometry(domGeometry *geom, domBind_material* pDomBindMaterial, const osg::Geode** ppOriginalGeode = NULL); + osgAnimation::Bone* getOrCreateBone(domNode *pDomNode); + osgAnimation::Skeleton* getOrCreateSkeleton(domNode *pDomNode); osg::Geode* processInstanceGeometry( domInstance_geometry *ig ); - osg::Geode* processGeometry( domGeometry *geo ); - osg::Geode* processInstanceController( domInstance_controller *ictrl ); - typedef std::map< daeElement*, domSourceReader > SourceMap; - typedef std::map< int, osg::IntArray*, std::less > IndexMap; + osg::Geode* processMesh(domMesh* pDomMesh); + osg::Geode* processConvexMesh(domConvex_mesh* pDomConvexMesh); + osg::Geode* processSpline(domSpline* pDomSpline); + osg::Geode* processGeometry(domGeometry *pDomGeometry); + + typedef std::vector domInstance_controllerList; + + void processSkins(); + //Process skins attached to one skeleton + void processSkeletonSkins(domNode* skeletonRoot, const domInstance_controllerList&); + void processSkin(domSkin* pDomSkin, domNode* skeletonRoot, osgAnimation::Skeleton*, domBind_material* pDomBindMaterial); + osg::Node* processMorph(domMorph* pDomMorph, domBind_material* pDomBindMaterial); + osg::Node* processInstanceController( domInstance_controller *ictrl ); template< typename T > - void processSinglePPrimitive(osg::Geode* geode, T *group, SourceMap &sources, GLenum mode ); + void processSinglePPrimitive(osg::Geode* geode, const domMesh* pDomMesh, const T* group, SourceMap& sources, GLenum mode); template< typename T > - void processMultiPPrimitive(osg::Geode* geode, T *group, SourceMap &sources, GLenum mode ); + void processMultiPPrimitive(osg::Geode* geode, const domMesh* pDomMesh, const T* group, SourceMap& sources, GLenum mode); - void processPolylist(osg::Geode* geode, domPolylist *group, SourceMap &sources ); + template + void processPolygons(osg::Geode* geode, const domMesh* pDomMesh, const T *group, SourceMap& sources); - void resolveArrays( domInputLocalOffset_Array &inputs, osg::Geometry *geom, - SourceMap &sources, IndexMap &index_map ); + void processPolylist(osg::Geode* geode, const domMesh* pDomMesh, const domPolylist *group, SourceMap &sources); + void processPolygons(osg::Geode* geode, const domMesh* pDomMesh, const domPolygons *group, SourceMap &sources); - void processP( domP *p, osg::Geometry *&geom, IndexMap &index_map, osg::DrawArrayLengths* dal/*GLenum mode*/ ); + void resolveMeshArrays(const domP_Array&, + const domInputLocalOffset_Array& inputs, const domMesh* pDomMesh, + osg::Geometry* geometry, SourceMap &sources, + std::vector >& vertexLists); //material/effect processing void processBindMaterial( domBind_material *bm, domGeometry *geom, osg::Geode *geode, osg::Geode *cachedGeode ); void processMaterial(osg::StateSet *ss, domMaterial *mat ); void processEffect(osg::StateSet *ss, domEffect *effect ); void processProfileCOMMON(osg::StateSet *ss, domProfile_COMMON *pc ); - bool processColorOrTextureType( domCommon_color_or_texture_type *cot, + bool processColorOrTextureType(const osg::StateSet*, + domCommon_color_or_texture_type *cot, osg::Material::ColorMode channel, osg::Material *mat, domCommon_float_or_param_type *fop = NULL, - osg::StateAttribute **sa = NULL, + osg::Texture2D **sa = NULL, bool normalizeShininess=false); void processTransparencySettings( domCommon_transparent_type *ctt, domCommon_float_or_param_type *pTransparency, - osg::StateSet *ss, + osg::StateSet*, osg::Material *material, xsNCName diffuseTextureName ); - bool GetFloat4Param(xsNCName Reference, domFloat4 &f4); - bool GetFloatParam(xsNCName Reference, domFloat &f); + bool GetFloat4Param(xsNCName Reference, domFloat4 &f4) const; + bool GetFloatParam(xsNCName Reference, domFloat &f) const; - osg::StateAttribute *processTexture( domCommon_color_or_texture_type_complexType::domTexture *tex ); + std::string processImagePath(const domImage*) const; + osg::Image* processImageTransparency(const osg::Image*, domFx_opaque_enum, float transparency) const; + osg::Texture2D* processTexture( domCommon_color_or_texture_type_complexType::domTexture *tex, const osg::StateSet*, TextureUnitUsage, domFx_opaque_enum = FX_OPAQUE_ENUM_A_ONE, float transparency = 1.0f); + void copyTextureCoordinateSet(const osg::StateSet* ss, const osg::Geometry* cachedGeometry, osg::Geometry* clonedGeometry, const domInstance_material* im, TextureUnitUsage); //scene objects osg::Node* processLight( domLight *dlight ); osg::Node* processCamera( domCamera *dcamera ); -protected: - DAE *dae; - osg::Node* rootNode; + domNode* getRootJoint(domNode*) const; + domNode* findJointNode(daeElement* searchFrom, domInstance_controller*) const; + domNode* findSkeletonNode(daeElement* searchFrom, domInstance_controller*) const; + + /// Return whether the node is used as a bone. Note that while many files + /// identify joints with type="JOINT", some don't do this, while others + /// incorrectly identify every node as a joint. + bool isJoint(const domNode* node) const {return _jointSet.find(node) != _jointSet.end();} + +private: + + DAE *_dae; + osg::Node* _rootNode; osg::ref_ptr _rootStateSet; + domCOLLADA* _document; + domVisual_scene* _visualScene; std::map _targetMap; - int m_numlights; + int _numlights; - domInstance_effect *currentInstance_effect; - domEffect *currentEffect; - - typedef std::map< domGeometry*, osg::ref_ptr > domGeometryGeodeMap; - typedef std::map< domMaterial*, osg::ref_ptr > domMaterialStateSetMap; - typedef std::map< std::string, osg::ref_ptr > MaterialStateSetMap; + domInstance_effect *_currentInstance_effect; + domEffect *_currentEffect; + /// Maps an animated element to a domchannel to quickly find which animation influence this element + // TODO a single element can be animated by multiple channels (with different members like translate.x or morphweights(2) ) + daeElementDomChannelMap _daeElementDomChannelMap; + /// Maps a domchannel to an animationupdatecallback + domChannelOsgAnimationUpdateCallbackMap _domChannelOsgAnimationUpdateCallbackMap; /// Maps geometry to a Geode - domGeometryGeodeMap geometryMap; + domGeometryGeodeMap _geometryMap; + /// All nodes in the document that are used as joints. + std::set _jointSet; + /// Maps a node (of type joint) to a osgAnimation::Bone + domNodeOsgBoneMap _jointMap; + /// Maps a node (of type joint) to a osgAnimation::Skeleton + domNodeOsgSkeletonMap _skeletonMap; // Maps material target to stateset - domMaterialStateSetMap materialMap; + domMaterialStateSetMap _materialMap; // Maps material symbol to stateset - MaterialStateSetMap materialMap2; + MaterialStateSetMap _materialMap2; + TextureParametersMap _textureParamMap; + TextureToCoordSetMap _texCoordSetMap; + domInstance_controllerList _skinInstanceControllers; + OldToNewIndexMap _oldToNewIndexMap; - enum AuthoringTool - { - UNKNOWN, - GOOGLE_SKETCHUP - }; - AuthoringTool m_AuthoringTool; - bool m_StrictTransparency; + AuthoringTool _authoringTool; + bool _strictTransparency, _invertTransparency; + + // Additional Information + std::string _assetUnitName; + float _assetUnitMeter; + domUpAxisType _assetUp_axis; }; } diff --git a/src/osgPlugins/dae/daeWAnimations.cpp b/src/osgPlugins/dae/daeWAnimations.cpp new file mode 100644 index 000000000..05dc50a79 --- /dev/null +++ b/src/osgPlugins/dae/daeWAnimations.cpp @@ -0,0 +1,483 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include "daeWriter.h" + +#include + +#include +#include +#include +#include +#include +//#include +//#include + +#include +#include +#include +#include +#include +#include + +using namespace osgDAE; + + +void daeWriter::writeAnimations( osg::Node &node ) +{ + osg::NodeCallback* ncb = node.getUpdateCallback(); + if (ncb) + { + osgAnimation::AnimationManagerBase* am = dynamic_cast(ncb); + if (am) + { + // Create library of animations if not existing yet + if (!_domLibraryAnimations) + { + _domLibraryAnimations = daeSafeCast( dom->add( COLLADA_ELEMENT_LIBRARY_ANIMATIONS ) ); + } + + osgAnimation::AnimationList animationList = am->getAnimationList(); + for (size_t i = 0; i < animationList.size(); i++) + { + domAnimation* pDomAnimation = daeSafeCast< domAnimation >( _domLibraryAnimations->add( COLLADA_ELEMENT_ANIMATION ) ); + domAnimation* pMainDomAnimation = pDomAnimation; + + osg::ref_ptr animation = animationList[i]; + std::string animationName = animation->getName(); + if (animationName.empty()) + animationName = "animation"; + animationName = uniquify( animationName ); + pDomAnimation->setId(animationName.c_str()); + + // Handle multiple channels within an animation + osgAnimation::ChannelList animationChannels = animation->getChannels(); + for (size_t j=0; j < animationChannels.size(); j++) + { + osgAnimation::Channel* channel = animationChannels[j].get(); + std::string channelName = channel->getName(); + + // Wrap each animation channel into it's own child when more than 1 channel + if (animationChannels.size() > 1) + { + pDomAnimation = daeSafeCast< domAnimation >( pMainDomAnimation->add( COLLADA_ELEMENT_ANIMATION ) ); + + if (channelName.empty()) + channelName = "channel"; + animationName = uniquify( channelName ); + pDomAnimation->setId(channelName.c_str()); + } + + std::string sourceName = channelName + "_sampler"; + std::string inputSourceName = channelName + "_input"; + std::string outputSourceName = channelName + "_output"; + std::string interpolationSourceName = channelName + "_interpolation"; + + // Fill dom sources based on sampler + osgAnimation::Sampler* animationSampler = channel->getSampler(); + osgAnimation::KeyframeContainer* kfc = animationSampler->getKeyframeContainer(); + int size = kfc->size(); + + float valueStride = 1; + + domListOfFloats timeValues; + domListOfFloats frameValues; + domListOfNames interpolationValues; + + osgAnimation::Vec3KeyframeContainer* v3kc = dynamic_cast(kfc); + if (v3kc) + { + valueStride = 3; + for (size_t i=0; i < v3kc->size(); i++) + { + timeValues.append((*v3kc)[i].getTime()); + osg::Vec3 vec = (*v3kc)[i].getValue(); + + // This needs some serious cleanup + if (channelName.find("euler") != std::string::npos) + { + frameValues.append(osg::RadiansToDegrees(vec.x())); + frameValues.append(osg::RadiansToDegrees(vec.y())); + frameValues.append(osg::RadiansToDegrees(vec.z())); + } + else + { + frameValues.append(vec.x()); + frameValues.append(vec.y()); + frameValues.append(vec.z()); + } + interpolationValues.append("LINEAR"); + } + } + osgAnimation::FloatKeyframeContainer* fkc = dynamic_cast(kfc); + if (fkc) + { + valueStride = 1; + for (size_t i=0; i < fkc->size(); i++) + { + timeValues.append((*fkc)[i].getTime()); + frameValues.append((*fkc)[i].getValue()); + interpolationValues.append("LINEAR"); + } + } + + // time values + domSource* pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); + pDomSource->setId(inputSourceName.c_str()); + + domFloat_array* pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string inputArrayName = inputSourceName + "_array"; + pDomFloatArray->setId(inputArrayName.c_str()); + pDomFloatArray->setCount(size); + pDomFloatArray->setValue(timeValues); + + domSource::domTechnique_common* pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + std::string url = "#" + inputArrayName; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(1); + + domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("TIME"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + + // interpolator type + pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); + pDomSource->setId(interpolationSourceName.c_str()); + + domName_array* pDomNameArray = daeSafeCast< domName_array >(pDomSource->add(COLLADA_ELEMENT_NAME_ARRAY)); + std::string interpolationArrayName = interpolationSourceName + "_array"; + pDomNameArray->setId(interpolationArrayName.c_str()); + pDomNameArray->setCount(size); + pDomNameArray->setValue(interpolationValues); + + // + pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + // + pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + url = "#" + interpolationArrayName; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(1); + + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("INTERPOLATION"); + pDomParam->setType(COLLADA_TYPE_NAME); + + // Split up access to the euler float array into three sources, because we need to target three separate transforms + if (channelName.find("euler") != std::string::npos) + { + pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); + std::string outputSourceNameX = outputSourceName + "_X"; + pDomSource->setId(outputSourceNameX.c_str()); + + pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string outputArrayNameX = outputSourceNameX + "_array"; + pDomFloatArray->setId(outputArrayNameX.c_str()); + + // TODO, flexible handling of different keyframe types, see osg exporter for inspiration + pDomFloatArray->setCount(size * valueStride); + pDomFloatArray->setValue(frameValues); + + pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + url = "#" + outputArrayNameX; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(valueStride); + pDomAccessor->setOffset(0); + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("ANGLE"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + + pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); + std::string outputSourceNameY = outputSourceName + "_Y"; + pDomSource->setId(outputSourceNameY.c_str()); + + pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string outputArrayNameY = outputSourceNameY + "_array"; + pDomFloatArray->setId(outputArrayNameY.c_str()); + + pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + url = "#" + outputArrayNameY; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(valueStride); + pDomAccessor->setOffset(1); + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("ANGLE"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + + pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); + std::string outputSourceNameZ = outputSourceName + "_Z"; + pDomSource->setId(outputSourceNameZ.c_str()); + + pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string outputArrayNameZ = outputSourceNameZ + "_array"; + pDomFloatArray->setId(outputArrayNameZ.c_str()); + + pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + url = "#" + outputArrayNameZ; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(valueStride); + pDomAccessor->setOffset(2); + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("ANGLE"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + + { + domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); + std::string sourceNameX = sourceName + "_X"; + pDomSampler->setId(sourceNameX.c_str()); + + // Fill dom sampler based on common semantics + { + domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); + std::string url = "#" + inputSourceName; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); + url = "#" + outputSourceNameX; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); + url = "#" + interpolationSourceName; + pDomInput->setSource(url.c_str()); + } + + // Set sampler as source + domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); + std::string url = "#" + sourceNameX; + pDomChannel->setSource(url.c_str()); + + // targetName contains the name of the updateCallback + std::string targetName = channel->getTargetName(); + + // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" + osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); + if (node) + { + std::string domChannelTargetName = node->getName() + "/rotateX.ANGLE"; + pDomChannel->setTarget(domChannelTargetName.c_str()); + } + else + { + osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl; + } + } + + { + domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); + std::string sourceNameY = sourceName + "_Y"; + pDomSampler->setId(sourceNameY.c_str()); + + // Fill dom sampler based on common semantics + { + domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); + std::string url = "#" + inputSourceName; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); + url = "#" + outputSourceNameY; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); + url = "#" + interpolationSourceName; + pDomInput->setSource(url.c_str()); + } + + // Set sampler as source + domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); + std::string url = "#" + sourceNameY; + pDomChannel->setSource(url.c_str()); + + // targetName contains the name of the updateCallback + std::string targetName = channel->getTargetName(); + + // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" + osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); + if (node) + { + std::string domChannelTargetName = node->getName() + "/rotateY.ANGLE"; + pDomChannel->setTarget(domChannelTargetName.c_str()); + } + else + { + osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl; + } + } + + { + domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); + std::string sourceNameZ = sourceName + "_Z"; + pDomSampler->setId(sourceNameZ.c_str()); + + // Fill dom sampler based on common semantics + { + domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); + std::string url = "#" + inputSourceName; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); + url = "#" + outputSourceNameZ; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); + url = "#" + interpolationSourceName; + pDomInput->setSource(url.c_str()); + } + + // Set sampler as source + domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); + std::string url = "#" + sourceNameZ; + pDomChannel->setSource(url.c_str()); + + // targetName contains the name of the updateCallback + std::string targetName = channel->getTargetName(); + + // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" + osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); + if (node) + { + std::string domChannelTargetName = node->getName() + "/rotateZ.ANGLE"; + pDomChannel->setTarget(domChannelTargetName.c_str()); + } + else + { + osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl; + } + } + } + else + { + // values in keyframecontainer + pDomSource = daeSafeCast< domSource >(pDomAnimation->add(COLLADA_ELEMENT_SOURCE)); + pDomSource->setId(outputSourceName.c_str()); + + pDomFloatArray = daeSafeCast< domFloat_array >(pDomSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string outputArrayName = outputSourceName + "_array"; + pDomFloatArray->setId(outputArrayName.c_str()); + + // TODO, flexible handling of different keyframe types, see osg exporter for inspiration + pDomFloatArray->setCount(size * valueStride); + pDomFloatArray->setValue(frameValues); + + pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + url = "#" + outputArrayName; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(valueStride); + + if (v3kc) + { + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("X"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("Y"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName("Z"); + pDomParam->setType(COLLADA_TYPE_FLOAT); + } + if (fkc) + { + // + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setType(COLLADA_TYPE_FLOAT); + } + + domSampler* pDomSampler = daeSafeCast< domSampler >(pDomAnimation->add(COLLADA_ELEMENT_SAMPLER)); + pDomSampler->setId(sourceName.c_str()); + + // Fill dom sampler based on common semantics + { + domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INPUT); + std::string url = "#" + inputSourceName; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_OUTPUT); + url = "#" + outputSourceName; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomSampler->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INTERPOLATION); + url = "#" + interpolationSourceName; + pDomInput->setSource(url.c_str()); + } + + // Set sampler as source + domChannel* pDomChannel = daeSafeCast< domChannel >(pDomAnimation->add(COLLADA_ELEMENT_CHANNEL)); + std::string url = "#" + sourceName; + pDomChannel->setSource(url.c_str()); + + // targetName contains the name of the updateCallback + std::string targetName = channel->getTargetName(); + + // based on the type of updateCallback we can determine the transform element to target eg. "nodeName/translation" + osg::Node* node = _animatedNodeCollector.getTargetNode(targetName); + if (node) + { + std::string domChannelTargetName = node->getName(); + + if (channelName.find("position") != std::string::npos) + { + domChannelTargetName += "/translate"; + } + else if (channelName.find("scale") != std::string::npos) + { + domChannelTargetName += "/scale"; + } + + pDomChannel->setTarget(domChannelTargetName.c_str()); + } + else + { + osg::notify( osg::WARN ) << "Could not find animation target '" << targetName << "'" << std::endl; + } + } + } + } + } + } +} + diff --git a/src/osgPlugins/dae/daeWGeometry.cpp b/src/osgPlugins/dae/daeWGeometry.cpp index f4c8639f5..68984b335 100644 --- a/src/osgPlugins/dae/daeWGeometry.cpp +++ b/src/osgPlugins/dae/daeWGeometry.cpp @@ -12,6 +12,7 @@ */ #include "daeWriter.h" +#include #include #include @@ -19,10 +20,347 @@ #include #include #include +//#include #include -using namespace osgdae; +using namespace osgDAE; + +domGeometry* daeWriter::getOrCreateDomGeometry(osg::Geometry* pOsgGeometry) +{ + // See if geometry exists in cache + OsgGeometryDomGeometryMap::iterator iter = geometryMap.find( pOsgGeometry ); + if ( iter != geometryMap.end() ) + { + return iter->second; + } + else + { + if (!lib_geoms) + { + lib_geoms = daeSafeCast< domLibrary_geometries >( dom->add( COLLADA_ELEMENT_LIBRARY_GEOMETRIES ) ); + } + domGeometry* pDomGeometry = daeSafeCast< domGeometry >( lib_geoms->add( COLLADA_ELEMENT_GEOMETRY ) ); + + std::string name = pOsgGeometry->getName(); + if (name.empty()) + name = uniquify("geometry"); + else + name = uniquify(name); + pDomGeometry->setId( name.c_str() ); + #ifndef EARTH_GEO + geometryMap.insert( std::make_pair( pOsgGeometry, pDomGeometry ) ); + #endif + + if ( !processGeometry( pOsgGeometry, pDomGeometry, name ) ) + { + daeElement::removeFromParent( pDomGeometry ); + return NULL; + } + return pDomGeometry; + } +} + +void daeWriter::writeRigGeometry(osgAnimation::RigGeometry *pOsgRigGeometry) +{ + // See if controller exists in cache + OsgRigGeometryDomControllerMap::iterator iter = _osgRigGeometryDomControllerMap.find(pOsgRigGeometry); + domController* pDomController = NULL; + if ( iter != _osgRigGeometryDomControllerMap.end() ) + { + pDomController = iter->second; + } + else + { + domGeometry* pDomGeometry = getOrCreateDomGeometry(pOsgRigGeometry); + if (pDomGeometry) + { + if (!lib_controllers) + { + lib_controllers = daeSafeCast< domLibrary_controllers >( dom->add( COLLADA_ELEMENT_LIBRARY_CONTROLLERS ) ); + } + + // + // 1 + // source + // 0..1 + // 3..* + // 1 + // 1 + // 0..1 + pDomController = daeSafeCast< domController >( lib_controllers->add( COLLADA_ELEMENT_CONTROLLER) ); + std::string name = pOsgRigGeometry->getName(); + if (name.empty()) + name = uniquify("skincontroller"); + else + name = uniquify(name); + pDomController->setId( name.c_str() ); + _osgRigGeometryDomControllerMap.insert( std::make_pair( pOsgRigGeometry, pDomController ) ); + + // Link to cache hit or created + domSkin* pDomSkin = daeSafeCast< domSkin >(pDomController->add( COLLADA_ELEMENT_SKIN )); + std::string url = "#" + std::string(pDomGeometry->getId()); + pDomSkin->setSource(url.c_str()); + + domSkin::domBind_shape_matrix* pDomBindShapeMatrix = daeSafeCast< domSkin::domBind_shape_matrix >(pDomSkin->add( COLLADA_ELEMENT_BIND_SHAPE_MATRIX )); + + domSource* pDomJointsSource = daeSafeCast< domSource >(pDomSkin->add( COLLADA_ELEMENT_SOURCE )); + std::string skinJointsName = name + "_skin_joints"; + pDomJointsSource->setId(skinJointsName.c_str()); + + domListOfNames jointNames; // TODO fill with joint ids + int size = 0; // TODO number of animated joints + + osgAnimation::VertexInfluenceMap* vim = pOsgRigGeometry->getInfluenceMap(); + osgAnimation::VertexInfluenceMap::iterator iter = vim->begin(); + while (iter != vim->end()) + { + jointNames.append(iter->first.c_str()); + //iter->second.getn + ++iter; + } + + domName_array* pDomJointsNameArray = daeSafeCast< domName_array >(pDomJointsSource->add(COLLADA_ELEMENT_NAME_ARRAY)); + std::string jointsNameArrayName = name + "_joints_array"; + pDomJointsNameArray->setId(jointsNameArrayName.c_str()); + pDomJointsNameArray->setCount(size); + pDomJointsNameArray->setValue(jointNames); + { + domSource::domTechnique_common* pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomJointsSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + std::string url = "#" + jointsNameArrayName; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + + domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setType(COLLADA_TYPE_NAME); + } + + domSource* pDomSkinBindPoseSource = daeSafeCast< domSource >(pDomSkin->add( COLLADA_ELEMENT_SOURCE )); + std::string skinBindPoseName = name + "_skin_bind_pose"; + pDomSkinBindPoseSource->setId(skinBindPoseName.c_str()); + + domListOfFloats matrices; // TODO fill with bind matrices + int numMatrices = 0; // TODO number of bind matrices + domFloat_array* pDomMatricesArray = daeSafeCast< domFloat_array >(pDomSkinBindPoseSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string matricesArrayName = name + "_matrices_array"; + pDomMatricesArray->setId(matricesArrayName.c_str()); + pDomMatricesArray->setCount(numMatrices); + pDomMatricesArray->setValue(matrices); + { + domSource::domTechnique_common* pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSkinBindPoseSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + std::string url = "#" + matricesArrayName; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + pDomAccessor->setStride(16); + + domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setType(COLLADA_TYPE_FLOAT4X4); + } + + domSource* pDomSkinWeightsSource = daeSafeCast< domSource >(pDomSkin->add( COLLADA_ELEMENT_SOURCE )); + std::string skinWeightsName = name + "_skin_weights"; + pDomSkinWeightsSource->setId(skinWeightsName.c_str()); + + domListOfFloats weights; // TODO fill with vertex weights + int numWeights = 0; // TODO number of vertices vertex weights + domFloat_array* pDomWeightsArray = daeSafeCast< domFloat_array >(pDomSkinWeightsSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + std::string weightsArrayName = name + "_weights_array"; + pDomWeightsArray->setId(weightsArrayName.c_str()); + pDomWeightsArray->setCount(numWeights); + pDomWeightsArray->setValue(weights); + { + domSource::domTechnique_common* pDomSourceTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomSkinWeightsSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + + domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomSourceTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + std::string url = "#" + weightsArrayName; + pDomAccessor->setSource(url.c_str()); + pDomAccessor->setCount(size); + + domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setType(COLLADA_TYPE_FLOAT); + } + + domSkin::domJoints* pDomJoints = daeSafeCast< domSkin::domJoints >(pDomSkin->add( COLLADA_ELEMENT_JOINTS )); + + domInputLocal* pDomInput = daeSafeCast< domInputLocal >(pDomJoints->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_JOINT); + url = "#" + skinJointsName; + pDomInput->setSource(url.c_str()); + + pDomInput = daeSafeCast< domInputLocal >(pDomJoints->add(COLLADA_ELEMENT_INPUT)); + pDomInput->setSemantic(COMMON_PROFILE_INPUT_INV_BIND_MATRIX); + url = "#" + skinBindPoseName; + pDomInput->setSource(url.c_str()); + + domSkin::domVertex_weights* pDomVertexWeights = daeSafeCast< domSkin::domVertex_weights >(pDomSkin->add( COLLADA_ELEMENT_VERTEX_WEIGHTS )); + pDomVertexWeights->setCount(0);// TODO set number of vertex weights + + domInputLocalOffset* pDomInputLocalOffset = daeSafeCast< domInputLocalOffset >(pDomVertexWeights->add(COLLADA_ELEMENT_INPUT)); + pDomInputLocalOffset->setSemantic(COMMON_PROFILE_INPUT_JOINT); + url = "#" + skinJointsName; + pDomInputLocalOffset->setSource(url.c_str()); + pDomInputLocalOffset->setOffset(0); + + pDomInputLocalOffset = daeSafeCast< domInputLocalOffset >(pDomVertexWeights->add(COLLADA_ELEMENT_INPUT)); + pDomInputLocalOffset->setSemantic(COMMON_PROFILE_INPUT_WEIGHT); + url = "#" + weightsArrayName; + pDomInputLocalOffset->setSource(url.c_str()); + pDomInputLocalOffset->setOffset(1); + + domSkin::domVertex_weights::domVcount* pDomVcount = daeSafeCast< domSkin::domVertex_weights::domVcount >(pDomVertexWeights->add(COLLADA_ELEMENT_VCOUNT)); + domListOfUInts valueCounts; + // TODO + pDomVcount->setValue(valueCounts); + domSkin::domVertex_weights::domV* pDomV = daeSafeCast< domSkin::domVertex_weights::domV >(pDomVertexWeights->add(COLLADA_ELEMENT_V)); + domListOfInts values; + //TODO + pDomV->setValue(values); + } + } + + if (pDomController) + { + // Link to cache hit or created + domInstance_controller* pDomInstanceController = daeSafeCast< domInstance_controller >( currentNode->add( COLLADA_ELEMENT_INSTANCE_CONTROLLER ) ); + std::string url = "#" + std::string(pDomController->getId()); + pDomInstanceController->setUrl( url.c_str() ); + } +} + +void daeWriter::writeMorphGeometry(osgAnimation::MorphGeometry *pOsgMorphGeometry) +{ + // See if controller exists in cache + OsgMorphGeometryDomControllerMap::iterator iter = _osgMorphGeometryDomControllerMap.find(pOsgMorphGeometry); + domController* pDomController = NULL; + if ( iter != _osgMorphGeometryDomControllerMap.end() ) + { + pDomController = iter->second; + } + else + { + domGeometry* pDomGeometry = getOrCreateDomGeometry(pOsgMorphGeometry); + if (pDomGeometry) + { + if (!lib_controllers) + { + lib_controllers = daeSafeCast< domLibrary_controllers >( dom->add( COLLADA_ELEMENT_LIBRARY_CONTROLLERS ) ); + } + + // + // 1 + // 2..* + // 1 + // 2..* + // 0..* + // 0..* + pDomController = daeSafeCast< domController >( lib_controllers->add( COLLADA_ELEMENT_CONTROLLER) ); + std::string name = pOsgMorphGeometry->getName(); + if (name.empty()) + name = uniquify("morphcontroller"); + else + name = uniquify(name); + pDomController->setId( name.c_str() ); + _osgMorphGeometryDomControllerMap.insert( std::make_pair( pOsgMorphGeometry, pDomController ) ); + + // Link to cache hit or created + domMorph* pDomMorph = daeSafeCast< domMorph >(pDomController->add( COLLADA_ELEMENT_MORPH )); + std::string url = "#" + std::string(pDomGeometry->getId()); + pDomMorph->setSource(url.c_str()); + pDomMorph->setMethod(MORPHMETHODTYPE_NORMALIZED); + //pDomMorph->setMethod(MORPHMETHODTYPE_RELATIVE); + + domSource* pDomTargetsSource = daeSafeCast< domSource >(pDomMorph->add( COLLADA_ELEMENT_SOURCE )); + std::string targetsName = name + "_morph_targets"; + pDomTargetsSource->setId(targetsName.c_str()); + + domIDREF_array* pDomIDREFArray = daeSafeCast< domIDREF_array >(pDomTargetsSource->add(COLLADA_ELEMENT_IDREF_ARRAY)); + xsIDREFS idrefs; + osgAnimation::MorphGeometry::MorphTargetList morphTargetList = pOsgMorphGeometry->getMorphTargetList(); + for (unsigned int i=0; i < morphTargetList.size(); i++) + { + domGeometry* pDomGeometry = getOrCreateDomGeometry(morphTargetList[i].getGeometry()); + idrefs.append(pDomGeometry->getId()); + } + pDomIDREFArray->setValue(idrefs); + std::string targetsArrayName = targetsName + "_array"; + pDomIDREFArray->setId(targetsArrayName.c_str()); + pDomIDREFArray->setCount(morphTargetList.size()); + + domSource::domTechnique_common* pDomTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomTargetsSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + domAccessor* pDomAccessor = daeSafeCast< domAccessor >(pDomTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + pDomAccessor->setCount(morphTargetList.size()); + url = "#" + targetsArrayName; + pDomAccessor->setSource(url.c_str()); + + domParam* pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName(COMMON_PROFILE_INPUT_MORPH_TARGET); + pDomParam->setType("IDREF"); // COLLADA_TYPE_IDREF does not exist + + domSource* pDomWeightsSource = daeSafeCast< domSource >(pDomMorph->add( COLLADA_ELEMENT_SOURCE )); + std::string weightsName = name + "_morph_weights"; + pDomWeightsSource->setId(weightsName.c_str()); + + domFloat_array* pDomFloatArray = daeSafeCast< domFloat_array >(pDomWeightsSource->add(COLLADA_ELEMENT_FLOAT_ARRAY)); + domListOfFloats weights; + for (unsigned int i=0; i < morphTargetList.size(); i++) + { + weights.append(morphTargetList[i].getWeight()); + } + pDomFloatArray->setValue(weights); + std::string weigthsArrayName = weightsName + "_array"; + pDomFloatArray->setId(weigthsArrayName.c_str()); + pDomFloatArray->setCount(morphTargetList.size()); + + pDomTechniqueCommon = daeSafeCast< domSource::domTechnique_common >(pDomWeightsSource->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); + pDomAccessor = daeSafeCast< domAccessor >(pDomTechniqueCommon->add(COLLADA_ELEMENT_ACCESSOR)); + pDomAccessor->setCount(morphTargetList.size()); + url = "#" + weightsName; + pDomAccessor->setSource(url.c_str()); + + pDomParam = daeSafeCast< domParam >(pDomAccessor->add(COLLADA_ELEMENT_PARAM)); + pDomParam->setName(COMMON_PROFILE_INPUT_MORPH_WEIGHT); + pDomParam->setType(COLLADA_TYPE_FLOAT); + + domMorph::domTargets* pDomTargets = daeSafeCast< domMorph::domTargets >(pDomMorph->add( COLLADA_ELEMENT_TARGETS )); + + domInputLocal* pDomTargetsInput = daeSafeCast< domInputLocal >(pDomTargets->add( COLLADA_ELEMENT_INPUT )); + pDomTargetsInput->setSemantic(COMMON_PROFILE_INPUT_MORPH_TARGET); + url = "#" + targetsName; + pDomTargetsInput->setSource(url.c_str()); + + domInputLocal* pDomWeightsInput = daeSafeCast< domInputLocal >(pDomTargets->add( COLLADA_ELEMENT_INPUT )); + pDomWeightsInput->setSemantic(COMMON_PROFILE_INPUT_MORPH_WEIGHT); + url = "#" + weightsName; + pDomWeightsInput->setSource(url.c_str()); + } + } + + if (pDomController) + { + // Transparency at drawable level + if (pOsgMorphGeometry->getStateSet()) + m_CurrentRenderingHint = pOsgMorphGeometry->getStateSet()->getRenderingHint(); + + pushStateSet(pOsgMorphGeometry->getStateSet()); + + // Link to cache hit or created + domInstance_controller* pDomInstanceController = daeSafeCast< domInstance_controller >( currentNode->add( COLLADA_ELEMENT_INSTANCE_CONTROLLER ) ); + std::string url = "#" + std::string(pDomController->getId()); + pDomInstanceController->setUrl( url.c_str() ); + + if (!stateSetStack.empty()) + { + domBind_material *pDomBindMaterial = daeSafeCast< domBind_material >( pDomInstanceController->add( COLLADA_ELEMENT_BIND_MATERIAL ) ); + processMaterial( currentStateSet.get(), pDomBindMaterial, pOsgMorphGeometry->getName() ); + } + + popStateSet(pOsgMorphGeometry->getStateSet()); + } +} void daeWriter::apply( osg::Geode &node ) { @@ -42,53 +380,50 @@ void daeWriter::apply( osg::Geode &node ) if ( g != NULL ) { - // Transparency at drawable level - if (NULL != g->getStateSet()) - m_CurrentRenderingHint = g->getStateSet()->getRenderingHint(); - - pushStateSet(g->getStateSet()); - std::map< osg::Geometry*, domGeometry *>::iterator iter = geometryMap.find( g ); - if ( iter != geometryMap.end() ) + osgAnimation::RigGeometry *pOsgRigGeometry = dynamic_cast(g); + if (pOsgRigGeometry) { - domInstance_geometry *ig = daeSafeCast< domInstance_geometry >( currentNode->add( COLLADA_ELEMENT_INSTANCE_GEOMETRY ) ); - - std::string url = "#" + std::string( iter->second->getId() ); - ig->setUrl( url.c_str() ); - if (!stateSetStack.empty()) - processMaterial( currentStateSet.get(), ig, iter->second->getId() ); + writeRigGeometry(pOsgRigGeometry); } else { - if ( lib_geoms == NULL ) + osgAnimation::MorphGeometry *pOsgMorphGeometry = dynamic_cast(g); + if (pOsgMorphGeometry) { - lib_geoms = daeSafeCast< domLibrary_geometries >( dom->add( COLLADA_ELEMENT_LIBRARY_GEOMETRIES ) ); + writeMorphGeometry(pOsgMorphGeometry); } - std::string name = node.getName(); - if ( name.empty() ) name = "geometry"; - name = uniquify( name ); - - domGeometryRef geo = daeSafeCast< domGeometry >( lib_geoms->add( COLLADA_ELEMENT_GEOMETRY ) ); - geo->setId( name.c_str() ); - - if ( !processGeometry( g, geo, name ) ) + else { - daeElement::removeFromParent( geo ); - continue; - } + // Write a default osg::Geometry - domInstance_geometry *ig = daeSafeCast< domInstance_geometry >( currentNode->add( COLLADA_ELEMENT_INSTANCE_GEOMETRY ) ); + // Transparency at drawable level + if (NULL != g->getStateSet()) + m_CurrentRenderingHint = g->getStateSet()->getRenderingHint(); + + pushStateSet(g->getStateSet()); - std::string url = "#" + name; - ig->setUrl( url.c_str() ); + domGeometry* pDomGeometry = getOrCreateDomGeometry(g); + if (pDomGeometry) + { + // Link to cache hit or created + domInstance_geometry *pDomInstanceGeometry = daeSafeCast< domInstance_geometry >( currentNode->add( COLLADA_ELEMENT_INSTANCE_GEOMETRY ) ); + std::string url = "#" + std::string(pDomGeometry->getId()); + pDomInstanceGeometry->setUrl( url.c_str() ); -#ifndef EARTH_GEO - geometryMap.insert( std::make_pair( g, geo ) ); -#endif + if (!stateSetStack.empty()) + { + domBind_material *pDomBindMaterial = daeSafeCast< domBind_material >( pDomInstanceGeometry->add( COLLADA_ELEMENT_BIND_MATERIAL ) ); + processMaterial( currentStateSet.get(), pDomBindMaterial, pDomGeometry->getId() ); + } + } - if (!stateSetStack.empty()) - processMaterial( currentStateSet.get(), ig, name ); + popStateSet(g->getStateSet()); + } } - popStateSet(g->getStateSet()); + } + else + { + osg::notify( osg::WARN ) << "Non-geometry drawables are not supported" << std::endl; } } @@ -142,36 +477,40 @@ bool daeWriter::processGeometry( osg::Geometry *geom, domGeometry *geo, const st domSource *norm = NULL; domSource *color = NULL; std::vector< domSource * >texcoord; + std::vector< domSource * > vertexAttribute; domLines *lines = NULL; domLinestrips *linestrips = NULL; domTriangles *tris = NULL; domTristrips *tristrips = NULL; domTrifans *trifans = NULL; domPolygons *polys = NULL; - domPolylist *polylist = NULL; + domPolylist *polylist = NULL; - //TODO: Make sure the assumptions about arrays is correct. - // Probably not so I should make each thing more flexible so arrays can be different sizes. - - /*osg::Vec3Array *verts = (osg::Vec3Array *)geom->getVertexArray(); - osg::IndexArray *vertInds = geom->getVertexIndices(); - - osg::Vec3Array *normals = (osg::Vec3Array *)geom->getNormalArray(); - osg::IndexArray *normalInds = geom->getNormalIndices(); - - osg::Vec4Array *colors = (osg::Vec4Array *)geom->getColorArray(); - osg::IndexArray *colorInds = geom->getColorIndices();*/ - ArrayNIndices verts( geom->getVertexArray(), geom->getVertexIndices() ); ArrayNIndices normals( geom->getNormalArray(), geom->getNormalIndices() ); ArrayNIndices colors( geom->getColorArray(), geom->getColorIndices() ); + + // RS BUG + // getNumTexCoordArrays may return larger number + // where getTexCoordArray(0) may have a BIND_OFF and an empty arrat std::vector texcoords; for ( unsigned int i = 0; i < geom->getNumTexCoordArrays(); i++ ) { - texcoords.push_back( ArrayNIndices( geom->getTexCoordArray( i ), geom->getTexCoordIndices( i ) ) ); + if (geom->getTexCoordArray(i)) + { + texcoords.push_back( ArrayNIndices( geom->getTexCoordArray( i ), geom->getTexCoordIndices( i ) ) ); + } } - - //process POSITION + std::vector vertexAttributes; + for ( unsigned int i = 0; i < geom->getNumVertexAttribArrays(); i++ ) + { + if (geom->getVertexAttribArray(i)) + { + vertexAttributes.push_back(ArrayNIndices( geom->getVertexAttribArray( i ), geom->getVertexAttribIndices(i))); + } + } + + // process POSITION std::string sName = name + "-positions"; pos = createSource( mesh, sName, verts.mode ); @@ -218,8 +557,8 @@ bool daeWriter::processGeometry( osg::Geometry *geom, domGeometry *geo, const st vertices->setId( vName.c_str() ); //make a POSITION input in it - domInputLocal *il = daeSafeCast< domInputLocal >( vertices->add( COLLADA_ELEMENT_INPUT) ); - il->setSemantic(COMMON_PROFILE_INPUT_POSITION); + domInputLocal *il = daeSafeCast< domInputLocal >( vertices->add( COLLADA_ELEMENT_INPUT ) ); + il->setSemantic( COMMON_PROFILE_INPUT_POSITION ); std::string url = "#" + std::string( pos->getId() ); il->setSource( url.c_str() ); @@ -272,8 +611,8 @@ bool daeWriter::processGeometry( osg::Geometry *geom, domGeometry *geo, const st //if NORMAL shares same indices as POSITION put it in the vertices /*if ( normalInds == vertInds && vertInds != NULL ) { - il = daeSafeCast< domInputLocal >( vertices->add( COLLADA_ELEMENT_INPUT) ); - il->setSemantic(COMMON_PROFILE_INPUT_NORMAL); + il = daeSafeCast< domInputLocal >( vertices->add( COLLADA_ELEMENT_INPUT ) ); + il->setSemantic( COMMON_PROFILE_INPUT_NORMAL ); url = "#" + std::string(md->norm->getId()); il->setSource( url.c_str() ); }*/ @@ -323,8 +662,8 @@ bool daeWriter::processGeometry( osg::Geometry *geom, domGeometry *geo, const st } //if COLOR shares same indices as POSITION put it in the vertices /*if ( colorInds == vertInds && vertInds != NULL ) { - il = daeSafeCast< domInputLocal >( vertices->add( COLLADA_ELEMENT_INPUT) ); - il->setSemantic(COMMON_PROFILE_INPUT_COLOR); + il = daeSafeCast< domInputLocal >( vertices->add( COLLADA_ELEMENT_INPUT ) ); + il->setSemantic( COMMON_PROFILE_INPUT_COLOR ); url = "#" + std::string(md->color->getId()); il->setSource( url.c_str() ); }*/ @@ -365,10 +704,10 @@ bool daeWriter::processGeometry( osg::Geometry *geom, domGeometry *geo, const st t->getTechnique_common()->getAccessor()->setCount( texcoords[ti].vec4->size() ); for ( unsigned int i = 0; i < texcoords[ti].vec4->size(); i++ ) { - t->getFloat_array()->getValue().append( (*texcoords[i].vec4)[ti].x() ); - t->getFloat_array()->getValue().append( (*texcoords[i].vec4)[ti].y() ); - t->getFloat_array()->getValue().append( (*texcoords[i].vec4)[ti].z() ); - t->getFloat_array()->getValue().append( (*texcoords[i].vec4)[ti].w() ); + t->getFloat_array()->getValue().append( (*texcoords[ti].vec4)[i].x() ); + t->getFloat_array()->getValue().append( (*texcoords[ti].vec4)[i].y() ); + t->getFloat_array()->getValue().append( (*texcoords[ti].vec4)[i].z() ); + t->getFloat_array()->getValue().append( (*texcoords[ti].vec4)[i].w() ); } break; default: @@ -379,6 +718,63 @@ bool daeWriter::processGeometry( osg::Geometry *geom, domGeometry *geo, const st texcoord.push_back( t ); } + //RS + //process TEXCOORD + //TODO: Do the same as normal and colors for texcoods. But in a loop since you can have many + for ( unsigned int ti = 0; ti < vertexAttributes.size(); ti++ ) + { + if (vertexAttributes[ti].mode != ArrayNIndices::NONE) + { + std::ostringstream intstr; + intstr << std::dec << ti; + sName = name + "-vertexAttribute_" + intstr.str(); + + domSource *t = createSource( mesh, sName, vertexAttributes[ti].mode, false, true ); + switch( vertexAttributes[ti].mode ) + { + case ArrayNIndices::VEC2: + t->getFloat_array()->setCount( vertexAttributes[ti].vec2->size() *2 ); + t->getTechnique_common()->getAccessor()->setCount( vertexAttributes[ti].vec2->size() ); + for ( unsigned int i = 0; i < vertexAttributes[ti].vec2->size(); i++ ) + { + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec2)[i].x() ); + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec2)[i].y() ); + } + break; + case ArrayNIndices::VEC3: + t->getFloat_array()->setCount( vertexAttributes[ti].vec3->size() *3 ); + t->getTechnique_common()->getAccessor()->setCount( vertexAttributes[ti].vec3->size() ); + for ( unsigned int i = 0; i < vertexAttributes[ti].vec3->size(); i++ ) + { + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec3)[i].x() ); + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec3)[i].y() ); + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec3)[i].z() ); + } + break; + case ArrayNIndices::VEC4: + t->getFloat_array()->setCount( vertexAttributes[ti].vec4->size() *4 ); + t->getTechnique_common()->getAccessor()->setCount( vertexAttributes[ti].vec4->size() ); + for ( unsigned int i = 0; i < vertexAttributes[ti].vec4->size(); i++ ) + { + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec4)[i].x() ); + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec4)[i].y() ); + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec4)[i].z() ); + t->getFloat_array()->getValue().append( (*vertexAttributes[ti].vec4)[i].w() ); + } + break; + default: + //##ti and not i + osg::notify( osg::WARN ) << "Invalid array type for vertex attribute" << ti << std::endl; + break; + } + vertexAttribute.push_back( t ); + } + else + { + osg::notify( osg::WARN ) << "Invalid array type for vertex attribute" << ti << std::endl; + } + } + //process each primitive group unsigned int ncount = 0; //Normal index counter unsigned int ccount = 0; //Color index counter @@ -1135,32 +1531,32 @@ Ty *daeWriter::createPrimGroup( daeString type, domMesh *mesh, domSource *norm, { unsigned int offset = 0; Ty *retVal = daeSafeCast< Ty >( mesh->add( type ) ); - domInputLocalOffset *ilo = daeSafeCast< domInputLocalOffset >(retVal->add( COLLADA_ELEMENT_INPUT)); + domInputLocalOffset *ilo = daeSafeCast< domInputLocalOffset >( retVal->add( COLLADA_ELEMENT_INPUT ) ); ilo->setOffset( offset++ ); - ilo->setSemantic(COMMON_PROFILE_INPUT_VERTEX); + ilo->setSemantic( COMMON_PROFILE_INPUT_VERTEX ); std::string url = "#" + std::string(mesh->getVertices()->getId()); ilo->setSource( url.c_str() ); if ( norm != NULL ) { - ilo = daeSafeCast< domInputLocalOffset >( retVal->add(COLLADA_ELEMENT_INPUT)); + ilo = daeSafeCast< domInputLocalOffset >( retVal->add( COLLADA_ELEMENT_INPUT ) ); ilo->setOffset( offset++ ); - ilo->setSemantic( COMMON_PROFILE_INPUT_NORMAL); + ilo->setSemantic( COMMON_PROFILE_INPUT_NORMAL ); url = "#" + std::string( norm->getId() ); ilo->setSource( url.c_str() ); } if ( color != NULL ) { - ilo = daeSafeCast< domInputLocalOffset >( retVal->add(COLLADA_ELEMENT_INPUT)); + ilo = daeSafeCast< domInputLocalOffset >( retVal->add( COLLADA_ELEMENT_INPUT ) ); ilo->setOffset( offset++ ); - ilo->setSemantic(COMMON_PROFILE_INPUT_COLOR); + ilo->setSemantic( COMMON_PROFILE_INPUT_COLOR ); url = "#" + std::string( color->getId() ); ilo->setSource( url.c_str() ); } for ( unsigned int i = 0; i < texcoord.size(); i++ ) { - ilo = daeSafeCast< domInputLocalOffset >( retVal->add(COLLADA_ELEMENT_INPUT)); + ilo = daeSafeCast< domInputLocalOffset >( retVal->add( COLLADA_ELEMENT_INPUT ) ); ilo->setOffset( offset++ ); - ilo->setSemantic(COMMON_PROFILE_INPUT_TEXCOORD); + ilo->setSemantic( COMMON_PROFILE_INPUT_TEXCOORD ); ilo->setSet( i ); url = "#" + std::string( texcoord[i]->getId() ); ilo->setSource( url.c_str() ); diff --git a/src/osgPlugins/dae/daeWMaterials.cpp b/src/osgPlugins/dae/daeWMaterials.cpp index 048b745f0..4f53636ec 100644 --- a/src/osgPlugins/dae/daeWMaterials.cpp +++ b/src/osgPlugins/dae/daeWMaterials.cpp @@ -28,22 +28,22 @@ #include "windows.h" #endif -using namespace osgdae; +using namespace osgDAE; -void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, const std::string &geoName ) +void daeWriter::processMaterial( osg::StateSet *ss, domBind_material *pDomBindMaterial, const std::string &geoName ) { osg::ref_ptr ssClean = CleanStateSet(ss); // Need to hold a ref to this or the materialMap.find() will delete it - domBind_material *bm = daeSafeCast< domBind_material >( ig->add( COLLADA_ELEMENT_BIND_MATERIAL ) ); - domBind_material::domTechnique_common *tc = daeSafeCast< domBind_material::domTechnique_common >( bm->add(COLLADA_ELEMENT_TECHNIQUE_COMMON)); - domInstance_material *im = daeSafeCast< domInstance_material >( tc->add( COLLADA_ELEMENT_INSTANCE_MATERIAL ) ); + domBind_material::domTechnique_common *tc = daeSafeCast< domBind_material::domTechnique_common >( pDomBindMaterial->add( COLLADA_ELEMENT_TECHNIQUE_COMMON ) ); + domInstance_material *pDomInstanceMaterial = daeSafeCast< domInstance_material >( tc->add( COLLADA_ELEMENT_INSTANCE_MATERIAL ) ); std::string symbol = geoName + "_material"; - im->setSymbol( symbol.c_str() ); + pDomInstanceMaterial->setSymbol( symbol.c_str() ); + // See if material already exists in cache MaterialMap::iterator iter = materialMap.find( ssClean ); if ( iter != materialMap.end() ) { std::string url = "#" + std::string( iter->second->getId() ); - im->setTarget( url.c_str() ); + pDomInstanceMaterial->setTarget( url.c_str() ); return; } @@ -63,7 +63,7 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co mat->setId( name.c_str() ); std::string url = "#" + name; - im->setTarget( url.c_str() ); + pDomInstanceMaterial->setTarget( url.c_str() ); domInstance_effect *ie = daeSafeCast( mat->add( COLLADA_ELEMENT_INSTANCE_EFFECT ) ); @@ -82,7 +82,7 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co domProfile_COMMON *pc = daeSafeCast< domProfile_COMMON >( effect->add( COLLADA_ELEMENT_PROFILE_COMMON ) ); domProfile_COMMON::domTechnique *pc_teq = daeSafeCast< domProfile_COMMON::domTechnique >( pc->add( COLLADA_ELEMENT_TECHNIQUE ) ); pc_teq->setSid( "t0" ); - domProfile_COMMON::domTechnique::domPhong *phong = daeSafeCast< domProfile_COMMON::domTechnique::domPhong >( pc_teq->add(COLLADA_ELEMENT_PHONG)); + domProfile_COMMON::domTechnique::domPhong *phong = daeSafeCast< domProfile_COMMON::domTechnique::domPhong >( pc_teq->add( COLLADA_ELEMENT_PHONG ) ); osg::Texture *tex = static_cast(ssClean->getTextureAttribute( 0, osg::StateAttribute::TEXTURE )); if ( ssClean->getTextureAttribute( 1, osg::StateAttribute::TEXTURE ) != NULL ) @@ -97,31 +97,33 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co img->setId( iName.c_str() ); osg::Image *osgimg = tex->getImage( 0 ); - domImage::domInit_from *imgif = daeSafeCast< domImage::domInit_from >( img->add(COLLADA_ELEMENT_INIT_FROM)); + + domImage::domInit_from *imgif = daeSafeCast< domImage::domInit_from >( img->add( COLLADA_ELEMENT_INIT_FROM ) ); std::string fileURI = ReaderWriterDAE::ConvertFilePathToColladaCompatibleURI(osgDB::findDataFile(osgimg->getFileName())); - daeURI dd(*dae, fileURI);//fileURI.c_str() ); + daeURI dd(*dae, fileURI); imgif->setValue( dd ); // The document URI should contain the canonical path it was created with imgif->getValue().makeRelativeTo(doc->getDocumentURI()); + #ifndef EARTH_TEX - domCommon_newparam_type *np = daeSafeCast< domCommon_newparam_type >(pc->add(COLLADA_ELEMENT_NEWPARAM)); + domCommon_newparam_type *np = daeSafeCast< domCommon_newparam_type >( pc->add(COLLADA_ELEMENT_NEWPARAM) ); std::string surfName = efName + "-surface"; np->setSid( surfName.c_str() ); - domFx_surface_common *surface = daeSafeCast< domFx_surface_common >(np->add(COLLADA_ELEMENT_SURFACE)); - domFx_surface_init_from_common *sif = daeSafeCast< domFx_surface_init_from_common >( surface->add(COLLADA_ELEMENT_INIT_FROM)); + domFx_surface_common *surface = daeSafeCast< domFx_surface_common >( np->add(COLLADA_ELEMENT_SURFACE) ); + domFx_surface_init_from_common *sif = daeSafeCast< domFx_surface_init_from_common >( surface->add(COLLADA_ELEMENT_INIT_FROM) ); sif->setValue( iName.c_str() ); surface->setType( FX_SURFACE_TYPE_ENUM_2D ); - np = daeSafeCast< domCommon_newparam_type >( pc->add(COLLADA_ELEMENT_NEWPARAM)); + np = daeSafeCast< domCommon_newparam_type >( pc->add( COLLADA_ELEMENT_NEWPARAM ) ); std::string sampName = efName + "-sampler"; np->setSid( sampName.c_str() ); - domFx_sampler2D_common *sampler = daeSafeCast< domFx_sampler2D_common >( np->add(COLLADA_ELEMENT_SAMPLER2D) ); - domFx_sampler2D_common_complexType::domSource *source = daeSafeCast< domFx_sampler2D_common_complexType::domSource >(sampler->add(COLLADA_ELEMENT_SOURCE)); + domFx_sampler2D_common *sampler = daeSafeCast< domFx_sampler2D_common >( np->add( COLLADA_ELEMENT_SAMPLER2D ) ); + domFx_sampler2D_common_complexType::domSource *source = daeSafeCast< domFx_sampler2D_common_complexType::domSource >( sampler->add( COLLADA_ELEMENT_SOURCE ) ); source->setValue( surfName.c_str() ); //set sampler state - domFx_sampler2D_common_complexType::domWrap_s *wrap_s = daeSafeCast< domFx_sampler2D_common_complexType::domWrap_s >(sampler->add(COLLADA_ELEMENT_WRAP_S)); + domFx_sampler2D_common_complexType::domWrap_s *wrap_s = daeSafeCast< domFx_sampler2D_common_complexType::domWrap_s >( sampler->add( COLLADA_ELEMENT_WRAP_S ) ); osg::Texture::WrapMode wrap = tex->getWrap( osg::Texture::WRAP_S ); switch( wrap ) { @@ -143,7 +145,7 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co break; } - domFx_sampler2D_common_complexType::domWrap_t *wrap_t = daeSafeCast< domFx_sampler2D_common_complexType::domWrap_t >(sampler->add( COLLADA_ELEMENT_WRAP_T)); + domFx_sampler2D_common_complexType::domWrap_t *wrap_t = daeSafeCast< domFx_sampler2D_common_complexType::domWrap_t >( sampler->add( COLLADA_ELEMENT_WRAP_T ) ); wrap = tex->getWrap( osg::Texture::WRAP_T ); switch( wrap ) { @@ -166,13 +168,13 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co } const osg::Vec4 &bcol = tex->getBorderColor(); - domFx_sampler2D_common_complexType::domBorder_color *dbcol = daeSafeCast< domFx_sampler2D_common_complexType::domBorder_color >(sampler->add(COLLADA_ELEMENT_BORDER_COLOR)); + domFx_sampler2D_common_complexType::domBorder_color *dbcol = daeSafeCast< domFx_sampler2D_common_complexType::domBorder_color >( sampler->add( COLLADA_ELEMENT_BORDER_COLOR ) ); dbcol->getValue().append( bcol.r() ); dbcol->getValue().append( bcol.g() ); dbcol->getValue().append( bcol.b() ); dbcol->getValue().append( bcol.a() ); - domFx_sampler2D_common_complexType::domMinfilter *minfilter = daeSafeCast< domFx_sampler2D_common_complexType::domMinfilter >(sampler->add(COLLADA_ELEMENT_MINFILTER)); + domFx_sampler2D_common_complexType::domMinfilter *minfilter = daeSafeCast< domFx_sampler2D_common_complexType::domMinfilter >( sampler->add( COLLADA_ELEMENT_MINFILTER ) ); osg::Texture::FilterMode mode = tex->getFilter( osg::Texture::MIN_FILTER ); switch( mode ) { @@ -196,7 +198,7 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co break; } - domFx_sampler2D_common_complexType::domMagfilter *magfilter = daeSafeCast< domFx_sampler2D_common_complexType::domMagfilter >(sampler->add(COLLADA_ELEMENT_MAGFILTER)); + domFx_sampler2D_common_complexType::domMagfilter *magfilter = daeSafeCast< domFx_sampler2D_common_complexType::domMagfilter >( sampler->add( COLLADA_ELEMENT_MAGFILTER ) ); mode = tex->getFilter( osg::Texture::MAG_FILTER ); switch( mode ) { @@ -221,20 +223,20 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co } - domCommon_color_or_texture_type *cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add(COLLADA_ELEMENT_DIFFUSE) ); - domCommon_color_or_texture_type_complexType::domTexture *dtex = daeSafeCast< domCommon_color_or_texture_type_complexType::domTexture >(cot->add(COLLADA_ELEMENT_TEXTURE)); + domCommon_color_or_texture_type *cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add( COLLADA_ELEMENT_DIFFUSE ) ); + domCommon_color_or_texture_type_complexType::domTexture *dtex = daeSafeCast< domCommon_color_or_texture_type_complexType::domTexture >( cot->add( COLLADA_ELEMENT_TEXTURE ) ); dtex->setTexture( sampName.c_str() ); dtex->setTexcoord( "texcoord0" ); #else - domCommon_color_or_texture_type *cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add(COLLADA_ELEMENT_DIFFUSE) ); - domCommon_color_or_texture_type_complexType::domTexture *dtex = daeSafeCast< domCommon_color_or_texture_type_complexType::domTexture >( cot->add(COLLADA_ELEMENT_TEXTURE)); + domCommon_color_or_texture_type *cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add( COLLADA_ELEMENT_DIFFUSE ) ); + domCommon_color_or_texture_type_complexType::domTexture *dtex = daeSafeCast< domCommon_color_or_texture_type_complexType::domTexture >( cot->add( COLLADA_ELEMENT_TEXTURE ) ); dtex->setTexture( iName.c_str() ); dtex->setTexcoord( "texcoord0" ); #endif - domInstance_material::domBind_vertex_input *bvi = daeSafeCast< domInstance_material::domBind_vertex_input >(im->add(COLLADA_ELEMENT_BIND_VERTEX_INPUT)); + domInstance_material::domBind_vertex_input *bvi = daeSafeCast< domInstance_material::domBind_vertex_input >( pDomInstanceMaterial->add( COLLADA_ELEMENT_BIND_VERTEX_INPUT ) ); bvi->setSemantic( "texcoord0" ); - bvi->setInput_semantic( "TEXCOORD" ); + bvi->setInput_semantic( COMMON_PROFILE_INPUT_TEXCOORD ); bvi->setInput_set( 0 ); } osg::Material *osgmat = static_cast(ssClean->getAttribute( osg::StateAttribute::MATERIAL )); @@ -246,15 +248,15 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co const osg::Vec4 &sCol = osgmat->getSpecularFrontAndBack()?osgmat->getSpecular( osg::Material::FRONT_AND_BACK ):osgmat->getSpecular( osg::Material::FRONT ); float shininess = osgmat->getShininessFrontAndBack()?osgmat->getShininess( osg::Material::FRONT_AND_BACK ):osgmat->getShininess( osg::Material::FRONT ); - domCommon_color_or_texture_type *cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add(COLLADA_ELEMENT_EMISSION)); - domCommon_color_or_texture_type_complexType::domColor *col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add(COLLADA_ELEMENT_COLOR)); + domCommon_color_or_texture_type *cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add( COLLADA_ELEMENT_EMISSION ) ); + domCommon_color_or_texture_type_complexType::domColor *col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add( COLLADA_ELEMENT_COLOR ) ); col->getValue().append( eCol.r() ); col->getValue().append( eCol.g() ); col->getValue().append( eCol.b() ); col->getValue().append( eCol.a() ); - cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add(COLLADA_ELEMENT_AMBIENT)); - col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >(cot->add(COLLADA_ELEMENT_COLOR)); + cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add( COLLADA_ELEMENT_AMBIENT ) ); + col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add( COLLADA_ELEMENT_COLOR ) ); col->getValue().append( aCol.r() ); col->getValue().append( aCol.g() ); col->getValue().append( aCol.b() ); @@ -264,8 +266,8 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co //### check if we really have a texture if ( phong->getDiffuse() == NULL ) { - cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add(COLLADA_ELEMENT_DIFFUSE)); - col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add(COLLADA_ELEMENT_COLOR)); + cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add( COLLADA_ELEMENT_DIFFUSE ) ); + col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add( COLLADA_ELEMENT_COLOR ) ); col->getValue().append( dCol.r() ); col->getValue().append( dCol.g() ); col->getValue().append( dCol.b() ); @@ -290,7 +292,7 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co extra->setType( "color" ); domTechnique *teq = daeSafeCast< domTechnique >( extra->add( COLLADA_ELEMENT_TECHNIQUE ) ); teq->setProfile( "SCEI" ); - domAny *any = (domAny*)(daeElement*)teq->add(COLLADA_ELEMENT_COLOR); + domAny *any = (domAny*)(daeElement*)teq->add( COLLADA_ELEMENT_COLOR ); std::ostringstream colVal; colVal << dCol.r() << " " << dCol.g() << " " << dCol.b() << " " << dCol.a(); @@ -298,15 +300,15 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co } } - cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add(COLLADA_ELEMENT_SPECULAR)); - col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add(COLLADA_ELEMENT_COLOR)); + cot = daeSafeCast< domCommon_color_or_texture_type >( phong->add( COLLADA_ELEMENT_SPECULAR ) ); + col = daeSafeCast< domCommon_color_or_texture_type_complexType::domColor >( cot->add( COLLADA_ELEMENT_COLOR ) ); col->getValue().append( sCol.r() ); col->getValue().append( sCol.g() ); col->getValue().append( sCol.b() ); col->getValue().append( sCol.a() ); - domCommon_float_or_param_type *fop = daeSafeCast< domCommon_float_or_param_type >( phong->add(COLLADA_ELEMENT_SHININESS)); - domCommon_float_or_param_type_complexType::domFloat *f = daeSafeCast< domCommon_float_or_param_type_complexType::domFloat >( fop->add(COLLADA_ELEMENT_FLOAT)); + domCommon_float_or_param_type *fop = daeSafeCast< domCommon_float_or_param_type >( phong->add( COLLADA_ELEMENT_SHININESS ) ); + domCommon_float_or_param_type_complexType::domFloat *f = daeSafeCast< domCommon_float_or_param_type_complexType::domFloat >( fop->add(COLLADA_ELEMENT_FLOAT) ); f->setValue( shininess ); } @@ -364,9 +366,9 @@ void daeWriter::processMaterial( osg::StateSet *ss, domInstance_geometry *ig, co } else if (tex != NULL && tex->getImage( 0 ) != NULL) { - domCommon_transparent_type *ctt = daeSafeCast< domCommon_transparent_type >( phong->add(COLLADA_ELEMENT_TRANSPARENT)); + domCommon_transparent_type *ctt = daeSafeCast< domCommon_transparent_type >( phong->add(COLLADA_ELEMENT_TRANSPARENT) ); ctt->setOpaque( FX_OPAQUE_ENUM_A_ONE ); - domCommon_color_or_texture_type_complexType::domTexture * dtex = daeSafeCast< domCommon_color_or_texture_type_complexType::domTexture >( ctt->add(COLLADA_ELEMENT_TEXTURE)); + domCommon_color_or_texture_type_complexType::domTexture * dtex = daeSafeCast< domCommon_color_or_texture_type_complexType::domTexture >( ctt->add(COLLADA_ELEMENT_TEXTURE) ); #ifndef EARTH_TEX std::string sampName = efName + "-sampler"; diff --git a/src/osgPlugins/dae/daeWSceneObjects.cpp b/src/osgPlugins/dae/daeWSceneObjects.cpp index a52230f6d..c5eb986df 100644 --- a/src/osgPlugins/dae/daeWSceneObjects.cpp +++ b/src/osgPlugins/dae/daeWSceneObjects.cpp @@ -28,7 +28,7 @@ #include #include -using namespace osgdae; +using namespace osgDAE; // Write non-standard node data as extra of type "Node" with "OpenSceneGraph" technique void daeWriter::writeNodeExtra(osg::Node &node) @@ -128,6 +128,8 @@ void daeWriter::apply( osg::Group &node ) } else { + writeAnimations(node); + currentNode->setId(getNodeName(node,"group").c_str()); } @@ -353,19 +355,21 @@ void daeWriter::apply( osg::LightSource &node ) { debugPrint( node ); - domInstance_light *pDomInstanceLight = daeSafeCast< domInstance_light >( currentNode->add(COLLADA_ELEMENT_INSTANCE_LIGHT)); + domInstance_light *il = daeSafeCast< domInstance_light >( currentNode->add( COLLADA_ELEMENT_INSTANCE_LIGHT ) ); std::string name = node.getName(); if ( name.empty() ) { name = uniquify( "light" ); } std::string url = "#" + name; - pDomInstanceLight->setUrl( url.c_str() ); + il->setUrl( url.c_str() ); if ( lib_lights == NULL ) { lib_lights = daeSafeCast< domLibrary_lights >( dom->add( COLLADA_ELEMENT_LIBRARY_LIGHTS ) ); } + domLight *light = daeSafeCast< domLight >( lib_lights->add( COLLADA_ELEMENT_LIGHT ) ); + light->setId( name.c_str() ); osg::Light* pOsgLight = node.getLight(); diff --git a/src/osgPlugins/dae/daeWTransforms.cpp b/src/osgPlugins/dae/daeWTransforms.cpp index 8c1234a6a..7918cc1ea 100644 --- a/src/osgPlugins/dae/daeWTransforms.cpp +++ b/src/osgPlugins/dae/daeWTransforms.cpp @@ -1,14 +1,14 @@ /* * Copyright 2006 Sony Computer Entertainment Inc. * - * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * http://research.scea.com/scea_shared_source_license.html * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - * implied. See the License for the specific language governing permissions and limitations under the - * License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. */ #include "daeWriter.h" @@ -19,9 +19,42 @@ #include #include +#include #include -using namespace osgdae; +using namespace osgDAE; + + +void daeWriter::writeUpdateTransformElements(const osg::Vec3 &pos, const osg::Quat &q, const osg::Vec3 &s) +{ + // Make a scale place element + domScale *scale = daeSafeCast< domScale >( currentNode->add( COLLADA_ELEMENT_SCALE ) ); + scale->setSid("scale"); + scale->getValue().append3( s.x(), s.y(), s.z() ); + + // Make a three rotate place elements for the euler angles + // TODO decompose quaternion into three euler angles + double angle; + osg::Vec3 axis; + q.getRotate( angle, axis ); + + domRotate *rot = daeSafeCast< domRotate >( currentNode->add( COLLADA_ELEMENT_ROTATE ) ); + rot->setSid("rotateZ"); + rot->getValue().append4( 0, 0, 1, osg::RadiansToDegrees(angle) ); + + rot = daeSafeCast< domRotate >( currentNode->add( COLLADA_ELEMENT_ROTATE ) ); + rot->setSid("rotateY"); + rot->getValue().append4( 0, 1, 0, osg::RadiansToDegrees(angle) ); + + rot = daeSafeCast< domRotate >( currentNode->add( COLLADA_ELEMENT_ROTATE ) ); + rot->setSid("rotateX"); + rot->getValue().append4( 1, 0, 0, osg::RadiansToDegrees(angle) ); + + // Make a translate place element + domTranslate *trans = daeSafeCast< domTranslate >( currentNode->add( COLLADA_ELEMENT_TRANSLATE ) ); + trans->setSid("translate"); + trans->getValue().append3( pos.x(), pos.y(), pos.z() ); +} //MATRIX void daeWriter::apply( osg::MatrixTransform &node ) @@ -38,30 +71,43 @@ void daeWriter::apply( osg::MatrixTransform &node ) } currentNode = daeSafeCast< domNode >(currentNode->add( COLLADA_ELEMENT_NODE ) ); - currentNode->setId(getNodeName(node,"matrixTransform").c_str()); - - domMatrix *mat = daeSafeCast< domMatrix >(currentNode->add( COLLADA_ELEMENT_MATRIX ) ); - const osg::Matrix::value_type *mat_vals = node.getMatrix().ptr(); - //for ( int i = 0; i < 16; i++ ) - //{ - // mat->getValue().append( mat_vals[i] ); - //} - mat->getValue().append( mat_vals[0] ); - mat->getValue().append( mat_vals[4] ); - mat->getValue().append( mat_vals[8] ); - mat->getValue().append( mat_vals[12] ); - mat->getValue().append( mat_vals[1] ); - mat->getValue().append( mat_vals[5] ); - mat->getValue().append( mat_vals[9] ); - mat->getValue().append( mat_vals[13] ); - mat->getValue().append( mat_vals[2] ); - mat->getValue().append( mat_vals[6] ); - mat->getValue().append( mat_vals[10] ); - mat->getValue().append( mat_vals[14] ); - mat->getValue().append( mat_vals[3] ); - mat->getValue().append( mat_vals[7] ); - mat->getValue().append( mat_vals[11] ); - mat->getValue().append( mat_vals[15] ); + std::string nodeName = getNodeName(node,"matrixTransform"); + currentNode->setId(nodeName.c_str()); + + osg::NodeCallback* ncb = node.getUpdateCallback(); + bool handled = false; + if (ncb) + { + osgAnimation::UpdateMatrixTransform* ut = dynamic_cast(ncb); + // If targeted by an animation we split up the matrix into multiple place element so they can be targeted individually + if (ut) + { + handled = true; + + const osg::Matrix &mat = node.getMatrix(); + + // Note: though this is a generic matrix, based on the fact that it will be animated by and UpdateMatrixTransform, + // we assume the initial matrix can be decomposed into translation, rotation and scale elements + writeUpdateTransformElements(mat.getTrans(), mat.getRotate(), mat.getScale()); + } + } + + // If not targeted by an animation simply write a single matrix place element + if (!handled) + { + domMatrix *mat = daeSafeCast< domMatrix >(currentNode->add( COLLADA_ELEMENT_MATRIX ) ); + nodeName += "_matrix"; + mat->setSid(nodeName.c_str()); + + const osg::Matrix::value_type *mat_vals = node.getMatrix().ptr(); + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < 4; j++ ) + { + mat->getValue().append( mat_vals[i + j*4] ); + } + } + } lastDepth = _nodePath.size(); @@ -84,41 +130,56 @@ void daeWriter::apply( osg::PositionAttitudeTransform &node ) lastDepth--; } currentNode = daeSafeCast< domNode >(currentNode->add( COLLADA_ELEMENT_NODE ) ); - currentNode->setId(getNodeName(node,"positionAttitudeTransform").c_str()); - + std::string nodeName = getNodeName(node,"positionAttitudeTransform"); + currentNode->setId(nodeName.c_str()); + const osg::Vec3 &pos = node.getPosition(); const osg::Quat &q = node.getAttitude(); const osg::Vec3 &s = node.getScale(); - if ( pos.x() != 0 || pos.y() != 0 || pos.z() != 0 ) + osg::NodeCallback* ncb = node.getUpdateCallback(); + bool handled = false; + if (ncb) { - //make a translate - domTranslate *trans = daeSafeCast< domTranslate >( currentNode->add( COLLADA_ELEMENT_TRANSLATE ) ); - trans->getValue().append( pos.x() ); - trans->getValue().append( pos.y() ); - trans->getValue().append( pos.z() ); + osgAnimation::UpdateMatrixTransform* ut = dynamic_cast(ncb); + // If targeted by an animation we split up the matrix into multiple place element so they can be targeted individually + if (ut) + { + handled = true; + + writeUpdateTransformElements(pos, q, s); + } } - double angle; - osg::Vec3 axis; - q.getRotate( angle, axis ); - if ( angle != 0 ) + // If not targeted by an animation simply add the elements that actually contribute to placement + if (!handled) { - //make a rotate - domRotate *rot = daeSafeCast< domRotate >( currentNode->add( COLLADA_ELEMENT_ROTATE ) ); - rot->getValue().append( axis.x() ); - rot->getValue().append( axis.y() ); - rot->getValue().append( axis.z() ); - rot->getValue().append( osg::RadiansToDegrees(angle) ); - } + if ( s.x() != 1 || s.y() != 1 || s.z() != 1 ) + { + // Make a scale place element + domScale *scale = daeSafeCast< domScale >( currentNode->add( COLLADA_ELEMENT_SCALE ) ); + scale->setSid("scale"); + scale->getValue().append3( s.x(), s.y(), s.z() ); + } + + double angle; + osg::Vec3 axis; + q.getRotate( angle, axis ); + if ( angle != 0 ) + { + // Make a rotate place element + domRotate *rot = daeSafeCast< domRotate >( currentNode->add( COLLADA_ELEMENT_ROTATE ) ); + rot->setSid("rotate"); + rot->getValue().append4( axis.x(), axis.y(), axis.z(), osg::RadiansToDegrees(angle) ); + } if ( s.x() != 1 || s.y() != 1 || s.z() != 1 ) - { - //make a scale - domScale *scale = daeSafeCast< domScale >( currentNode->add( COLLADA_ELEMENT_SCALE ) ); - scale->getValue().append( s.x() ); - scale->getValue().append( s.y() ); - scale->getValue().append( s.z() ); + { + // Make a translate place element + domTranslate *trans = daeSafeCast< domTranslate >( currentNode->add( COLLADA_ELEMENT_TRANSLATE ) ); + trans->setSid("translate"); + trans->getValue().append3( pos.x(), pos.y(), pos.z() ); + } } writeNodeExtra(node); @@ -128,7 +189,7 @@ void daeWriter::apply( osg::PositionAttitudeTransform &node ) traverse( node ); } -void daeWriter::apply( osg::Transform &node ) +void daeWriter::apply( osg::Transform &node ) { debugPrint( node ); @@ -139,7 +200,7 @@ void daeWriter::apply( osg::Transform &node ) lastDepth--; } currentNode = daeSafeCast< domNode >(currentNode->add( COLLADA_ELEMENT_NODE ) ); - + // If a DOFTransform node store it's data as extra "DOFTransform" data in the "OpenSceneGraph" technique osgSim::DOFTransform* dof = dynamic_cast(&node); if (writeExtras && dof) @@ -223,14 +284,24 @@ void daeWriter::apply( osg::Transform &node ) animationOn->setValue(toString(dof->getAnimationOn()).c_str()); domAny *putMatrix = (domAny*)teq->add("PutMatrix" ); - putMatrix->setValue(toString(dof->getPutMatrix()).c_str()); + putMatrix->setValue(toString(dof->getPutMatrix()).c_str()); currentNode->setId(getNodeName(node, "doftransform").c_str()); } else { - currentNode->setId(getNodeName(node, "transform").c_str()); - osg::notify( osg::WARN ) << "some other transform type. Missing " << node.getNumChildren() << " children" << std::endl; + osgAnimation::Bone* bone = dynamic_cast(&node); + if (bone) + { + domNode *pDomNode = daeSafeCast< domNode >(currentNode->add( COLLADA_ELEMENT_NODE )); + pDomNode->setType(NODETYPE_JOINT); + pDomNode->setId(getNodeName(node, "bone").c_str()); + } + else + { + currentNode->setId(getNodeName(node, "transform").c_str()); + osg::notify( osg::WARN ) << "some other transform type. Missing " << node.getNumChildren() << " children" << std::endl; + } } writeNodeExtra(node); @@ -240,7 +311,9 @@ void daeWriter::apply( osg::Transform &node ) traverse( node ); } -void daeWriter::apply( osg::CoordinateSystemNode &node ) +void daeWriter::apply( osg::CoordinateSystemNode &node ) { osg::notify( osg::WARN ) << "CoordinateSystemNode. Missing " << node.getNumChildren() << " children" << std::endl; } + + diff --git a/src/osgPlugins/dae/daeWriter.cpp b/src/osgPlugins/dae/daeWriter.cpp index d45d07d89..2575d0822 100644 --- a/src/osgPlugins/dae/daeWriter.cpp +++ b/src/osgPlugins/dae/daeWriter.cpp @@ -21,16 +21,9 @@ #include -namespace osgdae { +namespace osgDAE { -std::string toString(osg::Vec3f value) -{ - std::stringstream str; - str << value.x() << " " << value.y() << " " << value.z(); - return str.str(); -} - -std::string toString(osg::Vec3d value) +std::string toString(osg::Vec3 value) { std::stringstream str; str << value.x() << " " << value.y() << " " << value.z(); @@ -50,11 +43,11 @@ std::string toString(osg::Matrix value) daeWriter::daeWriter( DAE *dae_, const std::string &fileURI, bool _usePolygons, bool GoogleMode, TraversalMode tm, bool _writeExtras) : osg::NodeVisitor( tm ), dae(dae_), + _domLibraryAnimations(NULL), writeExtras(_writeExtras), rootName(*dae_), usePolygons (_usePolygons), - m_GoogleMode(GoogleMode), - m_CurrentRenderingHint(osg::StateSet::DEFAULT_BIN) + m_GoogleMode(GoogleMode) { success = true; @@ -64,12 +57,12 @@ daeWriter::daeWriter( DAE *dae_, const std::string &fileURI, bool _usePolygons, dae->getDatabase()->createDocument( fileURI.c_str(), &doc ); dom = (domCOLLADA*)doc->getDomRoot(); //create scene and instance visual scene - domCOLLADA::domScene *scene = daeSafeCast< domCOLLADA::domScene >( dom->add(COLLADA_ELEMENT_SCENE)); - domInstanceWithExtra *ivs = daeSafeCast< domInstanceWithExtra >( scene->add(COLLADA_ELEMENT_INSTANCE_VISUAL_SCENE)); + domCOLLADA::domScene *scene = daeSafeCast< domCOLLADA::domScene >( dom->add( COLLADA_ELEMENT_SCENE ) ); + domInstanceWithExtra *ivs = daeSafeCast< domInstanceWithExtra >( scene->add( COLLADA_ELEMENT_INSTANCE_VISUAL_SCENE ) ); ivs->setUrl( "#defaultScene" ); //create library visual scenes and a visual scene and the root node - lib_vis_scenes = daeSafeCast( dom->add(COLLADA_ELEMENT_LIBRARY_VISUAL_SCENES)); - vs = daeSafeCast< domVisual_scene >( lib_vis_scenes->add(COLLADA_ELEMENT_VISUAL_SCENE)); + lib_vis_scenes = daeSafeCast( dom->add( COLLADA_ELEMENT_LIBRARY_VISUAL_SCENES ) ); + vs = daeSafeCast< domVisual_scene >( lib_vis_scenes->add( COLLADA_ELEMENT_VISUAL_SCENE ) ); vs->setId( "defaultScene" ); currentNode = daeSafeCast< domNode >( vs->add( COLLADA_ELEMENT_NODE ) ); currentNode->setId( "sceneRoot" ); @@ -79,12 +72,16 @@ daeWriter::daeWriter( DAE *dae_, const std::string &fileURI, bool _usePolygons, lib_cameras = NULL; lib_effects = NULL; + lib_controllers = NULL; lib_geoms = NULL; lib_lights = NULL; lib_mats = NULL; lastDepth = 0; + // Clean up caches + uniqueNames.clear(); + currentStateSet = new osg::StateSet(); } @@ -104,20 +101,12 @@ void daeWriter::debugPrint( osg::Node &node ) #endif } -bool daeWriter::writeFile() -{ - if ( dae->save( (daeUInt)0 ) != DAE_OK ) - { - success = false; - } - return success; -} void daeWriter::setRootNode( const osg::Node &node ) { std::string fname = osgDB::findDataFile( node.getName() ); - //rootName = fname.c_str(); - //rootName.validate(); + + const_cast(&node)->accept( _animatedNodeCollector ); } //### provide a name to node @@ -166,6 +155,8 @@ void daeWriter::createAssetTag() domAsset::domCreated *c = daeSafeCast< domAsset::domCreated >(asset->add(COLLADA_ELEMENT_CREATED)); domAsset::domModified *m = daeSafeCast< domAsset::domModified >(asset->add(COLLADA_ELEMENT_MODIFIED)); domAsset::domUnit *u = daeSafeCast< domAsset::domUnit >(asset->add(COLLADA_ELEMENT_UNIT)); + domAsset::domUp_axis *up = daeSafeCast< domAsset::domUp_axis >(asset->add(COLLADA_ELEMENT_UP_AXIS)); + up->setValue(UPAXISTYPE_Z_UP); //TODO : set date and time c->setValue( "2006-07-25T00:00:00Z" ); diff --git a/src/osgPlugins/dae/daeWriter.h b/src/osgPlugins/dae/daeWriter.h index aed2d2ff2..fa001e8a3 100644 --- a/src/osgPlugins/dae/daeWriter.h +++ b/src/osgPlugins/dae/daeWriter.h @@ -1,14 +1,14 @@ /* * Copyright 2006 Sony Computer Entertainment Inc. * - * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this * file except in compliance with the License. You may obtain a copy of the License at: * http://research.scea.com/scea_shared_source_license.html * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - * implied. See the License for the specific language governing permissions and limitations under the - * License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. */ #ifndef _DAE_WRITER_H_ @@ -40,9 +40,14 @@ #include #include #include +#include +#include +#include +#include #include #include +#include class domCOLLADA; @@ -54,6 +59,7 @@ class domLibrary_geometries; class domLibrary_lights; class domLibrary_materials; class domLibrary_visual_scenes; +class domLibrary_animations; class domMaterial; class domMesh; class domNode; @@ -61,7 +67,7 @@ class domSource; class domVisual_scene; class domP; -namespace osgdae { +namespace osgDAE { /// Convert value to string using it's stream operator template @@ -74,11 +80,56 @@ std::string toString(T value) { std::string toString(osg::Vec3f value); std::string toString(osg::Vec3d value); std::string toString(osg::Matrix value); - + +// Collects all nodes that are targeted by an animation +class FindAnimatedNodeVisitor : public osg::NodeVisitor +{ +public: + FindAnimatedNodeVisitor(): + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) + {} + + virtual void apply(osg::Node& node) + { + osg::NodeCallback* ncb = node.getUpdateCallback(); + if (ncb) + { + osgAnimation::AnimationUpdateCallback* ut = dynamic_cast*>(ncb); + if (ut) + { + if (_updateCallbackNameNodeMap[ut->getName()] == NULL) + { + _updateCallbackNameNodeMap[ut->getName()] = &node; + } + else + { + // TODO store in a multimap and let the exporter create multiple s for each connected node + osg::notify( osg::WARN ) << "Multiple nodes using the same update callback not supported" << std::endl; + } + } + } + traverse(node); + } + + osg::Node* getTargetNode(const std::string& targetName) + { + UpdateCallbackNameNodeMap::iterator it = _updateCallbackNameNodeMap.find(targetName); + if (it != _updateCallbackNameNodeMap.end()) + { + return it->second; + } + return NULL; + } + +private: + typedef std::map< std::string, osg::Node*> UpdateCallbackNameNodeMap; + UpdateCallbackNameNodeMap _updateCallbackNameNodeMap; +}; + /** @class daeWriter -@brief Write a OSG scene into a DAE file -*/ +@brief Write a OSG scene into a DAE file +*/ class daeWriter : public osg::NodeVisitor { protected: @@ -116,33 +167,28 @@ public: //virtual void apply( osg::ClearNode &node) //virtual void apply( osg::OccluderNode &node) - void writeNodeExtra(osg::Node &node); - - - void traverse (osg::Node &node); - -/*protected: - struct MeshData { - domMesh *mesh; - domSource *pos; - domSource *norm; - domSource *color; - std::vector< domSource * > texcoord; - std::string name; - };*/ + + protected: //methods + + void writeAnimations(osg::Node& node); + void writeNodeExtra(osg::Node &node); + void writeUpdateTransformElements(const osg::Vec3 &pos, const osg::Quat &q, const osg::Vec3 &s); + void writeRigGeometry(osgAnimation::RigGeometry *pOsgRigGeometry); + void writeMorphGeometry(osgAnimation::MorphGeometry *pOsgMorphGeometry); + void debugPrint( osg::Node &node ); - - + + domGeometry* getOrCreateDomGeometry(osg::Geometry* pOsgGeometry); bool processGeometry( osg::Geometry *geom, domGeometry *geo, const std::string &name ); domSource* createSource( daeElement *parent, const std::string &baseName, int size, bool color = false, bool uv = false ); template < typename Ty > Ty *createPrimGroup( daeString type, domMesh *mesh, domSource *norm, domSource *color, const std::vector< domSource* > &texcoord ); - void processMaterial( osg::StateSet *ss, domInstance_geometry *ig, const std::string &geoName ); + void processMaterial( osg::StateSet *ss, domBind_material *pDomBindMaterial, const std::string &geoName ); void createAssetTag(); @@ -156,10 +202,12 @@ protected: //members domCOLLADA *dom; domLibrary_cameras *lib_cameras; domLibrary_effects *lib_effects; + domLibrary_controllers *lib_controllers; domLibrary_geometries *lib_geoms; domLibrary_lights *lib_lights; domLibrary_materials *lib_mats; domLibrary_visual_scenes *lib_vis_scenes; + domLibrary_animations* _domLibraryAnimations; domNode *currentNode; domVisual_scene *vs; @@ -176,19 +224,20 @@ protected: //members return ss1->compare(*ss2, true) < 0; } }; - - - std::map< std::string, int > uniqueNames; - - std::map< osg::Geometry*, domGeometry * > geometryMap; typedef std::map< osg::ref_ptr, domMaterial *, CompareStateSet> MaterialMap; + typedef std::stack > StateSetStack; + typedef std::map< osg::Geometry*, domGeometry *> OsgGeometryDomGeometryMap; + typedef std::map< osgAnimation::RigGeometry*, domController *> OsgRigGeometryDomControllerMap; + typedef std::map< osgAnimation::MorphGeometry*, domController *> OsgMorphGeometryDomControllerMap; + + std::map< std::string, int > uniqueNames; + OsgGeometryDomGeometryMap geometryMap; + OsgRigGeometryDomControllerMap _osgRigGeometryDomControllerMap; + OsgMorphGeometryDomControllerMap _osgMorphGeometryDomControllerMap; MaterialMap materialMap; - - typedef std::stack > StateSetStack; - StateSetStack stateSetStack; osg::ref_ptr currentStateSet; @@ -200,7 +249,7 @@ protected: //members osg::StateSet* CleanStateSet(osg::StateSet* pStateSet) const; protected: //inner classes - class ArrayNIndices + class ArrayNIndices { public: enum Mode { NONE = 0, VEC2 = 2, VEC3 = 3, VEC4 = 4 }; @@ -237,7 +286,7 @@ protected: //inner classes }; private: //members - + /** append elements (verts, normals, colors and texcoord) for file write */ void appendGeometryIndices(osg::Geometry *geom, domP * p, @@ -253,7 +302,7 @@ private: //members /** provide a name to node */ std::string getNodeName(const osg::Node & node,const std::string & defaultName); - + /** provide an unique name */ std::string uniquify( const std::string &name ); @@ -263,6 +312,8 @@ private: //members /** Current RenderingHint */ /** This are needed because the stateSet merge code currently does not handle it */ int m_CurrentRenderingHint; + + FindAnimatedNodeVisitor _animatedNodeCollector; }; } diff --git a/src/osgPlugins/dae/domSourceReader.cpp b/src/osgPlugins/dae/domSourceReader.cpp index 1f6e16536..f38aa1d18 100644 --- a/src/osgPlugins/dae/domSourceReader.cpp +++ b/src/osgPlugins/dae/domSourceReader.cpp @@ -15,7 +15,7 @@ #include -using namespace osgdae; +using namespace osgDAE; domSourceReader::domSourceReader() : m_array_type( None ), m_count( 0 ) {} @@ -32,7 +32,20 @@ domSourceReader::domSourceReader( domSource *src ) : m_array_type( None ), m_cou int stride = accessor->getStride(); m_count = accessor->getCount(); - switch ( stride ) { + // Only handle floats or name array for now... + daeDoubleArray* float_array = NULL; + if (src->getFloat_array()) + { + float_array = &(src->getFloat_array()->getValue()); + } + else if (src->getName_array()) + { + m_array_type = String; + return; + } + + switch (stride) + { case 1: m_array_type = Float; m_float_array = new osg::FloatArray(); @@ -49,39 +62,48 @@ domSourceReader::domSourceReader( domSource *src ) : m_array_type( None ), m_cou m_array_type = Vec4; m_vec4_array = new osg::Vec4Array(); break; + case 16: + m_array_type = Matrix; + m_matrix_array = new osg::MatrixfArray(); + break; default: osg::notify(osg::WARN)<<"Unsupported stride: "<getFloat_array() != NULL ) { - float_array = &(src->getFloat_array()->getValue()); - } - if ( !float_array ) { - osg::notify(osg::WARN)<<"No float array found"<getCount(); i++ ) { - switch ( accessor->getStride() ) { - case 1: - m_float_array->push_back( va[i] ); - break; - case 2: - m_vec2_array->push_back( osg::Vec2( va[i*2], va[i*2+1] ) ); - break; - case 3: - m_vec3_array->push_back( osg::Vec3( va[i*3], va[i*3+1], va[i*3+2] ) ); - break; - case 4: - m_vec4_array->push_back( osg::Vec4( va[i*4], va[i*4+1], va[i*4+2], va[i*4+3] ) ); - break; - default: - osg::notify(osg::WARN)<<"Unsupported stride in Source: "<getStride()<getCount(); i++ ) + { + switch ( accessor->getStride() ) + { + case 1: + m_float_array->push_back(va[i]); + break; + case 2: + m_vec2_array->push_back( osg::Vec2( va[i*2], va[i*2+1])); + break; + case 3: + m_vec3_array->push_back( osg::Vec3( va[i*3], va[i*3+1], va[i*3+2])); + break; + case 4: + m_vec4_array->push_back( osg::Vec4( va[i*4], va[i*4+1], va[i*4+2], va[i*4+3])); + break; + case 16: + m_matrix_array->push_back(osg::Matrixf( va[i*16+0], va[i*16+4], va[i*16+8], va[i*16+12], + va[i*16+1], va[i*16+5], va[i*16+9], va[i*16+13], + va[i*16+2], va[i*16+6], va[i*16+10], va[i*16+14], + va[i*16+3], va[i*16+7], va[i*16+11], va[i*16+15])); + break; + default: + osg::notify(osg::WARN) << "Unsupported stride in Source: " << accessor->getStride() << std::endl; + return; + } } } + else + { + osg::notify(osg::WARN) << "No float array found" << std::endl; + } } diff --git a/src/osgPlugins/dae/domSourceReader.h b/src/osgPlugins/dae/domSourceReader.h index 6aee08e09..227b94bf6 100644 --- a/src/osgPlugins/dae/domSourceReader.h +++ b/src/osgPlugins/dae/domSourceReader.h @@ -19,7 +19,7 @@ class domSource; -namespace osgdae { +namespace osgDAE { /** @class domSourceReader @@ -28,7 +28,7 @@ namespace osgdae { class domSourceReader { public: - enum ArrayType {None,Float,Vec2,Vec3,Vec4}; + enum ArrayType {None,Float,Vec2,Vec3,Vec4,Matrix,String}; public: @@ -44,7 +44,9 @@ public: osg::Vec3Array* getVec3Array() { return m_vec3_array.get(); }; osg::Vec4Array* getVec4Array() { return m_vec4_array.get(); }; - + + osg::MatrixfArray* getMatrixArray() { return m_matrix_array.get(); }; + int getCount() const { return m_count; }; #define ASSERT_TYPE(type) if (type!=m_array_type) { osg::notify(osg::WARN)<<"Wrong array type requested ("#type" != "< m_vec2_array; osg::ref_ptr m_vec3_array; osg::ref_ptr m_vec4_array; + osg::ref_ptr m_matrix_array; };