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 "Resources/BsResource.h"
7#include "Math/BsVector3.h"
8#include "Math/BsQuaternion.h"
9#include "Animation/BsAnimationCurve.h"
10#include <array>
11
12namespace bs
13{
14 /** @addtogroup Animation
15 * @{
16 */
17
18 struct AnimationCurveMapping;
19
20 /** A set of animation curves representing translation/rotation/scale and generic animation. */
21 struct BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Animation) AnimationCurves
22 {
23 BS_SCRIPT_EXPORT()
24 AnimationCurves() = default;
25
26 /**
27 * Registers a new curve used for animating position.
28 *
29 * @param[in] name Unique name of the curve. This name will be used mapping the curve to the relevant bone
30 * in a skeleton, if any.
31 * @param[in] curve Curve to add to the clip.
32 */
33 BS_SCRIPT_EXPORT(n:AddPositionCurve)
34 void addPositionCurve(const String& name, const TAnimationCurve<Vector3>& curve);
35
36 /**
37 * Registers a new curve used for animating rotation.
38 *
39 * @param[in] name Unique name of the curve. This name will be used mapping the curve to the relevant bone
40 * in a skeleton, if any.
41 * @param[in] curve Curve to add to the clip.
42 */
43 BS_SCRIPT_EXPORT(n:AddRotationCurve)
44 void addRotationCurve(const String& name, const TAnimationCurve<Quaternion>& curve);
45
46 /**
47 * Registers a new curve used for animating scale.
48 *
49 * @param[in] name Unique name of the curve. This name will be used mapping the curve to the relevant bone
50 * in a skeleton, if any.
51 * @param[in] curve Curve to add to the clip.
52 */
53 BS_SCRIPT_EXPORT(n:AddScaleCurve)
54 void addScaleCurve(const String& name, const TAnimationCurve<Vector3>& curve);
55
56 /**
57 * Registers a new curve used for generic animation.
58 *
59 * @param[in] name Unique name of the curve. This can be used for retrieving the value of the curve
60 * from animation.
61 * @param[in] curve Curve to add to the clip.
62 */
63 BS_SCRIPT_EXPORT(n:AddGenericCurve)
64 void addGenericCurve(const String& name, const TAnimationCurve<float>& curve);
65
66 /** Removes an existing curve from the clip. */
67 BS_SCRIPT_EXPORT(n:RemovePositionCurve)
68 void removePositionCurve(const String& name);
69
70 /** Removes an existing curve from the clip. */
71 BS_SCRIPT_EXPORT(n:RemoveRotationCurve)
72 void removeRotationCurve(const String& name);
73
74 /** Removes an existing curve from the clip. */
75 BS_SCRIPT_EXPORT(n:RemoveScaleCurve)
76 void removeScaleCurve(const String& name);
77
78 /** Removes an existing curve from the clip. */
79 BS_SCRIPT_EXPORT(n:RemoveGenericCurve)
80 void removeGenericCurve(const String& name);
81
82 /** Curves for animating scene object's position. */
83 Vector<TNamedAnimationCurve<Vector3>> position;
84
85 /** Curves for animating scene object's rotation. */
86 Vector<TNamedAnimationCurve<Quaternion>> rotation;
87
88 /** Curves for animating scene object's scale. */
89 Vector<TNamedAnimationCurve<Vector3>> scale;
90
91 /** Curves for animating generic component properties. */
92 Vector<TNamedAnimationCurve<float>> generic;
93 };
94
95 /** Contains a set of animation curves used for moving and rotating the root bone. */
96 struct BS_SCRIPT_EXPORT(m:Animation) RootMotion
97 {
98 RootMotion() = default;
99 RootMotion(const TAnimationCurve<Vector3>& position, const TAnimationCurve<Quaternion>& rotation)
100 :position(position), rotation(rotation)
101 { }
102
103 /** Animation curve representing the movement of the root bone. */
104 TAnimationCurve<Vector3> position;
105
106 /** Animation curve representing the rotation of the root bone. */
107 TAnimationCurve<Quaternion> rotation;
108 };
109
110 /** Event that is triggered when animation reaches a certain point. */
111 struct BS_SCRIPT_EXPORT(m:Animation,pl:true) AnimationEvent
112 {
113 AnimationEvent() = default;
114
115 /**
116 * Constructs a new animation event.
117 *
118 * @param[in] name Name used to identify the event when triggered.
119 * @param[in] time Time at which to trigger the event, in seconds.
120 */
121 AnimationEvent(const String& name, float time)
122 :name(name), time(time)
123 { }
124
125 /** Name used to identify the event when triggered. */
126 String name;
127
128 /** Time at which to trigger the event, in seconds. */
129 float time = 0.0f;
130 };
131
132 /** Types of curves in an AnimationClip. */
133 enum class CurveType
134 {
135 Position,
136 Rotation,
137 Scale,
138 Generic,
139 MorphFrame,
140 MorphWeight,
141 Count // Keep at end
142 };
143
144 /**
145 * Contains animation curves for translation/rotation/scale of scene objects/skeleton bones, as well as curves for
146 * generic property animation.
147 */
148 class BS_CORE_EXPORT BS_SCRIPT_EXPORT(m:Animation) AnimationClip : public Resource
149 {
150 public:
151 virtual ~AnimationClip() = default;
152
153 /** @copydoc setCurves() */
154 BS_SCRIPT_EXPORT(n:Curves,pr:getter)
155 SPtr<AnimationCurves> getCurves() const { return mCurves; }
156
157 /**
158 * A set of all curves stored in the animation. Returned value will not be updated if the animation clip curves are
159 * added or removed, as it is a copy of clip's internal values.
160 */
161 BS_SCRIPT_EXPORT(n:Curves,pr:setter)
162 void setCurves(const AnimationCurves& curves);
163
164 /** @copydoc setEvents() */
165 BS_SCRIPT_EXPORT(n:Events,pr:getter)
166 const Vector<AnimationEvent>& getEvents() const { return mEvents; }
167
168 /** A set of all events to be triggered as the animation is playing. */
169 BS_SCRIPT_EXPORT(n:Events,pr:setter)
170 void setEvents(const Vector<AnimationEvent>& events) { mEvents = events; }
171
172 /**
173 * Returns a set of curves containing motion of the root bone. This allows the user to evaluate the root bone
174 * animation curves manually, instead of through the normal animation process. This property is only available
175 * if animation clip was imported with root motion import enabled.
176 */
177 BS_SCRIPT_EXPORT(n:RootMotion,pr:getter)
178 SPtr<RootMotion> getRootMotion() const { return mRootMotion; }
179
180 /** Checks if animation clip has root motion curves separate from the normal animation curves. */
181 BS_SCRIPT_EXPORT(n:HasRootMotion,pr:getter)
182 bool hasRootMotion() const;
183
184 /**
185 * Maps skeleton bone names to animation curve names, and returns a set of indices that can be easily used for
186 * locating an animation curve based on the bone index.
187 *
188 * @param[in] skeleton Skeleton to create the mapping for.
189 * @param[out] mapping Pre-allocated array that will receive output animation clip indices. The array must
190 * be large enough to store an index for every bone in the @p skeleton. Bones that have
191 * no related animation curves will be assigned value -1.
192 */
193 void getBoneMapping(const Skeleton& skeleton, AnimationCurveMapping* mapping) const;
194
195 /**
196 * Attempts to find translation/rotation/scale curves with the specified name and fills the mapping structure with
197 * their indices, which can then be used for quick lookup.
198 *
199 * @param[in] name Name of the curves to look up.
200 * @param[out] mapping Triple containing the translation/rotation/scale indices of the found curves. Indices
201 * will be -1 for curves that haven't been found.
202 */
203 void getCurveMapping(const String& name, AnimationCurveMapping& mapping) const;
204
205 /**
206 * Attempts to find a generic curve with the specified name and fills output with found index, which can then be
207 * used for quick lookup.
208 *
209 * @param[in] name Name of the curve to look up.
210 * @param[out] frameIdx Index of the curve animating the morph shape frames, or -1 if not found.
211 * @param[out] weightIdx Index of the curve animating the channel weight, or -1 if not found.
212 */
213 void getMorphMapping(const String& name, UINT32& frameIdx, UINT32& weightIdx) const;
214
215 /**
216 * Checks are the curves contained within the clip additive. Additive clips are intended to be added on top of
217 * other clips.
218 */
219 BS_SCRIPT_EXPORT(n:IsAddtive,pr:getter)
220 bool isAdditive() const { return mIsAdditive; }
221
222 /** Returns the length of the animation clip, in seconds. */
223 BS_SCRIPT_EXPORT(n:Length,pr:getter)
224 float getLength() const { return mLength; }
225
226 /** @copydoc setSampleRate() */
227 BS_SCRIPT_EXPORT(n:SampleRate,pr:getter)
228 UINT32 getSampleRate() const { return mSampleRate; }
229
230 /**
231 * Number of samples per second the animation clip curves were sampled at. This value is not used by the animation
232 * clip or curves directly since unevenly spaced keyframes are supported. But it can be of value when determining
233 * the original sample rate of an imported animation or similar.
234 */
235 BS_SCRIPT_EXPORT(n:SampleRate,pr:setter)
236 void setSampleRate(UINT32 sampleRate) { mSampleRate = sampleRate; }
237
238 /**
239 * Returns a version that can be used for detecting modifications on the clip by external systems. Whenever the clip
240 * is modified the version is increased by one.
241 */
242 UINT64 getVersion() const { return mVersion; }
243
244 /**
245 * Creates an animation clip with no curves. After creation make sure to register some animation curves before
246 * using it.
247 */
248 BS_SCRIPT_EXPORT(ec:AnimationClip)
249 static HAnimationClip create(bool isAdditive = false);
250
251 /**
252 * Creates an animation clip with specified curves.
253 *
254 * @param[in] curves Curves to initialize the animation with.
255 * @param[in] isAdditive Determines does the clip contain additive curve data. This will change the behaviour
256 * how is the clip blended with other animations.
257 * @param[in] sampleRate If animation uses evenly spaced keyframes, number of samples per second. Not relevant
258 * if keyframes are unevenly spaced.
259 * @param[in] rootMotion Optional set of curves that can be used for animating the root bone. Not used by the
260 * animation system directly but is instead provided to the user for manual evaluation.
261 */
262 BS_SCRIPT_EXPORT(ec:AnimationClip)
263 static HAnimationClip create(const SPtr<AnimationCurves>& curves, bool isAdditive = false, UINT32 sampleRate = 1,
264 const SPtr<RootMotion>& rootMotion = nullptr);
265
266 public: // ***** INTERNAL ******
267 /** @name Internal
268 * @{
269 */
270
271 /** Creates a new AnimationClip without initializing it. Use create() for normal use. */
272 static SPtr<AnimationClip> _createPtr(const SPtr<AnimationCurves>& curves, bool isAdditive = false,
273 UINT32 sampleRate = 1, const SPtr<RootMotion>& rootMotion = nullptr);
274
275 /** @} */
276
277 protected:
278 AnimationClip();
279 AnimationClip(const SPtr<AnimationCurves>& curves, bool isAdditive, UINT32 sampleRate,
280 const SPtr<RootMotion>& rootMotion);
281
282 /** @copydoc Resource::initialize() */
283 void initialize() override;
284
285 /** Creates a name -> curve index mapping for quicker curve lookup by name. */
286 void buildNameMapping();
287
288 /** Calculate the length of the clip based on assigned curves. */
289 void calculateLength();
290
291 UINT64 mVersion;
292
293 /**
294 * Contains all the animation curves in the clip. It's important this field is immutable so it may be used on other
295 * threads. This means any modifications to the field will require a brand new data structure to be generated and
296 * all existing data copied (plus the modification).
297 */
298 SPtr<AnimationCurves> mCurves;
299
300 /**
301 * A set of curves containing motion of the root bone. If this is non-empty it should be true that mCurves does not
302 * contain animation curves for the root bone. Root motion will not be evaluated through normal animation process
303 * but is instead provided for the user for manual evaluation.
304 */
305 SPtr<RootMotion> mRootMotion;
306
307 /**
308 * Contains a map from curve name to curve index. Indices are stored as specified in CurveType enum.
309 */
310 UnorderedMap<String, std::array<UINT32, (int)CurveType::Count>> mNameMapping;
311
312 Vector<AnimationEvent> mEvents;
313 bool mIsAdditive;
314 float mLength;
315 UINT32 mSampleRate;
316
317 /************************************************************************/
318 /* SERIALIZATION */
319 /************************************************************************/
320 public:
321 friend class AnimationClipRTTI;
322 static RTTITypeBase* getRTTIStatic();
323 RTTITypeBase* getRTTI() const override;
324
325 /**
326 * Creates an AnimationClip with no data. You must populate its data manually followed by a call to initialize().
327 *
328 * @note For serialization use only.
329 */
330 static SPtr<AnimationClip> createEmpty();
331 };
332
333 /** @} */
334}