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#include "ecs_animation.h"
16
17#include <algorithm>
18#include <PropertyTools/property_api_impl.inl>
19#include <PropertyTools/property_data.h>
20
21#include <3d/ecs/systems/intf_animation_system.h>
22#include <3d/ecs/systems/intf_node_system.h>
23#include <core/intf_engine.h>
24
25#include <meta/api/make_callback.h>
26
27#include "ecs_util.h"
28
29using namespace BASE_NS;
30using namespace CORE_NS;
31using namespace CORE3D_NS;
32using namespace META_NS;
33
34SCENE_BEGIN_NAMESPACE()
35
36namespace {
37
38bool EndsWith(string_view str, string_view postfix)
39{
40    return str.size() >= postfix.size() && 0 == str.compare(str.size() - postfix.size(), postfix.size(), postfix);
41}
42
43template<typename type>
44const BASE_NS::Uid GetArrayTypeUid()
45{
46    return UidFromType<type[]>();
47}
48
49PropertyData::PropertyOffset RLock(IPropertyHandle& targetHandle, string_view property)
50{
51    PropertyData pData;
52
53    string path, name;
54    auto containerHandle = ResolveContainerProperty(targetHandle, string(property), path, name);
55    if (containerHandle) {
56        if (auto po = pData.RLock(*containerHandle, name); po) {
57            return po;
58        }
59    }
60
61    if (auto po = pData.RLock(targetHandle, property); po) {
62        return po;
63    }
64
65    return PropertyData::PropertyOffset();
66}
67
68BASE_NS::string ResolvePathToAnimationRoot(IEcs& ecs, Entity root, Entity target)
69{
70    BASE_NS::string result;
71
72    CORE3D_NS::INodeSystem* nodeSystem = GetSystem<INodeSystem>(ecs);
73    if (nodeSystem) {
74        auto rootNode = nodeSystem->GetNode(root);
75        auto node = nodeSystem->GetNode(target);
76        if (rootNode && node) {
77            if (rootNode->IsAncestorOf(*node)) {
78                auto current = node;
79                while (current) {
80                    // Found root node, break.
81                    if (current == rootNode) {
82                        break;
83                    }
84
85                    // Add this to path.
86                    if (result.empty()) {
87                        result = current->GetName();
88                    } else {
89                        result = current->GetName() + "/" + result;
90                    }
91
92                    // Process parent next.
93                    current = current->GetParent();
94                }
95            }
96        }
97    }
98
99    return result;
100}
101
102void UpdateTrackTargets(IEcs& ecs, Entity animationEntity, Entity rootNode)
103{
104    auto& nameManager_ = *GetManager<INameComponentManager>(ecs);
105    auto animationManager = GetManager<IAnimationComponentManager>(ecs);
106    auto animationTrackManager = GetManager<IAnimationTrackComponentManager>(ecs);
107    auto& entityManager = ecs.GetEntityManager();
108
109    auto* nodeSystem = GetSystem<INodeSystem>(ecs);
110    CORE_ASSERT(nodeSystem);
111    if (!nodeSystem) {
112        return;
113    }
114    auto* node = nodeSystem->GetNode(rootNode);
115    CORE_ASSERT(node);
116    if (!node) {
117        return;
118    }
119
120    if (const ScopedHandle<const AnimationComponent> animationData = animationManager->Read(animationEntity);
121        animationData) {
122        vector<Entity> targetEntities;
123        targetEntities.reserve(animationData->tracks.size());
124        std::transform(animationData->tracks.begin(), animationData->tracks.end(), std::back_inserter(targetEntities),
125            [&nameManager = nameManager_, &node](const Entity& trackEntity) {
126                if (auto nameHandle = nameManager.Read(trackEntity); nameHandle) {
127                    if (nameHandle->name.empty()) {
128                        return node->GetEntity();
129                    } else {
130                        if (auto targetNode = node->LookupNodeByPath(nameHandle->name); targetNode) {
131                            return targetNode->GetEntity();
132                        }
133                    }
134                }
135                return Entity {};
136            });
137        if (animationData->tracks.size() == targetEntities.size()) {
138            auto targetIt = targetEntities.begin();
139            for (const auto& trackEntity : animationData->tracks) {
140                if (auto track = animationTrackManager->Write(trackEntity); track) {
141                    if (track->target) {
142                        CORE_LOG_D("AnimationTrack %s already targetted",
143                            to_hex(static_cast<const Entity&>(track->target).id).data());
144                    }
145                    track->target = entityManager.GetReferenceCounted(*targetIt);
146                }
147                ++targetIt;
148            }
149        }
150    }
151}
152} // namespace
153
154IProperty::Ptr EcsTrackAnimation::Keyframes() const
155{
156    return keyframes_;
157}
158
159size_t EcsTrackAnimation::AddKeyframe(float timestamp, const META_NS::IAny::ConstPtr& from)
160{
161    // TODO: Implement.
162    return {};
163}
164
165bool EcsTrackAnimation::RemoveKeyframe(size_t index)
166{
167    // TODO: Implement.
168    return false;
169}
170void EcsTrackAnimation::RemoveAllKeyframes()
171{
172    // TODO: Implement.
173}
174
175bool EcsTrackAnimation::Build(const META_NS::IMetadata::Ptr&)
176{
177    auto& registry = META_NS::GetObjectRegistry();
178
179    auto arr = ConstructArrayProperty<float>("Keyframes", {}, META_NS::ObjectFlagBits::NONE);
180    keyframes_ = arr.GetProperty();
181    if (!keyframes_) {
182        CORE_LOG_E("Invalid property type for EcsTrackAnimation: <float>");
183        return false;
184    }
185    return true;
186}
187
188void EcsTrackAnimation::Step(const META_NS::IClock::ConstPtr& clock)
189{
190    // TODO: Implement.
191}
192
193void EcsTrackAnimation::Start()
194{
195    // TODO: Implement.
196    META_NS::Invoke<META_NS::IOnChanged>(OnStarted());
197}
198
199void EcsTrackAnimation::Stop()
200{
201}
202
203void EcsTrackAnimation::Pause()
204{
205}
206
207void EcsTrackAnimation::Restart()
208{
209    Stop();
210    Start();
211}
212
213void EcsTrackAnimation::Finish()
214{
215    // TODO: Implement.
216}
217
218void EcsTrackAnimation::Seek(float position)
219{
220    // TODO: Implement.
221}
222
223BASE_NS::string EcsAnimation::GetName() const
224{
225    return Name()->GetValue();
226}
227
228bool EcsAnimation::Build(const META_NS::IMetadata::Ptr& /*data*/)
229{
230    GetSelf<META_NS::IRequiredInterfaces>()->SetRequiredInterfaces({ IEcsTrackAnimation::UID });
231
232    // TODO: Loop count.
233    // TODO: Running.
234    Name()->OnChanged()->AddHandler(MakeCallback<IOnChanged>([this] { OnNamePropertyChanged(); }));
235    Duration()->OnChanged()->AddHandler(MakeCallback<IOnChanged>([this] { OnDurationPropertyChanged(); }));
236    Progress()->OnChanged()->AddHandler(MakeCallback<IOnChanged>([this] { OnProgressPropertyChanged(); }));
237
238    return true;
239}
240
241bool EcsAnimation::SetRootEntity(CORE_NS::Entity entity)
242{
243    if (EntityUtil::IsValid(root_)) {
244        // Cannot change root entity, once set.
245        return entity == root_;
246    }
247
248    root_ = entity;
249    return true;
250}
251
252CORE_NS::Entity EcsAnimation::GetRootEntity() const
253{
254    return root_;
255}
256
257bool EcsAnimation::Retarget(CORE_NS::Entity entity)
258{
259    if (ecs_) {
260        UpdateTrackTargets(*ecs_, GetEntity(), entity);
261        root_ = entity;
262
263        return true;
264    }
265    return false;
266}
267
268void EcsAnimation::SetEntity(CORE_NS::IEcs& ecs, CORE_NS::Entity entity)
269{
270    ecs_ = &ecs;
271    entity_ = ecs_->GetEntityManager().GetReferenceCounted(entity);
272    animationStateManager_ = nullptr;
273
274    for (auto manager : ecs.GetComponentManagers()) {
275        if (manager->GetName() == "AnimationStateComponent") {
276            animationStateManager_ = manager;
277            break;
278        }
279    }
280
281    animationManager_ = GetManager<IAnimationComponentManager>(*ecs_);
282    animationTrackManager_ = GetManager<IAnimationTrackComponentManager>(*ecs_);
283    animationInputManager_ = GetManager<IAnimationInputComponentManager>(*ecs_);
284    animationOutputManager_ = GetManager<IAnimationOutputComponentManager>(*ecs_);
285    nameManager_ = GetManager<INameComponentManager>(*ecs_);
286
287    OnAnimationNameChanged(IEcs::ComponentListener::EventType::MODIFIED);
288    OnAnimationChanged(IEcs::ComponentListener::EventType::MODIFIED);
289
290    if (auto ecsListener = ecsListener_.lock()) {
291        auto po = GetSelf<SCENE_NS::IEcsProxyObject>();
292        ecsListener->AddEntity(entity_, po, *nameManager_);
293        ecsListener->AddEntity(entity_, po, *animationStateManager_);
294        ecsListener->AddEntity(entity_, po, *animationManager_);
295        ecsListener->AddEntity(entity_, po, *animationTrackManager_);
296        ecsListener->AddEntity(entity_, po, *animationInputManager_);
297    }
298
299    // If the animation root is not set, then try to resolve it.
300    if (!EntityUtil::IsValid(root_)) {
301        root_ = TryResolveAnimationRoot();
302    }
303
304    META_ACCESS_PROPERTY(Valid)->SetValue(true);
305}
306
307CORE_NS::Entity EcsAnimation::TryResolveAnimationRoot()
308{
309    if (!ecs_) {
310        return CORE_NS::Entity {};
311    }
312
313    CORE3D_NS::INodeSystem* nodeSystem = GetSystem<INodeSystem>(*ecs_);
314
315    // We will try to resolve the animation root from the first animation track.
316    auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
317    if (tracks.empty()) {
318        // No way to resolve, no tracks.
319        return {};
320    }
321
322    IEcsTrackAnimation::Ptr track = tracks.at(0);
323
324    BASE_NS::string path;
325    if (auto nameHandle = nameManager_->Read(track->GetEntity()); nameHandle) {
326        // The name of the track actually represents the path to the animated entity.
327        path = nameHandle->name;
328    }
329
330    ISceneNode* node = nullptr;
331    if (auto trackHandle = animationTrackManager_->Read(track->GetEntity()); trackHandle) {
332        // This is the node that is being animated by the path.
333        node = nodeSystem->GetNode(trackHandle->target);
334        if (!node) {
335            return {};
336        }
337
338        // Resolve the path to parent.
339        while (!path.empty()) {
340            BASE_NS::string suffix = node->GetName();
341            if (EndsWith(path, suffix)) {
342                // Remove name of this node from the path.
343                path = string(path.substr(0, path.length() - suffix.length()));
344                if (EndsWith(path, "/")) {
345                    path = string(path.substr(0, path.length() - 1));
346                }
347                // Go up the tree.
348                node = node->GetParent();
349            } else {
350                // Error.
351                node = nullptr;
352                break;
353            }
354        }
355    }
356
357    if (node) {
358        SetRootEntity(node->GetEntity());
359    }
360
361    return node ? node->GetEntity() : Entity {};
362}
363
364void EcsAnimation::AddAnimation(const IAnimation::Ptr& animation)
365{
366    GetSelf<META_NS::IContainer>()->Add(interface_pointer_cast<IObject>(animation));
367    // ToDo: Can we rely that someone has added an listener for tracks
368}
369
370void EcsAnimation::RemoveAnimation(const IAnimation::Ptr& animation)
371{
372    GetSelf<META_NS::IContainer>()->Remove(interface_pointer_cast<IObject>(animation));
373    // ToDo: In principle, the tracks should deal common listener and removing parent, might be worth checking though
374}
375
376vector<IAnimation::Ptr> EcsAnimation::GetAnimations() const
377{
378    return META_NS::GetAll<IAnimation>(GetSelf<META_NS::IContainer>());
379}
380
381void EcsAnimation::DoComponentEvent(
382    IEcs::ComponentListener::EventType type, const IComponentManager& componentManager, const CORE_NS::Entity& entity)
383{
384    bool isAnimationNameChange = componentManager.GetUid() == INameComponentManager::UID;
385    bool isAnimationChange = componentManager.GetUid() == IAnimationComponentManager::UID;
386    bool isAnimationStateChange = componentManager.GetUid() == animationStateManager_->GetUid();
387    bool isAnimationInputChange = componentManager.GetUid() == IAnimationInputComponentManager::UID;
388    bool isAnimationTrackChange = componentManager.GetUid() == IAnimationTrackComponentManager::UID;
389
390    if (isAnimationChange || isAnimationStateChange) {
391        // For animation and animation state, we are interested about changes concerning entity_.
392
393        if (isAnimationChange) {
394            // Animation has changed for this entity.
395            OnAnimationChanged(type);
396        } else if (isAnimationStateChange) {
397            // Animation state has changed for this entity.
398            OnAnimationStateChanged(type);
399        }
400    } else if (isAnimationTrackChange) {
401        OnAnimationTracksChanged(type, entity);
402    } else if (isAnimationInputChange) {
403        OnAnimationInputsChanged(type, entity);
404    } else if (isAnimationNameChange) {
405        OnAnimationNameChanged(type);
406    }
407}
408
409void EcsAnimation::OnAnimationStateChanged(IEcs::ComponentListener::EventType event)
410{
411    // This function is triggered when ECS dispatch changes at the end of the frame.
412    if (!ecs_ || event != IEcs::ComponentListener::EventType::MODIFIED) {
413        return;
414    }
415
416    // Propagate changes back to object's properties.
417    auto stateHandle = animationStateManager_->GetData(entity_);
418    const auto metaData = stateHandle->Owner()->MetaData();
419    for (const auto& data : metaData) {
420        if (data.name == "time") {
421            auto* time = static_cast<const float*>(
422                static_cast<const void*>(static_cast<const uint8_t*>(stateHandle->RLock()) + data.offset));
423
424            updateGuard_ = true;
425            auto duration = GetValue(TotalDuration()).ToSecondsFloat();
426            if (duration > 0) {
427                SetProgress(*time / duration);
428            } else {
429                SetProgress(0);
430            }
431            updateGuard_ = false;
432
433            stateHandle->RUnlock();
434            break;
435        }
436    }
437}
438
439void EcsAnimation::OnAnimationNameChanged(IEcs::ComponentListener::EventType event)
440{
441    // This function is triggered when ECS dispatch changes at the end of the frame.
442    if (!ecs_ || event != IEcs::ComponentListener::EventType::MODIFIED) {
443        return;
444    }
445
446    if (nameManager_->HasComponent(GetEntity())) {
447        updateGuard_ = true;
448        SetValue(Name(), nameManager_->Get(GetEntity()).name);
449        updateGuard_ = false;
450    }
451}
452
453void EcsAnimation::OnAnimationChanged(IEcs::ComponentListener::EventType event)
454{
455    // This function is triggered when ECS dispatch changes at the end of the frame.
456    if (!ecs_ || event == IEcs::ComponentListener::EventType::DESTROYED) {
457        return;
458    }
459
460    // Propagate changes back to object's properties.
461    auto handle = animationManager_->Read(entity_);
462    if (handle) {
463        updateGuard_ = true;
464        repeatCount_ = static_cast<int32_t>(handle->repeatCount);
465        // Update animation duration.
466        SetValue(META_ACCESS_PROPERTY(Duration), TimeSpan::Seconds(handle->duration));
467        updateGuard_ = false;
468
469        // Update animation tracks, if needed.
470        if (IsAnimationTrackArrayModified()) {
471            GatherAnimationTracks();
472        }
473    }
474}
475
476void EcsAnimation::OnAnimationTracksChanged(IEcs::ComponentListener::EventType event, CORE_NS::Entity entity)
477{
478    bool animationHasNewOrRemovedTracks = false;
479
480    auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
481
482    // Go through all created / modified / destroyed track entities.
483    auto iterator = std::find_if(
484        tracks.begin(), tracks.end(), [entity](const auto& track) { return track->GetEntity() == entity; });
485    if (iterator != tracks.end()) {
486        // Animation track was reported changed.
487        if (event == IEcs::ComponentListener::EventType::MODIFIED) {
488            // An animation track has been modified, so update it.
489            auto index = std::distance(tracks.begin(), iterator);
490
491            auto track = tracks.at(index);
492            OnAnimationTrackChanged(*track, entity);
493
494        } else {
495            // If we have new or removed tracks, we will simply update all.
496            animationHasNewOrRemovedTracks = true;
497        }
498    }
499
500    // If new or removed tracks, refresh all.
501    if (animationHasNewOrRemovedTracks) {
502        GatherAnimationTracks();
503    }
504}
505
506void EcsAnimation::OnAnimationInputsChanged(IEcs::ComponentListener::EventType event, CORE_NS::Entity entity)
507{
508    if (!ecs_) {
509        return;
510    }
511
512    auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
513    // Go through all created / modified / destroyed track entities.
514    for (size_t i = 0; i < tracks.size(); ++i) {
515        auto track = tracks.at(i);
516        if (auto trackHandle = animationTrackManager_->Read(track->GetEntity()); trackHandle) {
517            if (trackHandle->timestamps == entity) {
518                // Timestamps for this track have changed.
519                OnAnimationTimestampsChanged(*track, trackHandle->timestamps);
520                break;
521            }
522        }
523    }
524}
525
526bool EcsAnimation::IsAnimationTrackArrayModified()
527{
528    if (!ecs_) {
529        return false;
530    }
531
532    auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
533    auto handle = animationManager_->Read(entity_);
534    if (handle) {
535        if (handle->tracks.size() != tracks.size()) {
536            // The amount of tracks is different.
537            return true;
538        }
539
540        for (size_t i = 0; i < handle->tracks.size(); ++i) {
541            if (tracks.at(i)->GetEntity() != handle->tracks[i]) {
542                // Track entity id has changed.
543                return true;
544            }
545        }
546    }
547
548    return false;
549}
550
551void EcsAnimation::GatherAnimationTracks()
552{
553    if (!ecs_) {
554        return;
555    }
556
557    // Take copy of the animation tracks.
558    auto oldTracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
559
560    // Clear tracks.
561    GetSelf<META_NS::IContainer>()->RemoveAll();
562
563    // Update animation tracks.
564    auto handle = animationManager_->Read(entity_);
565    if (handle) {
566        for (const auto& track : handle->tracks) {
567            // See if we have this track stored.
568            auto it = std::find_if(oldTracks.begin(), oldTracks.end(), [track](const auto& t) {
569                if (t->GetEntity() == track) {
570                    return true;
571                }
572                return false;
573            });
574
575            IEcsTrackAnimation::Ptr animationTrack;
576            if (it != oldTracks.end()) {
577                animationTrack = *it;
578            } else {
579                animationTrack =
580                    META_NS::GetObjectRegistry().Create<IEcsTrackAnimation>(SCENE_NS::ClassId::EcsTrackAnimation);
581                interface_pointer_cast<SCENE_NS::IEcsProxyObject>(animationTrack)
582                    ->SetCommonListener(ecsListener_.lock());
583            }
584
585            OnAnimationTrackChanged(*animationTrack, track);
586            GetSelf<META_NS::IContainer>()->Add(interface_pointer_cast<IObject>(animationTrack));
587        }
588    }
589}
590
591void EcsAnimation::OnAnimationTrackChanged(IEcsTrackAnimation& track, Entity trackEntity)
592{
593    if (!ecs_ || updateGuard_) {
594        return;
595    }
596
597    if (auto trackHandle = animationTrackManager_->Read(trackEntity); trackHandle) {
598        auto nameComponent = nameManager_->Get(trackHandle->target.operator Entity());
599
600        track.SetEntity(trackEntity);
601
602        auto named = interface_cast<INamed>(&track);
603        if (named) {
604            SetValue(named->Name(), nameComponent.name + '.' + trackHandle->property.data());
605        }
606        OnAnimationTimestampsChanged(track, trackHandle->timestamps);
607    }
608}
609
610void EcsAnimation::UpdateTimestamps(IEcsTrackAnimation& track, Entity timestampEntity)
611{
612    if (!ecs_) {
613        return;
614    }
615
616    if (animationInputManager_->HasComponent(timestampEntity)) {
617        const auto timestamps = animationInputManager_->Get(timestampEntity);
618        const auto trackAnimation = interface_cast<ITrackAnimation>(&track);
619        const auto timedAnimation = interface_cast<ITimedAnimation>(&track);
620
621        vector<float> times;
622        times.reserve(timestamps.timestamps.size());
623
624        const auto duration = META_NS::GetValue(timedAnimation->Duration()).ToSecondsFloat();
625
626        for (const auto timestamp : timestamps.timestamps) {
627            const auto offset = (duration > 0) ? (timestamp / duration) : 0.0f;
628            times.push_back(offset);
629        }
630
631        trackAnimation->Timestamps()->SetValue(times);
632    }
633}
634void EcsAnimation::OnAnimationTimestampsChanged(IEcsTrackAnimation& track, Entity timestampEntity)
635{
636    if (!ecs_ || updateGuard_) {
637        return;
638    }
639
640    UpdateTimestamps(track, timestampEntity);
641
642    // If any of the tracks in this animation is sharing the same timestamps, then make all tracks read-only.
643    bool hasSharedTimestamps = false;
644
645    auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
646    for (const auto& other : tracks) {
647        if (other->GetEntity() != track.GetEntity()) {
648            if (auto trackHandle = animationTrackManager_->Read(other->GetEntity()); trackHandle) {
649                if (trackHandle->timestamps == timestampEntity) {
650                    hasSharedTimestamps = true;
651                    break;
652                }
653            }
654        }
655    }
656
657    if (GetValue(ReadOnly()) != hasSharedTimestamps) {
658        META_ACCESS_PROPERTY(ReadOnly)->SetValue(hasSharedTimestamps);
659    }
660}
661
662void EcsAnimation::SetDuration(uint32_t ms)
663{
664    if (!ecs_) {
665        return;
666    }
667
668    const auto newDuration = TimeSpan::Milliseconds(ms);
669    const auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
670    for (const auto& track : tracks) {
671        if (const auto trackHandle = animationTrackManager_->Read(track->GetEntity()); trackHandle) {
672            interface_cast<META_NS::ITimedAnimation>(track)->Duration()->SetValue(newDuration);
673            UpdateTimestamps(*track, trackHandle->timestamps);
674        }
675    }
676
677    META_ACCESS_PROPERTY(Duration)->SetValue(newDuration);
678}
679
680void EcsAnimation::AddKey(IEcsTrackAnimation::Ptr track, float time)
681{
682    if (!ecs_) {
683        return;
684    }
685
686    updateGuard_ = true;
687
688    auto handle = animationTrackManager_->Read(track->GetEntity());
689    if (handle) {
690        auto propertyData = GetProperty(handle->component, handle->target, handle->property);
691        if (propertyData) {
692            SetKeyFrameData(track->GetEntity(), time, propertyData.data);
693        }
694    }
695
696    UpdateAnimationTrackDuration(track->GetEntity());
697
698    updateGuard_ = false;
699
700    uint32_t timeMs = static_cast<uint32_t>(time * 1000.0f);
701    if (GetValue(Duration()).ToMilliseconds() < timeMs) {
702        META_ACCESS_PROPERTY(Duration)->SetValue(TimeSpan::Milliseconds(timeMs));
703    }
704
705    OnAnimationTrackChanged(*track, track->GetEntity());
706}
707
708void EcsAnimation::RemoveKey(IEcsTrackAnimation::Ptr track, uint32_t index)
709{
710    if (!ecs_) {
711        return;
712    }
713
714    updateGuard_ = true;
715
716    // remove timestamp at pos
717    if (auto animationTrack = animationTrackManager_->Read(track->GetEntity()); animationTrack) {
718        auto stampsEntity = animationTrack->timestamps.operator Entity();
719        auto inputData = animationInputManager_->Write(stampsEntity);
720        vector<float>::iterator iit = inputData->timestamps.begin();
721        inputData->timestamps.erase(iit + index);
722        // remove data at pos
723        auto dataEntity = animationTrack->data.operator Entity();
724        if (auto outputData = animationOutputManager_->Write(dataEntity); outputData) {
725            auto targetEntity = animationTrack->target.operator Entity();
726
727            auto manager = ecs_->GetComponentManager(animationTrack->component);
728
729            auto target = manager->GetData(targetEntity);
730            auto poffset = RLock(*target, animationTrack->property);
731            vector<uint8_t>::iterator oit = outputData->data.begin();
732            outputData->data.erase(oit + (index * poffset.property->size),
733                oit + (index * poffset.property->size) + poffset.property->size);
734        }
735    }
736
737    updateGuard_ = false;
738
739    OnAnimationTrackChanged(*track, track->GetEntity());
740}
741
742void EcsAnimation::UpdateKey(IEcsTrackAnimation::Ptr track, uint32_t oldKeyIndex, uint32_t newKeyIndex, float time)
743{
744    if (!ecs_) {
745        return;
746    }
747
748    updateGuard_ = true;
749
750    {
751        auto animationTrack = animationTrackManager_->Read(track->GetEntity());
752        auto stampsEntity = animationTrack->timestamps.operator Entity();
753        auto inputData = animationInputManager_->Write(stampsEntity);
754
755        auto dataEntity = animationTrack->data.operator Entity();
756        auto outputData = animationOutputManager_->Write(dataEntity);
757        auto targetEntity = animationTrack->target.operator Entity();
758
759        vector<float>::iterator iit = inputData->timestamps.begin();
760        inputData->timestamps.erase(iit + oldKeyIndex);
761
762        auto manager = ecs_->GetComponentManager(animationTrack->component);
763
764        auto target = manager->GetData(targetEntity);
765        auto poffset = RLock(*target, animationTrack->property);
766        if (poffset) {
767            vector<uint8_t>::iterator oit = outputData->data.begin();
768
769            vector<uint8_t> moveData(oit + (oldKeyIndex * poffset.property->size),
770                oit + (oldKeyIndex * poffset.property->size) + poffset.property->size);
771
772            outputData->data.erase(oit + (oldKeyIndex * poffset.property->size),
773                oit + (oldKeyIndex * poffset.property->size) + poffset.property->size);
774
775            inputData->timestamps.insert(iit + newKeyIndex, time);
776
777            vector<uint8_t>::iterator noit = outputData->data.begin();
778            outputData->data.insert(
779                noit + (newKeyIndex * poffset.property->size), std::begin(moveData), std::end(moveData));
780        }
781    }
782
783    updateGuard_ = false;
784
785    uint32_t timeMs = static_cast<uint32_t>(time * 1000.0f);
786    if (GetValue(Duration()).ToMilliseconds() < timeMs) {
787        META_ACCESS_PROPERTY(Duration)->SetValue(TimeSpan::Milliseconds(timeMs));
788    }
789
790    OnAnimationTrackChanged(*track, track->GetEntity());
791}
792
793void EcsAnimation::OnDestroyAnimationTrack(IEcsTrackAnimation::Ptr track)
794{
795    if (ecs_) {
796        if (auto trackHandle = animationTrackManager_->Read(track->GetEntity()); trackHandle) {
797            // Destroy timestamps and keys.
798            ecs_->GetEntityManager().Destroy(trackHandle->timestamps);
799            ecs_->GetEntityManager().Destroy(trackHandle->data);
800        }
801
802        {
803            // Remove track from animation.
804            const auto animationHandle = animationManager_->Write(GetEntity());
805            auto it = std::find(animationHandle->tracks.begin(), animationHandle->tracks.end(), track->GetEntity());
806            if (it != animationHandle->tracks.end()) {
807                animationHandle->tracks.erase(it);
808            }
809        }
810
811        // Destroy track.
812        ecs_->GetEntityManager().Destroy(track->GetEntity());
813    }
814
815    GetSelf<META_NS::IContainer>()->Remove(interface_pointer_cast<IObject>(track));
816}
817
818BASE_NS::vector<CORE_NS::EntityReference> EcsAnimation::GetAllRelatedEntities() const
819{
820    BASE_NS::vector<CORE_NS::EntityReference> result;
821
822    if (animationManager_) {
823        auto animationHandle = animationManager_->Read(GetEntity());
824        if (animationHandle) {
825            for (auto& track : animationHandle->tracks) {
826                const auto trackHandle = animationTrackManager_->Read(track);
827                if (trackHandle) {
828                    if (EntityUtil::IsValid(trackHandle->timestamps)) {
829                        result.push_back(ecs_->GetEntityManager().GetReferenceCounted(trackHandle->timestamps));
830                    }
831
832                    if (EntityUtil::IsValid(trackHandle->data)) {
833                        result.push_back(ecs_->GetEntityManager().GetReferenceCounted(trackHandle->data));
834                    }
835
836                    result.push_back(ecs_->GetEntityManager().GetReferenceCounted(track));
837                }
838            }
839        }
840
841        // Add self.
842        result.push_back(ecs_->GetEntityManager().GetReferenceCounted(GetEntity()));
843    }
844
845    return result;
846}
847
848IEcsTrackAnimation::Ptr EcsAnimation::CreateAnimationTrack(
849    CORE_NS::Entity rootEntity, CORE_NS::Entity targetEntity, BASE_NS::string_view fullPropertyPath)
850{
851    // Extract property path.
852    auto separatorPosition = fullPropertyPath.find_first_of('.');
853    if (!ecs_ || separatorPosition == BASE_NS::string::npos) {
854        return {};
855    }
856
857    IComponentManager* componentManager { nullptr };
858    auto componentManagerName = fullPropertyPath.substr(0, separatorPosition);
859    auto propertyPath = fullPropertyPath.substr(separatorPosition + 1);
860
861    for (const auto& manager : ecs_->GetComponentManagers()) {
862        if (manager->GetName() == componentManagerName) {
863            componentManager = manager;
864            break;
865        }
866    }
867
868    if (!componentManager) {
869        return {};
870    }
871
872    updateGuard_ = true;
873
874    EntityReference animationTrack;
875
876    if (IPropertyHandle* targetHandle = componentManager->GetData(targetEntity); targetHandle) {
877        if (auto po = RLock(*targetHandle, propertyPath); po) {
878            BASE_NS::string targetName = "Unnamed";
879            if (nameManager_->HasComponent(targetEntity)) {
880                targetName = nameManager_->Get(targetEntity).name;
881            }
882
883            const auto timeStamps = ecs_->GetEntityManager().CreateReferenceCounted();
884            {
885                NameComponent nameComponent;
886                nameComponent.name = "TimeStamps - " + targetName + ":" + propertyPath;
887                nameManager_->Set(timeStamps, nameComponent);
888
889                animationInputManager_->Create(timeStamps);
890            }
891
892            const auto keys = ecs_->GetEntityManager().CreateReferenceCounted();
893            {
894                NameComponent nameComponent;
895                nameComponent.name = "Keys - " + targetName + ":" + propertyPath;
896                nameManager_->Set(keys, nameComponent);
897
898                animationOutputManager_->Create(keys);
899                auto keysHandle = animationOutputManager_->Write(keys);
900                keysHandle->type = po.property->type;
901            }
902
903            const auto targetRef = ecs_->GetEntityManager().GetReferenceCounted(targetEntity);
904            animationTrack = ecs_->GetEntityManager().CreateReferenceCounted();
905            {
906                NameComponent nameComponent;
907                nameComponent.name = ResolvePathToAnimationRoot(*ecs_, rootEntity, targetEntity);
908                nameManager_->Set(animationTrack, nameComponent);
909
910                animationTrackManager_->Create(animationTrack);
911                auto trackHandle = animationTrackManager_->Write(animationTrack);
912                trackHandle->component = componentManager->GetUid();
913                trackHandle->property = propertyPath;
914                trackHandle->interpolationMode = AnimationTrackComponent::Interpolation::LINEAR;
915                trackHandle->timestamps = timeStamps;
916                trackHandle->data = keys;
917                trackHandle->target = targetRef;
918            }
919
920            const auto animationHandle = animationManager_->Write(GetEntity());
921            animationHandle->tracks.emplace_back(animationTrack);
922        }
923    }
924
925    updateGuard_ = false;
926
927    IEcsTrackAnimation::Ptr track =
928        META_NS::GetObjectRegistry().Create<IEcsTrackAnimation>(SCENE_NS::ClassId::EcsTrackAnimation);
929    OnAnimationTrackChanged(*track, animationTrack);
930
931    GetSelf<META_NS::IContainer>()->Add(interface_pointer_cast<IObject>(track));
932
933    return track;
934}
935
936IEcsTrackAnimation::Ptr EcsAnimation::GetAnimationTrack(CORE_NS::Entity target, BASE_NS::string_view fullPropertyPath)
937{
938    // Extract property path.
939    auto separatorPosition = fullPropertyPath.find_first_of('.');
940    if (!ecs_ || separatorPosition == BASE_NS::string::npos) {
941        return {};
942    }
943
944    auto propertyPath = fullPropertyPath.substr(separatorPosition + 1);
945
946    auto tracks = META_NS::GetAll<IEcsTrackAnimation>(GetSelf<META_NS::IContainer>());
947    for (auto& track : tracks) {
948        const auto trackHandle = animationTrackManager_->Read(track->GetEntity());
949        if (trackHandle) {
950            if (trackHandle->target == target && trackHandle->property == propertyPath) {
951                return track;
952            }
953        }
954    }
955
956    return {};
957}
958
959void EcsAnimation::DestroyAnimationTrack(IEcsTrackAnimation::Ptr track)
960{
961    updateGuard_ = true;
962    OnDestroyAnimationTrack(track);
963    updateGuard_ = false;
964}
965
966void EcsAnimation::DestroyAllAnimationTracks()
967{
968    updateGuard_ = true;
969    auto container = GetSelf<META_NS::IContainer>();
970    while (container->GetSize() > 0) {
971        auto topmost = container->GetAt(0);
972        if (auto track = interface_pointer_cast<IEcsTrackAnimation>(topmost)) {
973            OnDestroyAnimationTrack(track);
974        } else {
975            container->Remove(topmost);
976        }
977    }
978    updateGuard_ = false;
979}
980
981void EcsAnimation::Destroy()
982{
983    SCENE_PLUGIN_VERBOSE_LOG("Tearing down: %s", META_NS::GetValue(Name()).c_str());
984
985    if (ecs_ && entity_) {
986        if (auto ecsListener = ecsListener_.lock()) {
987            auto po = GetSelf<SCENE_NS::IEcsProxyObject>();
988            ecsListener->RemoveEntity(entity_, po, *nameManager_);
989            ecsListener->RemoveEntity(entity_, po, *animationStateManager_);
990            ecsListener->RemoveEntity(entity_, po, *animationManager_);
991            ecsListener->RemoveEntity(entity_, po, *animationTrackManager_);
992            ecsListener->RemoveEntity(entity_, po, *animationInputManager_);
993        }
994    }
995
996    root_ = {};
997    entity_ = {};
998}
999
1000void EcsAnimation::OnDurationPropertyChanged()
1001{
1002    SetValue(META_ACCESS_PROPERTY(TotalDuration), GetValue(Duration()) * repeatCount_);
1003    if (!ecs_ || updateGuard_) {
1004        return;
1005    }
1006
1007    auto handle = animationManager_->Write(entity_);
1008    if (handle) {
1009        handle->duration = GetValue(Duration()).ToSecondsFloat();
1010    }
1011}
1012
1013void EcsAnimation::OnNamePropertyChanged()
1014{
1015    if (!ecs_ || updateGuard_) {
1016        return;
1017    }
1018
1019    auto handle = nameManager_->Write(entity_);
1020    if (handle) {
1021        handle->name = GetValue(Name());
1022    }
1023}
1024
1025void EcsAnimation::OnProgressPropertyChanged()
1026{
1027    if (updateGuard_) {
1028        return;
1029    }
1030
1031    auto progress = GetValue(Progress());
1032    auto duration = GetValue(TotalDuration()).ToMilliseconds();
1033    SetTime(uint32_t(progress * duration));
1034}
1035
1036CORE_NS::Entity EcsAnimation::GetEntity() const
1037{
1038    return entity_;
1039}
1040
1041void EcsAnimation::SetProgress(float progress)
1042{
1043    META_ACCESS_PROPERTY(Progress)->SetValue(Base::Math::clamp(progress, 0.0f, 1.0f));
1044}
1045
1046void EcsAnimation::SetTime(uint32_t value)
1047{
1048    if (ecs_) {
1049        auto stateHandle = animationStateManager_->GetData(entity_);
1050        const auto metaData = stateHandle->Owner()->MetaData();
1051        for (const auto& data : metaData) {
1052            if (data.name == "time") {
1053                auto* time =
1054                    static_cast<float*>(static_cast<void*>(static_cast<uint8_t*>(stateHandle->WLock()) + data.offset));
1055                *time = value / 1000.0f;
1056                stateHandle->WUnlock();
1057                break;
1058            }
1059        }
1060    }
1061}
1062
1063void EcsAnimation::Step(const IClock::ConstPtr& clock)
1064{
1065    auto duration = GetValue(TotalDuration()).ToSecondsFloat();
1066    if (duration <= 0.0f) {
1067        return;
1068    }
1069
1070    if (!lastFrameTime_) {
1071        lastFrameTime_ = clock->GetTime();
1072    }
1073
1074    auto step = (clock->GetTime() - *lastFrameTime_).ToSecondsFloat();
1075    SetProgress(GetValue(Progress()) + (step / duration));
1076
1077    lastFrameTime_ = clock->GetTime();
1078}
1079
1080void EcsAnimation::Start()
1081{
1082    auto loopAnimation_ = true;
1083
1084    if (animationManager_) {
1085        if (auto animation = animationManager_->Write(entity_); animation) {
1086            animation->state = AnimationComponent::PlaybackState::PAUSE;
1087            if (loopAnimation_) {
1088                animation->repeatCount = AnimationComponent::REPEAT_COUNT_INFINITE;
1089            } else {
1090                animation->repeatCount = 0;
1091            }
1092        }
1093    }
1094
1095    lastFrameTime_.reset();
1096    META_ACCESS_PROPERTY(Running)->SetValue(true);
1097    META_NS::Invoke<META_NS::IOnChanged>(OnStarted());
1098}
1099
1100void EcsAnimation::Stop()
1101{
1102    if (animationManager_) {
1103        if (auto animation = animationManager_->Write(entity_); animation) {
1104            animation->state = AnimationComponent::PlaybackState::PAUSE;
1105        }
1106    }
1107    lastFrameTime_.reset();
1108    META_ACCESS_PROPERTY(Running)->SetValue(false);
1109}
1110
1111void EcsAnimation::Pause()
1112{
1113    if (animationManager_) {
1114        if (auto animation = animationManager_->Write(entity_); animation) {
1115            animation->state = AnimationComponent::PlaybackState::PAUSE;
1116        }
1117    }
1118    META_ACCESS_PROPERTY(Running)->SetValue(false);
1119}
1120
1121void EcsAnimation::Restart()
1122{
1123    Stop();
1124    Start();
1125}
1126
1127void EcsAnimation::Finish()
1128{
1129    Seek(1.0f);
1130}
1131
1132void EcsAnimation::Seek(float position)
1133{
1134    // TODO: Implement.
1135    SetProgress(position);
1136}
1137
1138EcsAnimation::Data EcsAnimation::GetProperty(Uid componentUid, Entity entity, string property) const
1139{
1140    Data data;
1141    if (ecs_) {
1142        auto cm = ecs_->GetComponentManager(componentUid);
1143        if (IPropertyHandle* targetHandle = cm->GetData(entity); targetHandle) {
1144            if (auto po = RLock(*targetHandle, property); po) {
1145                data.property = &(po.property->type);
1146                const uint8_t* src = reinterpret_cast<uint8_t*>(po.offset);
1147                data.data.resize(po.property->size);
1148                CloneData(data.data.data(), data.data.size(), src, po.property->size);
1149            }
1150        }
1151    }
1152    return data;
1153}
1154
1155void EcsAnimation::SetKeyFrameData(Entity animationTrack, float timeStamp, vector<uint8_t> valueData)
1156{
1157    if (!ecs_) {
1158        return;
1159    }
1160
1161    BASE_ASSERT(EntityUtil::IsValid(animationTrack));
1162    auto trackHandle = animationTrackManager_->Read(animationTrack);
1163    if (trackHandle) {
1164        const auto timeStamps = trackHandle->timestamps;
1165        const auto keys = trackHandle->data;
1166        BASE_ASSERT(EntityUtil::IsValid(timeStamps));
1167        BASE_ASSERT(EntityUtil::IsValid(keys));
1168
1169        size_t index = 0;
1170        bool replace = false;
1171        // insert/replace the timestamp
1172        // find out the position where keyframe belongs based on timestamp
1173        auto timeLineHandle = animationInputManager_->Write(timeStamps);
1174        if (timeLineHandle) {
1175            auto& data = timeLineHandle->timestamps;
1176            const size_t oldCount = data.size();
1177            const size_t newCount = oldCount + 1;
1178
1179            for (vector<float>::iterator it = data.begin(); it < data.end(); it++) {
1180                if (*it == timeStamp) {
1181                    replace = true;
1182                    break;
1183                }
1184                if (*it > timeStamp) {
1185                    break;
1186                }
1187                index++;
1188            }
1189
1190            if (!replace) {
1191                // reserve space for one more
1192                data.reserve(newCount);
1193                vector<float>::iterator insertIterator = data.begin() + index;
1194                data.insert(insertIterator, timeStamp);
1195            }
1196        }
1197
1198        auto keysHandle = animationOutputManager_->Write(keys);
1199        if (keysHandle) {
1200            auto& data = keysHandle->data;
1201            const size_t oldSize = data.size();
1202            const size_t oldCount = oldSize / valueData.size();
1203
1204            if (!replace) {
1205                const size_t newCount = oldCount + 1;
1206                const size_t newSize = newCount * valueData.size();
1207                // reserve space for one more
1208                data.reserve(newSize);
1209                vector<unsigned char>::iterator insertIterator = data.begin() + (index * valueData.size());
1210                data.insert(insertIterator, valueData.begin(), valueData.end());
1211            } else {
1212                auto dst = data.data() + index * valueData.size();
1213                CloneData(dst, valueData.size(), valueData.data(), valueData.size());
1214            }
1215        }
1216    }
1217}
1218
1219void EcsAnimation::UpdateAnimationTrackDuration(Entity animationTrack)
1220{
1221    if (ecs_) {
1222        auto duration = GetTrackDuration(animationTrack);
1223        auto animationTrackHandle = animationTrackManager_->Read(animationTrack);
1224        if (animationTrackHandle) {
1225            auto target = animationTrackHandle->target;
1226            if (EntityUtil::IsValid(target)) {
1227                if (IPropertyHandle* targetHandle = animationManager_->GetData(target); targetHandle) {
1228                    PropertyData pData;
1229                    if (auto po = pData.WLock(*targetHandle, "duration"); po) {
1230                        float* dst = reinterpret_cast<float*>(po.offset);
1231                        *dst = duration;
1232                    }
1233                }
1234            }
1235        }
1236    }
1237}
1238
1239float EcsAnimation::GetTrackDuration(Entity animationTrack)
1240{
1241    BASE_ASSERT(EntityUtil::IsValid(animationTrack));
1242
1243    float duration = 0.0f;
1244
1245    if (ecs_) {
1246        auto trackHandle = animationTrackManager_->Read(animationTrack);
1247        if (trackHandle) {
1248            const auto timeStamps = trackHandle->timestamps;
1249            BASE_ASSERT(EntityUtil::IsValid(timeStamps));
1250
1251            auto timeLineHandle = animationInputManager_->Read(timeStamps);
1252            if (timeLineHandle) {
1253                auto& stamps = timeLineHandle->timestamps;
1254                for (auto& t : stamps) {
1255                    if (t > duration) {
1256                        duration = t;
1257                    }
1258                }
1259            }
1260        }
1261    }
1262
1263    return duration;
1264}
1265/*
1266bool EcsAnimation::Export(Serialization::IExportContext& context, Serialization::ClassPrimitive& value) const
1267{
1268    return ObjectContainerFwd::Export(context, value);
1269}
1270
1271bool EcsAnimation::Import(Serialization::IImportContext& context, const Serialization::ClassPrimitive& value)
1272{
1273    return ObjectContainerFwd::Import(context, value);
1274}
1275*/
1276void RegisterEcsAnimationObjectType()
1277{
1278    META_NS::GetObjectRegistry().RegisterObjectType<EcsTrackAnimation>();
1279    META_NS::GetObjectRegistry().RegisterObjectType<EcsAnimation>();
1280}
1281
1282void UnregisterEcsAnimationObjectType()
1283{
1284    META_NS::GetObjectRegistry().UnregisterObjectType<EcsTrackAnimation>();
1285    META_NS::GetObjectRegistry().UnregisterObjectType<EcsAnimation>();
1286}
1287
1288SCENE_END_NAMESPACE()
1289