1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "BsCorePrerequisites.h"
6#include "CoreThread/BsCoreObject.h"
7#include "Resources/BsIResourceListener.h"
8#include "Utility/BsFlags.h"
9#include "Animation/BsSkeleton.h"
10#include "Animation/BsSkeletonMask.h"
11#include "Math/BsVector2.h"
12#include "Math/BsAABox.h"
13
14namespace bs
15{
16 /** @addtogroup Animation
17 * @{
18 */
19
20 /** Determines how an animation clip behaves when it reaches the end. */
21 enum class BS_SCRIPT_EXPORT(m:Animation) AnimWrapMode
22 {
23 Loop, /**< Loop around to the beginning/end when the last/first frame is reached. */
24 Clamp /**< Clamp to end/beginning, keeping the last/first frame active. */
25 };
26
27 /** Contains information about a currently playing animation clip. */
28 struct BS_SCRIPT_EXPORT(pl:true,m:Animation) AnimationClipState
29 {
30 AnimationClipState() = default;
31
32 /** Layer the clip is playing on. Multiple clips can be played simulatenously on different layers. */
33 UINT32 layer = 0;
34 float time = 0.0f; /**< Current time the animation is playing from. */
35 float speed = 1.0f; /**< Speed at which the animation is playing. */
36 float weight = 1.0f; /**< Determines how much of an influence does the clip have on the final pose. */
37 /** Determines what happens to other animation clips when a new clip starts playing. */
38 AnimWrapMode wrapMode = AnimWrapMode::Loop;
39 /**
40 * Determines should the time be advanced automatically. Certain type of animation clips don't involve playback
41 * (e.g. for blending where animation weight controls the animation).
42 */
43 bool stopped = false;
44 };
45
46 /** @} */
47
48 /** @addtogroup Animation-Internal
49 * @{
50 */
51
52 /** Flags that determine which portion of Animation was changed and needs to be updated. */
53 enum class AnimDirtyStateFlag
54 {
55 Clean = 0,
56 Value = 1 << 0,
57 Layout = 1 << 1,
58 All = 1 << 2,
59 Culling = 1 << 3,
60 MorphWeights = 1 << 4
61 };
62
63 typedef Flags<AnimDirtyStateFlag> AnimDirtyState;
64 BS_FLAGS_OPERATORS(AnimDirtyStateFlag)
65
66 /** Type of playback for animation clips. */
67 enum class AnimPlaybackType
68 {
69 /** Play back the animation normally by advancing time. */
70 Normal,
71 /** Sample only a single frame from the animation. */
72 Sampled,
73 /** Do not play the animation. */
74 None
75 };
76
77 /** Steps used for progressing through the animation when it is being sampled a single frame. */
78 enum class AnimSampleStep
79 {
80 /** No sample. Either no playback at all or normal playback. */
81 None,
82
83 /** Sample is being done this frame. */
84 Frame,
85
86 /** Sample has been performed some previous frame. */
87 Done
88 };
89
90 /** Internal information about a single playing animation clip within Animation. */
91 struct AnimationClipInfo
92 {
93 AnimationClipInfo() = default;
94 AnimationClipInfo(const HAnimationClip& clip);
95
96 HAnimationClip clip;
97 AnimationClipState state;
98 AnimPlaybackType playbackType = AnimPlaybackType::Normal;
99
100 float fadeDirection = 0.0f;
101 float fadeTime = 0.0f;
102 float fadeLength = 0.0f;
103
104 /**
105 * Version of the animation curves used by the AnimationProxy. Used to detecting the internal animation curves
106 * changed.
107 */
108 UINT64 curveVersion = 0;
109 UINT32 layerIdx = (UINT32)-1; /**< Layer index this clip belongs to in AnimationProxy structure. */
110 UINT32 stateIdx = (UINT32)-1; /**< State index this clip belongs to in AnimationProxy structure. */
111 };
112
113 /** Represents an animation clip used in 1D blending. Each clip has a position on the number line. */
114 struct BS_CORE_EXPORT BS_SCRIPT_EXPORT(pl:true,m:Animation) BlendClipInfo
115 {
116 BlendClipInfo() = default;
117
118 HAnimationClip clip;
119 float position = 0.0f;
120 };
121
122 /** Defines a 1D blend where multiple animation clips are blended between each other using linear interpolation. */
123 struct BS_CORE_EXPORT BS_SCRIPT_EXPORT(pl:true,m:Animation) Blend1DInfo
124 {
125 Vector<BlendClipInfo> clips;
126 };
127
128 /** Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation. */
129 struct BS_SCRIPT_EXPORT(pl:true,m:Animation) Blend2DInfo
130 {
131 HAnimationClip topLeftClip;
132 HAnimationClip topRightClip;
133 HAnimationClip botLeftClip;
134 HAnimationClip botRightClip;
135 };
136
137 /** Contains a mapping between a scene object and an animation curve it is animated with. */
138 struct AnimatedSceneObject
139 {
140 HSceneObject so;
141 String curveName;
142 };
143
144 /** Information about a set of morph shapes blended sequentially. */
145 struct MorphChannelInfo
146 {
147 float weight;
148 UINT32 shapeStart;
149 UINT32 shapeCount;
150
151 UINT32 frameCurveIdx;
152 UINT32 weightCurveIdx;
153 };
154
155 /** Morph shape and its contribution to the final shape. */
156 struct MorphShapeInfo
157 {
158 SPtr<MorphShape> shape;
159 float frameWeight;
160 float finalWeight;
161 };
162
163 /** Contains information about a scene object that is animated by a specific animation curve. */
164 struct AnimatedSceneObjectInfo
165 {
166 UINT64 id; /**< Instance ID of the scene object. */
167 INT32 boneIdx; /**< Bone from which to access the transform. If -1 then no bone mapping is present. */
168 INT32 layerIdx; /**< If no bone mapping, layer on which the animation containing the referenced curve is in. */
169 INT32 stateIdx; /**< If no bone mapping, animation state containing the referenced curve. */
170 AnimationCurveMapping curveIndices; /**< Indices of the curves used for the transform. */
171 UINT32 hash; /**< Hash value of the scene object's transform. */
172 };
173
174 /** Represents a copy of the Animation data for use specifically on the animation thread. */
175 struct AnimationProxy
176 {
177 AnimationProxy(UINT64 id);
178 AnimationProxy(const AnimationProxy&) = delete;
179 ~AnimationProxy();
180
181 AnimationProxy& operator=(const AnimationProxy&) = delete;
182
183 /**
184 * Rebuilds the internal proxy data according to the newly assigned skeleton and clips. This should be called
185 * whenever the animation skeleton changes.
186 *
187 * @param[in] skeleton New skeleton to assign to the proxy.
188 * @param[in] mask Mask that filters which skeleton bones are enabled or disabled.
189 * @param[in, out] clipInfos Potentially new clip infos that will be used for rebuilding the proxy. Once the
190 * method completes clip info layout and state indices will be populated for
191 * further use in the update*() methods.
192 * @param[in] sceneObjects A list of scene objects that are influenced by specific animation curves.
193 * @param[in] morphShapes Morph shapes used for per-vertex animation.
194 *
195 * @note Should be called from the sim thread when the caller is sure the animation thread is not using it.
196 */
197 void rebuild(const SPtr<Skeleton>& skeleton, const SkeletonMask& mask, Vector<AnimationClipInfo>& clipInfos,
198 const Vector<AnimatedSceneObject>& sceneObjects, const SPtr<MorphShapes>& morphShapes);
199
200 /**
201 * Rebuilds the internal proxy data according to the newly clips. This should be called whenever clips are added
202 * or removed, or clip layout indices change.
203 *
204 * @param[in, out] clipInfos New clip infos that will be used for rebuilding the proxy. Once the method
205 * completes clip info layout and state indices will be populated for further use
206 * in the update*() methods.
207 * @param[in] sceneObjects A list of scene objects that are influenced by specific animation curves.
208 * * @param[in] morphShapes Morph shapes used for per-vertex animation.
209 *
210 * @note Should be called from the sim thread when the caller is sure the animation thread is not using it.
211 */
212 void rebuild(Vector<AnimationClipInfo>& clipInfos, const Vector<AnimatedSceneObject>& sceneObjects,
213 const SPtr<MorphShapes>& morphShapes);
214
215 /**
216 * Updates the proxy data with new information about the clips. Caller must guarantee that clip layout didn't
217 * change since the last call to rebuild().
218 *
219 * @note Should be called from the sim thread when the caller is sure the animation thread is not using it.
220 */
221 void updateClipInfos(const Vector<AnimationClipInfo>& clipInfos);
222
223 /**
224 * Updates the proxy data with new weights used for morph shapes. Caller must ensure the weights are ordered so
225 * they match with the morph shapes provided to the last rebuild() call.
226 */
227 void updateMorphChannelWeights(const Vector<float>& weights);
228
229 /**
230 * Updates the proxy data with new scene object transforms. Caller must guarantee that clip layout didn't
231 * change since the last call to rebuild().
232 *
233 * @note Should be called from the sim thread when the caller is sure the animation thread is not using it.
234 */
235 void updateTransforms(const Vector<AnimatedSceneObject>& sceneObjects);
236
237 /**
238 * Updates the proxy data with new clip times. Caller must guarantee that clip layout didn't change since the last
239 * call to rebuild().
240 *
241 * @note Should be called from the sim thread when the caller is sure the animation thread is not using it.
242 */
243 void updateTime(const Vector<AnimationClipInfo>& clipInfos);
244
245 /** Destroys all dynamically allocated objects. */
246 void clear();
247
248 UINT64 id;
249
250 // Skeletal animation
251 AnimationStateLayer* layers = nullptr;
252 UINT32 numLayers = 0;
253 SPtr<Skeleton> skeleton;
254 SkeletonMask skeletonMask;
255 UINT32 numSceneObjects = 0;
256 AnimatedSceneObjectInfo* sceneObjectInfos = nullptr;
257 Matrix4* sceneObjectTransforms = nullptr;
258
259 // Morph shape animation
260 MorphChannelInfo* morphChannelInfos = nullptr;
261 MorphShapeInfo* morphShapeInfos = nullptr;
262 UINT32 numMorphChannels = 0;
263 UINT32 numMorphShapes = 0;
264 UINT32 numMorphVertices = 0;
265 bool morphChannelWeightsDirty = false;
266
267 // Culling
268 AABox mBounds;
269 bool mCullEnabled = true;
270
271 // Single frame sample
272 AnimSampleStep sampleStep = AnimSampleStep::None;
273
274 // Evaluation results
275 LocalSkeletonPose skeletonPose;
276 LocalSkeletonPose sceneObjectPose;
277 UINT32 numGenericCurves = 0;
278 float* genericCurveOutputs = nullptr;
279 bool wasCulled = false;
280 };
281
282 /**
283 * Handles animation playback. Takes one or multiple animation clips as input and evaluates them every animation update
284 * tick depending on set properties. The evaluated data is used by the core thread for skeletal animation, by the sim
285 * thread for updating attached scene objects and bones (if skeleton is attached), or the data is made available for
286 * manual queries in the case of generic animation.
287 */
288 class BS_CORE_EXPORT Animation : public CoreObject, public IResourceListener
289 {
290 public:
291 ~Animation();
292
293 /**
294 * Changes the skeleton which will the translation/rotation/scale animation values manipulate. If no skeleton is set
295 * the animation will only evaluate the generic curves, and the root translation/rotation/scale curves.
296 */
297 void setSkeleton(const SPtr<Skeleton>& skeleton);
298
299 /**
300 * Sets morph shapes that can be used for per-vertex blending animation. After they're set call
301 * setMorphShapeWeight() to apply morph shapes.
302 */
303 void setMorphShapes(const SPtr<MorphShapes>& morphShapes);
304
305 /**
306 * Changes a weight of a single morph channel, determining how much of it to apply on top of the base mesh.
307 *
308 * @param idx Index of the morph channel to modify. This must match the channels contained in the object
309 * provided to setMorphShapes().
310 * @param weight Weight that determines how much of the channel to apply to the mesh, in range [0, 1].
311 */
312 void setMorphChannelWeight(UINT32 idx, float weight);
313
314 /**
315 * Sets a mask that allows certain bones from the skeleton to be disabled. Caller must ensure that the mask matches
316 * the skeleton assigned to the animation.
317 */
318 void setMask(const SkeletonMask& mask);
319
320 /**
321 * Determines the wrap mode for all active animations. Wrap mode determines what happens when animation reaches the
322 * first or last frame.
323 */
324 void setWrapMode(AnimWrapMode wrapMode);
325
326 /** Determines the speed for all animations. The default value is 1.0f. Use negative values to play-back in reverse. */
327 void setSpeed(float speed);
328
329 /** Determines bounds that will be used for animation culling, if enabled. Bounds must be in world space. */
330 void setBounds(const AABox& bounds);
331
332 /** @copydoc setBounds */
333 const AABox& getBounds() const { return mBounds; }
334
335 /**
336 * When enabled, animation that is not in a view of any camera will not be evaluated. View determination is done by
337 * checking the bounds provided in setBounds().
338 */
339 void setCulling(bool cull);
340
341 /** @copydoc setCulling */
342 bool getCulling() const { return mCull; }
343
344 /**
345 * Plays the specified animation clip.
346 *
347 * @param[in] clip Clip to play.
348 */
349 void play(const HAnimationClip& clip);
350
351 /**
352 * Plays the specified animation clip on top of the animation currently playing in the main layer. Multiple
353 * such clips can be playing at once, as long as you ensure each is given its own layer. Each animation can
354 * also have a weight that determines how much it influences the main animation.
355 *
356 * @param[in] clip Clip to additively blend. Must contain additive animation curves.
357 * @param[in] weight Determines how much of an effect will the blended animation have on the final output.
358 * In range [0, 1].
359 * @param[in] fadeLength Applies the blend over a specified time period, increasing the weight as the time
360 * passes. Set to zero to blend immediately. In seconds.
361 * @param[in] layer Layer to play the clip in. Multiple additive clips can be playing at once in separate
362 * layers and each layer has its own weight.
363 */
364 void blendAdditive(const HAnimationClip& clip, float weight, float fadeLength = 0.0f, UINT32 layer = 0);
365
366 /**
367 * Blend multiple animation clips between each other using linear interpolation. Unlike normal animations these
368 * animations are not advanced with the progress of time, and is instead expected the user manually changes the
369 * @p t parameter.
370 *
371 * @param[in] info Information about the clips to blend. Clip positions must be sorted from lowest to highest.
372 * @param[in] t Parameter that controls the blending. Range depends on the positions of the provided
373 * animation clips.
374 */
375 void blend1D(const Blend1DInfo& info, float t);
376
377 /**
378 * Blend four animation clips between each other using bilinear interpolation. Unlike normal animations these
379 * animations are not advanced with the progress of time, and is instead expected the user manually changes the
380 * @p t parameter.
381 *
382 * @param[in] info Information about the clips to blend.
383 * @param[in] t Parameter that controls the blending, in range [(0, 0), (1, 1)]. t = (0, 0) means top left
384 * animation has full influence, t = (1, 0) means top right animation has full influence,
385 * t = (0, 1) means bottom left animation has full influence, t = (1, 1) means bottom right
386 * animation has full influence.
387 */
388 void blend2D(const Blend2DInfo& info, const Vector2& t);
389
390 /**
391 * Fades the specified animation clip in, while fading other playing animation out, over the specified time
392 * period.
393 *
394 * @param[in] clip Clip to fade in.
395 * @param[in] fadeLength Determines the time period over which the fade occurs. In seconds.
396 */
397 void crossFade(const HAnimationClip& clip, float fadeLength);
398
399 /**
400 * Samples an animation clip at the specified time, displaying only that particular frame without further playback.
401 *
402 * @param[in] clip Animation clip to sample.
403 * @param[in] time Time to sample the clip at.
404 */
405 void sample(const HAnimationClip& clip, float time);
406
407 /**
408 * Stops playing all animations on the provided layer. Specify -1 to stop animation on the main layer
409 * (non-additive animations).
410 */
411 void stop(UINT32 layer);
412
413 /** Stops playing all animations. */
414 void stopAll();
415
416 /** Checks if any animation clips are currently playing. */
417 bool isPlaying() const;
418
419 /** Returns the total number of animation clips influencing this animation. */
420 UINT32 getNumClips() const;
421
422 /**
423 * Returns one of the animation clips influencing this animation.
424 *
425 * @param[in] idx Sequential index of the animation clip to retrieve. In range [0, getNumClips()].
426 * @return Animation clip at the specified index, or null if the index is out of range.
427 */
428 HAnimationClip getClip(UINT32 idx) const;
429
430 /**
431 * Retrieves detailed information about a currently playing animation clip.
432 *
433 * @param[in] clip Clip to retrieve the information for.
434 * @param[out] state Animation clip state containing the requested information. Only valid if the method returns
435 * true.
436 * @return True if the state was found (animation clip is playing), false otherwise.
437 */
438 bool getState(const HAnimationClip& clip, AnimationClipState& state);
439
440 /**
441 * Changes the state of a playing animation clip. If animation clip is not currently playing the playback is started
442 * for the clip.
443 *
444 * @param[in] clip Clip to change the state for.
445 * @param[in] state New state of the animation (e.g. changing the time for seeking).
446 */
447 void setState(const HAnimationClip& clip, AnimationClipState state);
448
449 /**
450 * Ensures that any position/rotation/scale animation of a specific animation curve is transfered to the
451 * the provided scene object. Also allow the opposite operation which can allow scene object transform changes
452 * to manipulate object bones.
453 *
454 * @param[in] curve Name of the curve (bone) to connect the scene object with. Use empty string to map to the
455 * root bone, regardless of the bone name.
456 * @param[in] so Scene object to influence by the curve modifications, and vice versa.
457 */
458 void mapCurveToSceneObject(const String& curve, const HSceneObject& so);
459
460 /** Removes the curve <-> scene object mapping that was set via mapCurveToSceneObject(). */
461 void unmapSceneObject(const HSceneObject& so);
462
463 /**
464 * Retrieves an evaluated value for a generic curve with the specified index.
465 *
466 * @param[in] curveIdx The curve index referencing a set of curves from the first playing animation clip.
467 * Generic curves from all other clips are ignored.
468 * @param[out] value Value of the generic curve. Only valid if the method return true.
469 * @return True if the value was retrieved successfully. The method might fail if animation update
470 * didn't yet have a chance to execute and values are not yet available, or if the
471 * animation clip changed since the last frame (the last problem can be avoided by ensuring
472 * to read the curve values before changing the clip).
473 */
474 bool getGenericCurveValue(UINT32 curveIdx, float& value);
475
476 /** Creates a new empty Animation object. */
477 static SPtr<Animation> create();
478
479 /** Triggered whenever an animation event is reached. */
480 Event<void(const HAnimationClip&, const String&)> onEventTriggered;
481
482 /** @name Internal
483 * @{
484 */
485
486 /** Returns the unique ID for this animation object. */
487 UINT64 _getId() const { return mId; }
488
489 /** Checks if any currently set animation clips perform animation of the root bone. */
490 bool _getAnimatesRoot() const;
491
492 /** @} */
493 private:
494 friend class AnimationManager;
495
496 Animation();
497
498 /**
499 * Triggers any events between the last frame and current one.
500 *
501 * @param[in] delta Time elapsed since the last call to this method.
502 */
503 void triggerEvents(float delta);
504
505 /**
506 * Updates the animation proxy object based on the currently set skeleton, playing clips and dirty flags.
507 *
508 * @param[in] timeDelta Seconds passed since the last call to this method.
509 */
510 void updateAnimProxy(float timeDelta);
511
512 /**
513 * Applies any outputs stored in the animation proxy (as written by the animation thread), and uses them to update
514 * the animation state on the simulation thread. Caller must ensure that the animation thread has finished
515 * with the animation proxy.
516 */
517 void updateFromProxy();
518
519 /**
520 * Registers a new animation in the specified layer, or returns an existing animation clip info if the animation is
521 * already registered. If @p stopExisting is true any existing animations in the layer will be stopped. Layout
522 * will be marked as dirty if any changes were made.
523 */
524 AnimationClipInfo* addClip(const HAnimationClip& clip, UINT32 layer, bool stopExisting = true);
525
526 /** @copydoc IResourceListener::getListenerResources */
527 void getListenerResources(Vector<HResource>& resources) override;
528
529 /** @copydoc IResourceListener::notifyResourceLoaded */
530 void notifyResourceLoaded(const HResource& resource) override;
531
532 /** @copydoc IResourceListener::notifyResourceChanged */
533 void notifyResourceChanged(const HResource& resource) override;
534
535 UINT64 mId;
536 AnimWrapMode mDefaultWrapMode = AnimWrapMode::Loop;
537 float mDefaultSpeed = 1.0f;
538 AABox mBounds;
539 bool mCull = true;
540 AnimDirtyState mDirty = AnimDirtyStateFlag::All;
541
542 SPtr<Skeleton> mSkeleton;
543 SkeletonMask mSkeletonMask;
544 SPtr<MorphShapes> mMorphShapes;
545 Vector<float> mMorphChannelWeights;
546 Vector<AnimationClipInfo> mClipInfos;
547 UnorderedMap<UINT64, AnimatedSceneObject> mSceneObjects;
548 Vector<float> mGenericCurveOutputs;
549 bool mGenericCurveValuesValid = false;
550 AnimSampleStep mSampleStep = AnimSampleStep::None;
551
552 // Animation thread only
553 SPtr<AnimationProxy> mAnimProxy;
554 };
555
556 /** @} */
557}