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 | |
13 | using namespace std::placeholders; |
14 | |
15 | namespace 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 | } |