1 #ifndef GAME_RENDER_ANIMATION_H 2 #define GAME_RENDER_ANIMATION_H 3 4 #include "../mwworld/ptr.hpp" 5 6 #include <components/sceneutil/controller.hpp> 7 #include <components/sceneutil/textkeymap.hpp> 8 #include <components/sceneutil/util.hpp> 9 10 #include <vector> 11 12 namespace ESM 13 { 14 struct Light; 15 struct MagicEffect; 16 } 17 18 namespace Resource 19 { 20 class ResourceSystem; 21 } 22 23 namespace SceneUtil 24 { 25 class KeyframeHolder; 26 class KeyframeController; 27 class LightSource; 28 class LightListCallback; 29 class Skeleton; 30 } 31 32 namespace MWRender 33 { 34 35 class ResetAccumRootCallback; 36 class RotateController; 37 class TransparencyUpdater; 38 39 class EffectAnimationTime : public SceneUtil::ControllerSource 40 { 41 private: 42 float mTime; 43 public: 44 float getValue(osg::NodeVisitor* nv) override; 45 46 void addTime(float duration); 47 void resetTime(float time); 48 float getTime() const; 49 EffectAnimationTime()50 EffectAnimationTime() : mTime(0) { } 51 }; 52 53 /// @brief Detaches the node from its parent when the object goes out of scope. 54 class PartHolder 55 { 56 public: 57 PartHolder(osg::ref_ptr<osg::Node> node); 58 59 ~PartHolder(); 60 getNode()61 osg::ref_ptr<osg::Node> getNode() 62 { 63 return mNode; 64 } 65 66 private: 67 osg::ref_ptr<osg::Node> mNode; 68 69 void operator= (const PartHolder&); 70 PartHolder(const PartHolder&); 71 }; 72 typedef std::shared_ptr<PartHolder> PartHolderPtr; 73 74 struct EffectParams 75 { 76 std::string mModelName; // Just here so we don't add the same effect twice 77 std::shared_ptr<EffectAnimationTime> mAnimTime; 78 float mMaxControllerLength; 79 int mEffectId; 80 bool mLoop; 81 std::string mBoneName; 82 }; 83 84 class Animation : public osg::Referenced 85 { 86 public: 87 enum BoneGroup { 88 BoneGroup_LowerBody = 0, 89 BoneGroup_Torso, 90 BoneGroup_LeftArm, 91 BoneGroup_RightArm 92 }; 93 94 enum BlendMask { 95 BlendMask_LowerBody = 1<<0, 96 BlendMask_Torso = 1<<1, 97 BlendMask_LeftArm = 1<<2, 98 BlendMask_RightArm = 1<<3, 99 100 BlendMask_UpperBody = BlendMask_Torso | BlendMask_LeftArm | BlendMask_RightArm, 101 102 BlendMask_All = BlendMask_LowerBody | BlendMask_UpperBody 103 }; 104 /* This is the number of *discrete* blend masks. */ 105 static constexpr size_t sNumBlendMasks = 4; 106 107 /// Holds an animation priority value for each BoneGroup. 108 struct AnimPriority 109 { 110 /// Convenience constructor, initialises all priorities to the same value. AnimPriorityMWRender::Animation::AnimPriority111 AnimPriority(int priority) 112 { 113 for (unsigned int i=0; i<sNumBlendMasks; ++i) 114 mPriority[i] = priority; 115 } 116 operator ==MWRender::Animation::AnimPriority117 bool operator == (const AnimPriority& other) const 118 { 119 for (unsigned int i=0; i<sNumBlendMasks; ++i) 120 if (other.mPriority[i] != mPriority[i]) 121 return false; 122 return true; 123 } 124 operator []MWRender::Animation::AnimPriority125 int& operator[] (BoneGroup n) 126 { 127 return mPriority[n]; 128 } 129 operator []MWRender::Animation::AnimPriority130 const int& operator[] (BoneGroup n) const 131 { 132 return mPriority[n]; 133 } 134 containsMWRender::Animation::AnimPriority135 bool contains(int priority) const 136 { 137 for (unsigned int i=0; i<sNumBlendMasks; ++i) 138 if (priority == mPriority[i]) 139 return true; 140 return false; 141 } 142 143 int mPriority[sNumBlendMasks]; 144 }; 145 146 class TextKeyListener 147 { 148 public: 149 virtual void handleTextKey(const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, 150 const SceneUtil::TextKeyMap& map) = 0; 151 152 virtual ~TextKeyListener() = default; 153 }; 154 155 void setTextKeyListener(TextKeyListener* listener); 156 updateCarriedLeftVisible(const int weaptype) const157 virtual bool updateCarriedLeftVisible(const int weaptype) const { return false; }; 158 159 protected: 160 class AnimationTime : public SceneUtil::ControllerSource 161 { 162 private: 163 std::shared_ptr<float> mTimePtr; 164 165 public: 166 setTimePtr(std::shared_ptr<float> time)167 void setTimePtr(std::shared_ptr<float> time) 168 { mTimePtr = time; } getTimePtr() const169 std::shared_ptr<float> getTimePtr() const 170 { return mTimePtr; } 171 172 float getValue(osg::NodeVisitor* nv) override; 173 }; 174 175 class NullAnimationTime : public SceneUtil::ControllerSource 176 { 177 public: getValue(osg::NodeVisitor * nv)178 float getValue(osg::NodeVisitor *nv) override 179 { 180 return 0.f; 181 } 182 }; 183 184 struct AnimSource; 185 186 struct AnimState { 187 std::shared_ptr<AnimSource> mSource; 188 float mStartTime; 189 float mLoopStartTime; 190 float mLoopStopTime; 191 float mStopTime; 192 193 typedef std::shared_ptr<float> TimePtr; 194 TimePtr mTime; 195 float mSpeedMult; 196 197 bool mPlaying; 198 bool mLoopingEnabled; 199 size_t mLoopCount; 200 201 AnimPriority mPriority; 202 int mBlendMask; 203 bool mAutoDisable; 204 AnimStateMWRender::Animation::AnimState205 AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f), 206 mTime(new float), mSpeedMult(1.0f), mPlaying(false), mLoopingEnabled(true), 207 mLoopCount(0), mPriority(0), mBlendMask(0), mAutoDisable(true) 208 { 209 } 210 ~AnimState(); 211 getTimeMWRender::Animation::AnimState212 float getTime() const 213 { 214 return *mTime; 215 } setTimeMWRender::Animation::AnimState216 void setTime(float time) 217 { 218 *mTime = time; 219 } 220 shouldLoopMWRender::Animation::AnimState221 bool shouldLoop() const 222 { 223 return getTime() >= mLoopStopTime && mLoopingEnabled && mLoopCount > 0; 224 } 225 }; 226 typedef std::map<std::string,AnimState> AnimStateMap; 227 AnimStateMap mStates; 228 229 typedef std::vector<std::shared_ptr<AnimSource> > AnimSourceList; 230 AnimSourceList mAnimSources; 231 232 osg::ref_ptr<osg::Group> mInsert; 233 234 osg::ref_ptr<osg::Group> mObjectRoot; 235 SceneUtil::Skeleton* mSkeleton; 236 237 // The node expected to accumulate movement during movement animations. 238 osg::ref_ptr<osg::Node> mAccumRoot; 239 240 // The controller animating that node. 241 osg::ref_ptr<SceneUtil::KeyframeController> mAccumCtrl; 242 243 // Used to reset the position of the accumulation root every frame - the movement should be applied to the physics system 244 osg::ref_ptr<ResetAccumRootCallback> mResetAccumRootCallback; 245 246 // Keep track of controllers that we added to our scene graph. 247 // We may need to rebuild these controllers when the active animation groups / sources change. 248 std::vector<std::pair<osg::ref_ptr<osg::Node>, osg::ref_ptr<osg::NodeCallback>>> mActiveControllers; 249 250 std::shared_ptr<AnimationTime> mAnimationTimePtr[sNumBlendMasks]; 251 252 // Stored in all lowercase for a case-insensitive lookup 253 typedef std::map<std::string, osg::ref_ptr<osg::MatrixTransform> > NodeMap; 254 mutable NodeMap mNodeMap; 255 mutable bool mNodeMapCreated; 256 257 MWWorld::Ptr mPtr; 258 259 Resource::ResourceSystem* mResourceSystem; 260 261 osg::Vec3f mAccumulate; 262 263 TextKeyListener* mTextKeyListener; 264 265 osg::ref_ptr<RotateController> mHeadController; 266 osg::ref_ptr<RotateController> mSpineController; 267 osg::ref_ptr<RotateController> mRootController; 268 float mHeadYawRadians; 269 float mHeadPitchRadians; 270 float mUpperBodyYawRadians; 271 float mLegsYawRadians; 272 float mBodyPitchRadians; 273 274 RotateController* addRotateController(std::string bone); 275 276 bool mHasMagicEffects; 277 278 osg::ref_ptr<SceneUtil::LightSource> mGlowLight; 279 osg::ref_ptr<SceneUtil::GlowUpdater> mGlowUpdater; 280 osg::ref_ptr<TransparencyUpdater> mTransparencyUpdater; 281 osg::ref_ptr<SceneUtil::LightSource> mExtraLightSource; 282 283 float mAlpha; 284 285 mutable std::map<std::string, float> mAnimVelocities; 286 287 osg::ref_ptr<SceneUtil::LightListCallback> mLightListCallback; 288 289 const NodeMap& getNodeMap() const; 290 291 /* Sets the appropriate animations on the bone groups based on priority. 292 */ 293 void resetActiveGroups(); 294 295 size_t detectBlendMask(const osg::Node* node) const; 296 297 /* Updates the position of the accum root node for the given time, and 298 * returns the wanted movement vector from the previous time. */ 299 void updatePosition(float oldtime, float newtime, osg::Vec3f& position); 300 301 /* Resets the animation to the time of the specified start marker, without 302 * moving anything, and set the end time to the specified stop marker. If 303 * the marker is not found, or if the markers are the same, it returns 304 * false. 305 */ 306 bool reset(AnimState &state, const SceneUtil::TextKeyMap &keys, 307 const std::string &groupname, const std::string &start, const std::string &stop, 308 float startpoint, bool loopfallback); 309 310 void handleTextKey(AnimState &state, const std::string &groupname, SceneUtil::TextKeyMap::ConstIterator key, 311 const SceneUtil::TextKeyMap& map); 312 313 /** Sets the root model of the object. 314 * 315 * Note that you must make sure all animation sources are cleared before resetting the object 316 * root. All nodes previously retrieved with getNode will also become invalidated. 317 * @param forceskeleton Wrap the object root in a Skeleton, even if it contains no skinned parts. Use this if you intend to add skinned parts manually. 318 * @param baseonly If true, then any meshes or particle systems in the model are ignored 319 * (useful for NPCs, where only the skeleton is needed for the root, and the actual NPC parts are then assembled from separate files). 320 */ 321 void setObjectRoot(const std::string &model, bool forceskeleton, bool baseonly, bool isCreature); 322 323 void loadAllAnimationsInFolder(const std::string &model, const std::string &baseModel); 324 325 /** Adds the keyframe controllers in the specified model as a new animation source. 326 * @note Later added animation sources have the highest priority when it comes to finding a particular animation. 327 * @param model The file to add the keyframes for. Note that the .nif file extension will be replaced with .kf. 328 * @param baseModel The filename of the mObjectRoot, only used for error messages. 329 */ 330 void addAnimSource(const std::string &model, const std::string& baseModel); 331 void addSingleAnimSource(const std::string &model, const std::string& baseModel); 332 333 /** Adds an additional light to the given node using the specified ESM record. */ 334 void addExtraLight(osg::ref_ptr<osg::Group> parent, const ESM::Light *light); 335 336 void clearAnimSources(); 337 338 /** 339 * Provided to allow derived classes adding their own controllers. Note, the controllers must be added to mActiveControllers 340 * so they get cleaned up properly on the next controller rebuild. A controller rebuild may be necessary to ensure correct ordering. 341 */ 342 virtual void addControllers(); 343 344 public: 345 346 Animation(const MWWorld::Ptr &ptr, osg::ref_ptr<osg::Group> parentNode, Resource::ResourceSystem* resourceSystem); 347 348 /// Must be thread safe 349 virtual ~Animation(); 350 351 MWWorld::ConstPtr getPtr() const; 352 353 MWWorld::Ptr getPtr(); 354 355 /// Set active flag on the object skeleton, if one exists. 356 /// @see SceneUtil::Skeleton::setActive 357 /// 0 = Inactive, 1 = Active in place, 2 = Active 358 void setActive(int active); 359 360 osg::Group* getOrCreateObjectRoot(); 361 362 osg::Group* getObjectRoot(); 363 364 /** 365 * @brief Add an effect mesh attached to a bone or the insert scene node 366 * @param model 367 * @param effectId An ID for this effect by which you can identify it later. If this is not wanted, set to -1. 368 * @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true, 369 * you need to remove it manually using removeEffect when the effect should end. 370 * @param bonename Bone to attach to, or empty string to use the scene node instead 371 * @param texture override the texture specified in the model's materials - if empty, do not override 372 * @note Will not add an effect twice. 373 */ 374 void addEffect (const std::string& model, int effectId, bool loop = false, const std::string& bonename = "", const std::string& texture = ""); 375 void removeEffect (int effectId); 376 void removeEffects (); 377 void getLoopingEffects (std::vector<int>& out) const; 378 379 // Add a spell casting glow to an object. From measuring video taken from the original engine, 380 // the glow seems to be about 1.5 seconds except for telekinesis, which is 1 second. 381 void addSpellCastGlow(const ESM::MagicEffect *effect, float glowDuration = 1.5); 382 383 virtual void updatePtr(const MWWorld::Ptr &ptr); 384 385 bool hasAnimation(const std::string &anim) const; 386 387 // Specifies the axis' to accumulate on. Non-accumulated axis will just 388 // move visually, but not affect the actual movement. Each x/y/z value 389 // should be on the scale of 0 to 1. 390 void setAccumulation(const osg::Vec3f& accum); 391 392 /** Plays an animation. 393 * \param groupname Name of the animation group to play. 394 * \param priority Priority of the animation. The animation will play on 395 * bone groups that don't have another animation set of a 396 * higher priority. 397 * \param blendMask Bone groups to play the animation on. 398 * \param autodisable Automatically disable the animation when it stops 399 * playing. 400 * \param speedmult Speed multiplier for the animation. 401 * \param start Key marker from which to start. 402 * \param stop Key marker to stop at. 403 * \param startpoint How far in between the two markers to start. 0 starts 404 * at the start marker, 1 starts at the stop marker. 405 * \param loops How many times to loop the animation. This will use the 406 * "loop start" and "loop stop" markers if they exist, 407 * otherwise it may fall back to "start" and "stop", but only if 408 * the \a loopFallback parameter is true. 409 * \param loopFallback Allow looping an animation that has no loop keys, i.e. fall back to use 410 * the "start" and "stop" keys for looping? 411 */ 412 void play(const std::string &groupname, const AnimPriority& priority, int blendMask, bool autodisable, 413 float speedmult, const std::string &start, const std::string &stop, 414 float startpoint, size_t loops, bool loopfallback=false); 415 416 /** Adjust the speed multiplier of an already playing animation. 417 */ 418 void adjustSpeedMult (const std::string& groupname, float speedmult); 419 420 /** Returns true if the named animation group is playing. */ 421 bool isPlaying(const std::string &groupname) const; 422 423 /// Returns true if no important animations are currently playing on the upper body. 424 bool upperBodyReady() const; 425 426 /** Gets info about the given animation group. 427 * \param groupname Animation group to check. 428 * \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc. 429 * \param speedmult Stores the animation speed multiplier 430 * \return True if the animation is active, false otherwise. 431 */ 432 bool getInfo(const std::string &groupname, float *complete=nullptr, float *speedmult=nullptr) const; 433 434 /// Get the absolute position in the animation track of the first text key with the given group. 435 float getStartTime(const std::string &groupname) const; 436 437 /// Get the absolute position in the animation track of the text key 438 float getTextKeyTime(const std::string &textKey) const; 439 440 /// Get the current absolute position in the animation track for the animation that is currently playing from the given group. 441 float getCurrentTime(const std::string& groupname) const; 442 443 size_t getCurrentLoopCount(const std::string& groupname) const; 444 445 /** Disables the specified animation group; 446 * \param groupname Animation group to disable. 447 */ 448 void disable(const std::string &groupname); 449 450 /** Retrieves the velocity (in units per second) that the animation will move. */ 451 float getVelocity(const std::string &groupname) const; 452 453 virtual osg::Vec3f runAnimation(float duration); 454 455 void setLoopingEnabled(const std::string &groupname, bool enabled); 456 457 /// This is typically called as part of runAnimation, but may be called manually if needed. 458 void updateEffects(); 459 460 /// Return a node with the specified name, or nullptr if not existing. 461 /// @note The matching is case-insensitive. 462 const osg::Node* getNode(const std::string& name) const; 463 useShieldAnimations() const464 virtual bool useShieldAnimations() const { return false; } showWeapons(bool showWeapon)465 virtual void showWeapons(bool showWeapon) {} getCarriedLeftShown() const466 virtual bool getCarriedLeftShown() const { return false; } showCarriedLeft(bool show)467 virtual void showCarriedLeft(bool show) {} setWeaponGroup(const std::string & group,bool relativeDuration)468 virtual void setWeaponGroup(const std::string& group, bool relativeDuration) {} setVampire(bool vampire)469 virtual void setVampire(bool vampire) {} 470 /// A value < 1 makes the animation translucent, 1.f = fully opaque 471 void setAlpha(float alpha); setPitchFactor(float factor)472 virtual void setPitchFactor(float factor) {} attachArrow()473 virtual void attachArrow() {} detachArrow()474 virtual void detachArrow() {} releaseArrow(float attackStrength)475 virtual void releaseArrow(float attackStrength) {} enableHeadAnimation(bool enable)476 virtual void enableHeadAnimation(bool enable) {} 477 // TODO: move outside of this class 478 /// Makes this object glow, by placing a Light in its center. 479 /// @param effect Controls the radius and intensity of the light. 480 virtual void setLightEffect(float effect); 481 482 virtual void setHeadPitch(float pitchRadians); 483 virtual void setHeadYaw(float yawRadians); 484 virtual float getHeadPitch() const; 485 virtual float getHeadYaw() const; 486 setUpperBodyYawRadians(float v)487 virtual void setUpperBodyYawRadians(float v) { mUpperBodyYawRadians = v; } setLegsYawRadians(float v)488 virtual void setLegsYawRadians(float v) { mLegsYawRadians = v; } getUpperBodyYawRadians() const489 virtual float getUpperBodyYawRadians() const { return mUpperBodyYawRadians; } getLegsYawRadians() const490 virtual float getLegsYawRadians() const { return mLegsYawRadians; } setBodyPitchRadians(float v)491 virtual void setBodyPitchRadians(float v) { mBodyPitchRadians = v; } getBodyPitchRadians() const492 virtual float getBodyPitchRadians() const { return mBodyPitchRadians; } 493 setAccurateAiming(bool enabled)494 virtual void setAccurateAiming(bool enabled) {} canBeHarvested() const495 virtual bool canBeHarvested() const { return false; } 496 497 private: 498 Animation(const Animation&); 499 void operator=(Animation&); 500 }; 501 502 class ObjectAnimation : public Animation { 503 public: 504 ObjectAnimation(const MWWorld::Ptr& ptr, const std::string &model, Resource::ResourceSystem* resourceSystem, bool animated, bool allowLight); 505 506 bool canBeHarvested() const override; 507 }; 508 509 class UpdateVfxCallback : public osg::NodeCallback 510 { 511 public: UpdateVfxCallback(EffectParams & params)512 UpdateVfxCallback(EffectParams& params) 513 : mFinished(false) 514 , mParams(params) 515 , mStartingTime(0) 516 { 517 } 518 519 bool mFinished; 520 EffectParams mParams; 521 522 void operator()(osg::Node* node, osg::NodeVisitor* nv) override; 523 524 private: 525 double mStartingTime; 526 }; 527 528 } 529 #endif 530