(Animation) Calculate AutoReverse looping mode progress at Animation time, not Animator. 78/321178/2
authorEunki, Hong <eunkiki.hong@samsung.com>
Mon, 17 Mar 2025 02:26:19 +0000 (11:26 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 17 Mar 2025 03:29:52 +0000 (12:29 +0900)
Until now, we control the looping mode's progress at Animator side.
It make some mis-implements when we add newly animators.

AnimateTo(A)
SetLoopingMode(AUTO_REVERSE) ///< A become auto-reversed..
AnimateTo(B)                 ///< B is not auto-reversed!!

Since LoopingMode is Animation's property, we should apply newly added animators.

But actually, Animation know all informations, so I think Animation could control
the calculation logics.

Change-Id: I5820985f59066a279a59bd9c19adfede068fde59
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-Animation.cpp
dali/internal/update/animation/scene-graph-animation.cpp
dali/internal/update/animation/scene-graph-animator.h

index 03d399a9a4a3452779dbd82b8c22f59b4f281b4f..0839a11c09199a565ad6d68e73cb0a13705ea8b8 100644 (file)
@@ -13696,6 +13696,126 @@ int UtcDaliAnimationSetLoopingModeP3(void)
   END_TEST;
 }
 
+int UtcDaliAnimationSetLoopingModeP4(void)
+{
+  // Test Loop Count is 1 (== default) and Loop mode being set 'before' Animators connected
+  TestApplication    application;
+  Integration::Scene stage(application.GetScene());
+
+  // LoopingMode::AUTO_REVERSE
+  {
+    Actor actor = Actor::New();
+    stage.Add(actor);
+
+    float     durationSeconds(1.0f);
+    Animation animation = Animation::New(durationSeconds);
+    DALI_TEST_CHECK(1 == animation.GetLoopCount());
+
+    bool                 signalReceived(false);
+    AnimationFinishCheck finishCheck(signalReceived);
+    animation.FinishedSignal().Connect(&application, finishCheck);
+    application.SendNotification();
+
+    Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+
+    DALI_TEST_CHECK(animation.GetLoopingMode() == Animation::RESTART);
+    animation.SetLoopingMode(Animation::AUTO_REVERSE);
+    DALI_TEST_CHECK(animation.GetLoopingMode() == Animation::AUTO_REVERSE);
+    animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition);
+
+    // Start the animation
+    animation.Play();
+    application.Render(0);
+    application.SendNotification();
+
+    application.Render(static_cast<unsigned int>(durationSeconds * 0.5f * 1000.0f) /* 50% time progress */);
+    application.SendNotification();
+    finishCheck.CheckSignalNotReceived();
+
+    // AUTO_REVERSE mode means, for Animation duration time, the actor starts from the beginning, passes the targetPosition,
+    // and arrives at the beginning.
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), targetPosition, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render(static_cast<unsigned int>(durationSeconds * 0.5f * 1000.0f) /* 100% time progress */);
+
+    application.SendNotification();
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render(static_cast<unsigned int>(durationSeconds * 1.0f * 1000.0f) + 1u /*just beyond the animation duration*/);
+
+    application.SendNotification();
+    application.Render(0);
+    application.SendNotification();
+    finishCheck.CheckSignalReceived();
+
+    // After all animation finished, arrives at the beginning.
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION);
+
+    finishCheck.Reset();
+  }
+
+  // LoopingMode::AUTO_REVERSE in Reverse mode, which begin from the end
+  {
+    Actor actor = Actor::New();
+    stage.Add(actor);
+
+    float     durationSeconds(1.0f);
+    Animation animation = Animation::New(durationSeconds);
+    DALI_TEST_CHECK(1 == animation.GetLoopCount());
+
+    bool                 signalReceived(false);
+    AnimationFinishCheck finishCheck(signalReceived);
+    animation.FinishedSignal().Connect(&application, finishCheck);
+    application.SendNotification();
+
+    // Specify a negative multiplier to play the animation in reverse
+    animation.SetSpeedFactor(-1.0f);
+
+    animation.SetLoopingMode(Animation::AUTO_REVERSE);
+    DALI_TEST_CHECK(animation.GetLoopingMode() == Animation::AUTO_REVERSE);
+
+    Vector3 targetPosition(100.0f, 100.0f, 100.0f);
+    animation.AnimateTo(Property(actor, Actor::Property::POSITION), targetPosition);
+
+    // Start the animation
+    animation.Play();
+    application.Render(0);
+    application.SendNotification();
+
+    application.Render(static_cast<unsigned int>(durationSeconds * 0.5f * 1000.0f) /* 50% time progress */);
+    application.SendNotification();
+    finishCheck.CheckSignalNotReceived();
+
+    // Setting a negative speed factor is to play the animation in reverse.
+    // So, when LoopingMode::AUTO_REVERSE and SetSpeedFactor( -1.0f ) is, for Animation duration time,
+    // the actor starts from the targetPosition, passes the beginning, and arrives at the targetPosition.
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), Vector3(0.0f, 0.0f, 0.0f), TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render(static_cast<unsigned int>(durationSeconds * 0.5f * 1000.0f) /* 100% time progress */);
+
+    application.SendNotification();
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), targetPosition, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render(static_cast<unsigned int>(durationSeconds * 1.0f * 1000.0f) + 1u /*just beyond the animation duration*/);
+
+    application.SendNotification();
+    application.Render(0);
+    application.SendNotification();
+    finishCheck.CheckSignalReceived();
+
+    // After all animation finished, arrives at the target.
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), targetPosition, TEST_LOCATION);
+
+    finishCheck.Reset();
+  }
+
+  END_TEST;
+}
+
 int UtcDaliAnimationGetLoopingModeP(void)
 {
   TestApplication application;
index afbc0d380ac7b6b9f63e07a1313de054ee818349..864812c2cefd4327beb751a14b4923a2fbf6613d 100644 (file)
@@ -351,14 +351,6 @@ void Animation::OnDestroy(BufferIndex bufferIndex)
 void Animation::SetLoopingMode(bool loopingMode)
 {
   mAutoReverseEnabled = loopingMode;
-
-  for(auto&& item : mAnimators)
-  {
-    // Send some variables together to figure out the Animation status
-    item->SetSpeedFactor(mSpeedFactor);
-    item->SetLoopCount(mLoopCount);
-    item->SetLoopingMode(loopingMode);
-  }
 }
 
 void Animation::AddAnimator(OwnerPointer<AnimatorBase>& animator)
@@ -539,6 +531,18 @@ void Animation::UpdateAnimators(BufferIndex bufferIndex, bool bake, bool animati
         {
           progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f, 1.0f);
         }
+        if(mAutoReverseEnabled)
+        {
+          if(mSpeedFactor > 0.0f)
+          {
+            progress = 1.0f - 2.0f * std::abs(progress - 0.5f);
+          }
+          // Reverse mode
+          else if(mSpeedFactor < 0.0f)
+          {
+            progress = 2.0f * std::abs(progress - 0.5f);
+          }
+        }
         animator->Update(bufferIndex, progress, mIsFirstLoop ? mBlendPoint : 0.0f, bake);
 
         if(animatorDuration > 0.0f && (elapsedSecondsClamped - intervalDelay) <= animatorDuration)
index c6d14473d2d5e8e4ac9e6fb7f5852604fd179c56..f5f8ad5de1187dec9b423561281ca73cf408c795 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_SCENE_GRAPH_ANIMATOR_H
 
 /*
- * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -81,14 +81,12 @@ public:
     mPropertyOwner(propertyOwner),
     mDurationSeconds(timePeriod.durationSeconds),
     mIntervalDelaySeconds(timePeriod.delaySeconds),
-    mSpeedFactor(1.0f),
     mCurrentProgress(0.f),
     mAlphaFunction(alphaFunction),
     mDisconnectAction(Dali::Animation::BAKE_FINAL),
     mAnimationPlaying(false),
     mEnabled(true),
     mConnectedToSceneGraph(false),
-    mAutoReverseEnabled(false),
     mDelayed(false)
   {
   }
@@ -186,40 +184,6 @@ public:
     return mDurationSeconds;
   }
 
-  void SetSpeedFactor(float factor)
-  {
-    mSpeedFactor = factor;
-  }
-
-  void SetLoopCount(int32_t loopCount)
-  {
-    mLoopCount = loopCount;
-  }
-
-  float SetProgress(float progress)
-  {
-    float value = 0.0f;
-
-    if(mAutoReverseEnabled)
-    {
-      if(mSpeedFactor > 0.0f)
-      {
-        value = 1.0f - 2.0f * std::abs(progress - 0.5f);
-      }
-      // Reverse mode
-      else if(mSpeedFactor < 0.0f)
-      {
-        value = 2.0f * std::abs(progress - 0.5f);
-      }
-    }
-    else
-    {
-      value = progress;
-    }
-
-    return value;
-  }
-
   /**
    * Set the delay before the animator should take effect.
    * The default is zero i.e. no delay.
@@ -442,15 +406,6 @@ public:
     return mEnabled;
   }
 
-  /**
-   * @brief Sets the looping mode.
-   * @param[in] loopingMode True when the looping mode is AUTO_REVERSE
-   */
-  void SetLoopingMode(bool loopingMode)
-  {
-    mAutoReverseEnabled = loopingMode;
-  }
-
   /**
    * Returns wheter the target object of the animator is still valid
    * or has been destroyed.
@@ -471,12 +426,6 @@ public:
    */
   void Update(BufferIndex bufferIndex, float progress, float blendPoint, bool bake)
   {
-    if(mLoopCount >= 0)
-    {
-      // Update the progress value
-      progress = SetProgress(progress);
-    }
-
     if(mPropertyOwner)
     {
       mPropertyOwner->SetUpdated(true);
@@ -519,18 +468,15 @@ protected:
 
   float mDurationSeconds;
   float mIntervalDelaySeconds;
-  float mSpeedFactor;
   float mCurrentProgress;
 
   AlphaFunction mAlphaFunction;
 
-  int32_t                    mLoopCount{1};
   Dali::Animation::EndAction mDisconnectAction;          ///< EndAction to apply when target object gets disconnected from the stage.
   bool                       mAnimationPlaying : 1;      ///< whether disconnect has been applied while it's running.
   bool                       mEnabled : 1;               ///< Animator is "enabled" while its target object is valid and on the stage.
   bool                       mConnectedToSceneGraph : 1; ///< True if ConnectToSceneGraph() has been called in update-thread.
-  bool                       mAutoReverseEnabled : 1;
-  bool                       mDelayed : 1; ///< True if the animator is in delayed state
+  bool                       mDelayed : 1;               ///< True if the animator is in delayed state
 };
 
 /**