1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "animation_system.h"
17 
18 #include <PropertyTools/property_api_impl.inl>
19 #include <PropertyTools/property_data.h>
20 #include <PropertyTools/property_macros.h>
21 #include <algorithm>
22 #include <cfloat>
23 #include <cinttypes>
24 #include <numeric>
25 
26 #include <3d/ecs/components/animation_component.h>
27 #include <3d/ecs/components/animation_input_component.h>
28 #include <3d/ecs/components/animation_output_component.h>
29 #include <3d/ecs/components/animation_state_component.h>
30 #include <3d/ecs/components/animation_track_component.h>
31 #include <3d/ecs/components/name_component.h>
32 #include <3d/ecs/components/node_component.h>
33 #include <3d/ecs/components/transform_component.h>
34 #include <3d/ecs/systems/intf_node_system.h>
35 #include <base/math/quaternion_util.h>
36 #include <base/math/spline.h>
37 #include <base/util/uid_util.h>
38 #include <core/ecs/intf_ecs.h>
39 #include <core/implementation_uids.h>
40 #include <core/intf_engine.h>
41 #include <core/log.h>
42 #include <core/namespace.h>
43 #include <core/perf/cpu_perf_scope.h>
44 #include <core/perf/intf_performance_data_manager.h>
45 #include <core/plugin/intf_plugin_register.h>
46 
47 #include "ecs/components/initial_transform_component.h"
48 #include "ecs/systems/animation_playback.h"
49 
50 CORE3D_BEGIN_NAMESPACE()
51 using namespace BASE_NS;
52 using namespace CORE_NS;
53 
54 struct AnimationSystem::PropertyEntry {
55     uint64_t componentAndProperty;
56     IComponentManager* component;
57     uintptr_t propertyOffset;
58     const Property* property;
59 };
60 
61 struct AnimationSystem::InterpolationData {
62     AnimationTrackComponent::Interpolation mode;
63     size_t startIndex;
64     size_t endIndex;
65 
66     float t;
67     float weight;
68 };
69 
70 class AnimationSystem::InitTask final : public IThreadPool::ITask {
71 public:
InitTask(AnimationSystem & system,size_t offset,size_t count)72     InitTask(AnimationSystem& system, size_t offset, size_t count) : system_(system), offset_(offset), count_(count) {};
73 
operator ()()74     void operator()() override
75     {
76         const auto results = array_view(static_cast<const uint32_t*>(system_.trackOrder_.data()) + offset_, count_);
77         system_.InitializeTrackValues(results);
78     }
79 
80 protected:
Destroy()81     void Destroy() override {}
82 
83 private:
84     AnimationSystem& system_;
85     size_t offset_;
86     size_t count_;
87 };
88 
89 class AnimationSystem::AnimateTask final : public IThreadPool::ITask {
90 public:
AnimateTask(AnimationSystem & system,size_t offset,size_t count)91     AnimateTask(AnimationSystem& system, size_t offset, size_t count)
92         : system_(system), offset_(offset), count_(count) {};
93 
operator ()()94     void operator()() override
95     {
96         const auto results = array_view(static_cast<const uint32_t*>(system_.trackOrder_.data()) + offset_, count_);
97         system_.Calculate(results);
98         system_.AnimateTracks(results);
99     }
100 
101 protected:
Destroy()102     void Destroy() override {}
103 
104 private:
105     AnimationSystem& system_;
106     size_t offset_;
107     size_t count_;
108 };
109 
110 namespace {
111 BEGIN_PROPERTY(AnimationSystem::Properties, SystemMetadata)
112     DECL_PROPERTY2(AnimationSystem::Properties, minTaskSize, "Task size", PropertyFlags::IS_SLIDER)
113 END_PROPERTY();
114 
115 constexpr PropertyTypeDecl PROPERTY_HANDLE_PTR_T = PROPERTYTYPE(CORE_NS::IPropertyHandle*);
116 
117 // values contain in-tangent, spline vertex, out-tangent.
118 template<typename T>
119 struct SplineValues {
120     T inTangent;
121     T splineVertex;
122     T outTangent;
123 };
124 
125 template<typename To, typename From>
Cast(From * from)126 inline To Cast(From* from)
127 {
128     if constexpr (is_const_v<remove_pointer_t<From>>) {
129         return static_cast<const To>(static_cast<const void*>(from));
130     } else {
131         return static_cast<To>(static_cast<void*>(from));
132     }
133 }
134 
135 template<typename To, typename From>
Cast(From & from)136 inline To Cast(From& from)
137 {
138     if constexpr (is_const_v<remove_reference_t<From>>) {
139         return *static_cast<const remove_reference_t<To>*>(static_cast<const void*>(&from));
140     } else {
141         return *static_cast<remove_reference_t<To>*>(static_cast<void*>(&from));
142     }
143 }
144 
145 template<typename To, typename From>
Get(From && from)146 static inline To Get(From&& from)
147 {
148     if constexpr (is_same_v<remove_const_t<remove_reference_t<To>>, float>) {
149         return from.initialData.floatValue;
150     }
151     if constexpr (is_same_v<remove_const_t<remove_reference_t<To>>, Math::Vec2>) {
152         return from.initialData.vec2Value;
153     }
154     if constexpr (is_same_v<remove_const_t<remove_reference_t<To>>, Math::Vec3>) {
155         return from.initialData.vec3Value;
156     }
157     if constexpr (is_same_v<remove_const_t<remove_reference_t<To>>, Math::Vec4>) {
158         return from.initialData.vec4Value;
159     }
160 }
161 
162 template<class T>
Step(const T & start,const T & end,float offset)163 T Step(const T& start, const T& end, float offset)
164 {
165     return (offset > 0.5f) ? end : start;
166 }
167 
InterpolateQuatSplineGLTF(const Math::Vec4 & start,const Math::Vec4 & outTangent,const Math::Vec4 & end,const Math::Vec4 & inTangent,float offset)168 Math::Quat InterpolateQuatSplineGLTF(const Math::Vec4& start, const Math::Vec4& outTangent, const Math::Vec4& end,
169     const Math::Vec4& inTangent, float offset)
170 {
171     const Math::Vec4 vec = Math::Hermite(start, outTangent, end, inTangent, offset);
172     return Normalize(Cast<Math::Quat>(vec));
173 }
174 
Lerp(const float & v1,const float & v2,float t)175 constexpr float Lerp(const float& v1, const float& v2, float t)
176 {
177     t = Math::clamp01(t);
178     return v1 + (v2 - v1) * t;
179 }
180 
Assign(const PropertyTypeDecl & type,uint8_t * dst,const InitialTransformComponent & src)181 void Assign(const PropertyTypeDecl& type, uint8_t* dst, const InitialTransformComponent& src)
182 {
183     switch (type) {
184         // Primitive types
185         case PropertyType::FLOAT_T: {
186             *Cast<float*>(dst) = src.initialData.floatValue;
187             break;
188         }
189 
190         // Extended types
191         case PropertyType::VEC2_T: {
192             *Cast<Math::Vec2*>(dst) = src.initialData.vec2Value;
193             break;
194         }
195 
196         case PropertyType::VEC3_T: {
197             *Cast<Math::Vec3*>(dst) = src.initialData.vec3Value;
198             break;
199         }
200 
201         case PropertyType::VEC4_T: {
202             *Cast<Math::Vec4*>(dst) = src.initialData.vec4Value;
203             break;
204         }
205 
206         case PropertyType::QUAT_T: {
207             *Cast<Math::Quat*>(dst) = src.initialData.quatValue;
208             break;
209         }
210 
211         case PropertyType::FLOAT_VECTOR_T: {
212             // target property is an actual vector
213             auto* dstVector = Cast<vector<float>*>(dst);
214             // while initial values is the contents of the vector i.e. an array.
215             const auto& srcVec = src.initialData.floatVectorValue;
216             const auto* srcF = srcVec.data();
217             const auto count = Math::min(srcVec.size(), dstVector->size());
218             std::copy(srcF, srcF + count, dstVector->data());
219             break;
220         }
221         default: {
222             CORE_LOG_ONCE_D(to_string(type.typeHash), "AnimateTrack failed, unsupported type %s", type.name.data());
223             break;
224         }
225     }
226 }
227 
228 template<typename T>
Mult(uint8_t * dst,const T & src)229 inline void Mult(uint8_t* dst, const T& src)
230 {
231     auto* dstType = Cast<T*>(dst);
232     *dstType = src * *dstType;
233 }
234 
235 template<typename T>
Add(uint8_t * dst,const T src)236 inline void Add(uint8_t* dst, const T src)
237 {
238     auto* dstType = Cast<T*>(dst);
239     *dstType = *dstType + src;
240 }
241 
Add(const PropertyTypeDecl & type,uint8_t * dst,const InitialTransformComponent & src)242 void Add(const PropertyTypeDecl& type, uint8_t* dst, const InitialTransformComponent& src)
243 {
244     switch (type) {
245         // Primitive types
246         case PropertyType::FLOAT_T: {
247             Add<float>(dst, src.initialData.floatValue);
248             break;
249         }
250 
251         // Extended types
252         case PropertyType::VEC2_T: {
253             Add<Math::Vec2>(dst, src.initialData.vec2Value);
254             break;
255         }
256 
257         case PropertyType::VEC3_T: {
258             Add<Math::Vec3>(dst, src.initialData.vec3Value);
259             break;
260         }
261 
262         case PropertyType::VEC4_T: {
263             Add<Math::Vec4>(dst, src.initialData.vec4Value);
264             break;
265         }
266 
267         case PropertyType::QUAT_T: {
268             Mult<Math::Quat>(dst, src.initialData.quatValue);
269             break;
270         }
271 
272         case PropertyType::FLOAT_VECTOR_T: {
273             // target property is an actual vector
274             auto* dstVector = Cast<vector<float>*>(dst);
275             // while result is the contents of the vector i.e. an array.
276             auto& srcVector = src.initialData.floatVectorValue;
277             const auto* srcF = srcVector.data();
278             const auto count = Math::min(srcVector.size(), dstVector->size());
279             std::transform(srcF, srcF + count, dstVector->data(), dstVector->data(),
280                 [](float initial, float result) { return initial + result; });
281             break;
282         }
283         default: {
284             CORE_LOG_ONCE_D(to_string(type.typeHash), "AnimateTrack failed, unsupported type %s", type.name.data());
285             break;
286         }
287     }
288 }
289 
290 struct AnimationKeyDataOffsets {
291     size_t startKeyOffset;
292     size_t endKeyOffset;
293 };
294 
295 template<typename T>
Interpolate(const AnimationSystem::InterpolationData & interpolation,array_view<const uint8_t> frameValues,const InitialTransformComponent & initialValue,InitialTransformComponent & animatedValue)296 void Interpolate(const AnimationSystem::InterpolationData& interpolation, array_view<const uint8_t> frameValues,
297     const InitialTransformComponent& initialValue, InitialTransformComponent& animatedValue)
298 {
299     auto values = array_view(Cast<const T*>(frameValues.data()), frameValues.size() / sizeof(T));
300     if ((interpolation.startIndex < values.size()) && (interpolation.endIndex < values.size())) {
301         T result;
302         switch (interpolation.mode) {
303             case AnimationTrackComponent::Interpolation::STEP:
304                 result = Step(values[interpolation.startIndex], values[interpolation.endIndex], interpolation.t);
305                 break;
306 
307             default:
308             case AnimationTrackComponent::Interpolation::LINEAR:
309                 result = Lerp(values[interpolation.startIndex], values[interpolation.endIndex], interpolation.t);
310                 break;
311 
312             case AnimationTrackComponent::Interpolation::SPLINE: {
313                 const auto& p0 = Cast<const SplineValues<T>&>(values[interpolation.startIndex]);
314                 const auto& p1 = Cast<const SplineValues<T>&>(values[interpolation.endIndex]);
315                 result = Math::Hermite(p0.splineVertex, p0.outTangent, p1.splineVertex, p1.inTangent, interpolation.t);
316                 break;
317             }
318         }
319         const T& initial = Get<const T&>(initialValue);
320         // Could assign to animatedValue directly but that happens via a jump to operator=. Types have been checked in
321         // AnimateTracks.
322         T& target = Get<T&>(animatedValue);
323         // Convert result to relative and apply weight.
324         target = (result - initial) * interpolation.weight;
325     } else {
326         Get<T&>(animatedValue) = {};
327     }
328 }
329 
330 template<>
331 void Interpolate<Math::Quat>(const AnimationSystem::InterpolationData& interpolation,
332     array_view<const uint8_t> frameValues, const InitialTransformComponent& initialValue,
333     InitialTransformComponent& animatedValue)
334 {
335     static constexpr Math::Quat identity(0.0f, 0.0f, 0.0f, 1.0f);
336     auto values = array_view(Cast<const Math::Vec4*>(frameValues.data()), frameValues.size() / sizeof(Math::Vec4));
337     if ((interpolation.startIndex < values.size()) && (interpolation.endIndex < values.size())) {
338         Math::Quat result;
339 
340         switch (interpolation.mode) {
341             case AnimationTrackComponent::Interpolation::STEP: {
342                 result = Step(Cast<const Math::Quat&>(values[interpolation.startIndex]),
343                     Cast<const Math::Quat&>(values[interpolation.endIndex]), interpolation.t);
344                 break;
345             }
346 
347             default:
348             case AnimationTrackComponent::Interpolation::LINEAR: {
349                 result = Slerp(Cast<const Math::Quat&>(values[interpolation.startIndex]),
350                     Cast<const Math::Quat&>(values[interpolation.endIndex]), interpolation.t);
351                 break;
352             }
353 
354             case AnimationTrackComponent::Interpolation::SPLINE: {
355                 auto const p0 = reinterpret_cast<const SplineValues<Math::Vec4>*>(&values[interpolation.startIndex]);
356                 auto const p1 = reinterpret_cast<const SplineValues<Math::Vec4>*>(&values[interpolation.endIndex]);
357                 result = InterpolateQuatSplineGLTF(
358                     p0->splineVertex, p0->outTangent, p1->splineVertex, p1->inTangent, interpolation.t);
359                 break;
360             }
361         }
362         const Math::Quat inverseInitialRotation = Inverse(initialValue.initialData.quatValue);
363         const Math::Quat delta = Normalize(Slerp(identity, result * inverseInitialRotation, interpolation.weight));
364 
365         animatedValue.initialData.quatValue = delta;
366     } else {
367         animatedValue.initialData.quatValue = identity;
368     }
369 }
370 
AnimateArray(const AnimationSystem::InterpolationData & interpolation,array_view<const uint8_t> frameValues,const InitialTransformComponent & initialValue,InitialTransformComponent & animatedValue)371 void AnimateArray(const AnimationSystem::InterpolationData& interpolation, array_view<const uint8_t> frameValues,
372     const InitialTransformComponent& initialValue, InitialTransformComponent& animatedValue)
373 {
374     const auto initialValues =
375         array_view(initialValue.initialData.floatVectorValue.data(), initialValue.initialData.floatVectorValue.size());
376 
377     auto animated = array_view(
378         animatedValue.initialData.floatVectorValue.data(), animatedValue.initialData.floatVectorValue.size());
379     const auto targetCount = animated.size();
380 
381     // Re-calculate the start/end offsets as each value is actually an array of values.
382     const size_t startDataOffset = interpolation.startIndex * targetCount;
383     const size_t endDataOffset = interpolation.endIndex * targetCount;
384 
385     if (interpolation.mode == AnimationTrackComponent::Interpolation::SPLINE) {
386         const auto values = array_view(
387             Cast<const SplineValues<float>*>(frameValues.data()), frameValues.size() / sizeof(SplineValues<float>));
388         if (((startDataOffset + targetCount) <= values.size()) && ((endDataOffset + targetCount) <= values.size())) {
389             auto p0 = values.data() + startDataOffset;
390             auto p1 = values.data() + endDataOffset;
391             const auto p0E = p0 + targetCount;
392             auto iI = initialValues.cbegin();
393             auto aI = animated.begin();
394             for (; p0 != p0E; ++p0, ++p1, ++iI, ++aI) {
395                 *aI =
396                     (Math::Hermite(p0->splineVertex, p0->outTangent, p1->splineVertex, p1->inTangent, interpolation.t) -
397                         *iI) *
398                     interpolation.weight;
399             }
400         }
401     } else {
402         const auto values = array_view(Cast<const float*>(frameValues.data()), frameValues.size() / sizeof(float));
403         if (((startDataOffset + targetCount) <= values.size()) && ((endDataOffset + targetCount) <= values.size())) {
404             auto p0 = values.data() + startDataOffset;
405             auto p1 = values.data() + endDataOffset;
406             const auto p0E = p0 + targetCount;
407             auto iI = initialValues.cbegin();
408             auto aI = animated.begin();
409             if (interpolation.mode == AnimationTrackComponent::Interpolation::STEP) {
410                 for (; p0 != p0E; ++p0, ++p1, ++iI, ++aI) {
411                     *aI = (Step(*p0, *p1, interpolation.t) - *iI) * interpolation.weight;
412                 }
413             } else {
414                 for (; p0 != p0E; ++p0, ++p1, ++iI, ++aI) {
415                     *aI = (Math::lerp(*p0, *p1, interpolation.t) - *iI) * interpolation.weight;
416                 }
417             }
418         }
419     }
420 }
421 
AnimateTrack(const PropertyTypeDecl & type,const AnimationSystem::InterpolationData & interpolationData,const AnimationOutputComponent & outputComponent,const InitialTransformComponent & initialValue,InitialTransformComponent & resultValue)422 void AnimateTrack(const PropertyTypeDecl& type, const AnimationSystem::InterpolationData& interpolationData,
423     const AnimationOutputComponent& outputComponent, const InitialTransformComponent& initialValue,
424     InitialTransformComponent& resultValue)
425 {
426     switch (type) {
427         // Primitive types
428         case PropertyType::FLOAT_T: {
429             Interpolate<float>(interpolationData, outputComponent.data, initialValue, resultValue);
430             break;
431         }
432 
433         // Extended types
434         case PropertyType::VEC2_T: {
435             Interpolate<Math::Vec2>(interpolationData, outputComponent.data, initialValue, resultValue);
436             break;
437         }
438 
439         case PropertyType::VEC3_T: {
440             Interpolate<Math::Vec3>(interpolationData, outputComponent.data, initialValue, resultValue);
441             break;
442         }
443 
444         case PropertyType::VEC4_T: {
445             Interpolate<Math::Vec4>(interpolationData, outputComponent.data, initialValue, resultValue);
446             break;
447         }
448 
449         case PropertyType::QUAT_T: {
450             Interpolate<Math::Quat>(interpolationData, outputComponent.data, initialValue, resultValue);
451             break;
452         }
453 
454         case PropertyType::FLOAT_VECTOR_T: {
455             AnimateArray(interpolationData, outputComponent.data, initialValue, resultValue);
456             break;
457         }
458         default: {
459             CORE_LOG_ONCE_D(to_string(type.typeHash), "AnimateTrack failed, unsupported type %s", type.name.data());
460             return;
461         }
462     }
463 }
464 
FindFrameIndices(const bool forward,const float currentTimestamp,const array_view<const float> timestamps,size_t & currentFrameIndex,size_t & nextFrameIndex)465 void FindFrameIndices(const bool forward, const float currentTimestamp, const array_view<const float> timestamps,
466     size_t& currentFrameIndex, size_t& nextFrameIndex)
467 {
468     size_t current;
469     size_t next;
470     const auto begin = timestamps.begin();
471     const auto end = timestamps.end();
472     const auto pos = std::upper_bound(begin, end, currentTimestamp);
473     if (forward) {
474         // Find next frame (forward).
475         next = static_cast<size_t>(std::distance(begin, pos));
476         current = next - 1;
477     } else {
478         // Find next frame (backward).
479         current = static_cast<size_t>(std::distance(begin, pos));
480         next = current ? current - 1 : 0;
481     }
482 
483     // Clamp timestamps to valid range.
484     currentFrameIndex = std::clamp(current, size_t(0), timestamps.size() - 1);
485     nextFrameIndex = std::clamp(next, size_t(0), timestamps.size() - 1);
486 }
487 
UpdateStateAndTracks(IAnimationStateComponentManager & stateManager_,IAnimationTrackComponentManager & trackManager_,IAnimationInputComponentManager & inputManager_,Entity animationEntity,const AnimationComponent & animationComponent,array_view<const Entity> targetEntities)488 void UpdateStateAndTracks(IAnimationStateComponentManager& stateManager_,
489     IAnimationTrackComponentManager& trackManager_, IAnimationInputComponentManager& inputManager_,
490     Entity animationEntity, const AnimationComponent& animationComponent, array_view<const Entity> targetEntities)
491 {
492     auto stateHandle = stateManager_.Write(animationEntity);
493     stateHandle->trackStates.reserve(animationComponent.tracks.size());
494 
495     auto& entityManager = stateManager_.GetEcs().GetEntityManager();
496     auto targetIt = targetEntities.begin();
497     for (const auto& trackEntity : animationComponent.tracks) {
498         stateHandle->trackStates.push_back({ Entity(trackEntity), 0U });
499         auto& trackState = stateHandle->trackStates.back();
500 
501         if (auto track = trackManager_.Write(trackEntity); track) {
502             track->target = entityManager.GetReferenceCounted(*targetIt);
503 
504             if (auto inputData = inputManager_.Read(track->timestamps); inputData) {
505                 trackState.length = inputData->timestamps.size();
506             }
507         }
508         ++targetIt;
509     }
510 }
511 
CopyInitialDataComponent(InitialTransformComponent & dst,const Property * property,const array_view<const uint8_t> src)512 void CopyInitialDataComponent(
513     InitialTransformComponent& dst, const Property* property, const array_view<const uint8_t> src)
514 {
515     switch (property->type) {
516         case PropertyType::FLOAT_T: {
517             dst = *Cast<const float*>(src.data());
518             break;
519         }
520 
521         case PropertyType::VEC2_T: {
522             dst = *Cast<const Math::Vec2*>(src.data());
523             break;
524         }
525 
526         case PropertyType::VEC3_T: {
527             dst = *Cast<const Math::Vec3*>(src.data());
528             break;
529         }
530 
531         case PropertyType::VEC4_T: {
532             dst = *Cast<const Math::Vec4*>(src.data());
533             break;
534         }
535 
536         case PropertyType::QUAT_T: {
537             dst = *Cast<const Math::Quat*>(src.data());
538             break;
539         }
540 
541         case PropertyType::FLOAT_VECTOR_T: {
542             dst = *Cast<const vector<float>*>(src.data());
543             break;
544         }
545         default: {
546             CORE_LOG_ONCE_D(
547                 to_string(property->type.typeHash), "AnimateTrack failed, unsupported type %s", property->name.data());
548             return;
549         }
550     }
551 }
552 
FindDynamicProperty(const AnimationTrackComponent & animationTrack,const IPropertyHandle * dynamicProperties,AnimationSystem::PropertyEntry entry)553 AnimationSystem::PropertyEntry FindDynamicProperty(const AnimationTrackComponent& animationTrack,
554     const IPropertyHandle* dynamicProperties, AnimationSystem::PropertyEntry entry)
555 {
556     if (dynamicProperties) {
557         auto propertyName = string_view(animationTrack.property);
558         if (propertyName.starts_with(entry.property->name)) {
559             propertyName.remove_prefix(entry.property->name.size() + 1U);
560         }
561 
562         auto c = PropertyData::FindProperty(dynamicProperties->Owner()->MetaData(), propertyName);
563         if (c && c.propertyPath == propertyName) {
564             entry.property = c.property;
565             entry.propertyOffset = c.offset;
566         }
567     }
568     return entry;
569 }
570 
ResetTime(AnimationStateComponent & state,const AnimationComponent & animation)571 void ResetTime(AnimationStateComponent& state, const AnimationComponent& animation)
572 {
573     state.time = (animation.duration != 0.f) ? std::fmod(state.time, animation.duration) : 0.f;
574     if (state.time < 0.f) {
575         // When speed is negative time will end up negative. Wrap around to end of animation length.
576         state.time = animation.duration + state.time;
577     }
578 
579     // This is called when SetTimePosition was called, animation was stopped, or playback is repeted. In all cases
580     // mark the animation dirty so animation system will run at least one update cycle to get the animated object to
581     // correct state.
582     state.dirty = true;
583 }
584 } // namespace
585 
AnimationSystem(IEcs & ecs)586 AnimationSystem::AnimationSystem(IEcs& ecs)
587     : ecs_(ecs), systemPropertyApi_(&systemProperties_, array_view(SystemMetadata)),
588       initialTransformManager_(*(GetManager<IInitialTransformComponentManager>(ecs_))),
589       animationManager_(*(GetManager<IAnimationComponentManager>(ecs_))),
590       inputManager_(*(GetManager<IAnimationInputComponentManager>(ecs_))),
591       outputManager_(*(GetManager<IAnimationOutputComponentManager>(ecs_))),
592       stateManager_(*(GetManager<IAnimationStateComponentManager>(ecs_))),
593       animationTrackManager_(*(GetManager<IAnimationTrackComponentManager>(ecs_))),
594       nameManager_(*(GetManager<INameComponentManager>(ecs_))), threadPool_(ecs.GetThreadPool())
595 {}
596 
SetActive(bool state)597 void AnimationSystem::SetActive(bool state)
598 {
599     active_ = state;
600 }
601 
IsActive() const602 bool AnimationSystem::IsActive() const
603 {
604     return active_;
605 }
606 
GetName() const607 string_view AnimationSystem::GetName() const
608 {
609     return CORE3D_NS::GetName(this);
610 }
611 
GetUid() const612 Uid AnimationSystem::GetUid() const
613 {
614     return UID;
615 }
616 
GetProperties()617 IPropertyHandle* AnimationSystem::GetProperties()
618 {
619     return systemPropertyApi_.GetData();
620 }
621 
GetProperties() const622 const IPropertyHandle* AnimationSystem::GetProperties() const
623 {
624     return systemPropertyApi_.GetData();
625 }
626 
SetProperties(const IPropertyHandle & data)627 void AnimationSystem::SetProperties(const IPropertyHandle& data)
628 {
629     if (data.Owner() != &systemPropertyApi_) {
630         return;
631     }
632     if (const auto in = ScopedHandle<const AnimationSystem::Properties>(&data); in) {
633         systemProperties_.minTaskSize = in->minTaskSize;
634     }
635 }
636 
GetECS() const637 const IEcs& AnimationSystem::GetECS() const
638 {
639     return ecs_;
640 }
641 
Initialize()642 void AnimationSystem::Initialize()
643 {
644     ecs_.AddListener(animationManager_, *this);
645     ecs_.AddListener(animationTrackManager_, *this);
646     {
647         const ComponentQuery::Operation operations[] = {
648             { animationTrackManager_, ComponentQuery::Operation::REQUIRE },
649         };
650         trackQuery_.SetEcsListenersEnabled(true);
651         trackQuery_.SetupQuery(initialTransformManager_, operations, true);
652     }
653     {
654         const ComponentQuery::Operation operations[] = {
655             { stateManager_, ComponentQuery::Operation::REQUIRE },
656         };
657         animationQuery_.SetEcsListenersEnabled(true);
658         animationQuery_.SetupQuery(animationManager_, operations, false);
659     }
660 }
661 
Update(bool frameRenderingQueued,uint64_t time,uint64_t delta)662 bool AnimationSystem::Update(bool frameRenderingQueued, uint64_t time, uint64_t delta)
663 {
664     if (!active_) {
665         return false;
666     }
667 
668     if ((stateGeneration_ == stateManager_.GetGenerationCounter()) && [](const auto& stateManager) -> bool {
669             const auto states = stateManager.GetComponentCount();
670             for (auto id = 0U; id < states; ++id) {
671                 if (stateManager.Read(id)->dirty) {
672                     return false;
673                 }
674             }
675             return true;
676         }(stateManager_)) {
677         return false;
678     }
679 
680     animationQuery_.Execute();
681     trackQuery_.Execute();
682 
683     auto results = animationQuery_.GetResults();
684     if (animationGeneration_ != animationManager_.GetGenerationCounter()) {
685         // Handle stopped animations first to avoid glitches. Alternative would be filtering out tracks which are
686         // stopped, but there's another track targeting the same property.
687         animationOrder_.clear();
688         animationOrder_.reserve(results.size());
689         uint32_t index = 0U;
690         uint32_t stopped = 0U;
691         for (const auto& row : results) {
692             auto animationHandle = animationManager_.Read(row.components[0]);
693             if (animationHandle->state == AnimationComponent::PlaybackState::STOP) {
694                 animationOrder_.insert(animationOrder_.cbegin() + stopped, index);
695                 ++stopped;
696             } else {
697                 animationOrder_.push_back(index);
698             }
699             ++index;
700         }
701     }
702 
703     // Reset trackValues_ to stopped state before walking through animations.
704     trackValues_.resize(animationTrackManager_.GetComponentCount());
705     for (auto& values : trackValues_) {
706         values.stopped = true;
707     }
708 
709     frameIndices_.resize(animationTrackManager_.GetComponentCount());
710 
711     const auto deltaS = static_cast<float>(delta) / 1000000.f;
712     // For each animation update the playback state, time position and weight, and gather tracks in trackOrder_.
713     trackOrder_.clear();
714     trackOrder_.reserve(animationTrackManager_.GetComponentCount());
715     for (const auto& index : animationOrder_) {
716         const auto& row = results[index];
717         bool setPaused = false;
718         {
719             auto animationHandle = animationManager_.Read(row.components[0]);
720             auto stateHandle = stateManager_.Write(row.components[1]);
721             if (animationHandle && stateHandle) {
722                 UpdateAnimation(*stateHandle, *animationHandle, trackQuery_, deltaS);
723                 setPaused =
724                     (stateHandle->completed && animationHandle->state == AnimationComponent::PlaybackState::PLAY);
725             }
726         }
727         if (setPaused) {
728             animationManager_.Write(row.components[0])->state = AnimationComponent::PlaybackState::PAUSE;
729         }
730     }
731 
732     {
733         // Would constructing a reordered ResultRow array be better than indices into the results? Would be one
734         // indirection less in processing...
735     }
736 
737     // Calculate how many tasks will be needed
738     {
739         const auto threadCount = threadPool_->GetNumberOfThreads() + 1;
740         const auto resultCount = trackOrder_.size();
741         taskSize_ = Math::max(systemProperties_.minTaskSize, resultCount / threadCount);
742         if (taskSize_ == 0) {
743             taskSize_ = 1;
744         }
745         tasks_ = ((resultCount / taskSize_)) ? (resultCount / taskSize_) - 1U : 0U;
746         remaining_ = resultCount - (tasks_ * taskSize_);
747     }
748 
749     taskId_ = 0U;
750     taskResults_.clear();
751 
752     // Start tasks for filling the initial values to trackValues_.
753     InitializeTrackValues();
754 
755     // For each track batch reset the target properties to initial values, wait for trackValues_ to be filled and start
756     // new task to handle the actual animation.
757     ResetToInitialTrackValues();
758 
759     // Update target properties, this will apply animations on top of current value.
760     WriteUpdatedTrackValues();
761 
762     stateGeneration_ = stateManager_.GetGenerationCounter();
763     return !animationQuery_.GetResults().empty();
764 }
765 
Uninitialize()766 void AnimationSystem::Uninitialize()
767 {
768     ecs_.RemoveListener(animationManager_, *this);
769     ecs_.RemoveListener(animationTrackManager_, *this);
770 
771     trackQuery_.SetEcsListenersEnabled(false);
772     animationQuery_.SetEcsListenersEnabled(false);
773 
774     animations_.clear();
775 }
776 
CreatePlayback(Entity const & animationEntity,ISceneNode const & node)777 IAnimationPlayback* AnimationSystem::CreatePlayback(Entity const& animationEntity, ISceneNode const& node)
778 {
779     vector<Entity> targetEntities;
780     if (const auto animationData = animationManager_.Read(animationEntity); animationData) {
781         targetEntities.reserve(animationData->tracks.size());
782         for (const auto& trackEntity : animationData->tracks) {
783             if (auto nameHandle = nameManager_.Read(trackEntity); nameHandle) {
784                 if (auto targetNode = node.LookupNodeByPath(nameHandle->name); targetNode) {
785                     targetEntities.push_back(targetNode->GetEntity());
786                 } else {
787                     CORE_LOG_D("Cannot find target node for animation track '%s'.", nameHandle->name.data());
788                     return nullptr;
789                 }
790             } else {
791                 CORE_LOG_D("Cannot match unnamed animation track.");
792                 return nullptr;
793             }
794         }
795     }
796     return CreatePlayback(animationEntity, targetEntities);
797 }
798 
CreatePlayback(const Entity animationEntity,const array_view<const Entity> targetEntities)799 IAnimationPlayback* AnimationSystem::CreatePlayback(
800     const Entity animationEntity, const array_view<const Entity> targetEntities)
801 {
802     // animationEntity must be have an animation component and track count must match target entites, and
803     // target entities must be valid.
804     if (const auto animationHandle = animationManager_.Read(animationEntity);
805         !animationHandle || (animationHandle->tracks.size() != targetEntities.size()) ||
806         !std::all_of(targetEntities.begin(), targetEntities.end(),
807             [](const Entity& entity) { return EntityUtil::IsValid(entity); })) {
808         return nullptr;
809     } else {
810         if (!stateManager_.HasComponent(animationEntity)) {
811             stateManager_.Create(animationEntity);
812         }
813         UpdateStateAndTracks(
814             stateManager_, animationTrackManager_, inputManager_, animationEntity, *animationHandle, targetEntities);
815     }
816     // Create animation runtime.
817     auto playback = make_unique<AnimationPlayback>(animationEntity, targetEntities, ecs_);
818 
819     IAnimationPlayback* result = playback.get();
820 
821     animations_.push_back(move(playback));
822 
823     return result;
824 }
825 
DestroyPlayback(IAnimationPlayback * playback)826 void AnimationSystem::DestroyPlayback(IAnimationPlayback* playback)
827 {
828     for (auto it = animations_.cbegin(); it != animations_.cend(); ++it) {
829         if (it->get() == playback) {
830             animations_.erase(it);
831             break;
832         }
833     }
834 }
835 
GetPlaybackCount() const836 size_t AnimationSystem::GetPlaybackCount() const
837 {
838     return animations_.size();
839 }
840 
GetPlayback(size_t index) const841 IAnimationPlayback* AnimationSystem::GetPlayback(size_t index) const
842 {
843     if (index < animations_.size()) {
844         return animations_[index].get();
845     }
846     return nullptr;
847 }
848 
OnComponentEvent(EventType type,const IComponentManager & componentManager,const array_view<const Entity> entities)849 void AnimationSystem::OnComponentEvent(
850     EventType type, const IComponentManager& componentManager, const array_view<const Entity> entities)
851 {
852     switch (type) {
853         case IEcs::ComponentListener::EventType::CREATED: {
854             if (&componentManager == &animationManager_) {
855                 OnAnimationComponentsCreated(entities);
856             }
857             break;
858         }
859         case IEcs::ComponentListener::EventType::DESTROYED: {
860             if (&componentManager == &animationManager_) {
861                 for (const auto entity : entities) {
862                     stateManager_.Destroy(entity);
863                 }
864             }
865             break;
866         }
867         case IEcs::ComponentListener::EventType::MODIFIED: {
868             if (&componentManager == &animationManager_) {
869                 OnAnimationComponentsUpdated(entities);
870             } else if (&componentManager == &animationTrackManager_) {
871                 OnAnimationTrackComponentsUpdated(entities);
872             }
873             break;
874         }
875     }
876 }
877 
OnAnimationComponentsCreated(BASE_NS::array_view<const CORE_NS::Entity> entities)878 void AnimationSystem::OnAnimationComponentsCreated(BASE_NS::array_view<const CORE_NS::Entity> entities)
879 {
880     for (const auto entity : entities) {
881         stateManager_.Create(entity);
882         auto stateHandle = stateManager_.Write(entity);
883         AnimationStateComponent& state = *stateHandle;
884         if (auto animationHandle = animationManager_.Read(entity); animationHandle) {
885             state.trackStates.reserve(animationHandle->tracks.size());
886 
887             for (const auto& trackEntity : animationHandle->tracks) {
888                 state.trackStates.push_back({ Entity(trackEntity), 0U });
889                 AnimationStateComponent::TrackState& trackState = state.trackStates.back();
890 
891                 const uint32_t componentId = initialTransformManager_.GetComponentId(trackState.entity);
892                 if (componentId == IComponentManager::INVALID_COMPONENT_ID) {
893                     // Store initial state for later use.
894                     if (auto trackHandle = animationTrackManager_.Read(trackState.entity); trackHandle) {
895                         InitializeInitialDataComponent(trackState.entity, *trackHandle);
896                     }
897                 }
898             }
899         }
900     }
901 }
902 
OnAnimationComponentsUpdated(BASE_NS::array_view<const CORE_NS::Entity> entities)903 void AnimationSystem::OnAnimationComponentsUpdated(BASE_NS::array_view<const CORE_NS::Entity> entities)
904 {
905     for (const auto entity : entities) {
906         auto stateHandle = stateManager_.Write(entity);
907         auto animationHandle = animationManager_.Read(entity);
908         const auto oldState = stateHandle->state;
909         const auto newState = animationHandle->state;
910         if (newState != AnimationComponent::PlaybackState::STOP) {
911             stateHandle->dirty = true;
912         }
913         if (oldState != newState) {
914             if (oldState == AnimationComponent::PlaybackState::STOP &&
915                 newState == AnimationComponent::PlaybackState::PLAY) {
916                 ResetTime(*stateHandle, *animationHandle);
917                 // if playing backwards and time has been reset to zero jump to the end of the animation.
918                 if ((animationHandle->speed < 0.f) && stateHandle->time == 0.f) {
919                     stateHandle->time = animationHandle->duration;
920                 }
921             } else if (newState == AnimationComponent::PlaybackState::STOP) {
922                 stateHandle->time = 0.0f;
923                 stateHandle->currentLoop = 0;
924                 stateHandle->completed = false;
925             } else if (newState == AnimationComponent::PlaybackState::PLAY) {
926                 stateHandle->completed = false;
927             }
928             stateHandle->state = animationHandle->state;
929         }
930 
931         auto& trackStates = stateHandle->trackStates;
932         if (trackStates.size() != animationHandle->tracks.size()) {
933             trackStates.resize(animationHandle->tracks.size());
934         }
935         auto stateIt = trackStates.begin();
936         for (const auto& trackEntity : animationHandle->tracks) {
937             stateIt->entity = trackEntity;
938             if (auto track = animationTrackManager_.Read(trackEntity); track) {
939                 if (auto inputData = inputManager_.Read(track->timestamps); inputData) {
940                     auto& input = *inputData;
941                     stateIt->length = input.timestamps.size();
942                 }
943             }
944             ++stateIt;
945         }
946     }
947 }
948 
OnAnimationTrackComponentsUpdated(BASE_NS::array_view<const CORE_NS::Entity> entities)949 void AnimationSystem::OnAnimationTrackComponentsUpdated(BASE_NS::array_view<const CORE_NS::Entity> entities)
950 {
951     for (const auto entity : entities) {
952         if (auto trackHandle = animationTrackManager_.Read(entity); trackHandle) {
953             InitializeInitialDataComponent(entity, *trackHandle);
954         }
955     }
956 }
957 
UpdateAnimation(AnimationStateComponent & state,const AnimationComponent & animation,const CORE_NS::ComponentQuery & trackQuery,float delta)958 void AnimationSystem::UpdateAnimation(AnimationStateComponent& state, const AnimationComponent& animation,
959     const CORE_NS::ComponentQuery& trackQuery, float delta)
960 {
961     const auto* const begin = trackQuery_.GetResults().data();
962     switch (animation.state) {
963         case AnimationComponent::PlaybackState::STOP: {
964             // Mark the tracks of this animation as stopped so that they are skipped in later steps.
965             for (const auto& trackState : state.trackStates) {
966                 if (!EntityUtil::IsValid(trackState.entity)) {
967                     continue;
968                 }
969                 if (auto row = trackQuery.FindResultRow(trackState.entity); row) {
970                     trackOrder_.push_back(static_cast<uint32_t>(std::distance(begin, row)));
971                     trackValues_[row->components[1]].stopped = true;
972                 }
973             }
974             // Stopped animation doesn't need any actions.
975             state.dirty = false;
976             return;
977         }
978         case AnimationComponent::PlaybackState::PLAY: {
979             if (!(state.options & AnimationStateComponent::FlagBits::MANUAL_PROGRESS)) {
980                 // Update time (in seconds).
981                 state.time += animation.speed * delta;
982             }
983             break;
984         }
985         case AnimationComponent::PlaybackState::PAUSE: {
986             // Paused animation doesn't need updates after this one.
987             state.dirty = false;
988             break;
989         }
990         default:
991             break;
992     }
993 
994     // All tracks completed?
995     const auto timePosition = state.time + animation.startOffset;
996     if ((timePosition >= (animation.duration + FLT_EPSILON)) || (timePosition <= -FLT_EPSILON)) {
997         const bool repeat = (animation.repeatCount == AnimationComponent::REPEAT_COUNT_INFINITE) || // Infinite repeat.
998                             animation.repeatCount > state.currentLoop; // Need to repeat until repeat count reached.
999         if (repeat) {
1000             // Need to repeat, go to start.
1001             state.currentLoop++;
1002 
1003             ResetTime(state, animation);
1004         } else {
1005             // Animation completed.
1006             state.completed = true;
1007             state.time = Math::clamp(state.time, -FLT_EPSILON, animation.duration + FLT_EPSILON);
1008         }
1009     }
1010 
1011     // Set the animation's state to each track.
1012     const auto forward = (animation.speed >= 0.f);
1013     for (const auto& trackState : state.trackStates) {
1014         auto row = EntityUtil::IsValid(trackState.entity) ? trackQuery.FindResultRow(trackState.entity) : nullptr;
1015         if (row) {
1016             trackOrder_.push_back(static_cast<uint32_t>(std::distance(begin, row)));
1017             auto& track = trackValues_[row->components[1]];
1018             track.timePosition = timePosition;
1019             track.weight = animation.weight;
1020             track.stopped = false;
1021             track.forward = forward;
1022         }
1023     }
1024 }
1025 
InitializeTrackValues()1026 void AnimationSystem::InitializeTrackValues()
1027 {
1028     initTasks_.clear();
1029     initTaskCount_ = 0U;
1030     if (tasks_) {
1031         // Reserve for twice as many tasks as both init and anim task results are in the same vector.
1032         taskResults_.reserve(taskResults_.size() + tasks_ * 2U);
1033 
1034         initTasks_.reserve(remaining_ ? (tasks_ + 1U) : tasks_);
1035         initTaskStart_ = taskId_;
1036         for (size_t i = 0; i < tasks_; ++i) {
1037             auto& task = initTasks_.emplace_back(*this, i * taskSize_, taskSize_);
1038             taskResults_.push_back(threadPool_->Push(IThreadPool::ITask::Ptr { &task }));
1039             ++taskId_;
1040         }
1041     }
1042     if (remaining_ >= systemProperties_.minTaskSize) {
1043         auto& task = initTasks_.emplace_back(*this, tasks_ * taskSize_, remaining_);
1044         taskResults_.push_back(threadPool_->Push(IThreadPool::ITask::Ptr { &task }));
1045         ++taskId_;
1046     } else {
1047         InitializeTrackValues(array_view(static_cast<const uint32_t*>(trackOrder_.data()), remaining_));
1048     }
1049     initTaskCount_ = taskId_ - initTaskStart_;
1050 }
1051 
ResetToInitialTrackValues()1052 void AnimationSystem::ResetToInitialTrackValues()
1053 {
1054     animTasks_.clear();
1055     animTasks_.reserve(remaining_ ? (tasks_ + 1U) : tasks_);
1056     animTaskStart_ = taskId_;
1057     animTaskCount_ = 0;
1058     auto batch = [this](size_t i, size_t offset, size_t count) {
1059         // Wait that initial values for this track batch have been filled
1060         taskResults_[i]->Wait();
1061 
1062         // Start task for calculating which keyframes are used and interpolate between them.
1063         auto& task = animTasks_.emplace_back(*this, offset, count);
1064         taskResults_.push_back(threadPool_->Push(IThreadPool::ITask::Ptr { &task }));
1065         ++taskId_;
1066 
1067         // While task is running reset the target property so we can later sum the results of multiple tracks.
1068         ResetTargetProperties(array_view(static_cast<const uint32_t*>(trackOrder_.data()) + offset, count));
1069     };
1070     for (size_t i = 0U; i < tasks_; ++i) {
1071         batch(i, i * taskSize_, taskSize_);
1072     }
1073     if (remaining_ >= systemProperties_.minTaskSize) {
1074         batch(tasks_, tasks_ * taskSize_, remaining_);
1075     } else {
1076         const auto results = array_view(static_cast<const uint32_t*>(trackOrder_.data()), remaining_);
1077         Calculate(results);
1078         AnimateTracks(results);
1079         ResetTargetProperties(results);
1080     }
1081     animTaskCount_ = taskId_ - animTaskStart_;
1082 }
1083 
WriteUpdatedTrackValues()1084 void AnimationSystem::WriteUpdatedTrackValues()
1085 {
1086     auto batch = [this](size_t i, size_t offset, size_t count) {
1087         // Wait for animation task for this batch to finish.
1088         taskResults_[i + animTaskStart_]->Wait();
1089 
1090         // Apply the result of each track animation to the target property.
1091         ApplyResults(array_view(static_cast<const uint32_t*>(trackOrder_.data()) + offset, count));
1092     };
1093     for (size_t i = 0U; i < tasks_; ++i) {
1094         batch(i, i * taskSize_, taskSize_);
1095     }
1096     if (remaining_ >= systemProperties_.minTaskSize) {
1097         batch(tasks_, tasks_ * taskSize_, remaining_);
1098     } else {
1099         ApplyResults(array_view(static_cast<const uint32_t*>(trackOrder_.data()), remaining_));
1100     }
1101 }
1102 
ResetTargetProperties(array_view<const uint32_t> resultIndices)1103 void AnimationSystem::ResetTargetProperties(array_view<const uint32_t> resultIndices)
1104 {
1105     auto results = trackQuery_.GetResults();
1106     for (auto index : resultIndices) {
1107         const ComponentQuery::ResultRow& row = results[index];
1108         const auto trackId = row.components[1];
1109 
1110         const auto& values = trackValues_[trackId];
1111 
1112         auto trackHandle = animationTrackManager_.Read(trackId);
1113         auto entry = GetEntry(*trackHandle);
1114         if (entry.component && entry.property) {
1115             if (IPropertyHandle* targetHandle = entry.component->GetData(trackHandle->target); targetHandle) {
1116                 // special handling for IPropertyHandle*. need to fetch the property types behind the pointer and
1117                 // update targetHandle.
1118                 if (entry.property->type == PROPERTY_HANDLE_PTR_T) {
1119                     auto componentBase = ScopedHandle<const uint8_t>(targetHandle);
1120                     auto* dynamicProperties = *Cast<IPropertyHandle* const*>(&*componentBase + entry.propertyOffset);
1121                     if (dynamicProperties) {
1122                         entry = FindDynamicProperty(*trackHandle, dynamicProperties, entry);
1123                         targetHandle = dynamicProperties;
1124                     } else {
1125                         continue;
1126                     }
1127                 }
1128                 if (entry.property->type == values.initial.type) {
1129                     if (auto baseAddress = ScopedHandle<uint8_t>(targetHandle); baseAddress) {
1130                         Assign(entry.property->type, &*baseAddress + entry.propertyOffset, values.initial);
1131                     }
1132                 } else {
1133                     CORE_LOG_ONCE_D(to_string(Hash(trackId, entry.property->type.typeHash)),
1134                         "ResetTargetProperties failed, type mismatch %" PRIx64, entry.property->type.typeHash);
1135                 }
1136             }
1137         }
1138     }
1139 }
1140 
InitializeTrackValues(array_view<const uint32_t> resultIndices)1141 void AnimationSystem::InitializeTrackValues(array_view<const uint32_t> resultIndices)
1142 {
1143     auto& initialTransformManager = initialTransformManager_;
1144     auto& trackValues = trackValues_;
1145     auto results = trackQuery_.GetResults();
1146     for (auto index : resultIndices) {
1147         const ComponentQuery::ResultRow& row = results[index];
1148         auto initialHandle = initialTransformManager.Read(row.components[0]);
1149         auto& values = trackValues[row.components[1]];
1150         values.initial = *initialHandle;
1151         values.result = *initialHandle;
1152         values.updated = false;
1153     }
1154 }
1155 
Calculate(array_view<const uint32_t> resultIndices)1156 void AnimationSystem::Calculate(array_view<const uint32_t> resultIndices)
1157 {
1158     auto results = trackQuery_.GetResults();
1159     for (auto index : resultIndices) {
1160         const ComponentQuery::ResultRow& row = results[index];
1161         const auto trackId = row.components[1];
1162         if (trackValues_[trackId].stopped) {
1163             continue;
1164         }
1165         if (auto track = animationTrackManager_.Read(trackId); track) {
1166             if (const auto inputData = inputManager_.Read(track->timestamps); inputData) {
1167                 const array_view<const float> timestamps = inputData->timestamps;
1168                 // Ensure we have data.
1169                 if (!timestamps.empty()) {
1170                     size_t currentFrameIndex;
1171                     size_t nextFrameIndex;
1172 
1173                     const auto currentTime = trackValues_[trackId].timePosition;
1174                     FindFrameIndices(
1175                         trackValues_[trackId].forward, currentTime, timestamps, currentFrameIndex, nextFrameIndex);
1176 
1177                     float currentOffset = 0.f;
1178                     if (currentFrameIndex != nextFrameIndex) {
1179                         const float startFrameTime = timestamps[currentFrameIndex];
1180                         const float endFrameTime = timestamps[nextFrameIndex];
1181 
1182                         currentOffset = (currentTime - startFrameTime) / (endFrameTime - startFrameTime);
1183                         currentOffset = std::clamp(currentOffset, 0.0f, 1.0f);
1184                     }
1185 
1186                     frameIndices_[trackId] = FrameData { currentOffset, currentFrameIndex, nextFrameIndex };
1187                 }
1188             }
1189         }
1190     }
1191 }
1192 
AnimateTracks(array_view<const uint32_t> resultIndices)1193 void AnimationSystem::AnimateTracks(array_view<const uint32_t> resultIndices)
1194 {
1195     auto results = trackQuery_.GetResults();
1196     for (auto index : resultIndices) {
1197         const ComponentQuery::ResultRow& row = results[index];
1198         const auto trackId = row.components[1];
1199         auto& trackValues = trackValues_[trackId];
1200         if (trackValues.stopped) {
1201             continue;
1202         }
1203         if (auto trackHandle = animationTrackManager_.Read(trackId); trackHandle) {
1204             if (auto outputHandle = outputManager_.Read(trackHandle->data); outputHandle) {
1205                 if (outputHandle->data.empty()) {
1206                     continue;
1207                 }
1208 
1209                 auto entry = GetEntry(*trackHandle);
1210                 if (!entry.component || !entry.property) {
1211                     return;
1212                 }
1213 
1214                 // special handling for IPropertyHandle*. need to fetch the property types behind the pointer.
1215                 if (entry.property->type == PROPERTY_HANDLE_PTR_T) {
1216                     if (const IPropertyHandle* targetHandle = entry.component->GetData(trackHandle->target);
1217                         targetHandle) {
1218                         auto componentBase = ScopedHandle<const uint8_t>(targetHandle);
1219                         auto* dynamicProperties =
1220                             *Cast<const IPropertyHandle* const*>(&*componentBase + entry.propertyOffset);
1221                         if (dynamicProperties) {
1222                             entry = FindDynamicProperty(*trackHandle, dynamicProperties, entry);
1223                         }
1224                     }
1225                 }
1226 
1227                 if ((outputHandle->type != entry.property->type) || (outputHandle->type != trackValues.initial.type) ||
1228                     (outputHandle->type != trackValues.result.type)) {
1229                     CORE_LOG_ONCE_D(to_string(Hash(trackId, outputHandle->type)),
1230                         "AnimateTrack failed, unexpected type %" PRIx64, outputHandle->type);
1231                     continue;
1232                 }
1233 
1234                 // spline interpolation takes three values: in-tangent, data point, and out-tangent
1235                 const size_t inputCount =
1236                     (trackHandle->interpolationMode == AnimationTrackComponent::Interpolation::SPLINE) ? 3U : 1U;
1237                 const FrameData& currentFrame = frameIndices_[trackId];
1238                 const InterpolationData interpolationData { trackHandle->interpolationMode,
1239                     currentFrame.currentFrameIndex * inputCount, currentFrame.nextFrameIndex * inputCount,
1240                     currentFrame.currentOffset, trackValues.weight };
1241 
1242                 // initial data is needed only when weight < 1 or there are multiple animations targeting the same
1243                 // property.
1244                 // identifying such tracks would save updating the initial data in AnimationSystem::Update and
1245                 // interpolating between initial and result.
1246                 AnimateTrack(
1247                     entry.property->type, interpolationData, *outputHandle, trackValues.initial, trackValues.result);
1248                 trackValues.updated = true;
1249             }
1250         }
1251     }
1252 }
1253 
ApplyResults(array_view<const uint32_t> resultIndices)1254 void AnimationSystem::ApplyResults(array_view<const uint32_t> resultIndices)
1255 {
1256     auto results = trackQuery_.GetResults();
1257     for (auto index : resultIndices) {
1258         const ComponentQuery::ResultRow& row = results[index];
1259         const auto trackId = row.components[1];
1260         auto& values = trackValues_[trackId];
1261 
1262         if (!values.stopped && values.updated) {
1263             values.updated = false;
1264             auto trackHandle = animationTrackManager_.Read(trackId);
1265             auto entry = GetEntry(*trackHandle);
1266             if (entry.component && entry.property) {
1267                 if (IPropertyHandle* targetHandle = entry.component->GetData(trackHandle->target); targetHandle) {
1268                     // special handling for IPropertyHandle*. need to fetch the property types behind the pointer and
1269                     // update targetHandle.
1270                     if (entry.property->type == PROPERTY_HANDLE_PTR_T) {
1271                         auto componentBase = ScopedHandle<const uint8_t>(targetHandle);
1272                         auto* dynamicProperties =
1273                             *Cast<IPropertyHandle* const*>(&*componentBase + entry.propertyOffset);
1274                         if (dynamicProperties) {
1275                             entry = FindDynamicProperty(*trackHandle, dynamicProperties, entry);
1276                             targetHandle = dynamicProperties;
1277                         } else {
1278                             continue;
1279                         }
1280                     }
1281                     if (entry.property->type == values.result.type) {
1282                         if (auto baseAddress = ScopedHandle<uint8_t>(targetHandle); baseAddress) {
1283                             Add(entry.property->type, &*baseAddress + entry.propertyOffset, values.result);
1284                         }
1285                     } else {
1286                         CORE_LOG_ONCE_D(to_string(Hash(trackId, entry.property->type.typeHash)),
1287                             "ApplyResults failed, type mismatch %" PRIx64, entry.property->type.typeHash);
1288                     }
1289                 }
1290             }
1291         }
1292     }
1293 }
1294 
GetEntry(const AnimationTrackComponent & track)1295 const AnimationSystem::PropertyEntry& AnimationSystem::GetEntry(const AnimationTrackComponent& track)
1296 {
1297     // Cache component manager and property lookups
1298     const auto key = Hash(track.component, track.property);
1299     auto pos = std::lower_bound(std::begin(propertyCache_), std::end(propertyCache_), key,
1300         [](const PropertyEntry& element, uint64_t value) { return element.componentAndProperty < value; });
1301     // Add new manager-propery combinations
1302     if ((pos == std::end(propertyCache_)) || (pos->componentAndProperty != key)) {
1303         pos = propertyCache_.insert(pos, PropertyEntry { key, ecs_.GetComponentManager(track.component), 0U, nullptr });
1304     }
1305     // Try to get the component manager if it's missing
1306     if (!pos->component) {
1307         pos->component = ecs_.GetComponentManager(track.component);
1308     }
1309 
1310     // Try to get the property type if it's missing and there's a valid target
1311     if (pos->component && !pos->property && EntityUtil::IsValid(track.target)) {
1312         if (IPropertyHandle* targetHandle = pos->component->GetData(track.target); targetHandle) {
1313             const auto propertyOffset = PropertyData::FindProperty(targetHandle->Owner()->MetaData(), track.property);
1314             if (propertyOffset.property) {
1315                 pos->propertyOffset = propertyOffset.offset;
1316                 pos->property = propertyOffset.property;
1317             }
1318         }
1319     }
1320     return *pos;
1321 }
1322 
InitializeInitialDataComponent(Entity trackEntity,const AnimationTrackComponent & animationTrack)1323 void AnimationSystem::InitializeInitialDataComponent(Entity trackEntity, const AnimationTrackComponent& animationTrack)
1324 {
1325     if (auto entry = GetEntry(animationTrack); entry.component && entry.property) {
1326         if (IPropertyHandle* targetHandle = entry.component->GetData(animationTrack.target); targetHandle) {
1327             initialTransformManager_.Create(trackEntity);
1328             auto dstHandle = initialTransformManager_.Write(trackEntity);
1329 
1330             auto componentBase = ScopedHandle<const uint8_t>(targetHandle);
1331             if (entry.property->type != PROPERTY_HANDLE_PTR_T) {
1332                 auto src = array_view(&*componentBase + entry.propertyOffset, entry.property->size);
1333                 CopyInitialDataComponent(*dstHandle, entry.property, src);
1334             } else {
1335                 // special handling for property handles
1336                 auto* dynamicProperties = *Cast<const IPropertyHandle* const*>(&*componentBase + entry.propertyOffset);
1337                 if (dynamicProperties) {
1338                     entry = FindDynamicProperty(animationTrack, dynamicProperties, entry);
1339                     auto dynamicPropertiesBase = ScopedHandle<const uint8_t>(dynamicProperties);
1340                     auto src = array_view(&*dynamicPropertiesBase + entry.propertyOffset, entry.property->size);
1341                     CopyInitialDataComponent(*dstHandle, entry.property, src);
1342                 }
1343             }
1344         }
1345     }
1346 }
1347 
IAnimationSystemInstance(IEcs & ecs)1348 ISystem* IAnimationSystemInstance(IEcs& ecs)
1349 {
1350     return new AnimationSystem(ecs);
1351 }
1352 
IAnimationSystemDestroy(ISystem * instance)1353 void IAnimationSystemDestroy(ISystem* instance)
1354 {
1355     delete static_cast<AnimationSystem*>(instance);
1356 }
1357 CORE3D_END_NAMESPACE()
1358