From ae50d8d95664ade69d881cfa993b1d3912960fe0 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 9 Mar 2009 17:38:39 +0000 Subject: [PATCH] From Roland Smeenk & Cedric Pinson, "Summary of changes: From Roland -Added MorphGeometry -Bone Bindmatrix is only calculated if needed -osgAnimation plugin now supports all available channel types (before only linear vec3 or quat channels) -osgAnimation plugin now supports MorphGeometry -osgAnimation plugin now supports animation and channel weights, animation playmode, duration and starttime -removed osgAnimationManager.cpp from CMakeList From Cedric -fixed the last_update field (it was only updated at the first update) in BasicAnimationManager.cpp - Refactore some part of MorphGeometry minor changes - Add osganimationmorph as example " --- examples/CMakeLists.txt | 1 + examples/osganimationmorph/CMakeLists.txt | 3 + .../osganimationmorph/osganimationmorph.cpp | 136 ++++ include/osgAnimation/Animation | 2 + include/osgAnimation/CubicBezier | 15 + include/osgAnimation/Keyframe | 7 + include/osgAnimation/LinkVisitor | 2 +- include/osgAnimation/MorphGeometry | 153 +++++ include/osgAnimation/Skeleton | 2 +- src/osgAnimation/AnimationManagerBase.cpp | 2 +- src/osgAnimation/BasicAnimationManager.cpp | 19 +- src/osgAnimation/Bone.cpp | 28 +- src/osgAnimation/CMakeLists.txt | 54 +- src/osgAnimation/MorphGeometry.cpp | 277 ++++++++ src/osgAnimation/Skeleton.cpp | 4 +- src/osgPlugins/osgAnimation/ReaderWriter.cpp | 598 ++++++++++++++++-- 16 files changed, 1204 insertions(+), 99 deletions(-) create mode 100644 examples/osganimationmorph/CMakeLists.txt create mode 100644 examples/osganimationmorph/osganimationmorph.cpp create mode 100644 include/osgAnimation/MorphGeometry create mode 100644 src/osgAnimation/MorphGeometry.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3651704e5..d42f043e6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -115,6 +115,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osganimationtimeline) ADD_SUBDIRECTORY(osganimationnode) ADD_SUBDIRECTORY(osganimationmakepath) + ADD_SUBDIRECTORY(osganimationmorph) ADD_SUBDIRECTORY(osganimationskinning) ADD_SUBDIRECTORY(osganimationsolid) ADD_SUBDIRECTORY(osganimationviewer) diff --git a/examples/osganimationmorph/CMakeLists.txt b/examples/osganimationmorph/CMakeLists.txt new file mode 100644 index 000000000..9e30b61ca --- /dev/null +++ b/examples/osganimationmorph/CMakeLists.txt @@ -0,0 +1,3 @@ +SET(TARGET_SRC osganimationmorph.cpp ) +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +SETUP_EXAMPLE(osganimationmorph) diff --git a/examples/osganimationmorph/osganimationmorph.cpp b/examples/osganimationmorph/osganimationmorph.cpp new file mode 100644 index 000000000..84929edf1 --- /dev/null +++ b/examples/osganimationmorph/osganimationmorph.cpp @@ -0,0 +1,136 @@ +/* -*-c++-*- + * Copyright (C) 2008 Cedric Pinson + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +struct GeometryFinder : public osg::NodeVisitor +{ + osg::ref_ptr _geom; + GeometryFinder() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + void apply(osg::Geode& geode) + { + if (_geom.valid()) + return; + for (int i = 0; i < geode.getNumDrawables(); i++) + { + osg::Geometry* geom = dynamic_cast(geode.getDrawable(i)); + if (geom) { + _geom = geom; + return; + } + } + } +}; + +osg::Geometry* getShape(const std::string& name) +{ + osg::Node* shape0 = osgDB::readNodeFile(name); + GeometryFinder finder; + shape0->accept(finder); + return finder._geom.get(); +} + + +int main (int argc, char* argv[]) +{ + osg::ArgumentParser arguments(&argc, argv); + osgViewer::Viewer viewer(arguments); + + + osgAnimation::Animation* animation = new osgAnimation::Animation; + osgAnimation::FloatLinearChannel* channel0 = new osgAnimation::FloatLinearChannel; + channel0->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(0,0.0)); + channel0->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(1,1.0)); + channel0->setTargetName("MorphNodeCallback"); + channel0->setName("0"); + + osgAnimation::FloatLinearChannel* channel1 = new osgAnimation::FloatLinearChannel; + channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(0,1.0)); + channel1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(1,0.0)); + channel1->setTargetName("MorphNodeCallback"); + channel1->setName("1"); + + animation->addChannel(channel0); + animation->addChannel(channel1); + animation->setName("Morph"); + animation->computeDuration(); + animation->setPlaymode(osgAnimation::Animation::PPONG); + osgAnimation::BasicAnimationManager* bam = new osgAnimation::BasicAnimationManager; + bam->registerAnimation(animation); + + osg::Geometry* geom0 = getShape("morphtarget_shape0.osg"); + if (!geom0) { + std::cerr << "can't read morphtarget_shape0.osg" << std::endl; + return 0; + } + + osg::Geometry* geom1 = getShape("morphtarget_shape1.osg"); + if (!geom1) { + std::cerr << "can't read morphtarget_shape1.osg" << std::endl; + return 0; + } + + // initialize with the first shape + osgAnimation::MorphGeometry* morph = new osgAnimation::MorphGeometry(*geom0); + morph->addMorphTarget(geom0); + morph->addMorphTarget(geom1); + + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + + + osg::Group* scene = new osg::Group; + scene->addUpdateCallback(bam); + + osg::Geode* geode = new osg::Geode; + geode->addDrawable(morph); + geode->addUpdateCallback(new osgAnimation::UpdateMorph("MorphNodeCallback")); + scene->addChild(geode); + + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + + // let's run ! + viewer.setSceneData( scene ); + viewer.realize(); + + bam->playAnimation(animation); + + + while (!viewer.done()) + { + viewer.frame(); + } + + osgDB::writeNodeFile(*scene, "morph_scene.osg"); + + return 0; +} + + diff --git a/include/osgAnimation/Animation b/include/osgAnimation/Animation index 550c8de23..9e5476da9 100644 --- a/include/osgAnimation/Animation +++ b/include/osgAnimation/Animation @@ -75,6 +75,8 @@ namespace osgAnimation void resetTargets(); void setPlaymode (PlayMode mode) { _playmode = mode; } + PlayMode getPlayMode() const { return _playmode; } + void setStartTime(float time) { _startTime = time;} float getStartTime() const { return _startTime;} diff --git a/include/osgAnimation/CubicBezier b/include/osgAnimation/CubicBezier index bd6da32b0..718fc2110 100644 --- a/include/osgAnimation/CubicBezier +++ b/include/osgAnimation/CubicBezier @@ -42,6 +42,21 @@ namespace osgAnimation const T& getPosition() const { return mPoint[0];} const T& getTangentPoint1() const { return mPoint[1];} const T& getTangentPoint2() const { return mPoint[2];} + + // steaming operators. + friend std::ostream& operator << (std::ostream& output, const TemplateCubicBezier& vec) + { + output << vec.mPoint[0] << " " + << vec.mPoint[1] << " " + << vec.mPoint[2]; + return output; // to enable cascading + } + + friend std::istream& operator >> (std::istream& input, TemplateCubicBezier& vec) + { + input >> vec.mPoint[0] >> vec.mPoint[1] >> vec.mPoint[2]; + return input; + } }; diff --git a/include/osgAnimation/Keyframe b/include/osgAnimation/Keyframe index f1b57323f..e194cc5ca 100644 --- a/include/osgAnimation/Keyframe +++ b/include/osgAnimation/Keyframe @@ -98,6 +98,9 @@ namespace osgAnimation typedef TemplateKeyframe FloatKeyframe; typedef TemplateKeyframeContainer FloatKeyframeContainer; + + typedef TemplateKeyframe DoubleKeyframe; + typedef TemplateKeyframeContainer DoubleKeyframeContainer; typedef TemplateKeyframe Vec2Keyframe; typedef TemplateKeyframeContainer Vec2KeyframeContainer; @@ -116,12 +119,16 @@ namespace osgAnimation typedef TemplateKeyframe FloatCubicBezierKeyframe; typedef TemplateKeyframeContainer FloatCubicBezierKeyframeContainer; + typedef TemplateKeyframe DoubleCubicBezierKeyframe; typedef TemplateKeyframeContainer DoubleCubicBezierKeyframeContainer; + typedef TemplateKeyframe Vec2CubicBezierKeyframe; typedef TemplateKeyframeContainer Vec2CubicBezierKeyframeContainer; + typedef TemplateKeyframe Vec3CubicBezierKeyframe; typedef TemplateKeyframeContainer Vec3CubicBezierKeyframeContainer; + typedef TemplateKeyframe Vec4CubicBezierKeyframe; typedef TemplateKeyframeContainer Vec4CubicBezierKeyframeContainer; diff --git a/include/osgAnimation/LinkVisitor b/include/osgAnimation/LinkVisitor index 0492a4d65..0fb72c126 100644 --- a/include/osgAnimation/LinkVisitor +++ b/include/osgAnimation/LinkVisitor @@ -32,7 +32,7 @@ namespace osgAnimation META_NodeVisitor("osgAnimation","LinkVisitor") - void apply(osg::Node& node) + void apply(osg::Node& node) { osgAnimation::AnimationUpdateCallback* cb = dynamic_cast(node.getUpdateCallback()); if (cb) diff --git a/include/osgAnimation/MorphGeometry b/include/osgAnimation/MorphGeometry new file mode 100644 index 000000000..0bb054eaa --- /dev/null +++ b/include/osgAnimation/MorphGeometry @@ -0,0 +1,153 @@ +/* -*-c++-*- + * Copyright (C) 2008 Cedric Pinson + * + * This library is open source and may be redistributed and/or modified under + * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or + * (at your option) any later version. The full license is in LICENSE file + * included with this distribution, and on the openscenegraph.org website. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * OpenSceneGraph Public License for more details. +*/ + +#ifndef OSGANIMATION_MORPHGEOMETRY_H +#define OSGANIMATION_MORPHGEOMETRY_H + +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT MorphGeometry : public osg::Geometry + { + + public: + + enum Method { + NORMALIZED, + RELATIVE + }; + + class MorphTarget + { + protected: + osg::ref_ptr _geom; + float _weight; + public: + MorphTarget(osg::Geometry* geom, float w = 1.0) : _geom(geom), _weight(w) {} + void setWeight(float weight) { _weight = weight; } + const float getWeight() const { return _weight; } + osg::Geometry* getGeometry() { return _geom.get(); } + const osg::Geometry* getGeometry() const { return _geom.get(); } + void setGeometry(osg::Geometry* geom) { _geom = geom; } + }; + + typedef std::vector MorphTargetList; + + struct UpdateVertex : public osg::Drawable::UpdateCallback + { + virtual void update(osg::NodeVisitor*, osg::Drawable* drw) + { + MorphGeometry* geom = dynamic_cast(drw); + if (!geom) + return; + + geom->transformSoftwareMethod(); + } + }; + + MorphGeometry(); + MorphGeometry(const osg::Geometry& b); + MorphGeometry(const MorphGeometry& b, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY); + + virtual osg::Object* cloneType() const { return new MorphGeometry(); } + virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new MorphGeometry(*this,copyop); } + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } + virtual const char* libraryName() const { return "osgAnimation"; } + virtual const char* className() const { return "MorphGeometry"; } + + virtual void transformSoftwareMethod(); + + /** Set the morphing method. */ + void setMethod(Method method) { _method = method; } + /** Get the morphing method. */ + inline Method getMethod() const { return _method; } + + /** Set flag for morphing normals. */ + void setMorphNormals(bool morphNormals) { _morphNormals = morphNormals; } + /** Get the flag for morphing normals. */ + inline bool getMorphNormals() const { return _morphNormals; } + + /** Add a \c MorphTarget to the \c MorphGeometry. + * If \c MorphTarget is not \c NULL and is not contained in the \c MorphGeometry + * then increment its reference count, add it to the MorphTargets list and + * dirty the bounding sphere to force it to be recomputed on the next + * call to \c getBound(). + * @param MorphTarget The \c MorphTarget to be added to the \c MorphGeometry. + * @return \c true for success; \c false otherwise. + */ + virtual void addMorphTarget( osg::Geometry *morphTarget, float weight = 1.0 ) { _morphTargets.push_back(MorphTarget(morphTarget, weight)); _dirty = true; } + + void setWeight(unsigned int index, float morphWeight) + { + if (index < _morphTargets.size()) + { + _morphTargets[index].setWeight(morphWeight); + dirty(); + } + } + + /** Set the MorphGeometry dirty.*/ + void dirty() { _dirty = true; } + + /** Get the list of MorphTargets.*/ + const MorphTargetList& getMorphTargetList() const { return _morphTargets; } + + /** Get the list of MorphTargets. Warning if you modify this array you will have to call dirty() */ + MorphTargetList& getMorphTargetList() { return _morphTargets; } + + /** Return the \c MorphTarget at position \c i.*/ + inline const MorphTarget& getMorphTarget( unsigned int i ) const { return _morphTargets[i]; } + + /** Return the \c MorphTarget at position \c i.*/ + inline MorphTarget& getMorphTarget( unsigned int i ) { return _morphTargets[i]; } + + protected: + /// Do we need to recalculate the morphed geometry? + bool _dirty; + + Method _method; + MorphTargetList _morphTargets; + + std::vector _positionSource; + std::vector _normalSource; + + /// Do we also morph between normals? + bool _morphNormals; + }; + + class OSGANIMATION_EXPORT UpdateMorph : public AnimationUpdateCallback + { + protected: + std::map > _weightTargets; + + public: + + META_Object(osgAnimation, UpdateMorph); + + UpdateMorph(const std::string& name = ""); + UpdateMorph(const UpdateMorph& apc,const osg::CopyOp& copyop); + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + bool needLink() const; + bool link(osgAnimation::Channel* channel); + }; + +} + +#endif diff --git a/include/osgAnimation/Skeleton b/include/osgAnimation/Skeleton index b1e69b1a5..5a1b0de77 100644 --- a/include/osgAnimation/Skeleton +++ b/include/osgAnimation/Skeleton @@ -38,7 +38,7 @@ namespace osgAnimation Skeleton(const Skeleton& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY) : Bone(b,copyop) {} Skeleton(); void setDefaultUpdateCallback(void); - void computeBindMatrix() { _invBindInSkeletonSpace = osg::Matrix::inverse(_bindInBoneSpace); } + void computeBindMatrix() { _invBindInSkeletonSpace = osg::Matrix::inverse(_bindInBoneSpace); _needToRecomputeBindMatrix = false; } }; } diff --git a/src/osgAnimation/AnimationManagerBase.cpp b/src/osgAnimation/AnimationManagerBase.cpp index 6bb3f5443..f5dedf168 100644 --- a/src/osgAnimation/AnimationManagerBase.cpp +++ b/src/osgAnimation/AnimationManagerBase.cpp @@ -37,7 +37,7 @@ void AnimationManagerBase::normalizeTargets() void AnimationManagerBase::operator()(osg::Node* node, osg::NodeVisitor* nv) { - if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) { if (needToLink()) { diff --git a/src/osgAnimation/BasicAnimationManager.cpp b/src/osgAnimation/BasicAnimationManager.cpp index df438fa36..95b821354 100644 --- a/src/osgAnimation/BasicAnimationManager.cpp +++ b/src/osgAnimation/BasicAnimationManager.cpp @@ -47,14 +47,14 @@ void BasicAnimationManager::stopAll() void BasicAnimationManager::playAnimation(Animation* pAnimation, int priority, float weight) { if (!findAnimation(pAnimation)) - { return; - } if ( isPlaying(pAnimation) ) stopAnimation(pAnimation); _animationsPlaying[priority].push_back(pAnimation); + // for debug + //std::cout << "player Animation " << pAnimation->getName() << " at " << _lastUpdate << std::endl; pAnimation->setStartTime(_lastUpdate); pAnimation->setWeight(weight); } @@ -62,7 +62,7 @@ void BasicAnimationManager::playAnimation(Animation* pAnimation, int priority, f bool BasicAnimationManager::stopAnimation(Animation* pAnimation) { // search though the layer and remove animation - for( AnimationLayers::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) + for( AnimationLayers::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) { AnimationList& list = iterAnim->second; for (AnimationList::iterator it = list.begin(); it != list.end(); it++) @@ -79,8 +79,7 @@ bool BasicAnimationManager::stopAnimation(Animation* pAnimation) void BasicAnimationManager::update (double time) { - if (!_lastUpdate) - _lastUpdate = time; + _lastUpdate = time; // keep time of last update // could filtered with an active flag for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); it++) @@ -94,8 +93,16 @@ void BasicAnimationManager::update (double time) AnimationList& list = iterAnim->second; for (unsigned int i = 0; i < list.size(); i++) { - if (! list[i]->update(time)) + if (! list[i]->update(time)) + { + // debug + // std::cout << list[i]->getName() << " finished at " << time << std::endl; toremove.push_back(i); + } else + { + // debug + //std::cout << list[i]->getName() << " updated" << std::endl; + } } // remove finished animation diff --git a/src/osgAnimation/Bone.cpp b/src/osgAnimation/Bone.cpp index 8f75a5ea1..faf5f7322 100644 --- a/src/osgAnimation/Bone.cpp +++ b/src/osgAnimation/Bone.cpp @@ -16,7 +16,7 @@ #include #include -osgAnimation::Bone::UpdateBone::UpdateBone(const osgAnimation::Bone::UpdateBone& apc,const osg::CopyOp& copyop): +osgAnimation::Bone::UpdateBone::UpdateBone(const osgAnimation::Bone::UpdateBone& apc,const osg::CopyOp& copyop) : osgAnimation::AnimationUpdateCallback(apc, copyop), _position(apc._position), _quaternion(apc._quaternion), @@ -25,11 +25,12 @@ osgAnimation::Bone::UpdateBone::UpdateBone(const osgAnimation::Bone::UpdateBone& } -osgAnimation::Bone::Bone(const Bone& b, const osg::CopyOp& copyop) - : osg::Transform(b,copyop), - _position(b._position), +osgAnimation::Bone::Bone(const Bone& b, const osg::CopyOp& copyop) : + osg::Transform(b,copyop), + _position(b._position), _rotation(b._rotation), - _scale(b._scale) + _scale(b._scale), + _needToRecomputeBindMatrix(true) { } @@ -56,24 +57,7 @@ void osgAnimation::Bone::computeBindMatrix() _needToRecomputeBindMatrix = false; if (!parent) { -#if 0 - // no more parent means, we get the skeleton - if (getParents().empty()) { - osg::notify(osg::WARN) << "Warning " << className() <<"::computeBindMatrix you should not have this message, it means you miss to attach this bone(" << getName() <<") to a Skeleton node" << std::endl; - return; - } else if (getParents().size() > 1) { - osg::notify(osg::WARN) << "Warning " << className() <<"::computeBindMatrix you have more than one parent in a skeleton structure (" << getName() <<") unknown behaviour" << std::endl; - return; - } - osgAnimation::Skeleton* skel = dynamic_cast(getParents()[0]); - if (!skel) { - osg::notify(osg::WARN) << "Warning " << className() <<"::computeBindMatrix you should not have this message, it means you miss to attach this bone(" << getName() <<") to a Skeleton node" << std::endl; - return; - } - _invBindInSkeletonSpace = osg::Matrix::inverse(skel->getMatrix()) * _invBindInSkeletonSpace; -#else osg::notify(osg::WARN) << "Warning " << className() <<"::computeBindMatrix you should not have this message, it means you miss to attach this bone(" << getName() <<") to a Skeleton node" << std::endl; -#endif return; } _invBindInSkeletonSpace = parent->getInvBindMatrixInSkeletonSpace() * _invBindInSkeletonSpace; diff --git a/src/osgAnimation/CMakeLists.txt b/src/osgAnimation/CMakeLists.txt index 8245e7495..381d5ef6b 100644 --- a/src/osgAnimation/CMakeLists.txt +++ b/src/osgAnimation/CMakeLists.txt @@ -10,47 +10,49 @@ SET(LIB_NAME osgAnimation) SET(HEADER_PATH ${OpenSceneGraph_SOURCE_DIR}/include/${LIB_NAME}) SET(LIB_PUBLIC_HEADERS - ${HEADER_PATH}/Export - ${HEADER_PATH}/Bone - ${HEADER_PATH}/Skeleton - ${HEADER_PATH}/Channel - ${HEADER_PATH}/Sampler - ${HEADER_PATH}/Interpolator - ${HEADER_PATH}/Target ${HEADER_PATH}/Animation - ${HEADER_PATH}/Keyframe - ${HEADER_PATH}/Skinning - ${HEADER_PATH}/CubicBezier - ${HEADER_PATH}/Vec3Packed - ${HEADER_PATH}/BasicAnimationManager - ${HEADER_PATH}/TimelineAnimationManager ${HEADER_PATH}/AnimationManagerBase - ${HEADER_PATH}/UpdateCallback - ${HEADER_PATH}/LinkVisitor - ${HEADER_PATH}/VertexInfluence - ${HEADER_PATH}/EaseMotion ${HEADER_PATH}/Assert - ${HEADER_PATH}/Timeline + ${HEADER_PATH}/BasicAnimationManager + ${HEADER_PATH}/Bone + ${HEADER_PATH}/Channel + ${HEADER_PATH}/CubicBezier + ${HEADER_PATH}/EaseMotion + ${HEADER_PATH}/Export + ${HEADER_PATH}/Interpolator + ${HEADER_PATH}/Keyframe + ${HEADER_PATH}/LinkVisitor + ${HEADER_PATH}/MorphGeometry ${HEADER_PATH}/RigGeometry + ${HEADER_PATH}/Sampler + ${HEADER_PATH}/Skeleton + ${HEADER_PATH}/Skinning + ${HEADER_PATH}/Target + ${HEADER_PATH}/Timeline + ${HEADER_PATH}/TimelineAnimationManager + ${HEADER_PATH}/UpdateCallback + ${HEADER_PATH}/Vec3Packed + ${HEADER_PATH}/VertexInfluence ) ADD_LIBRARY(${LIB_NAME} ${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC} ${LIB_PUBLIC_HEADERS} - Channel.cpp - Target.cpp Animation.cpp - Bone.cpp - RigGeometry.cpp + AnimationManagerBase.cpp AnimationManager.cpp BasicAnimationManager.cpp - TimelineAnimationManager.cpp - AnimationManagerBase.cpp + Bone.cpp + Channel.cpp + MorphGeometry.cpp + RigGeometry.cpp Skeleton.cpp - VertexInfluence.cpp - UpdateCallback.cpp + Target.cpp + TimelineAnimationManager.cpp Timeline.cpp + UpdateCallback.cpp + VertexInfluence.cpp ${OPENSCENEGRAPH_VERSIONINFO_RC} ) diff --git a/src/osgAnimation/MorphGeometry.cpp b/src/osgAnimation/MorphGeometry.cpp new file mode 100644 index 000000000..6fc7b0a39 --- /dev/null +++ b/src/osgAnimation/MorphGeometry.cpp @@ -0,0 +1,277 @@ +/* -*-c++-*- + * Copyright (C) 2008 Cedric Pinson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * + * Roland Smeenk + * Cedric Pinson + * + */ +#include +#include + +#include + +using namespace osgAnimation; + +MorphGeometry::MorphGeometry() : + _dirty(false), + _method(NORMALIZED), + _morphNormals(true) +{ + setUseDisplayList(false); + setUpdateCallback(new UpdateVertex); + setDataVariance(osg::Object::DYNAMIC); + setUseVertexBufferObjects(true); +} + +MorphGeometry::MorphGeometry(const osg::Geometry& b) : + osg::Geometry(b, osg::CopyOp::DEEP_COPY_ARRAYS), + _dirty(false), + _method(NORMALIZED), + _morphNormals(true) +{ + setUseDisplayList(false); + setUpdateCallback(new UpdateVertex); + setDataVariance(osg::Object::DYNAMIC); + setUseVertexBufferObjects(true); + if (b.getInternalOptimizedGeometry()) + computeInternalOptimizedGeometry(); +} + +MorphGeometry::MorphGeometry(const MorphGeometry& b, const osg::CopyOp& copyop) : + osg::Geometry(b,copyop), + _dirty(b._dirty), + _method(b._method), + _morphTargets(b._morphTargets), + _positionSource(b._positionSource), + _normalSource(b._normalSource), + _morphNormals(b._morphNormals) +{ + setUseDisplayList(false); + setUseVertexBufferObjects(true); + if (b.getInternalOptimizedGeometry()) + computeInternalOptimizedGeometry(); +} + +void MorphGeometry::transformSoftwareMethod() +{ + if (_dirty) + { + // See if we have an internal optimized geometry + osg::Geometry* morphGeometry = this; + if (_internalOptimizedGeometry.valid()) + morphGeometry = _internalOptimizedGeometry; + + osg::Vec3Array* pos = dynamic_cast(morphGeometry->getVertexArray()); + if (pos && _positionSource.size() != pos->size()) + { + _positionSource = std::vector(pos->begin(),pos->end()); + pos->setDataVariance(osg::Object::DYNAMIC); + } + + osg::Vec3Array* normal = dynamic_cast(morphGeometry->getNormalArray()); + if (normal && _normalSource.size() != normal->size()) + { + _normalSource = std::vector(normal->begin(),normal->end()); + normal->setDataVariance(osg::Object::DYNAMIC); + } + + + if (!_positionSource.empty()) + { + bool initialized = false; + if (_method == NORMALIZED) + { + // base * 1 - (sum of weights) + sum of (weight * target) + float baseWeight = 0; + for (unsigned int i=0; i < _morphTargets.size(); i++) + { + baseWeight += _morphTargets[i].getWeight(); + } + baseWeight = 1 - baseWeight; + + if (baseWeight != 0) + { + initialized = true; + for (unsigned int i=0; i < pos->size(); i++) + { + (*pos)[i] = _positionSource[i] * baseWeight; + } + if (_morphNormals) + { + for (unsigned int i=0; i < normal->size(); i++) + { + (*normal)[i] = _normalSource[i] * baseWeight; + } + } + } + } + else //if (_method == RELATIVE) + { + // base + sum of (weight * target) + initialized = true; + for (unsigned int i=0; i < pos->size(); i++) + { + (*pos)[i] = _positionSource[i]; + } + if (_morphNormals) + { + for (unsigned int i=0; i < normal->size(); i++) + { + (*normal)[i] = _normalSource[i]; + } + } + } + + for (unsigned int i=0; i < _morphTargets.size(); i++) + { + if (_morphTargets[i].getWeight() > 0) + { + // See if any the targets use the internal optimized geometry + osg::Geometry* targetGeometry = _morphTargets[i].getGeometry()->getInternalOptimizedGeometry(); + if (!targetGeometry) + targetGeometry = _morphTargets[i].getGeometry(); + + osg::Vec3Array* targetPos = dynamic_cast(targetGeometry->getVertexArray()); + osg::Vec3Array* targetNormals = dynamic_cast(targetGeometry->getNormalArray()); + + if (initialized) + { + // If vertices are initialized, add the morphtargets + for (unsigned int j=0; j < pos->size(); j++) + { + (*pos)[j] += (*targetPos)[j] * _morphTargets[i].getWeight(); + } + + if (_morphNormals) + { + for (unsigned int j=0; j < normal->size(); j++) + { + (*normal)[j] += (*targetNormals)[j] * _morphTargets[i].getWeight(); + } + } + } + else + { + // If not initialized, initialize with this morph target + initialized = true; + for (unsigned int j=0; j < pos->size(); j++) + { + (*pos)[j] = (*targetPos)[j] * _morphTargets[i].getWeight(); + } + + if (_morphNormals) + { + for (unsigned int j=0; j < normal->size(); j++) + { + (*normal)[j] = (*targetNormals)[j] * _morphTargets[i].getWeight(); + } + } + } + } + } + + pos->dirty(); + if (_morphNormals) + { + for (unsigned int j=0; j < normal->size(); j++) + { + (*normal)[j].normalize(); + } + normal->dirty(); + } + } + + dirtyBound(); + _dirty = false; + } +} + +UpdateMorph::UpdateMorph(const UpdateMorph& apc,const osg::CopyOp& copyop) : AnimationUpdateCallback(apc, copyop) +{ +} + +UpdateMorph::UpdateMorph(const std::string& name) : AnimationUpdateCallback(name) +{ +} + +/** Callback method called by the NodeVisitor when visiting a node.*/ +void UpdateMorph::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + osg::Geode* geode = dynamic_cast(node); + if (geode) + { + unsigned int numDrawables = geode->getNumDrawables(); + for (unsigned int i = 0; i != numDrawables; ++i) + { + osgAnimation::MorphGeometry* morph = dynamic_cast(geode->getDrawable(i)); + if (morph) + { + // Update morph weights + std::map >::iterator iter = _weightTargets.begin(); + while (iter != _weightTargets.end()) + { + if (iter->second->getValue() >= 0) + { + morph->setWeight(iter->first, iter->second->getValue()); + } + ++iter; + } + } + } + } + } + traverse(node,nv); +} + + + +bool UpdateMorph::needLink() const +{ + // the idea is to return true if nothing is linked + return (_weightTargets.size() == 0); +} + +bool UpdateMorph::link(osgAnimation::Channel* channel) +{ + // Typically morph geometries only have the weights for morph targets animated + + // Expect a weight value + // TODO Should we make this more generic to handle other things than single values? + int weightIndex = atoi(channel->getName().c_str()); + + if (weightIndex >= 0) + { + osgAnimation::FloatLinearChannel* fc = dynamic_cast(channel); + if (fc) + { + osgAnimation::FloatTarget* ft = new osgAnimation::FloatTarget; + _weightTargets[weightIndex] = ft; + ft->setValue(-1); + fc->setTarget(ft); + return true; + } + } + else + { + std::cerr << "Channel " << channel->getName() << " does not contain a valid symbolic name for this class" << std::endl; + } + return false; +} diff --git a/src/osgAnimation/Skeleton.cpp b/src/osgAnimation/Skeleton.cpp index 62db25588..496868e21 100644 --- a/src/osgAnimation/Skeleton.cpp +++ b/src/osgAnimation/Skeleton.cpp @@ -27,7 +27,9 @@ struct computeBindMatrixVisitor : public osg::NodeVisitor Bone* bone = dynamic_cast(&node); if (!bone) return; - bone->computeBindMatrix(); + if (bone->needToComputeBindMatrix()) + bone->computeBindMatrix(); + traverse(node); } }; diff --git a/src/osgPlugins/osgAnimation/ReaderWriter.cpp b/src/osgPlugins/osgAnimation/ReaderWriter.cpp index 192a8002b..b9b054a34 100644 --- a/src/osgPlugins/osgAnimation/ReaderWriter.cpp +++ b/src/osgPlugins/osgAnimation/ReaderWriter.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -121,13 +122,83 @@ RegisterDotOsgWrapperProxy g_atkRootSkeletonProxy DotOsgWrapper::READ_AND_WRITE ); +// Helper method for reading channels +bool Animation_readChannel(osgAnimation::Channel* pChannel, Input& fr) +{ + bool iteratorAdvanced = false; + std::string name = "unknown"; + if (fr.matchSequence("name %s")) + { + if (fr[1].getStr()) + name = fr[1].getStr(); + fr += 2; + iteratorAdvanced = true; + } + pChannel->setName(name); + std::string target = "unknown"; + if (fr.matchSequence("target %s")) + { + if (fr[1].getStr()) + target = fr[1].getStr(); + fr += 2; + iteratorAdvanced = true; + } + pChannel->setTargetName(target); + float weight = 1.0; + if (fr.matchSequence("weight %f")) + { + fr[1].getFloat(weight); + fr += 2; + iteratorAdvanced = true; + } + pChannel->setWeight(weight); + return iteratorAdvanced; +} -bool Animation_readLocalData(Object& obj, Input& fr) +bool Animation_readLocalData(Object& obj, Input& fr) { osgAnimation::Animation& anim = dynamic_cast(obj); bool iteratorAdvanced = false; + + if (fr.matchSequence("playmode %w")) + { + if (fr[1].matchWord("ONCE")) anim.setPlaymode(osgAnimation::Animation::ONCE); + else if (fr[1].matchWord("STAY")) anim.setPlaymode(osgAnimation::Animation::STAY); + else if (fr[1].matchWord("LOOP")) anim.setPlaymode(osgAnimation::Animation::LOOP); + else if (fr[1].matchWord("PPONG")) anim.setPlaymode(osgAnimation::Animation::PPONG); + fr += 2; + iteratorAdvanced = true; + } + + if (fr.matchSequence("weight %f")) + { + float weight; + fr[1].getFloat(weight); + fr += 2; + iteratorAdvanced = true; + anim.setWeight(weight); + } + + if (fr.matchSequence("duration %f")) + { + float duration; + fr[1].getFloat(duration); + fr += 2; + iteratorAdvanced = true; + anim.setDuration(duration); + } + + if (fr.matchSequence("starttime %f")) + { + float starttime; + fr[1].getFloat(starttime); + fr += 2; + iteratorAdvanced = true; + anim.setStartTime(starttime); + } + int nbChannels = 0; if (fr.matchSequence("num_channels %i")) { @@ -138,30 +209,267 @@ bool Animation_readLocalData(Object& obj, Input& fr) for (int i = 0; i < nbChannels; i++) { - if (fr.matchSequence("Channel {")) + if (fr.matchSequence("DoubleLinearChannel {")) + { + fr += 2; + + osgAnimation::DoubleLinearChannel* channel = new osgAnimation::DoubleLinearChannel; + + if (Animation_readChannel(channel, fr)) + iteratorAdvanced = true; + + int nbKeys; + if (fr.matchSequence("Keyframes %i {")) + { + fr[1].getInt(nbKeys); + fr += 3; + iteratorAdvanced = true; + + for (int k = 0; k < nbKeys; k++) + { + double v; + float time; + if (fr.matchSequence("key %f %f")) + { + fr[1].getFloat(time); + fr[2].getFloat(v); + fr += 3; + channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::DoubleKeyframe(time, v)); + iteratorAdvanced = true; + } + } + anim.addChannel(channel); + + if (fr.matchSequence("}")) // keyframes + fr += 1; + } + if (fr.matchSequence("}")) // channel + fr += 1; + } + else if (fr.matchSequence("FloatLinearChannel {")) + { + fr += 2; + + osgAnimation::FloatLinearChannel* channel = new osgAnimation::FloatLinearChannel; + + if (Animation_readChannel(channel, fr)) + iteratorAdvanced = true; + + int nbKeys; + if (fr.matchSequence("Keyframes %i {")) + { + fr[1].getInt(nbKeys); + fr += 3; + iteratorAdvanced = true; + + for (int k = 0; k < nbKeys; k++) + { + float v; + float time; + if (fr.matchSequence("key %f %f")) + { + fr[1].getFloat(time); + fr[2].getFloat(v); + fr += 3; + channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::FloatKeyframe(time, v)); + iteratorAdvanced = true; + } + } + anim.addChannel(channel); + + if (fr.matchSequence("}")) // keyframes + fr += 1; + } + if (fr.matchSequence("}")) // channel + fr += 1; + } + else if (fr.matchSequence("Vec2LinearChannel {")) + { + fr += 2; + + osgAnimation::Vec2LinearChannel* channel = new osgAnimation::Vec2LinearChannel; + + if (Animation_readChannel(channel, fr)) + iteratorAdvanced = true; + + int nbKeys; + if (fr.matchSequence("Keyframes %i {")) + { + fr[1].getInt(nbKeys); + fr += 3; + iteratorAdvanced = true; + + for (int k = 0; k < nbKeys; k++) + { + osg::Vec2 v; + float time; + if (fr.matchSequence("key %f %f %f")) + { + fr[1].getFloat(time); + fr[2].getFloat(v[0]); + fr[3].getFloat(v[1]); + fr += 4; + channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec2Keyframe(time, v)); + iteratorAdvanced = true; + } + } + anim.addChannel(channel); + + if (fr.matchSequence("}")) // keyframes + fr += 1; + } + if (fr.matchSequence("}")) // channel + fr += 1; + } + else if (fr.matchSequence("Vec3LinearChannel {")) + { + fr += 2; + + osgAnimation::Vec3LinearChannel* channel = new osgAnimation::Vec3LinearChannel; + + if (Animation_readChannel(channel, fr)) + iteratorAdvanced = true; + + int nbKeys; + if (fr.matchSequence("Keyframes %i {")) + { + fr[1].getInt(nbKeys); + fr += 3; + iteratorAdvanced = true; + + for (int k = 0; k < nbKeys; k++) + { + osg::Vec3 v; + float time; + if (fr.matchSequence("key %f %f %f %f")) + { + fr[1].getFloat(time); + fr[2].getFloat(v[0]); + fr[3].getFloat(v[1]); + fr[4].getFloat(v[2]); + fr += 5; + channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(time, v)); + iteratorAdvanced = true; + } + } + anim.addChannel(channel); + + if (fr.matchSequence("}")) // keyframes + fr += 1; + } + if (fr.matchSequence("}")) // channel + fr += 1; + } + else if (fr.matchSequence("Vec4LinearChannel {")) + { + fr += 2; + + osgAnimation::Vec4LinearChannel* channel = new osgAnimation::Vec4LinearChannel; + + if (Animation_readChannel(channel, fr)) + iteratorAdvanced = true; + + int nbKeys; + if (fr.matchSequence("Keyframes %i {")) + { + fr[1].getInt(nbKeys); + fr += 3; + iteratorAdvanced = true; + + for (int k = 0; k < nbKeys; k++) + { + osg::Vec4 v; + float time; + if (fr.matchSequence("key %f %f %f %f %f")) + { + fr[1].getFloat(time); + fr[2].getFloat(v[0]); + fr[3].getFloat(v[1]); + fr[4].getFloat(v[2]); + fr[5].getFloat(v[3]); + fr += 6; + channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec4Keyframe(time, v)); + iteratorAdvanced = true; + } + } + anim.addChannel(channel); + + if (fr.matchSequence("}")) // keyframes + fr += 1; + } + if (fr.matchSequence("}")) // channel + fr += 1; + } + else if (fr.matchSequence("QuatSphericalLinearChannel {")) + { + fr += 2; + + osgAnimation::QuatSphericalLinearChannel* channel = new osgAnimation::QuatSphericalLinearChannel; + + if (Animation_readChannel(channel, fr)) + iteratorAdvanced = true; + + int nbKeys; + if (fr.matchSequence("Keyframes %i {")) + { + fr[1].getInt(nbKeys); + fr += 3; + iteratorAdvanced = true; + + for (int k = 0; k < nbKeys; k++) + { + osg::Quat q; + float time; + if (fr.matchSequence("key %f %f %f %f %f")) + { + fr[1].getFloat(time); + fr[2].getFloat(q[0]); + fr[3].getFloat(q[1]); + fr[4].getFloat(q[2]); + fr[5].getFloat(q[3]); + fr += 6; + channel->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::QuatKeyframe(time, q)); + iteratorAdvanced = true; + } + } + anim.addChannel(channel); + + if (fr.matchSequence("}")) // keyframes + fr += 1; + } + if (fr.matchSequence("}")) // channel + fr += 1; + } + // Deprecated + // Reading of old channel info + // Kept here for easy conversion of old .osg data to new format + else if (fr.matchSequence("Channel {")) { fr += 2; std::string name = "unknown"; if (fr.matchSequence("name %s")) { - name = fr[1].getStr(); + if (fr[1].getStr()) + name = fr[1].getStr(); fr += 2; iteratorAdvanced = true; } std::string target = "unknown"; if (fr.matchSequence("target %s")) { - target = fr[1].getStr(); + if (fr[1].getStr()) + target = fr[1].getStr(); fr += 2; iteratorAdvanced = true; } - std::string type; + std::string type = "unknown"; int nbKeys; if (fr.matchSequence("Keyframes %s %i {")) { - type = fr[1].getStr(); + if (fr[1].getStr()) + type = fr[1].getStr(); fr[2].getInt(nbKeys); fr += 4; iteratorAdvanced = true; @@ -175,11 +483,11 @@ bool Animation_readLocalData(Object& obj, Input& fr) } else if (type == "Vec3") { - channel = new osgAnimation::Vec3LinearChannel; osgAnimation::Vec3LinearChannel* c = new osgAnimation::Vec3LinearChannel; c->getOrCreateSampler()->getOrCreateKeyframeContainer(); channel = c; } + if (channel) { for (int k = 0; k < nbKeys; k++) @@ -229,60 +537,130 @@ bool Animation_readLocalData(Object& obj, Input& fr) return iteratorAdvanced; } +// Helper method for writing channels +template +void Animation_writeChannel(const std::string& channelString, ChannelType* pChannel, Output& fw) +{ + fw.indent() << channelString.c_str() << " {" << std::endl; + fw.moveIn(); + fw.indent() << "name \"" << pChannel->getName() << "\"" << std::endl; + fw.indent() << "target \"" << pChannel->getTargetName() << "\"" << std::endl; + + fw.indent() << "weight " << pChannel->getWeight() << std::endl; + + ContainerType* kfc = pChannel->getSamplerTyped()->getKeyframeContainerTyped(); + if (kfc) + { + fw.indent() << "Keyframes " << kfc->size() << " {" << std::endl; + fw.moveIn(); + for (unsigned int k = 0; k < kfc->size(); k++) + { + fw.indent() << "key " << (*kfc)[k].getTime() << " " << (*kfc)[k].getValue() << std::endl; + } + fw.moveOut(); + fw.indent() << "}" << std::endl; + fw.moveOut(); + fw.indent() << "}" << std::endl; + } +} + bool Animation_writeLocalData(const Object& obj, Output& fw) { const osgAnimation::Animation& anim = dynamic_cast(obj); + switch (anim.getPlayMode()) + { + case osgAnimation::Animation::ONCE: + fw.indent() << "playmode ONCE" << std::endl; + break; + case osgAnimation::Animation::STAY: + fw.indent() << "playmode STAY" << std::endl; + break; + case osgAnimation::Animation::LOOP: + fw.indent() << "playmode LOOP" << std::endl; + break; + case osgAnimation::Animation::PPONG: + fw.indent() << "playmode PPONG" << std::endl; + break; + default: + break; + } + + fw.indent() << "weight " << anim.getWeight() << std::endl; + fw.indent() << "duration " << anim.getDuration() << std::endl; + fw.indent() << "starttime " << anim.getStartTime() << std::endl; + fw.indent() << "num_channels " << anim.getChannels().size() << std::endl; for (unsigned int i = 0; i < anim.getChannels().size(); i++) { - fw.indent() << "Channel {" << std::endl; - fw.moveIn(); - fw.indent() << "name \"" << anim.getChannels()[i]->getName() << "\"" << std::endl; - fw.indent() << "target \"" << anim.getChannels()[i]->getTargetName() << "\"" << std::endl; + osgAnimation::Channel* pChannel = anim.getChannels()[i]; - std::string type = "unknown"; - if (anim.getChannels()[i]->getName() == std::string("quaternion")) + osgAnimation::DoubleLinearChannel* pDlc = dynamic_cast(pChannel); + if (pDlc) { - type = "Quat"; + Animation_writeChannel("DoubleLinearChannel", pDlc, fw); + continue; } - else if (anim.getChannels()[i]->getName() == std::string("rotation")) + osgAnimation::FloatLinearChannel* pFlc = dynamic_cast(pChannel); + if (pFlc) { - type = "Quat"; + Animation_writeChannel("FloatLinearChannel", pFlc, fw); + continue; } - else if (anim.getChannels()[i]->getName() == std::string("euler")) + osgAnimation::Vec2LinearChannel* pV2lc = dynamic_cast(pChannel); + if (pV2lc) { - type = "Vec3"; + Animation_writeChannel("Vec2LinearChannel", pV2lc, fw); + continue; } - else if (anim.getChannels()[i]->getName() == std::string("scale")) + osgAnimation::Vec3LinearChannel* pV3lc = dynamic_cast(pChannel); + if (pV3lc) { - type = "Vec3"; + Animation_writeChannel("Vec3LinearChannel", pV3lc, fw); + continue; } - else if (anim.getChannels()[i]->getName() == std::string("position")) + osgAnimation::Vec4LinearChannel* pV4lc = dynamic_cast(pChannel); + if (pV4lc) { - type = "Vec3"; + Animation_writeChannel("Vec4LinearChannel", pV4lc, fw); + continue; } - - osgAnimation::KeyframeContainer* kf = anim.getChannels()[i]->getSampler()->getKeyframeContainer(); - fw.indent() << "Keyframes \"" << type << "\" " << kf->size() << " {" << std::endl; - fw.moveIn(); - for (unsigned int k = 0; k < kf->size(); k++) + osgAnimation::QuatSphericalLinearChannel* pQslc = dynamic_cast(pChannel); + if (pQslc) { - if (type == "Vec3") - { - osgAnimation::Vec3KeyframeContainer* kk = dynamic_cast(kf); - fw.indent() << "key " << (*kk)[k].getTime() << " " << (*kk)[k].getValue() << std::endl; - } - else if ( type == "Quat") - { - osgAnimation::QuatKeyframeContainer* kk = dynamic_cast(kf); - fw.indent() << "key " << (*kk)[k].getTime() << " " << (*kk)[k].getValue() << std::endl; - } + Animation_writeChannel("QuatSphericalLinearChannel", pQslc, fw); + continue; + } + osgAnimation::FloatCubicBezierChannel* pFcbc = dynamic_cast(pChannel); + if (pFcbc) + { + Animation_writeChannel("FloatCubicBezierChannel", pFcbc, fw); + continue; + } + osgAnimation::DoubleCubicBezierChannel* pDcbc = dynamic_cast(pChannel); + if (pDcbc) + { + Animation_writeChannel("DoubleCubicBezierChannel", pDcbc, fw); + continue; + } + osgAnimation::Vec2CubicBezierChannel* pV2cbc = dynamic_cast(pChannel); + if (pV2cbc) + { + Animation_writeChannel("Vec2CubicBezierChannel", pV2cbc, fw); + continue; + } + osgAnimation::Vec3CubicBezierChannel* pV3cbc = dynamic_cast(pChannel); + if (pV3cbc) + { + Animation_writeChannel("Vec3CubicBezierChannel", pV3cbc, fw); + continue; + } + osgAnimation::Vec4CubicBezierChannel* pV4cbc = dynamic_cast(pChannel); + if (pV4cbc) + { + Animation_writeChannel("Vec4CubicBezierChannel", pV4cbc, fw); + continue; } - fw.moveOut(); - fw.indent() << "}" << std::endl; - fw.moveOut(); - fw.indent() << "}" << std::endl; } return true; } @@ -478,6 +856,123 @@ RegisterDotOsgWrapperProxy g_atkRigGeometryProxy ); +bool MorphGeometry_readLocalData(Object& obj, Input& fr) +{ + osgAnimation::MorphGeometry& geom = dynamic_cast(obj); + + bool iteratorAdvanced = false; + + if (fr[0].matchWord("method")) + { + if (fr[1].matchWord("NORMALIZED")) + { + geom.setMethod(osgAnimation::MorphGeometry::NORMALIZED); + fr+=2; + iteratorAdvanced = true; + } + else if (fr[1].matchWord("RELATIVE")) + { + geom.setMethod(osgAnimation::MorphGeometry::RELATIVE); + fr+=2; + iteratorAdvanced = true; + } + } + + if (fr[0].matchWord("morphNormals")) + { + if (fr[1].matchWord("TRUE")) + { + geom.setMorphNormals(true); + fr+=2; + iteratorAdvanced = true; + } + else if (fr[1].matchWord("FALSE")) + { + geom.setMorphNormals(false); + fr+=2; + iteratorAdvanced = true; + } + } + + int num_morphTargets = 0; + if (fr.matchSequence("num_morphTargets %i")) + { + fr[1].getInt(num_morphTargets); + fr += 2; + iteratorAdvanced = true; + } + + for (int i = 0; i < num_morphTargets; i++) + { + if (fr.matchSequence("MorphTarget {")) + { + int entry = fr[0].getNoNestedBrackets(); + fr += 2; + iteratorAdvanced = true; + + while (!fr.eof() && fr[0].getNoNestedBrackets()>entry) + { + + float weight = 1.0; + if (fr.matchSequence("weight %f")) + { + fr[1].getFloat(weight); + fr += 2; + } + osg::Drawable* drawable = NULL; + drawable = fr.readDrawable(); + osg::Geometry* geometry = dynamic_cast(drawable); + if (geometry) + geom.addMorphTarget(geometry, weight); + } + if (fr.matchSequence("}")) + fr += 1; + } + } + + return iteratorAdvanced; +} + +bool MorphGeometry_writeLocalData(const Object& obj, Output& fw) +{ + const osgAnimation::MorphGeometry& geom = dynamic_cast(obj); + + switch(geom.getMethod()) + { + case(osgAnimation::MorphGeometry::NORMALIZED): fw.indent() << "method NORMALIZED"<