[Tizen] Use BoundingBox for 3D model hit test 79/324979/1 accepted/tizen_7.0_unified tizen_7.0 accepted/tizen/7.0/unified/20250603.172044 accepted/tizen/7.0/unified/20250605.075921
authorSeungho Baek <sbsh.baek@samsung.com>
Thu, 29 May 2025 12:42:24 +0000 (21:42 +0900)
committerSeungho Baek <sbsh.baek@samsung.com>
Thu, 29 May 2025 12:47:09 +0000 (21:47 +0900)
Change-Id: I2403a03598628025d03130d0e75417af614fc52b
Signed-off-by: Seungho Baek <sbsh.baek@samsung.com>
dali/internal/event/events/hit-test-algorithm-impl.cpp
dali/internal/event/events/ray-test.cpp
dali/internal/event/events/ray-test.h

index ebadf6d1083a7136b74f65b78b7f837171431f4c..a1c16682aed83f06d69805d3a6e98543678c03d0 100644 (file)
@@ -175,7 +175,7 @@ void HitTestActor(const RenderTask&         renderTask,
                   const float&              nearClippingPlane,
                   const float&              farClippingPlane,
                   HitTestInterface&         hitCheck,
-                  const RayTest&            rayTest,
+                  RayTest&                  rayTest,
                   const Integration::Point& point,
                   const uint32_t            eventTime,
                   bool                      clippingActor,
@@ -195,8 +195,21 @@ void HitTestActor(const RenderTask&         renderTask,
       Vector2 hitPointLocal;
       float   distance;
 
+      Vector3 hitPointLocal3D;
+      Dali::Layer::Behavior layerBehavior = actor.GetLayer().GetProperty<Dali::Layer::Behavior>(Dali::Layer::Property::BEHAVIOR);
+
+      bool isHitted = false;
+      if(layerBehavior == Dali::Layer::Behavior::LAYER_3D)
+      {
+        isHitted = rayTest.ActorBoundingBoxTest(actor, rayOrigin, rayDir, hitPointLocal3D, distance);
+      }
+      else
+      {
+        isHitted = rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance);
+      }
+
       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
-      if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
+      if(isHitted)
       {
         // Calculate z coordinate value in Camera Space.
         const Matrix&  viewMatrix          = renderTask.GetCameraActor()->GetViewMatrix();
@@ -296,7 +309,7 @@ HitActor HitTestWithinLayer(Actor&                                     actor,
                             const bool&                                overlayed,
                             bool&                                      overlayHit,
                             bool                                       layerIs3d,
-                            const RayTest&                             rayTest,
+                            RayTest&                                   rayTest,
                             const Integration::Point&                  point,
                             const uint32_t                             eventTime)
 {
@@ -459,7 +472,7 @@ bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
                        Vector2                                    screenCoordinates,
                        Results&                                   results,
                        HitTestInterface&                          hitCheck,
-                       const RayTest&                             rayTest)
+                       RayTest&                                   rayTest)
 {
   if(renderTask.IsHittable(screenCoordinates))
   {
index c2e3e29e60ec4902d5b698d32cc4b6113a6d28d9..4e32684624f5c46da10fad1d58ab7c79bc563247 100644 (file)
@@ -195,6 +195,84 @@ bool RayTest::ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin,
   return hit;
 }
 
+bool RayTest::ActorBoundingBoxTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector3& hitPointLocal, float& distance)
+{
+  bool hit = false;
+
+  if(actor.OnScene())
+  {
+    const Node& node = actor.GetNode();
+
+    // Transforms the ray to the local reference system.
+    // Calculate the inverse of Model matrix
+    Matrix modelMatrix(false /*don't init*/);
+    modelMatrix           = node.GetWorldMatrix(0);
+    Matrix invModelMatrix = modelMatrix;
+    invModelMatrix.Invert();
+
+    Vector4 rayOriginLocal(invModelMatrix * rayOrigin);
+    Vector4 rayDirVector = rayDir;
+    rayDirVector.w       = 0.0f; // Make it Vector.
+    Vector4 rayDirLocal(invModelMatrix * rayDirVector);
+    rayDirLocal.Normalize();
+
+    Vector3 currentSize = node.GetSize(EventThreadServices::Get().GetEventBufferIndex());
+    Vector3 AABBMin     = Vector3(-currentSize.width * 0.5f, -currentSize.height * 0.5f, -currentSize.depth * 0.5f);
+    Vector3 AABBMax     = Vector3(currentSize.width * 0.5f, currentSize.height * 0.5f, currentSize.depth * 0.5f);
+
+    float distanceMin = 0.0f;
+    float distanceMax = std::numeric_limits<float>::infinity();
+    for(uint32_t i = 0; i < 3u; ++i)
+    {
+      float rayOriginScalar    = (i == 0) ? rayOriginLocal.x : ((i == 1) ? rayOriginLocal.y : rayOriginLocal.z);
+      float rayDirectionScalar = (i == 0) ? rayDirLocal.x : ((i == 1) ? rayDirLocal.y : rayDirLocal.z);
+      float boxMin             = (i == 0) ? AABBMin.x : ((i == 1) ? AABBMin.y : AABBMin.z);
+      float boxMax             = (i == 0) ? AABBMax.x : ((i == 1) ? AABBMax.y : AABBMax.z);
+
+      // Check the ray is parallel to an axis.
+      if(std::abs(rayDirectionScalar) < Math::MACHINE_EPSILON_1)
+      {
+        // If the ray is parallel to an axis and the ray's origin along that axis lies outside the
+        // corresponding minimum and maximum bounds of the AABB, there can be no intersection
+        // between the ray and the AABB.
+        if(rayOriginScalar < boxMin || rayOriginScalar > boxMax)
+        {
+          return false;
+        }
+      }
+      else
+      {
+        float hit1 = (boxMin - rayOriginScalar) / rayDirectionScalar;
+        float hit2 = (boxMax - rayOriginScalar) / rayDirectionScalar;
+
+        if(hit1 > hit2)
+        {
+          std::swap(hit1, hit2);
+        }
+
+        distanceMin = std::max(distanceMin, hit1);
+        distanceMax = std::min(distanceMax, hit2);
+
+        if(distanceMin > distanceMax)
+        {
+          return false;
+        }
+      }
+    }
+
+    Vector4 hitPointLocalVector4 = rayOriginLocal + rayDirLocal * distanceMin;
+    hitPointLocalVector4.w       = 1.0f; // Make it Position
+    distance                     = Vector3(rayOrigin - (modelMatrix * hitPointLocalVector4)).Length();
+    hitPointLocal                = Vector3(hitPointLocalVector4);
+    hitPointLocal.x += currentSize.x * 0.5f;
+    hitPointLocal.y = currentSize.y * 0.5f - hitPointLocal.y;
+
+    hit = true;
+  }
+
+  return hit;
+}
+
 } // namespace Internal
 
 } // namespace Dali
index eb37162108fe3aca8c5322103d1c9d3ee4a87ec4..efc0bc74264e4fbeb0eeba437a85fbc1c197c608 100644 (file)
@@ -72,6 +72,20 @@ public:
    */
   bool ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance) const;
 
+  /**
+   * Performs a ray-actor test with the given pick-ray and the given actor's Bounding Box.
+   *
+   * @param[in] actor The actor to perform the ray-sphere test on
+   * @param[in] rayOrigin The ray origin in the world's reference system.
+   * @param[in] rayDir The ray director vector in the world's reference system.
+   * @param[out] hitPointLocal The hit point in the Actor's local reference system.
+   * @param[out] distance The distance from the hit point to the camera.
+   * @return True if the ray intersects the actor's Bounding Box.
+   *
+   * @note The actor coordinates are relative to the top-left (0.0, 0.0, 0.5)
+   */
+  bool ActorBoundingBoxTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector3& hitPointLocal, float& distance);
+
 private:
   const EventThreadServices& mEventThreadServices;
 };