[Tizen] LookAt API for actor 50/293850/2 accepted/tizen/7.0/unified/20230608.164733 accepted/tizen/7.0/unified/20230615.051447 accepted/tizen/7.0/unified/20230615.061139
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 28 Mar 2023 08:04:39 +0000 (17:04 +0900)
committerhuiyu <huiyu.eun@samsung.com>
Wed, 7 Jun 2023 05:59:05 +0000 (14:59 +0900)
Make LookAt API for common Actor.
Note, this isn't follow target position. Just calculate Quaternion
and apply instancely.

Change-Id: I0fb107f954034fb5aa410cc607122b4e61988e9b
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-Actor.cpp
dali/devel-api/actors/actor-devel.cpp
dali/devel-api/actors/actor-devel.h
dali/internal/event/actors/actor-coords.cpp
dali/internal/event/actors/actor-coords.h

index 06f0dd7..8294c99 100644 (file)
@@ -11687,3 +11687,68 @@ int UtcDaliActorCalculateWorldColor04(void)
 
   END_TEST;
 }
+
+int UtcDaliActorCalculateLookAt(void)
+{
+  TestApplication application;
+
+  tet_infoline("Test that actor rotate right value of orientation");
+
+  Actor actor = Actor::New();
+
+  actor[Actor::Property::POSITION]      = Vector3(100.0f, 0.0f, 0.0f);
+  actor[Actor::Property::ANCHOR_POINT]  = AnchorPoint::CENTER;
+  actor[Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER;
+
+  application.GetScene().Add(actor);
+
+  application.SendNotification();
+  application.Render(0);
+
+  Quaternion actorQuaternion;
+
+  tet_printf("Test with target only\n");
+  Dali::DevelActor::LookAt(actor, Vector3::ZERO);
+  actorQuaternion = actor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  DALI_TEST_EQUALS(actorQuaternion, Quaternion(Radian(Degree(90.0f)), Vector3::NEGATIVE_YAXIS), TEST_LOCATION);
+
+  tet_printf("Test with target + up\n");
+  Dali::DevelActor::LookAt(actor, Vector3::ZERO, Vector3::ZAXIS);
+  actorQuaternion = actor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  DALI_TEST_EQUALS(actorQuaternion, Quaternion(Radian(Degree(90.0f)), Vector3::XAXIS) * Quaternion(Radian(Degree(90.0f)), Vector3::NEGATIVE_YAXIS), TEST_LOCATION);
+
+  tet_printf("Test with target + up + localForward\n");
+  Dali::DevelActor::LookAt(actor, Vector3::ZERO, Vector3::NEGATIVE_YAXIS, Vector3::NEGATIVE_XAXIS);
+  actorQuaternion = actor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  DALI_TEST_EQUALS(actorQuaternion, Quaternion(Radian(Degree(180.0f)), Vector3::XAXIS), TEST_LOCATION);
+
+  tet_printf("Test with target + up + localForward + localUp\n");
+  Dali::DevelActor::LookAt(actor, Vector3::ZERO, Vector3::NEGATIVE_YAXIS, Vector3::NEGATIVE_YAXIS, Vector3::XAXIS);
+  actorQuaternion = actor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  DALI_TEST_EQUALS(actorQuaternion, Quaternion(Radian(Degree(90.0f)), Vector3::NEGATIVE_ZAXIS), TEST_LOCATION);
+
+  // Reset quaternion
+  actor[Actor::Property::ORIENTATION] = Quaternion();
+
+  Actor actor2                           = Actor::New();
+  actor2[Actor::Property::POSITION]      = Vector3(0.0f, 50.0f, -10.0f);
+  actor2[Actor::Property::ANCHOR_POINT]  = AnchorPoint::CENTER;
+  actor2[Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER;
+  actor.Add(actor2);
+
+  tet_printf("Test whether lookat calculate well by using event side values only\n");
+  Dali::DevelActor::LookAt(actor2, Vector3(100.0f, 50.0f, 1.0f));
+  actorQuaternion = actor2.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  DALI_TEST_EQUALS(actorQuaternion, Quaternion(), TEST_LOCATION);
+
+  actor[Actor::Property::ORIENTATION] = Quaternion(Radian(Degree(90.0f)), Vector3::ZAXIS);
+
+  DALI_TEST_EQUALS(Dali::DevelActor::GetWorldTransform(actor2).GetTranslation3(), Vector3(50.0f, 0.0f, -10.0f), TEST_LOCATION);
+
+  tet_printf("Test whether lookat calculate well inherit by parent orientation\n");
+  Dali::DevelActor::LookAt(actor2, Vector3(50.0f, 0.0f, 1.0f), Vector3::NEGATIVE_XAXIS);
+  actorQuaternion = actor2.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+  DALI_TEST_EQUALS(actorQuaternion, Quaternion(), TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index fc54e89..8289468 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -79,6 +79,12 @@ Vector4 GetWorldColor(Actor actor)
   return CalculateActorWorldColor(GetImplementation(actor));
 }
 
+void LookAt(Actor actor, Vector3 target, Vector3 up, Vector3 localForward, Vector3 localUp)
+{
+  auto orientation = CalculateActorLookAtOrientation(GetImplementation(actor), target, up, localForward, localUp);
+  GetImplementation(actor).SetOrientation(orientation);
+}
+
 } // namespace DevelActor
 
 } // namespace Dali
index 7b985a0..02130de 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_ACTOR_DEVEL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -413,6 +413,26 @@ DALI_CORE_API Matrix GetWorldTransform(Actor actor);
  */
 DALI_CORE_API Vector4 GetWorldColor(Actor actor);
 
+/**
+ * Rotate the actor look at specific position.
+ * It will change the actor's orientation property.
+ *
+ * This calculates the world transform from scratch using only event
+ * side properties - it does not rely on the update thread to have
+ * already calculated the transform.
+ *
+ * @note Target position should be setup by world coordinates.
+ * @note The result of invalid input is not determined.
+ *       (ex : forward vector or actor-to-target vector has same direction with up, One of them is ZERO)
+ *
+ * @param[in] actor The actor for which to calculate the look at orientation.
+ * @param[in] target The target world position to look at.
+ * @param[in] up The up vector after target look at. Default is +Y axis.
+ * @param[in] localForward The forward vector of actor when it's orientation is not applied. Default is +Z axis.
+ * @param[in] localUp The up vector of actor when it's orientation is not applied. Default is +Y axis.
+ */
+DALI_CORE_API void LookAt(Actor actor, Vector3 target, Vector3 up = Vector3::YAXIS, Vector3 localForward = Vector3::ZAXIS, Vector3 localUp = Vector3::YAXIS);
+
 } // namespace DevelActor
 
 } // namespace Dali
