diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 0a0fa8242..b400a6cab 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -108,6 +108,12 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgvertexprogram) ADD_SUBDIRECTORY(osgvolume) ADD_SUBDIRECTORY(osgwindows) + ADD_SUBDIRECTORY(osganimationtimeline) + ADD_SUBDIRECTORY(osganimationnode) + ADD_SUBDIRECTORY(osganimationmakepath) + ADD_SUBDIRECTORY(osganimationskinning) + ADD_SUBDIRECTORY(osganimationsolid) + # ADD_SUBDIRECTORY(osganimationviewer) IF (POPPLER_FOUND AND CAIRO_FOUND) ADD_SUBDIRECTORY(osgpdf) @@ -176,6 +182,7 @@ IF(DYNAMIC_OPENSCENEGRAPH) ADD_SUBDIRECTORY(osgwidgetwindow) ENDIF(BUILD_OSGWIDGET) + IF (LIBVNCSERVER_FOUND) ADD_SUBDIRECTORY(osgvnc) ENDIF(LIBVNCSERVER_FOUND) diff --git a/examples/osganimationmakepath/CMakeLists.txt b/examples/osganimationmakepath/CMakeLists.txt new file mode 100644 index 000000000..159dfa55d --- /dev/null +++ b/examples/osganimationmakepath/CMakeLists.txt @@ -0,0 +1,3 @@ +SET(TARGET_SRC osganimationmakepath.cpp ) +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +SETUP_EXAMPLE(osganimationmakepath) diff --git a/examples/osganimationmakepath/osganimationmakepath.cpp b/examples/osganimationmakepath/osganimationmakepath.cpp new file mode 100644 index 000000000..de23cf305 --- /dev/null +++ b/examples/osganimationmakepath/osganimationmakepath.cpp @@ -0,0 +1,337 @@ +/* -*-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. + * + * Authors: + * Jeremy Moles + * Cedric Pinson +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + class AnimtkUpdateCallback : public osg::NodeCallback + { + public: + META_Object(osgAnimation, AnimtkUpdateCallback); + + AnimtkUpdateCallback() + { + _sampler = new osgAnimation::Vec3CubicBezierSampler; + _playing = false; + _lastUpdate = 0; + } + AnimtkUpdateCallback(const AnimtkUpdateCallback& val, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY): + osg::Object(val, copyop), + osg::NodeCallback(val, copyop), + _sampler(val._sampler), + _startTime(val._startTime), + _currentTime(val._currentTime), + _playing(val._playing), + _lastUpdate(val._lastUpdate) + { + } + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && + nv->getFrameStamp() && + nv->getFrameStamp()->getFrameNumber() != _lastUpdate) + { + + _lastUpdate = nv->getFrameStamp()->getFrameNumber(); + _currentTime = osg::Timer::instance()->tick(); + + if (_playing && _sampler.get() && _sampler->getKeyframeContainer()) + { + osg::MatrixTransform* transform = dynamic_cast(node); + if (transform) { + osg::Vec3 result; + float t = osg::Timer::instance()->delta_s(_startTime, _currentTime); + float duration = _sampler->getEndTime() - _sampler->getStartTime(); + t = fmod(t, duration); + t += _sampler->getStartTime(); + _sampler->getValueAt(t, result); + transform->setMatrix(osg::Matrix::translate(result)); + } + } + } + // note, callback is responsible for scenegraph traversal so + // they must call traverse(node,nv) to ensure that the + // scene graph subtree (and associated callbacks) are traversed. + traverse(node,nv); + } + + void start() { _startTime = osg::Timer::instance()->tick(); _currentTime = _startTime; _playing = true;} + void stop() { _currentTime = _startTime; _playing = false;} + + osg::ref_ptr _sampler; + osg::Timer_t _startTime; + osg::Timer_t _currentTime; + bool _playing; + int _lastUpdate; + }; + + +class AnimtkStateSetUpdateCallback : public osg::StateSet::Callback +{ +public: + META_Object(osgAnimation, AnimtkStateSetUpdateCallback); + + AnimtkStateSetUpdateCallback() + { + _sampler = new osgAnimation::Vec4LinearSampler; + _playing = false; + _lastUpdate = 0; + } + + AnimtkStateSetUpdateCallback(const AnimtkStateSetUpdateCallback& val, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY): + osg::Object(val, copyop), + osg::StateSet::Callback(val, copyop), + _sampler(val._sampler), + _startTime(val._startTime), + _currentTime(val._currentTime), + _playing(val._playing), + _lastUpdate(val._lastUpdate) + { + } + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::StateSet* state, osg::NodeVisitor* nv) + { + if (state && + nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && + nv->getFrameStamp() && + nv->getFrameStamp()->getFrameNumber() != _lastUpdate) { + + _lastUpdate = nv->getFrameStamp()->getFrameNumber(); + _currentTime = osg::Timer::instance()->tick(); + + if (_playing && _sampler.get() && _sampler->getKeyframeContainer()) + { + osg::Material* material = dynamic_cast(state->getAttribute(osg::StateAttribute::MATERIAL)); + if (material) + { + osg::Vec4 result; + float t = osg::Timer::instance()->delta_s(_startTime, _currentTime); + float duration = _sampler->getEndTime() - _sampler->getStartTime(); + t = fmod(t, duration); + t += _sampler->getStartTime(); + _sampler->getValueAt(t, result); + material->setDiffuse(osg::Material::FRONT_AND_BACK, result); + } + } + } + } + + void start() { _startTime = osg::Timer::instance()->tick(); _currentTime = _startTime; _playing = true;} + void stop() { _currentTime = _startTime; _playing = false;} + + osg::ref_ptr _sampler; + osg::Timer_t _startTime; + osg::Timer_t _currentTime; + bool _playing; + int _lastUpdate; +}; + +// This won't really give good results in any situation, but it does demonstrate +// on possible "fast" usage... +class MakePathTimeCallback: public AnimtkUpdateCallback +{ + osg::ref_ptr _geode; + float _lastAdd; + float _addSeconds; + +public: + MakePathTimeCallback(osg::Geode* geode): + _geode(geode), + _lastAdd(0.0f), + _addSeconds(0.08f) { + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + float t = osg::Timer::instance()->delta_s(_startTime, _currentTime); + + if(_lastAdd + _addSeconds <= t && t <= 8.0f) + { + osg::Vec3 pos; + + _sampler->getValueAt(t, pos); + + _geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(pos, 0.5f))); + _geode->dirtyBound(); + + _lastAdd += _addSeconds; + } + + AnimtkUpdateCallback::operator()(node, nv); + } +}; + +// This will give great results if you DO NOT have VSYNC enabled and can generate +// decent FPS. +class MakePathDistanceCallback: public AnimtkUpdateCallback +{ + osg::ref_ptr _geode; + osg::Vec3 _lastAdd; + float _threshold; + unsigned int _count; + +public: + MakePathDistanceCallback(osg::Geode* geode): + _geode(geode), + _threshold(0.5f), + _count(0) {} + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + static bool countReported = false; + + float t = osg::Timer::instance()->delta_s(_startTime, _currentTime); + + osg::Vec3 pos; + + _sampler->getValueAt(t, pos); + + osg::Vec3 distance = _lastAdd - pos; + + if(t <= 8.0f && distance.length() >= _threshold) + { + _geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(pos, 0.25f))); + _lastAdd = pos; + _count++; + } + else if(t > 8.0f) + { + if(!countReported) std::cout << "Created " << _count << " nodes." << std::endl; + countReported = true; + } + + AnimtkUpdateCallback::operator()(node, nv); + } +}; + +osg::StateSet* setupStateSet() +{ + osg::StateSet* st = new osg::StateSet(); + + st->setAttributeAndModes(new osg::Material(), true); + st->setMode(GL_BLEND, true); + + AnimtkStateSetUpdateCallback* callback = new AnimtkStateSetUpdateCallback(); + osgAnimation::Vec4KeyframeContainer* keys = callback->_sampler->getOrCreateKeyframeContainer(); + keys->push_back(osgAnimation::Vec4Keyframe(0, osg::Vec4(1,0,0,1))); + keys->push_back(osgAnimation::Vec4Keyframe(2, osg::Vec4(0.,1,0,1))); + keys->push_back(osgAnimation::Vec4Keyframe(4, osg::Vec4(0,0,1,1))); + keys->push_back(osgAnimation::Vec4Keyframe(6, osg::Vec4(0,0,1,1))); + keys->push_back(osgAnimation::Vec4Keyframe(8, osg::Vec4(0,1,0,1))); + keys->push_back(osgAnimation::Vec4Keyframe(10, osg::Vec4(1,0,0,1))); + callback->start(); + st->setUpdateCallback(callback); + + return st; +} + +osg::MatrixTransform* setupAnimtkNode(osg::Geode* staticGeode) +{ + osg::Vec3 v[4]; + + v[0] = osg::Vec3( 0, 0, 0); + v[1] = osg::Vec3(20, 40, 60); + v[2] = osg::Vec3(40, 60, 20); + v[3] = osg::Vec3(60, 20, 40); + v[4] = osg::Vec3( 0, 0, 0); + + osg::MatrixTransform* node = new osg::MatrixTransform(); + AnimtkUpdateCallback* callback = new MakePathDistanceCallback(staticGeode); + osgAnimation::Vec3CubicBezierKeyframeContainer* keys = callback->_sampler->getOrCreateKeyframeContainer(); + + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(0, osgAnimation::Vec3CubicBezier( + v[0], + v[0] + (v[0] - v[3]), + v[1] - (v[1] - v[0]) + ))); + + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(2, osgAnimation::Vec3CubicBezier( + v[1], + v[1] + (v[1] - v[0]), + v[2] - (v[2] - v[1]) + ))); + + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(4, osgAnimation::Vec3CubicBezier( + v[2], + v[2] + (v[2] - v[1]), + v[3] - (v[3] - v[2]) + ))); + + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(6, osgAnimation::Vec3CubicBezier( + v[3], + v[3] + (v[3] - v[2]), + v[4] - (v[4] - v[3]) + ))); + + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(8, osgAnimation::Vec3CubicBezier( + v[4], + v[4] + (v[4] - v[3]), + v[0] - (v[0] - v[4]) + ))); + + callback->start(); + node->setUpdateCallback(callback); + + osg::Geode* geode = new osg::Geode(); + + geode->setStateSet(setupStateSet()); + geode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), 2))); + + node->addChild(geode); + + return node; +} + +int main(int argc, char** argv) +{ + osgViewer::Viewer viewer; + + osgGA::TrackballManipulator* tbm = new osgGA::TrackballManipulator(); + + viewer.setCameraManipulator(tbm); + + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + + osg::Group* root = new osg::Group(); + osg::Geode* geode = new osg::Geode(); + + geode->setStateSet(setupStateSet()); + + root->setInitialBound(osg::BoundingSphere(osg::Vec3(10,0,20), 50)); + root->addChild(setupAnimtkNode(geode)); + root->addChild(geode); + + viewer.setSceneData(root); + + // tbm->setDistance(150); + + return viewer.run(); +} diff --git a/examples/osganimationnode/CMakeLists.txt b/examples/osganimationnode/CMakeLists.txt new file mode 100644 index 000000000..bf454b530 --- /dev/null +++ b/examples/osganimationnode/CMakeLists.txt @@ -0,0 +1,3 @@ +#SET(TARGET_SRC osganimationnode.cpp ) +#SET(TARGET_ADDED_LIBRARIES osgAnimation ) +#SETUP_EXAMPLE(osganimationnode) diff --git a/examples/osganimationnode/osganimationnode.cpp b/examples/osganimationnode/osganimationnode.cpp new file mode 100644 index 000000000..64eba4d5e --- /dev/null +++ b/examples/osganimationnode/osganimationnode.cpp @@ -0,0 +1,272 @@ +/* -*-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 + +class AnimtkUpdateCallback : public osg::NodeCallback +{ +public: + META_Object(osgAnimation, AnimtkUpdateCallback); + + AnimtkUpdateCallback() + { + _sampler = new osgAnimation::Vec3CubicBezierSampler; + _playing = false; + _lastUpdate = 0; + } + AnimtkUpdateCallback(const AnimtkUpdateCallback& val, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY): + osg::Object(val, copyop), + osg::NodeCallback(val, copyop), + _sampler(val._sampler), + _startTime(val._startTime), + _currentTime(val._currentTime), + _playing(val._playing), + _lastUpdate(val._lastUpdate) + { + } + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && + nv->getFrameStamp() && + nv->getFrameStamp()->getFrameNumber() != _lastUpdate) { + + _lastUpdate = nv->getFrameStamp()->getFrameNumber(); + _currentTime = osg::Timer::instance()->tick(); + + if (_playing && _sampler.get() && _sampler->getKeyframeContainer()) { + osg::MatrixTransform* transform = dynamic_cast(node); + if (transform) { + osg::Vec3 result; + float t = osg::Timer::instance()->delta_s(_startTime, _currentTime); + float duration = _sampler->getEndTime() - _sampler->getStartTime(); + t = fmod(t, duration); + t += _sampler->getStartTime(); + _sampler->getValueAt(t, result); + transform->setMatrix(osg::Matrix::translate(result)); + } + } + } + // note, callback is responsible for scenegraph traversal so + // they must call traverse(node,nv) to ensure that the + // scene graph subtree (and associated callbacks) are traversed. + traverse(node,nv); + } + + void start() { _startTime = osg::Timer::instance()->tick(); _currentTime = _startTime; _playing = true;} + void stop() { _currentTime = _startTime; _playing = false;} + + osg::ref_ptr _sampler; + osg::Timer_t _startTime; + osg::Timer_t _currentTime; + bool _playing; + int _lastUpdate; +}; + + +class AnimtkStateSetUpdateCallback : public osg::StateSet::Callback +{ +public: + META_Object(osgAnimation, AnimtkStateSetUpdateCallback); + + AnimtkStateSetUpdateCallback() + { + _sampler = new osgAnimation::Vec4LinearSampler; + _playing = false; + _lastUpdate = 0; + } + + AnimtkStateSetUpdateCallback(const AnimtkStateSetUpdateCallback& val, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY): + osg::Object(val, copyop), + osg::StateSet::Callback(val, copyop), + _sampler(val._sampler), + _startTime(val._startTime), + _currentTime(val._currentTime), + _playing(val._playing), + _lastUpdate(val._lastUpdate) + { + } + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::StateSet* state, osg::NodeVisitor* nv) + { + if (state && + nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && + nv->getFrameStamp() && + nv->getFrameStamp()->getFrameNumber() != _lastUpdate) + { + + _lastUpdate = nv->getFrameStamp()->getFrameNumber(); + _currentTime = osg::Timer::instance()->tick(); + + if (_playing && _sampler.get() && _sampler->getKeyframeContainer()) + { + osg::Material* material = dynamic_cast(state->getAttribute(osg::StateAttribute::MATERIAL)); + if (material) + { + osg::Vec4 result; + float t = osg::Timer::instance()->delta_s(_startTime, _currentTime); + float duration = _sampler->getEndTime() - _sampler->getStartTime(); + t = fmod(t, duration); + t += _sampler->getStartTime(); + _sampler->getValueAt(t, result); + material->setDiffuse(osg::Material::FRONT_AND_BACK, result); + } + } + } + } + + void start() { _startTime = osg::Timer::instance()->tick(); _currentTime = _startTime; _playing = true;} + void stop() { _currentTime = _startTime; _playing = false;} + + osg::ref_ptr _sampler; + osg::Timer_t _startTime; + osg::Timer_t _currentTime; + bool _playing; + int _lastUpdate; +}; + + +osg::Geode* createAxis() +{ + osg::Geode* geode = new osg::Geode; + osg::ref_ptr geometry (new osg::Geometry()); + + osg::ref_ptr vertices (new osg::Vec3Array()); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 10.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 10.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 10.0)); + geometry->setVertexArray (vertices.get()); + + osg::ref_ptr colors (new osg::Vec4Array()); + colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); + geometry->setColorArray (colors.get()); + + geometry->setColorBinding (osg::Geometry::BIND_PER_VERTEX); + geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,6)); + + geode->addDrawable( geometry.get() ); + geode->getOrCreateStateSet()->setMode(GL_LIGHTING, false); + return geode; +} + +osg::StateSet* setupStateSet() +{ + osg::StateSet* st = new osg::StateSet; + st->setAttributeAndModes(new osg::Material, true); + st->setMode(GL_BLEND, true); + AnimtkStateSetUpdateCallback* callback = new AnimtkStateSetUpdateCallback; + osgAnimation::Vec4KeyframeContainer* keys = callback->_sampler->getOrCreateKeyframeContainer(); + keys->push_back(osgAnimation::Vec4Keyframe(0, osg::Vec4(0,0,0,0))); + keys->push_back(osgAnimation::Vec4Keyframe(2, osg::Vec4(0.5,0,0,0.5))); + keys->push_back(osgAnimation::Vec4Keyframe(4, osg::Vec4(0,0.5,0,1))); + keys->push_back(osgAnimation::Vec4Keyframe(6, osg::Vec4(0,0,0.5,1))); + keys->push_back(osgAnimation::Vec4Keyframe(8, osg::Vec4(1,1,1,0.5))); + keys->push_back(osgAnimation::Vec4Keyframe(10, osg::Vec4(0,0,0,0))); + callback->start(); + st->setUpdateCallback(callback); + return st; +} + +osg::Node* setupCube() +{ + osg::Geode* geode = new osg::Geode; + geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,0.0f),2))); + geode->setStateSet(setupStateSet()); + return geode; +} + +osg::MatrixTransform* setupAnimtkNode() +{ + osg::Vec3 v[4]; + v[0] = osg::Vec3(0,0,0); + v[1] = osg::Vec3(10,-50,0); + v[2] = osg::Vec3(30,-10,20); + v[3] = osg::Vec3(-10,20,-20); + v[4] = osg::Vec3(0,0,0); + osg::MatrixTransform* node = new osg::MatrixTransform; + AnimtkUpdateCallback* callback = new AnimtkUpdateCallback; + osgAnimation::Vec3CubicBezierKeyframeContainer* keys = callback->_sampler->getOrCreateKeyframeContainer(); + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(0, osgAnimation::Vec3CubicBezier( + v[0], // pos + v[0] + (v[0] - v[3]), // p1 + v[1] - (v[1] - v[0]) // p2 + ))); + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(2, osgAnimation::Vec3CubicBezier( + v[1], // pos + v[1] + (v[1] - v[0]), + v[2] - (v[2] - v[1]) + ))); + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(4, osgAnimation::Vec3CubicBezier( + v[2], // pos + v[2] + (v[2] - v[1]), + v[3] - (v[3] - v[2]) + ))); + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(6, osgAnimation::Vec3CubicBezier( + v[3], // pos + v[3] + (v[3] - v[2]), + v[4] - (v[4] - v[3]) + ))); + keys->push_back(osgAnimation::Vec3CubicBezierKeyframe(8, osgAnimation::Vec3CubicBezier( + v[4], // pos + v[4] + (v[4] - v[3]), + v[0] - (v[0] - v[4]) + ))); + + callback->start(); + node->setUpdateCallback(callback); + node->addChild(setupCube()); + return node; +} + +int main (int argc, char* argv[]) +{ + osgViewer::Viewer viewer; + osgGA::TrackballManipulator* manipulator = new osgGA::TrackballManipulator(); + viewer.setCameraManipulator(manipulator); + + osg::Group* root = new osg::Group; + root->setInitialBound(osg::BoundingSphere(osg::Vec3(10,0,10), 30)); + root->addChild(createAxis()); + + osg::MatrixTransform* node = setupAnimtkNode(); + node->addChild(createAxis()); + root->addChild(node); + + viewer.setSceneData( root ); + viewer.realize(); + + while (!viewer.done()) + { + viewer.frame(); + } + +} diff --git a/examples/osganimationskinning/CMakeLists.txt b/examples/osganimationskinning/CMakeLists.txt new file mode 100644 index 000000000..0510d7c0d --- /dev/null +++ b/examples/osganimationskinning/CMakeLists.txt @@ -0,0 +1,3 @@ +SET(TARGET_SRC osganimationskinning.cpp ) +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +SETUP_EXAMPLE(osganimationskinning) diff --git a/examples/osganimationskinning/osganimationskinning.cpp b/examples/osganimationskinning/osganimationskinning.cpp new file mode 100644 index 000000000..e21710cff --- /dev/null +++ b/examples/osganimationskinning/osganimationskinning.cpp @@ -0,0 +1,267 @@ +/* -*-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 + +osg::Geode* createAxis() +{ + osg::Geode* geode (new osg::Geode()); + osg::Geometry* geometry (new osg::Geometry()); + + osg::Vec3Array* vertices (new osg::Vec3Array()); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 1.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 1.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 1.0)); + geometry->setVertexArray (vertices); + + osg::Vec4Array* colors (new osg::Vec4Array()); + colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); + geometry->setColorArray (colors); + + geometry->setColorBinding (osg::Geometry::BIND_PER_VERTEX); + geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,6)); + + geode->addDrawable( geometry ); + return geode; +} + +osgAnimation::RigGeometry* createTesselatedBox(int nsplit, float size) +{ + osgAnimation::RigGeometry* geometry = new osgAnimation::RigGeometry; + + osg::ref_ptr vertices (new osg::Vec3Array()); + osg::ref_ptr colors (new osg::Vec3Array()); + geometry->setVertexArray (vertices.get()); + geometry->setColorArray (colors.get()); + geometry->setColorBinding (osg::Geometry::BIND_PER_VERTEX); + + float step = size / nsplit; + float s = 0.5/4.0; + for (int i = 0; i < nsplit; i++) + { + float x = -1 + i * step; + std::cout << x << std::endl; + vertices->push_back (osg::Vec3 ( x, s, s)); + vertices->push_back (osg::Vec3 ( x, -s, s)); + vertices->push_back (osg::Vec3 ( x, -s, -s)); + vertices->push_back (osg::Vec3 ( x, s, -s)); + osg::Vec3 c (0,0,0); + c[i%3] = 1; + colors->push_back (c); + colors->push_back (c); + colors->push_back (c); + colors->push_back (c); + } + + osg::ref_ptr array = new osg::UIntArray; + for (int i = 0; i < nsplit - 1; i++) + { + int base = i * 4; + array->push_back(base); + array->push_back(base+1); + array->push_back(base+4); + array->push_back(base+1); + array->push_back(base+5); + array->push_back(base+4); + + array->push_back(base+3); + array->push_back(base); + array->push_back(base+4); + array->push_back(base+7); + array->push_back(base+3); + array->push_back(base+4); + + array->push_back(base+5); + array->push_back(base+1); + array->push_back(base+2); + array->push_back(base+2); + array->push_back(base+6); + array->push_back(base+5); + + array->push_back(base+2); + array->push_back(base+3); + array->push_back(base+7); + array->push_back(base+6); + array->push_back(base+2); + array->push_back(base+7); + } + + geometry->addPrimitiveSet(new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, array->size(), &array->front())); + geometry->setUseDisplayList( false ); + return geometry; +} + + +void initVertexMap(osgAnimation::Bone* b0, + osgAnimation::Bone* b1, + osgAnimation::Bone* b2, + osgAnimation::RigGeometry* geom, + osg::Vec3Array* array) +{ + osgAnimation::VertexInfluenceSet vertexesInfluences; + osgAnimation::VertexInfluenceMap* vim = new osgAnimation::VertexInfluenceMap; + + (*vim)[b0->getName()].setName(b0->getName()); + (*vim)[b1->getName()].setName(b1->getName()); + (*vim)[b2->getName()].setName(b2->getName()); + + for (int i = 0; i < (int)array->size(); i++) + { + float val = (*array)[i][0]; + std::cout << val << std::endl; + if (val >= -1 && val <= 0) + (*vim)[b0->getName()].push_back(osgAnimation::VertexIndexWeight(i,1)); + else if ( val > 0 && val <= 1) + (*vim)[b1->getName()].push_back(osgAnimation::VertexIndexWeight(i,1)); + else if ( val > 1) + (*vim)[b2->getName()].push_back(osgAnimation::VertexIndexWeight(i,1)); + } + + geom->setInfluenceMap(vim); +} + + + +int main (int argc, char* argv[]) +{ + osgViewer::Viewer viewer; + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + + osg::ref_ptr skelroot = new osgAnimation::Skeleton; + osg::ref_ptr root = new osgAnimation::Bone; + { + root->setBindMatrixInBoneSpace(osg::Matrix::identity()); + root->setBindMatrixInBoneSpace(osg::Matrix::translate(-1,0,0)); + root->setName("root"); + } + + osg::ref_ptr right0 = new osgAnimation::Bone; + right0->setBindMatrixInBoneSpace(osg::Matrix::translate(1,0,0)); + right0->setName("right0"); + + osg::ref_ptr right1 = new osgAnimation::Bone; + right1->setBindMatrixInBoneSpace(osg::Matrix::translate(1,0,0)); + right1->setName("right1"); + + root->addChild(right0.get()); + right0->addChild(right1.get()); + skelroot->addChild(root.get()); + + osg::ref_ptr manager = new osgAnimation::AnimationManager; + + osgAnimation::Animation* anim = new osgAnimation::Animation; + { + osgAnimation::QuatKeyframeContainer* keys0 = new osgAnimation::QuatKeyframeContainer; + osg::Quat rotate; + rotate.makeRotate(osg::PI_2, osg::Vec3(0,0,1)); + keys0->push_back(osgAnimation::QuatKeyframe(0,osg::Quat(0,0,0,1))); + keys0->push_back(osgAnimation::QuatKeyframe(3,rotate)); + keys0->push_back(osgAnimation::QuatKeyframe(6,rotate)); + osgAnimation::QuatSphericalLinearSampler* sampler = new osgAnimation::QuatSphericalLinearSampler; + sampler->setKeyframeContainer(keys0); + osgAnimation::AnimationUpdateCallback* cb = dynamic_cast(right0->getUpdateCallback()); + cb->setName("right0"); + osgAnimation::QuatSphericalLinearChannel* channel = new osgAnimation::QuatSphericalLinearChannel(sampler); + channel->setName("quaternion"); + channel->setTargetName("right0"); + //cb->link(channel); + anim->addChannel(channel); + } + + { + osgAnimation::QuatKeyframeContainer* keys1 = new osgAnimation::QuatKeyframeContainer; + osg::Quat rotate; + rotate.makeRotate(osg::PI_2, osg::Vec3(0,0,1)); + keys1->push_back(osgAnimation::QuatKeyframe(0,osg::Quat(0,0,0,1))); + keys1->push_back(osgAnimation::QuatKeyframe(3,osg::Quat(0,0,0,1))); + keys1->push_back(osgAnimation::QuatKeyframe(6,rotate)); + osgAnimation::QuatSphericalLinearSampler* sampler = new osgAnimation::QuatSphericalLinearSampler; + sampler->setKeyframeContainer(keys1); + osgAnimation::QuatSphericalLinearChannel* channel = new osgAnimation::QuatSphericalLinearChannel(sampler); + osgAnimation::AnimationUpdateCallback* cb = dynamic_cast(right1->getUpdateCallback()); + cb->setName("right1"); + channel->setName("quaternion"); + channel->setTargetName("right1"); + //cb->link(channel); + anim->addChannel(channel); + } + manager->registerAnimation(anim); + manager->buildTargetReference(); + + // let's start ! + manager->playAnimation(anim); + + // we will use local data from the skeleton + osg::Group* scene = new osg::Group; + osg::MatrixTransform* rootTransform = new osg::MatrixTransform; + rootTransform->setMatrix(osg::Matrix::rotate(osg::PI_2,osg::Vec3(1,0,0))); + right0->addChild(createAxis()); + right0->setDataVariance(osg::Object::DYNAMIC); + right1->addChild(createAxis()); + right1->setDataVariance(osg::Object::DYNAMIC); + osg::MatrixTransform* trueroot = new osg::MatrixTransform; + trueroot->setMatrix(osg::Matrix(root->getMatrixInBoneSpace().ptr())); + trueroot->addChild(createAxis()); + trueroot->setDataVariance(osg::Object::DYNAMIC); + rootTransform->addChild(manager.get()); + scene->addChild(rootTransform); + manager->addChild(skelroot.get()); + + osgAnimation::RigGeometry* geom = createTesselatedBox(4, 4.0); + osg::Geode* geode = new osg::Geode; + geode->addDrawable(geom); + skelroot->addChild(geode); + osg::ref_ptr src = dynamic_cast(geom->getVertexArray()); + geom->getOrCreateStateSet()->setMode(GL_LIGHTING, false); + geom->setDataVariance(osg::Object::DYNAMIC); + OSGANIMATION_ASSERT(src); + + initVertexMap(root.get(), right0.get(), right1.get(), geom, src.get()); + + geom->buildVertexSet(); + geom->buildTransformer(skelroot.get()); + + // let's run ! + viewer.setSceneData( scene ); + viewer.realize(); + + while (!viewer.done()) + { + viewer.frame(); + } + + return 0; +} + + diff --git a/examples/osganimationsolid/CMakeLists.txt b/examples/osganimationsolid/CMakeLists.txt new file mode 100644 index 000000000..e4bf0ef54 --- /dev/null +++ b/examples/osganimationsolid/CMakeLists.txt @@ -0,0 +1,3 @@ +SET(TARGET_SRC osganimationsolid.cpp ) +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +SETUP_EXAMPLE(osganimationsolid) diff --git a/examples/osganimationsolid/osganimationsolid.cpp b/examples/osganimationsolid/osganimationsolid.cpp new file mode 100644 index 000000000..274ff5c99 --- /dev/null +++ b/examples/osganimationsolid/osganimationsolid.cpp @@ -0,0 +1,118 @@ +/* -*-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 + +using namespace osgAnimation; + +osg::ref_ptr createAxis() +{ + osg::ref_ptr geode (new osg::Geode()); + osg::ref_ptr geometry (new osg::Geometry()); + + osg::ref_ptr vertices (new osg::Vec3Array()); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 10.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 10.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 0.0)); + vertices->push_back (osg::Vec3 ( 0.0, 0.0, 10.0)); + geometry->setVertexArray (vertices.get()); + + osg::ref_ptr colors (new osg::Vec4Array()); + colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); + colors->push_back (osg::Vec4 (0.0f, 0.0f, 1.0f, 1.0f)); + geometry->setColorArray (colors.get()); + + geometry->setColorBinding (osg::Geometry::BIND_PER_VERTEX); + geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES,0,6)); + + geode->addDrawable( geometry.get() ); + geode->getOrCreateStateSet()->setMode(GL_LIGHTING, false); + return geode; +} + + +int main (int argc, char* argv[]) +{ + osgViewer::Viewer viewer; + viewer.setCameraManipulator(new osgGA::TrackballManipulator()); + + osg::Group* root = new osg::Group; + + osg::ref_ptr axe = createAxis(); + osg::ref_ptr geode = new osg::Geode; + geode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0.0f,0.0f,0.0f),0.5))); + + osg::ref_ptr trans = new osg::MatrixTransform(); + trans->setName("AnimatedNode"); + trans->setDataVariance(osg::Object::DYNAMIC); + trans->setUpdateCallback(new osgAnimation::UpdateTransform("AnimatedCallback")); + trans->setMatrix(osg::Matrix::identity()); + trans->addChild (geode.get()); + + root->addChild (axe.get()); + root->addChild (trans.get()); + + // Define a scheduler for our animations + osgAnimation::AnimationManager* mng = new osgAnimation::AnimationManager(); + + + mng->addChild(root); + + // And we finaly define our channel + osgAnimation::Vec3LinearChannel* channelAnimation1 = new osgAnimation::Vec3LinearChannel; + channelAnimation1->setTargetName("AnimatedCallback"); + channelAnimation1->setName("position"); + channelAnimation1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0,0,0))); + channelAnimation1->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(2, osg::Vec3(1,1,0))); + osgAnimation::Animation* anim1 = new osgAnimation::Animation; + anim1->addChannel(channelAnimation1); + anim1->setPlaymode(osgAnimation::Animation::PPONG); + + + osgAnimation::Vec3LinearChannel* channelAnimation2 = new osgAnimation::Vec3LinearChannel; + channelAnimation2->setTargetName("AnimatedCallback"); + channelAnimation2->setName("euler"); + channelAnimation2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(0, osg::Vec3(0,0,0))); + channelAnimation2->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(1.5, osg::Vec3(2*osg::PI,0,0))); + osgAnimation::Animation* anim2 = new osgAnimation::Animation; + anim2->addChannel(channelAnimation2); + anim2->setPlaymode(osgAnimation::Animation::LOOP); + + + // We register all animation inside the scheduler + mng->registerAnimation(anim1); + mng->registerAnimation(anim2); + + mng->playAnimation(anim1); + mng->playAnimation(anim2); + + viewer.setSceneData( mng ); + return viewer.run(); +} diff --git a/examples/osganimationtimeline/CMakeLists.txt b/examples/osganimationtimeline/CMakeLists.txt new file mode 100644 index 000000000..daface860 --- /dev/null +++ b/examples/osganimationtimeline/CMakeLists.txt @@ -0,0 +1,3 @@ +SET(TARGET_SRC osganimationtimeline.cpp ) +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +SETUP_EXAMPLE(osganimationtimeline) diff --git a/examples/osganimationtimeline/osganimationtimeline.cpp b/examples/osganimationtimeline/osganimationtimeline.cpp new file mode 100644 index 000000000..0a18bb856 --- /dev/null +++ b/examples/osganimationtimeline/osganimationtimeline.cpp @@ -0,0 +1,209 @@ +/* -*-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 +#include +#include + + +struct NoseBegin : public osgAnimation::Action::Callback +{ + virtual void operator()(osgAnimation::Action* action) + { + std::cout << "sacrebleu, it scratches my nose, let me scratch it" << std::endl; + std::cout << "process NoseBegin call back " << action->getName() << std::endl << std::endl; + } +}; + +struct NoseEnd : public osgAnimation::Action::Callback +{ + virtual void operator()(osgAnimation::Action* action) + { + std::cout << "shhhrt shrrrrt shhhhhhrrrrt, haaa it's better"<< std::endl; + std::cout << "process NoseEnd call back " << action->getName() << std::endl << std::endl; + } +}; + +struct ExampleTimelineUsage : public osgGA::GUIEventHandler +{ + osg::ref_ptr _mainLoop; + osg::ref_ptr _scratchHead; + osg::ref_ptr _scratchNose; + osg::ref_ptr _manager; + + bool _releaseKey; + + ExampleTimelineUsage(osgAnimation::AnimationManagerTimeline* manager) + { + _releaseKey = false; + _manager = manager; + + osgAnimation::AnimationMap map = _manager->getAnimationMap(); + _mainLoop = new osgAnimation::StripAnimation(map["Idle_Main"].get(),0.0,0.0); + _mainLoop->setLoop(0); // means forever + + _scratchHead = new osgAnimation::StripAnimation(map["Idle_Head_Scratch.02"].get(),0.2,0.3); + _scratchHead->setLoop(1); // one time + + map["Idle_Nose_Scratch.01"]->setDuration(10.0); // set this animation duration to 10 seconds + _scratchNose = new osgAnimation::StripAnimation(map["Idle_Nose_Scratch.01"].get(),0.2,0.3); + _scratchNose->setLoop(1); // one time + + // add the main loop at priority 0 at time 0. + + osgAnimation::Timeline* tml = _manager->getTimeline(); + tml->play(); + tml->addActionAt(0.0, _mainLoop.get(), 0); + + + // add a scratch head priority 1 at 3.0 second. + tml->addActionAt(5.0, _scratchHead.get(), 1); + + // populate time with scratch head + for (int i = 1; i < 20; i++) + { + // we add a scratch head priority 1 each 10 second + // note: + // it's possible to add the same instance more then once on the timeline + // the only things you need to take care is if you remove it. It will remove + // all instance that exist on the timeline. If you need to differtiate + // it's better to create a new instance + tml->addActionAt(5.0 + 10.0 * i, _scratchHead.get(), 1); + } + + // we will add the scratch nose action only when the player hit a key + // in the operator() + + // now we will add callback at end and begin of animation of Idle_Nose_Scratch.02 + _scratchNose->setCallback(0.0, new NoseBegin); + _scratchNose->setCallback(_scratchNose->getNumFrames()-1, new NoseEnd); + } + + bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) + { + if (ea.getEventType() == osgGA::GUIEventAdapter::KEYUP) + { + _releaseKey = true; + } + return false; + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + if (_releaseKey) // we hit a key and release it execute an action + { + osgAnimation::Timeline* tml = _manager->getTimeline(); + // dont play if already playing + if (!tml->isActive(_scratchNose.get())) + { + // add this animation on top of two other + // we add one to evaluate the animation at the next frame, else we + // will miss the current frame + tml->addActionAt(tml->getCurrentFrame() + 1, _scratchNose.get(), 2); + } + _releaseKey = false; + } + } + else + { + osgGA::EventVisitor* ev = dynamic_cast(nv); + if (ev && ev->getActionAdapter() && !ev->getEvents().empty()) + { + for(osgGA::EventQueue::Events::iterator itr = ev->getEvents().begin(); + itr != ev->getEvents().end(); + ++itr) + { + handleWithCheckAgainstIgnoreHandledEventsMask(*(*itr), *(ev->getActionAdapter()), node, nv); + } + } + } + traverse(node, nv); + } + +}; + + +int main (int argc, char* argv[]) +{ + std::cerr << "This example workd only with osgAnimation/nathan.osg" << std::endl; + + osg::ArgumentParser psr(&argc, argv); + + osgViewer::Viewer viewer(psr); + osg::ref_ptr group = new osg::Group(); + + std::string file = "osgAnimation/nathan.osg"; + if(argc >= 2) + file = psr[1]; + + // replace the manager + osgAnimation::AnimationManagerBase* animationManager = dynamic_cast(osgDB::readNodeFile(file)); + if(!animationManager) + { + std::cerr << "Couldn't convert the file's toplevel object into an AnimationManager." << std::endl; + return 1; + } + + osg::ref_ptr tl = new osgAnimation::AnimationManagerTimeline(*animationManager); + + animationManager->removeChildren(0, animationManager->getNumChildren()); + ExampleTimelineUsage* callback = new ExampleTimelineUsage(tl.get()); + group->addChild(tl.get()); + group->setEventCallback(callback); + group->setUpdateCallback(callback); + + + // add the state manipulator + viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) ); + + // add the thread model handler + viewer.addEventHandler(new osgViewer::ThreadingHandler); + + // add the window size toggle handler + viewer.addEventHandler(new osgViewer::WindowSizeHandler); + + // add the stats handler + viewer.addEventHandler(new osgViewer::StatsHandler); + + // add the help handler + viewer.addEventHandler(new osgViewer::HelpHandler(psr.getApplicationUsage())); + + // add the LOD Scale handler + viewer.addEventHandler(new osgViewer::LODScaleHandler); + + // add the screen capture handler + viewer.addEventHandler(new osgViewer::ScreenCaptureHandler); + + viewer.setSceneData(group.get()); + + return viewer.run(); +} + + diff --git a/examples/osganimationviewer/AnimtkViewer b/examples/osganimationviewer/AnimtkViewer new file mode 100644 index 000000000..d6a754ff2 --- /dev/null +++ b/examples/osganimationviewer/AnimtkViewer @@ -0,0 +1,123 @@ +/* -*-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. + * + * Authors: + * Cedric Pinson + * jeremy Moles +*/ + + +#ifndef ANIMTKVIEWER_H +#define ANIMTKVIEWER_H + +#include +#include +#include + +class AnimtkViewerModelController +{ +public: + typedef std::vector AnimationMapVector; + + static AnimtkViewerModelController& instance() + { + static AnimtkViewerModelController avmc; + return avmc; + } + + static bool setModel(osgAnimation::AnimationManager* model) + { + AnimtkViewerModelController& self = instance(); + self._model = model; + self._map = self._model->getAnimationMap(); + + for(osgAnimation::AnimationMap::iterator it = self._map.begin(); it != self._map.end(); it++) + self._amv.push_back(it->first); + + return true; + } + + bool list() + { + std::cout << "Animation List:" << std::endl; + for(osgAnimation::AnimationMap::iterator it = _map.begin(); it != _map.end(); it++) + std::cout << it->first << std::endl; + return true; + } + + bool play() + { + if(_focus < _amv.size()) + { + std::cout << "Play " << _amv[_focus] << std::endl; + _model->playAnimation(_map[_amv[_focus]].get()); + return true; + } + + return false; + } + + bool stop() + { + if(_focus < _amv.size()) + { + std::cout << "Stop " << _amv[_focus] << std::endl; + _model->stopAnimation(_map[_amv[_focus]].get()); + return true; + } + return false; + } + + bool next() + { + _focus = (_focus + 1) % _map.size(); + std::cout << "Current now is " << _amv[_focus] << std::endl; + return true; + } + + bool previous() + { + _focus = (_map.size() + _focus - 1) % _map.size(); + std::cout << "Current now is " << _amv[_focus] << std::endl; + return true; + } + + bool playByName(const std::string& name) + { + for(unsigned int i = 0; i < _amv.size(); i++) if(_amv[i] == name) _focus = i; + _model->playAnimation(_map[name].get()); + return true; + } + + const std::string& getCurrentAnimationName() const + { + return _amv[_focus]; + } + + const AnimationMapVector& getAnimationMap() const + { + return _amv; + } + +private: + osg::ref_ptr _model; + osgAnimation::AnimationMap _map; + AnimationMapVector _amv; + unsigned int _focus; + + AnimtkViewerModelController(): + _model(0), + _focus(0) {} +}; + +#endif diff --git a/examples/osganimationviewer/AnimtkViewer.cpp b/examples/osganimationviewer/AnimtkViewer.cpp new file mode 100644 index 000000000..8d619c3ba --- /dev/null +++ b/examples/osganimationviewer/AnimtkViewer.cpp @@ -0,0 +1,116 @@ +/* -*-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. + * + * Authors: + * Cedric Pinson + * jeremy Moles +*/ + +#include "AnimtkViewerKeyHandler" +#include "AnimtkViewerGUI" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const int WIDTH = 1440; +const int HEIGHT = 900; + + +osg::Geode* createAxis() +{ + osg::Geode* geode = new osg::Geode(); + osg::Geometry* geometry = new osg::Geometry(); + osg::Vec3Array* vertices = new osg::Vec3Array(); + osg::Vec4Array* colors = new osg::Vec4Array(); + + vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); + vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); + vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); + vertices->push_back(osg::Vec3(0.0f, 1.0f, 0.0f)); + vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); + vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f)); + + colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); + colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); + colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); + colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); + + geometry->setVertexArray(vertices); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 6)); + geometry->getOrCreateStateSet()->setMode(GL_LIGHTING, false); + + geode->addDrawable(geometry); + + return geode; +} + +int main(int argc, char** argv) +{ + osg::ArgumentParser psr(&argc, argv); + + if(argc < 2) + { + std::cerr << "usage: AnimtkViewer " << std::endl; + return 1; + } + + osgViewer::Viewer viewer(psr); + osg::ref_ptr group = new osg::Group(); + + osgAnimation::AnimationManager* animationManager = dynamic_cast(osgDB::readNodeFile(psr[1])); + + if(!animationManager) + { + std::cerr << "Couldn't convert the file's toplevel object into an AnimationManager." << std::endl; + return 1; + } + + // Set our Singleton's model. + AnimtkViewerModelController::setModel(animationManager); + + animationManager->addChild(createAxis()); + + AnimtkViewerGUI* gui = new AnimtkViewerGUI(&viewer, WIDTH, HEIGHT, 0x1234); + osg::Camera* camera = gui->createParentOrthoCamera(); + + animationManager->setNodeMask(0x0001); + + group->addChild(animationManager); + group->addChild(camera); + + viewer.addEventHandler(new AnimtkKeyEventHandler()); + viewer.addEventHandler(new osgViewer::StatsHandler()); + viewer.addEventHandler(new osgViewer::WindowSizeHandler()); + viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); + viewer.addEventHandler(new osgWidget::MouseHandler(gui)); + viewer.addEventHandler(new osgWidget::KeyboardHandler(gui)); + viewer.addEventHandler(new osgWidget::ResizeHandler(gui, camera)); + viewer.setSceneData(group.get()); + + viewer.setUpViewInWindow(40, 40, WIDTH, HEIGHT); + + return viewer.run(); +} diff --git a/examples/osganimationviewer/AnimtkViewerGUI b/examples/osganimationviewer/AnimtkViewerGUI new file mode 100644 index 000000000..543c4027a --- /dev/null +++ b/examples/osganimationviewer/AnimtkViewerGUI @@ -0,0 +1,46 @@ +/* -*-c++-*- + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + Authors: + + Jeremy Moles + Cedric Pinson +*/ + +#ifndef ANIMTKVIEWERGUI_H +#define ANIMTKVIEWERGUI_H + +#include + +class AnimtkViewerGUI: public osgWidget::WindowManager { + osg::ref_ptr _buttonBox; + osg::ref_ptr _listBox; + osg::ref_ptr _labelBox; + +protected: + osgWidget::Widget* _createButton(const std::string&); + + bool _buttonPush(osgWidget::Event&); + bool _listMouseHover(osgWidget::Event&); + + void _createButtonBox(); + void _createListBox(); + void _createLabelBox(); + +public: + AnimtkViewerGUI(osgViewer::View*, float, float, unsigned int); +}; + +#endif diff --git a/examples/osganimationviewer/AnimtkViewerGUI.cpp b/examples/osganimationviewer/AnimtkViewerGUI.cpp new file mode 100644 index 000000000..1f6967023 --- /dev/null +++ b/examples/osganimationviewer/AnimtkViewerGUI.cpp @@ -0,0 +1,407 @@ +/* -*-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. + * + * Authors: + * Cedric Pinson + * jeremy Moles +*/ + +#include "AnimtkViewer" +#include "AnimtkViewerGUI" + +#include +#include +#include + +const std::string IMAGE_PATH = "osgAnimation/Images/"; + +template +struct Sampler: public osg::Drawable::UpdateCallback +{ + T _motion; + Sampler() { + } +}; + +typedef Sampler WidgetSampler; + +struct ButtonFunctor: public WidgetSampler +{ + float _direction; + float _previous; + + const float _speed; + + ButtonFunctor(): _speed(5) { _direction = -_speed; _previous = 0;} + + bool enter(osgWidget::Event& ev) + { + _direction = _speed; + return true; + } + + bool leave(osgWidget::Event& ev) + { + _direction = -_speed; + return true; + } + + void update(osg::NodeVisitor* nv , osg::Drawable* geom) + { + const osg::FrameStamp* f = nv->getFrameStamp(); + float dt = f->getSimulationTime() - _previous; + _previous = f->getSimulationTime(); + update(dt,dynamic_cast(geom)); + } + + void update(float t, osgWidget::Widget* w) + { + if (!w) return; + _motion.update(t*_direction); + float val = _motion.getValue()*0.5; + val += 0.5; + if (val >= 1.0) + val = 1.0; + w->setColor(osg::Vec4(val, val, val, 1)); + } +}; + +struct LabelFunctor: public WidgetSampler +{ + float _previous; + bool _active; + + const float _fadeOutTime; + + osgAnimation::OutCubicMotion _scaleSampler; + + LabelFunctor(): + _fadeOutTime(1.5f) +{ + _previous = 0.0f; + _active = false; + + _scaleSampler = osgAnimation::OutCubicMotion(0.5, 1.0, 1.0); + } + + void setActive(bool active) +{ + _active = active; + + if(active) _motion.reset(); + + _scaleSampler.reset(); + } + + void update(osg::NodeVisitor* nv , osg::Drawable* geom) +{ + const osg::FrameStamp* f = nv->getFrameStamp(); + + float st = f->getSimulationTime(); + float dt = st - _previous; + + _previous = st; + + if(!_active) return; + + update(dt, dynamic_cast(geom)); + updateScale(dt, dynamic_cast(geom)); + } + + void update(float t, osgWidget::Label* w) +{ + if(!w) return; + + _motion.update(t / _fadeOutTime); + + float val = _motion.getValue(); + + if(val >= 1.0f) { + _motion.reset(); + _active = false; + } + + w->setFontColor(osg::Vec4(0.0f, 0.0f, 0.0f, (1.0f - val) * 0.7f)); + } + + void updateScale(float t, osgWidget::Label* w) +{ + _scaleSampler.update(t); + float val = _scaleSampler.getValue(); + osgWidget::Window* win = w->getParent(); + win->setScale(val); + win->update(); + } + +}; + + +struct ListFunctor: public osg::NodeCallback +{ + float _previous; + int _direction; + + osgAnimation::InQuadMotion _transformSampler; + + ListFunctor() +{ + _direction = 1; + _previous = 0; + + _transformSampler.update(1.0f); + } + + void toggleShown() +{ + if(_direction == 1) _direction = -1; + + else _direction = 1; + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + const osg::FrameStamp* f = nv->getFrameStamp(); + + float st = f->getSimulationTime(); + float dt = st - _previous; + + _previous = st; + + _transformSampler.update((dt * _direction) / 0.5f); + + float val = _transformSampler.getValue(); + + if(val > 1.0f || val < 0.0f) return; + + osgWidget::Window* win = dynamic_cast(node); + + float w = win->getWidth(); + float wmw = win->getWindowManager()->getWidth(); + + win->setX((wmw - w) + (val * w)); + win->update(); + } + +}; + +// This is a temporary hack to "prevent" dragging on Widgets and Windows. +bool eatDrag(osgWidget::Event&) +{ + return true; +} + +AnimtkViewerGUI::AnimtkViewerGUI(osgViewer::View* view, float w, float h, unsigned int mask): + osgWidget::WindowManager(view, w, h, mask, 0) +{ + _createButtonBox(); + _createLabelBox(); + _createListBox(); + + _labelBox->setAnchorHorizontal(osgWidget::Window::HA_LEFT); + _labelBox->setY(74.0f); + _labelBox->setVisibilityMode(osgWidget::Window::VM_ENTIRE); + + _listBox->setOrigin(getWidth(), 74.0f); + + addChild(_buttonBox.get()); + addChild(_labelBox.get()); + addChild(_listBox.get()); + + resizeAllWindows(); + + // Remember, you can't call resizePercent until AFTER the box is parented + // by a WindowManager; how could it possibly resize itself if it doesn't know + // how large it's viewable area is? + _buttonBox->resizePercent(100.0f); + _buttonBox->resizeAdd(0.0f, 10.0f); +} + +osgWidget::Widget* AnimtkViewerGUI::_createButton(const std::string& name) +{ + osgWidget::Widget* b = new osgWidget::Widget(name, 64.0f, 64.0f); + + if(!b) return 0; + + b->setImage(IMAGE_PATH + name + ".png", true); + b->setEventMask(osgWidget::EVENT_MASK_MOUSE_DRAG); + + ButtonFunctor* bt = new ButtonFunctor(); + b->setUpdateCallback(bt); + + b->addCallback(new osgWidget::Callback(&ButtonFunctor::enter, bt, osgWidget::EVENT_MOUSE_ENTER)); + b->addCallback(new osgWidget::Callback(&ButtonFunctor::leave, bt, osgWidget::EVENT_MOUSE_LEAVE)); + b->addCallback(new osgWidget::Callback(&AnimtkViewerGUI::_buttonPush, this, osgWidget::EVENT_MOUSE_PUSH)); + b->addCallback(new osgWidget::Callback(&eatDrag, osgWidget::EVENT_MOUSE_DRAG)); + + return b; +} + +bool AnimtkViewerGUI::_listMouseHover(osgWidget::Event& ev) +{ + osgWidget::Label* l = dynamic_cast(ev.getWidget()); + + if(!l) return false; + + if(ev.type == osgWidget::EVENT_MOUSE_ENTER) l->setFontColor(1.0f, 1.0f, 1.0f, 1.0f); + + else if(ev.type == osgWidget::EVENT_MOUSE_LEAVE) l->setFontColor(1.0f, 1.0f, 1.0f, 0.3f); + + else if(ev.type == osgWidget::EVENT_MOUSE_PUSH) { + AnimtkViewerModelController::instance().playByName(ev.getWidget()->getName()); + } + + else return false; + + return true; +} + +bool AnimtkViewerGUI::_buttonPush(osgWidget::Event& ev) +{ + if(!ev.getWidget()) return false; + + osgWidget::Label* l = static_cast(_labelBox->getByName("label")); + + if(!l) return false; + + LabelFunctor* lf = dynamic_cast(l->getUpdateCallback()); + + if(!lf) return false; + + // We're safe at this point, so begin processing. + AnimtkViewerModelController& mc = AnimtkViewerModelController::instance(); + std::string name = ev.getWidget()->getName(); + + if(name == "play") mc.play(); + + else if(name == "stop") mc.stop(); + + else if(name == "next") +{ + mc.next(); + + l->setFontColor(osg::Vec4(0.0f, 0.0f, 0.0f, 0.7f)); + l->setLabel(mc.getCurrentAnimationName()); + lf->setActive(true); + } + + else if(name == "back") +{ + mc.previous(); + + l->setFontColor(osg::Vec4(0.0f, 0.0f, 0.0f, 0.7f)); + l->setLabel(mc.getCurrentAnimationName()); + lf->setActive(true); + } + + else if(name == "pause") +{ + } + + else if(name == "open") +{ + ListFunctor* lsf = dynamic_cast(_listBox->getUpdateCallback()); + + if(!lsf) return false; + + lsf->toggleShown(); + } + + else return false; + + return true; +} + +void AnimtkViewerGUI::_createButtonBox() +{ + _buttonBox = new osgWidget::Box("buttonBox", osgWidget::Box::HORIZONTAL); + + osgWidget::Widget* space = new osgWidget::Widget("nullSpace", 0.0f, 0.0f); + osgWidget::Widget* back = _createButton("back"); + osgWidget::Widget* next = _createButton("next"); + osgWidget::Widget* play = _createButton("play"); + osgWidget::Widget* pause = _createButton("pause"); + osgWidget::Widget* stop = _createButton("stop"); + osgWidget::Widget* open = _createButton("open"); + + space->setCanFill(true); + space->setColor(0.0f, 0.0f, 0.0f, 0.0f); + + _buttonBox->addWidget(space); + _buttonBox->addWidget(back); + _buttonBox->addWidget(next); + _buttonBox->addWidget(play); + _buttonBox->addWidget(pause); + _buttonBox->addWidget(stop); + _buttonBox->addWidget(open); + _buttonBox->addWidget(osg::clone(space, "space1", osg::CopyOp::DEEP_COPY_ALL)); + _buttonBox->getBackground()->setColor(0.0f, 0.0f, 0.0f, 0.7f); + + _buttonBox->setEventMask(osgWidget::EVENT_MASK_MOUSE_DRAG); + _buttonBox->addCallback(new osgWidget::Callback(&eatDrag, osgWidget::EVENT_MOUSE_DRAG)); +} + +void AnimtkViewerGUI::_createListBox() +{ + _listBox = new osgWidget::Box("listBox", osgWidget::Box::VERTICAL); + + const AnimtkViewerModelController::AnimationMapVector& amv = + AnimtkViewerModelController::instance().getAnimationMap() + ; + + for( + AnimtkViewerModelController::AnimationMapVector::const_iterator i = amv.begin(); + i != amv.end(); + i++ + ) +{ + osgWidget::Label* label = new osgWidget::Label(*i); + + label->setCanFill(true); + label->setFont("fonts/Vera.ttf"); + label->setFontSize(15); + label->setFontColor(1.0f, 1.0f, 1.0f, 0.3f); + label->setPadding(5.0f); + label->setAlignHorizontal(osgWidget::Widget::HA_RIGHT); + label->setLabel(*i); + label->setEventMask(osgWidget::EVENT_MASK_MOUSE_DRAG); + label->addCallback(new osgWidget::Callback(&AnimtkViewerGUI::_listMouseHover, this, osgWidget::EVENT_MOUSE_ENTER)); + label->addCallback(new osgWidget::Callback(&AnimtkViewerGUI::_listMouseHover, this, osgWidget::EVENT_MOUSE_LEAVE)); + label->addCallback(new osgWidget::Callback(&AnimtkViewerGUI::_listMouseHover, this, osgWidget::EVENT_MOUSE_PUSH)); + + _listBox->addWidget(label); + } + + ListFunctor* lf = new ListFunctor(); + + _listBox->setUpdateCallback(lf); + _listBox->getBackground()->setColor(0.0f, 0.0f, 0.0f, 0.7f); +} + +void AnimtkViewerGUI::_createLabelBox() +{ + _labelBox = new osgWidget::Box("labelBox", osgWidget::Box::VERTICAL); + + osgWidget::Label* label = new osgWidget::Label("label"); + + label->setFont("fonts/Vera.ttf"); + label->setFontSize(50); + label->setFontColor(0.0f, 0.0f, 0.0f, 0.7f); + label->setAlignHorizontal(osgWidget::Widget::HA_LEFT); + label->setPadding(10.0f); + + LabelFunctor* lf = new LabelFunctor(); + label->setUpdateCallback(lf); + + _labelBox->addWidget(label); + _labelBox->getBackground()->setColor(0.0f, 0.0f, 0.0f, 0.0f); +} diff --git a/examples/osganimationviewer/AnimtkViewerKeyHandler b/examples/osganimationviewer/AnimtkViewerKeyHandler new file mode 100644 index 000000000..83c90f597 --- /dev/null +++ b/examples/osganimationviewer/AnimtkViewerKeyHandler @@ -0,0 +1,53 @@ +/* -*-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: + * + * Cedric Pinson + * + */ + +#ifndef ANIMTKVIEWER_KEYHANDLER_H +#define ANIMTKVIEWER_KEYHANDLER_H + +#include "AnimtkViewer" + +#include + +class AnimtkKeyEventHandler : public osgGA::GUIEventHandler +{ +public: + AnimtkKeyEventHandler(); + bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, + osg::Object*, osg::NodeVisitor*); + void printUsage() const; + +protected: + + enum Binding + { + List, + Help, + Play, + Next, + Prev, + }; + + std::map _actionKeys; +}; + +#endif diff --git a/examples/osganimationviewer/AnimtkViewerKeyHandler.cpp b/examples/osganimationviewer/AnimtkViewerKeyHandler.cpp new file mode 100644 index 000000000..3e1d1ba40 --- /dev/null +++ b/examples/osganimationviewer/AnimtkViewerKeyHandler.cpp @@ -0,0 +1,63 @@ +/* -*-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: + * + * Cedric Pinson + * + */ + +#include "AnimtkViewerKeyHandler" + +AnimtkKeyEventHandler::AnimtkKeyEventHandler() +{ + _actionKeys[List] = 'l'; + _actionKeys[Help] = 'h'; + _actionKeys[Play] = 'p'; + _actionKeys[Next] = ']'; + _actionKeys[Prev] = '['; +} + +void AnimtkKeyEventHandler::printUsage() const +{ + std::cout << (char) _actionKeys.find(Help)->second << " for Help" << std::endl; + std::cout << (char) _actionKeys.find(List)->second << " for List" << std::endl; + std::cout << (char) _actionKeys.find(Play)->second << " for Play" << std::endl; + std::cout << (char) _actionKeys.find(Next)->second << " for selext Next item" << std::endl; + std::cout << (char) _actionKeys.find(Prev)->second << " for selext Previous item" << std::endl; +} + + +bool AnimtkKeyEventHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa, + osg::Object*, osg::NodeVisitor*) +{ + AnimtkViewerModelController& mc = AnimtkViewerModelController::instance(); + if(ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN) + { + if (ea.getKey() == _actionKeys[List]) return mc.list(); + else if (ea.getKey() == _actionKeys[Play]) return mc.play(); + else if (ea.getKey() == _actionKeys[Next]) return mc.next(); + else if (ea.getKey() == _actionKeys[Prev]) return mc.previous(); + else if (ea.getKey() == _actionKeys[Help]) + { + printUsage(); + return true; + } + } + + return false; +} diff --git a/examples/osganimationviewer/CMakeLists.txt b/examples/osganimationviewer/CMakeLists.txt new file mode 100644 index 000000000..c18644a66 --- /dev/null +++ b/examples/osganimationviewer/CMakeLists.txt @@ -0,0 +1,7 @@ +SET(TARGET_SRC + AnimtkViewer.cpp + AnimtkViewerKeyHandler.cpp + AnimtkViewerGUI.cpp + ) +SET(TARGET_ADDED_LIBRARIES osgAnimation osgWidget) +SETUP_EXAMPLE(osganimationviewer) diff --git a/include/osgAnimation/Animation b/include/osgAnimation/Animation new file mode 100644 index 000000000..b104f3577 --- /dev/null +++ b/include/osgAnimation/Animation @@ -0,0 +1,103 @@ +/* -*-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_ANIMATION_H +#define OSGANIMATION_ANIMATION_H + +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT Animation : public virtual osg::Object + { + public: + META_Object(osgAnimation, Animation) + + Animation() : _duration(0), _weight(0), _startTime(0), _playmode(LOOP) {} + Animation(const osgAnimation::Animation& anim, const osg::CopyOp&); + + enum PlayMode + { + ONCE, + STAY, + LOOP, + PPONG + }; + + // addChannel insert the channel and call the computeDuration function + void addChannel (Channel* pChannel); + + /** Those accessors let you add and remove channels + * if you modify something that can change the duration + * you are supposed to call computeDuration or setDuration + */ + ChannelList& getChannels(); + const ChannelList& getChannels() const; + + /** Change the duration of animation + * then evaluate the animation in the range 0-duration + * it stretch the animation in time. + * see computeDuration too + */ + void setDuration(double duration); + + + /** Compute duration from channel and keyframes + * if the duration is not specified you should + * call this method before using it + */ + void computeDuration(); + + float getDuration() const; + + + void setWeight (float weight); + float getWeight() const; + + bool update (float time); + void resetTargets(); + + void setPlaymode (PlayMode mode) { _playmode = mode; } + void setStartTime(float time) { _startTime = time;} + float getStartTime() const { return _startTime;} + + protected: + + double computeDurationFromChannels() const; + + ~Animation() {} + + std::string _name; + double _duration; + double _originalDuration; + float _weight; + float _startTime; + PlayMode _playmode; + ChannelList _channels; + + }; + + typedef std::vector > AnimationList; + typedef std::map > AnimationMap; + + +} + +#endif diff --git a/include/osgAnimation/AnimationManager b/include/osgAnimation/AnimationManager new file mode 100644 index 000000000..e7dd98cb7 --- /dev/null +++ b/include/osgAnimation/AnimationManager @@ -0,0 +1,58 @@ +/* -*-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_ANIMATION_MANAGER_H +#define OSGANIMATION_ANIMATION_MANAGER_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT AnimationManager : public AnimationManagerBase + { + public: + + META_Node(osgAnimation, AnimationManager); + + AnimationManager(); + AnimationManager(const AnimationManager& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY) : + AnimationManagerBase(b,copyop) {} + virtual ~AnimationManager(); + + void update (double time); + + void playAnimation (Animation* pAnimation, int priority = 0, float weight = 1.0); + bool stopAnimation (Animation* pAnimation); + + bool findAnimation (Animation* pAnimation); + bool isPlaying (Animation* pAnimation); + bool isPlaying (const std::string& animationName); + + void stopAll(); + + protected: + typedef std::map AnimationLayers; + + AnimationLayers _animationsPlaying; + + // clock + double _lastUpdate; + }; + +} +#endif diff --git a/include/osgAnimation/AnimationManagerBase b/include/osgAnimation/AnimationManagerBase new file mode 100644 index 000000000..53a221621 --- /dev/null +++ b/include/osgAnimation/AnimationManagerBase @@ -0,0 +1,98 @@ +/* -*-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_ANIMATIONMANAGERBASE_H +#define OSGANIMATION_ANIMATIONMANAGERBASE_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT AnimationManagerBase : public osg::Group + { + public: + typedef std::set > TargetSet; + struct UpdateCallback : public osg::NodeCallback + { + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + AnimationManagerBase* b = dynamic_cast(node); + if (b) + { + if (b->needToLink()) + { + /** manager need to link, it means that an animation has been added + so we need to relink all item animated with all animations. + We apply the linker visitor on the manager node to affect + all its children. + But it should not be done here, it should be done in the + update of AnimationManager + */ + b->link(); + } + const osg::FrameStamp* fs = nv->getFrameStamp(); + b->update(fs->getSimulationTime()); + } + } + traverse(node,nv); + } + }; + + AnimationManagerBase(); + AnimationManagerBase(const AnimationManagerBase& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY) : + osg::Group(b,copyop) + { + _animations = b._animations; + _targets = b._targets; + _needToLink = b._needToLink; + } + virtual ~AnimationManagerBase(); + + virtual void update (double time) = 0; + virtual void buildTargetReference(); + virtual void registerAnimation (Animation* animation); + virtual void link(); + virtual bool needToLink() const; + const AnimationList& getAnimationList() const { return _animations;} + AnimationMap getAnimationMap() const; + + void clearTargets() + { + for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); it++) + (*it).get()->reset(); + } + void normalizeTargets() + { + for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); it++) + (*it).get()->normalize(); + } + + + protected: + + AnimationList _animations; + TargetSet _targets; + bool _needToLink; + + }; + +} +#endif diff --git a/include/osgAnimation/Assert b/include/osgAnimation/Assert new file mode 100644 index 000000000..3b4f1e31d --- /dev/null +++ b/include/osgAnimation/Assert @@ -0,0 +1,49 @@ +/* -*-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_ASSERT_H +#define OSGANIMATION_ASSERT_H + +#include +#include +#include + +namespace osgAnimation +{ + + struct ThrowAssert : public std::exception + { + virtual const char* what() const throw () { return mMsg.c_str();} + + std::string mMsg; + ThrowAssert(const std::string& msg, const char* file, int line ) + { + std::stringstream ss; + ss << "Assert (" << msg << ") in file " << file << " at line " << line; + mMsg = ss.str(); + } + + ThrowAssert() {} + virtual ~ThrowAssert() throw () {} + }; +} + +#ifdef OSGANIMATION_ASSERT_THROW +#define OSGANIMATION_ASSERT(a) if (!(a)) throw osgAnimation::ThrowAssert(std::string(#a),__FILE__,__LINE__); + +#else +#define OSGANIMATION_ASSERT(a) {if (!(a)) *((int*)0) = 0;} +#endif + +#endif diff --git a/include/osgAnimation/Bone b/include/osgAnimation/Bone new file mode 100644 index 000000000..cafca6647 --- /dev/null +++ b/include/osgAnimation/Bone @@ -0,0 +1,300 @@ +/* -*-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_BONE_H +#define OSGANIMATION_BONE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + inline osg::Matrix operator* (const osg::Matrix matrix, double v) + { + osg::Matrix result; + for (int i = 0; i < 16; i++) + result.ptr()[i] = matrix.ptr()[i] * v; + return result; + } + + inline osg::Matrix operator+ (const osg::Matrix a, const osg::Matrix b) + { + osg::Matrix result; + for (int i = 0; i < 16; i++) + result.ptr()[i] = a.ptr()[i] + b.ptr()[i]; + return result; + } + +} + + +namespace osgAnimation +{ + + // A bone can't have more than one parent Bone, so sharing a part of Bone's hierarchy + // has not sense. You can share the entire hierarchie but not only a part of + class OSGANIMATION_EXPORT Bone : public osg::Transform + { + public: + typedef osg::ref_ptr PointerType; + typedef std::map BoneMap; + typedef osg::Matrix MatrixType; + + META_Node(osgAnimation, Bone); + Bone(const Bone& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY) : + osg::Transform(b,copyop), + _position(b._position), + _rotation(b._rotation), + _scale(b._scale) {} + + Bone(const std::string& name = "") + { + setName(name); + _needToRecomputeBindMatrix = false; + setUpdateCallback(new UpdateBone); + } + + + struct BoneMapVisitor : public osg::NodeVisitor + { + BoneMap _map; + BoneMapVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + + void apply(osg::Node& node) { return; } + void apply(osg::Transform& node) + { + Bone* bone = dynamic_cast(&node); + if (bone) + { + _map[bone->getName()] = bone; + traverse(node); + } + } + }; + + struct FindNearestParentAnimationManager : public osg::NodeVisitor + { + osg::ref_ptr _manager; + FindNearestParentAnimationManager() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_PARENTS) {} + void apply(osg::Group& node) + { + if (_manager.valid()) + return; + _manager = dynamic_cast(&node); + traverse(node); + } + }; + + + class OSGANIMATION_EXPORT UpdateBone : public AnimationUpdateCallback + { + public: + osg::ref_ptr _position; + osg::ref_ptr _quaternion; + osg::ref_ptr _scale; + + public: + META_Object(osgAnimation, UpdateBone); + UpdateBone(const UpdateBone& apc,const osg::CopyOp& copyop); + + UpdateBone(const std::string& name = "") + { + setName(name); + _quaternion = new osgAnimation::QuatTarget; + _position = new osgAnimation::Vec3Target; + _scale = new osgAnimation::Vec3Target; + } + + void update(osgAnimation::Bone& bone) + { + bone.setTranslation(_position->getValue()); + bone.setRotation(_quaternion->getValue()); + bone.setScale(_scale->getValue()); + bone.dirtyBound(); + } + + bool needLink() const + { + // the idea is to return true if nothing is linked + return !((_position->getCount() + _quaternion->getCount() + _scale->getCount()) > 3); + } + + bool link(osgAnimation::Channel* channel) + { + if (channel->getName().find("quaternion") != std::string::npos) + { + osgAnimation::QuatSphericalLinearChannel* qc = dynamic_cast(channel); + if (qc) + { + qc->setTarget(_quaternion.get()); + return true; + } + } + else if (channel->getName().find("position") != std::string::npos) + { + osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); + if (vc) + { + vc->setTarget(_position.get()); + return true; + } + } + else if (channel->getName().find("scale") != std::string::npos) + { + osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); + if (vc) + { + vc->setTarget(_scale.get()); + return true; + } + } + else + { + std::cerr << "Channel " << channel->getName() << " does not contain a valid symbolic name for this class" << std::endl; + } + return false; + } + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + Bone* b = dynamic_cast(node); + if (b && !_manager.valid()) + { + FindNearestParentAnimationManager finder; + + if (b->getParents().size() > 1) + { + osg::notify(osg::WARN) << "A Bone should not have multi parent ( " << b->getName() << " ) has parents "; + osg::notify(osg::WARN) << "( " << b->getParents()[0]->getName(); + for (int i = 1; i < (int)b->getParents().size(); i++) + osg::notify(osg::WARN) << ", " << b->getParents()[i]->getName(); + osg::notify(osg::WARN) << ")" << std::endl; + return; + } + b->getParents()[0]->accept(finder); + + if (!finder._manager.valid()) + { + osg::notify(osg::WARN) << "Warning can't update Bone, path to parent AnimationManagerBase not found" << std::endl; + return; + } + + _manager = finder._manager.get(); + } + + updateLink(); + update(*b); + } + traverse(node,nv); + } + }; + + + + osg::Vec3 _position; + osg::Quat _rotation; + osg::Vec3 _scale; + + virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; + virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const; + + // flag to recompute bind pose + bool _needToRecomputeBindMatrix; + + // bind data + osg::Matrix _bindInBoneSpace; + osg::Matrix _invBindInSkeletonSpace; + + // bone updated + osg::Matrix _boneInSkeletonSpace; + + Bone* getBoneParent(); + const Bone* getBoneParent() const; + + void setTranslation(const osg::Vec3& trans) { _position = trans;} + void setRotation(const osg::Quat& quat) { _rotation = quat;} + void setScale(const osg::Vec3& scale) { _scale = scale;} + + const osg::Vec3& getTranslation() const { return _position;} + const osg::Quat& getRotation() const { return _rotation;} + osg::Matrix getMatrixInBoneSpace() const { return (osg::Matrix(getRotation())) * osg::Matrix::translate(getTranslation()) * _bindInBoneSpace;} + const osg::Matrix& getBindMatrixInBoneSpace() const { return _bindInBoneSpace; } + const osg::Matrix& getMatrixInSkeletonSpace() const { return _boneInSkeletonSpace; } + const osg::Matrix& getInvBindMatrixInSkeletonSpace() const { return _invBindInSkeletonSpace;} + + void setBindMatrixInBoneSpace(const osg::Matrix& matrix) + { + _bindInBoneSpace = matrix; + _needToRecomputeBindMatrix = true; + } + + inline bool needToComputeBindMatrix() { return _needToRecomputeBindMatrix;} + virtual void computeBindMatrix(); + + bool needLink() const; + + void setNeedToComputeBindMatrix(bool state) { _needToRecomputeBindMatrix = state; } + + /** Add Node to Group. + * If node is not NULL and is not contained in Group then increment its + * reference count, add it to the child list and dirty the bounding + * sphere to force it to recompute on next getBound() and return true for success. + * Otherwise return false. Scene nodes can't be added as child nodes. + */ + virtual bool addChild( Node *child ); + BoneMap getBoneMap(); + + }; + + + inline bool Bone::computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor*) const + { + if (_referenceFrame==RELATIVE_RF) + matrix.preMult(getMatrixInBoneSpace()); + else + matrix = getMatrixInBoneSpace(); + return true; + } + + + inline bool Bone::computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor*) const + { + if (_referenceFrame==RELATIVE_RF) + matrix.postMult(osg::Matrix::inverse(getMatrixInBoneSpace())); + else + matrix = osg::Matrix::inverse(getMatrixInBoneSpace()); + return true; + } + +} +#endif diff --git a/include/osgAnimation/Channel b/include/osgAnimation/Channel new file mode 100644 index 000000000..06ae1aa56 --- /dev/null +++ b/include/osgAnimation/Channel @@ -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. +*/ + +#ifndef OSGANIMATION_CHANNEL_H +#define OSGANIMATION_CHANNEL_H + +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT Channel : public osg::Referenced + { + public: + + Channel(); + virtual ~Channel(); + + virtual void update(float time) = 0; + virtual void reset() = 0; + virtual Target* getTarget() = 0; + + const std::string& getName() const; + void setName(const std::string& name); + + virtual float getStartTime() const = 0; + virtual float getEndTime() const = 0; + + const std::string& getTargetName() const; + void setTargetName(const std::string& name); + + float getWeight() const; + void setWeight(float w); + + virtual Sampler* getSampler() = 0; + virtual const Sampler* getSampler() const = 0; + + protected: + + std::string _targetName; + std::string _name; + float _weight; + }; + + + template + class TemplateChannel : public Channel + { + public: + + typedef typename SamplerType::UsingType UsingType; + typedef TemplateTarget TargetType; + typedef TemplateKeyframeContainer KeyframeContainerType; + + TemplateChannel (SamplerType* s = 0,TargetType* target = 0) + { + if (target) + _target = target; + else + _target = new TargetType; + _sampler = s; + } + + virtual ~TemplateChannel() {} + virtual void update(float time) + { + // skip if weight == 0 + if (_weight < 1e-4) + return; + typename SamplerType::UsingType value; + _sampler->getValueAt(time, value); + _target->update(_weight, value); + } + virtual void reset() { _target->reset(); } + virtual Target* getTarget() { return _target.get();} + + SamplerType* getOrCreateSampler() + { + if (!_sampler.valid()) + _sampler = new SamplerType; + return _sampler.get(); + } + + Sampler* getSampler() { return _sampler.get(); } + const Sampler* getSampler() const { return _sampler.get(); } + + SamplerType* getSamplerTyped() { return _sampler.get();} + const SamplerType* getSamplerTyped() const { return _sampler.get();} + void setSampler(SamplerType* sampler) { _sampler = sampler; } + + TargetType* getTargetTyped() { return _target.get(); } + void setTarget(TargetType* target) { _target = target; } + + virtual float getStartTime() const { OSGANIMATION_ASSERT(_sampler.valid() && "no sampler attached to channel"); return _sampler->getStartTime(); } + virtual float getEndTime() const { OSGANIMATION_ASSERT(_sampler.valid() && "no sampler attached to channel"); return _sampler->getEndTime(); } + + protected: + osg::ref_ptr _target; + osg::ref_ptr _sampler; + }; + + + typedef std::vector > ChannelList; + typedef TemplateChannel DoubleLinearChannel; + typedef TemplateChannel FloatLinearChannel; + + typedef TemplateChannel Vec2LinearChannel; + typedef TemplateChannel Vec3LinearChannel; + typedef TemplateChannel Vec4LinearChannel; + typedef TemplateChannel QuatSphericalLinearChannel; + + typedef TemplateChannel FloatCubicBezierChannel; + typedef TemplateChannel DoubleCubicBezierChannel; + typedef TemplateChannel Vec2CubicBezierChannel; + typedef TemplateChannel Vec3CubicBezierChannel; + typedef TemplateChannel Vec4CubicBezierChannel; + +} + +#endif diff --git a/include/osgAnimation/CubicBezier b/include/osgAnimation/CubicBezier new file mode 100644 index 000000000..bd6da32b0 --- /dev/null +++ b/include/osgAnimation/CubicBezier @@ -0,0 +1,56 @@ +/* -*-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_CUBIC_BEZIER_H +#define OSGANIMATION_CUBIC_BEZIER_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + template + struct TemplateCubicBezier + { + T mPoint[3]; + const T& getP0() const { return mPoint[0];} + const T& getP1() const { return mPoint[1];} + const T& getP2() const { return mPoint[2];} + TemplateCubicBezier(const T& v0, const T& v1, const T& v2) + { + mPoint[0] = v0; + mPoint[1] = v1; + mPoint[2] = v2; + } + + TemplateCubicBezier() {} + + const T& getPosition() const { return mPoint[0];} + const T& getTangentPoint1() const { return mPoint[1];} + const T& getTangentPoint2() const { return mPoint[2];} + }; + + + typedef TemplateCubicBezier FloatCubicBezier; + typedef TemplateCubicBezier DoubleCubicBezier; + typedef TemplateCubicBezier Vec2CubicBezier; + typedef TemplateCubicBezier Vec3CubicBezier; + typedef TemplateCubicBezier Vec4CubicBezier; + +} + +#endif diff --git a/include/osgAnimation/EaseMotion b/include/osgAnimation/EaseMotion new file mode 100644 index 000000000..875fd32c0 --- /dev/null +++ b/include/osgAnimation/EaseMotion @@ -0,0 +1,238 @@ +/* -*-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_EASE_MOTION_H +#define OSGANIMATION_EASE_MOTION_H + +namespace osgAnimation { + + struct LinearFunction + { + inline static void getValueAt(float t, float& result) { result = t;} + }; + + + struct OutBounceFunction + { + inline static void getValueAt(float t, float& result) + { + if ((t) < (1/2.75)) + { + result = 7.5625 * t * t; + } + else if (t < (2/2.75)) + { + t = t - (1.5/2.75); + result = 7.5625* t * t + .75; + } + else if (t < (2.5/2.75)) + { + t = t - (2.25/2.75); + result = 7.5625 * t * t + .9375; + } + else + { + t = t - (2.625/2.75); + result = 7.5625* t * t + .984375; + } + } + }; + + struct InBounceFunction + { + inline static void getValueAt(float t, float& result) + { + OutBounceFunction::getValueAt(1-t, result); + result = 1 - result; + } + }; + + struct InOutBounceFunction + { + inline static void getValueAt(float t, float& result) + { + if (t < 0.5) + { + InBounceFunction::getValueAt(t * 2, result); + result *= 0.5; + } + else + { + OutBounceFunction::getValueAt(t * 2 - 1 , result); + result = result * 0.5 + 0.5; + } + } + }; + + + struct OutQuadFunction + { + inline static void getValueAt(float t, float& result) { result = - (t * (t -2.0));} + }; + + struct InQuadFunction + { + inline static void getValueAt(float t, float& result) { result = t*t;} + }; + struct InOutQuadFunction + { + inline static void getValueAt(float t, float& result) + { + t = t * 2.0; + if (t < 1.0) + result = 0.5 * t * t; + else + { + t = t - 1.0; + result = - 0.5 * t * ( t - 2) - 1; + } + } + }; + + + struct OutCubicFunction + { + inline static void getValueAt(float t, float& result) { t = t-1.0; result = t*t*t + 1;} + }; + struct InCubicFunction + { + inline static void getValueAt(float t, float& result) { result = t*t*t;} + }; + struct InOutCubicFunction + { + inline static void getValueAt(float t, float& result) + { + t = t * 2; + if (t < 1.0) + result = 0.5 * t * t * t; + else { + t = t - 2; + result = 0.5 * t * t * t + 2; + } + } + }; + + class Motion + { + public: + typedef float value_type; + enum TimeBehaviour + { + CLAMP, + LOOP, + }; + Motion(float startValue = 0, float duration = 1, float changeValue = 1, TimeBehaviour tb = CLAMP) : _time(0), _b(startValue), _c(changeValue), _d(duration), _behaviour(tb) {} + virtual ~Motion() {} + void reset() { setTime(0);} + float getTime() const { return _time; } + void update(float dt) + { + _time += dt; + switch (_behaviour) + { + case CLAMP: + if (_time > _d) + _time = _d; + else if (_time < 0.0) + _time = 0.0; + break; + case LOOP: + _time = fmodf(_time, _d); + break; + } + } + + void setTime(float time) { _time = time; update(0);} + void getValue(value_type& result) const { getValueAt(_time, result); } + value_type getValue() const + { + value_type result; + getValueAt(_time, result); + return result; + } + + void getValueAt(float time, value_type& result) const + { + getValueInNormalizedRange(time/_d, result); + result = result * _c + _b; + } + value_type getValueAt(float time) const + { + value_type result; + getValueAt(time, result); + return result; + } + + virtual void getValueInNormalizedRange(float t, value_type& result) const = 0; + + protected: + float _time; + float _b; + float _c; + float _d; + TimeBehaviour _behaviour; + }; + + + template + struct MathMotionTemplate : public Motion + { + MathMotionTemplate(float startValue = 0, float duration = 1, float changeValue = 1, TimeBehaviour tb = CLAMP) : Motion(startValue, duration, changeValue, tb) {} + virtual void getValueInNormalizedRange(float t, value_type& result) const { T::getValueAt(t, result); } + }; + + template + struct SamplerMotionTemplate : public Motion + { + T _sampler; + SamplerMotionTemplate(float startValue = 0, float duration = 1, float changeValue = 1, TimeBehaviour tb = CLAMP) : Motion(startValue, duration, changeValue, tb) {} + T& getSampler() { return _sampler;} + const T& getSampler() const { return _sampler;} + virtual void getValueInNormalizedRange(float t, value_type& result) const + { + if (!_sampler.getKeyframeContainer()) + { + result = 0; + return; + } + float size = _sampler.getEndTime() - _sampler.getStartTime(); + t = t * size + _sampler.getStartTime(); + _sampler.getValueAt(t, result); + } + }; + + + + // linear + typedef MathMotionTemplate LinearMotion; + + // cubic + typedef MathMotionTemplate OutCubicMotion; + typedef MathMotionTemplate InCubicMotion; + typedef MathMotionTemplate InOutCubicMotion; + + // quad + typedef MathMotionTemplate OutQuadMotion; + typedef MathMotionTemplate InQuadMotion; + typedef MathMotionTemplate InOutQuadMotion; + + // bounce + typedef MathMotionTemplate OutBounceMotion; + typedef MathMotionTemplate InBounceMotion; + typedef MathMotionTemplate InOutBounceMotion; + + +} + +#endif diff --git a/include/osgAnimation/Export b/include/osgAnimation/Export new file mode 100644 index 000000000..089f534e0 --- /dev/null +++ b/include/osgAnimation/Export @@ -0,0 +1,65 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield + * + * 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. +*/ + +// The following symbol has a underscore suffix for compatibility. +#ifndef OSGANIMATION_EXPORT_ +#define OSGANIMATION_EXPORT_ 1 + +#if defined(_MSC_VER) + #pragma warning( disable : 4244 ) + #pragma warning( disable : 4251 ) + #pragma warning( disable : 4267 ) + #pragma warning( disable : 4275 ) + #pragma warning( disable : 4290 ) + #pragma warning( disable : 4786 ) + #pragma warning( disable : 4305 ) + #pragma warning( disable : 4996 ) +#endif + +#if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) || defined( __BCPLUSPLUS__) || defined( __MWERKS__) + # if defined( OSG_LIBRARY_STATIC ) + # define OSGANIMATION_EXPORT + # elif defined( OSGANIMATION_LIBRARY ) + # define OSGANIMATION_EXPORT __declspec(dllexport) + # else + # define OSGANIMATION_EXPORT __declspec(dllimport) + #endif +#else + #define OSGANIMATION_EXPORT +#endif + +// set up define for whether member templates are supported by VisualStudio compilers. +#ifdef _MSC_VER +# if (_MSC_VER >= 1300) +# define __STL_MEMBER_TEMPLATES +# endif +#endif +/* Define NULL pointer value */ + +#ifndef NULL + #ifdef __cplusplus + #define NULL 0 + #else + #define NULL ((void *)0) + #endif +#endif + +/** + +\namespace osgAnimation + +The osgAnimation library provides general purpose utility classes for animation. + +*/ + +#endif diff --git a/include/osgAnimation/Interpolator b/include/osgAnimation/Interpolator new file mode 100644 index 000000000..79157b0c9 --- /dev/null +++ b/include/osgAnimation/Interpolator @@ -0,0 +1,206 @@ +/* -*-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_INTERPOLATOR_H +#define OSGANIMATION_INTERPOLATOR_H + +#include +#include +#include + +namespace osgAnimation +{ + + template + class TemplateInterpolatorBase + { + public: + typedef KEY KeyframeType; + typedef TYPE UsingType; + + public: + mutable int _lastKeyAccess; + + TemplateInterpolatorBase() : _lastKeyAccess(-1) {} + + void reset() { _lastKeyAccess = -1; } + int getKeyIndexFromTime(const TemplateKeyframeContainer& keys, float time) const + { + OSGANIMATION_ASSERT(!keys.empty() && "no keys"); + OSGANIMATION_ASSERT(time >= keys.front().getTime() && time <= keys.back().getTime() && "bad range"); + + // todo use a cache + int key_size = keys.size(); + const TemplateKeyframe* keysVector = &keys.front(); + for (int i = 0; i < key_size-1; i++) + { + float time0 = keysVector[i].getTime(); + float time1 = keysVector[i+1].getTime(); +#ifndef OSGANIMATION_NO_EXTRA_CHECK + if ( time0>time1 ) + OSGANIMATION_ASSERT(0 && "invalid input data"); +#endif + if ( time >= time0 && time < time1 ) + { + _lastKeyAccess = i; + return i; + } + } + std::cout << time << " first key " << keysVector[0].getTime() << " last key " << keysVector[key_size-1].getTime() << std::endl; + *((int *)0) = 0; + OSGANIMATION_ASSERT(0 && "impossible has happened"); + return -1; + } + }; + + + template + class TemplateLinearInterpolator : public TemplateInterpolatorBase + { + public: + + TemplateLinearInterpolator() {} + void getValue(const TemplateKeyframeContainer& keyframes, float time, TYPE& result) const + { + + if (time >= keyframes.back().getTime()) + { + result = keyframes.back().getValue(); + return; + } + else if (time <= keyframes.front().getTime()) + { + result = keyframes.front().getValue(); + return; + } + + int i = getKeyIndexFromTime(keyframes,time); + float blend = (time - keyframes[i].getTime()) / ( keyframes[i+1].getTime() - keyframes[i].getTime()); + const TYPE& v1 = keyframes[i].getValue(); + const TYPE& v2 = keyframes[i+1].getValue(); + result = v1*(1-blend) + v2*blend; + } + }; + + + template + class TemplateSphericalLinearInterpolator : public TemplateInterpolatorBase + { + public: + TemplateSphericalLinearInterpolator() {} + void getValue(const TemplateKeyframeContainer& keyframes, float time, TYPE& result) const + { + if (time >= keyframes.back().getTime()) + { + result = keyframes.back().getValue(); + return; + } + else if (time <= keyframes.front().getTime()) + { + result = keyframes.front().getValue(); + return; + } + + int i = getKeyIndexFromTime(keyframes,time); + float blend = (time - keyframes[i].getTime()) / ( keyframes[i+1].getTime() - keyframes[i].getTime()); + const TYPE& q1 = keyframes[i].getValue(); + const TYPE& q2 = keyframes[i+1].getValue(); + result.slerp(blend,q1,q2); + } + }; + + + template + class TemplateLinearPackedInterpolator : public TemplateInterpolatorBase + { + public: + + TemplateLinearPackedInterpolator() {} + void getValue(const TemplateKeyframeContainer& keyframes, float time, TYPE& result) const + { + if (time >= keyframes.back().getTime()) + { + keyframes.back().getValue().uncompress(keyframes.mScale, keyframes.mMin, result); + return; + } + else if (time <= keyframes.front().getTime()) + { + keyframes.front().getValue().uncompress(keyframes.mScale, keyframes.mMin, result); + return; + } + + int i = getKeyIndexFromTime(keyframes,time); + float blend = (time - keyframes[i].getTime()) / ( keyframes[i+1].getTime() - keyframes[i].getTime()); + TYPE v1,v2; + keyframes[i].getValue().uncompress(keyframes.mScale, keyframes.mMin, v1); + keyframes[i+1].getValue().uncompress(keyframes.mScale, keyframes.mMin, v2); + result = v1*(1-blend) + v2*blend; + } + }; + + + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + template + class TemplateCubicBezierInterpolator : public TemplateInterpolatorBase + { + public: + + TemplateCubicBezierInterpolator() {} + void getValue(const TemplateKeyframeContainer& keyframes, float time, TYPE& result) const + { + + if (time >= keyframes.back().getTime()) + { + result = keyframes.back().getValue().getPosition(); + return; + } + else if (time <= keyframes.front().getTime()) + { + result = keyframes.front().getValue().getPosition(); + return; + } + + int i = getKeyIndexFromTime(keyframes,time); + + float t = (time - keyframes[i].getTime()) / ( keyframes[i+1].getTime() - keyframes[i].getTime()); + float one_minus_t = 1.0-t; + float one_minus_t2 = one_minus_t * one_minus_t; + float one_minus_t3 = one_minus_t2 * one_minus_t; + float t2 = t * t; + + TYPE v0 = keyframes[i].getValue().getPosition() * one_minus_t3; + TYPE v1 = keyframes[i].getValue().getTangentPoint1() * (3.0 * t * one_minus_t2); + TYPE v2 = keyframes[i].getValue().getTangentPoint2() * (3.0 * t2 * one_minus_t); + TYPE v3 = keyframes[i+1].getValue().getPosition() * (t2 * t); + + result = v0 + v1 + v2 + v3; + } + }; + + typedef TemplateLinearInterpolator DoubleLinearInterpolator; + typedef TemplateLinearInterpolator FloatLinearInterpolator; + typedef TemplateLinearInterpolator Vec2LinearInterpolator; + typedef TemplateLinearInterpolator Vec3LinearInterpolator; + typedef TemplateLinearInterpolator Vec3PackedLinearInterpolator; + typedef TemplateLinearInterpolator Vec4LinearInterpolator; + typedef TemplateSphericalLinearInterpolator QuatSphericalLinearInterpolator; + + typedef TemplateCubicBezierInterpolator FloatCubicBezierInterpolator; + typedef TemplateCubicBezierInterpolator DoubleCubicBezierInterpolator; + typedef TemplateCubicBezierInterpolator Vec2CubicBezierInterpolator; + typedef TemplateCubicBezierInterpolator Vec3CubicBezierInterpolator; + typedef TemplateCubicBezierInterpolator Vec4CubicBezierInterpolator; + +} +#endif diff --git a/include/osgAnimation/Keyframe b/include/osgAnimation/Keyframe new file mode 100644 index 000000000..f1b57323f --- /dev/null +++ b/include/osgAnimation/Keyframe @@ -0,0 +1,130 @@ +/* -*-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_KEYFRAME_H +#define OSGANIMATION_KEYFRAME_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class Keyframe + { + public: + float getTime() const { return _time; } + void setTime(float time) { _time = time; } + + protected: + float _time; + + }; + + template + class TemplateKeyframe : public Keyframe + { + protected: + T _value; + public: + TemplateKeyframe () {} + ~TemplateKeyframe () {} + + TemplateKeyframe (float time, const T& value) + { + _time = time; + _value = value; + } + + void setValue(const T& value) { _value = value;} + const T& getValue() const { return _value;} + }; + + + class KeyframeContainer : public osg::Referenced + { + public: + KeyframeContainer() {} + virtual unsigned int size() const = 0; + protected: + ~KeyframeContainer() {} + std::string _name; + }; + + + template + class TemplateKeyframeContainer : public std::vector >, public KeyframeContainer + { + public: + // const char* getKeyframeType() { return #T ;} + TemplateKeyframeContainer() {} + typedef TemplateKeyframe KeyType; + + virtual unsigned int size() const { return (unsigned int)std::vector >::size(); } + + }; + + template <> + class TemplateKeyframeContainer : public std::vector >, public KeyframeContainer + { + public: + typedef TemplateKeyframe KeyType; + + TemplateKeyframeContainer() {} + const char* getKeyframeType() { return "Vec3Packed" ;} + void init(const osg::Vec3f& min, const osg::Vec3f& scale) { _min = min; _scale = scale; } + + osg::Vec3f _min; + osg::Vec3f _scale; + }; + + + typedef TemplateKeyframe FloatKeyframe; + typedef TemplateKeyframeContainer FloatKeyframeContainer; + + typedef TemplateKeyframe Vec2Keyframe; + typedef TemplateKeyframeContainer Vec2KeyframeContainer; + + typedef TemplateKeyframe Vec3Keyframe; + typedef TemplateKeyframeContainer Vec3KeyframeContainer; + + typedef TemplateKeyframe Vec4Keyframe; + typedef TemplateKeyframeContainer Vec4KeyframeContainer; + + typedef TemplateKeyframe QuatKeyframe; + typedef TemplateKeyframeContainer QuatKeyframeContainer; + + typedef TemplateKeyframe Vec3PackedKeyframe; + typedef TemplateKeyframeContainer Vec3PackedKeyframeContainer; + + 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; + +} + +#endif diff --git a/include/osgAnimation/LinkVisitor b/include/osgAnimation/LinkVisitor new file mode 100644 index 000000000..afc318220 --- /dev/null +++ b/include/osgAnimation/LinkVisitor @@ -0,0 +1,50 @@ +/* -*-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_NODE_VISITOR_H +#define OSGANIMATION_NODE_VISITOR_H + +#include +#include +#include + +namespace osgAnimation +{ + + struct LinkVisitor : public osg::NodeVisitor + { + AnimationList _animations; + unsigned int _nbLinkedTarget; + LinkVisitor(Animation* animation) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { _animations.push_back(animation); _nbLinkedTarget = 0;} + LinkVisitor(const AnimationList& animations) : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { _animations = animations; _nbLinkedTarget = 0;} + void apply(osg::Node& node) + { + osgAnimation::AnimationUpdateCallback* cb = dynamic_cast(node.getUpdateCallback()); + if (cb) + { + int result = 0; + for (int i = 0; i < (int)_animations.size(); i++) + { + result += cb->link(_animations[i].get()); + _nbLinkedTarget += result; + } + std::cout << "LinkVisitor links " << result << " for \"" << cb->getName() << '"' << std::endl; + } + traverse(node); + } + }; + +} + +#endif diff --git a/include/osgAnimation/RigGeometry b/include/osgAnimation/RigGeometry new file mode 100644 index 000000000..f55acaf82 --- /dev/null +++ b/include/osgAnimation/RigGeometry @@ -0,0 +1,159 @@ +/* -*-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_RIGGEOMETRY_H +#define OSGANIMATION_RIGGEOMETRY_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT RigGeometry : public osg::Geometry + { + public: + + struct FindNearestParentSkeleton : public osg::NodeVisitor + { + osg::ref_ptr _root; + FindNearestParentSkeleton() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_PARENTS) {} + void apply(osg::Transform& node) + { + if (_root.valid()) + return; + _root = dynamic_cast(&node); + traverse(node); + } + }; + + + struct UpdateVertex : public osg::Drawable::UpdateCallback + { + virtual void update(osg::NodeVisitor*, osg::Drawable* drw) + { + RigGeometry* geom = dynamic_cast(drw); + if (!geom) + return; + if (!geom->getSkeleton() && !geom->getParents().empty()) + { + FindNearestParentSkeleton finder; + if (geom->getParents().size() > 1) + osg::notify(osg::WARN) << "A RigGeometry should not have multi parent ( " << geom->getName() << " )" << std::endl; + geom->getParents()[0]->accept(finder); + + if (!finder._root.valid()) + return; + geom->buildVertexSet(); + geom->buildTransformer(finder._root.get()); + } + + if (!geom->getSkeleton()) + return; + + if (geom->getNeedToComputeMatrix()) + geom->computeMatrixFromRootSkeleton(); + geom->transformSoftwareMethod(); + } + }; + + /** BuildVertexTransformerVisitor is used to setup RigGeometry drawable + * throw a subgraph. + */ + struct BuildVertexTransformerVisitor : public osg::NodeVisitor + { + osg::ref_ptr _root; + BuildVertexTransformerVisitor(Skeleton* root): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { _root = root;} + void apply(osg::Geode& node) + { + int num = node.getNumDrawables(); + for (int i = 0; i < num; i++) { + RigGeometry* geom = dynamic_cast(node.getDrawable(i)); + if (geom) + { + geom->buildVertexSet(); + geom->buildTransformer(_root.get()); + } + } + } + }; + + + RigGeometry() + { + setUseDisplayList(false); + setUpdateCallback(new UpdateVertex); + setDataVariance(osg::Object::DYNAMIC); + _needToComputeMatrix = true; + _matrixFromSkeletonToGeometry = _invMatrixFromSkeletonToGeometry = osg::Matrix::identity(); + } + RigGeometry(const osg::Geometry& b) : osg::Geometry(b, osg::CopyOp::SHALLOW_COPY) + { + setUseDisplayList(false); + setUpdateCallback(new UpdateVertex); + setDataVariance(osg::Object::DYNAMIC); + _needToComputeMatrix = true; + _matrixFromSkeletonToGeometry = _invMatrixFromSkeletonToGeometry = osg::Matrix::identity(); + } + + RigGeometry(const RigGeometry& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY) : + osg::Geometry(b,copyop), + _positionSource(b._positionSource), + _normalSource(b._normalSource), + _vertexInfluenceSet(b._vertexInfluenceSet), + _vertexInfluenceMap(b._vertexInfluenceMap), + _transformVertexes(b._transformVertexes), + _needToComputeMatrix(b._needToComputeMatrix) {} + + virtual osg::Object* cloneType() const { return new RigGeometry(); } + virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new RigGeometry(*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 "RigGeometry"; } + + + void setInfluenceMap(osgAnimation::VertexInfluenceMap* vertexInfluenceMap) { _vertexInfluenceMap = vertexInfluenceMap; } + const osgAnimation::VertexInfluenceMap* getInfluenceMap() const { return _vertexInfluenceMap.get();} + osgAnimation::VertexInfluenceMap* getInfluenceMap() { return _vertexInfluenceMap.get();} + void buildVertexSet(); + void buildTransformer(Skeleton* root); + void computeMatrixFromRootSkeleton(); + + void setNeedToComputeMatrix(bool state) { _needToComputeMatrix = state;} + bool getNeedToComputeMatrix() const { return _needToComputeMatrix;} + + const Skeleton* getSkeleton() const; + Skeleton* getSkeleton(); + virtual void transformSoftwareMethod(); + const osgAnimation::VertexInfluenceSet& getVertexInfluenceSet() const { return _vertexInfluenceSet;} + + std::vector _positionSource; + std::vector _normalSource; + + osgAnimation::VertexInfluenceSet _vertexInfluenceSet; + osg::ref_ptr _vertexInfluenceMap; + osgAnimation::TransformVertexFunctor _transformVertexes; + + osg::Matrix _matrixFromSkeletonToGeometry; + osg::Matrix _invMatrixFromSkeletonToGeometry; + osg::observer_ptr _root; + bool _needToComputeMatrix; + + }; + +} + +#endif diff --git a/include/osgAnimation/Sampler b/include/osgAnimation/Sampler new file mode 100644 index 000000000..6a999b38d --- /dev/null +++ b/include/osgAnimation/Sampler @@ -0,0 +1,124 @@ +/* -*-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_SAMPLER_H +#define OSGANIMATION_SAMPLER_H + +#include +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class Sampler : public osg::Referenced + { + public: + virtual KeyframeContainer* getKeyframeContainer() = 0; + virtual const KeyframeContainer* getKeyframeContainer() const = 0; + protected: + }; + + // Sampler generic + template + class TemplateSampler : public Sampler + { + public: + typedef typename F::KeyframeType KeyframeType; + typedef TemplateKeyframeContainer KeyframeContainerType; + typedef typename F::UsingType UsingType; + typedef F FunctorType; + + TemplateSampler() {} + ~TemplateSampler() {} + + void getValueAt(float time, UsingType& result) const { _functor.getValue(*_keyframes, time, result);} + void setKeyframeContainer(KeyframeContainerType* kf) { _keyframes = kf;} + + virtual KeyframeContainer* getKeyframeContainer() { return _keyframes.get(); } + virtual const KeyframeContainer* getKeyframeContainer() const { return _keyframes.get();} + + KeyframeContainerType* getKeyframeContainerTyped() { return _keyframes.get();} + const KeyframeContainerType* getKeyframeContainerTyped() const { return _keyframes.get();} + KeyframeContainerType* getOrCreateKeyframeContainer() + { + if (_keyframes != 0) + return _keyframes.get(); + _keyframes = new KeyframeContainerType; + return _keyframes.get(); + } + + float getStartTime() const + { + OSGANIMATION_ASSERT(_keyframes.valid() && !_keyframes->empty() && "no keyframes"); + return _keyframes->front().getTime(); + } + + float getEndTime() const + { + OSGANIMATION_ASSERT(_keyframes.valid() && !_keyframes->empty() && "no keyframes"); + return _keyframes->back().getTime(); + } + + protected: + + FunctorType _functor; + osg::ref_ptr _keyframes; + }; + + + template + class TemplateCompositeSampler : public osg::Referenced + { + VALUESAMPLERTYPE& _value; + TIMESAMPLERTYPE& _time; + + public: + typedef typename VALUESAMPLERTYPE::FunctorType::UsingType UsingType; + typedef typename VALUESAMPLERTYPE::FunctorType::KeyframeType KeyframeType; + + TemplateCompositeSampler(VALUESAMPLERTYPE& value, TIMESAMPLERTYPE& time) : _value(value), _time(time) + { + } + + void getValueAt(float time, typename VALUESAMPLERTYPE::FunctorType::UsingType& result) + { + float newtime; + _time.getValueAt(time, newtime); + _value.getValueAt(newtime, result); + } + float getStartTime() const {return _time.getStartTime(); } + float getEndTime() const {return _time.getEndTime();} + }; + + + typedef TemplateSampler DoubleLinearSampler; + typedef TemplateSampler FloatLinearSampler; + typedef TemplateSampler Vec2LinearSampler; + typedef TemplateSampler Vec3LinearSampler; + typedef TemplateSampler Vec4LinearSampler; + typedef TemplateSampler QuatSphericalLinearSampler; + typedef TemplateSampler FloatCubicBezierSampler; + typedef TemplateSampler DoubleCubicBezierSampler; + typedef TemplateSampler Vec2CubicBezierSampler; + typedef TemplateSampler Vec3CubicBezierSampler; + typedef TemplateSampler Vec4CubicBezierSampler; + +}; + +#endif diff --git a/include/osgAnimation/Skeleton b/include/osgAnimation/Skeleton new file mode 100644 index 000000000..fc45dc7d2 --- /dev/null +++ b/include/osgAnimation/Skeleton @@ -0,0 +1,44 @@ +/* -*-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_SKELETON_H +#define OSGANIMATION_SKELETON_H + +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT Skeleton : public Bone + { + public: + META_Node(osgAnimation, Skeleton); + + struct UpdateCallback : public osg::NodeCallback + { + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + }; + + Skeleton(const Skeleton& b, const osg::CopyOp& copyop= osg::CopyOp::SHALLOW_COPY) : Bone(b,copyop) {} + Skeleton(); + + void computeBindMatrix() { _invBindInSkeletonSpace = osg::Matrix::inverse(_bindInBoneSpace); } + + }; + +} + +#endif diff --git a/include/osgAnimation/Skinning b/include/osgAnimation/Skinning new file mode 100644 index 000000000..07f2a747c --- /dev/null +++ b/include/osgAnimation/Skinning @@ -0,0 +1,167 @@ +/* -*-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_SKINNING_H +#define OSGANIMATION_SKINNING_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + + class TransformVertexFunctor + { + public: + typedef osg::Matrix MatrixType; + typedef osgAnimation::Bone BoneType; + typedef Bone::BoneMap BoneMap; + + class BoneWeight + { + public: + BoneWeight(BoneType* bone, float weight) : _bone(bone), _weight(weight) {} + const BoneType* getBone() const { return &(*_bone); } + float getWeight() const { return _weight; } + protected: + osg::ref_ptr _bone; + float _weight; + }; + + typedef std::vector BoneWeightList; + typedef std::vector VertexList; + + class UniqBoneSetVertexSet + { + public: + BoneWeightList& getBones() { return _bones; } + VertexList& getVertexes() { return _vertexes; } + void computeMatrixForVertexSet() + { + if (_bones.empty()) + { + osg::notify(osg::WARN) << "TransformVertexFunctor::UniqBoneSetVertexSet no bones found" << std::endl; + _result = MatrixType::identity(); + return; + } + _result = MatrixType(0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0); + int size = _bones.size(); + for (int i = 0; i < size; i++) + { + const BoneType* bone = _bones[i].getBone(); + const MatrixType& invBindMatrix = bone->getInvBindMatrixInSkeletonSpace(); + const MatrixType& matrix = bone->getMatrixInSkeletonSpace(); + double w = _bones[i].getWeight(); + _result = _result + ( (invBindMatrix * matrix ) * w); + } + } + const MatrixType& getMatrix() const { return _result;} + protected: + BoneWeightList _bones; + VertexList _vertexes; + MatrixType _result; + }; + + + void init(const BoneMap& map, const osgAnimation::VertexInfluenceSet::UniqVertexSetToBoneSetList& influence) + { + _boneSetVertexSet.clear(); + + int size = influence.size(); + _boneSetVertexSet.resize(size); + for (int i = 0; i < size; i++) + { + const osgAnimation::VertexInfluenceSet::UniqVertexSetToBoneSet& inf = influence[i]; + int nbBones = inf.getBones().size(); + + for (int b = 0; b < nbBones; b++) + { + const std::string& bname = inf.getBones()[b].getBoneName(); + float weight = inf.getBones()[b].getWeight(); + if (map.find(bname) == map.end()) + { + std::cerr << "Warning TransformVertexFunctor Bone " << bname << " not found, skip the influence group " <second.get(); + _boneSetVertexSet[i].getBones().push_back(BoneWeight(bone, weight)); + } + _boneSetVertexSet[i].getVertexes() = inf.getVertexes(); + } + } + + + template void compute(const V* src, V* dst) + { + OSGANIMATION_ASSERT(src != dst); + int size = _boneSetVertexSet.size(); + for (int i = 0; i < size; i++) + { + UniqBoneSetVertexSet& uniq = _boneSetVertexSet[i]; + uniq.computeMatrixForVertexSet(); + const MatrixType& matrix = uniq.getMatrix(); + + const VertexList& vertexes = uniq.getVertexes(); + int vertexSize = vertexes.size(); + for (int j = 0; j < vertexSize; j++) + { + int idx = vertexes[j]; + dst[idx] = src[idx] * matrix; + } + } + } + + template void compute(const MatrixType& transform, const MatrixType& invTransform, const V* src, V* dst) + { + OSGANIMATION_ASSERT(src != dst); + int size = _boneSetVertexSet.size(); + for (int i = 0; i < size; i++) + { + UniqBoneSetVertexSet& uniq = _boneSetVertexSet[i]; + uniq.computeMatrixForVertexSet(); + MatrixType matrix = transform * uniq.getMatrix() * invTransform; + + const VertexList& vertexes = uniq.getVertexes(); + int vertexSize = vertexes.size(); + for (int j = 0; j < vertexSize; j++) + { + int idx = vertexes[j]; + dst[idx] = src[idx] * matrix; + } + } + } + + protected: + + std::vector _boneSetVertexSet; + }; +} + +#endif diff --git a/include/osgAnimation/Target b/include/osgAnimation/Target new file mode 100644 index 000000000..bbb40df14 --- /dev/null +++ b/include/osgAnimation/Target @@ -0,0 +1,132 @@ +/* -*-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_TARGET_H +#define OSGANIMATION_TARGET_H + +#include +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class Channel; + + class OSGANIMATION_EXPORT Target : public osg::Referenced + { + public: + + Target(); + virtual ~Target(); + virtual void normalize() = 0; + float getWeight() const { return _weight; } + void reset() { _weight = 0;} + int getCount() const { return referenceCount(); } + protected: + + void addWeight(float w) { _weight += w; } + float _weight; + }; + + + template + class TemplateTarget : public Target + { + public: + + TemplateTarget() {} + TemplateTarget(const T& v) { setValue(v); } + + void update(float weight, const T& val) + { + if (!_weight) + _target = val * weight; + else + { + weight = (1.0 - _weight) * weight; + _target += val * weight; + } + addWeight(weight); + } + const T& getValue() const { return _target;} + + void normalize() + { + float weightSummed = getWeight(); + if (fabs(weightSummed) < 1e-4 || fabs(weightSummed-1) < 1e-4) + return; + (_target) /= weightSummed; + } + + void setValue(const T& value) { _target = value;} + + protected: + + T _target; + }; + + + // Target Specialisation for Quaternions + template <> + class TemplateTarget< osg::Quat > : public Target + { + public: + + TemplateTarget () {} + + const osg::Quat& getValue() const { return _target;} + void update(float weight, const osg::Quat& val) + { + if (!_weight) + _target = val * weight; + else + { + weight = (1.0 - _weight) * weight; + _target += val * weight; + } + addWeight(weight); + } + + // maybe normalize could be non virtual and put on ITarget + void normalize() + { + float weightSummed = getWeight(); + if (fabs(weightSummed) < 1e-4 || fabs(weightSummed-1.0) < 1e-4) + return; + (_target) /= weightSummed; + } + + void setValue(const osg::Quat& value) { _target = value;} + + protected: + + osg::Quat _target; + }; + + typedef TemplateTarget QuatTarget; + typedef TemplateTarget Vec3Target; + typedef TemplateTarget Vec4Target; + typedef TemplateTarget Vec2Target; + typedef TemplateTarget FloatTarget; + typedef TemplateTarget DoubleTarget; + +} + +#endif diff --git a/include/osgAnimation/Timeline b/include/osgAnimation/Timeline new file mode 100644 index 000000000..66127a902 --- /dev/null +++ b/include/osgAnimation/Timeline @@ -0,0 +1,563 @@ +/* -*-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_TIMELINE_H +#define OSGANIMATION_TIMELINE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class Action : public virtual osg::Object + { + public: + + class Callback : public virtual osg::Object + { + public: + Callback(){} + Callback(const Callback& nc,const osg::CopyOp&) {} + + META_Object(osgAnimation,Callback); + + virtual void operator()(Action* action) {} + void addNestedCallback(Callback* callback) + { + if (_nested.valid()) + _nested->addNestedCallback(callback); + else + _nested = callback; + } + + protected: + osg::ref_ptr _nested; + }; + + + typedef std::map > FrameCallback; + + META_Object(osgAnimation, Action); + Action() + { + _numberFrame = 25; + _fps = 25; + _speed = 1.0; + _loop = 1; + } + Action(const Action& nc,const osg::CopyOp&) {} + + void setCallback(double when, Callback* callback) + { + setCallback(static_cast(floor(when*_fps)), callback); + } + + void setCallback(unsigned int frame, Callback* callback) + { + if (_framesCallback[frame].valid()) + _framesCallback[frame]->addNestedCallback(callback); + else + _framesCallback[frame] = callback; + } + Callback* getCallback(unsigned int frame) + { + if (_framesCallback.find(frame) == _framesCallback.end()) + return 0; + return _framesCallback[frame].get(); + } + + void setNumFrames(unsigned int numFrames) { _numberFrame = numFrames;} + void setDuration(double duration) { _numberFrame = static_cast(floor(duration * _fps)); } + + unsigned int getNumFrames() const { return _numberFrame;} + double getDuration() const { return _numberFrame * 1.0 / _fps; } + + // 0 means infini else it's the number of loop + virtual void setLoop(int nb) { _loop = nb; } + virtual unsigned int getLoop() const { return _loop;} + + // get the number of loop, the frame relative to loop. + // return true if in range, and false if out of range. + bool evaluateFrame(unsigned int frame, unsigned int& resultframe, unsigned int& nbloop ) + { + nbloop = frame / getNumFrames(); + resultframe = frame; + + if (frame > getNumFrames()-1) + { + if (!getLoop()) + resultframe = frame % getNumFrames(); + else + { + if (nbloop >= getLoop()) + return false; + else + resultframe = frame % getNumFrames(); + } + } + return true; + } + + virtual void evaluate(unsigned int frame) + { + unsigned int frameInAction; + unsigned int loopDone; + if (!evaluateFrame(frame, frameInAction, loopDone)) + osg::notify(osg::DEBUG_INFO) << getName() << " Action frame " << frameInAction << " finished" << std::endl; + else + osg::notify(osg::DEBUG_INFO) << getName() << " Action frame " << frame << " relative to loop " << frameInAction << " no loop " << loopDone<< std::endl; + } + + virtual void evaluateCallback(unsigned int frame) + { + unsigned int frameInAction; + unsigned int loopDone; + if (!evaluateFrame(frame, frameInAction, loopDone)) + return; + + frame = frameInAction; + if (_framesCallback.find(frame) != _framesCallback.end()) + { + std::cout << getName() << " evaluate callback " << _framesCallback[frame]->getName() << " at " << frame << std::endl; + (*_framesCallback[frame])(this); + } + } + + protected: + FrameCallback _framesCallback; + + double _speed; + unsigned int _fps; + unsigned int _numberFrame; + unsigned int _loop; + + enum State + { + Play, + Stop, + }; + + State _state; + }; + + + class Timeline : public virtual osg::Object + { + protected: + typedef std::pair > FrameAction; + typedef std::vector ActionList; + typedef std::map ActionLayers; + + ActionLayers _actions; + + double _lastUpdate; + double _speed; + unsigned int _currentFrame; + unsigned int _fps; + unsigned int _numberFrame; + unsigned int _previousFrameEvaluated; + bool _loop; + bool _initFirstFrame; + + enum State + { + Play, + Stop, + }; + + State _state; + + // to manage pending operation + bool _evaluating; + + struct Command + { + Command():_priority(0) {} + Command(int priority, const FrameAction& action) : _priority(priority), _action(action) {} + int _priority; + FrameAction _action; + }; + + typedef std::vector CommandList; + CommandList _addActionOperations; + ActionList _removeActionOperations; + + void setEvaluating(bool state) { _evaluating = state;} + void processPendingOperation() + { + // process all pending add action operation + while( !_addActionOperations.empty()) + { + internalAddAction(_addActionOperations.back()._priority, _addActionOperations.back()._action); + _addActionOperations.pop_back(); + } + + // process all pending remove action operation + while( !_removeActionOperations.empty()) + { + internalRemoveAction(_removeActionOperations.back().second); + _removeActionOperations.pop_back(); + } + } + + void internalRemoveAction(Action* action) + { + for (ActionLayers::iterator it = _actions.begin(); it != _actions.end(); it++) + { + ActionList& fa = it->second; + for (unsigned int i = 0; i < fa.size(); i++) + if (fa[i].second.get() == action) + { + fa.erase(fa.begin() + i); + return; + } + } + } + void internalAddAction(int priority, const FrameAction& ftl) + { + _actions[priority].push_back(ftl); + } + + public: + + META_Object(osgAnimation, Timeline); + + Timeline() + { + _lastUpdate = 0; + _currentFrame = 0; + _fps = 25; + _speed = 1.0; + _state = Stop; + _initFirstFrame = false; + _previousFrameEvaluated = 0; + _evaluating = 0; + _numberFrame = -1; // something like infinity + setName("Timeline"); + } + Timeline(const Timeline& nc,const osg::CopyOp&) {} + State getStatus() const { return _state; } + const ActionList& getActionLayer(int i) + { + return _actions[i]; + } + unsigned int getCurrentFrame() const { return _currentFrame;} + double getCurrentTime() const { return _currentFrame * 1.0 / _fps;} + + void play() { _state = Play; } + void gotoFrame(unsigned int frame) { _currentFrame = frame; } + void stop() { _state = Stop; } + bool getEvaluating() const { return _evaluating;} + + bool isActive(Action* activeAction) + { + // update from high priority to low priority + for( ActionLayers::iterator iterAnim = _actions.begin(); iterAnim != _actions.end(); ++iterAnim ) + { + // update all animation + ActionList& list = iterAnim->second; + for (unsigned int i = 0; i < list.size(); i++) + { + Action* action = list[i].second.get(); + if (action == activeAction) + { + unsigned int firstFrame = list[i].first; + // check if current frame of timeline hit an action interval + if (_currentFrame >= firstFrame && + _currentFrame < (firstFrame + action->getNumFrames()) ) + return true; + } + } + } + return false; + } + + void removeAction(Action* action) + { + if (getEvaluating()) + _removeActionOperations.push_back(FrameAction(0, action)); + else + internalRemoveAction(action); + } + + virtual void addActionAt(unsigned int frame, Action* action, int priority = 0) + { + if (getEvaluating()) + _addActionOperations.push_back(Command(priority,FrameAction(frame, action))); + else + internalAddAction(priority, FrameAction(frame, action)); + } + virtual void addActionAt(double t, Action* action, int priority = 0) + { + unsigned int frame = static_cast(floor(t * _fps)); + addActionAt(frame, action, priority); + } + + virtual void evaluate(unsigned int frame) + { + setEvaluating(true); + osg::notify(osg::DEBUG_INFO) << getName() << " evaluate frame " << _currentFrame << std::endl; + + // update from high priority to low priority + for( ActionLayers::reverse_iterator iterAnim = _actions.rbegin(); iterAnim != _actions.rend(); ++iterAnim ) + { + // update all animation + ActionList& list = iterAnim->second; + for (unsigned int i = 0; i < list.size(); i++) + { + unsigned int firstFrame = list[i].first; + Action* action = list[i].second.get(); + // check if current frame of timeline hit an action interval + if (frame >= firstFrame && + frame < (firstFrame + action->getNumFrames()) ) + action->evaluate(frame - firstFrame); + } + } + setEvaluating(false); + + // evaluate callback after updating all animation + evaluateCallback(frame); + _previousFrameEvaluated = frame; + } + + virtual void evaluateCallback(unsigned int frame) + { + // update from high priority to low priority + for( ActionLayers::reverse_iterator iterAnim = _actions.rbegin(); iterAnim != _actions.rend(); ++iterAnim ) + { + // update all animation + ActionList& list = iterAnim->second; + for (unsigned int i = 0; i < list.size(); i++) + { + unsigned int firstFrame = list[i].first; + Action* action = list[i].second.get(); + // check if current frame of timeline hit an action interval + if (frame >= firstFrame && + frame < (firstFrame + action->getNumFrames()) ) + action->evaluateCallback(frame - firstFrame); + } + } + processPendingOperation(); + } + + virtual void update(double simulationTime) + { + // first time we call update we generate one frame + if (!_initFirstFrame) + { + _lastUpdate = simulationTime; + _initFirstFrame = true; + evaluate(_currentFrame); + } + + // find the number of frame pass since the last update + double delta = (simulationTime - _lastUpdate); + double nbframes = delta * _fps * _speed; + unsigned int nb = static_cast(floor(nbframes)); + + for (unsigned int i = 0; i < nb; i++) + { + if (_state == Play) + _currentFrame++; + evaluate(_currentFrame); + } + if (nb) + { + _lastUpdate += ((double)nb) / _fps; + } + } + }; + + + + // blend in from 0 to weight in duration + class BlendIn : public Action + { + double _weight; + osg::ref_ptr _animation; + + public: + BlendIn(Animation* animation, double duration, double weight) + { + _animation = animation; + _weight = weight; + float d = duration * _fps; + setNumFrames(static_cast(floor(d)) + 1); + setName("BlendIn"); + } + double getWeight() const { return _weight;} + virtual void evaluate(unsigned int frame) + { + Action::evaluate(frame); + // frame + 1 because the start is 0 and we want to start the blend in at the first + // frame. + double ratio = ( (frame+1) * 1.0 / (getNumFrames()) ); + double w = _weight; + if (frame < getNumFrames() -1 ) // the last frame we set the target weight the user asked + w = _weight * ratio; + _animation->setWeight(w); + } + }; + + // blend in from 0 to weight in duration + class BlendOut : public Action + { + double _weight; + osg::ref_ptr _animation; + public: + BlendOut(Animation* animation, double duration) + { + _animation = animation; + float d = duration * _fps; + setNumFrames(static_cast(floor(d) + 1)); + _weight = 1.0; + setName("BlendOut"); + } + double getWeight() const { return _weight;} + virtual void evaluate(unsigned int frame) + { + Action::evaluate(frame); + // frame + 1 because the start is 0 and we want to start the blend in at the first + // frame. + double ratio = ( (frame+1) * 1.0 / (getNumFrames()) ); + double w = 0.0; + if (frame < getNumFrames() -1 ) // the last frame we set the target weight the user asked + w = _weight * (1.0-ratio); + _animation->setWeight(w); + } + }; + + + class ActionAnimation : public Action + { + public: + ActionAnimation(Animation* animation) : _animation(animation) + { + setDuration(animation->getDuration()); + setName(animation->getName()); + } + virtual void evaluate(unsigned int frame) + { + Action::evaluate(frame); + _animation->update(frame * 1.0/_fps); + } + Animation* getAnimation() { return _animation.get(); } + protected: + osg::ref_ptr _animation; + }; + + + // encapsulate animation with blend in blend out for classic usage + class StripAnimation : public Action + { + protected: + typedef std::pair > FrameAction; + + public: + StripAnimation(Animation* animation, double blendInDuration, double blendOutDuration, double blendInWeightTarget = 1.0 ) + { + _blendIn = new BlendIn(animation, blendInDuration, blendInWeightTarget); + _animation = new ActionAnimation(animation); + unsigned int start = static_cast(floor((_animation->getDuration() - blendOutDuration) * _fps)); + _blendOut = FrameAction(start, new BlendOut(animation, blendOutDuration)); + setName(animation->getName() + "_Strip"); + _blendIn->setName(_animation->getName() + "_" + _blendIn->getName()); + _blendOut.second->setName(_animation->getName() + "_" + _blendOut.second->getName()); + setDuration(animation->getDuration()); + } + + ActionAnimation* getActionAnimation() { return _animation.get(); } + BlendIn* getBlendIn() { return _blendIn.get(); } + BlendOut* getBlendOut() { return dynamic_cast(_blendOut.second.get()); } + const ActionAnimation* getActionAnimation() const { return _animation.get(); } + const BlendIn* getBlendIn() const { return _blendIn.get(); } + const BlendOut* getBlendOut() const { return dynamic_cast(_blendOut.second.get()); } + + unsigned int getLoop() const { return _animation->getLoop(); } + void setLoop(unsigned int loop) + { + _animation->setLoop(loop); + if (!loop) + setDuration(-1); + else + setDuration(loop * _animation->getDuration()); + + // duration changed re evaluate the blendout duration + unsigned int start = static_cast(floor((getDuration() - _blendOut.second->getDuration()) * _fps)); + _blendOut = FrameAction(start, _blendOut.second); + } + + virtual void evaluate(unsigned int frame) + { + if (frame > getNumFrames() - 1) + return; + + Action::evaluate(frame); + if (frame < _blendIn->getNumFrames()) + _blendIn->evaluate(frame); + if (frame >= _blendOut.first) + _blendOut.second->evaluate(frame - _blendOut.first); + _animation->evaluate(frame); + } + + protected: + osg::ref_ptr _blendIn; + FrameAction _blendOut; + osg::ref_ptr _animation; + }; + + + class RunAction : public Action::Callback + { + protected: + osg::ref_ptr _tm; + osg::ref_ptr _action; + + public: + RunAction(Timeline* tm, Action* a) : _tm(tm), _action(a) {} + virtual void operator()(Action* action) + { + _tm->addActionAt(_tm->getCurrentFrame(), _action.get()); // warning we are trsversing the vector + } + }; + + + + class AnimationManagerTimeline : public AnimationManagerBase + { + protected: + osg::ref_ptr _timeline; + + public: + META_Node(osgAnimation, AnimationManagerTimeline); + AnimationManagerTimeline(); + AnimationManagerTimeline(const AnimationManagerBase& manager); + AnimationManagerTimeline(const AnimationManagerTimeline& nc,const osg::CopyOp&) {} + + Timeline* getTimeline() { return _timeline.get(); } + void update(double time); + + }; + + +} + +#endif diff --git a/include/osgAnimation/UpdateCallback b/include/osgAnimation/UpdateCallback new file mode 100644 index 000000000..d0d396089 --- /dev/null +++ b/include/osgAnimation/UpdateCallback @@ -0,0 +1,68 @@ +/* -*-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_UPDATE_CALLBACK_H +#define OSGANIMATION_UPDATE_CALLBACK_H + +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT AnimationUpdateCallback : public osg::NodeCallback + { + protected: + osg::observer_ptr _manager; + + public: + AnimationUpdateCallback(const std::string& name = "") { setName(name); } + AnimationUpdateCallback(const AnimationUpdateCallback& apc,const osg::CopyOp& copyop); + osgAnimation::AnimationManagerBase* getAnimationManager(); + virtual bool needLink() const = 0; + virtual bool link(osgAnimation::Channel* channel) = 0; + virtual int link(osgAnimation::Animation* animation); + virtual void updateLink(); + }; + + + class OSGANIMATION_EXPORT UpdateTransform : public AnimationUpdateCallback + { + protected: + osg::ref_ptr _euler; + osg::ref_ptr _position; + osg::ref_ptr _scale; + + public: + + META_Object(osgAnimation, UpdateTransform); + + UpdateTransform(const std::string& name = ""); + UpdateTransform(const UpdateTransform& apc,const osg::CopyOp& copyop); + + /** Callback method called by the NodeVisitor when visiting a node.*/ + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); + void update(osg::MatrixTransform& mat); + void update(osg::PositionAttitudeTransform& pat); + bool needLink() const; + bool link(osgAnimation::Channel* channel); + }; + + +} + +#endif diff --git a/include/osgAnimation/Vec3Packed b/include/osgAnimation/Vec3Packed new file mode 100644 index 000000000..82c653177 --- /dev/null +++ b/include/osgAnimation/Vec3Packed @@ -0,0 +1,118 @@ +/* -*-c++-*- +*/ +//****************************************************************************// +// loader.cpp // +// Copyright (C) 2001, 2002 Bruno 'Beosil' Heidelberger // +//****************************************************************************// +// This library is free software; you can redistribute it and/or modify it // +// under the terms of the GNU Lesser General Public License as published by // +// the Free Software Foundation; either version 2.1 of the License, or (at // +// your option) any later version. // +//****************************************************************************// +/*****************************************************************************/ +/** Loads a core compressed keyframe instance. + * + * This function loads a core compressed keyframe instance from a data source. + * + * @param dataSrc The data source to load the core compressed keyframe instance from. + * + * @return One of the following values: + * \li a pointer to the core keyframe + * \li \b 0 if an error happened + * Authors + * Igor Kravchenko + * Cedric Pinson + *****************************************************************************/ + + +#ifndef OSGANIMATION_PACKED_H +#define OSGANIMATION_PACKED_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + + struct Vec3Packed + { + typedef unsigned int uint32_t; + uint32_t m32bits; + Vec3Packed(uint32_t val): m32bits(val) {} + Vec3Packed(): m32bits(0) {} + + void uncompress(const osg::Vec3& scale, const osg::Vec3& min, osg::Vec3& result) const + { + uint32_t pt[3]; + pt[0] = m32bits & 0x7ff; + pt[1] = (m32bits >> 11) & 0x7ff; + pt[2] = m32bits >> 22; + result[0] = scale[0] * pt[0] + min[0]; + result[1] = scale[1] * pt[1] + min[1]; + result[2] = scale[2] * pt[2] + min[2]; + } + + void compress(const osg::Vec3f& src, const osg::Vec3f& min, const osg::Vec3f& scaleInv) + { + uint32_t srci[3]; + srci[0] = osg::minimum(static_cast(((src[0] - min[0] )*scaleInv[0])), uint32_t(2047)); + srci[1] = osg::minimum(static_cast(((src[1] - min[1] )*scaleInv[1])), uint32_t(2047)); + srci[2] = osg::minimum(static_cast(((src[2] - min[2] )*scaleInv[2])), uint32_t(1023)); + m32bits = srci[0] + (srci[1] << 11) + (srci[2] << 22); + } + }; + + struct Vec3ArrayPacked + { + std::vector mVecCompressed; + osg::Vec3 mMin; + osg::Vec3 mScale; + osg::Vec3 mScaleInv; + + void analyze(const std::vector& src) + { + //analyze the keys + mMin.set(FLT_MAX, FLT_MAX, FLT_MAX); + osg::Vec3 maxp(-FLT_MAX, -FLT_MAX, -FLT_MAX); + int nb = (int)src.size(); + for(int i = 0; i < nb; i++) + { + const osg::Vec3 &pos = src[i]; + for(int j = 0; j < 3; j++) + { + maxp[j] = osg::maximum(pos[j],maxp[j]); + mMin[j] = osg::minimum(pos[j],mMin[j]); + } + } + + osg::Vec3 diff = maxp - mMin; + mScaleInv.set(0,0,0); + if (diff[0] != 0) + mScaleInv[0] = 2047.0/diff[0]; + + if (diff[1] != 0) + mScaleInv[1] = 2047.0/diff[1]; + + if (diff[2] != 0) + mScaleInv[2] = 1023.0/diff[2]; + + mScale[0] = diff[0] / 2047; + mScale[1] = diff[1] / 2047; + mScale[2] = diff[2] / 1023; + } + + void compress(const std::vector& src) + { + mVecCompressed.resize(src.size()); + // save all core keyframes + for(int i = 0; i < (int)src.size(); i++) + mVecCompressed[i].compress(src[i], mMin, mScaleInv); + } + }; + +} + +#endif diff --git a/include/osgAnimation/VertexInfluence b/include/osgAnimation/VertexInfluence new file mode 100644 index 000000000..aadbdc5d3 --- /dev/null +++ b/include/osgAnimation/VertexInfluence @@ -0,0 +1,108 @@ +/* -*-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_VERTEX_INFLUENCES_H +#define OSGANIMATION_VERTEX_INFLUENCES_H + +#include +#include +#include +#include +#include + +namespace osgAnimation +{ + + // first is vertex index, and second the weight, the + typedef std::pair VertexIndexWeight; + typedef std::vector VertexList; + class OSGANIMATION_EXPORT VertexInfluence : public VertexList + { + public: + const std::string& getName() const { return _name;} + void setName(const std::string& name) { _name = name;} + + protected: + // the name is the bone to link to + std::string _name; + }; + + // typedef std::map VertexInfluenceMap; + + class VertexInfluenceMap : public std::map , public osg::Object + { + public: + META_Object(osgAnimation, VertexInfluenceMap); + + VertexInfluenceMap() {} + VertexInfluenceMap(const osgAnimation::VertexInfluenceMap& infl, const osg::CopyOp&) {;} + }; + + + // this class manage VertexInfluence database by mesh + // reference bones per vertex ... + class VertexInfluenceSet + { + public: + typedef std::vector BoneToVertexList; + + class BoneWeight + { + public: + BoneWeight(const std::string& name, float weight) : _boneName(name), _weight(weight) {} + const std::string& getBoneName() const { return _boneName; } + float getWeight() const { return _weight; } + void setWeight(float weight) { _weight = weight; } + bool operator==(const BoneWeight& b) const { return (_boneName == b.getBoneName() && _weight == b.getWeight()); } + protected: + std::string _boneName; + float _weight; + }; + + typedef std::vector BoneWeightList; + typedef std::map VertexIndexToBoneWeightMap; + + class UniqVertexSetToBoneSet + { + public: + void setBones(BoneWeightList& bones) { _bones = bones;} + const BoneWeightList& getBones() const { return _bones;} + std::vector& getVertexes() { return _vertexes;} + const std::vector& getVertexes() const { return _vertexes;} + protected: + std::vector _vertexes; + BoneWeightList _bones; // here we could limit matrix operation by caching (weight * matrix) + }; + + typedef std::vector UniqVertexSetToBoneSetList; + + const UniqVertexSetToBoneSetList& getUniqVertexSetToBoneSetList() const { return _uniqVertexSetToBoneSet;} + void addVertexInfluence(const VertexInfluence& v) { _bone2Vertexes.push_back(v); } + void buildVertex2BoneList(); + void buildUniqVertexSetToBoneSetList(); + void clear() + { + _bone2Vertexes.clear(); + _bone2Vertexes.clear(); + _uniqVertexSetToBoneSet.clear(); + } + protected: + BoneToVertexList _bone2Vertexes; + VertexIndexToBoneWeightMap _vertex2Bones; + UniqVertexSetToBoneSetList _uniqVertexSetToBoneSet; + }; + +} + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2d3ffb9a8..fdcee4423 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,14 +6,15 @@ FOREACH( mylibfolder osgUtil osgGA osgText - osgManipulator - osgSim + osgViewer + osgAnimation osgFX + osgManipulator osgParticle osgShadow + osgSim osgTerrain osgVolume - osgViewer ) ADD_SUBDIRECTORY(${mylibfolder}) diff --git a/src/osgAnimation/Animation.cpp b/src/osgAnimation/Animation.cpp new file mode 100644 index 000000000..2f30a7794 --- /dev/null +++ b/src/osgAnimation/Animation.cpp @@ -0,0 +1,147 @@ +/* -*-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 + +using namespace osgAnimation; + +Animation::Animation(const osgAnimation::Animation& anim, const osg::CopyOp& c) +{ + _duration = anim._duration; + _originalDuration = anim._originalDuration; + _weight = anim._weight; + _startTime = anim._startTime; + _playmode = anim._playmode; +} + + +void Animation::addChannel(Channel* pChannel) +{ + _channels.push_back(pChannel); + if (!_duration) + computeDuration(); + else + _originalDuration = computeDurationFromChannels(); +} + +double Animation::computeDurationFromChannels() const +{ + double tmin = 1e5; + double tmax = -1e5; + ChannelList::const_iterator chan; + for( chan=_channels.begin(); chan!=_channels.end(); chan++ ) + { + float min = (*chan)->getStartTime(); + if (min < tmin) + tmin = min; + float max = (*chan)->getEndTime(); + if (max > tmax) + tmax = max; + } + return tmax-tmin; +} + +void Animation::computeDuration() +{ + _duration = computeDurationFromChannels(); + _originalDuration = _duration; +} + +osgAnimation::ChannelList& Animation::getChannels() +{ + return _channels; +} + +const osgAnimation::ChannelList& Animation::getChannels() const +{ + return _channels; +} + + +void Animation::setDuration(double duration) +{ + _originalDuration = computeDurationFromChannels(); + _duration = duration; +} + +float Animation::getDuration() const +{ + return _duration; +} + +float Animation::getWeight () const +{ + return _weight; +} + +void Animation::setWeight (float weight) +{ + _weight = weight; +} + +bool Animation::update (float time) +{ + if (!_duration) // if not initialized then do it + computeDuration(); + + double ratio = _originalDuration / _duration; + + float t = (time - _startTime) * ratio; + switch (_playmode) + { + case ONCE: + if (t > _duration) + return false; + break; + case STAY: + if (t > _duration) + t = _duration; + break; + case LOOP: + if (!_duration) + t = _startTime; + else if (t > _duration) + t = fmodf(t, _duration); + // std::cout << "t " << t << " duration " << _duration << std::endl; + break; + case PPONG: + if (!_duration) + t = _startTime; + else + { + int tt = (int) (t / _duration); + t = fmodf(t, _duration); + if (tt%2) + t = _duration - t; + } + break; + } + + // std::cout << "t " << t << " / " << _duration << std::endl; + + ChannelList::const_iterator chan; + for( chan=_channels.begin(); chan!=_channels.end(); ++chan) + { + (*chan)->setWeight(_weight); + (*chan)->update(t); + } + return true; +} + +void Animation::resetTargets() +{ + ChannelList::const_iterator chan; + for( chan=_channels.begin(); chan!=_channels.end(); ++chan) + (*chan)->reset(); +} diff --git a/src/osgAnimation/AnimationManager.cpp b/src/osgAnimation/AnimationManager.cpp new file mode 100644 index 000000000..d0f008917 --- /dev/null +++ b/src/osgAnimation/AnimationManager.cpp @@ -0,0 +1,141 @@ +/* -*-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 + +using namespace osgAnimation; + + +AnimationManager::~AnimationManager() {} + + +void AnimationManager::stopAll() +{ + // loop over all playing animation + for( AnimationLayers::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) + { + AnimationList& list = iterAnim->second; + for (AnimationList::iterator it = list.begin(); it != list.end(); it++) + (*it)->resetTargets(); + } + _animationsPlaying.clear(); +} + +AnimationManager::AnimationManager() +{ + _lastUpdate = 0; +} +void AnimationManager::playAnimation(Animation* pAnimation, int priority, float weight) +{ + bool r = findAnimation(pAnimation); + OSGANIMATION_ASSERT(r && "This animation is not registered"); + + if ( isPlaying(pAnimation) ) + stopAnimation(pAnimation); + + _animationsPlaying[priority].push_back(pAnimation); + pAnimation->setStartTime(_lastUpdate); + pAnimation->setWeight(weight); +} + +bool AnimationManager::stopAnimation(Animation* pAnimation) +{ + // search though the layer and remove animation + for( AnimationLayers::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) + { + AnimationList& list = iterAnim->second; + for (AnimationList::iterator it = list.begin(); it != list.end(); it++) + if( (*it) == pAnimation ) + { + (*it)->resetTargets(); + list.erase(it); + return true; + } + } + return false; +} + + +void AnimationManager::update (double time) +{ + if (!_lastUpdate) + _lastUpdate = time; + + // could filtered with an active flag + for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); it++) + (*it).get()->reset(); + + + // update from high priority to low priority + for( AnimationLayers::reverse_iterator iterAnim = _animationsPlaying.rbegin(); iterAnim != _animationsPlaying.rend(); ++iterAnim ) + { + // update all animation + std::vector toremove; + AnimationList& list = iterAnim->second; + for (unsigned int i = 0; i < list.size(); i++) + { + if (! list[i]->update(time)) + toremove.push_back(i); + } + + // remove finished animation + while (!toremove.empty()) + { + list.erase(list.begin() + toremove.back()); + toremove.pop_back(); + } + } + + for (TargetSet::iterator it = _targets.begin(); it != _targets.end(); it++) + (*it).get()->normalize(); +} + + +bool AnimationManager::findAnimation(Animation* pAnimation) +{ + for( AnimationList::const_iterator iterAnim = _animations.begin(); iterAnim != _animations.end(); ++iterAnim ) + { + if ( (*iterAnim) == pAnimation ) + return true; + } + return false; +} + + +bool AnimationManager::isPlaying(Animation* pAnimation) +{ + for( AnimationLayers::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) + { + AnimationList& list = iterAnim->second; + for (AnimationList::iterator it = list.begin(); it != list.end(); it++) + if ( (*it) == pAnimation ) + return true; + } + return false; +} + +bool AnimationManager::isPlaying(const std::string& name) +{ + // loop over all playing animation + for( AnimationLayers::iterator iterAnim = _animationsPlaying.begin(); iterAnim != _animationsPlaying.end(); ++iterAnim ) + { + AnimationList& list = iterAnim->second; + for (AnimationList::iterator it = list.begin(); it != list.end(); it++) + if ( (*it)->getName() == name ) + return true; + } + return false; +} diff --git a/src/osgAnimation/AnimationManagerBase.cpp b/src/osgAnimation/AnimationManagerBase.cpp new file mode 100644 index 000000000..8a989a013 --- /dev/null +++ b/src/osgAnimation/AnimationManagerBase.cpp @@ -0,0 +1,71 @@ +/* -*-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 + +using namespace osgAnimation; + + +AnimationManagerBase::~AnimationManagerBase() {} + +AnimationManagerBase::AnimationManagerBase() +{ + setUpdateCallback(new UpdateCallback); + _needToLink = false; +} + +void AnimationManagerBase::buildTargetReference() +{ + _targets.clear(); + for( AnimationList::iterator iterAnim = _animations.begin(); iterAnim != _animations.end(); ++iterAnim ) + { + Animation* anim = (*iterAnim).get(); + for (ChannelList::iterator it = anim->getChannels().begin(); + it != anim->getChannels().end(); + it++) + _targets.insert((*it)->getTarget()); + } +} + + +void AnimationManagerBase::registerAnimation (Animation* animation) +{ + _needToLink = true; + _animations.push_back(animation); + buildTargetReference(); +} + +bool AnimationManagerBase::needToLink() const { return _needToLink; } + + + +void AnimationManagerBase::link() +{ + LinkVisitor linker(_animations); + accept(linker); + _needToLink = false; + buildTargetReference(); +} + + +osgAnimation::AnimationMap AnimationManagerBase::getAnimationMap() const +{ + osgAnimation::AnimationMap map; + for (AnimationList::const_iterator it = _animations.begin(); it != _animations.end(); it++) + map[(*it)->getName()] = *it; + return map; +} + diff --git a/src/osgAnimation/Bone.cpp b/src/osgAnimation/Bone.cpp new file mode 100644 index 000000000..781259934 --- /dev/null +++ b/src/osgAnimation/Bone.cpp @@ -0,0 +1,104 @@ +/* -*-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 + +osgAnimation::Bone::UpdateBone::UpdateBone(const osgAnimation::Bone::UpdateBone& apc,const osg::CopyOp& copyop): + osgAnimation::AnimationUpdateCallback(apc, copyop), + _position(apc._position), + _quaternion(apc._quaternion), + _scale(apc._scale) +{ +} + +void osgAnimation::Bone::computeBindMatrix() +{ + _invBindInSkeletonSpace = osg::Matrix::inverse(_bindInBoneSpace); + const Bone* parent = getBoneParent(); + _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; +} + +osgAnimation::Bone* osgAnimation::Bone::getBoneParent() +{ + if (getParents().empty()) + return 0; + osg::Node::ParentList parents = getParents(); + for (osg::Node::ParentList::iterator it = parents.begin(); it != parents.end(); it++) + { + Bone* pb = dynamic_cast(*it); + if (pb) + return pb; + } + return 0; +} +const osgAnimation::Bone* osgAnimation::Bone::getBoneParent() const +{ + if (getParents().empty()) + return 0; + const osg::Node::ParentList& parents = getParents(); + for (osg::Node::ParentList::const_iterator it = parents.begin(); it != parents.end(); it++) + { + const Bone* pb = dynamic_cast(*it); + if (pb) + return pb; + } + return 0; +} + + +/** Add Node to Group. + * If node is not NULL and is not contained in Group then increment its + * reference count, add it to the child list and dirty the bounding + * sphere to force it to recompute on next getBound() and return true for success. + * Otherwise return false. Scene nodes can't be added as child nodes. + */ +bool osgAnimation::Bone::addChild( Node *child ) +{ + Bone* bone = dynamic_cast(child); + if (bone) + bone->setNeedToComputeBindMatrix(true); + return osg::Group::addChild(child); +} + +osgAnimation::Bone::BoneMap osgAnimation::Bone::getBoneMap() +{ + BoneMapVisitor mapVisitor; + this->accept(mapVisitor); + return mapVisitor._map; +} diff --git a/src/osgAnimation/CMakeLists.txt b/src/osgAnimation/CMakeLists.txt new file mode 100644 index 000000000..cf9d7b380 --- /dev/null +++ b/src/osgAnimation/CMakeLists.txt @@ -0,0 +1,56 @@ + +IF (DYNAMIC_OPENSCENEGRAPH) + ADD_DEFINITIONS(-DOSGANIMATION_LIBRARY) +ELSE (DYNAMIC_OPENSCENEGRAPH) + ADD_DEFINITIONS(-DOSG_LIBRARY_STATIC) +ENDIF(DYNAMIC_OPENSCENEGRAPH) + +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}/AnimationManager + ${HEADER_PATH}/AnimationManagerBase + ${HEADER_PATH}/UpdateCallback + ${HEADER_PATH}/LinkVisitor + ${HEADER_PATH}/VertexInfluence + ${HEADER_PATH}/EaseMotion + ${HEADER_PATH}/Assert + ${HEADER_PATH}/Timeline +) + + +ADD_LIBRARY(${LIB_NAME} + ${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC} + ${LIB_PUBLIC_HEADERS} + Channel.cpp + Target.cpp + Animation.cpp + Bone.cpp + RigGeometry.cpp + AnimationManager.cpp + AnimationManagerBase.cpp + Skeleton.cpp + VertexInfluence.cpp + UpdateCallback.cpp + Timeline.cpp +) + +LINK_INTERNAL(${LIB_NAME} + osg +) +LINK_CORELIB_DEFAULT(${LIB_NAME}) +INCLUDE(ModuleInstall OPTIONAL) diff --git a/src/osgAnimation/Channel.cpp b/src/osgAnimation/Channel.cpp new file mode 100644 index 000000000..559d83573 --- /dev/null +++ b/src/osgAnimation/Channel.cpp @@ -0,0 +1,29 @@ +/* -*-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 +using namespace osgAnimation; + +Channel::Channel() { _weight=1; } +Channel::~Channel() {} + +const std::string& Channel::getName() const { return _name; } +void Channel::setName (const std::string& name) { _name = name; } + +const std::string& Channel::getTargetName() const { return _targetName;} +void Channel::setTargetName (const std::string& name) { _targetName = name; } + +float Channel::getWeight() const { return _weight;} +void Channel::setWeight(float w) { _weight = w;} + diff --git a/src/osgAnimation/RigGeometry.cpp b/src/osgAnimation/RigGeometry.cpp new file mode 100644 index 000000000..8cb946c76 --- /dev/null +++ b/src/osgAnimation/RigGeometry.cpp @@ -0,0 +1,93 @@ +/* -*-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: + * + * Cedric Pinson + * + */ +#include + +void osgAnimation::RigGeometry::buildTransformer(Skeleton* root) +{ + Bone::BoneMap bm = root->getBoneMap(); + _transformVertexes.init(bm, _vertexInfluenceSet.getUniqVertexSetToBoneSetList()); + _root = root; +} + +void osgAnimation::RigGeometry::buildVertexSet() +{ + if (!_vertexInfluenceMap.valid()) + { + osg::notify(osg::WARN) << "buildVertexSet can't be called without VertexInfluence already set to the RigGeometry ( " << getName() << " ) " << std::endl; + return; + } + _vertexInfluenceSet.clear(); + for (osgAnimation::VertexInfluenceMap::iterator it = _vertexInfluenceMap->begin(); + it != _vertexInfluenceMap->end(); + it++) + _vertexInfluenceSet.addVertexInfluence(it->second); + + _vertexInfluenceSet.buildVertex2BoneList(); + _vertexInfluenceSet.buildUniqVertexSetToBoneSetList(); + std::cout << "uniq groups " << _vertexInfluenceSet.getUniqVertexSetToBoneSetList().size() << " for " << getName() << std::endl; +} + +void osgAnimation::RigGeometry::computeMatrixFromRootSkeleton() +{ + if (!_root.valid()) + { + osg::notify(osg::WARN) << "Warning " << className() <<"::computeMatrixFromRootSkeleton if you have this message it means you miss to call buildTransformer(Skeleton* root), or your RigGeometry (" << getName() <<") is not attached to a Skeleton subgraph" << std::endl; + return; + } + osg::MatrixList mtxList = getParent(0)->getWorldMatrices(_root.get()); + _matrixFromSkeletonToGeometry = mtxList[0]; + _invMatrixFromSkeletonToGeometry = osg::Matrix::inverse(_matrixFromSkeletonToGeometry); + _needToComputeMatrix = false; +} + +void osgAnimation::RigGeometry::transformSoftwareMethod() +{ + // std::cout << getName() << " _matrixFromSkeletonToGeometry" << _matrixFromSkeletonToGeometry << std::endl; + osg::Vec3Array* pos = dynamic_cast(getVertexArray()); + if (pos && _positionSource.size() != pos->size()) + { + _positionSource = std::vector(pos->begin(),pos->end()); + getVertexArray()->setDataVariance(osg::Object::DYNAMIC); + } + osg::Vec3Array* normal = dynamic_cast(getNormalArray()); + if (normal && _normalSource.size() != normal->size()) + { + _normalSource = std::vector(normal->begin(),normal->end()); + getNormalArray()->setDataVariance(osg::Object::DYNAMIC); + } + + if (!_positionSource.empty()) + { + _transformVertexes.compute(_matrixFromSkeletonToGeometry, _invMatrixFromSkeletonToGeometry, &_positionSource.front(), &pos->front()); + pos->dirty(); + } + if (!_normalSource.empty()) + { + _transformVertexes.compute(_matrixFromSkeletonToGeometry, _invMatrixFromSkeletonToGeometry, &_normalSource.front(), &normal->front()); + normal->dirty(); + } + dirtyBound(); +} + +const osgAnimation::Skeleton* osgAnimation::RigGeometry::getSkeleton() const { return _root.get(); } +osgAnimation::Skeleton* osgAnimation::RigGeometry::getSkeleton() { return _root.get(); } diff --git a/src/osgAnimation/Skeleton.cpp b/src/osgAnimation/Skeleton.cpp new file mode 100644 index 000000000..f74759b91 --- /dev/null +++ b/src/osgAnimation/Skeleton.cpp @@ -0,0 +1,94 @@ +/* -*-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 + +using namespace osgAnimation; + +struct computeBindMatrixVisitor : public osg::NodeVisitor +{ + osg::Matrix _skeleton; + computeBindMatrixVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + void apply(osg::Node& node) { return ;} + void apply(osg::Transform& node) + { + Bone* bone = dynamic_cast(&node); + if (!bone) + return; + bone->computeBindMatrix(); + traverse(node); + } +}; + +struct updateMatrixVisitor : public osg::NodeVisitor +{ + osg::Matrix _skeleton; + updateMatrixVisitor(): osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + void apply(osg::Node& node) { return ;} + void apply(osg::Transform& node) + { + // the idea is to traverse the skeleton or bone but to stop if other node is found + Bone* bone = dynamic_cast(&node); + if (!bone) + return; + + Bone* parent = bone->getBoneParent(); + if (bone->needToComputeBindMatrix()) + { + computeBindMatrixVisitor visitor; + bone->accept(visitor); + } + + if (parent) + bone->_boneInSkeletonSpace = bone->getMatrixInBoneSpace() * bone->getBoneParent()->getMatrixInSkeletonSpace(); + else + bone->_boneInSkeletonSpace = bone->getMatrixInBoneSpace();// * _skeleton; + + traverse(node); + } +}; + +void Skeleton::UpdateCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + Skeleton* b = dynamic_cast(node); + if (b) + { + // apply the updater only on the root bone, The udpateMatrixVisitor will + // traverse only bone and will update only bone. Then we continu on the classic + // process. It's important to update Bone before other things because the update + // of RigGeometry need it + updateMatrixVisitor visitor; +#if 0 + visitor._skeleton = b->getMatrix(); + int numChildren = b->getNumChildren(); + for (int i = 0; i < numChildren; i++) + { + b->getChild(i)->accept(visitor); + } +#else + b->accept(visitor); +#endif + } + } + traverse(node,nv); +} + + +Skeleton::Skeleton() +{ + setUpdateCallback(new UpdateCallback()); +} diff --git a/src/osgAnimation/Target.cpp b/src/osgAnimation/Target.cpp new file mode 100644 index 000000000..7bb2da215 --- /dev/null +++ b/src/osgAnimation/Target.cpp @@ -0,0 +1,22 @@ +/* -*-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 + +using namespace osgAnimation; + +Target::Target() { reset();} +Target::~Target() {} diff --git a/src/osgAnimation/Timeline.cpp b/src/osgAnimation/Timeline.cpp new file mode 100644 index 000000000..a34c74854 --- /dev/null +++ b/src/osgAnimation/Timeline.cpp @@ -0,0 +1,49 @@ +/* -*-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 + +using namespace osgAnimation; + +// temporary +// the problem comes that the AnimationManagerBase should only a group +// and it's data should be in an update callback +struct TimelineAdaptator : public Timeline +{ + osg::ref_ptr _manager; + + TimelineAdaptator(AnimationManagerTimeline* manager) : _manager(manager) {} + void evaluate(unsigned int frame) + { + _manager->clearTargets(); + Timeline::evaluate(frame); + _manager->normalizeTargets(); + } +}; + +AnimationManagerTimeline::AnimationManagerTimeline() +{ + _timeline = new TimelineAdaptator(this); +} + +AnimationManagerTimeline::AnimationManagerTimeline(const AnimationManagerBase& manager) : AnimationManagerBase(manager) +{ + _timeline = new TimelineAdaptator(this); +} + +void AnimationManagerTimeline::update(double time) +{ + _timeline->update(time); +} + diff --git a/src/osgAnimation/UpdateCallback.cpp b/src/osgAnimation/UpdateCallback.cpp new file mode 100644 index 000000000..c2b96786c --- /dev/null +++ b/src/osgAnimation/UpdateCallback.cpp @@ -0,0 +1,170 @@ +/* -*-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 + +using namespace osgAnimation; + +osgAnimation::AnimationManagerBase* AnimationUpdateCallback::getAnimationManager() { return _manager.get(); } + +AnimationUpdateCallback::AnimationUpdateCallback(const AnimationUpdateCallback& apc,const osg::CopyOp& copyop): + osg::NodeCallback(apc, copyop), + _manager(apc._manager) {} + +int AnimationUpdateCallback::link(osgAnimation::Animation* animation) +{ + int nbLinks = 0; + for (osgAnimation::ChannelList::iterator it = animation->getChannels().begin(); + it != animation->getChannels().end(); + it++) + { + std::string targetName = (*it)->getTargetName(); + if (targetName == getName()) + { + link((*it).get()); + nbLinks++; + } + } + return nbLinks; +} + +void AnimationUpdateCallback::updateLink() +{ + if (_manager.valid()) + { + if (needLink()) + { + /** this item is not linked yet then we do it for all animation + registered in the manager. + Maybe this function should be on the manager side like + _manager->linkItem(Bone); + */ + AnimationMap map = _manager->getAnimationMap(); + for (AnimationMap::iterator it = map.begin(); it != map.end(); it++) + link(it->second.get()); + _manager->buildTargetReference(); + } + } +} + + + + +UpdateTransform::UpdateTransform(const UpdateTransform& apc,const osg::CopyOp& copyop) + : AnimationUpdateCallback(apc, copyop), + _euler(apc._euler), + _position(apc._position), + _scale(apc._scale) +{ +} + +UpdateTransform::UpdateTransform(const std::string& name) : AnimationUpdateCallback(name) +{ + _euler = new osgAnimation::Vec3Target; + _position = new osgAnimation::Vec3Target; + _scale = new osgAnimation::Vec3Target(osg::Vec3(1,1,1)); +} + +/** Callback method called by the NodeVisitor when visiting a node.*/ +void UpdateTransform::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + if (nv && nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) + { + osg::MatrixTransform* matrix = dynamic_cast(node); + if (matrix) + { + update(*matrix); + } + else + { + osg::PositionAttitudeTransform* pat = dynamic_cast(node); + if (pat) + update(*pat); + } + } + traverse(node,nv); +} + +void UpdateTransform::update(osg::MatrixTransform& mat) +{ + float z = _euler->getValue()[2]; + float x = _euler->getValue()[0]; + float y = _euler->getValue()[1]; + osg::Matrix m = + osg::Matrix::rotate(x,1.0,0.0,0.0) * + osg::Matrix::rotate(y,0.0,1.0,0.0) * + osg::Matrix::rotate(z,0.0,0.0,1.0); + mat.setMatrix(osg::Matrix::scale(_scale->getValue()) * + m * + osg::Matrix::translate(_position->getValue())); + mat.dirtyBound(); +} + +void UpdateTransform::update(osg::PositionAttitudeTransform& pat) +{ + float heading = _euler->getValue()[0]; + float pitch = _euler->getValue()[1]; + float roll = _euler->getValue()[2]; + osg::Matrix m = osg::Matrix::rotate(roll,0.0,1.0,0.0) * osg::Matrix::rotate(pitch,1.0,0.0,0.0) * osg::Matrix::rotate(-heading,0.0,0.0,1.0); + osg::Quat q = m.getRotate(); + + pat.setPosition(_position->getValue()); + pat.setScale(_scale->getValue()); + pat.setAttitude(q); + pat.dirtyBound(); +} + +bool UpdateTransform::needLink() const +{ + // the idea is to return true if nothing is linked + return !((_position->getCount() + _euler->getCount() + _scale->getCount()) > 3); +} + +bool UpdateTransform::link(osgAnimation::Channel* channel) +{ + if (channel->getName().find("euler") != std::string::npos) + { + osgAnimation::Vec3LinearChannel* qc = dynamic_cast(channel); + if (qc) + { + qc->setTarget(_euler.get()); + return true; + } + } + else if (channel->getName().find("position") != std::string::npos) + { + osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); + if (vc) + { + vc->setTarget(_position.get()); + return true; + } + } + else if (channel->getName().find("scale") != std::string::npos) + { + osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); + if (vc) + { + vc->setTarget(_scale.get()); + 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/VertexInfluence.cpp b/src/osgAnimation/VertexInfluence.cpp new file mode 100644 index 000000000..3ef29f0db --- /dev/null +++ b/src/osgAnimation/VertexInfluence.cpp @@ -0,0 +1,130 @@ +/* -*-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 + +using namespace osgAnimation; + +// this class manage VertexInfluence database by mesh +// reference bones per vertex ... +void osgAnimation::VertexInfluenceSet::buildVertex2BoneList() +{ + _vertex2Bones.clear(); + for (BoneToVertexList::const_iterator it = _bone2Vertexes.begin(); it != _bone2Vertexes.end(); it++) + { + const VertexInfluence& vi = (*it); + int size = vi.size(); + for (int i = 0; i < size; i++) + { + VertexIndexWeight viw = vi[i]; + int index = viw.first; + float weight = viw.second; + if (vi.getName().empty()) + std::cout << "osgAnimation::VertexInfluenceSet::buildVertex2BoneList warning vertex " << index << " is not assigned to a bone" << std::endl; + _vertex2Bones[index].push_back(BoneWeight(vi.getName(), weight)); + } + } + + // normalize weight per vertex + for (VertexIndexToBoneWeightMap::iterator it = _vertex2Bones.begin(); it != _vertex2Bones.end(); it++) + { + BoneWeightList& bones = it->second; + int size = bones.size(); + float sum = 0; + for (int i = 0; i < size; i++) + sum += bones[i].getWeight(); + if (sum < 1e-4) + { + std::cerr << "VertexInfluenceSet::buildVertex2BoneList warning the vertex " << it->first << " seems to have 0 weight, skip normalize for this vertex" << std::endl; + } + else + { + float mult = 1.0/sum; + for (int i = 0; i < size; i++) + bones[i].setWeight(bones[i].getWeight() * mult); + } + } +} + + + +// sort by name and weight +struct SortByNameAndWeight : public std::less +{ + bool operator()(const osgAnimation::VertexInfluenceSet::BoneWeight& b0, + const osgAnimation::VertexInfluenceSet::BoneWeight& b1) const + { + if (b0.getBoneName() < b1.getBoneName()) + return true; + else if (b0.getBoneName() > b1.getBoneName()) + return false; + if (b0.getWeight() < b1.getWeight()) + return true; + return false; + } +}; + +struct SortByBoneWeightList : public std::less +{ + bool operator()(const osgAnimation::VertexInfluenceSet::BoneWeightList& b0, + const osgAnimation::VertexInfluenceSet::BoneWeightList& b1) const + { + if (b0.size() < b1.size()) + return true; + else if (b0.size() > b1.size()) + return false; + + int size = b0.size(); + for (int i = 0; i < size; i++) + { + bool result = SortByNameAndWeight()(b0[i], b1[i]); + if (result) + return true; + else if (SortByNameAndWeight()(b1[i], b0[i])) + return false; + } + return false; + } +}; + +void osgAnimation::VertexInfluenceSet::buildUniqVertexSetToBoneSetList() +{ + _uniqVertexSetToBoneSet.clear(); + + typedef std::map UnifyBoneGroup; + UnifyBoneGroup unifyBuffer; + + for (VertexIndexToBoneWeightMap::iterator it = _vertex2Bones.begin(); it != _vertex2Bones.end(); it++) + { + BoneWeightList bones = it->second; + int vertexIndex = it->first; + + // sort the vector to have a consistent key + std::sort(bones.begin(), bones.end(), SortByNameAndWeight()); + + // we use the vector as key to differentiate group + UnifyBoneGroup::iterator result = unifyBuffer.find(bones); + if (result == unifyBuffer.end()) + unifyBuffer[bones].setBones(bones); + unifyBuffer[bones].getVertexes().push_back(vertexIndex); + } + + _uniqVertexSetToBoneSet.reserve(unifyBuffer.size()); + for (UnifyBoneGroup::iterator it = unifyBuffer.begin(); it != unifyBuffer.end(); it++) + { + _uniqVertexSetToBoneSet.push_back(it->second); + } +} diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index afe5092c5..cca012c1e 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -33,6 +33,7 @@ SET(TARGET_COMMON_LIBRARIES # # NodeKit/Psudo loader plugins # +ADD_SUBDIRECTORY(osgAnimation) ADD_SUBDIRECTORY(osgFX) ADD_SUBDIRECTORY(osgParticle) ADD_SUBDIRECTORY(osgSim) diff --git a/src/osgPlugins/osgAnimation/CMakeLists.txt b/src/osgPlugins/osgAnimation/CMakeLists.txt new file mode 100644 index 000000000..993bd6b7b --- /dev/null +++ b/src/osgPlugins/osgAnimation/CMakeLists.txt @@ -0,0 +1,9 @@ +SET(TARGET_SRC + ReaderWriter.cpp +) + +SET(TARGET_ADDED_LIBRARIES osgAnimation ) +#### end var setup ### +SETUP_PLUGIN(osganimation) + + diff --git a/src/osgPlugins/osgAnimation/ReaderWriter.cpp b/src/osgPlugins/osgAnimation/ReaderWriter.cpp new file mode 100644 index 000000000..d17a281aa --- /dev/null +++ b/src/osgPlugins/osgAnimation/ReaderWriter.cpp @@ -0,0 +1,491 @@ +/* -*-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 + +using namespace osgDB; +using namespace osg; + +bool Bone_readLocalData(Object& obj, Input& fr) +{ + osgAnimation::Bone& bone = dynamic_cast(obj); + + osg::Quat att; + bool iteratorAdvanced = false; + if (fr.matchSequence("bindQuaternion %f %f %f %f")) + { + fr[1].getFloat(att[0]); + fr[2].getFloat(att[1]); + fr[3].getFloat(att[2]); + fr[4].getFloat(att[3]); + + fr += 5; + iteratorAdvanced = true; + } + + osg::Vec3d pos(0,0,0); + if (fr.matchSequence("bindPosition %f %f %f")) + { + fr[1].getFloat(pos[0]); + fr[2].getFloat(pos[1]); + fr[3].getFloat(pos[2]); + + fr += 4; + iteratorAdvanced = true; + } + + osg::Vec3d scale(1,1,1); + if (fr.matchSequence("bindScale %f %f %f")) + { + fr[1].getFloat(scale[0]); + fr[2].getFloat(scale[1]); + fr[3].getFloat(scale[2]); + + fr += 4; + iteratorAdvanced = true; + } + + bone.setBindMatrixInBoneSpace( osg::Matrix(att) * osg::Matrix::translate(pos)); + if (bone.getUpdateCallback() && bone.getUpdateCallback()->getName().empty() && bone.getUpdateCallback()->getNestedCallback()) + bone.setUpdateCallback(bone.getUpdateCallback()->getNestedCallback()); // skip the default callback build in constructor of Bone + return iteratorAdvanced; +} + +bool Bone_writeLocalData(const Object& obj, Output& fw) +{ + const osgAnimation::Bone& bone = dynamic_cast(obj); + osg::Vec3 t; + osg::Quat r; + osg::Vec3 s; + osg::Quat rs; + bone.getBindMatrixInBoneSpace().decompose(t,r,s,rs); + fw.indent() << "bindQuaternion " << r << std::endl; + fw.indent() << "bindPosition " << t << std::endl; + fw.indent() << "bindScale " << s << std::endl; + return true; +} + +RegisterDotOsgWrapperProxy g_atkBoneProxy +( + new osgAnimation::Bone, + "osgAnimation::Bone", + "Object Node Transform osgAnimation::Bone Group", + &Bone_readLocalData, + &Bone_writeLocalData + ); + + + +bool Skeleton_readLocalData(Object& obj, Input& fr) +{ + return false; +} +bool Skeleton_writeLocalData(const Object& obj, Output& fr) +{ + return true; +} +RegisterDotOsgWrapperProxy g_atkRootSkeletonProxy +( + new osgAnimation::Skeleton, + "osgAnimation::Skeleton", + "Object Node Transform osgAnimation::Bone osgAnimation::Skeleton Group", + &Skeleton_readLocalData, + &Skeleton_writeLocalData, + DotOsgWrapper::READ_AND_WRITE + ); + + + + +bool Animation_readLocalData(Object& obj, Input& fr) +{ + osgAnimation::Animation& anim = dynamic_cast(obj); + bool iteratorAdvanced = false; + int nbChannels = 0; + if (fr.matchSequence("num_channels %i")) + { + fr[1].getInt(nbChannels); + fr += 2; + iteratorAdvanced = true; + } + + for (int i = 0; i < nbChannels; i++) + { + if (fr.matchSequence("Channel {")) + { + fr += 2; + + std::string name = "unknown"; + if (fr.matchSequence("name %s")) + { + name = fr[1].getStr(); + fr += 2; + iteratorAdvanced = true; + } + std::string target = "unknown"; + if (fr.matchSequence("target %s")) + { + target = fr[1].getStr(); + fr += 2; + iteratorAdvanced = true; + } + + std::string type; + int nbKeys; + if (fr.matchSequence("Keyframes %s %i {")) + { + type = fr[1].getStr(); + fr[2].getInt(nbKeys); + fr += 4; + iteratorAdvanced = true; + + osgAnimation::Channel* channel = 0; + if (type == "Quat") + { + osgAnimation::QuatSphericalLinearChannel* c = new osgAnimation::QuatSphericalLinearChannel; + c->getOrCreateSampler()->getOrCreateKeyframeContainer(); + channel = c; + } + 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++) + { + if (type == "Quat") + { + osg::Quat q; + float time; + 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; + osgAnimation::QuatSphericalLinearChannel* c = dynamic_cast(channel); + c->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::QuatKeyframe(time, q)); + iteratorAdvanced = true; + } + else if (type == "Vec3") + { + osg::Vec3 v; + float time; + 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; + osgAnimation::Vec3LinearChannel* c = dynamic_cast(channel); + c->getOrCreateSampler()->getOrCreateKeyframeContainer()->push_back(osgAnimation::Vec3Keyframe(time, v)); + iteratorAdvanced = true; + } + } + channel->setName(name); + channel->setTargetName(target); + anim.addChannel(channel); + } + if (fr.matchSequence("}")) // keyframes + fr += 1; + + if (fr.matchSequence("}")) // channel + fr += 1; + } + } + } + return iteratorAdvanced; +} + +bool Animation_writeLocalData(const Object& obj, Output& fw) +{ + const osgAnimation::Animation& anim = dynamic_cast(obj); + + 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; + + std::string type = "unknown"; + if (anim.getChannels()[i]->getName() == std::string("quaternion")) + { + type = "Quat"; + } + else if (anim.getChannels()[i]->getName() == std::string("rotation")) + { + type = "Quat"; + } + else if (anim.getChannels()[i]->getName() == std::string("euler")) + { + type = "Vec3"; + } + else if (anim.getChannels()[i]->getName() == std::string("scale")) + { + type = "Vec3"; + } + else if (anim.getChannels()[i]->getName() == std::string("position")) + { + type = "Vec3"; + } + + 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++) + { + 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; + } + } + fw.moveOut(); + fw.indent() << "}" << std::endl; + fw.moveOut(); + fw.indent() << "}" << std::endl; + } + return true; +} +RegisterDotOsgWrapperProxy g_atkAnimationProxy +( + new osgAnimation::Animation, + "osgAnimation::Animation", + "Object osgAnimation::Animation", + &Animation_readLocalData, + &Animation_writeLocalData + ); + + + +bool AnimationManager_readLocalData(Object& obj, Input& fr) +{ + osgAnimation::AnimationManager& manager = dynamic_cast(obj); + int nbAnims = 0; + bool iteratorAdvanced = false; + + if (fr.matchSequence("num_animations %i")) + { + fr[1].getInt(nbAnims); + fr += 2; + iteratorAdvanced = true; + } + + for (int i = 0; i < nbAnims; i++) + { + Object* o = fr.readObject(); + osgAnimation::Animation* a = dynamic_cast(o); + if (a) + { + manager.registerAnimation(a); + iteratorAdvanced = true; + } + else + osg::notify(osg::WARN)<<"Warning: can't read an animation object"<< std::endl; + } + + return iteratorAdvanced; +} + +bool AnimationManager_writeLocalData(const Object& obj, Output& fw) +{ + const osgAnimation::AnimationManager& manager = dynamic_cast(obj); + + osgAnimation::AnimationMap map = manager.getAnimationMap(); + int nbanims = map.size(); + fw.indent() << "num_animations " << nbanims << std::endl; + for (osgAnimation::AnimationMap::iterator it = map.begin(); it != map.end(); it++) + { + if (!fw.writeObject(*it->second)) + osg::notify(osg::WARN)<<"Warning: can't write an animation object"<< std::endl; + } + return true; +} + +RegisterDotOsgWrapperProxy g_atkAnimationManagerProxy +( + new osgAnimation::AnimationManager, + "osgAnimation::AnimationManager", + "Object Node Group osgAnimation::AnimationManager", + &AnimationManager_readLocalData, + &AnimationManager_writeLocalData, + DotOsgWrapper::READ_AND_WRITE + ); + + +bool RigGeometry_readLocalData(Object& obj, Input& fr) +{ + osgAnimation::RigGeometry& geom = dynamic_cast(obj); + osg::ref_ptr vmap = new osgAnimation::VertexInfluenceMap; + + int nbGroups = 0; + bool iteratorAdvanced = false; + if (fr.matchSequence("num_influences %i")) + { + fr[1].getInt(nbGroups); + fr += 2; + iteratorAdvanced = true; + } + + for (int i = 0; i < nbGroups; i++) + { + int nbVertexes = 0; + std::string name; + if (fr.matchSequence("osgAnimation::VertexInfluence %s %i {")) + { + name = fr[1].getStr(); + fr[2].getInt(nbVertexes); + fr += 4; + iteratorAdvanced = true; + } + + osgAnimation::VertexInfluence vi; + vi.setName(name); + vi.reserve(nbVertexes); + for (int j = 0; j < nbVertexes; j++) + { + int index = -1; + float weight = 1; + if (fr.matchSequence("%i %f")) + { + fr[0].getInt(index); + fr[1].getFloat(weight); + fr += 2; + iteratorAdvanced = true; + } + vi.push_back(osgAnimation::VertexIndexWeight(index, weight)); + } + if (fr.matchSequence("}")) + { + fr+=1; + } + (*vmap)[name] = vi; + } + if (!vmap->empty()) + geom.setInfluenceMap(vmap.get()); + + return iteratorAdvanced; +} + +bool RigGeometry_writeLocalData(const Object& obj, Output& fw) +{ + const osgAnimation::RigGeometry& geom = dynamic_cast(obj); + const osgAnimation::VertexInfluenceMap* vm = geom.getInfluenceMap(); + if (!vm) + return true; + fw.indent() << "num_influences " << vm->size() << std::endl; + fw.moveIn(); + for (osgAnimation::VertexInfluenceMap::const_iterator it = vm->begin(); it != vm->end(); it++) + { + std::string name = it->first; + if (name.empty()) + name = "Empty"; + fw.indent() << "osgAnimation::VertexInfluence \"" << name << "\" " << it->second.size() << " {" << std::endl; + fw.moveIn(); + const osgAnimation::VertexInfluence& vi = it->second; + for (osgAnimation::VertexInfluence::const_iterator itv = vi.begin(); itv != vi.end(); itv++) + { + fw.indent() << itv->first << " " << itv->second << std::endl; + } + fw.moveOut(); + fw.indent() << "}" << std::endl; + } + fw.moveOut(); + return true; +} + +RegisterDotOsgWrapperProxy g_atkRigGeometryProxy +( + new osgAnimation::RigGeometry, + "osgAnimation::RigGeometry", + "Object Drawable osgAnimation::RigGeometry Geometry", + &RigGeometry_readLocalData, + &RigGeometry_writeLocalData, + DotOsgWrapper::READ_AND_WRITE + ); + + + +bool UpdateBone_readLocalData(Object& obj, Input& fr) +{ + bool iteratorAdvanced = false; + return iteratorAdvanced; +} + +bool UpdateBone_writeLocalData(const Object& obj, Output& fw) +{ + return true; +} + +RegisterDotOsgWrapperProxy g_atkUpdateBoneProxy +( + new osgAnimation::Bone::UpdateBone, + "osgAnimation::UpdateBone", + "Object osgAnimation::UpdateBone", + &UpdateBone_readLocalData, + &UpdateBone_writeLocalData, + DotOsgWrapper::READ_AND_WRITE + ); + + + +bool UpdateTransform_readLocalData(Object& obj, Input& fr) +{ + bool iteratorAdvanced = false; + return iteratorAdvanced; +} + +bool UpdateTransform_writeLocalData(const Object& obj, Output& fw) +{ + return true; +} + +RegisterDotOsgWrapperProxy g_atkUpdateTransformProxy +( + new osgAnimation::UpdateTransform, + "osgAnimation::UpdateTransform", + "Object osgAnimation::UpdateTransform", + &UpdateTransform_readLocalData, + &UpdateTransform_writeLocalData, + DotOsgWrapper::READ_AND_WRITE + ); +