From bbca791251b2b61d2bafd9adaf600ade18a125f8 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Fri, 26 Feb 2010 14:41:50 +0000 Subject: [PATCH] From Michael Platings, "Here's the all-new, all-dancing DAE plugin, with support for reading osgAnimation. It's been tested with the majority of the samples in the COLLADA test repository and works with all of them either as well as, or better than, the version of the plugin currently in SVN. Known issue: vertex animation (AKA morphing) doesn't work at present, but that's a relatively unpopular method of animating so it's not high on my priority list." Follow up email: "I've been informed that the previous DAE submission didn't build on unix, so here's the submission again with the fixes. Thanks to Gregory Potdevin and Benjamin Bozou. Also, my apologies to Roland for not crediting his part in making DAE animation happen, my work was indeed built on top of his work. Thanks also to Marius Heise and of course Cedric Pinson." Changes by Robert Osfield, fixed compile issues when compile without C* automatic conversion enabled in ref_ptr<> and constructor initialization fixes to address some warnings under gcc. --- include/osg/Array | 14 +- include/osg/Matrixf | 110 +++ src/osgPlugins/dae/CMakeLists.txt | 5 +- src/osgPlugins/dae/ReaderWriterDAE.cpp | 12 +- src/osgPlugins/dae/ReaderWriterDAE.h | 7 +- src/osgPlugins/dae/daeRAnimations.cpp | 891 ++++++++++++++++++ src/osgPlugins/dae/daeRGeometry.cpp | 1140 +++++++++++++++-------- src/osgPlugins/dae/daeRMaterials.cpp | 1074 ++++++++++----------- src/osgPlugins/dae/daeRSceneObjects.cpp | 16 +- src/osgPlugins/dae/daeRSkinning.cpp | 533 +++++++++++ src/osgPlugins/dae/daeRTransforms.cpp | 256 ++--- src/osgPlugins/dae/daeReader.cpp | 483 +++++++--- src/osgPlugins/dae/daeReader.h | 281 ++++-- src/osgPlugins/dae/daeWAnimations.cpp | 483 ++++++++++ src/osgPlugins/dae/daeWGeometry.cpp | 536 +++++++++-- src/osgPlugins/dae/daeWMaterials.cpp | 84 +- src/osgPlugins/dae/daeWSceneObjects.cpp | 10 +- src/osgPlugins/dae/daeWTransforms.cpp | 195 ++-- src/osgPlugins/dae/daeWriter.cpp | 41 +- src/osgPlugins/dae/daeWriter.h | 125 ++- src/osgPlugins/dae/domSourceReader.cpp | 82 +- src/osgPlugins/dae/domSourceReader.h | 11 +- 22 files changed, 4812 insertions(+), 1577 deletions(-) create mode 100644 src/osgPlugins/dae/daeRAnimations.cpp create mode 100644 src/osgPlugins/dae/daeRSkinning.cpp create mode 100644 src/osgPlugins/dae/daeWAnimations.cpp 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; };