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 | |
8 | namespace 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 | } |