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 "Components/BsCAnimation.h"
4#include "Scene/BsSceneObject.h"
5#include "Components/BsCRenderable.h"
6#include "Components/BsCBone.h"
7#include "Mesh/BsMesh.h"
8#include "Animation/BsMorphShapes.h"
9#include "Animation/BsAnimationClip.h"
10#include "Private/RTTI/BsCAnimationRTTI.h"
11#include "Scene/BsSceneManager.h"
12
13using namespace std::placeholders;
14
15namespace bs
16{
17 CAnimation::CAnimation()
18 {
19 mNotifyFlags = TCF_Transform;
20 setFlag(ComponentFlag::AlwaysRun, true);
21
22 setName("Animation");
23 }
24
25 CAnimation::CAnimation(const HSceneObject& parent)
26 : Component(parent)
27 {
28 mNotifyFlags = TCF_Transform;
29 setFlag(ComponentFlag::AlwaysRun, true);
30
31 setName("Animation");
32 }
33
34 void CAnimation::setDefaultClip(const HAnimationClip& clip)
35 {
36 mDefaultClip = clip;
37
38 if(clip.isLoaded() && mInternal != nullptr && !mPreviewMode)
39 mInternal->play(clip);
40 }
41
42 void CAnimation::setWrapMode(AnimWrapMode wrapMode)
43 {
44 mWrapMode = wrapMode;
45
46 if (mInternal != nullptr && !mPreviewMode)
47 mInternal->setWrapMode(wrapMode);
48 }
49
50 void CAnimation::setSpeed(float speed)
51 {
52 mSpeed = speed;
53
54 if (mInternal != nullptr && !mPreviewMode)
55 mInternal->setSpeed(speed);
56 }
57
58 void CAnimation::play(const HAnimationClip& clip)
59 {
60 if (mInternal != nullptr && !mPreviewMode)
61 mInternal->play(clip);
62 }
63
64 void CAnimation::blendAdditive(const HAnimationClip& clip, float weight, float fadeLength, UINT32 layer)
65 {
66 if (mInternal != nullptr && !mPreviewMode)
67 mInternal->blendAdditive(clip, weight, fadeLength, layer);
68 }
69
70 void CAnimation::blend1D(const Blend1DInfo& info, float t)
71 {
72 if (mInternal != nullptr && !mPreviewMode)
73 mInternal->blend1D(info, t);
74 }
75
76 void CAnimation::blend2D(const Blend2DInfo& info, const Vector2& t)
77 {
78 if (mInternal != nullptr && !mPreviewMode)
79 mInternal->blend2D(info, t);
80 }
81
82 void CAnimation::crossFade(const HAnimationClip& clip, float fadeLength)
83 {
84 if (mInternal != nullptr && !mPreviewMode)
85 mInternal->crossFade(clip, fadeLength);
86 }
87
88 void CAnimation::sample(const HAnimationClip& clip, float time)
89 {
90 if (mInternal != nullptr)
91 mInternal->sample(clip, time);
92 }
93
94 void CAnimation::stop(UINT32 layer)
95 {
96 if (mInternal != nullptr && !mPreviewMode)
97 mInternal->stop(layer);
98 }
99
100 void CAnimation::stopAll()
101 {
102 if (mInternal != nullptr && !mPreviewMode)
103 mInternal->stopAll();
104 }
105
106 bool CAnimation::isPlaying() const
107 {
108 if (mInternal != nullptr)
109 return mInternal->isPlaying();
110
111 return false;
112 }
113
114 bool CAnimation::getState(const HAnimationClip& clip, AnimationClipState& state)
115 {
116 if (mInternal != nullptr)
117 return mInternal->getState(clip, state);
118
119 return false;
120 }
121
122 void CAnimation::setState(const HAnimationClip& clip, AnimationClipState state)
123 {
124 if (mInternal != nullptr)
125 return mInternal->setState(clip, state);
126 }
127
128 void CAnimation::setMorphChannelWeight(const String& name, float weight)
129 {
130 if (mInternal == nullptr)
131 return;
132
133 if (mAnimatedRenderable == nullptr)
134 return;
135
136 HMesh mesh = mAnimatedRenderable->getMesh();
137 if (!mesh.isLoaded())
138 return;
139
140 SPtr<MorphShapes> morphShapes = mesh->getMorphShapes();
141 if (morphShapes == nullptr)
142 return;
143
144 const Vector<SPtr<MorphChannel>>& channels = morphShapes->getChannels();
145 for (UINT32 i = 0; i < (UINT32)channels.size(); i++)
146 {
147 if (channels[i]->getName() == name)
148 {
149 mInternal->setMorphChannelWeight(i, weight);
150 break;
151 }
152 }
153 }
154
155 void CAnimation::setBounds(const AABox& bounds)
156 {
157 mBounds = bounds;
158
159 if(mUseBounds)
160 {
161 if(mAnimatedRenderable != nullptr)
162 {
163 SPtr<Renderable> renderable = mAnimatedRenderable->_getInternal();
164 if (renderable != nullptr)
165 renderable->setOverrideBounds(bounds);
166
167 if(mInternal != nullptr && !mPreviewMode)
168 {
169 AABox bounds = mBounds;
170
171 bounds.transformAffine(SO()->getWorldMatrix());
172 mInternal->setBounds(bounds);
173 }
174 }
175 }
176 }
177
178 void CAnimation::setUseBounds(bool enable)
179 {
180 mUseBounds = enable;
181
182 _updateBounds();
183 }
184
185 void CAnimation::setEnableCull(bool enable)
186 {
187 mEnableCull = enable;
188
189 if (mInternal != nullptr && !mPreviewMode)
190 mInternal->setCulling(enable);
191 }
192
193 UINT32 CAnimation::getNumClips() const
194 {
195 if (mInternal != nullptr)
196 return mInternal->getNumClips();
197
198 return 0;
199 }
200
201 HAnimationClip CAnimation::getClip(UINT32 idx) const
202 {
203 if (mInternal != nullptr)
204 return mInternal->getClip(idx);
205
206 return HAnimationClip();
207 }
208
209 void CAnimation::onInitialized()
210 {
211
212 }
213
214 void CAnimation::onDestroyed()
215 {
216 destroyInternal();
217 }
218
219 void CAnimation::onDisabled()
220 {
221 destroyInternal();
222 }
223
224 void CAnimation::onEnabled()
225 {
226 if(mPreviewMode)
227 {
228 destroyInternal();
229 mPreviewMode = false;
230 }
231
232 if(SceneManager::instance().isRunning())
233 restoreInternal(false);
234 }
235
236 void CAnimation::update()
237 {
238 const bool isRunning = SceneManager::instance().isRunning();
239 if(!isRunning && !mPreviewMode)
240 {
241 // Make sure attached CBone components match the position of the skeleton bones even when the component is not
242 // otherwise running.
243
244 HRenderable animatedRenderable = SO()->getComponent<CRenderable>();
245 if(animatedRenderable)
246 {
247 HMesh mesh = animatedRenderable->getMesh();
248 if(mesh.isLoaded())
249 {
250 const SPtr<Skeleton>& skeleton = mesh->getSkeleton();
251 if(skeleton)
252 {
253 for (auto& entry : mMappingInfos)
254 {
255 // We allow a null bone for the root bone mapping, should be non-null for everything else
256 if(!entry.isMappedToBone || entry.bone == nullptr)
257 continue;
258
259 const UINT32 numBones = skeleton->getNumBones();
260 for (UINT32 j = 0; j < numBones; j++)
261 {
262 if (skeleton->getBoneInfo(j).name == entry.bone->getBoneName())
263 {
264 Matrix4 bindPose = skeleton->getInvBindPose(j).inverseAffine();
265 bindPose = SO()->getTransform().getMatrix() * bindPose;
266
267 Vector3 position, scale;
268 Quaternion rotation;
269 bindPose.decomposition(position, rotation, scale);
270
271 entry.sceneObject->setWorldPosition(position);
272 entry.sceneObject->setWorldRotation(rotation);
273 entry.sceneObject->setWorldScale(scale);
274
275 break;
276 }
277 }
278 }
279 }
280 }
281 }
282 }
283
284 if (mInternal == nullptr || !isRunning)
285 return;
286
287 HAnimationClip newPrimaryClip = mInternal->getClip(0);
288 if (newPrimaryClip != mPrimaryPlayingClip)
289 _refreshClipMappings();
290
291 if (_scriptUpdateFloatProperties)
292 _scriptUpdateFloatProperties();
293 }
294
295 void CAnimation::onTransformChanged(TransformChangedFlags flags)
296 {
297 if (!SO()->getActive())
298 return;
299
300 if ((flags & (TCF_Transform)) != 0)
301 _updateBounds(false);
302 }
303
304 void CAnimation::restoreInternal(bool previewMode)
305 {
306 if (mInternal != nullptr)
307 destroyInternal();
308
309 mInternal = Animation::create();
310
311 mAnimatedRenderable = SO()->getComponent<CRenderable>();
312
313 if (!previewMode)
314 {
315 mInternal->onEventTriggered.connect(std::bind(&CAnimation::eventTriggered, this, _1, _2));
316
317 mInternal->setWrapMode(mWrapMode);
318 mInternal->setSpeed(mSpeed);
319 mInternal->setCulling(mEnableCull);
320 }
321
322 _updateBounds();
323
324 if (!previewMode)
325 {
326 if (mDefaultClip.isLoaded())
327 mInternal->play(mDefaultClip);
328
329 mPrimaryPlayingClip = mInternal->getClip(0);
330 if (mPrimaryPlayingClip.isLoaded())
331 {
332 if (_scriptRebuildFloatProperties)
333 _scriptRebuildFloatProperties(mPrimaryPlayingClip);
334 }
335 }
336
337 setBoneMappings();
338
339 if(!previewMode)
340 updateSceneObjectMapping();
341
342 if (mAnimatedRenderable != nullptr)
343 mAnimatedRenderable->_registerAnimation(static_object_cast<CAnimation>(mThisHandle));
344 }
345
346 void CAnimation::destroyInternal()
347 {
348 if (mAnimatedRenderable != nullptr)
349 mAnimatedRenderable->_unregisterAnimation();
350
351 mPrimaryPlayingClip = nullptr;
352
353 // This should release the last reference and destroy the internal listener
354 mInternal = nullptr;
355 }
356
357 bool CAnimation::_togglePreviewMode(bool enabled)
358 {
359 bool isRunning = SceneManager::instance().isRunning();
360
361 if(enabled)
362 {
363 // Cannot enable preview while running
364 if (isRunning)
365 return false;
366
367 if(!mPreviewMode)
368 {
369 // Make sure not to re-enable preview mode if already enabled because it rebuilds the internal Animation
370 // component, changing its ID. If animation evaluation is async then the new ID will not have any animation
371 // attached for one frame. This can look weird when sampling the animation for preview purposes
372 // (e.g. scrubbing in editor), in which case animation will reset to T pose for a single frame before
373 // settling on the chosen frame.
374 restoreInternal(true);
375 mPreviewMode = true;
376 }
377
378 return true;
379 }
380 else
381 {
382 if (!isRunning)
383 destroyInternal();
384
385 mPreviewMode = false;
386 return false;
387 }
388 }
389
390 bool CAnimation::_getGenericCurveValue(UINT32 curveIdx, float& value)
391 {
392 if (mInternal == nullptr)
393 return false;
394
395 return mInternal->getGenericCurveValue(curveIdx, value);
396 }
397
398 void CAnimation::mapCurveToSceneObject(const String& curve, const HSceneObject& so)
399 {
400 if (mInternal == nullptr)
401 return;
402
403 mInternal->mapCurveToSceneObject(curve, so);
404 }
405
406 void CAnimation::unmapSceneObject(const HSceneObject& so)
407 {
408 if (mInternal == nullptr)
409 return;
410
411 mInternal->unmapSceneObject(so);
412 }
413
414 void CAnimation::_addBone(HBone bone)
415 {
416 const HSceneObject& currentSO = bone->SO();
417
418 SceneObjectMappingInfo newMapping;
419 newMapping.sceneObject = currentSO;
420 newMapping.isMappedToBone = true;
421 newMapping.bone = std::move(bone);
422
423 mMappingInfos.push_back(newMapping);
424
425 if(mInternal)
426 mInternal->mapCurveToSceneObject(newMapping.bone->getBoneName(), newMapping.sceneObject);
427 }
428
429 void CAnimation::_removeBone(const HBone& bone)
430 {
431 HSceneObject newSO;
432 for (UINT32 i = 0; i < (UINT32)mMappingInfos.size(); i++)
433 {
434 if (mMappingInfos[i].bone == bone)
435 {
436 if(mInternal)
437 mInternal->unmapSceneObject(mMappingInfos[i].sceneObject);
438
439 mMappingInfos.erase(mMappingInfos.begin() + i);
440 i--;
441 }
442 }
443 }
444
445 void CAnimation::_notifyBoneChanged(const HBone& bone)
446 {
447 if (mInternal == nullptr)
448 return;
449
450 for (UINT32 i = 0; i < (UINT32)mMappingInfos.size(); i++)
451 {
452 if (mMappingInfos[i].bone == bone)
453 {
454 mInternal->unmapSceneObject(mMappingInfos[i].sceneObject);
455 mInternal->mapCurveToSceneObject(bone->getBoneName(), mMappingInfos[i].sceneObject);
456 break;
457 }
458 }
459 }
460
461 void CAnimation::_registerRenderable(const HRenderable& renderable)
462 {
463 mAnimatedRenderable = renderable;
464
465 _updateBounds();
466 }
467
468 void CAnimation::_unregisterRenderable()
469 {
470 mAnimatedRenderable = nullptr;
471 }
472
473 void CAnimation::_updateBounds(bool updateRenderable)
474 {
475 SPtr<Renderable> renderable;
476 if (updateRenderable && mAnimatedRenderable != nullptr)
477 renderable = mAnimatedRenderable->_getInternal();
478
479 if (mUseBounds)
480 {
481 if (renderable != nullptr)
482 {
483 renderable->setUseOverrideBounds(true);
484 renderable->setOverrideBounds(mBounds);
485 }
486
487 if (mInternal != nullptr)
488 {
489 AABox bounds = mBounds;
490 bounds.transformAffine(SO()->getWorldMatrix());
491
492 mInternal->setBounds(bounds);
493 }
494 }
495 else
496 {
497 if (renderable != nullptr)
498 renderable->setUseOverrideBounds(false);
499
500 if (mInternal != nullptr)
501 {
502 AABox bounds;
503 if (mAnimatedRenderable != nullptr)
504 bounds = mAnimatedRenderable->getBounds().getBox();
505
506 mInternal->setBounds(bounds);
507 }
508 }
509 }
510
511 void CAnimation::setBoneMappings()
512 {
513 mMappingInfos.clear();
514
515 SceneObjectMappingInfo rootMapping;
516 rootMapping.sceneObject = SO();
517 rootMapping.isMappedToBone = true;
518
519 mMappingInfos.push_back(rootMapping);
520 mInternal->mapCurveToSceneObject("", rootMapping.sceneObject);
521
522 Vector<HBone> childBones = findChildBones();
523 for (auto& entry : childBones)
524 _addBone(entry);
525 }
526
527 void CAnimation::updateSceneObjectMapping()
528 {
529 Vector<SceneObjectMappingInfo> newMappingInfos;
530 for(auto& entry : mMappingInfos)
531 {
532 if (entry.isMappedToBone)
533 newMappingInfos.push_back(entry);
534 else
535 unmapSceneObject(entry.sceneObject);
536 }
537
538 if (mPrimaryPlayingClip.isLoaded())
539 {
540 HSceneObject root = SO();
541
542 const auto& findMappings = [&](const String& name, AnimationCurveFlags flags)
543 {
544 if (flags.isSet(AnimationCurveFlag::ImportedCurve))
545 return;
546
547 HSceneObject currentSO = root->findPath(name);
548
549 bool found = false;
550 for (UINT32 i = 0; i < (UINT32)newMappingInfos.size(); i++)
551 {
552 if (newMappingInfos[i].sceneObject == currentSO)
553 {
554 found = true;
555 break;
556 }
557 }
558
559 if (!found)
560 {
561 SceneObjectMappingInfo newMappingInfo;
562 newMappingInfo.isMappedToBone = false;
563 newMappingInfo.sceneObject = currentSO;
564
565 newMappingInfos.push_back(newMappingInfo);
566 mapCurveToSceneObject(name, currentSO);
567 }
568 };
569
570 SPtr<AnimationCurves> curves = mPrimaryPlayingClip->getCurves();
571 for(auto& curve : curves->position)
572 findMappings(curve.name, curve.flags);
573
574 for(auto& curve : curves->rotation)
575 findMappings(curve.name, curve.flags);
576
577 for(auto& curve : curves->scale)
578 findMappings(curve.name, curve.flags);
579 }
580
581 mMappingInfos = newMappingInfos;
582 }
583
584 void CAnimation::_refreshClipMappings()
585 {
586 mPrimaryPlayingClip = mInternal->getClip(0);
587
588 if (_scriptRebuildFloatProperties)
589 _scriptRebuildFloatProperties(mPrimaryPlayingClip);
590
591 updateSceneObjectMapping();
592 }
593
594 Vector<HBone> CAnimation::findChildBones()
595 {
596 Stack<HSceneObject> todo;
597 todo.push(SO());
598
599 Vector<HBone> bones;
600 while (todo.size() > 0)
601 {
602 HSceneObject currentSO = todo.top();
603 todo.pop();
604
605 HBone bone = currentSO->getComponent<CBone>();
606 if (bone != nullptr)
607 {
608 bone->_setParent(static_object_cast<CAnimation>(getHandle()), true);
609 bones.push_back(bone);
610 }
611
612 int childCount = currentSO->getNumChildren();
613 for (int i = 0; i < childCount; i++)
614 {
615 HSceneObject child = currentSO->getChild(i);
616 if (child->getComponent<CAnimation>() != nullptr)
617 continue;
618
619 todo.push(child);
620 }
621 }
622
623 return bones;
624 }
625
626 void CAnimation::eventTriggered(const HAnimationClip& clip, const String& name)
627 {
628 onEventTriggered(clip, name);
629
630 if(_scriptOnEventTriggered)
631 _scriptOnEventTriggered(clip, name);
632 }
633
634 RTTITypeBase* CAnimation::getRTTIStatic()
635 {
636 return CAnimationRTTI::instance();
637 }
638
639 RTTITypeBase* CAnimation::getRTTI() const
640 {
641 return CAnimation::getRTTIStatic();
642 }
643}