18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci#include <scene_plugin/api/scene_uid.h>
168bf80f4bSopenharmony_ci
178bf80f4bSopenharmony_ci#include <3d/ecs/components/mesh_component.h>
188bf80f4bSopenharmony_ci
198bf80f4bSopenharmony_ci#include <meta/ext/concrete_base_object.h>
208bf80f4bSopenharmony_ci#include <meta/interface/animation/modifiers/intf_loop.h>
218bf80f4bSopenharmony_ci#include <meta/interface/animation/modifiers/intf_speed.h>
228bf80f4bSopenharmony_ci
238bf80f4bSopenharmony_ci#include "bind_templates.inl"
248bf80f4bSopenharmony_ci#include "intf_node_private.h"
258bf80f4bSopenharmony_ci#include "intf_submesh_bridge.h"
268bf80f4bSopenharmony_ci#include "node_impl.h"
278bf80f4bSopenharmony_ci#include "submesh_handler_uid.h"
288bf80f4bSopenharmony_ci#include "task_utils.h"
298bf80f4bSopenharmony_ci
308bf80f4bSopenharmony_ciusing SCENE_NS::MakeTask;
318bf80f4bSopenharmony_cinamespace {
328bf80f4bSopenharmony_ciclass AnimImpl
338bf80f4bSopenharmony_ci    : public META_NS::ConcreteBaseMetaObjectFwd<AnimImpl, NodeImpl, SCENE_NS::ClassId::Animation,
348bf80f4bSopenharmony_ci          META_NS::IParallelAnimation, META_NS::IAttachment, META_NS::ITimedAnimation, META_NS::IStartableAnimation> {
358bf80f4bSopenharmony_ci    using Super = META_NS::ConcreteBaseMetaObjectFwd<AnimImpl, NodeImpl, SCENE_NS::ClassId::Animation,
368bf80f4bSopenharmony_ci        META_NS::IParallelAnimation, META_NS::IAttachment, META_NS::ITimedAnimation, META_NS::IStartableAnimation>;
378bf80f4bSopenharmony_ci
388bf80f4bSopenharmony_ci    // From IEcsAnimation
398bf80f4bSopenharmony_ci    // META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(IEcsAnimation, bool, ReadOnly, false,
408bf80f4bSopenharmony_ci    // META_NS::DefaultPropFlags::R_FLAGS)
418bf80f4bSopenharmony_ci
428bf80f4bSopenharmony_ci    // From INamed
438bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(META_NS::INamed, BASE_NS::string, Name, {})
448bf80f4bSopenharmony_ci
458bf80f4bSopenharmony_ci    // From IAnimation
468bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(IAnimation, bool, Enabled, true)
478bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(IAnimation, bool, Valid, false, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
488bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(IAnimation, META_NS::TimeSpan, TotalDuration,
498bf80f4bSopenharmony_ci        META_NS::TimeSpan::Milliseconds(500), META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
508bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(IAnimation, bool, Running, false, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
518bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(IAnimation, float, Progress, 0, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
528bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(IAnimation, META_NS::ICurve1D::Ptr, Curve)
538bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(
548bf80f4bSopenharmony_ci        IAnimation, META_NS::IAnimationController::WeakPtr, Controller, {}, META_NS::DEFAULT_PROPERTY_FLAGS_NO_SER)
558bf80f4bSopenharmony_ci
568bf80f4bSopenharmony_ci    // from IAttachment IAnimationWithModifiableDuration
578bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(META_NS::IAttachment, META_NS::IObject::WeakPtr, DataContext)
588bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_READONLY_PROPERTY(META_NS::IAttachment, META_NS::IAttach::WeakPtr, AttachedTo)
598bf80f4bSopenharmony_ci
608bf80f4bSopenharmony_ci    // from ITimedAnimation
618bf80f4bSopenharmony_ci    META_IMPLEMENT_INTERFACE_PROPERTY(
628bf80f4bSopenharmony_ci        META_NS::ITimedAnimation, META_NS::TimeSpan, Duration, META_NS::TimeSpan::Milliseconds(500))
638bf80f4bSopenharmony_ci
648bf80f4bSopenharmony_ci    META_IMPLEMENT_EVENT(META_NS::IOnChanged, OnFinished)
658bf80f4bSopenharmony_ci    META_IMPLEMENT_EVENT(META_NS::IOnChanged, OnStarted)
668bf80f4bSopenharmony_ci
678bf80f4bSopenharmony_ci    // IAttach
688bf80f4bSopenharmony_ci    bool Attach(
698bf80f4bSopenharmony_ci        const BASE_NS::shared_ptr<META_NS::IObject>& attachment, const META_NS::IObject::Ptr& dataContext) override;
708bf80f4bSopenharmony_ci    bool Detach(const META_NS::IObject::Ptr& attachment) override;
718bf80f4bSopenharmony_ci
728bf80f4bSopenharmony_ci    /**
738bf80f4bSopenharmony_ci     * @brief Called by the framework when an the attachment is being attached to an IObject. If this
748bf80f4bSopenharmony_ci     *        function succeeds, the object is attached to the target.
758bf80f4bSopenharmony_ci     * @param object The IObject instance the attachment is attached to.
768bf80f4bSopenharmony_ci     * @param dataContext The data context for this attachment.
778bf80f4bSopenharmony_ci     * @note The data context can be the same object as the object being attached to, or
788bf80f4bSopenharmony_ci     *       something else. It is up to the attachment to decide how to handle them.
798bf80f4bSopenharmony_ci     * @return The implementation should return true if the attachment can be attached to target object.
808bf80f4bSopenharmony_ci     *         If the attachment cannot be added, the implementation should return false.
818bf80f4bSopenharmony_ci     */
828bf80f4bSopenharmony_ci    bool Attaching(const IAttach::Ptr& target, const IObject::Ptr& dataContext) override
838bf80f4bSopenharmony_ci    {
848bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(AttachedTo)->SetValue(target);
858bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(DataContext)->SetValue(dataContext);
868bf80f4bSopenharmony_ci        return true;
878bf80f4bSopenharmony_ci    }
888bf80f4bSopenharmony_ci    /**
898bf80f4bSopenharmony_ci     * @brief Detach the attachment from an object.
908bf80f4bSopenharmony_ci     * @param object The object to attach to.
918bf80f4bSopenharmony_ci     * @return If the attachment can be detached from the target, the implementation should return true.
928bf80f4bSopenharmony_ci     *         If detaching is not possible, the implementation should return false. In such a case the
938bf80f4bSopenharmony_ci     *         target may choose to not remove the attachment. During for example object destruction,
948bf80f4bSopenharmony_ci     *         the target will ignore the return value.
958bf80f4bSopenharmony_ci     */
968bf80f4bSopenharmony_ci    bool Detaching(const IAttach::Ptr& target) override
978bf80f4bSopenharmony_ci    {
988bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(AttachedTo)->SetValue({});
998bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(DataContext)->SetValue({});
1008bf80f4bSopenharmony_ci
1018bf80f4bSopenharmony_ci        // TODO: Remove this line of code, once framework automatically removes animations
1028bf80f4bSopenharmony_ci        // from animation controller when objects are destroyed.
1038bf80f4bSopenharmony_ci
1048bf80f4bSopenharmony_ci        auto animation = GetSelf<IAnimation>();
1058bf80f4bSopenharmony_ci        if (animation) {
1068bf80f4bSopenharmony_ci            auto controller = META_NS::GetValue(animation->Controller()).lock();
1078bf80f4bSopenharmony_ci            if (controller) {
1088bf80f4bSopenharmony_ci                controller->RemoveAnimation(animation);
1098bf80f4bSopenharmony_ci            }
1108bf80f4bSopenharmony_ci        }
1118bf80f4bSopenharmony_ci
1128bf80f4bSopenharmony_ci        return true;
1138bf80f4bSopenharmony_ci    }
1148bf80f4bSopenharmony_ci
1158bf80f4bSopenharmony_ci    void AddAnimation(const IAnimation::Ptr&) override {}
1168bf80f4bSopenharmony_ci    void RemoveAnimation(const IAnimation::Ptr&) override {}
1178bf80f4bSopenharmony_ci
1188bf80f4bSopenharmony_ci    BASE_NS::vector<META_NS::IAnimation::Ptr> GetAnimations() const override
1198bf80f4bSopenharmony_ci    {
1208bf80f4bSopenharmony_ci        return {};
1218bf80f4bSopenharmony_ci    }
1228bf80f4bSopenharmony_ci
1238bf80f4bSopenharmony_ci    void Step(const META_NS::IClock::ConstPtr& clock) override
1248bf80f4bSopenharmony_ci    {
1258bf80f4bSopenharmony_ci        // Nothing to do. Animation is stepped by Engine.
1268bf80f4bSopenharmony_ci    }
1278bf80f4bSopenharmony_ci
1288bf80f4bSopenharmony_ci    void Start() override
1298bf80f4bSopenharmony_ci    {
1308bf80f4bSopenharmony_ci        // Set ecs animation to pause
1318bf80f4bSopenharmony_ci	if (META_ACCESS_PROPERTY_VALUE(Enabled)) {
1328bf80f4bSopenharmony_ci            META_NS::SetValue(animationState_, (uint8_t)CORE3D_NS::AnimationComponent::PlaybackState::PLAY);
1338bf80f4bSopenharmony_ci
1348bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(Running)->SetValue(true);
1358bf80f4bSopenharmony_ci            META_NS::Invoke<META_NS::IOnChanged>(OnStarted());
1368bf80f4bSopenharmony_ci        }
1378bf80f4bSopenharmony_ci    }
1388bf80f4bSopenharmony_ci
1398bf80f4bSopenharmony_ci    void InternalStop()
1408bf80f4bSopenharmony_ci    {
1418bf80f4bSopenharmony_ci        // Set ecs animation to pause
1428bf80f4bSopenharmony_ci        META_NS::SetValue(animationState_, (uint8_t)CORE3D_NS::AnimationComponent::PlaybackState::STOP);
1438bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Running)->SetValue(false);
1448bf80f4bSopenharmony_ci        SetProgress(0.0f);
1458bf80f4bSopenharmony_ci    }
1468bf80f4bSopenharmony_ci
1478bf80f4bSopenharmony_ci    void Stop() override
1488bf80f4bSopenharmony_ci    {
1498bf80f4bSopenharmony_ci	if (META_ACCESS_PROPERTY_VALUE(Enabled)) {
1508bf80f4bSopenharmony_ci            InternalStop();
1518bf80f4bSopenharmony_ci	}
1528bf80f4bSopenharmony_ci    }
1538bf80f4bSopenharmony_ci
1548bf80f4bSopenharmony_ci    void Pause() override
1558bf80f4bSopenharmony_ci    {
1568bf80f4bSopenharmony_ci        if (META_ACCESS_PROPERTY_VALUE(Enabled)) {
1578bf80f4bSopenharmony_ci            META_NS::SetValue(animationState_, (uint8_t)CORE3D_NS::AnimationComponent::PlaybackState::PAUSE);
1588bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(Running)->SetValue(false);
1598bf80f4bSopenharmony_ci        }
1608bf80f4bSopenharmony_ci    }
1618bf80f4bSopenharmony_ci
1628bf80f4bSopenharmony_ci    void Restart() override
1638bf80f4bSopenharmony_ci    {
1648bf80f4bSopenharmony_ci        Stop();
1658bf80f4bSopenharmony_ci        Start();
1668bf80f4bSopenharmony_ci    }
1678bf80f4bSopenharmony_ci
1688bf80f4bSopenharmony_ci    void Finish() override
1698bf80f4bSopenharmony_ci    {
1708bf80f4bSopenharmony_ci        if (META_ACCESS_PROPERTY_VALUE(Enabled)) {
1718bf80f4bSopenharmony_ci            META_NS::SetValue(animationState_, (uint8_t)CORE3D_NS::AnimationComponent::PlaybackState::STOP);
1728bf80f4bSopenharmony_ci            META_ACCESS_PROPERTY(Running)->SetValue(false);
1738bf80f4bSopenharmony_ci            Seek(1.0f);
1748bf80f4bSopenharmony_ci            META_NS::Invoke<META_NS::IOnChanged>(OnFinished());
1758bf80f4bSopenharmony_ci        }
1768bf80f4bSopenharmony_ci    }
1778bf80f4bSopenharmony_ci
1788bf80f4bSopenharmony_ci    void Seek(float position) override
1798bf80f4bSopenharmony_ci    {
1808bf80f4bSopenharmony_ci        if (META_ACCESS_PROPERTY_VALUE(Enabled)) {
1818bf80f4bSopenharmony_ci            position = BASE_NS::Math::clamp01(position);
1828bf80f4bSopenharmony_ci            SetProgress(position);
1838bf80f4bSopenharmony_ci        }
1848bf80f4bSopenharmony_ci    }
1858bf80f4bSopenharmony_ci
1868bf80f4bSopenharmony_ci    void SetProgress(float progress)
1878bf80f4bSopenharmony_ci    {
1888bf80f4bSopenharmony_ci        progress = Base::Math::clamp(progress, 0.0f, 1.0f);
1898bf80f4bSopenharmony_ci        animationStateTime_->SetValue(progress * GetValue(TotalDuration()).ToSecondsFloat());
1908bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Progress)->SetValue(progress);
1918bf80f4bSopenharmony_ci    }
1928bf80f4bSopenharmony_ci
1938bf80f4bSopenharmony_ci    // void AddKey(IEcsTrackAnimation::Ptr track, float time) override {}
1948bf80f4bSopenharmony_ci    // void RemoveKey(IEcsTrackAnimation::Ptr track, uint32_t index) override {}
1958bf80f4bSopenharmony_ci    // void UpdateKey(IEcsTrackAnimation::Ptr track, uint32_t oldKeyIndex, uint32_t newKeyIndex, float time)
1968bf80f4bSopenharmony_ci    // override {}
1978bf80f4bSopenharmony_ci
1988bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_COMPONENT_NAME = "AnimationComponent";
1998bf80f4bSopenharmony_ci    static constexpr size_t ANIMATION_COMPONENT_NAME_LEN = ANIMATION_COMPONENT_NAME.size() + 1;
2008bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_STATE = "AnimationComponent.state";
2018bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_REPEATS = "AnimationComponent.repeatCount";
2028bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_SOFFSET = "AnimationComponent.startOffset";
2038bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_DURATION = "AnimationComponent.duration";
2048bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_WEIGHT = "AnimationComponent.weight";
2058bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_SPEED = "AnimationComponent.speed";
2068bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_TRACKS = "AnimationComponent.tracks";
2078bf80f4bSopenharmony_ci
2088bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_STATE_COMPONENT_NAME = "AnimationStateComponent";
2098bf80f4bSopenharmony_ci    static constexpr size_t ANIMATION_STATE_COMPONENT_NAME_LEN = ANIMATION_STATE_COMPONENT_NAME.size() + 1;
2108bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_STATE_TIME = "AnimationStateComponent.time";
2118bf80f4bSopenharmony_ci    static constexpr BASE_NS::string_view ANIMATION_OPTIONS = "AnimationStateComponent.options";
2128bf80f4bSopenharmony_ci
2138bf80f4bSopenharmony_ci    bool Build(const IMetadata::Ptr& data) override
2148bf80f4bSopenharmony_ci    {
2158bf80f4bSopenharmony_ci        bool ret = false;
2168bf80f4bSopenharmony_ci        if (ret = NodeImpl::Build(data); ret) {
2178bf80f4bSopenharmony_ci            PropertyNameMask()[ANIMATION_COMPONENT_NAME] = { ANIMATION_STATE.substr(ANIMATION_COMPONENT_NAME_LEN),
2188bf80f4bSopenharmony_ci                ANIMATION_REPEATS.substr(ANIMATION_COMPONENT_NAME_LEN),
2198bf80f4bSopenharmony_ci                ANIMATION_SOFFSET.substr(ANIMATION_COMPONENT_NAME_LEN),
2208bf80f4bSopenharmony_ci                ANIMATION_DURATION.substr(ANIMATION_COMPONENT_NAME_LEN),
2218bf80f4bSopenharmony_ci                ANIMATION_SPEED.substr(ANIMATION_COMPONENT_NAME_LEN),
2228bf80f4bSopenharmony_ci                ANIMATION_TRACKS.substr(ANIMATION_COMPONENT_NAME_LEN) };
2238bf80f4bSopenharmony_ci            PropertyNameMask()[ANIMATION_STATE_COMPONENT_NAME] = { ANIMATION_STATE_TIME.substr(
2248bf80f4bSopenharmony_ci                                                                       ANIMATION_STATE_COMPONENT_NAME_LEN),
2258bf80f4bSopenharmony_ci                ANIMATION_OPTIONS.substr(ANIMATION_STATE_COMPONENT_NAME_LEN) };
2268bf80f4bSopenharmony_ci        }
2278bf80f4bSopenharmony_ci        return ret;
2288bf80f4bSopenharmony_ci    }
2298bf80f4bSopenharmony_ci
2308bf80f4bSopenharmony_ci    void SetValid(bool valid)
2318bf80f4bSopenharmony_ci    {
2328bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Valid)->SetValue(valid);
2338bf80f4bSopenharmony_ci        if (valid) {
2348bf80f4bSopenharmony_ci            if (auto ecsScene = EcsScene()) {
2358bf80f4bSopenharmony_ci                // Animation controller does not add animation unless it is valid (i.e. connected)
2368bf80f4bSopenharmony_ci                ecsScene->AddApplicationTask(MakeTask(
2378bf80f4bSopenharmony_ci                                                 [](const auto& attachment, const auto& controller) {
2388bf80f4bSopenharmony_ci                                                     if (attachment && controller) {
2398bf80f4bSopenharmony_ci                                                         controller->AddAnimation(attachment);
2408bf80f4bSopenharmony_ci                                                     }
2418bf80f4bSopenharmony_ci                                                     return false;
2428bf80f4bSopenharmony_ci                                                 },
2438bf80f4bSopenharmony_ci                                                 GetSelf<META_NS::IAnimation>(),
2448bf80f4bSopenharmony_ci                                                 interface_pointer_cast<META_NS::IAnimationController>(ecsScene)),
2458bf80f4bSopenharmony_ci                    false);
2468bf80f4bSopenharmony_ci            }
2478bf80f4bSopenharmony_ci        }
2488bf80f4bSopenharmony_ci    }
2498bf80f4bSopenharmony_ci
2508bf80f4bSopenharmony_ci    void UpdateTotalDuration() const
2518bf80f4bSopenharmony_ci    {
2528bf80f4bSopenharmony_ci        const auto speed = animationSpeed_ ? animationSpeed_->GetValue() : 1.f;
2538bf80f4bSopenharmony_ci        const auto origDuration = animationOrigDuration_ ? animationOrigDuration_->GetValue() : 0.f;
2548bf80f4bSopenharmony_ci
2558bf80f4bSopenharmony_ci	// By default we will play once.
2568bf80f4bSopenharmony_ci	float times = 1.0f;
2578bf80f4bSopenharmony_ci
2588bf80f4bSopenharmony_ci        // In case we have repeats requested, then play also them.
2598bf80f4bSopenharmony_ci        if (animationRepeatCount_) {
2608bf80f4bSopenharmony_ci            times += static_cast<float>(animationRepeatCount_->GetValue());
2618bf80f4bSopenharmony_ci        }
2628bf80f4bSopenharmony_ci        const auto newTotalDuration =
2638bf80f4bSopenharmony_ci            speed != 0.f ? META_NS::TimeSpan::Seconds(origDuration * times / speed) : META_NS::TimeSpan::Infinite();
2648bf80f4bSopenharmony_ci
2658bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(TotalDuration)->SetValue(newTotalDuration);
2668bf80f4bSopenharmony_ci    }
2678bf80f4bSopenharmony_ci
2688bf80f4bSopenharmony_ci    void UpdateProgress() const
2698bf80f4bSopenharmony_ci    {
2708bf80f4bSopenharmony_ci        const auto currentTime = animationStateTime_ ? animationStateTime_->GetValue() : 0.f;
2718bf80f4bSopenharmony_ci        const auto totalDuration = TotalDuration()->GetValue().ToSecondsFloat();
2728bf80f4bSopenharmony_ci        const auto newProgress = totalDuration != 0.f ? currentTime / totalDuration : 1.f;
2738bf80f4bSopenharmony_ci        META_ACCESS_PROPERTY(Progress)->SetValue(newProgress);
2748bf80f4bSopenharmony_ci    }
2758bf80f4bSopenharmony_ci
2768bf80f4bSopenharmony_ci    bool CompleteInitialization(const BASE_NS::string& path) override
2778bf80f4bSopenharmony_ci    {
2788bf80f4bSopenharmony_ci        // At the end we would not even like to call this, to be refactored
2798bf80f4bSopenharmony_ci        if (!NodeImpl::CompleteInitialization(path)) {
2808bf80f4bSopenharmony_ci            return false;
2818bf80f4bSopenharmony_ci        }
2828bf80f4bSopenharmony_ci
2838bf80f4bSopenharmony_ci        auto meta = interface_pointer_cast<META_NS::IMetadata>(ecsObject_);
2848bf80f4bSopenharmony_ci        BindChanges(propHandler_, Name(), meta, "Name");
2858bf80f4bSopenharmony_ci        // Track timestamps do not scale automatically when total duration changes, have engine duration separated
2868bf80f4bSopenharmony_ci        // from ours BindChanges<float, META_NS::TimeSpan>(
2878bf80f4bSopenharmony_ci
2888bf80f4bSopenharmony_ci        animationState_ = meta->GetPropertyByName<uint8_t>(ANIMATION_STATE);
2898bf80f4bSopenharmony_ci        animationOptions_ = meta->GetPropertyByName<uint8_t>(ANIMATION_OPTIONS);
2908bf80f4bSopenharmony_ci        animationStateTime_ = meta->GetPropertyByName<float>(ANIMATION_STATE_TIME);
2918bf80f4bSopenharmony_ci        animationSpeed_ = meta->GetPropertyByName<float>(ANIMATION_SPEED);
2928bf80f4bSopenharmony_ci        animationRepeatCount_ = meta->GetPropertyByName<uint32_t>(ANIMATION_REPEATS);
2938bf80f4bSopenharmony_ci        animationOrigDuration_ = meta->GetPropertyByName<float>(ANIMATION_DURATION);
2948bf80f4bSopenharmony_ci        if (animationSpeed_) {
2958bf80f4bSopenharmony_ci            propHandler_.NewHandler(nullptr, nullptr).Subscribe(animationSpeed_, [this]() { UpdateTotalDuration(); });
2968bf80f4bSopenharmony_ci        }
2978bf80f4bSopenharmony_ci        if (animationRepeatCount_) {
2988bf80f4bSopenharmony_ci            propHandler_.NewHandler(nullptr, nullptr).Subscribe(animationRepeatCount_, [this]() {
2998bf80f4bSopenharmony_ci                UpdateTotalDuration();
3008bf80f4bSopenharmony_ci            });
3018bf80f4bSopenharmony_ci        }
3028bf80f4bSopenharmony_ci        if (animationOrigDuration_) {
3038bf80f4bSopenharmony_ci            propHandler_.NewHandler(nullptr, nullptr).Subscribe(animationOrigDuration_, [this]() {
3048bf80f4bSopenharmony_ci                UpdateTotalDuration();
3058bf80f4bSopenharmony_ci            });
3068bf80f4bSopenharmony_ci        }
3078bf80f4bSopenharmony_ci        if (animationStateTime_) {
3088bf80f4bSopenharmony_ci            propHandler_.NewHandler(nullptr, nullptr).Subscribe(animationStateTime_, [this]() { UpdateProgress(); });
3098bf80f4bSopenharmony_ci        }
3108bf80f4bSopenharmony_ci        propHandler_.NewHandler(nullptr, nullptr).Subscribe(Enabled(), [this] {
3118bf80f4bSopenharmony_ci            if (!Enabled()->GetValue()) {
3128bf80f4bSopenharmony_ci                InternalStop();
3138bf80f4bSopenharmony_ci            }
3148bf80f4bSopenharmony_ci        });
3158bf80f4bSopenharmony_ci        TotalDuration()->OnChanged()->AddHandler(
3168bf80f4bSopenharmony_ci            META_NS::MakeCallback<META_NS::IOnChanged>([this] { UpdateProgress(); }));
3178bf80f4bSopenharmony_ci        if (animationOptions_) {
3188bf80f4bSopenharmony_ci            animationOptions_->SetValue(0);
3198bf80f4bSopenharmony_ci        }
3208bf80f4bSopenharmony_ci
3218bf80f4bSopenharmony_ci        if (animationStateTime_) {
3228bf80f4bSopenharmony_ci        }
3238bf80f4bSopenharmony_ci        if (animationRepeatCount_) {
3248bf80f4bSopenharmony_ci            animationRepeatCount_->SetValue(0);
3258bf80f4bSopenharmony_ci        }
3268bf80f4bSopenharmony_ci        UpdateTotalDuration();
3278bf80f4bSopenharmony_ci
3288bf80f4bSopenharmony_ci        // properties are suitable for direct value binding, adding the custom handlers for the rest:
3298bf80f4bSopenharmony_ci
3308bf80f4bSopenharmony_ci        // We interpret the animation valid if it is connected
3318bf80f4bSopenharmony_ci        SetValid(true);
3328bf80f4bSopenharmony_ci
3338bf80f4bSopenharmony_ci        propHandler_.NewHandler(nullptr, nullptr)
3348bf80f4bSopenharmony_ci            .Subscribe(META_ACCESS_PROPERTY(ConnectionStatus), META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
3358bf80f4bSopenharmony_ci                SetValid(META_ACCESS_PROPERTY(ConnectionStatus)->GetValue() == ECS_OBJ_STATUS_CONNECTED);
3368bf80f4bSopenharmony_ci            }));
3378bf80f4bSopenharmony_ci
3388bf80f4bSopenharmony_ci        propHandler_.NewHandler(nullptr, nullptr)
3398bf80f4bSopenharmony_ci            .Subscribe(META_ACCESS_PROPERTY(Progress), META_NS::MakeCallback<META_NS::IOnChanged>([this]() {
3408bf80f4bSopenharmony_ci                if (META_NS::GetValue(META_ACCESS_PROPERTY(Progress)) >= 1.0f) {
3418bf80f4bSopenharmony_ci                    META_ACCESS_PROPERTY(Running)->SetValue(false);
3428bf80f4bSopenharmony_ci                    META_NS::Invoke<META_NS::IOnChanged>(OnFinished());
3438bf80f4bSopenharmony_ci                }
3448bf80f4bSopenharmony_ci            }));
3458bf80f4bSopenharmony_ci        return true;
3468bf80f4bSopenharmony_ci    }
3478bf80f4bSopenharmony_ci
3488bf80f4bSopenharmony_ci    bool BuildChildren(SCENE_NS::INode::BuildBehavior) override
3498bf80f4bSopenharmony_ci    {
3508bf80f4bSopenharmony_ci        // in typical cases we should not have children
3518bf80f4bSopenharmony_ci        if (META_NS::GetValue(META_ACCESS_PROPERTY(Status)) == SCENE_NS::INode::NODE_STATUS_CONNECTED) {
3528bf80f4bSopenharmony_ci            SetStatus(SCENE_NS::INode::NODE_STATUS_FULLY_CONNECTED);
3538bf80f4bSopenharmony_ci            META_NS::Invoke<META_NS::IOnChanged>(OnBound());
3548bf80f4bSopenharmony_ci            bound_ = true;
3558bf80f4bSopenharmony_ci        }
3568bf80f4bSopenharmony_ci        return true;
3578bf80f4bSopenharmony_ci    }
3588bf80f4bSopenharmony_ci
3598bf80f4bSopenharmony_ci    void AttachModifier(const BASE_NS::shared_ptr<META_NS::IAnimationModifier>& attachment);
3608bf80f4bSopenharmony_ci    void DetachModifier(const BASE_NS::shared_ptr<META_NS::IAnimationModifier>& attachment);
3618bf80f4bSopenharmony_ci
3628bf80f4bSopenharmony_ci    mutable META_NS::EventImpl<META_NS::IOnChanged> onFinished_;
3638bf80f4bSopenharmony_ci    mutable META_NS::EventImpl<META_NS::IOnChanged> onStarted_;
3648bf80f4bSopenharmony_ci
3658bf80f4bSopenharmony_ci    META_NS::Property<float> animationStateTime_;
3668bf80f4bSopenharmony_ci    META_NS::Property<float> animationSpeed_;
3678bf80f4bSopenharmony_ci    META_NS::Property<float> animationOrigDuration_;
3688bf80f4bSopenharmony_ci    META_NS::Property<uint8_t> animationOptions_;
3698bf80f4bSopenharmony_ci    META_NS::Property<uint8_t> animationState_;
3708bf80f4bSopenharmony_ci    META_NS::Property<uint32_t> animationRepeatCount_;
3718bf80f4bSopenharmony_ci};
3728bf80f4bSopenharmony_cistruct LoopCountConverter {
3738bf80f4bSopenharmony_ci    static uint32_t ToEcs(SceneHolder& sceneHolder, const int32_t& v)
3748bf80f4bSopenharmony_ci    {
3758bf80f4bSopenharmony_ci        return v < 0 ? CORE3D_NS::AnimationComponent::REPEAT_COUNT_INFINITE : static_cast<uint32_t>(v);
3768bf80f4bSopenharmony_ci    }
3778bf80f4bSopenharmony_ci    static int32_t ToUi(SceneHolder& sceneHolder, const uint32_t& v)
3788bf80f4bSopenharmony_ci    {
3798bf80f4bSopenharmony_ci        return v == CORE3D_NS::AnimationComponent::REPEAT_COUNT_INFINITE ? -1 : static_cast<int32_t>(v);
3808bf80f4bSopenharmony_ci    }
3818bf80f4bSopenharmony_ci};
3828bf80f4bSopenharmony_ci
3838bf80f4bSopenharmony_cibool AnimImpl::Attach(const BASE_NS::shared_ptr<META_NS::IObject>& attachment, const IObject::Ptr& dataContext)
3848bf80f4bSopenharmony_ci{
3858bf80f4bSopenharmony_ci    if (auto am = interface_pointer_cast<META_NS::IAnimationModifier>(attachment)) {
3868bf80f4bSopenharmony_ci        AttachModifier(am);
3878bf80f4bSopenharmony_ci    }
3888bf80f4bSopenharmony_ci    return Super::Attach(attachment, dataContext);
3898bf80f4bSopenharmony_ci}
3908bf80f4bSopenharmony_ci
3918bf80f4bSopenharmony_cibool AnimImpl::Detach(const META_NS::IObject::Ptr& attachment)
3928bf80f4bSopenharmony_ci{
3938bf80f4bSopenharmony_ci    if (auto am = interface_pointer_cast<META_NS::IAnimationModifier>(attachment)) {
3948bf80f4bSopenharmony_ci        DetachModifier(am);
3958bf80f4bSopenharmony_ci    }
3968bf80f4bSopenharmony_ci    return Super::Detach(attachment);
3978bf80f4bSopenharmony_ci}
3988bf80f4bSopenharmony_ci
3998bf80f4bSopenharmony_civoid AnimImpl::AttachModifier(const BASE_NS::shared_ptr<META_NS::IAnimationModifier>& attachment)
4008bf80f4bSopenharmony_ci{
4018bf80f4bSopenharmony_ci    if (auto loop = interface_pointer_cast<META_NS::AnimationModifiers::ILoop>(attachment)) {
4028bf80f4bSopenharmony_ci        BindPropChanges<int32_t, uint32_t, LoopCountConverter>(propHandler_, loop->LoopCount(), animationRepeatCount_);
4038bf80f4bSopenharmony_ci    } else if (auto speed = interface_pointer_cast<META_NS::AnimationModifiers::ISpeed>(attachment)) {
4048bf80f4bSopenharmony_ci        BindPropChanges<float, float>(propHandler_, speed->SpeedFactor(), animationSpeed_);
4058bf80f4bSopenharmony_ci    } else {
4068bf80f4bSopenharmony_ci        CORE_LOG_E("Not implemented!");
4078bf80f4bSopenharmony_ci    }
4088bf80f4bSopenharmony_ci}
4098bf80f4bSopenharmony_civoid AnimImpl::DetachModifier(const BASE_NS::shared_ptr<META_NS::IAnimationModifier>& attachment)
4108bf80f4bSopenharmony_ci{
4118bf80f4bSopenharmony_ci    if (auto loop = interface_pointer_cast<META_NS::AnimationModifiers::ILoop>(attachment)) {
4128bf80f4bSopenharmony_ci        propHandler_.EraseHandler(animationRepeatCount_, loop->LoopCount());
4138bf80f4bSopenharmony_ci        animationRepeatCount_->SetValue(0);
4148bf80f4bSopenharmony_ci    } else if (auto speed = interface_pointer_cast<META_NS::AnimationModifiers::ISpeed>(attachment)) {
4158bf80f4bSopenharmony_ci        propHandler_.EraseHandler(animationSpeed_, speed->SpeedFactor());
4168bf80f4bSopenharmony_ci        animationSpeed_->SetValue(1.f);
4178bf80f4bSopenharmony_ci    } else {
4188bf80f4bSopenharmony_ci        CORE_LOG_E("Not implemented!");
4198bf80f4bSopenharmony_ci    }
4208bf80f4bSopenharmony_ci}
4218bf80f4bSopenharmony_ci} // namespace
4228bf80f4bSopenharmony_ciSCENE_BEGIN_NAMESPACE()
4238bf80f4bSopenharmony_ci
4248bf80f4bSopenharmony_civoid RegisterAnimImpl()
4258bf80f4bSopenharmony_ci{
4268bf80f4bSopenharmony_ci    META_NS::GetObjectRegistry().RegisterObjectType<AnimImpl>();
4278bf80f4bSopenharmony_ci}
4288bf80f4bSopenharmony_civoid UnregisterAnimImpl()
4298bf80f4bSopenharmony_ci{
4308bf80f4bSopenharmony_ci    META_NS::GetObjectRegistry().UnregisterObjectType<AnimImpl>();
4318bf80f4bSopenharmony_ci}
4328bf80f4bSopenharmony_ciSCENE_END_NAMESPACE();
433