| 1 | /* -*-c++-*- |
|---|
| 2 | * Copyright (C) 2008 Cedric Pinson <mornifle@plopbyte.net> |
|---|
| 3 | * |
|---|
| 4 | * This library is open source and may be redistributed and/or modified under |
|---|
| 5 | * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or |
|---|
| 6 | * (at your option) any later version. The full license is in LICENSE file |
|---|
| 7 | * included with this distribution, and on the openscenegraph.org website. |
|---|
| 8 | * |
|---|
| 9 | * This library is distributed in the hope that it will be useful, |
|---|
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 12 | * OpenSceneGraph Public License for more details. |
|---|
| 13 | */ |
|---|
| 14 | |
|---|
| 15 | #ifndef OSGANIMATION_TIMELINE_H |
|---|
| 16 | #define OSGANIMATION_TIMELINE_H |
|---|
| 17 | |
|---|
| 18 | #include <osgAnimation/Export> |
|---|
| 19 | #include <osg/Object> |
|---|
| 20 | #include <map> |
|---|
| 21 | #include <vector> |
|---|
| 22 | #include <osg/Notify> |
|---|
| 23 | #include <osg/Group> |
|---|
| 24 | #include <osgAnimation/Animation> |
|---|
| 25 | #include <osgAnimation/AnimationManagerBase> |
|---|
| 26 | |
|---|
| 27 | namespace osgAnimation |
|---|
| 28 | { |
|---|
| 29 | |
|---|
| 30 | class Action : public osg::Object |
|---|
| 31 | { |
|---|
| 32 | public: |
|---|
| 33 | |
|---|
| 34 | class Callback : public osg::Object |
|---|
| 35 | { |
|---|
| 36 | public: |
|---|
| 37 | Callback(){} |
|---|
| 38 | Callback(const Callback&,const osg::CopyOp&) {} |
|---|
| 39 | |
|---|
| 40 | META_Object(osgAnimation,Callback); |
|---|
| 41 | |
|---|
| 42 | virtual void operator()(Action* /*action*/) {} |
|---|
| 43 | |
|---|
| 44 | void addNestedCallback(Callback* callback) |
|---|
| 45 | { |
|---|
| 46 | if (_nested.valid()) |
|---|
| 47 | _nested->addNestedCallback(callback); |
|---|
| 48 | else |
|---|
| 49 | _nested = callback; |
|---|
| 50 | } |
|---|
| 51 | |
|---|
| 52 | protected: |
|---|
| 53 | osg::ref_ptr<Callback> _nested; |
|---|
| 54 | }; |
|---|
| 55 | |
|---|
| 56 | |
|---|
| 57 | typedef std::map<unsigned int, osg::ref_ptr<Callback> > FrameCallback; |
|---|
| 58 | |
|---|
| 59 | META_Object(osgAnimation, Action); |
|---|
| 60 | |
|---|
| 61 | Action() |
|---|
| 62 | { |
|---|
| 63 | _numberFrame = 25; |
|---|
| 64 | _fps = 25; |
|---|
| 65 | _speed = 1.0; |
|---|
| 66 | _loop = 1; |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | Action(const Action&,const osg::CopyOp&) {} |
|---|
| 70 | |
|---|
| 71 | void setCallback(double when, Callback* callback) |
|---|
| 72 | { |
|---|
| 73 | setCallback(static_cast<unsigned int>(floor(when*_fps)), callback); |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | void setCallback(unsigned int frame, Callback* callback) |
|---|
| 77 | { |
|---|
| 78 | if (_framesCallback[frame].valid()) |
|---|
| 79 | _framesCallback[frame]->addNestedCallback(callback); |
|---|
| 80 | else |
|---|
| 81 | _framesCallback[frame] = callback; |
|---|
| 82 | } |
|---|
| 83 | Callback* getCallback(unsigned int frame) |
|---|
| 84 | { |
|---|
| 85 | if (_framesCallback.find(frame) == _framesCallback.end()) |
|---|
| 86 | return 0; |
|---|
| 87 | return _framesCallback[frame].get(); |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | void setNumFrames(unsigned int numFrames) { _numberFrame = numFrames;} |
|---|
| 91 | void setDuration(double duration) { _numberFrame = static_cast<unsigned int>(floor(duration * _fps)); } |
|---|
| 92 | |
|---|
| 93 | unsigned int getNumFrames() const { return _numberFrame;} |
|---|
| 94 | double getDuration() const { return _numberFrame * 1.0 / _fps; } |
|---|
| 95 | |
|---|
| 96 | // 0 means infini else it's the number of loop |
|---|
| 97 | virtual void setLoop(int nb) { _loop = nb; } |
|---|
| 98 | virtual unsigned int getLoop() const { return _loop;} |
|---|
| 99 | |
|---|
| 100 | // get the number of loop, the frame relative to loop. |
|---|
| 101 | // return true if in range, and false if out of range. |
|---|
| 102 | bool evaluateFrame(unsigned int frame, unsigned int& resultframe, unsigned int& nbloop ) |
|---|
| 103 | { |
|---|
| 104 | nbloop = frame / getNumFrames(); |
|---|
| 105 | resultframe = frame; |
|---|
| 106 | |
|---|
| 107 | if (frame > getNumFrames()-1) |
|---|
| 108 | { |
|---|
| 109 | if (!getLoop()) |
|---|
| 110 | resultframe = frame % getNumFrames(); |
|---|
| 111 | else |
|---|
| 112 | { |
|---|
| 113 | if (nbloop >= getLoop()) |
|---|
| 114 | return false; |
|---|
| 115 | else |
|---|
| 116 | resultframe = frame % getNumFrames(); |
|---|
| 117 | } |
|---|
| 118 | } |
|---|
| 119 | return true; |
|---|
| 120 | } |
|---|
| 121 | |
|---|
| 122 | virtual void evaluate(unsigned int frame) |
|---|
| 123 | { |
|---|
| 124 | unsigned int frameInAction; |
|---|
| 125 | unsigned int loopDone; |
|---|
| 126 | if (!evaluateFrame(frame, frameInAction, loopDone)) |
|---|
| 127 | osg::notify(osg::DEBUG_INFO) << getName() << " Action frame " << frameInAction << " finished" << std::endl; |
|---|
| 128 | else |
|---|
| 129 | osg::notify(osg::DEBUG_INFO) << getName() << " Action frame " << frame << " relative to loop " << frameInAction << " no loop " << loopDone<< std::endl; |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | virtual void evaluateCallback(unsigned int frame) |
|---|
| 133 | { |
|---|
| 134 | unsigned int frameInAction; |
|---|
| 135 | unsigned int loopDone; |
|---|
| 136 | if (!evaluateFrame(frame, frameInAction, loopDone)) |
|---|
| 137 | return; |
|---|
| 138 | |
|---|
| 139 | frame = frameInAction; |
|---|
| 140 | if (_framesCallback.find(frame) != _framesCallback.end()) |
|---|
| 141 | { |
|---|
| 142 | std::cout << getName() << " evaluate callback " << _framesCallback[frame]->getName() << " at " << frame << std::endl; |
|---|
| 143 | (*_framesCallback[frame])(this); |
|---|
| 144 | } |
|---|
| 145 | } |
|---|
| 146 | |
|---|
| 147 | protected: |
|---|
| 148 | FrameCallback _framesCallback; |
|---|
| 149 | |
|---|
| 150 | double _speed; |
|---|
| 151 | unsigned int _fps; |
|---|
| 152 | unsigned int _numberFrame; |
|---|
| 153 | unsigned int _loop; |
|---|
| 154 | |
|---|
| 155 | enum State |
|---|
| 156 | { |
|---|
| 157 | Play, |
|---|
| 158 | Stop |
|---|
| 159 | }; |
|---|
| 160 | |
|---|
| 161 | State _state; |
|---|
| 162 | }; |
|---|
| 163 | |
|---|
| 164 | |
|---|
| 165 | class Timeline : public osg::Object |
|---|
| 166 | { |
|---|
| 167 | protected: |
|---|
| 168 | typedef std::pair<unsigned int, osg::ref_ptr<Action> > FrameAction; |
|---|
| 169 | typedef std::vector<FrameAction> ActionList; |
|---|
| 170 | typedef std::map<int, ActionList> ActionLayers; |
|---|
| 171 | enum State |
|---|
| 172 | { |
|---|
| 173 | Play, |
|---|
| 174 | Stop |
|---|
| 175 | }; |
|---|
| 176 | |
|---|
| 177 | |
|---|
| 178 | ActionLayers _actions; |
|---|
| 179 | double _lastUpdate; |
|---|
| 180 | double _speed; |
|---|
| 181 | unsigned int _currentFrame; |
|---|
| 182 | unsigned int _fps; |
|---|
| 183 | unsigned int _numberFrame; |
|---|
| 184 | unsigned int _previousFrameEvaluated; |
|---|
| 185 | bool _loop; |
|---|
| 186 | bool _initFirstFrame; |
|---|
| 187 | |
|---|
| 188 | State _state; |
|---|
| 189 | |
|---|
| 190 | // to manage pending operation |
|---|
| 191 | bool _evaluating; |
|---|
| 192 | |
|---|
| 193 | struct Command |
|---|
| 194 | { |
|---|
| 195 | Command():_priority(0) {} |
|---|
| 196 | Command(int priority, const FrameAction& action) : _priority(priority), _action(action) {} |
|---|
| 197 | int _priority; |
|---|
| 198 | FrameAction _action; |
|---|
| 199 | }; |
|---|
| 200 | |
|---|
| 201 | typedef std::vector<Command> CommandList; |
|---|
| 202 | CommandList _addActionOperations; |
|---|
| 203 | ActionList _removeActionOperations; |
|---|
| 204 | |
|---|
| 205 | void setEvaluating(bool state) { _evaluating = state;} |
|---|
| 206 | void processPendingOperation() |
|---|
| 207 | { |
|---|
| 208 | // process all pending add action operation |
|---|
| 209 | while( !_addActionOperations.empty()) |
|---|
| 210 | { |
|---|
| 211 | internalAddAction(_addActionOperations.back()._priority, _addActionOperations.back()._action); |
|---|
| 212 | _addActionOperations.pop_back(); |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | // process all pending remove action operation |
|---|
| 216 | while( !_removeActionOperations.empty()) |
|---|
| 217 | { |
|---|
| 218 | internalRemoveAction(_removeActionOperations.back().second.get()); |
|---|
| 219 | _removeActionOperations.pop_back(); |
|---|
| 220 | } |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | void internalRemoveAction(Action* action) |
|---|
| 224 | { |
|---|
| 225 | for (ActionLayers::iterator it = _actions.begin(); it != _actions.end(); it++) |
|---|
| 226 | { |
|---|
| 227 | ActionList& fa = it->second; |
|---|
| 228 | for (unsigned int i = 0; i < fa.size(); i++) |
|---|
| 229 | if (fa[i].second.get() == action) |
|---|
| 230 | { |
|---|
| 231 | fa.erase(fa.begin() + i); |
|---|
| 232 | return; |
|---|
| 233 | } |
|---|
| 234 | } |
|---|
| 235 | } |
|---|
| 236 | void internalAddAction(int priority, const FrameAction& ftl) |
|---|
| 237 | { |
|---|
| 238 | _actions[priority].push_back(ftl); |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | public: |
|---|
| 242 | |
|---|
| 243 | META_Object(osgAnimation, Timeline); |
|---|
| 244 | |
|---|
| 245 | Timeline(); |
|---|
| 246 | Timeline(const Timeline& nc,const osg::CopyOp& op = osg::CopyOp::SHALLOW_COPY); |
|---|
| 247 | State getStatus() const { return _state; } |
|---|
| 248 | const ActionList& getActionLayer(int i) |
|---|
| 249 | { |
|---|
| 250 | return _actions[i]; |
|---|
| 251 | } |
|---|
| 252 | unsigned int getCurrentFrame() const { return _currentFrame;} |
|---|
| 253 | double getCurrentTime() const { return _currentFrame * 1.0 / _fps;} |
|---|
| 254 | |
|---|
| 255 | void play() { _state = Play; } |
|---|
| 256 | void gotoFrame(unsigned int frame) { _currentFrame = frame; } |
|---|
| 257 | void stop() { _state = Stop; } |
|---|
| 258 | bool getEvaluating() const { return _evaluating;} |
|---|
| 259 | |
|---|
| 260 | bool isActive(Action* activeAction) |
|---|
| 261 | { |
|---|
| 262 | // update from high priority to low priority |
|---|
| 263 | for( ActionLayers::iterator iterAnim = _actions.begin(); iterAnim != _actions.end(); ++iterAnim ) |
|---|
| 264 | { |
|---|
| 265 | // update all animation |
|---|
| 266 | ActionList& list = iterAnim->second; |
|---|
| 267 | for (unsigned int i = 0; i < list.size(); i++) |
|---|
| 268 | { |
|---|
| 269 | Action* action = list[i].second.get(); |
|---|
| 270 | if (action == activeAction) |
|---|
| 271 | { |
|---|
| 272 | unsigned int firstFrame = list[i].first; |
|---|
| 273 | // check if current frame of timeline hit an action interval |
|---|
| 274 | if (_currentFrame >= firstFrame && |
|---|
| 275 | _currentFrame < (firstFrame + action->getNumFrames()) ) |
|---|
| 276 | return true; |
|---|
| 277 | } |
|---|
| 278 | } |
|---|
| 279 | } |
|---|
| 280 | return false; |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | void removeAction(Action* action) |
|---|
| 284 | { |
|---|
| 285 | if (getEvaluating()) |
|---|
| 286 | _removeActionOperations.push_back(FrameAction(0, action)); |
|---|
| 287 | else |
|---|
| 288 | internalRemoveAction(action); |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | virtual void addActionAt(unsigned int frame, Action* action, int priority = 0) |
|---|
| 292 | { |
|---|
| 293 | if (getEvaluating()) |
|---|
| 294 | _addActionOperations.push_back(Command(priority,FrameAction(frame, action))); |
|---|
| 295 | else |
|---|
| 296 | internalAddAction(priority, FrameAction(frame, action)); |
|---|
| 297 | } |
|---|
| 298 | virtual void addActionAt(double t, Action* action, int priority = 0) |
|---|
| 299 | { |
|---|
| 300 | unsigned int frame = static_cast<unsigned int>(floor(t * _fps)); |
|---|
| 301 | addActionAt(frame, action, priority); |
|---|
| 302 | } |
|---|
| 303 | |
|---|
| 304 | virtual void evaluate(unsigned int frame) |
|---|
| 305 | { |
|---|
| 306 | setEvaluating(true); |
|---|
| 307 | osg::notify(osg::DEBUG_INFO) << getName() << " evaluate frame " << _currentFrame << std::endl; |
|---|
| 308 | |
|---|
| 309 | // update from high priority to low priority |
|---|
| 310 | for( ActionLayers::reverse_iterator iterAnim = _actions.rbegin(); iterAnim != _actions.rend(); ++iterAnim ) |
|---|
| 311 | { |
|---|
| 312 | // update all animation |
|---|
| 313 | ActionList& list = iterAnim->second; |
|---|
| 314 | for (unsigned int i = 0; i < list.size(); i++) |
|---|
| 315 | { |
|---|
| 316 | unsigned int firstFrame = list[i].first; |
|---|
| 317 | Action* action = list[i].second.get(); |
|---|
| 318 | // check if current frame of timeline hit an action interval |
|---|
| 319 | if (frame >= firstFrame && |
|---|
| 320 | frame < (firstFrame + action->getNumFrames()) ) |
|---|
| 321 | action->evaluate(frame - firstFrame); |
|---|
| 322 | } |
|---|
| 323 | } |
|---|
| 324 | setEvaluating(false); |
|---|
| 325 | |
|---|
| 326 | // evaluate callback after updating all animation |
|---|
| 327 | evaluateCallback(frame); |
|---|
| 328 | _previousFrameEvaluated = frame; |
|---|
| 329 | } |
|---|
| 330 | |
|---|
| 331 | virtual void evaluateCallback(unsigned int frame) |
|---|
| 332 | { |
|---|
| 333 | // update from high priority to low priority |
|---|
| 334 | for( ActionLayers::reverse_iterator iterAnim = _actions.rbegin(); iterAnim != _actions.rend(); ++iterAnim ) |
|---|
| 335 | { |
|---|
| 336 | // update all animation |
|---|
| 337 | ActionList& list = iterAnim->second; |
|---|
| 338 | for (unsigned int i = 0; i < list.size(); i++) |
|---|
| 339 | { |
|---|
| 340 | unsigned int firstFrame = list[i].first; |
|---|
| 341 | Action* action = list[i].second.get(); |
|---|
| 342 | // check if current frame of timeline hit an action interval |
|---|
| 343 | if (frame >= firstFrame && |
|---|
| 344 | frame < (firstFrame + action->getNumFrames()) ) |
|---|
| 345 | action->evaluateCallback(frame - firstFrame); |
|---|
| 346 | } |
|---|
| 347 | } |
|---|
| 348 | processPendingOperation(); |
|---|
| 349 | } |
|---|
| 350 | |
|---|
| 351 | virtual void update(double simulationTime) |
|---|
| 352 | { |
|---|
| 353 | // first time we call update we generate one frame |
|---|
| 354 | if (!_initFirstFrame) |
|---|
| 355 | { |
|---|
| 356 | _lastUpdate = simulationTime; |
|---|
| 357 | _initFirstFrame = true; |
|---|
| 358 | evaluate(_currentFrame); |
|---|
| 359 | } |
|---|
| 360 | |
|---|
| 361 | // find the number of frame pass since the last update |
|---|
| 362 | double delta = (simulationTime - _lastUpdate); |
|---|
| 363 | double nbframes = delta * _fps * _speed; |
|---|
| 364 | unsigned int nb = static_cast<unsigned int>(floor(nbframes)); |
|---|
| 365 | |
|---|
| 366 | for (unsigned int i = 0; i < nb; i++) |
|---|
| 367 | { |
|---|
| 368 | if (_state == Play) |
|---|
| 369 | _currentFrame++; |
|---|
| 370 | evaluate(_currentFrame); |
|---|
| 371 | } |
|---|
| 372 | if (nb) |
|---|
| 373 | { |
|---|
| 374 | _lastUpdate += ((double)nb) / _fps; |
|---|
| 375 | } |
|---|
| 376 | } |
|---|
| 377 | }; |
|---|
| 378 | |
|---|
| 379 | |
|---|
| 380 | |
|---|
| 381 | // blend in from 0 to weight in duration |
|---|
| 382 | class BlendIn : public Action |
|---|
| 383 | { |
|---|
| 384 | double _weight; |
|---|
| 385 | osg::ref_ptr<Animation> _animation; |
|---|
| 386 | |
|---|
| 387 | public: |
|---|
| 388 | BlendIn(Animation* animation, double duration, double weight) |
|---|
| 389 | { |
|---|
| 390 | _animation = animation; |
|---|
| 391 | _weight = weight; |
|---|
| 392 | float d = duration * _fps; |
|---|
| 393 | setNumFrames(static_cast<unsigned int>(floor(d)) + 1); |
|---|
| 394 | setName("BlendIn"); |
|---|
| 395 | } |
|---|
| 396 | double getWeight() const { return _weight;} |
|---|
| 397 | virtual void evaluate(unsigned int frame) |
|---|
| 398 | { |
|---|
| 399 | Action::evaluate(frame); |
|---|
| 400 | // frame + 1 because the start is 0 and we want to start the blend in at the first |
|---|
| 401 | // frame. |
|---|
| 402 | double ratio = ( (frame+1) * 1.0 / (getNumFrames()) ); |
|---|
| 403 | double w = _weight; |
|---|
| 404 | if (frame < getNumFrames() -1 ) // the last frame we set the target weight the user asked |
|---|
| 405 | w = _weight * ratio; |
|---|
| 406 | _animation->setWeight(w); |
|---|
| 407 | } |
|---|
| 408 | }; |
|---|
| 409 | |
|---|
| 410 | // blend in from 0 to weight in duration |
|---|
| 411 | class BlendOut : public Action |
|---|
| 412 | { |
|---|
| 413 | double _weight; |
|---|
| 414 | osg::ref_ptr<Animation> _animation; |
|---|
| 415 | public: |
|---|
| 416 | BlendOut(Animation* animation, double duration) |
|---|
| 417 | { |
|---|
| 418 | _animation = animation; |
|---|
| 419 | float d = duration * _fps; |
|---|
| 420 | setNumFrames(static_cast<unsigned int>(floor(d) + 1)); |
|---|
| 421 | _weight = 1.0; |
|---|
| 422 | setName("BlendOut"); |
|---|
| 423 | } |
|---|
| 424 | double getWeight() const { return _weight;} |
|---|
| 425 | virtual void evaluate(unsigned int frame) |
|---|
| 426 | { |
|---|
| 427 | Action::evaluate(frame); |
|---|
| 428 | // frame + 1 because the start is 0 and we want to start the blend in at the first |
|---|
| 429 | // frame. |
|---|
| 430 | double ratio = ( (frame+1) * 1.0 / (getNumFrames()) ); |
|---|
| 431 | double w = 0.0; |
|---|
| 432 | if (frame < getNumFrames() -1 ) // the last frame we set the target weight the user asked |
|---|
| 433 | w = _weight * (1.0-ratio); |
|---|
| 434 | _animation->setWeight(w); |
|---|
| 435 | } |
|---|
| 436 | }; |
|---|
| 437 | |
|---|
| 438 | |
|---|
| 439 | class ActionAnimation : public Action |
|---|
| 440 | { |
|---|
| 441 | public: |
|---|
| 442 | ActionAnimation(Animation* animation) : _animation(animation) |
|---|
| 443 | { |
|---|
| 444 | setDuration(animation->getDuration()); |
|---|
| 445 | setName(animation->getName()); |
|---|
| 446 | } |
|---|
| 447 | virtual void evaluate(unsigned int frame) |
|---|
| 448 | { |
|---|
| 449 | Action::evaluate(frame); |
|---|
| 450 | _animation->update(frame * 1.0/_fps); |
|---|
| 451 | } |
|---|
| 452 | Animation* getAnimation() { return _animation.get(); } |
|---|
| 453 | protected: |
|---|
| 454 | osg::ref_ptr<Animation> _animation; |
|---|
| 455 | }; |
|---|
| 456 | |
|---|
| 457 | |
|---|
| 458 | // encapsulate animation with blend in blend out for classic usage |
|---|
| 459 | class StripAnimation : public Action |
|---|
| 460 | { |
|---|
| 461 | protected: |
|---|
| 462 | typedef std::pair<unsigned int, osg::ref_ptr<Action> > FrameAction; |
|---|
| 463 | |
|---|
| 464 | public: |
|---|
| 465 | StripAnimation(Animation* animation, double blendInDuration, double blendOutDuration, double blendInWeightTarget = 1.0 ) |
|---|
| 466 | { |
|---|
| 467 | _blendIn = new BlendIn(animation, blendInDuration, blendInWeightTarget); |
|---|
| 468 | _animation = new ActionAnimation(animation); |
|---|
| 469 | unsigned int start = static_cast<unsigned int>(floor((_animation->getDuration() - blendOutDuration) * _fps)); |
|---|
| 470 | _blendOut = FrameAction(start, new BlendOut(animation, blendOutDuration)); |
|---|
| 471 | setName(animation->getName() + "_Strip"); |
|---|
| 472 | _blendIn->setName(_animation->getName() + "_" + _blendIn->getName()); |
|---|
| 473 | _blendOut.second->setName(_animation->getName() + "_" + _blendOut.second->getName()); |
|---|
| 474 | setDuration(animation->getDuration()); |
|---|
| 475 | } |
|---|
| 476 | |
|---|
| 477 | ActionAnimation* getActionAnimation() { return _animation.get(); } |
|---|
| 478 | BlendIn* getBlendIn() { return _blendIn.get(); } |
|---|
| 479 | BlendOut* getBlendOut() { return dynamic_cast<BlendOut*>(_blendOut.second.get()); } |
|---|
| 480 | const ActionAnimation* getActionAnimation() const { return _animation.get(); } |
|---|
| 481 | const BlendIn* getBlendIn() const { return _blendIn.get(); } |
|---|
| 482 | const BlendOut* getBlendOut() const { return dynamic_cast<BlendOut*>(_blendOut.second.get()); } |
|---|
| 483 | |
|---|
| 484 | unsigned int getLoop() const { return _animation->getLoop(); } |
|---|
| 485 | void setLoop(unsigned int loop) |
|---|
| 486 | { |
|---|
| 487 | _animation->setLoop(loop); |
|---|
| 488 | if (!loop) |
|---|
| 489 | setDuration(-1); |
|---|
| 490 | else |
|---|
| 491 | setDuration(loop * _animation->getDuration()); |
|---|
| 492 | |
|---|
| 493 | // duration changed re evaluate the blendout duration |
|---|
| 494 | unsigned int start = static_cast<unsigned int>(floor((getDuration() - _blendOut.second->getDuration()) * _fps)); |
|---|
| 495 | _blendOut = FrameAction(start, _blendOut.second); |
|---|
| 496 | } |
|---|
| 497 | |
|---|
| 498 | virtual void evaluate(unsigned int frame) |
|---|
| 499 | { |
|---|
| 500 | if (frame > getNumFrames() - 1) |
|---|
| 501 | return; |
|---|
| 502 | |
|---|
| 503 | Action::evaluate(frame); |
|---|
| 504 | if (frame < _blendIn->getNumFrames()) |
|---|
| 505 | _blendIn->evaluate(frame); |
|---|
| 506 | if (frame >= _blendOut.first) |
|---|
| 507 | _blendOut.second->evaluate(frame - _blendOut.first); |
|---|
| 508 | _animation->evaluate(frame); |
|---|
| 509 | } |
|---|
| 510 | |
|---|
| 511 | protected: |
|---|
| 512 | osg::ref_ptr<BlendIn> _blendIn; |
|---|
| 513 | FrameAction _blendOut; |
|---|
| 514 | osg::ref_ptr<ActionAnimation> _animation; |
|---|
| 515 | }; |
|---|
| 516 | |
|---|
| 517 | |
|---|
| 518 | class RunAction : public Action::Callback |
|---|
| 519 | { |
|---|
| 520 | protected: |
|---|
| 521 | osg::ref_ptr<Timeline> _tm; |
|---|
| 522 | osg::ref_ptr<Action> _action; |
|---|
| 523 | |
|---|
| 524 | public: |
|---|
| 525 | RunAction(Timeline* tm, Action* a) : _tm(tm), _action(a) {} |
|---|
| 526 | virtual void operator()(Action* /*action*/) |
|---|
| 527 | { |
|---|
| 528 | _tm->addActionAt(_tm->getCurrentFrame(), _action.get()); // warning we are trsversing the vector |
|---|
| 529 | } |
|---|
| 530 | }; |
|---|
| 531 | |
|---|
| 532 | |
|---|
| 533 | |
|---|
| 534 | } |
|---|
| 535 | |
|---|
| 536 | #endif |
|---|