index 2eb4042..91a0d9f 100644 (file)
@@ -65,6 +65,36 @@ bool GetViewportExtentsFromRenderTask(const RenderTask& renderTask, Rect<float>&
   }
   return true;
 }
+
+/**
+ * @brief Get the Orientation from Forward vector and Up vector
+ * If vectors are valid, return Quaternion to make forward direction as +Z, and up direction near as -Y axis.
+ * If some invalid vector inputed (like Zero length, or parallel vector), return identity quaternion
+ *
+ * @param[in] forward The vector that want to be +Z axis.
+ * @param[in] up The vector that want to be -Y axis.
+ * @return Quaternion to make forward direction as +Z, and up direction near as -Y axis.
+ */
+Quaternion GetOrientationFromForwardAndUpVector(Vector3 forward, Vector3 up)
+{
+  Vector3 vZ = forward;
+  vZ.Normalize();
+
+  Vector3 vX = up.Cross(vZ);
+  vX.Normalize();
+
+  // If something invalid input comes, vX length become zero.
+  if(DALI_UNLIKELY(Dali::EqualsZero(vX.Length())))
+  {
+    DALI_LOG_ERROR("Invalid value inputed, forward : %f %f %f ,  up : %f %f %f\n", forward.x, forward.y, forward.z, up.x, up.y, up.z);
+    return Quaternion();
+  }
+
+  Vector3 vY = vZ.Cross(vX);
+  vY.Normalize();
+
+  return Quaternion(vX, vY, vZ);
+}
 } // namespace
 bool ConvertScreenToLocal(
   const Matrix&   viewMatrix,
@@ -616,4 +646,29 @@ Vector4 CalculateActorWorldColor(const Actor& actor)
   return worldColor;
 }
 
+Quaternion CalculateActorLookAtOrientation(const Actor& actor, Vector3 target, Vector3 up, Vector3 localForward, Vector3 localUp)
+{
+  Vector3 currentWorldPosition = CalculateActorWorldTransform(actor).GetTranslation3();
+
+  Quaternion worldToTarget = GetOrientationFromForwardAndUpVector(target - currentWorldPosition, up);
+  Quaternion worldToLocal  = GetOrientationFromForwardAndUpVector(localForward, localUp);
+
+  // Rotate by this order : Local --> World --> Target
+  Quaternion ret = worldToTarget / worldToLocal;
+
+  // If we inherit orientation, get parent's world orientation, and revert it.
+  if(actor.IsOrientationInherited() && actor.GetParent())
+  {
+    // Get Parent information.
+    Vector3       parentPosition, parentScale;
+    Quaternion    parentOrientation;
+    const Matrix& parentMatrix = CalculateActorWorldTransform(*actor.GetParent());
+    parentMatrix.GetTransformComponents(parentPosition, parentOrientation, parentScale);
+
+    ret = ret / parentOrientation;
+  }
+
+  return ret;
+}
+
 } // namespace Dali::Internal
index af37dfa..a48f3f0 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_EVENT_ACTORS_ACTOR_COORDS_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -249,6 +249,18 @@ Matrix CalculateActorWorldTransform(const Actor& actor);
  */
 Vector4 CalculateActorWorldColor(const Actor& actor);
 
+/**
+ * @brief Get the rotate of the actor look at specific position.
+ *
+ * @param[in] actor The actor for which to calculate the look at orientation.
+ * @param[in] target The target world position to look at.
+ * @param[in] up The up vector after target look at.
+ * @param[in] localForward The forward vector of actor when it's orientation is not applied.
+ * @param[in] localUp The up vector of actor when it's orientation is not applied.
+ * @return The orientation result of this lookAt result.
+ */
+Quaternion CalculateActorLookAtOrientation(const Actor& actor, Vector3 target, Vector3 up, Vector3 localForward, Vector3 localUp);
+
 } // namespace Dali::Internal
 
 #endif // DALI_INTERNAL_EVENT_ACTORS_ACTOR_COORDS_H