From: Tim Moore Date: Wed, 18 Jul 2012 22:42:38 +0000 (+0200) Subject: Replace rotation animation update callbacks with cull callbacks X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=75087095b132ec7a42e14000c7c8b3b09147d720;p=simgear.git Replace rotation animation update callbacks with cull callbacks Update callbacks are expensive because they force large parts of the scenegraph to be traversed by OSG. --- diff --git a/simgear/scene/model/animation.cxx b/simgear/scene/model/animation.cxx index f8bfbcbd..eddf11d5 100644 --- a/simgear/scene/model/animation.cxx +++ b/simgear/scene/model/animation.cxx @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -722,60 +724,129 @@ SGTranslateAnimation::createAnimationGroup(osg::Group& parent) // Implementation of rotate/spin animation //////////////////////////////////////////////////////////////////////// -class SGRotateAnimation::UpdateCallback : public osg::NodeCallback { +//Cull callback +class RotAnimCallback : public osg::NodeCallback +{ public: - UpdateCallback(SGCondition const* condition, - SGExpressiond const* animationValue) : - _condition(condition), - _animationValue(animationValue) - { } - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - if (!_condition || _condition->test()) { - SGRotateTransform* transform; - transform = static_cast(node); - transform->setAngleDeg(_animationValue->getValue()); - } - traverse(node, nv); - } + RotAnimCallback(SGCondition const* condition, + SGExpressiond const* animationValue, + double initialValue = 0.0) : + _condition(condition), + _animationValue(animationValue), + _initialValue(initialValue), + _lastValue(0.0) + { } + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); public: - SGSharedPtr _condition; - SGSharedPtr _animationValue; + SGSharedPtr _condition; + SGSharedPtr _animationValue; + double _initialValue; + double _lastValue; }; -class SGRotateAnimation::SpinUpdateCallback : public osg::NodeCallback { +void RotAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + using namespace osg; + SGRotateTransform* transform = static_cast(node); + EffectCullVisitor* cv = dynamic_cast(nv); + + if (!cv) + return; + double angle = 0.0; + if (!_condition || _condition->test()) { + angle = _animationValue->getValue() - _initialValue; + _lastValue = angle; + } else { + angle = _lastValue; + } + const SGVec3d& sgcenter = transform->getCenter(); + const SGVec3d& sgaxis = transform->getAxis(); + Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2]) + * Matrixd::rotate(SGMiscd::deg2rad(angle), sgaxis[0], sgaxis[1], sgaxis[2]) + * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2]) + * *cv->getModelViewMatrix(); + ref_ptr refmat = new RefMatrix(mat); + cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame()); + traverse(transform, nv); + cv->popModelViewMatrix(); +} + +// Cull callback +class SpinAnimCallback : public osg::NodeCallback { public: - SpinUpdateCallback(SGCondition const* condition, - SGExpressiond const* animationValue) : + SpinAnimCallback(SGCondition const* condition, + SGExpressiond const* animationValue, + double initialValue = 0.0) : _condition(condition), _animationValue(animationValue), - _lastTime(-1) - { } - virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) - { - if (!_condition || _condition->test()) { - SGRotateTransform* transform; - transform = static_cast(node); - - double t = nv->getFrameStamp()->getReferenceTime(); - double dt = 0; - if (0 <= _lastTime) - dt = t - _lastTime; - _lastTime = t; - double velocity_rpms = _animationValue->getValue()/60; - double angle = transform->getAngleDeg(); - angle += dt*velocity_rpms*360; - angle -= 360*floor(angle/360); - transform->setAngleDeg(angle); - } - traverse(node, nv); - } + _initialValue(initialValue) + {} + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv); public: - SGSharedPtr _condition; - SGSharedPtr _animationValue; - double _lastTime; + SGSharedPtr _condition; + SGSharedPtr _animationValue; + double _initialValue; +protected: + // This cull callback can run in different threads if there is + // more than one camera. It is probably safe to overwrite the + // reference values in multiple threads, but we'll provide a + // threadsafe way to manage those values just to be safe. + struct ReferenceValues { + ReferenceValues(double t, double rot, double vel) + : _time(t), _rotation(rot), _rotVelocity(vel) + { + } + double _time; + double _rotation; + double _rotVelocity; + }; + OpenThreads::AtomicPtr _referenceValues; }; +void SpinAnimCallback::operator()(osg::Node* node, osg::NodeVisitor* nv) +{ + using namespace osg; + SGRotateTransform* transform = static_cast(node); + EffectCullVisitor* cv = dynamic_cast(nv); + if (!cv) + return; + if (!_condition || _condition->test()) { + double t = nv->getFrameStamp()->getReferenceTime(); + double rps = _animationValue->getValue() / 60.0; + ReferenceValues* refval = static_cast(_referenceValues.get()); + if (!refval || refval->_rotVelocity != rps) { + ReferenceValues* newref = 0; + if (!refval) { + // initialization + newref = new ReferenceValues(t, 0.0, rps); + } else { + double newRot = refval->_rotation + (t - refval->_time) * refval->_rotVelocity; + newref = new ReferenceValues(t, newRot, rps); + } + if (_referenceValues.assign(newref, refval)) { + delete refval; + } + refval = newref; + } + double rotation = refval->_rotation + (t - refval->_time) * rps; + double intPart; + double rot = modf(rotation, &intPart); + double angle = rot * 2.0 * osg::PI; + const SGVec3d& sgcenter = transform->getCenter(); + const SGVec3d& sgaxis = transform->getAxis(); + Matrixd mat = Matrixd::translate(-sgcenter[0], -sgcenter[1], -sgcenter[2]) + * Matrixd::rotate(angle, sgaxis[0], sgaxis[1], sgaxis[2]) + * Matrixd::translate(sgcenter[0], sgcenter[1], sgcenter[2]) + * *cv->getModelViewMatrix(); + ref_ptr refmat = new RefMatrix(mat); + cv->pushModelViewMatrix(refmat.get(), transform->getReferenceFrame()); + traverse(transform, nv); + cv->popModelViewMatrix(); + } else { + traverse(transform, nv); + } +} + SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, SGPropertyNode* modelRoot) : SGAnimation(configNode, modelRoot) @@ -792,7 +863,6 @@ SGRotateAnimation::SGRotateAnimation(const SGPropertyNode* configNode, _initialValue = _animationValue->getValue(); else _initialValue = 0; - _center = SGVec3d::zeros(); if (configNode->hasValue("axis/x1-m")) { SGVec3d v1, v2; @@ -823,12 +893,12 @@ SGRotateAnimation::createAnimationGroup(osg::Group& parent) SGRotateTransform* transform = new SGRotateTransform; transform->setName("rotate animation"); if (_isSpin) { - SpinUpdateCallback* uc; - uc = new SpinUpdateCallback(_condition, _animationValue); - transform->setUpdateCallback(uc); + SpinAnimCallback* cc; + cc = new SpinAnimCallback(_condition, _animationValue, _initialValue); + transform->setCullCallback(cc); } else if (_animationValue || !_animationValue->isConst()) { - UpdateCallback* uc = new UpdateCallback(_condition, _animationValue); - transform->setUpdateCallback(uc); + RotAnimCallback* cc = new RotAnimCallback(_condition, _animationValue, _initialValue); + transform->setCullCallback(cc); } transform->setCenter(_center); transform->setAxis(_axis);