diff --git a/include/osgAnimation/Action b/include/osgAnimation/Action new file mode 100644 index 000000000..af235aa63 --- /dev/null +++ b/include/osgAnimation/Action @@ -0,0 +1,208 @@ +/* -*-c++-*- + * Copyright (C) 2009 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_ACTION_H +#define OSGANIMATION_ACTION_H + +#include +#include +#include +#include +#include + +#define META_Action(library,name) \ + virtual osg::Object* cloneType() const { return new name (); } \ + virtual osg::Object* clone(const osg::CopyOp& copyop) const { return new name (*this,copyop); } \ + virtual bool isSameKindAs(const osg::Object* obj) const { return dynamic_cast(obj)!=NULL; } \ + virtual const char* className() const { return #name; } \ + virtual const char* libraryName() const { return #library; } \ + virtual void accept(osgAnimation::ActionVisitor& nv) { nv.apply(*this); } \ + + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT Action : public osg::Object + { + public: + + class Callback : public osg::Object + { + public: + Callback(){} + Callback(const Callback& nc,const osg::CopyOp&) : + _nestedCallback(nc._nestedCallback) {} + + META_Object(osgAnimation,Callback); + + virtual void operator()(Action* action, osgAnimation::ActionVisitor* nv) {} + + Callback* getNestedCallback() { return _nestedCallback.get(); } + void addNestedCallback(Callback* callback) + { + if (_nestedCallback.valid()) + _nestedCallback->addNestedCallback(callback); + else + _nestedCallback = callback; + } + + protected: + osg::ref_ptr _nestedCallback; + }; + + + typedef std::map > FrameCallback; + + META_Action(osgAnimation, Action); + + Action(); + Action(const Action&,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(); + } + + Callback* getFrameCallback(unsigned int frame); + Callback* getFrameCallback(double time); + unsigned int getFramesPerSecond() const { return _fps; } + + 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 ); + virtual void traverse(ActionVisitor& visitor) {} + //virtual void evaluate(unsigned int frame); + + protected: + FrameCallback _framesCallback; + + double _speed; + unsigned int _fps; + unsigned int _numberFrame; + unsigned int _loop; + + enum Status + { + Play, + Stop + }; + + Status _state; + }; + + + + + // blend in from 0 to weight in duration + class BlendIn : public Action + { + double _weight; + osg::ref_ptr _animation; + + public: + META_Action(osgAnimation, BlendIn); + BlendIn() : _weight(0) {} + BlendIn(const BlendIn& a, const osg::CopyOp& c) : Action(a,c) { _weight = a._weight; _animation = a._animation;} + BlendIn(Animation* animation, double duration, double weight); + double getWeight() const { return _weight;} + Animation* getAnimation() { return _animation.get(); } + void computeWeight(unsigned int frame); + }; + + // blend in from 0 to weight in duration + class BlendOut : public Action + { + double _weight; + osg::ref_ptr _animation; + public: + META_Action(osgAnimation, BlendOut); + BlendOut() : _weight(0) {} + BlendOut(const BlendOut& a, const osg::CopyOp& c) : Action(a,c) { _weight = a._weight; _animation = a._animation;} + BlendOut(Animation* animation, double duration); + Animation* getAnimation() { return _animation.get(); } + double getWeight() const { return _weight;} + void computeWeight(unsigned int frame); + }; + + + class ActionAnimation : public Action + { + public: + META_Action(osgAnimation, ActionAnimation); + ActionAnimation() {} + ActionAnimation(const ActionAnimation& a, const osg::CopyOp& c) : Action(a,c) { _animation = a._animation;} + ActionAnimation(Animation* animation); + void updateAnimation(unsigned int frame); + Animation* getAnimation() { return _animation.get(); } + + protected: + osg::ref_ptr _animation; + }; + + + // encapsulate animation with blend in blend out for classic usage + class StripAnimation : public Action + { + public: + META_Action(osgAnimation, StripAnimation); + StripAnimation() {} + StripAnimation(const StripAnimation& a, const osg::CopyOp& c); + StripAnimation(Animation* animation, double blendInDuration = 0.0, double blendOutDuration = 0.0, double blendInWeightTarget = 1.0 ); + ActionAnimation* getActionAnimation() { return _animation.get(); } + BlendIn* getBlendIn() { return _blendIn.get(); } + BlendOut* getBlendOut() { return _blendOut.second.get(); } + const ActionAnimation* getActionAnimation() const { return _animation.get(); } + const BlendIn* getBlendIn() const { return _blendIn.get(); } + const BlendOut* getBlendOut() const { return _blendOut.second.get(); } + unsigned int getBlendOutStartFrame() const { return _blendOut.first; } + + unsigned int getLoop() const { return _animation->getLoop(); } + void setLoop(unsigned int loop); + void computeWeightAndUpdateAnimation(unsigned int frame); + + protected: + typedef std::pair > FrameBlendOut; + osg::ref_ptr _blendIn; + FrameBlendOut _blendOut; + osg::ref_ptr _animation; + }; + + + +} + +#endif diff --git a/include/osgAnimation/ActionCallback b/include/osgAnimation/ActionCallback new file mode 100644 index 000000000..1bb082e5f --- /dev/null +++ b/include/osgAnimation/ActionCallback @@ -0,0 +1,37 @@ +/* -*-c++-*- + * Copyright (C) 2009 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_ACTION_CALLBACK_H +#define OSGANIMATION_ACTION_CALLBACK_H + +#include + +namespace osgAnimation +{ + + /** Callback used to run new action on the timeline.*/ + class RunAction : public Action::Callback + { + public: + RunAction(Action* a) : _action(a) {} + virtual void operator()(Action* action, ActionVisitor* visitor); + + protected: + osg::ref_ptr _action; + + }; + +} + +#endif diff --git a/include/osgAnimation/ActionVisitor b/include/osgAnimation/ActionVisitor new file mode 100644 index 000000000..596e054c6 --- /dev/null +++ b/include/osgAnimation/ActionVisitor @@ -0,0 +1,110 @@ +/* -*-c++-*- + * Copyright (C) 2009 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_ACTIONVISITOR_H +#define OSGANIMATION_ACTIONVISITOR_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + class Timeline; + class Action; + class BlendIn; + class BlendOut; + class ActionAnimation; + class StripAnimation; + +#define META_ActionVisitor(library,name) \ + virtual const char* libraryName() const { return #library; }\ + virtual const char* className() const { return #name; } + + + class OSGANIMATION_EXPORT ActionVisitor : public osg::Referenced + { + public: + std::vector _stackFrameAction; + std::vector > _stackTimeline; + + META_ActionVisitor(osgAnimation, ActionVisitor); + void traverse(Action& visitor); + + void pushFrameActionOnStack(FrameAction& fa); + void popFrameAction(); + + void pushTimelineOnStack(Timeline* tm); + void popTimeline(); + + Timeline* getCurrentTimeline(); + + virtual void apply(Action& action); + virtual void apply(Timeline& tm); + virtual void apply(BlendIn& action); + virtual void apply(BlendOut& action); + virtual void apply(ActionAnimation& action); + virtual void apply(StripAnimation& action); + }; + + + class OSGANIMATION_EXPORT UpdateActionVisitor : public osgAnimation::ActionVisitor + { + protected: + unsigned int _frame; + + public: + META_ActionVisitor(osgAnimation, UpdateActionVisitor); + UpdateActionVisitor(); + void setFrame(unsigned int frame) { _frame = frame;} + + bool isActive() const; + unsigned int getLocalFrame() const; + + void apply(Timeline& action); + void apply(Action& action); + void apply(BlendIn& action); + void apply(BlendOut& action); + void apply(ActionAnimation& action); + void apply(StripAnimation& action); + + }; + + + class OSGANIMATION_EXPORT ClearActionVisitor : public osgAnimation::ActionVisitor + { + public: + enum ClearType { + BEFORE_FRAME, + AFTER_FRAME + }; + + META_ActionVisitor(osgAnimation, ClearActionVisitor); + ClearActionVisitor(ClearType type = BEFORE_FRAME); + void setFrame(unsigned int frame) { _frame = frame;} + + void apply(Timeline& action); + void apply(Action& action); + + protected: + unsigned int _frame; + std::vector > _remove; + ClearType _clearType; + }; + +} + +#endif diff --git a/include/osgAnimation/Bone b/include/osgAnimation/Bone index c006f6f23..2c120396a 100644 --- a/include/osgAnimation/Bone +++ b/include/osgAnimation/Bone @@ -1,5 +1,5 @@ /* -*-c++-*- - * Copyright (C) 2008 Cedric Pinson + * 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 @@ -110,7 +110,7 @@ namespace osgAnimation _scale = new osgAnimation::Vec3Target; } - void update(osgAnimation::Bone& bone) + void update(osgAnimation::Bone& bone) { bone.setTranslation(_position->getValue()); bone.setRotation(_quaternion->getValue()); @@ -126,25 +126,25 @@ namespace osgAnimation bool link(osgAnimation::Channel* channel) { - if (channel->getName().find("quaternion") != std::string::npos) + if (channel->getName().find("quaternion") != std::string::npos) { osgAnimation::QuatSphericalLinearChannel* qc = dynamic_cast(channel); - if (qc) + if (qc) { qc->setTarget(_quaternion.get()); return true; } } - else if (channel->getName().find("position") != std::string::npos) + else if (channel->getName().find("position") != std::string::npos) { osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); - if (vc) + if (vc) { vc->setTarget(_position.get()); return true; } - } - else if (channel->getName().find("scale") != std::string::npos) + } + else if (channel->getName().find("scale") != std::string::npos) { osgAnimation::Vec3LinearChannel* vc = dynamic_cast(channel); if (vc) @@ -152,7 +152,7 @@ namespace osgAnimation vc->setTarget(_scale.get()); return true; } - } + } else { std::cerr << "Channel " << channel->getName() << " does not contain a valid symbolic name for this class" << std::endl; diff --git a/include/osgAnimation/FrameAction b/include/osgAnimation/FrameAction new file mode 100644 index 000000000..ae3595354 --- /dev/null +++ b/include/osgAnimation/FrameAction @@ -0,0 +1,26 @@ +/* -*-c++-*- + * Copyright (C) 2009 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_FRAMEACTION_H +#define OSGANIMATION_FRAMEACTION_H + +#include +#include + +namespace osgAnimation +{ + class Action; + typedef std::pair > FrameAction; +} +#endif diff --git a/include/osgAnimation/RigGeometry b/include/osgAnimation/RigGeometry index d939d81c2..40bdb07a1 100644 --- a/include/osgAnimation/RigGeometry +++ b/include/osgAnimation/RigGeometry @@ -89,7 +89,7 @@ namespace osgAnimation RigGeometry* geom = dynamic_cast(drw); if (!geom) return; - if (!geom->getSkeleton() && !geom->getParents().empty()) + if (!geom->getSkeleton() && !geom->getParents().empty()) { FindNearestParentSkeleton finder; if (geom->getParents().size() > 1) diff --git a/include/osgAnimation/StatsHandler b/include/osgAnimation/StatsHandler new file mode 100644 index 000000000..a1a09d2c3 --- /dev/null +++ b/include/osgAnimation/StatsHandler @@ -0,0 +1,115 @@ +/* -*-c++-*- + * Copyright (C) 2009 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_STATSHANDLER_H +#define OSGANIMATION_STATSHANDLER_H + +#include +#include +#include +#include +#include + +namespace osgAnimation +{ +#if 0 + struct StatAction + { + + std::string _name; + osg::ref_ptr _group; + osg::ref_ptr _label; + osg::ref_ptr _graph; + osg::ref_ptr _textLabel; + + void init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float heigh, const osg::Vec4& color); + void setPosition(const osg::Vec3& pos); + void setAlpha(float v); + }; + +#endif + +/** Event handler for adding on screen stats reporting to Viewers.*/ + class OSGANIMATION_EXPORT StatsHandler : public osgGA::GUIEventHandler + { + public: + + StatsHandler(); + + enum StatsType + { + NO_STATS = 0, + FRAME_RATE = 1, + LAST = 2 + }; + + void setKeyEventTogglesOnScreenStats(int key) { _keyEventTogglesOnScreenStats = key; } + int getKeyEventTogglesOnScreenStats() const { return _keyEventTogglesOnScreenStats; } + + void setKeyEventPrintsOutStats(int key) { _keyEventPrintsOutStats = key; } + int getKeyEventPrintsOutStats() const { return _keyEventPrintsOutStats; } + + double getBlockMultiplier() const { return _blockMultiplier; } + + void reset(); + + osg::Camera* getCamera() { return _camera.get(); } + const osg::Camera* getCamera() const { return _camera.get(); } + + virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa); + + /** Get the keyboard and mouse usage of this manipulator.*/ + virtual void getUsage(osg::ApplicationUsage& usage) const; + + void updateGraph(osgAnimation::StatsActionVisitor* visitor); //, float width, float height, float ystart); + + protected: + + void setUpHUDCamera(osgViewer::ViewerBase* viewer); + + osg::Geometry* createBackgroundRectangle(const osg::Vec3& pos, const float width, const float height, osg::Vec4& color); + + osg::Geometry* createGeometry(const osg::Vec3& pos, float height, const osg::Vec4& colour, unsigned int numBlocks); + + osg::Geometry* createFrameMarkers(const osg::Vec3& pos, float height, const osg::Vec4& colour, unsigned int numBlocks); + + osg::Geometry* createTick(const osg::Vec3& pos, float height, const osg::Vec4& colour, unsigned int numTicks); + + osg::Node* createCameraTimeStats(const std::string& font, osg::Vec3& pos, float startBlocks, bool acquireGPUStats, float characterSize, osg::Stats* viewerStats, osg::Camera* camera); + + void setUpScene(osgViewer::Viewer* viewer); + + int _keyEventTogglesOnScreenStats; + int _keyEventPrintsOutStats; + + int _statsType; + + bool _initialized; + osg::ref_ptr _camera; + + osg::ref_ptr _switch; + osg::ref_ptr _group; + + unsigned int _frameRateChildNum; + unsigned int _numBlocks; + double _blockMultiplier; + + float _statsWidth; + float _statsHeight; + +// std::map _actions; + }; + +} +#endif diff --git a/include/osgAnimation/StatsVisitor b/include/osgAnimation/StatsVisitor new file mode 100644 index 000000000..fc09a4c82 --- /dev/null +++ b/include/osgAnimation/StatsVisitor @@ -0,0 +1,53 @@ +/* -*-c++-*- + * Copyright (C) 2009 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_STATSVISITOR_H +#define OSGANIMATION_STATSVISITOR_H + +#include +#include +#include +#include + +namespace osgAnimation +{ + + class OSGANIMATION_EXPORT StatsActionVisitor : public osgAnimation::UpdateActionVisitor + { + protected: + osg::ref_ptr _stats; + std::vector _channels; + + public: + META_ActionVisitor(osgAnimation, StatsActionVisitor); + + StatsActionVisitor(); + StatsActionVisitor(osg::Stats* stats, unsigned int frame); + void reset(); + const std::vector& getChannels() const { return _channels; } + osg::Stats* getStats() { return _stats; } + void setStats(osg::Stats* stats) { _stats = stats; } + void setFrame(unsigned int frame) { _frame = frame; } + void apply(Timeline& action); + void apply(Action& action); + void apply(BlendIn& action); + void apply(BlendOut& action); + void apply(ActionAnimation& action); + void apply(StripAnimation& action); + + }; + +} + +#endif diff --git a/include/osgAnimation/Target b/include/osgAnimation/Target index 85f1d92c1..b12e20737 100644 --- a/include/osgAnimation/Target +++ b/include/osgAnimation/Target @@ -1,5 +1,5 @@ /* -*-c++-*- - * Copyright (C) 2008 Cedric Pinson + * 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 @@ -67,7 +67,7 @@ namespace osgAnimation } const T& getValue() const { return _target;} - void normalize() + void normalize() { float weightSummed = getWeight(); if (fabs(weightSummed) < 1e-4 || fabs(weightSummed-1) < 1e-4) @@ -91,6 +91,7 @@ namespace osgAnimation TemplateTarget () {} TemplateTarget (const osg::Quat& q) { setValue(q); } + const osg::Quat& getValue() const { return _target;} void update(float weight, const osg::Quat& val) { diff --git a/include/osgAnimation/Timeline b/include/osgAnimation/Timeline index c7c1847f1..09ff005d3 100644 --- a/include/osgAnimation/Timeline +++ b/include/osgAnimation/Timeline @@ -1,5 +1,5 @@ /* -*-c++-*- - * Copyright (C) 2008 Cedric Pinson + * 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 @@ -16,161 +16,27 @@ #define OSGANIMATION_TIMELINE_H #include -#include #include #include #include -#include -#include +#include +#include +#include #include namespace osgAnimation { + class StatsActionVisitor; - class Action : public osg::Object + class OSGANIMATION_EXPORT Timeline : public Action //osg::Object { public: - class Callback : public osg::Object - { - public: - Callback(){} - Callback(const Callback&,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&,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 ActionStatus - { - Play, - Stop - }; - - ActionStatus _state; - }; - - - class OSGANIMATION_EXPORT Timeline : public osg::Object - { - public: - - META_Object(osgAnimation, Timeline); - Timeline(); Timeline(const Timeline& nc,const osg::CopyOp& op = osg::CopyOp::SHALLOW_COPY); + META_Action(osgAnimation, Timeline); + enum TimelineStatus { Play, @@ -179,14 +45,10 @@ namespace osgAnimation TimelineStatus getStatus() const { return _state; } - typedef std::pair > FrameAction; typedef std::vector ActionList; typedef std::map ActionLayers; - const ActionList& getActionLayer(int i) - { - return _actions[i]; - } + const ActionList& getActionLayer(int i) { return _actions[i];} unsigned int getCurrentFrame() const { return _currentFrame;} double getCurrentTime() const { return _currentFrame * 1.0 / _fps;} @@ -195,139 +57,44 @@ namespace osgAnimation 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; - } + bool isActive(Action* activeAction); - void removeAction(Action* action) - { - if (getEvaluating()) - _removeActionOperations.push_back(FrameAction(0, action)); - else - internalRemoveAction(action); - } + void removeAction(Action* action); + virtual void addActionAt(unsigned int frame, Action* action, int priority = 0); + virtual void addActionAt(double t, Action* action, int priority = 0); + void addActionNow(Action* action, int priority = 0); - 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); - } + void clearActions(); - virtual void evaluate(unsigned int frame) - { - setEvaluating(true); - osg::notify(osg::DEBUG_INFO) << getName() << " evaluate frame " << _currentFrame << std::endl; + virtual void update(double simulationTime); + void setLastFrameEvaluated(unsigned int frame) { _previousFrameEvaluated = 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->evaluate(frame - firstFrame); - } - } - setEvaluating(false); + void setEvaluating(bool state) { _evaluating = state;} + void traverse(ActionVisitor& visitor); - // evaluate callback after updating all animation - evaluateCallback(frame); - _previousFrameEvaluated = frame; - } + void setStats(osg::Stats* stats); + osg::Stats* getStats(); + void collectStats(bool state); + osgAnimation::StatsActionVisitor* getStatsVisitor(); - 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(); - } + const ActionLayers& getActionLayers() const { return _actions; } - 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; - } - } + void processPendingOperation(); protected: - ActionLayers _actions; double _lastUpdate; double _speed; unsigned int _currentFrame; - unsigned int _fps; - unsigned int _numberFrame; unsigned int _previousFrameEvaluated; - bool _loop; bool _initFirstFrame; - TimelineStatus _state; + bool _collectStats; + osg::ref_ptr _stats; + osg::ref_ptr _statsVisitor; + // to manage pending operation bool _evaluating; @@ -343,199 +110,13 @@ namespace osgAnimation 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.get()); - _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); - } + void internalRemoveAction(Action* action); + void internalAddAction(int priority, const FrameAction& ftl); }; - // 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 - } - }; - - - } #endif diff --git a/src/osgAnimation/Action.cpp b/src/osgAnimation/Action.cpp new file mode 100644 index 000000000..37402a70a --- /dev/null +++ b/src/osgAnimation/Action.cpp @@ -0,0 +1,201 @@ +/* -*-c++-*- + * Copyright (C) 2009 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 + +osgAnimation::Action::Action() +{ + _numberFrame = 25; + _fps = 25; + _speed = 1.0; + _loop = 1; +} +osgAnimation::Action::Action(const Action&,const osg::CopyOp&) {} +osgAnimation::Action::Callback* osgAnimation::Action::getFrameCallback(unsigned int frame) +{ + if (_framesCallback.find(frame) != _framesCallback.end()) + { + return _framesCallback[frame]; + } + return 0; +} + +osgAnimation::Action::Callback* osgAnimation::Action::getFrameCallback(double time) +{ + unsigned int frame = static_cast(floor(time * _fps)); + return getFrameCallback(frame); +} + +bool osgAnimation::Action::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; +} + +#if 0 +void osgAnimation::Action::evaluate(unsigned int frame) +{ + unsigned int frameInAction; + unsigned int loopDone; + bool result = evaluateFrame(frame, frameInAction, loopDone); + if (!result) + { + osg::notify(osg::DEBUG_INFO) << getName() << " Action frame " << frameInAction << " finished" << std::endl; + return; + } + osg::notify(osg::DEBUG_INFO) << getName() << " Action frame " << frame << " relative to loop " << frameInAction << " no loop " << loopDone<< std::endl; + + frame = frameInAction; + if (_framesCallback.find(frame) != _framesCallback.end()) + { + osg::notify(osg::DEBUG_INFO) << getName() << " evaluate callback " << _framesCallback[frame]->getName() << " at " << frame << std::endl; + (*_framesCallback[frame])(this, visitor); + } +} +#endif + + +osgAnimation::BlendIn::BlendIn(Animation* animation, double duration, double weight) +{ + _animation = animation; + _weight = weight; + float d = duration * _fps; + setNumFrames(static_cast(floor(d)) + 1); + setName("BlendIn"); +} + +void osgAnimation::BlendIn::computeWeight(unsigned int 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 * ratio; + _animation->setWeight(w); +} + +#if 0 +void osgAnimation::BlendIn::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); +} +#endif + + +osgAnimation::BlendOut::BlendOut(Animation* animation, double duration) +{ + _animation = animation; + float d = duration * _fps; + setNumFrames(static_cast(floor(d) + 1)); + _weight = 1.0; + setName("BlendOut"); +} + +void osgAnimation::BlendOut::computeWeight(unsigned int frame) +{ + double ratio = ( (frame+1) * 1.0 / (getNumFrames()) ); + double w = _weight * (1.0-ratio); + _animation->setWeight(w); +} + +#if 0 +void osgAnimation::BlendOut::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); +} +#endif + + +osgAnimation::ActionAnimation::ActionAnimation(Animation* animation) : _animation(animation) +{ + Action::setDuration(animation->getDuration()); + setName(animation->getName()); +} +void osgAnimation::ActionAnimation::updateAnimation(unsigned int frame) +{ + _animation->update(frame * 1.0/_fps); +} + + + + + +osgAnimation::StripAnimation::StripAnimation(const StripAnimation& a, const osg::CopyOp& c) : Action(a,c) +{ + _animation = a._animation; + _blendIn = a._blendIn; + _blendOut = a._blendOut; +} + +osgAnimation::StripAnimation::StripAnimation(Animation* animation, double blendInDuration, double blendOutDuration, double blendInWeightTarget) +{ + _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()); +} + + +void osgAnimation::StripAnimation::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); +} + +void osgAnimation::StripAnimation::computeWeightAndUpdateAnimation(unsigned int frame) +{ + if (frame < _blendIn->getNumFrames()) + _blendIn->computeWeight(frame); + if (frame >= _blendOut.first) + _blendOut.second->computeWeight(frame - _blendOut.first); + _animation->updateAnimation(frame); +} diff --git a/src/osgAnimation/ActionCallback.cpp b/src/osgAnimation/ActionCallback.cpp new file mode 100644 index 000000000..611c224b7 --- /dev/null +++ b/src/osgAnimation/ActionCallback.cpp @@ -0,0 +1,23 @@ +/* -*-c++-*- + * Copyright (C) 2009 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 + +void osgAnimation::RunAction::operator()(Action* action, ActionVisitor* visitor) +{ + Timeline* tm = visitor->getCurrentTimeline(); + tm->addActionNow(_action.get()); +} + diff --git a/src/osgAnimation/ActionVisitor.cpp b/src/osgAnimation/ActionVisitor.cpp new file mode 100644 index 000000000..acd2d7137 --- /dev/null +++ b/src/osgAnimation/ActionVisitor.cpp @@ -0,0 +1,164 @@ +/* -*-c++-*- + * Copyright (C) 2009 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 + +void osgAnimation::ActionVisitor::pushFrameActionOnStack(FrameAction& fa) { _stackFrameAction.push_back(fa); } +void osgAnimation::ActionVisitor::popFrameAction() { _stackFrameAction.pop_back(); } +void osgAnimation::ActionVisitor::pushTimelineOnStack(Timeline* tm) { _stackTimeline.push_back(tm); } +void osgAnimation::ActionVisitor::popTimeline() { _stackTimeline.pop_back(); } +void osgAnimation::ActionVisitor::apply(Action& action) { traverse(action); } +void osgAnimation::ActionVisitor::apply(Timeline& tm) { tm.traverse(*this); } +void osgAnimation::ActionVisitor::apply(BlendIn& action) { apply(static_cast(action));} +void osgAnimation::ActionVisitor::apply(BlendOut& action) { apply(static_cast(action)); } +void osgAnimation::ActionVisitor::apply(ActionAnimation& action) { apply(static_cast(action)); } +void osgAnimation::ActionVisitor::apply(StripAnimation& action) { apply(static_cast(action)); } +void osgAnimation::ActionVisitor::traverse(Action& action) +{ + action.traverse(*this); +} + +osgAnimation::Timeline* osgAnimation::ActionVisitor::getCurrentTimeline() +{ + if (_stackTimeline.empty()) + return 0; + return _stackTimeline.back().get(); +} + +osgAnimation::UpdateActionVisitor::UpdateActionVisitor() { _frame = 0; } + + +void osgAnimation::UpdateActionVisitor::apply(Timeline& tm) +{ + tm.setEvaluating(true); + + tm.traverse(*this); + + tm.setEvaluating(false); + + tm.setLastFrameEvaluated(_frame); +} + +bool osgAnimation::UpdateActionVisitor::isActive() const +{ + FrameAction fa = _stackFrameAction.back(); + if (_frame < fa.first) + return false; + if (!fa.second.valid()) + return false; + if (getLocalFrame() >= fa.second->getNumFrames()) + return false; + return true; +} +unsigned int osgAnimation::UpdateActionVisitor::getLocalFrame() const +{ + return _frame - _stackFrameAction.back().first; +} + +void osgAnimation::UpdateActionVisitor::apply(Action& action) +{ + if (isActive()) + { + unsigned int frame = getLocalFrame(); + + unsigned int frameInAction; + unsigned int loopDone; + bool result = action.evaluateFrame(frame, frameInAction, loopDone); + if (!result) + { + osg::notify(osg::DEBUG_INFO) << action.getName() << " Action frame " << frameInAction << " finished" << std::endl; + return; + } + osg::notify(osg::DEBUG_INFO) << action.getName() << " Action frame " << frame << " relative to loop " << frameInAction << " no loop " << loopDone<< std::endl; + + frame = frameInAction; + Action::Callback* cb = action.getFrameCallback(frame); + while (cb) + { + osg::notify(osg::DEBUG_INFO) << action.getName() << " evaluate callback " << cb->getName() << " at " << frame << std::endl; + (*cb)(&action, this); + cb = cb->getNestedCallback(); + } + } +} + +void osgAnimation::UpdateActionVisitor::apply(BlendIn& action) +{ + if (isActive()) + { + unsigned int frame = getLocalFrame(); + apply(static_cast(action)); + action.computeWeight(frame); + } +} + +void osgAnimation::UpdateActionVisitor::apply(BlendOut& action) +{ + if (isActive()) + { + unsigned int frame = getLocalFrame(); + apply(static_cast(action)); + action.computeWeight(frame); + } +} + +void osgAnimation::UpdateActionVisitor::apply(ActionAnimation& action) +{ + if (isActive()) + { + unsigned int frame = getLocalFrame(); + apply(static_cast(action)); + action.updateAnimation(frame); + } +} + +void osgAnimation::UpdateActionVisitor::apply(StripAnimation& action) +{ + if (isActive()) + { + unsigned int frame = getLocalFrame(); + apply(static_cast(action)); + action.computeWeightAndUpdateAnimation(frame); + } +} + + + +osgAnimation::ClearActionVisitor::ClearActionVisitor(ClearType type) : _clearType(type) +{ +} + +void osgAnimation::ClearActionVisitor::apply(Timeline& tm) +{ + _remove.clear(); + tm.traverse(*this); + for (int i = 0; i < (int)_remove.size(); i++) + tm.removeAction(_remove[i]); +} +void osgAnimation::ClearActionVisitor::apply(Action& action) +{ + FrameAction fa = _stackFrameAction.back(); + switch( _clearType) { + case BEFORE_FRAME: + if (_frame > fa.first) + _remove.push_back(&action); + break; + case AFTER_FRAME: + if (_frame - fa.first > action.getNumFrames()) + _remove.push_back(&action); + break; + } +} diff --git a/src/osgAnimation/Animation.cpp b/src/osgAnimation/Animation.cpp index 5ac0c9ea4..f513492eb 100644 --- a/src/osgAnimation/Animation.cpp +++ b/src/osgAnimation/Animation.cpp @@ -131,7 +131,7 @@ bool Animation::update (float time) // std::cout << "t " << t << " / " << _duration << std::endl; ChannelList::const_iterator chan; - for( chan=_channels.begin(); chan!=_channels.end(); ++chan) + for( chan=_channels.begin(); chan!=_channels.end(); ++chan) { (*chan)->setWeight(_weight); (*chan)->update(t); diff --git a/src/osgAnimation/CMakeLists.txt b/src/osgAnimation/CMakeLists.txt index 65d496b33..4db8c3101 100644 --- a/src/osgAnimation/CMakeLists.txt +++ b/src/osgAnimation/CMakeLists.txt @@ -10,6 +10,9 @@ SET(LIB_NAME osgAnimation) SET(HEADER_PATH ${OpenSceneGraph_SOURCE_DIR}/include/${LIB_NAME}) SET(LIB_PUBLIC_HEADERS + ${HEADER_PATH}/Action + ${HEADER_PATH}/ActionCallback + ${HEADER_PATH}/ActionVisitor ${HEADER_PATH}/Animation ${HEADER_PATH}/AnimationManagerBase ${HEADER_PATH}/Assert @@ -19,6 +22,7 @@ SET(LIB_PUBLIC_HEADERS ${HEADER_PATH}/CubicBezier ${HEADER_PATH}/EaseMotion ${HEADER_PATH}/Export + ${HEADER_PATH}/FrameAction ${HEADER_PATH}/Interpolator ${HEADER_PATH}/Keyframe ${HEADER_PATH}/LinkVisitor @@ -27,6 +31,8 @@ SET(LIB_PUBLIC_HEADERS ${HEADER_PATH}/Sampler ${HEADER_PATH}/Skeleton ${HEADER_PATH}/Skinning + ${HEADER_PATH}/StatsVisitor + ${HEADER_PATH}/StatsHandler ${HEADER_PATH}/Target ${HEADER_PATH}/Timeline ${HEADER_PATH}/TimelineAnimationManager @@ -39,6 +45,9 @@ SET(LIB_PUBLIC_HEADERS ADD_LIBRARY(${LIB_NAME} ${OPENSCENEGRAPH_USER_DEFINED_DYNAMIC_OR_STATIC} ${LIB_PUBLIC_HEADERS} + Action.cpp + ActionCallback.cpp + ActionVisitor.cpp Animation.cpp AnimationManagerBase.cpp AnimationManager.cpp @@ -48,6 +57,8 @@ ADD_LIBRARY(${LIB_NAME} MorphGeometry.cpp RigGeometry.cpp Skeleton.cpp + StatsVisitor.cpp + StatsHandler.cpp Target.cpp TimelineAnimationManager.cpp Timeline.cpp diff --git a/src/osgAnimation/StatsHandler.cpp b/src/osgAnimation/StatsHandler.cpp new file mode 100644 index 000000000..c7ffd80b4 --- /dev/null +++ b/src/osgAnimation/StatsHandler.cpp @@ -0,0 +1,721 @@ +/* -*-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. +*/ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static unsigned int getRandomValueinRange(unsigned int v) +{ + return static_cast((rand() * 1.0 * v)/(RAND_MAX-1)); +} + + +namespace osgAnimation +{ + + +osg::Geometry* createBackgroundRectangle(const osg::Vec3& pos, const float width, const float height, osg::Vec4& color) +{ + osg::StateSet *ss = new osg::StateSet; + osg::Geometry* geometry = new osg::Geometry; + + geometry->setUseDisplayList(false); + geometry->setStateSet(ss); + + osg::Vec3Array* vertices = new osg::Vec3Array; + geometry->setVertexArray(vertices); + + vertices->push_back(osg::Vec3(pos.x(), pos.y(), 0)); + vertices->push_back(osg::Vec3(pos.x(), pos.y()-height,0)); + vertices->push_back(osg::Vec3(pos.x()+width, pos.y()-height,0)); + vertices->push_back(osg::Vec3(pos.x()+width, pos.y(),0)); + + osg::Vec4Array* colors = new osg::Vec4Array; + colors->push_back(color); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_OVERALL); + + osg::DrawElementsUInt *base = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS,0); + base->push_back(0); + base->push_back(1); + base->push_back(2); + base->push_back(3); + + geometry->addPrimitiveSet(base); + + return geometry; +} + +struct StatsGraph : public osg::MatrixTransform +{ + StatsGraph(osg::Vec3 pos, float width, float height) + : _pos(pos), _width(width), _height(height), + _statsGraphGeode(new osg::Geode) + { + _pos = pos - osg::Vec3(0, _height, 0.1); + setMatrix(osg::Matrix::translate(_pos)); + setDataVariance(osg::Object::DYNAMIC); + addChild(_statsGraphGeode.get()); + _statsGraphGeode->setCullingActive(false); + } + + void changeYposition(float y) + { + osg::Vec3 _pos = getMatrix().getTrans(); + _pos[1] = y - _height; + setMatrix(osg::Matrix::translate(_pos)); + } + + void addStatGraph(osg::Stats* viewerStats, osg::Stats* stats, const osg::Vec4& color, float max, const std::string& nameBegin, const std::string& nameEnd = "") + { + _statsGraphGeode->addDrawable(new Graph(_width, _height, viewerStats, stats, color, max, nameBegin, nameEnd)); + } + + osg::Vec3 _pos; + float _width; + float _height; + + osg::ref_ptr _statsGraphGeode; + + struct NeverCull : public osg::Drawable::CullCallback + { + NeverCull() {} + bool cull(osg::NodeVisitor* nv, osg::Drawable* drawable, osg::RenderInfo* renderInfo) const { return false;} + }; + + + struct Graph : public osg::Geometry + { + Graph(float width, float height, osg::Stats* viewerStats, osg::Stats* stats, + const osg::Vec4& color, float max, const std::string& nameBegin, const std::string& nameEnd = "") + { + setDataVariance(osg::Object::DYNAMIC); + setUseDisplayList(false); + + setVertexArray(new osg::Vec3Array); + getVertexArray()->setDataVariance(osg::Object::DYNAMIC); + setColor(color); + + setUpdateCallback(new GraphUpdateCallback(width, height, viewerStats, stats, max, nameBegin, nameEnd)); + setCullCallback(new NeverCull); + } + + void setColor(const osg::Vec4& color) { + osg::Vec4Array* colors = new osg::Vec4Array; + colors->push_back(color); + setColorArray(colors); + setColorBinding(osg::Geometry::BIND_OVERALL); + } + }; + + + struct GraphUpdateCallback : public osg::Drawable::UpdateCallback + { + + const unsigned int _width; + const unsigned int _height; + mutable unsigned int _curX; + osg::Stats* _viewerStats; + osg::Stats* _stats; + const float _max; + const std::string _nameBegin; + const std::string _nameEnd; + mutable int _frameNumber; + + GraphUpdateCallback(float width, float height, osg::Stats* viewerStats, osg::Stats* stats, + float max, const std::string& nameBegin, const std::string& nameEnd = "") + : _width((unsigned int)width), _height((unsigned int)height), _curX(0), + _viewerStats(viewerStats), _stats(stats), _max(max), _nameBegin(nameBegin), _nameEnd(nameEnd), _frameNumber(0) + { + } + virtual void update(osg::NodeVisitor* nv, osg::Drawable* drawable) + { + if (nv->getVisitorType() != osg::NodeVisitor::UPDATE_VISITOR) + return; + + osg::Geometry* geometry = const_cast(drawable->asGeometry()); + if (!geometry) return; + osg::Vec3Array* vertices = dynamic_cast(geometry->getVertexArray()); + if (!vertices) return; + + int frameNumber = nv->getFrameStamp()->getFrameNumber(); + if (frameNumber == _frameNumber) + return; + + + // Get stats + double value; + if (_nameEnd.empty()) + { + if (!_stats->getAttribute(_stats->getLatestFrameNumber(), _nameBegin, value )) + { + value = 0.0; + } + } + else + { + double beginValue, endValue; + if (_stats->getAttribute( frameNumber, _nameBegin, beginValue) && + _stats->getAttribute( frameNumber, _nameEnd, endValue) ) + { + value = endValue - beginValue; + } + else + { + value = 0.0; + } + } + // Add new vertex for this frame. + value = osg::clampTo(value, 0.0, double(_max)); + + if (!vertices->size()) { + for (int i = 0; i < (int)_width; i++) + vertices->push_back(osg::Vec3(float(_curX++), 0, 0)); + // Create primitive set if none exists. + if (geometry->getNumPrimitiveSets() == 0) + geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, 0)); + osg::DrawArrays* drawArrays = dynamic_cast(geometry->getPrimitiveSet(0)); + drawArrays->setFirst(0); + drawArrays->setCount(vertices->size()); + } + vertices->push_back(osg::Vec3(float(_curX), float(_height) / _max * value, 0)); + + unsigned int excedent = vertices->size() - _width; + vertices->erase(vertices->begin(), vertices->begin() + excedent); + + // Make the graph scroll when there is enough data. + // Note: We check the frame number so that even if we have + // many graphs, the transform is translated only once per + // frame. + static const float increment = -1.0; + if (_frameNumber != frameNumber) + { + // We know the exact layout of this part of the scene + // graph, so this is OK... + osg::MatrixTransform* transform = + geometry->getParent(0)->getParent(0)->asTransform()->asMatrixTransform(); + if (transform) + { + transform->setMatrix(transform->getMatrix() * osg::Matrix::translate(osg::Vec3(increment, 0, 0))); + } + } + + _curX++; + _frameNumber = frameNumber; + + geometry->dirtyBound(); + } + }; +}; + +// Drawcallback to draw averaged attribute +struct ValueTextDrawCallback : public virtual osg::Drawable::DrawCallback +{ + ValueTextDrawCallback(osg::Stats* stats, const std::string& name): + _stats(stats), + _attributeName(name), + _frameNumber(0) + { + } + + /** do customized draw code.*/ + virtual void drawImplementation(osg::RenderInfo& renderInfo,const osg::Drawable* drawable) const + { + osgText::Text* text = (osgText::Text*)drawable; + + int frameNumber = renderInfo.getState()->getFrameStamp()->getFrameNumber(); + if (frameNumber == _frameNumber) { + text->drawImplementation(renderInfo); + return; + } + + double value; + if (_stats->getAttribute(_stats->getLatestFrameNumber(), _attributeName, value)) + { + sprintf(_tmpText,"%4.2f",value); + text->setText(_tmpText); + } + else + { + text->setText(""); + } + _frameNumber = frameNumber; + text->drawImplementation(renderInfo); + } + + osg::ref_ptr _stats; + std::string _attributeName; + mutable char _tmpText[128]; + mutable int _frameNumber; +}; + + + + + + struct StatAction + { + double _lastTime; + std::string _name; + osg::ref_ptr _group; + osg::ref_ptr _label; + osg::ref_ptr _graph; + osg::ref_ptr _textLabel; + osgAnimation::OutCubicMotion _fade; + + StatAction() { _lastTime = 0; _fade = osgAnimation::OutCubicMotion(0,5); } + void init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float heigh, const osg::Vec4& color); + void setPosition(const osg::Vec3& pos); +#if 0 + void touch() + { + _lastTime = osg::Timer::instance()->time_s(); + float a = 1.0 - _fade.getValueAt(0.0); + setAlpha(a); + } + bool update() { + double t = osg::Timer::instance()->time_s(); + float alpha = 1.0 - _fade.getValueAt(t-_lastTime); + if (t - _lastTime > _fade.getDuration()) + return true; + setAlpha(alpha); + return false; + } +#endif + void setAlpha(float v); + }; + + + struct StatsTimeline : public osg::NodeCallback + { + static float _statsHeight; + static float _statsWidth; + + osg::ref_ptr _background; + osg::ref_ptr _timeline; + osg::ref_ptr _group; + std::map _actions; + + StatsTimeline() + { + _statsHeight = 1024; + _statsWidth = 1280; + } + osg::MatrixTransform* createStatsForTimeline(osgAnimation::Timeline* timeline) + { + _timeline = timeline; + + std::string font("fonts/arial.ttf"); + + float leftPos = 10.0f; + float startBlocks = 150.0f; + float characterSize = 20.0f; + + + osg::Vec4 backgroundColor(0.0, 0.0, 0.0f, 0.3); + float backgroundMargin = 5; + float backgroundSpacing = 3; + + osg::Vec4 color(1.0, 1.0, 1.0, 1.0); + + _group = new osg::MatrixTransform; + _group->setDataVariance(osg::Object::DYNAMIC); + + { + osg::Vec3 pos(leftPos, _statsHeight-24.0f,0.0f); + float topOfViewerStats = pos.y() + characterSize; + osg::ref_ptr stats = _timeline->getStats(); + pos.y() -= characterSize + backgroundMargin; + + { + osg::Geode* geode = new osg::Geode(); + _group->addChild(geode); + osg::ref_ptr timeLabel = new osgText::Text; + geode->addDrawable( timeLabel.get() ); + + timeLabel->setColor(color); + timeLabel->setFont(font); + timeLabel->setCharacterSize(characterSize); + timeLabel->setPosition(pos); + timeLabel->setText("Time: "); + + osg::ref_ptr timeLabelValue = new osgText::Text; + geode->addDrawable( timeLabelValue.get() ); + + timeLabelValue->setColor(color); + timeLabelValue->setFont(font); + timeLabelValue->setCharacterSize(characterSize); + timeLabelValue->setPosition(pos + osg::Vec3(startBlocks, 0,0)); + timeLabelValue->setText("0.0"); + + timeLabelValue->setDrawCallback(new ValueTextDrawCallback(stats,"Timeline")); + } + } + { + osg::Vec3 pos(leftPos, _statsHeight - 24.0f ,0.0f); + float topOfViewerStats = pos.y(); + osg::Geode* geode = new osg::Geode; + _background = createBackgroundRectangle( + pos + osg::Vec3(-backgroundMargin, backgroundMargin, 0), + _statsWidth - 2 * backgroundMargin, + (3 + 4.5 * 1) * characterSize + 2 * backgroundMargin, + backgroundColor); + geode->addDrawable(_background); + _group->addChild(geode); + } + + return _group; + } + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + if (nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR) { + updateGraph(); + } + traverse(node,nv); + } + + void updateGraph() + { + osgAnimation::StatsActionVisitor* visitor = _timeline->getStatsVisitor(); + if (!visitor) + return; + + std::string font("fonts/arial.ttf"); + float leftPos = 10.0f; + float characterSize = 20.0f; + + float backgroundMargin = 5; + float backgroundSpacing = 3; + float graphSpacing = 5; + + float width = _statsWidth - 4 * backgroundMargin; + float height = characterSize; + osg::Vec3 pos(leftPos, _statsHeight-24.0f,0.0f); + pos.y() -= characterSize *2 + backgroundMargin; + + for (std::map::iterator it = _actions.begin(); it != _actions.end(); it++) { + //if ((*it).second.update()) + (*it).second._group->setNodeMask(~1); + } + + const std::vector& channels = visitor->getChannels(); + std::map size; + for (int i = 0; i < (int)channels.size(); i++) { + std::string name = channels[i]; + if (_actions.find(name) == _actions.end()) { + osg::Vec4 color(getRandomValueinRange(255)/255.0, getRandomValueinRange(255)/255.0, getRandomValueinRange(255)/255.0, 1.0); + _actions[name].init(visitor->getStats(), name, pos, width, height, color); + _group->addChild(_actions[name]._group); + //_actions[name].touch(); + } else { + _actions[name].setPosition(pos); + //_actions[name].touch(); + } + _actions[name]._group->setNodeMask(-1); + size[name] = 0; + pos.y() -= characterSize + graphSpacing; + } + + pos.y() -= backgroundMargin; + osg::Vec3Array* array = dynamic_cast(_background->getVertexArray()); + float y = (*array)[0][1]; + y = y - (pos.y() + backgroundMargin); //(2 * backgroundMargin + (size.size() * (characterSize + graphSpacing))); + (*array)[1][1] = pos.y(); + (*array)[2][1] = pos.y(); + array->dirty(); + _background->dirtyBound(); + } + }; + float StatsTimeline::_statsHeight; + float StatsTimeline::_statsWidth; + + + +struct FindTimelineStats : public osg::NodeVisitor +{ + std::vector > _timelines; + + FindTimelineStats() : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {} + + void apply(osg::Node& node) { + osg::NodeCallback* cb = node.getUpdateCallback(); + while (cb) { + osgAnimation::TimelineAnimationManager* tam = dynamic_cast(cb); + if (tam) + _timelines.push_back(tam->getTimeline()); + cb = cb->getNestedCallback(); + } + traverse(node); + } +}; + + +StatsHandler::StatsHandler(): + _keyEventTogglesOnScreenStats('a'), + _keyEventPrintsOutStats('A'), + _statsType(NO_STATS), + _initialized(false), + _statsWidth(1280.0f), + _statsHeight(1024.0f) +{ + _camera = new osg::Camera; + _camera->setRenderer(new osgViewer::Renderer(_camera.get())); + _camera->setProjectionResizePolicy(osg::Camera::FIXED); +} + +bool StatsHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa) +{ + osgViewer::View* myview = dynamic_cast(&aa); + if (!myview) return false; + + osgViewer::Viewer* viewer = dynamic_cast(myview->getViewerBase()); + + if (!viewer->getSceneData()) + return false; + if (ea.getHandled()) return false; + + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::KEYDOWN): + { + if (ea.getKey()==_keyEventTogglesOnScreenStats) + { + if (viewer->getViewerStats()) + { + + if (!_switch.get()) { + FindTimelineStats finder; + viewer->getSceneData()->accept(finder); + if (finder._timelines.empty()) + return false; + } + + if (!_initialized) + { + setUpHUDCamera(viewer); + setUpScene(dynamic_cast(viewer)); + } + + ++_statsType; + + if (_statsType==LAST) _statsType = NO_STATS; + + + switch(_statsType) + { + case(NO_STATS): + { + _camera->setNodeMask(0x0); + _switch->setAllChildrenOff(); + break; + } + case(FRAME_RATE): + { + _camera->setNodeMask(0xffffffff); + _switch->setAllChildrenOn(); + break; + } + default: + break; + } + + + } + return true; + } + if (ea.getKey()==_keyEventPrintsOutStats) + { + FindTimelineStats finder; + viewer->getSceneData()->accept(finder); + if (!finder._timelines.empty()) { + osg::notify(osg::NOTICE)< StatsList; + StatsList statsList; + + for (int i = 0; i < (int)finder._timelines.size(); i++) + statsList.push_back(finder._timelines[0]->getStats()); + + for(int i = statsList[0]->getEarliestFrameNumber(); i<= statsList[0]->getLatestFrameNumber()-1; ++i) + { + for(StatsList::iterator itr = statsList.begin(); + itr != statsList.end(); + ++itr) + { + if (itr==statsList.begin()) (*itr)->report(osg::notify(osg::NOTICE), i); + else (*itr)->report(osg::notify(osg::NOTICE), i, " "); + } + osg::notify(osg::NOTICE)<setGraphicsContext(0); + _camera->removeChildren( 0, _camera->getNumChildren() ); +} + +void StatsHandler::setUpHUDCamera(osgViewer::ViewerBase* viewer) +{ + osgViewer::GraphicsWindow* window = dynamic_cast(_camera->getGraphicsContext()); + + if (!window) + { + osgViewer::Viewer::Windows windows; + viewer->getWindows(windows); + + if (windows.empty()) return; + + window = windows.front(); + } + + _camera->setGraphicsContext(window); + + _camera->setViewport(0, 0, window->getTraits()->width, window->getTraits()->height); + + _camera->setRenderOrder(osg::Camera::POST_RENDER, 10); + + _camera->setProjectionMatrix(osg::Matrix::ortho2D(0.0,_statsWidth,0.0,_statsHeight)); + _camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); + _camera->setViewMatrix(osg::Matrix::identity()); + + // only clear the depth buffer + _camera->setClearMask(0); //GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //-1); + _camera->setAllowEventFocus(false); + _camera->setCullMask(0x1); + osgViewer::Viewer* v = dynamic_cast(viewer); + v->getSceneData()->asGroup()->addChild(_camera); + _initialized = true; +} + +void StatsHandler::setUpScene(osgViewer::Viewer* viewer) +{ + if (!viewer->getSceneData()) + return; + + FindTimelineStats finder; + viewer->getSceneData()->accept(finder); + if (finder._timelines.empty()) + return; + + _switch = new osg::Switch; + osg::StateSet* stateset = _switch->getOrCreateStateSet(); + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + stateset->setMode(GL_BLEND,osg::StateAttribute::ON); + stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF); + stateset->setAttribute(new osg::PolygonMode(), osg::StateAttribute::PROTECTED); + + _group = new osg::Group; + _camera->addChild(_switch.get()); + _switch->addChild(_group); + + for (int i = 0; i < (int)finder._timelines.size(); i++) { + StatsTimeline* s = new StatsTimeline; + osg::MatrixTransform* m = s->createStatsForTimeline(finder._timelines[i]); + m->setUpdateCallback(s); + _group->addChild(m); + } +} + + + + +void StatAction::init(osg::Stats* stats, const std::string& name, const osg::Vec3& pos, float width, float height, const osg::Vec4& color) +{ + std::string font("fonts/arial.ttf"); + float characterSize = 20.0f; + float startBlocks = 150.0f; + + _name = name; + _group = new osg::Group; + + _label = new osg::Geode; + _textLabel = new osgText::Text; + _label->addDrawable(_textLabel); + _textLabel->setDataVariance(osg::Object::DYNAMIC); + _textLabel->setColor(color); + _textLabel->setFont(font); + _textLabel->setCharacterSize(characterSize); + _textLabel->setPosition(pos - osg::Vec3(0, height, 0)); + _textLabel->setText(name); + + StatsGraph* graph = new StatsGraph(pos + osg::Vec3(startBlocks, 0,0) , width-startBlocks, height); + graph->setCullingActive(false); + graph->addStatGraph(stats, stats, color, 1.0, name); + _graph = graph; + + _group->addChild(_label); + _group->addChild(_graph); +} +void StatAction::setAlpha(float v) +{ + std::cout << this << " color alpha " << v << std::endl; + StatsGraph* gfx = dynamic_cast(_graph.get()); + osg::Vec4 color = _textLabel->getColor(); + color[3] = v; + _textLabel->setColor(color); + for (int i = 0; i < (int) gfx->_statsGraphGeode->getNumDrawables(); i++) { + StatsGraph::Graph* g = dynamic_cast(gfx->_statsGraphGeode->getDrawable(0)); + g->setColor(color); + } +} + +void StatAction::setPosition(const osg::Vec3& pos) +{ + float characterSize = 20.0f; + StatsGraph* gfx = dynamic_cast(_graph.get()); + gfx->changeYposition(pos[1]); + _textLabel->setPosition(pos - osg::Vec3(0, characterSize,0)); + + + +} + + +void StatsHandler::getUsage(osg::ApplicationUsage& usage) const +{ + usage.addKeyboardMouseBinding("s","On screen stats."); + usage.addKeyboardMouseBinding("S","Output stats to console."); +} + +} diff --git a/src/osgAnimation/StatsVisitor.cpp b/src/osgAnimation/StatsVisitor.cpp new file mode 100644 index 000000000..513e1a906 --- /dev/null +++ b/src/osgAnimation/StatsVisitor.cpp @@ -0,0 +1,86 @@ +/* -*-c++-*- + * Copyright (C) 2009 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 + +osgAnimation::StatsActionVisitor::StatsActionVisitor() {} +void osgAnimation::StatsActionVisitor::reset() { _channels.clear(); } + +osgAnimation::StatsActionVisitor::StatsActionVisitor(osg::Stats* stats,unsigned int frame) +{ + _frame = frame; + _stats = stats; +} + +void osgAnimation::StatsActionVisitor::apply(Timeline& tm) +{ + _stats->setAttribute(_frame,"Timeline", tm.getCurrentTime()); + tm.traverse(*this); +} + +void osgAnimation::StatsActionVisitor::apply(Action& action) +{ + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(),0); + if (isActive()) + { + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(),1); + } +} + +void osgAnimation::StatsActionVisitor::apply(BlendIn& action) +{ + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(),0); + if (isActive()) + { + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(), action.getWeight()); + } +} + +void osgAnimation::StatsActionVisitor::apply(BlendOut& action) +{ + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(),0); + if (isActive()) + { + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(), action.getWeight()); + } +} + +void osgAnimation::StatsActionVisitor::apply(ActionAnimation& action) +{ + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(),0); + if (isActive()) + { + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(), action.getAnimation()->getWeight()); + } +} + +void osgAnimation::StatsActionVisitor::apply(StripAnimation& action) +{ + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(),0); + if (isActive()) + { + _channels.push_back(action.getName()); + _stats->setAttribute(_frame,action.getName(), action.getActionAnimation()->getAnimation()->getWeight()); + } +} diff --git a/src/osgAnimation/Timeline.cpp b/src/osgAnimation/Timeline.cpp index 5ab6b0d3c..c3f9a183c 100644 --- a/src/osgAnimation/Timeline.cpp +++ b/src/osgAnimation/Timeline.cpp @@ -13,6 +13,7 @@ */ #include +#include #include using namespace osgAnimation; @@ -29,11 +30,13 @@ osgAnimation::Timeline::Timeline() _previousFrameEvaluated = 0; _evaluating = 0; _numberFrame = UINT_MAX; // something like infinity + _collectStats = false; + _stats = new osg::Stats("Timeline"); setName("Timeline"); } -osgAnimation::Timeline::Timeline(const Timeline& nc,const osg::CopyOp& op) : osg::Object(nc, op), - _actions(nc._actions) +osgAnimation::Timeline::Timeline(const Timeline& nc,const osg::CopyOp& op) : Action(nc, op), + _actions(nc._actions) { _lastUpdate = 0; _currentFrame = 0; @@ -44,5 +47,200 @@ osgAnimation::Timeline::Timeline(const Timeline& nc,const osg::CopyOp& op) : osg _previousFrameEvaluated = 0; _evaluating = 0; _numberFrame = UINT_MAX; // something like infinity + _collectStats = false; + _stats = new osg::Stats("Timeline"); setName("Timeline"); } + +void osgAnimation::Timeline::traverse(ActionVisitor& visitor) +{ + visitor.pushTimelineOnStack(this); + // update from high priority to low priority + for( ActionLayers::reverse_iterator iterAnim = _actions.rbegin(); iterAnim != _actions.rend(); ++iterAnim ) + { + ActionList& list = iterAnim->second; + for (unsigned int i = 0; i < list.size(); i++) + { + visitor.pushFrameActionOnStack(list[i]); + if (list[i].second) list[i].second->accept(visitor); + visitor.popFrameAction(); + } + } + visitor.popTimeline(); +} + + +void osgAnimation::Timeline::setStats(osg::Stats* stats) { _stats = stats;} +osg::Stats* osgAnimation::Timeline::getStats() { return _stats;} +void osgAnimation::Timeline::collectStats(bool state) { _collectStats = state;} +osgAnimation::StatsActionVisitor* osgAnimation::Timeline::getStatsVisitor() { return _statsVisitor; } + +void osgAnimation::Timeline::clearActions() +{ + _actions.clear(); + _addActionOperations.clear(); + _removeActionOperations.clear(); +} + +void osgAnimation::Timeline::update(double simulationTime) +{ + // first time we call update we generate one frame + UpdateActionVisitor updateTimeline; + if (!_initFirstFrame) + { + _lastUpdate = simulationTime; + _initFirstFrame = true; + + updateTimeline.setFrame(_currentFrame); + accept(updateTimeline); + + if (_collectStats) + { + if (!_statsVisitor) + _statsVisitor = new osgAnimation::StatsActionVisitor(); + _statsVisitor->setStats(_stats); + _statsVisitor->setFrame(_currentFrame); + _statsVisitor->reset(); + accept(*_statsVisitor); + } + + processPendingOperation(); + } + + // 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++; + + updateTimeline.setFrame(_currentFrame); + accept(updateTimeline); + if (_collectStats) + { + if (!_statsVisitor) + _statsVisitor = new StatsActionVisitor; + _statsVisitor->setStats(_stats); + _statsVisitor->setFrame(_currentFrame); + _statsVisitor->reset(); + accept(*_statsVisitor); + } + + processPendingOperation(); + } + if (nb) + { + _lastUpdate += ((double)nb) / _fps; + } +} + +void osgAnimation::Timeline::removeAction(Action* action) +{ + if (getEvaluating()) + _removeActionOperations.push_back(FrameAction(0, action)); + else + internalRemoveAction(action); +} + +void osgAnimation::Timeline::addActionAt(unsigned int frame, Action* action, int priority) +{ + if (getEvaluating()) + _addActionOperations.push_back(Command(priority,FrameAction(frame, action))); + else + internalAddAction(priority, FrameAction(frame, action)); +} +void osgAnimation::Timeline::addActionAt(double t, Action* action, int priority) +{ + unsigned int frame = static_cast(floor(t * _fps)); + addActionAt(frame, action, priority); +} + +void osgAnimation::Timeline::addActionNow(Action* action, int priority) +{ + addActionAt(getCurrentFrame(), action, priority); +} + +void osgAnimation::Timeline::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.get()); + _removeActionOperations.pop_back(); + } +} + +void osgAnimation::Timeline::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 osgAnimation::Timeline::internalAddAction(int priority, const FrameAction& ftl) +{ + _actions[priority].insert(_actions[priority].begin(), ftl); +// _actions[priority].push_back(ftl); +} + +#if 0 +void osgAnimation::Timeline::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(); +} +#endif + +bool osgAnimation::Timeline::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; +}