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#include "Animation/BsSkeleton.h"
4#include "Animation/BsAnimationClip.h"
5#include "Animation/BsSkeletonMask.h"
6#include "Private/RTTI/BsSkeletonRTTI.h"
7
8namespace bs
9{
10 LocalSkeletonPose::LocalSkeletonPose(UINT32 numBones, bool individualOverride)
11 : numBones(numBones)
12 {
13 const UINT32 overridesPerBone = individualOverride ? 3 : 1;
14
15 UINT32 elementSize = sizeof(Vector3) * 2 + sizeof(Quaternion) + sizeof(bool) * overridesPerBone;
16 UINT8* buffer = (UINT8*)bs_alloc(elementSize * numBones);
17
18 positions = (Vector3*)buffer;
19 buffer += sizeof(Vector3) * numBones;
20
21 rotations = (Quaternion*)buffer;
22 buffer += sizeof(Quaternion) * numBones;
23
24 scales = (Vector3*)buffer;
25 buffer += sizeof(Vector3) * numBones;
26
27 hasOverride = (bool*)buffer;
28 }
29
30 LocalSkeletonPose::LocalSkeletonPose(UINT32 numPos, UINT32 numRot, UINT32 numScale)
31 {
32 UINT32 bufferSize = sizeof(Vector3) * numPos + sizeof(Quaternion) * numRot + sizeof(Vector3) * numScale;
33 UINT8* buffer = (UINT8*)bs_alloc(bufferSize);
34
35 positions = (Vector3*)buffer;
36 buffer += sizeof(Vector3) * numPos;
37
38 rotations = (Quaternion*)buffer;
39 buffer += sizeof(Quaternion) * numRot;
40
41 scales = (Vector3*)buffer;
42 }
43
44 LocalSkeletonPose::LocalSkeletonPose(LocalSkeletonPose&& other)
45 : positions{std::exchange(other.positions, nullptr)}
46 , rotations{std::exchange(other.rotations, nullptr)}
47 , scales{std::exchange(other.scales, nullptr)}
48 , hasOverride{std::exchange(other.hasOverride, nullptr)}
49 , numBones(std::exchange(other.numBones, 0))
50 { }
51
52 LocalSkeletonPose::~LocalSkeletonPose()
53 {
54 if (positions != nullptr)
55 bs_free(positions);
56 }
57
58 LocalSkeletonPose& LocalSkeletonPose::operator=(LocalSkeletonPose&& other)
59 {
60 if (this != &other)
61 {
62 if (positions != nullptr)
63 bs_free(positions);
64
65 positions = std::exchange(other.positions, nullptr);
66 rotations = std::exchange(other.rotations, nullptr);
67 scales = std::exchange(other.scales, nullptr);
68 hasOverride = std::exchange(other.hasOverride, nullptr);
69 numBones = std::exchange(other.numBones, 0);
70 }
71
72 return *this;
73 }
74
75 Skeleton::Skeleton(BONE_DESC* bones, UINT32 numBones)
76 : mNumBones(numBones), mBoneTransforms(bs_newN<Transform>(numBones)), mInvBindPoses(bs_newN<Matrix4>(numBones))
77 , mBoneInfo(bs_newN<SkeletonBoneInfo>(numBones))
78 {
79 for(UINT32 i = 0; i < numBones; i++)
80 {
81 mBoneTransforms[i] = bones[i].localTfrm;
82 mInvBindPoses[i] = bones[i].invBindPose;
83 mBoneInfo[i].name = bones[i].name;
84 mBoneInfo[i].parent = bones[i].parent;
85 }
86 }
87
88 Skeleton::~Skeleton()
89 {
90 if(mBoneTransforms != nullptr)
91 bs_deleteN(mBoneTransforms, mNumBones);
92
93 if(mInvBindPoses != nullptr)
94 bs_deleteN(mInvBindPoses, mNumBones);
95
96 if (mBoneInfo != nullptr)
97 bs_deleteN(mBoneInfo, mNumBones);
98 }
99
100 SPtr<Skeleton> Skeleton::create(BONE_DESC* bones, UINT32 numBones)
101 {
102 Skeleton* rawPtr = new (bs_alloc<Skeleton>()) Skeleton(bones, numBones);
103
104 return bs_shared_ptr<Skeleton>(rawPtr);
105 }
106
107 void Skeleton::getPose(Matrix4* pose, LocalSkeletonPose& localPose, const SkeletonMask& mask,
108 const AnimationClip& clip, float time, bool loop)
109 {
110 bs_frame_mark();
111 {
112 FrameVector<AnimationCurveMapping> boneToCurveMapping(mNumBones);
113
114 AnimationState state;
115 state.curves = clip.getCurves();
116 state.boneToCurveMapping = boneToCurveMapping.data();
117 state.loop = loop;
118 state.weight = 1.0f;
119 state.time = time;
120
121 FrameVector<TCurveCache<Vector3>> positionCache(state.curves->position.size());
122 FrameVector<TCurveCache<Quaternion>> rotationCache(state.curves->rotation.size());
123 FrameVector<TCurveCache<Vector3>> scaleCache(state.curves->scale.size());
124
125 state.positionCaches = positionCache.data();
126 state.rotationCaches = rotationCache.data();
127 state.scaleCaches = scaleCache.data();
128 state.genericCaches = nullptr;
129 state.disabled = false;
130
131 AnimationStateLayer layer;
132 layer.index = 0;
133 layer.additive = false;
134 layer.states = &state;
135 layer.numStates = 1;
136
137 clip.getBoneMapping(*this, state.boneToCurveMapping);
138
139 getPose(pose, localPose, mask, &layer, 1);
140 }
141 bs_frame_clear();
142 }
143
144 void Skeleton::getPose(Matrix4* pose, LocalSkeletonPose& localPose, const SkeletonMask& mask,
145 const AnimationStateLayer* layers, UINT32 numLayers)
146 {
147 // Note: If more performance is required this method could be optimized with vector instructions
148
149 assert(localPose.numBones == mNumBones);
150
151 for(UINT32 i = 0; i < mNumBones; i++)
152 {
153 localPose.positions[i] = Vector3::ZERO;
154 localPose.rotations[i] = Quaternion::ZERO;
155 localPose.scales[i] = Vector3::ONE;
156 }
157
158 bool* hasAnimCurve = bs_stack_alloc<bool>(mNumBones);
159 bs_zero_out(hasAnimCurve, mNumBones);
160
161 // Note: For a possible performance improvement consider keeping an array of only active (non-disabled) bones and
162 // just iterate over them without mask checks. Possibly also a list of active curve mappings to avoid those checks
163 // as well.
164 for(UINT32 i = 0; i < numLayers; i++)
165 {
166 const AnimationStateLayer& layer = layers[i];
167
168 float invLayerWeight;
169 if (layer.additive)
170 {
171 float weightSum = 0.0f;
172 for (UINT32 j = 0; j < layer.numStates; j++)
173 weightSum += layer.states[j].weight;
174
175 invLayerWeight = 1.0f / weightSum;
176 }
177 else
178 invLayerWeight = 1.0f;
179
180 for (UINT32 j = 0; j < layer.numStates; j++)
181 {
182 const AnimationState& state = layer.states[j];
183 if (state.disabled)
184 continue;
185
186 float normWeight = state.weight * invLayerWeight;
187
188 // Early exit for clips that don't contribute (which there could be plenty especially for sequential blends)
189 if (Math::approxEquals(normWeight, 0.0f))
190 continue;
191
192 for (UINT32 k = 0; k < mNumBones; k++)
193 {
194 if (!mask.isEnabled(k))
195 continue;
196
197 const AnimationCurveMapping& mapping = state.boneToCurveMapping[k];
198 UINT32 curveIdx = mapping.position;
199 if (curveIdx != (UINT32)-1)
200 {
201 const TAnimationCurve<Vector3>& curve = state.curves->position[curveIdx].curve;
202 localPose.positions[k] += curve.evaluate(state.time, state.positionCaches[curveIdx], state.loop) * normWeight;
203
204 localPose.hasOverride[k] = false;
205 hasAnimCurve[k] = true;
206 }
207
208 curveIdx = mapping.scale;
209 if (curveIdx != (UINT32)-1)
210 {
211 const TAnimationCurve<Vector3>& curve = state.curves->scale[curveIdx].curve;
212 localPose.scales[k] *= curve.evaluate(state.time, state.scaleCaches[curveIdx], state.loop) * normWeight;
213
214 localPose.hasOverride[k] = false;
215 hasAnimCurve[k] = true;
216 }
217
218 if (layer.additive)
219 {
220 curveIdx = mapping.rotation;
221 if (curveIdx != (UINT32)-1)
222 {
223 bool isAssigned = localPose.rotations[k].w != 0.0f;
224 if (!isAssigned)
225 localPose.rotations[k] = Quaternion::IDENTITY;
226
227 const TAnimationCurve<Quaternion>& curve = state.curves->rotation[curveIdx].curve;
228
229 Quaternion value = curve.evaluate(state.time, state.rotationCaches[curveIdx], state.loop);
230 value = Quaternion::lerp(normWeight, Quaternion::IDENTITY, value);
231
232 localPose.rotations[k] *= value;
233 localPose.hasOverride[k] = false;
234 hasAnimCurve[k] = true;
235 }
236 }
237 else
238 {
239 curveIdx = mapping.rotation;
240 if (curveIdx != (UINT32)-1)
241 {
242 const TAnimationCurve<Quaternion>& curve = state.curves->rotation[curveIdx].curve;
243 Quaternion value = curve.evaluate(state.time, state.rotationCaches[curveIdx], state.loop) * normWeight;
244
245 if (value.dot(localPose.rotations[k]) < 0.0f)
246 value = -value;
247
248 localPose.rotations[k] += value;
249 localPose.hasOverride[k] = false;
250 hasAnimCurve[k] = true;
251 }
252 }
253 }
254 }
255 }
256
257 // Apply default local tranform to non-animated bones (so that any potential child bones are transformed properly)
258 for(UINT32 i = 0; i < mNumBones; i++)
259 {
260 if(hasAnimCurve[i])
261 continue;
262
263 localPose.positions[i] = mBoneTransforms[i].getPosition();
264 localPose.rotations[i] = mBoneTransforms[i].getRotation();
265 localPose.scales[i] = mBoneTransforms[i].getScale();
266 }
267
268 // Calculate local pose matrices
269 UINT32 isGlobalBytes = sizeof(bool) * mNumBones;
270 bool* isGlobal = (bool*)bs_stack_alloc(isGlobalBytes);
271 memset(isGlobal, 0, isGlobalBytes);
272
273 for(UINT32 i = 0; i < mNumBones; i++)
274 {
275 bool isAssigned = localPose.rotations[i].w != 0.0f;
276 if (!isAssigned)
277 localPose.rotations[i] = Quaternion::IDENTITY;
278 else
279 localPose.rotations[i].normalize();
280
281 if (localPose.hasOverride[i])
282 {
283 isGlobal[i] = true;
284 continue;
285 }
286
287 pose[i] = Matrix4::TRS(localPose.positions[i], localPose.rotations[i], localPose.scales[i]);
288 }
289
290 // Calculate global poses
291 // Note: For a possible performance improvement consider sorting bones in such order so that parents (and overrides)
292 // always come before children, we no isGlobal check is needed.
293 std::function<void(UINT32)> calcGlobal = [&](UINT32 boneIdx)
294 {
295 UINT32 parentBoneIdx = mBoneInfo[boneIdx].parent;
296 if (parentBoneIdx == (UINT32)-1)
297 {
298 isGlobal[boneIdx] = true;
299 return;
300 }
301
302 if (!isGlobal[parentBoneIdx])
303 calcGlobal(parentBoneIdx);
304
305 pose[boneIdx] = pose[parentBoneIdx] * pose[boneIdx];
306 isGlobal[boneIdx] = true;
307 };
308
309 for (UINT32 i = 0; i < mNumBones; i++)
310 {
311 if (!isGlobal[i])
312 calcGlobal(i);
313 }
314
315 for (UINT32 i = 0; i < mNumBones; i++)
316 pose[i] = pose[i] * mInvBindPoses[i];
317
318 bs_stack_free(isGlobal);
319 bs_stack_free(hasAnimCurve);
320 }
321
322 Transform Skeleton::calcBoneTransform(UINT32 idx) const
323 {
324 if(idx >= mNumBones)
325 return Transform::IDENTITY;
326
327 Transform output = mBoneTransforms[idx];
328
329 UINT32 parentIdx = mBoneInfo[idx].parent;
330 while(parentIdx != (UINT32)-1)
331 {
332 output.makeWorld(mBoneTransforms[parentIdx]);
333
334 parentIdx = mBoneInfo[parentIdx].parent;
335 }
336
337 return output;
338 }
339
340 UINT32 Skeleton::getRootBoneIndex() const
341 {
342 for (UINT32 i = 0; i < mNumBones; i++)
343 {
344 if (mBoneInfo[i].parent == (UINT32)-1)
345 return i;
346 }
347
348 return (UINT32)-1;
349 }
350
351 SPtr<Skeleton> Skeleton::createEmpty()
352 {
353 Skeleton* rawPtr = new (bs_alloc<Skeleton>()) Skeleton();
354
355 SPtr<Skeleton> newSkeleton = bs_shared_ptr<Skeleton>(rawPtr);
356 return newSkeleton;
357 }
358
359 RTTITypeBase* Skeleton::getRTTIStatic()
360 {
361 return SkeletonRTTI::instance();
362 }
363
364 RTTITypeBase* Skeleton::getRTTI() const
365 {
366 return getRTTIStatic();
367 }
368}