Hit Test Fix for layered Hit test 45/317745/46
authorSeungho Baek <sbsh.baek@samsung.com>
Tue, 7 Jan 2025 07:19:35 +0000 (16:19 +0900)
committerSeungho Baek <sbsh.baek@samsung.com>
Thu, 20 Mar 2025 04:08:34 +0000 (13:08 +0900)
Change-Id: Id2611e20a20259dcfbd341fa37b3029cd326e3cc
Signed-off-by: Seungho Baek <sbsh.baek@samsung.com>
20 files changed:
automated-tests/src/dali-internal/CMakeLists.txt
automated-tests/src/dali-internal/tct-Dali-internal-RayTest.cpp [new file with mode: 0644]
automated-tests/src/dali/utc-Dali-Actor.cpp
automated-tests/src/dali/utc-Dali-GeoHitTestAlgorithm.cpp
automated-tests/src/dali/utc-Dali-HitTestAlgorithm.cpp
dali/internal/event/actors/actor-impl.cpp
dali/internal/event/actors/actor-impl.h
dali/internal/event/actors/actor-parent-impl.cpp
dali/internal/event/actors/camera-actor-impl.cpp
dali/internal/event/actors/camera-actor-impl.h
dali/internal/event/common/scene-impl.h
dali/internal/event/events/gesture-processor.cpp
dali/internal/event/events/hit-test-algorithm-impl.cpp
dali/internal/event/events/hit-test-algorithm-impl.h
dali/internal/event/events/hover-event-processor.cpp
dali/internal/event/events/ray-test.cpp
dali/internal/event/events/ray-test.h
dali/internal/event/render-tasks/render-task-impl.cpp
dali/internal/event/render-tasks/render-task-impl.h
dali/public-api/render-tasks/render-task.h

index 5ae002753c9d9e11edb88aed97f7ebe6924b935c..e382d22ccb6edefcad05fed79f6d48d311cfce34 100644 (file)
@@ -29,6 +29,7 @@ SET(TC_SOURCES
   utc-Dali-Internal-PinchGesture.cpp
   utc-Dali-Internal-PinchGestureProcessor.cpp
   utc-Dali-Internal-PipelineCache.cpp
+  tct-Dali-internal-RayTest.cpp
   utc-Dali-Internal-RotationGesture.cpp
   utc-Dali-Internal-Shader.cpp
   utc-Dali-Internal-TapGesture.cpp
diff --git a/automated-tests/src/dali-internal/tct-Dali-internal-RayTest.cpp b/automated-tests/src/dali-internal/tct-Dali-internal-RayTest.cpp
new file mode 100644 (file)
index 0000000..40109f9
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2022 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali-test-suite-utils.h>
+#include <dali/internal/event/actors/actor-impl.h>
+#include <dali/internal/event/events/ray-test.h>
+#include <dali/public-api/dali-core.h>
+#include <stdlib.h>
+
+#include <iostream>
+
+using namespace Dali;
+
+void utc_dali_internal_ray_test_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void utc_dali_internal_ray_test_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+int UtcDaliTapGestureActorBoundingBoxTestP(void)
+{
+  TestApplication application;
+  tet_infoline("Testing Dali::HitTestAlgorithm::BuildPickingRay positive test");
+
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::NAME, "Green");
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::SIZE, Vector3(1.0f, 1.0f, 1.0f));
+
+  application.GetScene().Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render(0);
+
+  Vector3 hitPointLocal;
+  float distance;
+  bool success = Dali::Internal::RayTest::ActorBoundingBoxTest(GetImplementation(actor), Vector4(0.0f, 0.0f, 10.0f, 1.0f), Vector4(0.0f, 0.0f, -1.0f, 0.0f), hitPointLocal, distance);
+
+  tet_printf("hitPointLocal : %f, %f, %f\n", hitPointLocal.x, hitPointLocal.y, hitPointLocal.z);
+  tet_printf("distance : %f\n", distance);
+  DALI_TEST_EQUAL(success, true);
+  DALI_TEST_EQUAL(distance, 9.5f);
+  DALI_TEST_EQUALS(hitPointLocal, Dali::Vector3::ONE * 0.5f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliTapGestureActorBoundingBoxTestN(void)
+{
+  TestApplication application;
+  tet_infoline("Testing Dali::HitTestAlgorithm::BuildPickingRay positive test");
+
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::NAME, "Green");
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::SIZE, Vector3(1.0f, 1.0f, 1.0f));
+
+  application.GetScene().Add(actor);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render(0);
+
+  Vector3 hitPointLocal;
+  float distance;
+  bool success = Dali::Internal::RayTest::ActorBoundingBoxTest(GetImplementation(actor), Vector4(0.0f, 0.0f, 10.0f, 1.0f), Vector4(0.0f, 0.5f, -1.0f, 0.0f), hitPointLocal, distance);
+
+  DALI_TEST_EQUAL(success, false);
+
+  END_TEST;
+}
index a4d3c63e597fb08f5fff406b3e69eb22e1786fb3..38fd77a3483c1aecb539d6c51fb30a74d411af78 100644 (file)
@@ -7427,9 +7427,14 @@ int UtcDaliActorGeoTouchLowerBelow(void)
   actorC.SetProperty(Actor::Property::WIDTH_RESIZE_POLICY, "FILL_TO_PARENT");
   actorC.SetProperty(Actor::Property::HEIGHT_RESIZE_POLICY, "FILL_TO_PARENT");
 
+  actorA.SetProperty(Actor::Property::NAME, "actorA");
+  actorB.SetProperty(Actor::Property::NAME, "actorB");
+  actorC.SetProperty(Actor::Property::NAME, "actorC");
+
   Actor container = Actor::New();
   container.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
   container.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+  container.SetProperty(Actor::Property::NAME, "container");
   stage.Add(container);
 
   container.Add(actorA);
index 941cb941b37f592f7cd9d10b8c27c643e5f84778..9ee75e8a7c2bb5ca3e00dcc7591c6c990381e43f 100644 (file)
@@ -483,8 +483,8 @@ int UtcDaliGeoHitTestAlgorithmOverlay(void)
   //Hit in the intersection red, green. Should pick the red actor since it is an child of overlay.
   HitTest(stage, Vector2(stageSize.x * 15.0f / 24.0f, stageSize.y * 11.0f / 24.0f), results, &DefaultIsActorTouchableFunction, Integration::Scene::TouchPropagationType::GEOMETRY);
   tet_printf("%d %d %d , %f %f\n", results.actor == red ? 1 : 0, results.actor == green ? 1 : 0, results.actor == blue ? 1 : 0, results.actorCoordinates.x, results.actorCoordinates.y);
-  DALI_TEST_CHECK(results.actor == red);
-  DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 3.0f / 12.0f, actorSize.y * 11.0f / 12.0f), TEST_LOCATION);
+  DALI_TEST_CHECK(results.actor == green);
+  DALI_TEST_EQUALS(results.actorCoordinates, Vector2(actorSize.x * 5.0f / 12.0f, actorSize.y * 1.0f / 12.0f), TEST_LOCATION);
 
   //Hit in the intersection blue, green. Should pick the blue actor since it is an overlay.
   HitTest(stage, Vector2(stageSize.x * 11.0f / 24.0f, stageSize.y * 13.0f / 24.0f), results, &DefaultIsActorTouchableFunction, Integration::Scene::TouchPropagationType::GEOMETRY);
@@ -565,7 +565,7 @@ int UtcDaliGeoHitTestAlgorithmOrder(void)
   offRenderTask.SetInputEnabled(true);
   offRenderTask.SetCameraActor(cameraActor);
   offRenderTask.SetSourceActor(green);
-  offRenderTask.SetScreenToFrameBufferMappingActor(green);
+  offRenderTask.SetScreenToFrameBufferMappingActor(blue);
 
   Dali::Texture texture      = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, unsigned(stageSize.width), unsigned(stageSize.height));
   FrameBuffer   renderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::DEPTH);
@@ -576,6 +576,7 @@ int UtcDaliGeoHitTestAlgorithmOrder(void)
   application.SendNotification();
   application.Render(10);
 
+  // Because the offRenderTask is set to exclusive, the green will not be rendered and it cannot be touched as a MappingActor.
   HitTestAlgorithm::Results results;
   HitTest(stage, stageSize / 2.0f, results, &DefaultIsActorTouchableFunction, Integration::Scene::TouchPropagationType::GEOMETRY);
   DALI_TEST_CHECK(results.actor == green);
@@ -623,7 +624,7 @@ int UtcDaliGeoHitTestAlgorithmExclusiveMultiple(void)
   offRenderTask.SetInputEnabled(true);
   offRenderTask.SetCameraActor(cameraActor);
   offRenderTask.SetSourceActor(green);
-  offRenderTask.SetScreenToFrameBufferMappingActor(green);
+  offRenderTask.SetScreenToFrameBufferMappingActor(blue);
 
   Dali::Texture texture      = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, unsigned(stageSize.width), unsigned(stageSize.height));
   FrameBuffer   renderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::DEPTH);
@@ -634,7 +635,7 @@ int UtcDaliGeoHitTestAlgorithmExclusiveMultiple(void)
   offRenderTask2.SetInputEnabled(true);
   offRenderTask2.SetCameraActor(cameraActor);
   offRenderTask2.SetSourceActor(green);
-  offRenderTask2.SetScreenToFrameBufferMappingActor(green);
+  offRenderTask2.SetScreenToFrameBufferMappingActor(blue);
   offRenderTask2.SetFrameBuffer(renderTarget);
 
   // Render and notify
index c01da5d2eda3232a36ec8c18ab191b3d49e6c0b0..ff8b69d839b52c22004450fe3d10e776a3b45327 100644 (file)
@@ -647,7 +647,7 @@ int UtcDaliHitTestAlgorithmOrder1(void)
   offRenderTask.SetInputEnabled(true);
   offRenderTask.SetCameraActor(cameraActor);
   offRenderTask.SetSourceActor(green);
-  offRenderTask.SetScreenToFrameBufferMappingActor(green);
+  offRenderTask.SetScreenToFrameBufferMappingActor(blue);
 
   Dali::Texture texture      = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, unsigned(stageSize.width), unsigned(stageSize.height));
   FrameBuffer   renderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::DEPTH);
@@ -973,7 +973,7 @@ int UtcDaliHitTestAlgorithmExclusiveMultiple(void)
   offRenderTask.SetInputEnabled(true);
   offRenderTask.SetCameraActor(cameraActor);
   offRenderTask.SetSourceActor(green);
-  offRenderTask.SetScreenToFrameBufferMappingActor(green);
+  offRenderTask.SetScreenToFrameBufferMappingActor(blue);
 
   Dali::Texture texture      = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGB888, unsigned(stageSize.width), unsigned(stageSize.height));
   FrameBuffer   renderTarget = FrameBuffer::New(stageSize.width, stageSize.height, FrameBuffer::Attachment::DEPTH);
@@ -984,7 +984,7 @@ int UtcDaliHitTestAlgorithmExclusiveMultiple(void)
   offRenderTask2.SetInputEnabled(true);
   offRenderTask2.SetCameraActor(cameraActor);
   offRenderTask2.SetSourceActor(green);
-  offRenderTask2.SetScreenToFrameBufferMappingActor(green);
+  offRenderTask2.SetScreenToFrameBufferMappingActor(blue);
   offRenderTask2.SetFrameBuffer(renderTarget);
 
   // Render and notify
index f476f6bb5ed15ff10c2deeeb6a448dfa52e60318..b593bf7e079ea971be92e1f784146600eb1fec6d 100644 (file)
@@ -994,9 +994,26 @@ bool Actor::IsTransparent() const
 
 void Actor::SetDrawMode(DrawMode::Type drawMode)
 {
+  if(mDrawMode == drawMode)
+  {
+    return;
+  }
+
   // this flag is not animatable so keep the value
   mDrawMode = drawMode;
 
+  if(mScene)
+  {
+    if(IsOverlay())
+    {
+      mScene->SetOverlayContent();
+    }
+    else
+    {
+      mScene->RemoveOverlayContent();
+    }
+  }
+
   // node is being used in a separate thread; queue a message to set the value
   SetDrawModeMessage(GetEventThreadServices(), GetNode(), drawMode);
 }
@@ -1404,7 +1421,7 @@ void Actor::NotifyStageConnection(bool notify)
   }
 }
 
-void Actor::DisconnectFromStage(bool notify)
+void Actor::DisconnectFromScene(bool notify)
 {
   // This container is used instead of walking the Actor hierachy.
   // It protects us when the Actor hierachy is modified during OnSceneDisconnectionExternal callbacks.
@@ -1427,7 +1444,7 @@ void Actor::DisconnectFromStage(bool notify)
 
 /**
  * This method is called by an actor or its parent, before a node removal message is sent.
- * This is recursive; the child calls DisconnectFromStage() for its children.
+ * This is recursive; the child calls DisconnectFromScene() for its children.
  */
 void Actor::DisconnectFromSceneGraph()
 {
@@ -1670,7 +1687,7 @@ void Actor::SetParent(ActorParent* parent, bool notify)
       DisconnectNodeMessage(GetEventThreadServices().GetUpdateManager(), GetNode());
 
       // Instruct each actor to discard pointers to the scene-graph
-      DisconnectFromStage(notify);
+      DisconnectFromScene(notify);
     }
 
     mScene = nullptr;
index 78b625fcf586c11bc7d887de7399373e6c786bef..a8d5fcaeca50da9d3af2ff0629059087aaf4fe9a 100644 (file)
@@ -1219,6 +1219,22 @@ public:
     return mKeyboardFocusableChildren;
   }
 
+  /**
+   * Sets this actor is used for mapping Actor of a RenderTask
+   */
+  void SetIsRenderTaskMappingActor(bool isRenderTaskMappingActor)
+  {
+    mIsRenderTaskMappingActor = isRenderTaskMappingActor;
+  }
+
+  /**
+   * Retrieves whether the Actor is used as a mapping Actor of a RenderTask.
+   */
+  bool IsRenderTaskMappingActor() const
+  {
+    return mIsRenderTaskMappingActor;
+  }
+
   /**
    * @copydoc Dali::DevelActor::IsHittable()
    */
@@ -1674,10 +1690,10 @@ protected:
   void NotifyStageConnection(bool notify);
 
   /**
-   * Called on a child during Remove() when the actor was previously on the Stage.
+   * Called on a child during Remove() when the actor was previously on the Scene.
    * @param[in] notify Emits notification if set to true.
    */
-  void DisconnectFromStage(bool notify);
+  void DisconnectFromScene(bool notify);
 
   /**
    * Disconnect the Node associated with this Actor from the scene-graph.
@@ -1685,7 +1701,7 @@ protected:
   void DisconnectFromSceneGraph();
 
   /**
-   * Helper for DisconnectFromStage, to notify a disconnected actor through the public API.
+   * Helper for DisconnectFromScene, to notify a disconnected actor through the public API.
    * @param[in] notify Emits notification if set to true.
    */
   void NotifyStageDisconnection(bool notify);
@@ -1862,7 +1878,7 @@ private:
 
   /**
    * For use in internal derived classes.
-   * This is called during DisconnectFromStage(), before the actor removes its node from the scene-graph.
+   * This is called during DisconnectFromScene(), before the actor removes its node from the scene-graph.
    * The derived class must not modify the actor hierachy (Add/Remove children) during this callback.
    */
   virtual void OnSceneDisconnectionInternal()
@@ -1879,7 +1895,7 @@ private:
 
   /**
    * For use in external (CustomActor) derived classes.
-   * This is called after the atomic DisconnectFromStage() traversal has been completed.
+   * This is called after the atomic DisconnectFromScene() traversal has been completed.
    */
   virtual void OnSceneDisconnectionExternal()
   {
@@ -2044,6 +2060,8 @@ protected:
   uint32_t    mSortedDepth; ///< The sorted depth index. A combination of tree traversal and sibling order.
   int16_t     mDepth;       ///< The depth in the hierarchy of the actor. Only 32,767 levels of depth are supported
 
+  bool mIsRenderTaskMappingActor{false};
+
   int16_t mLayer3DParentsCount; ///< The number of layer with 3D behaviour in ancestors include this. It will be 0 if actor is not on scene.
 
   const bool               mIsRoot : 1;                    ///< Flag to identify the root actor
index 253fc6183f49e361baf33a74e5503596210aec2f..ed8a3ff06ea0000e9f4bb2492bdaaed22cf8a0a5 100644 (file)
@@ -466,6 +466,11 @@ void ActorParentImpl::RecursiveConnectToScene(ActorContainer& connectionList, ui
   // This stage is atomic; avoid emitting callbacks until all Actors are connected
   connectionList.push_back(ActorPtr(&mOwner));
 
+  if(mOwner.mScene && mOwner.IsOverlay())
+  {
+    mOwner.mScene->SetOverlayContent();
+  }
+
   // Recursively connect children
   if(mChildren)
   {
@@ -479,6 +484,11 @@ void ActorParentImpl::RecursiveConnectToScene(ActorContainer& connectionList, ui
 
 void ActorParentImpl::RecursiveDisconnectFromScene(ActorContainer& disconnectionList)
 {
+  if(mOwner.mScene && mOwner.IsOverlay())
+  {
+    mOwner.mScene->RemoveOverlayContent();
+  }
+
   // need to change state first so that internals relying on IsOnScene() inside OnSceneDisconnectionInternal() get the correct value
   mOwner.mIsOnScene           = false;
   mOwner.mScene               = nullptr;
index 805ab2b583b0f02e83e5110d6763b5ce4e2fcf2d..1ac12e64f7596f28fd9ac0eb2b8837c36b1a666d 100644 (file)
@@ -574,7 +574,7 @@ bool CameraActor::BuildPickingRay(const Vector2&  screenCoordinates,
     rayOrigin.w = 1.0f;
 
     // Transform the touch point from the screen coordinate system to the world coordinates system.
-    Vector4       near(screenCoordinates.x - static_cast<float>(viewport.x),
+    Vector4 near(screenCoordinates.x - static_cast<float>(viewport.x),
                  static_cast<float>(viewport.height) - (screenCoordinates.y - static_cast<float>(viewport.y)),
                  0.f,
                  1.f);
@@ -584,6 +584,7 @@ bool CameraActor::BuildPickingRay(const Vector2&  screenCoordinates,
     // Compute the ray's director vector.
     rayDirection.x = near.x - rayOrigin.x;
     rayDirection.y = near.y - rayOrigin.y;
+    rayDirection.y = (mInvertYAxis) ? -rayDirection.y : rayDirection.y;
     rayDirection.z = near.z - rayOrigin.z;
     rayDirection.Normalize();
     rayDirection.w = 1.f;
@@ -604,6 +605,15 @@ bool CameraActor::BuildPickingRay(const Vector2&  screenCoordinates,
   return success;
 }
 
+bool CameraActor::BuildPickingRay(const Vector2& screenCoordinates,
+                                  const Vector2& screenSize,
+                                  Vector4&       rayOrigin,
+                                  Vector4&       rayDirection)
+{
+  Viewport viewport(0, 0, screenSize.x, screenSize.y);
+  return BuildPickingRay(screenCoordinates, viewport, rayOrigin, rayDirection);
+}
+
 const Matrix& CameraActor::GetViewMatrix() const
 {
   if(OnScene())
index 3a20af0c6ba9078043057955a257717d4d5ecca1..d226b1021b98835b2146a5706e1ca7b0674e1a51 100644 (file)
@@ -183,6 +183,16 @@ public:
    */
   bool BuildPickingRay(const Vector2& screenCoordinates, const Viewport& viewport, Vector4& rayOrigin, Vector4& rayDirection);
 
+  /**
+   * Build a picking ray with this camera and given screen coordinates (Not considers viewport.)
+   * @param [in] screenCoordinates the ray passed through
+   * @param [in] screenSize screen Size.
+   * @param [out] rayOrigin for the picking ray
+   * @param [out] rayDirection for the picking ray
+   * @return true if the building was successful, false if its not possible (camera is not valid for hit testing)
+   */
+  bool BuildPickingRay(const Vector2& screenCoordinates, const Vector2& screenSize, Vector4& rayOrigin, Vector4& rayDirection);
+
   /**
    * Retrieve the view-matrix; This will only be valid when the actor is on-stage.
    * @return The view-matrix.
index a31f5d7f985f6d9679f43cff81a40f741734e78a..2af5949efec860cf80e7b8bc10015ba4dbe7ad2c 100644 (file)
@@ -203,6 +203,21 @@ public:
    */
   Vector4 GetBackgroundColor() const;
 
+  void SetOverlayContent()
+  {
+    mOverlayContentCount++;
+  }
+
+  void RemoveOverlayContent()
+  {
+    mOverlayContentCount--;
+  }
+
+  bool HasOverlayContent() const
+  {
+    return !!mOverlayContentCount;
+  }
+
   /**
    * @brief Get the Scene scene graph object
    *
@@ -475,7 +490,7 @@ private:
 
   LayerPtr mOverlayLayer;
 
-  // Ordered list of currently on-stage layers
+  // Ordered list of currently on-scene layers
   OwnerPointer<LayerList> mLayerList;
 
   IntrusivePtr<CameraActor> mDefaultCamera;
@@ -499,6 +514,8 @@ private:
   // The native window id
   int32_t mNativeId;
 
+  uint32_t mOverlayContentCount{0u};
+
   // The pan gesture state
   Dali::GestureState mPanGestureState;
 
index bb69145028963c185bee7abc871eb8f9b8523856..4a6b247f7303a088c56563bde6e6c9659ef1d1cc 100644 (file)
@@ -61,9 +61,9 @@ struct GestureHitTestCheck : public HitTestAlgorithm::HitTestInterface
     return layer->IsTouchConsumed();
   }
 
-  bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
+  bool ActorRequiresHitResultCheck(Actor* actor, Vector2 hitPointLocal) override
   {
-    return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
+    return actor->EmitHitTestResultSignal(GetPoint(), hitPointLocal, GetTimeStamp());
   }
 
   GestureType::Value mType;
@@ -139,7 +139,6 @@ void GestureProcessor::ProcessAndEmit(HitTestAlgorithm::Results& hitTestResults)
   {
     Actor*  hitTestActor(&GetImplementation(hitTestResults.actor));
     Actor*  actor(hitTestActor);
-    RayTest rayTest;
 
     while(actor)
     {
@@ -171,11 +170,11 @@ void GestureProcessor::ProcessAndEmit(HitTestAlgorithm::Results& hitTestResults)
             if((size.x > 0.0f) && (size.y > 0.0f))
             {
               // Ensure tap is within the actor's area
-              if(rayTest.SphereTest(*actor, hitTestResults.rayOrigin, hitTestResults.rayDirection)) // Quick check
+              if(RayTest::SphereTest(*actor, hitTestResults.rayOrigin, hitTestResults.rayDirection)) // Quick check
               {
                 Vector2 hitPointLocal;
                 float   distance(0.0f);
-                if(rayTest.ActorTest(*actor, hitTestResults.rayOrigin, hitTestResults.rayDirection, hitPointLocal, distance))
+                if(RayTest::ActorTest(*actor, hitTestResults.rayOrigin, hitTestResults.rayDirection, hitPointLocal, distance))
                 {
                   // One of the parents was the gestured actor so we can emit the signal for that actor.
                   EmitGestureSignal(actor, gestureDetectors, hitPointLocal);
index ab5d59f9d5f234772bc3d83cf7bbdfb8d3b288f3..35ab6e1ec809a4569771aa511ad3bd2d4b67d777 100644 (file)
 
 // INTERNAL INCLUDES
 #include <dali/integration-api/debug.h>
+#include <dali/internal/event/actors/actor-coords.h>
 #include <dali/internal/event/actors/actor-impl.h>
 #include <dali/internal/event/actors/camera-actor-impl.h>
 #include <dali/internal/event/actors/layer-impl.h>
 #include <dali/internal/event/actors/layer-list.h>
 #include <dali/internal/event/common/projection.h>
+#include <dali/internal/event/common/scene-impl.h>
 #include <dali/internal/event/events/ray-test.h>
 #include <dali/internal/event/render-tasks/render-task-impl.h>
 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
@@ -37,19 +39,41 @@ namespace Dali::Internal::HitTestAlgorithm
 {
 namespace
 {
-struct HitActor
+enum HitType
 {
-  HitActor()
-  : actor(nullptr),
-    distance(std::numeric_limits<float>::max()),
-    depth(std::numeric_limits<int>::min())
+  HIT_ACTOR,
+  HIT_MAPPING_ACTOR
+};
+
+struct Ray
+{
+  Vector4 origin;
+  Vector4 direction;
+};
+
+struct HitResult
+{
+  HitResult(Actor* actor, Vector2 hitPosition, float distance, HitType hitType, Ray ray)
+  : mActor(actor),
+    mHitPosition(hitPosition),
+    mDistance(distance),
+    mHitType(hitType),
+    mRay(ray)
   {
   }
 
-  Actor*  actor;       ///< The actor hit (if actor is hit, then this is initialised).
-  Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
-  float   distance;    ///< Distance from ray origin to hit actor.
-  int32_t depth;       ///< Depth index of this actor.
+  Actor*  mActor;       ///< The actor hit (if actor is hit, then this is initialised).
+  Vector2 mHitPosition; ///< Position of hit (only valid if actor valid).
+  float   mDistance;
+  HitType mHitType;
+  Ray     mRay;
+};
+
+struct HitCommonInformation
+{
+  const RenderTaskList::ExclusivesContainer&  exclusives;
+  std::unordered_map<uint32_t, RenderTaskPtr> mappingActors;
+  LayerList&                                  layers;
 };
 
 /**
@@ -85,12 +109,12 @@ struct HitTestFunctionWrapper : public HitTestInterface
     return false;
   }
 
-  bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
+  bool ActorRequiresHitResultCheck(Actor* actor, Vector2 hitPointLocal) override
   {
     // Geometry way does not require Hittest from the client.
-    if(propagationType == Integration::Scene::TouchPropagationType::PARENT)
+    if(GetPropagationType() == Integration::Scene::TouchPropagationType::PARENT)
     {
-      return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
+      return actor->EmitHitTestResultSignal(GetPoint(), hitPointLocal, GetTimeStamp());
     }
     return true;
   }
@@ -121,17 +145,17 @@ struct ActorTouchableCheck : public HitTestInterface
     return layer->IsTouchConsumed();
   }
 
-  bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
+  bool ActorRequiresHitResultCheck(Actor* actor, Vector2 hitPointLocal) override
   {
     // The Geometry way behaves like AllowedOnlyOwnTouch is enabled.
-    if(point.GetState() != PointState::STARTED && (propagationType == Integration::Scene::TouchPropagationType::GEOMETRY || actor->IsAllowedOnlyOwnTouch()) && ownActor != actor)
+    if(GetPoint().GetState() != PointState::STARTED && (GetPropagationType() == Integration::Scene::TouchPropagationType::GEOMETRY || actor->IsAllowedOnlyOwnTouch()) && ownActor != actor)
     {
       return false;
     }
     // Geometry way does not require Hittest from the client.
-    if(propagationType == Integration::Scene::TouchPropagationType::PARENT)
+    if(GetPropagationType() == Integration::Scene::TouchPropagationType::PARENT)
     {
-      return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
+      return actor->EmitHitTestResultSignal(GetPoint(), hitPointLocal, GetTimeStamp());
     }
     return true;
   }
@@ -144,721 +168,507 @@ struct ActorTouchableCheck : public HitTestInterface
 };
 
 /**
- * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
+ * Return true if actor is sourceActor or a descendent of sourceActor
  */
-bool IsActorExclusiveToAnotherRenderTask(const Actor&                               actor,
-                                         const RenderTask&                          renderTask,
-                                         const RenderTaskList::ExclusivesContainer& exclusives)
-
+bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
 {
-  bool exclusiveByOtherTask = false;
-  if(exclusives.size())
+  if(&sourceActor == &actor)
   {
-    for(const auto& exclusive : exclusives)
-    {
-      if(exclusive.actor.GetActor() == &actor)
-      {
-        if(exclusive.renderTaskPtr != &renderTask)
-        {
-          exclusiveByOtherTask = true;
-        }
-        else
-        {
-          // Fast-out if render task is itself
-          return false;
-        }
-      }
-    }
+    return true;
+  }
+
+  Actor* parent = actor.GetParent();
+  if(parent)
+  {
+    return IsWithinSourceActors(sourceActor, *parent);
   }
-  return exclusiveByOtherTask;
+
+  // Not within source actors
+  return false;
 }
 
 /**
- * Checks if actor or anyone of it's parents are an overlay, until either the currentActor is reached or the root actor
- * @param actor The child-actor and it's parents to check
- * @param currentActor The top actor of this current branch which we should not go above
- * @return True if the actor or a parent is an overlay, false otherwise
+ * Returns true if the actor and all of the actor's parents are hittable.
  */
-inline bool IsOnOverlay(Actor* actor, Actor* currentActor)
+bool IsActorActuallyHittable(Actor* actor, HitTestInterface& hitCheck)
 {
-  while(actor && actor != currentActor)
+  Actor* currentActor = actor;
+  // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
+  while(currentActor)
   {
-    if(actor->IsOverlay())
+    if(!hitCheck.DescendActorHierarchy(currentActor))
     {
-      return true;
+      return false;
     }
-    actor = actor->GetParent();
+    currentActor = currentActor->GetParent();
   }
-  return false;
+
+  return true;
 }
 
 /**
- * Hit tests the given actor and updates the in/out variables appropriately
+ * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
  */
-bool HitTestActor(const RenderTask&                              renderTask,
-                  const Vector4&                                 rayOrigin,
-                  const Vector4&                                 rayDir,
-                  const float&                                   nearClippingPlane,
-                  const float&                                   farClippingPlane,
-                  HitTestInterface&                              hitCheck,
-                  const RayTest&                                 rayTest,
-                  const Integration::Point&                      point,
-                  const uint32_t                                 eventTime,
-                  bool                                           clippingActor,
-                  bool                                           overlayedActor,
-                  Actor&                                         actor,
-                  bool&                                          overlayHit,
-                  HitActor&                                      hit,
-                  const Integration::Scene::TouchPropagationType propagationType)
+void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
 {
-  bool isClippingOrHittable = clippingActor || hitCheck.IsActorHittable(&actor);
-  bool isGeometry           = propagationType == Integration::Scene::TouchPropagationType::GEOMETRY;
-  if(isClippingOrHittable || isGeometry)
-  {
-    Vector3 size(actor.GetCurrentSize());
+  CameraActor* cameraActor = renderTask.GetCameraActor();
+  nearClippingPlane        = cameraActor->GetNearClippingPlane();
+  farClippingPlane         = cameraActor->GetFarClippingPlane();
+}
 
-    // Ensure the actor has a valid size.
-    // If so, perform a quick ray sphere test to see if our ray is close to the actor.
-    if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
-    {
-      Vector2 hitPointLocal;
-      float   distance;
+RenderTask* curRenderTask;
 
-      // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
-      if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
-      {
-        // Calculate z coordinate value in Camera Space.
-        const Matrix&  viewMatrix          = renderTask.GetCameraActor()->GetViewMatrix();
-        const Vector4& hitDir              = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
-        const float    cameraDepthDistance = (viewMatrix * hitDir).z;
+bool HitTestRenderTask(std::vector<std::shared_ptr<HitResult>>& hitResultList,
+                       RenderTask&                              renderTask,
+                       HitCommonInformation&                    hitCommonInformation,
+                       const Vector2&                           screenSize,
+                       Vector2                                  screenCoordinates,
+                       HitTestInterface&                        hitCheck);
 
-        // Check if cameraDepthDistance is between clipping plane
-        if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
-        {
-          if(isGeometry && actor.GetParent())
-          {
-            // If the child touches outside the parent's size boundary, it should not be hit.
-            if(!overlayedActor && !clippingActor && !actor.GetParent()->IsLayer())
-            {
-              Vector2 hitPointLocal;
-              float   distance;
-              if(!(rayTest.SphereTest(*actor.GetParent(), rayOrigin, rayDir) &&
-                   rayTest.ActorTest(*actor.GetParent(), rayOrigin, rayDir, hitPointLocal, distance)))
-              {
-                return false;
-              }
-            }
-          }
-
-          if(overlayHit && !overlayedActor)
-          {
-            // If we have already hit an overlay and current actor is not an overlay ignore current actor.
-          }
-          else
-          {
-            if(overlayedActor)
-            {
-              overlayHit = true;
-            }
-
-            // If the hit actor does not want to hit, the hit-test continues.
-            if(isClippingOrHittable && hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime, propagationType))
-            {
-              hit.actor       = &actor;
-              hit.hitPosition = hitPointLocal;
-              hit.distance    = distance;
-              hit.depth       = actor.GetSortingDepth();
-
-              if(actor.GetRendererCount() > 0)
-              {
-                // Get renderer with maximum depth
-                int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
-                for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
-                {
-                  int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
-                  if(depth > rendererMaxDepth)
-                  {
-                    rendererMaxDepth = depth;
-                  }
-                }
-                hit.depth += rendererMaxDepth;
-              }
-            }
-          }
-        }
-      }
-    }
+bool IsActorPickable(const Ray&   ray,
+                     const float& projectedNearClippingDistance,
+                     const float& projectedFarClippingDistance,
+                     Actor&       actor)
+{
+  Vector2 hitPointLocal;
+  float   distance;
+  if(!RayTest::ActorTest(actor, ray.origin, ray.direction, hitPointLocal, distance))
+  {
+    return false;
+  }
+
+  if(distance < projectedNearClippingDistance || distance > projectedFarClippingDistance)
+  {
+    return false;
   }
   return true;
 }
 
 /**
- * When iterating through the children of an actor, this method updates the child-hit-data.
+ * Hit tests the given actor and updates the in/out variables appropriately
  */
-void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
+std::shared_ptr<HitResult> HitTestActor(const Ray&            ray,
+                                        const float&          projectedNearClippingDistance,
+                                        const float&          projectedFarClippingDistance,
+                                        HitTestInterface&     hitCheck,
+                                        Actor&                actor,
+                                        Dali::Layer::Behavior layerBehavior)
 {
-  bool updateChildHit = false;
-  if(currentHit.distance >= 0.0f)
+  if(!hitCheck.IsActorHittable(&actor) && !actor.IsRenderTaskMappingActor())
   {
-    if(layerIs3d)
+    return nullptr;
+  }
+
+  Vector3 size = actor.GetCurrentSize();
+  if(size.x <= 0.0f || size.y < 0.0f)
+  {
+    return nullptr;
+  }
+
+  Vector2 hitPointLocal;
+  float   distance;
+  if(layerBehavior == Dali::Layer::Behavior::LAYER_3D)
+  {
+    Vector3 hitPointLocalVector3;
+    bool    hitSucceeded = RayTest::ActorBoundingBoxTest(actor, ray.origin, ray.direction, hitPointLocalVector3, distance);
+    hitPointLocal        = Vector2(hitPointLocalVector3);
+    if(!hitSucceeded)
     {
-      updateChildHit = ((currentHit.depth > childHit.depth) ||
-                        ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
+      return nullptr;
     }
-    else
+  }
+  else
+  {
+    if(!RayTest::SphereTest(actor, ray.origin, ray.direction))
     {
-      updateChildHit = currentHit.depth >= childHit.depth;
+      return nullptr;
     }
-  }
 
-  if(updateChildHit)
-  {
-    if(!parentIsRenderable || currentHit.depth > hit.depth ||
-       (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
+    if(!RayTest::ActorTest(actor, ray.origin, ray.direction, hitPointLocal, distance))
     {
-      childHit = currentHit;
+      return nullptr;
     }
   }
-}
 
-/**
- * Recursively hit test all the actors, without crossing into other layers.
- * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
- * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
- * of touch vector). The closest Hit-Tested Actor is that which is returned.
- * Exceptions to this rule are:
- * - When comparing against renderable parents, if Actor is the same distance
- * or closer than it's renderable parent, then it takes priority.
- */
-HitActor HitTestWithinLayer(Actor&                                         actor,
-                            const RenderTask&                              renderTask,
-                            const RenderTaskList::ExclusivesContainer&     exclusives,
-                            const Vector4&                                 rayOrigin,
-                            const Vector4&                                 rayDir,
-                            const float&                                   nearClippingPlane,
-                            const float&                                   farClippingPlane,
-                            HitTestInterface&                              hitCheck,
-                            const bool&                                    overlayed,
-                            bool&                                          overlayHit,
-                            bool                                           layerIs3d,
-                            const RayTest&                                 rayTest,
-                            const Integration::Point&                      point,
-                            const uint32_t                                 eventTime,
-                            std::list<Dali::Internal::Actor*>&             actorLists,
-                            const Integration::Scene::TouchPropagationType propagationType)
-{
-  HitActor hit;
+  if(distance < projectedNearClippingDistance || distance > projectedFarClippingDistance)
+  {
+    return nullptr;
+  }
 
-  if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
+  if(!hitCheck.ActorRequiresHitResultCheck(&actor, hitPointLocal))
   {
-    return hit;
+    return nullptr;
   }
 
-  // For clipping, regardless of whether we have hit this actor or not.
-  // This is used later to ensure all nested clipped children have hit
-  // all clipping actors also for them to be counted as hit.
-  const ClippingMode::Type clippingMode   = actor.GetClippingMode();
-  bool                     clippingActor  = clippingMode != ClippingMode::DISABLED;
-  bool                     overlayedActor = overlayed || actor.IsOverlay();
+  std::shared_ptr<HitResult> hitResult = std::make_shared<HitResult>(&actor, hitPointLocal, distance, (actor.IsRenderTaskMappingActor()) ? HitType::HIT_MAPPING_ACTOR : HitType::HIT_ACTOR, ray);
+
+  return hitResult;
+}
+
+bool IsActorExclusive(const Actor&                               actor,
+                      const RenderTaskList::ExclusivesContainer& exclusives)
+
+{
+  auto result = std::find_if(exclusives.begin(), exclusives.end(), [&actor](const RenderTaskList::Exclusive& exclusive)
+                             { return exclusive.actor.GetActor() == &actor; });
+  return (result != exclusives.end());
+}
 
-  // If we are a clipping actor or hittable...
-  if(!HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit, propagationType))
+inline bool IsActorValid(Actor&                                     actor,
+                         const RenderTaskList::ExclusivesContainer& exclusives,
+                         HitTestInterface&                          hitCheck)
+{
+  if(IsActorExclusive(actor, exclusives))
   {
-    return hit;
+    return false;
   }
 
-  // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
-  // Only do this if we're using CLIP_CHILDREN though, as children whose drawing mode is OVERLAY_2D are not clipped when CLIP_TO_BOUNDING_BOX is selected.
-  if(clippingActor && !(hit.actor) && (clippingMode == ClippingMode::CLIP_CHILDREN))
+  if(actor.IsLayer())
   {
-    return hit;
+    return false;
   }
-  else if(propagationType == Integration::Scene::TouchPropagationType::GEOMETRY && hit.actor)
+
+  if(!hitCheck.DescendActorHierarchy(&actor))
   {
-    // Saves the actors that can be hit as a list
-    actorLists.push_back(hit.actor);
+    return false;
   }
 
-  // Find a child hit, until we run out of actors in the current layer.
-  HitActor childHit;
-  if(actor.GetChildCount() > 0)
+  return true;
+}
+
+bool HitTestActorRecursively(std::vector<std::shared_ptr<HitResult>>& hitResultList,
+                             ActorPtr                                 currentActor,
+                             HitCommonInformation&                    hitCommonInformation,
+                             const Ray&                               ray,
+                             const float&                             projectedNearClippingDistance,
+                             const float&                             projectedFarClippingDistance,
+                             HitTestInterface&                        hitCheck,
+                             Dali::Layer::Behavior                    layerBehavior,
+                             bool                                     isKeepingHitTestRequired,
+                             bool                                     isOverlay)
+{
+  if(!isOverlay && currentActor->IsOverlay())
   {
-    childHit.distance        = std::numeric_limits<float>::max();
-    childHit.depth           = std::numeric_limits<int32_t>::min();
-    ActorContainer& children = actor.GetChildrenInternal();
+    return false;
+  }
 
-    // Hit test ALL children and calculate their distance.
-    bool parentIsRenderable = actor.IsRenderable();
+  std::shared_ptr<HitResult> hitResultOfThisActor;
+  bool                       isClippingRequired = (layerBehavior != Dali::Layer::LAYER_3D) && ((currentActor->GetClippingMode() != ClippingMode::DISABLED) || (hitCheck.GetPropagationType() == Integration::Scene::TouchPropagationType::GEOMETRY));
 
-    for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
+  if(isClippingRequired)
+  {
+    if(!currentActor->IsLayer() && !IsActorPickable(ray, projectedNearClippingDistance, projectedFarClippingDistance, *(currentActor)))
     {
-      // Descend tree only if...
-      if(!(*iter)->IsLayer() &&                           // Child is NOT a layer, hit testing current layer only
-         (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
-      {
-        HitActor currentHit(HitTestWithinLayer((*iter->Get()),
-                                               renderTask,
-                                               exclusives,
-                                               rayOrigin,
-                                               rayDir,
-                                               nearClippingPlane,
-                                               farClippingPlane,
-                                               hitCheck,
-                                               overlayedActor,
-                                               overlayHit,
-                                               layerIs3d,
-                                               rayTest,
-                                               point,
-                                               eventTime,
-                                               actorLists,
-                                               propagationType));
-        // Make sure the set hit actor is actually hittable. This is usually required when we have some
-        // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
-        // are hittable or not.
-        if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
-        {
-          continue;
-        }
-
-        UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
-      }
+      return false;
     }
   }
 
-  if(childHit.actor)
+  if(currentActor->GetChildCount() > 0)
   {
-    // If child has been hit & current actor is clipping to bounding box...
-    if(clippingMode == ClippingMode::CLIP_TO_BOUNDING_BOX)
+    ActorContainer&                  children = currentActor->GetChildrenInternal();
+    ActorContainer::reverse_iterator endIter  = children.rend();
+    for(ActorContainer::reverse_iterator iter = children.rbegin(); endIter != iter; ++iter)
     {
-      // ...then make sure the clipping actor has actually been hit unless the child hit actor is on a child overlay.
-      if(hit.actor || IsOnOverlay(childHit.actor, &actor))
+      ActorPtr childActor = *iter;
+      if(!IsActorValid(*childActor, hitCommonInformation.exclusives, hitCheck))
       {
-        // Only then should we return the child hit in this scenario.
-        return childHit;
+        continue;
+      }
+
+      bool isHit = HitTestActorRecursively(hitResultList, childActor, hitCommonInformation, ray, projectedNearClippingDistance, projectedFarClippingDistance, hitCheck, layerBehavior, isKeepingHitTestRequired, isOverlay);
+      if(isKeepingHitTestRequired)
+      {
+        continue;
+      }
+
+      if(isHit)
+      {
+        return true;
       }
-    }
-    else
-    {
-      // no clipping concerns, return child hit.
-      return childHit;
     }
   }
 
-  return hit;
-}
-
-/**
- * Return true if actor is sourceActor or a descendent of sourceActor
- */
-bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
-{
-  if(&sourceActor == &actor)
+  hitResultOfThisActor = HitTestActor(ray, projectedNearClippingDistance, projectedFarClippingDistance, hitCheck, *currentActor, layerBehavior);
+  if(!hitResultOfThisActor)
   {
-    return true;
+    return false;
   }
 
-  Actor* parent = actor.GetParent();
-  if(parent)
+  RenderTaskPtr fboRenderTask = nullptr;
+  if(hitResultOfThisActor->mHitType == HitType::HIT_MAPPING_ACTOR)
   {
-    return IsWithinSourceActors(sourceActor, *parent);
+    auto iter = hitCommonInformation.mappingActors.find(hitResultOfThisActor->mActor->GetId());
+    if(iter != hitCommonInformation.mappingActors.end())
+    {
+      fboRenderTask = iter->second;
+    }
+    else
+    {
+      hitResultOfThisActor->mHitType = HitType::HIT_ACTOR;
+    }
   }
 
-  // Not within source actors
-  return false;
-}
-
-/**
- * Returns true if the actor and all of the actor's parents are hittable.
- */
-bool IsActorActuallyHittable(Actor* actor, HitTestInterface& hitCheck)
-{
-  Actor* currentActor = actor;
-  // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
-  while(currentActor)
+  if(fboRenderTask)
   {
-    if(!hitCheck.DescendActorHierarchy(currentActor))
+    std::vector<std::shared_ptr<HitResult>> frameBufferHitResultList;
+    Vector2                                 hitPosition = hitResultOfThisActor->mHitPosition;
+    Vector2                                 screenSize  = hitResultOfThisActor->mActor->GetCurrentSize().GetVectorXY();
+    if(!HitTestRenderTask(frameBufferHitResultList, *fboRenderTask, hitCommonInformation, screenSize, hitPosition, hitCheck))
     {
       return false;
     }
-    currentActor = currentActor->GetParent();
+
+    // Every FBO hit result should have same distance of this camera.
+    for(auto&& hitResult : frameBufferHitResultList)
+    {
+      hitResult->mDistance = hitResultOfThisActor->mDistance;
+      hitResultList.push_back(hitResult);
+    }
+  }
+  else
+  {
+    hitResultList.push_back(hitResultOfThisActor);
   }
 
   return true;
 }
 
-/**
- * Returns true if the layer and all of the layer's parents are hittable.
- */
-inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
+void RetrieveValidActorTrees(ActorContainer&                            validActorRoots,
+                             ActorPtr                                   parentActor,
+                             const RenderTaskList::ExclusivesContainer& exclusives,
+                             HitTestInterface&                          hitCheck,
+                             const Ray&                                 ray,
+                             const float&                               projectedNearClippingDistance,
+                             const float&                               projectedFarClippingDistance)
 {
-  bool hittable(true);
+  if(parentActor->GetChildCount() == 0)
+  {
+    return;
+  }
 
-  if(layer.IsClipping())
+  if((parentActor->GetClippingMode() == ClippingMode::CLIP_CHILDREN) &&
+     !IsActorPickable(ray, projectedNearClippingDistance, projectedFarClippingDistance, *parentActor))
   {
-    ClippingBox box = layer.GetClippingBox();
+    return;
+  }
 
-    if(screenCoordinates.x < static_cast<float>(box.x) ||
-       screenCoordinates.x > static_cast<float>(box.x + box.width) ||
-       screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
-       screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
+  ActorContainer&          children = parentActor->GetChildrenInternal();
+  ActorContainer::iterator endIter  = children.end();
+  for(ActorContainer::iterator iter = children.begin(); endIter != iter; ++iter)
+  {
+    ActorPtr childActor = *iter;
+    if(childActor->IsLayer())
     {
-      // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
-      hittable = false;
+      continue;
     }
-  }
 
-  if(hittable)
-  {
-    Actor* actor(&layer);
-    hittable = IsActorActuallyHittable(actor, hitCheck);
+    if(childActor->IsOverlay())
+    {
+      bool     valid       = true;
+      ActorPtr currentNode = childActor;
+      while(currentNode)
+      {
+        if(!IsActorValid(*childActor, exclusives, hitCheck))
+        {
+          valid = false;
+          break;
+        }
+        currentNode = currentNode->GetParent();
+      }
+      if(valid)
+      {
+        validActorRoots.push_back(iter->Get());
+      }
+    }
+    else
+    {
+      RetrieveValidActorTrees(validActorRoots, iter->Get(), exclusives, hitCheck, ray, projectedNearClippingDistance, projectedFarClippingDistance);
+    }
   }
-
-  return hittable;
 }
 
-/**
- * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
- */
-void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
+bool HitTestWithinSubTree(std::vector<std::shared_ptr<HitResult>>& hitResultList,
+                          Actor&                                   actor,
+                          HitCommonInformation&                    hitCommonInformation,
+                          const Ray&                               ray,
+                          const float&                             projectedNearClippingDistance,
+                          const float&                             projectedFarClippingDistance,
+                          HitTestInterface&                        hitCheck,
+                          Dali::Layer::Behavior                    layerBehavior)
 {
-  CameraActor* cameraActor = renderTask.GetCameraActor();
-  nearClippingPlane        = cameraActor->GetNearClippingPlane();
-  farClippingPlane         = cameraActor->GetFarClippingPlane();
-}
+  ActorContainer validActorRoots;
+  validActorRoots.push_back(&actor);
 
-void GeoHitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
-                          const Vector2&                             sceneSize,
-                          LayerList&                                 layers,
-                          RenderTask&                                renderTask,
-                          Vector2                                    screenCoordinates,
-                          Results&                                   results,
-                          HitTestInterface&                          hitCheck,
-                          const RayTest&                             rayTest)
-{
-  if(renderTask.IsHittable(screenCoordinates))
+  if(actor.GetScene().HasOverlayContent())
   {
-    Viewport viewport;
-    renderTask.GetHittableViewport(viewport);
+    RetrieveValidActorTrees(validActorRoots, &actor, hitCommonInformation.exclusives, hitCheck, ray, projectedNearClippingDistance, projectedFarClippingDistance);
+  }
 
-    if(screenCoordinates.x < static_cast<float>(viewport.x) ||
-       screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
-       screenCoordinates.y < static_cast<float>(viewport.y) ||
-       screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
+  bool isKeepingHitTestRequired = (hitCheck.GetPropagationType() == Integration::Scene::TouchPropagationType::GEOMETRY) || (layerBehavior == Dali::Layer::Behavior::LAYER_3D);
+
+  ActorContainer::reverse_iterator endIter = validActorRoots.rend();
+  for(ActorContainer::reverse_iterator iter = validActorRoots.rbegin(); endIter != iter; ++iter)
+  {
+    if(HitTestActorRecursively(hitResultList, *iter, hitCommonInformation, ray, projectedNearClippingDistance, projectedFarClippingDistance, hitCheck, layerBehavior, isKeepingHitTestRequired, iter->Get()->IsOverlay()))
     {
-      // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
-      return;
+      break;
     }
+  }
 
-    float nearClippingPlane, farClippingPlane;
-    GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
-
-    // Determine the layer depth of the source actor
-    Actor* sourceActor(renderTask.GetSourceActor());
-    if(sourceActor)
-    {
-      Dali::Layer sourceLayer(sourceActor->GetLayer());
-      if(sourceLayer)
-      {
-        const uint32_t sourceActorDepth(sourceLayer.GetProperty<int32_t>(Dali::Layer::Property::DEPTH));
-        CameraActor*   cameraActor     = renderTask.GetCameraActor();
-        bool           pickingPossible = cameraActor->BuildPickingRay(screenCoordinates,
-                                                            viewport,
-                                                            results.rayOrigin,
-                                                            results.rayDirection);
-        if(!pickingPossible)
-        {
-          return;
-        }
+  if(hitResultList.empty())
+  {
+    return false;
+  }
 
-        // Hit test starting with the top layer, working towards the bottom layer.
-        bool overlayHit = false;
+  if(layerBehavior == Dali::Layer::Behavior::LAYER_3D)
+  {
+    std::stable_sort(hitResultList.begin(), hitResultList.end(), [](std::shared_ptr<HitResult> first, std::shared_ptr<HitResult> second)
+                     {
+                        if(std::abs(first->mDistance - second->mDistance) < Dali::Epsilon<1000>::value)
+                        {
+                          return first->mActor->GetSortingDepth() > second->mActor->GetSortingDepth();
+                        }
+                        else
+                        {
+                          return first->mDistance < second->mDistance;
+                        } });
+  }
 
-        for(uint32_t i = 0; i < layers.GetLayerCount(); ++i)
-        {
-          Layer* layer(layers.GetLayer(i));
-          overlayHit = false;
-          HitActor hit;
-
-          // Ensure layer is touchable (also checks whether ancestors are also touchable)
-          if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
-          {
-            // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
-            if(sourceActorDepth == i)
-            {
-              // Recursively hit test the source actor & children, without crossing into other layers.
-              hit = HitTestWithinLayer(*sourceActor,
-                                       renderTask,
-                                       exclusives,
-                                       results.rayOrigin,
-                                       results.rayDirection,
-                                       nearClippingPlane,
-                                       farClippingPlane,
-                                       hitCheck,
-                                       overlayHit,
-                                       overlayHit,
-                                       layer->GetBehavior() == Dali::Layer::LAYER_3D,
-                                       rayTest,
-                                       results.point,
-                                       results.eventTime,
-                                       results.actorLists,
-                                       Integration::Scene::TouchPropagationType::GEOMETRY);
-            }
-            else if(IsWithinSourceActors(*sourceActor, *layer))
-            {
-              // Recursively hit test all the actors, without crossing into other layers.
-              hit = HitTestWithinLayer(*layer,
-                                       renderTask,
-                                       exclusives,
-                                       results.rayOrigin,
-                                       results.rayDirection,
-                                       nearClippingPlane,
-                                       farClippingPlane,
-                                       hitCheck,
-                                       overlayHit,
-                                       overlayHit,
-                                       layer->GetBehavior() == Dali::Layer::LAYER_3D,
-                                       rayTest,
-                                       results.point,
-                                       results.eventTime,
-                                       results.actorLists,
-                                       Integration::Scene::TouchPropagationType::GEOMETRY);
-            }
-          }
-
-          if(hit.actor)
-          {
-            results.renderTask       = RenderTaskPtr(&renderTask);
-            results.actor            = Dali::Actor(hit.actor);
-            results.actorCoordinates = hit.hitPosition;
-          }
-        }
-      }
-    }
+  if(hitCheck.GetPropagationType() == Integration::Scene::TouchPropagationType::PARENT)
+  {
+    hitResultList.resize(1);
   }
-  return;
+
+  return true;
 }
 
 /**
  * Hit test a RenderTask
  */
-bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
-                       const Vector2&                             sceneSize,
-                       LayerList&                                 layers,
-                       RenderTask&                                renderTask,
-                       Vector2                                    screenCoordinates,
-                       Results&                                   results,
-                       HitTestInterface&                          hitCheck,
-                       const RayTest&                             rayTest)
+bool HitTestRenderTask(std::vector<std::shared_ptr<HitResult>>& hitResultList,
+                       RenderTask&                              renderTask,
+                       HitCommonInformation&                    hitCommonInformation,
+                       const Vector2&                           screenSize,
+                       Vector2                                  screenCoordinates,
+                       HitTestInterface&                        hitCheck)
 {
-  if(renderTask.IsHittable(screenCoordinates))
+  curRenderTask = &renderTask;
+  if(!renderTask.IsInputAvailable())
   {
-    Viewport viewport;
-    renderTask.GetHittableViewport(viewport);
+    return false;
+  }
 
-    if(screenCoordinates.x < static_cast<float>(viewport.x) ||
-       screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
-       screenCoordinates.y < static_cast<float>(viewport.y) ||
-       screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
-    {
-      // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
-      return false;
-    }
+  Actor* sourceActor(renderTask.GetSourceActor());
+  if(!sourceActor)
+  {
+    return false;
+  }
 
-    float nearClippingPlane, farClippingPlane;
-    GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
+  Dali::Layer sourceLayer(sourceActor->GetLayer());
+  if(!sourceLayer)
+  {
+    return false;
+  }
+
+  Ray ray;
+  if(!renderTask.GetCameraActor()->BuildPickingRay(screenCoordinates, screenSize, ray.origin, ray.direction))
+  {
+    return false;
+  }
+
+  float nearClippingPlane, farClippingPlane;
+  GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
 
-    // Determine the layer depth of the source actor
-    Actor* sourceActor(renderTask.GetSourceActor());
+  // Recompute near and far clipping plane distance with the picking ray.
+  Vector3 centerDirection = renderTask.GetCameraActor()->GetCurrentWorldOrientation().Rotate(Dali::Vector3::ZAXIS);
+  centerDirection.Normalize();
+  float projectionFactor              = centerDirection.Dot(Vector3(ray.direction));
+  float projectedNearClippingDistance = nearClippingPlane / projectionFactor;
+  float projectedFarClippingDistance  = farClippingPlane / projectionFactor;
 
-    // Check the source actor is actually hittable or not.
-    if(sourceActor && IsActorActuallyHittable(sourceActor, hitCheck))
+  Vector2 hitPosition;
+  for(int32_t i = hitCommonInformation.layers.GetLayerCount() - 1; i >= 0; --i)
+  {
+    Layer* layer(hitCommonInformation.layers.GetLayer(i));
+    Actor* testRootActor = (sourceLayer == layer) ? sourceActor : ((IsWithinSourceActors(*sourceActor, *layer)) ? layer : nullptr);
+    if(!testRootActor)
     {
-      Dali::Layer sourceLayer(sourceActor->GetLayer());
-      if(sourceLayer)
-      {
-        const uint32_t sourceActorDepth(sourceLayer.GetProperty<int32_t>(Dali::Layer::Property::DEPTH));
-
-        CameraActor* cameraActor     = renderTask.GetCameraActor();
-        bool         pickingPossible = cameraActor->BuildPickingRay(
-          screenCoordinates,
-          viewport,
-          results.rayOrigin,
-          results.rayDirection);
-        if(!pickingPossible)
-        {
-          return false;
-        }
+      continue;
+    }
 
-        // Hit test starting with the top layer, working towards the bottom layer.
-        HitActor hit;
-        bool     overlayHit       = false;
-        bool     layerConsumesHit = false;
-
-        // Be used when we decide to consume layer.
-        // We should not consume hit if sourceLayer is above on consumable layer. Otherwise, we should consume. So just initialize it as 0.
-        // sourceLayerIndex can be a relative value to calculate the relationship with the layer.
-        // If the layer is consumed first, sourceLayerIndex is not the actual index, but it must be guaranteed to have an index smaller than the layer.
-        // If there is a sourceLayer above the consumable layer, the sourceLayerIndex is determined and the index of the consumable layer is also determined.
-        // Then we can calculate the relationship between the two layers.
-        bool    IsHitTestWithinLayer = false;
-        int32_t sourceLayerIndex     = 0;
-        int32_t consumedLayerIndex   = -1;
-
-        for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
-        {
-          Layer* layer(layers.GetLayer(i));
-          overlayHit           = false;
-          IsHitTestWithinLayer = false;
-
-          if(sourceLayer == layer)
-          {
-            sourceLayerIndex = i;
-          }
-
-          // Ensure layer is touchable (also checks whether ancestors are also touchable)
-          if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
-          {
-            // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
-            if(sourceActorDepth == static_cast<uint32_t>(i))
-            {
-              IsHitTestWithinLayer = true;
-              // Recursively hit test the source actor & children, without crossing into other layers.
-              hit = HitTestWithinLayer(*sourceActor,
-                                       renderTask,
-                                       exclusives,
-                                       results.rayOrigin,
-                                       results.rayDirection,
-                                       nearClippingPlane,
-                                       farClippingPlane,
-                                       hitCheck,
-                                       overlayHit,
-                                       overlayHit,
-                                       layer->GetBehavior() == Dali::Layer::LAYER_3D,
-                                       rayTest,
-                                       results.point,
-                                       results.eventTime,
-                                       results.actorLists,
-                                       Integration::Scene::TouchPropagationType::PARENT);
-            }
-            else if(IsWithinSourceActors(*sourceActor, *layer))
-            {
-              IsHitTestWithinLayer = true;
-              // Recursively hit test all the actors, without crossing into other layers.
-              hit = HitTestWithinLayer(*layer,
-                                       renderTask,
-                                       exclusives,
-                                       results.rayOrigin,
-                                       results.rayDirection,
-                                       nearClippingPlane,
-                                       farClippingPlane,
-                                       hitCheck,
-                                       overlayHit,
-                                       overlayHit,
-                                       layer->GetBehavior() == Dali::Layer::LAYER_3D,
-                                       rayTest,
-                                       results.point,
-                                       results.eventTime,
-                                       results.actorLists,
-                                       Integration::Scene::TouchPropagationType::PARENT);
-            }
-
-            // If this layer is set to consume the hit, then do not check any layers behind it
-            if(IsHitTestWithinLayer && hitCheck.DoesLayerConsumeHit(layer))
-            {
-              consumedLayerIndex = i;
-              layerConsumesHit   = true;
-              break;
-            }
-          }
-        }
+    // If source Actor is been exclusive by other RenderTask, skip it.
+    if(IsActorExclusive(*testRootActor, hitCommonInformation.exclusives) && (!renderTask.IsExclusive() || testRootActor != sourceActor))
+    {
+      continue;
+    }
 
-        if(hit.actor)
-        {
-          results.renderTask       = RenderTaskPtr(&renderTask);
-          results.actor            = Dali::Actor(hit.actor);
-          results.actorCoordinates = hit.hitPosition;
+    if(!IsActorActuallyHittable(testRootActor, hitCheck))
+    {
+      continue;
+    }
 
-          return true; // Success
-        }
+    std::vector<std::shared_ptr<HitResult>> subTreeHitResultList;
+    bool                                    isHit = HitTestWithinSubTree(subTreeHitResultList,
+                                      *testRootActor,
+                                      hitCommonInformation,
+                                      ray,
+                                      projectedNearClippingDistance,
+                                      projectedFarClippingDistance,
+                                      hitCheck,
+                                      layer->GetBehavior());
+
+    if(!isHit && hitCheck.DoesLayerConsumeHit(layer))
+    {
+      hitResultList.push_back(std::make_shared<HitResult>(layer, Vector2(0.0f, 0.0f), 0.0f, HitType::HIT_ACTOR, ray));
+      break;
+    }
 
-        if(layerConsumesHit)
-        {
-          // Consumes if the hitted layer is above the SourceActor's layer.
-          bool ret = sourceLayerIndex <= consumedLayerIndex;
-          if(ret)
-          {
-            DALI_LOG_RELEASE_INFO("layer is set to consume the hit\n");
-            results.renderTask = RenderTaskPtr(&renderTask);
-            results.actor      = Dali::Layer(layers.GetLayer(consumedLayerIndex));
-          }
-          return ret;
-        }
-      }
+    hitResultList.insert(hitResultList.end(), subTreeHitResultList.begin(), subTreeHitResultList.end());
+
+    if(isHit && hitCheck.GetPropagationType() == Integration::Scene::TouchPropagationType::PARENT)
+    {
+      break;
     }
   }
-  return false;
+
+  if(hitResultList.empty())
+  {
+    return false;
+  }
+
+  return true;
 }
 
-/**
- * Selects Prior Actor that is rendered later between firstActor and secondActor in the layer of rootActor.
- * if only one of Actor is included in the layer, returns the Actor.
- * if both of the firstActor and secondActor are not included in the layer, returns empty Actor.
- */
-Dali::Actor FindPriorActorInLayer(Dali::Actor rootActor, Dali::Actor firstActor, Dali::Actor secondActor)
+bool ConvertScreenCoordinates(Vector2& convertedScreenCoordinates, const Vector2& screenCoordinates, const RenderTask& targetRenderTask)
 {
-  Dali::Actor priorActor;
-  Dali::Layer layer               = rootActor.GetLayer();
-  bool        firstActorIncluded  = firstActor.GetLayer() == layer;
-  bool        secondActorIncluded = secondActor.GetLayer() == layer;
+  Viewport viewport;
+  targetRenderTask.GetViewport(viewport);
 
-  if(firstActorIncluded && !secondActorIncluded)
-  {
-    priorActor = firstActor;
-  }
-  else if(!firstActorIncluded && secondActorIncluded)
-  {
-    priorActor = secondActor;
-  }
-  else if(firstActorIncluded && secondActorIncluded)
+  if(screenCoordinates.x < static_cast<float>(viewport.x) ||
+     screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
+     screenCoordinates.y < static_cast<float>(viewport.y) ||
+     screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
   {
-    priorActor = (GetImplementation(firstActor).GetSortingDepth() < GetImplementation(secondActor).GetSortingDepth()) ? secondActor : firstActor;
+    // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
+    return false;
   }
+  convertedScreenCoordinates = screenCoordinates - Vector2(static_cast<float>(viewport.x), static_cast<float>(viewport.y));
 
-  return priorActor;
+  return true;
 }
 
-/**
- * Selects Prior Actor that is rendered later between firstActor and secondActor from child scene tree of rootActor.
- */
-Dali::Actor FindPriorActorInLayers(const LayerList& layers, Dali::Actor rootActor, Dali::Actor firstActor, Dali::Actor secondActor)
+void CollectMappingActors(RenderTaskList::RenderTaskContainer& tasks, std::unordered_map<uint32_t, RenderTaskPtr>& mappingActors)
 {
-  Dali::Layer    sourceLayer = rootActor.GetLayer();
-  const uint32_t sourceActorDepth(sourceLayer.GetProperty<int>(Dali::Layer::Property::DEPTH));
-
-  Dali::Actor priorActor;
-  uint32_t    layerCount = layers.GetLayerCount();
-  if(layerCount > 0)
+  RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
+  for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
   {
-    for(int32_t i = layerCount - 1; i >= 0; --i)
+    RenderTask& renderTask = *iter->Get();
+    if(renderTask.GetFrameBuffer() && renderTask.GetScreenToFrameBufferMappingActor())
     {
-      Layer* layer(layers.GetLayer(i));
-      if(sourceActorDepth == static_cast<uint32_t>(i))
-      {
-        priorActor = FindPriorActorInLayer(rootActor, firstActor, secondActor);
-      }
-      else if(IsWithinSourceActors(GetImplementation(rootActor), *layer))
-      {
-        Dali::Actor layerRoot = Dali::Actor(layer);
-        priorActor            = FindPriorActorInLayer(layerRoot, firstActor, secondActor);
-      }
-
-      if(priorActor)
-      {
-        break;
-      }
+      uint32_t mappingActorId       = renderTask.GetScreenToFrameBufferMappingActor().GetProperty<int32_t>(Dali::Actor::Property::ID);
+      mappingActors[mappingActorId] = RenderTaskPtr(&renderTask);
     }
   }
-  return priorActor;
 }
 
 /**
@@ -873,135 +683,64 @@ Dali::Actor FindPriorActorInLayers(const LayerList& layers, Dali::Actor rootActo
  * @return True if we have a hit, false otherwise
  */
 bool HitTestRenderTaskList(const Vector2&                                 sceneSize,
-                           LayerList&                                     layers,
                            RenderTaskList&                                taskList,
+                           LayerList&                                     layers,
                            const Vector2&                                 screenCoordinates,
-                           Results&                                       results,
                            HitTestInterface&                              hitCheck,
-                           const Integration::Scene::TouchPropagationType propagationType)
+                           const Integration::Scene::TouchPropagationType propagationType,
+                           Results&                                       results)
 {
-  if(propagationType == Integration::Scene::TouchPropagationType::GEOMETRY)
-  {
-    RenderTaskList::RenderTaskContainer&          tasks      = taskList.GetTasks();
-    RenderTaskList::RenderTaskContainer::iterator endIter    = tasks.end();
-    const auto&                                   exclusives = taskList.GetExclusivesList();
-    RayTest                                       rayTest;
+  bool                                 isHitSucceeded = false;
+  RenderTaskList::RenderTaskContainer& tasks          = taskList.GetTasks();
+  const auto&                          exclusives     = taskList.GetExclusivesList();
 
-    // Hit test order should be of draw order
-    for(RenderTaskList::RenderTaskContainer::iterator iter = tasks.begin(); endIter != iter; ++iter)
-    {
-      RenderTask& renderTask = *iter->Get();
-      GeoHitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest);
-    }
+  HitCommonInformation hitCommonInformation{exclusives, std::unordered_map<uint32_t, RenderTaskPtr>(), layers};
+  CollectMappingActors(tasks, hitCommonInformation.mappingActors);
 
-    return !results.actorLists.empty();
-  }
-  else
+  RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
+  for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
   {
-    RenderTaskList::RenderTaskContainer&                  tasks      = taskList.GetTasks();
-    RenderTaskList::RenderTaskContainer::reverse_iterator endIter    = tasks.rend();
-    const auto&                                           exclusives = taskList.GetExclusivesList();
-    RayTest                                               rayTest;
-
-    Results                                      storedResults = results;
-    std::vector<std::pair<Dali::Actor, Results>> offScreenHitResults;
-    // Hit test order should be reverse of draw order
-    for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
+    RenderTask& renderTask = *iter->Get();
+    if(renderTask.GetFrameBuffer() && renderTask.GetScreenToFrameBufferMappingActor())
     {
-      RenderTask& renderTask = *iter->Get();
-      if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
-      {
-        if(renderTask.GetFrameBuffer())
-        {
-          Results result = results;
-          offScreenHitResults.push_back(std::make_pair(renderTask.GetScreenToFrameBufferMappingActor(), std::move(result)));
-          continue;
-        }
+      continue;
+    }
 
-        if(offScreenHitResults.empty())
-        {
-          return true;
-        }
+    // Don't need to consider a RenderTask draws on window and its source actor is inside FBO.
+    // Can't support it.
+    Viewport                                viewport;
+    std::vector<std::shared_ptr<HitResult>> hitResults;
+    Vector2                                 convertedScreenCoordinates;
+    renderTask.GetViewport(viewport);
+    if(ConvertScreenCoordinates(convertedScreenCoordinates, screenCoordinates, renderTask) &&
+       HitTestRenderTask(hitResults, renderTask, hitCommonInformation, Vector2(viewport.width, viewport.height), convertedScreenCoordinates, hitCheck))
+    {
+      results.actor            = Dali::Actor(hitResults.front()->mActor);
+      results.renderTask       = RenderTaskPtr(&renderTask);
+      results.actorCoordinates = hitResults.front()->mHitPosition;
+      results.rayOrigin        = hitResults.front()->mRay.origin;
+      results.rayDirection     = hitResults.front()->mRay.direction;
 
-        Actor* sourceActor(renderTask.GetSourceActor());
-        for(auto&& pair : offScreenHitResults)
+      if(hitCheck.GetPropagationType() == Integration::Scene::TouchPropagationType::GEOMETRY)
+      {
+        std::vector<std::shared_ptr<HitResult>>::reverse_iterator endIter = hitResults.rend();
+        for(std::vector<std::shared_ptr<HitResult>>::reverse_iterator iter = hitResults.rbegin(); endIter != iter; ++iter)
         {
-          Dali::Actor mappingActor = pair.first;
-          if(!mappingActor || !IsWithinSourceActors(*sourceActor, GetImplementation(mappingActor)))
-          {
-            continue;
-          }
-
-          bool mappingActorInsideHitConsumingLayer = false;
-          if(GetImplementation(results.actor).IsLayer())
-          {
-            Dali::Layer resultLayer = Dali::Layer::DownCast(results.actor);
-            // Check the resultLayer is consuming hit even though the layer is not hittable.
-            // And check the resultLayer is the layer of mappingActor too.
-            if(hitCheck.DoesLayerConsumeHit(&GetImplementation(resultLayer)) && !hitCheck.IsActorHittable(&GetImplementation(results.actor)) && results.actor == mappingActor.GetLayer())
-            {
-              mappingActorInsideHitConsumingLayer = true;
-            }
-          }
-          if(mappingActorInsideHitConsumingLayer || mappingActor == FindPriorActorInLayers(layers, Dali::Actor(sourceActor), mappingActor, results.actor))
-          {
-            results = pair.second;
-            break;
-          }
+          results.actorLists.push_back(iter->get()->mActor);
         }
-        // Return true when an actor is hit (or layer in our render-task consumes the hit)
-        return true;
       }
+      isHitSucceeded = true;
+      break;
     }
-
-    // When no OnScreen Actor is hitted but there are hit results from OffScreen RenderTasks
-    // those use ScreenToFrameBufferFunction, simply returns first hitted result.
-    if(!offScreenHitResults.empty())
-    {
-      results = offScreenHitResults.front().second;
-      return true;
-    }
-
-    results = storedResults;
-  }
-  return false;
-}
-
-/**
- * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
- *
- * @param[in] sceneSize The scene size the tests will be performed in
- * @param[in] layers The list of layers to test
- * @param[in] taskList The list of render tasks
- * @param[out] results Ray information calculated by the camera
- * @param[in] hitCheck The hit testing interface object to use
- * @param[in] propagationType Whether the scene using geometry event propagation touch and hover events.
- * @return True if we have a hit, false otherwise
- */
-bool HitTestForEachRenderTask(const Vector2&                                 sceneSize,
-                              LayerList&                                     layers,
-                              RenderTaskList&                                taskList,
-                              const Vector2&                                 screenCoordinates,
-                              Results&                                       results,
-                              HitTestInterface&                              hitCheck,
-                              const Integration::Scene::TouchPropagationType propagationType)
-{
-  bool result = false;
-
-  if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, propagationType))
-  {
-    // Found hit.
-    result = true;
   }
-
-  return result;
+  return isHitSucceeded;
 }
 
 } // unnamed namespace
 
 HitTestInterface::~HitTestInterface() = default;
 
-bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func, const Integration::Scene::TouchPropagationType propagationType)
+bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func, const Integration::Scene::TouchPropagationType propagationType)
 {
   bool wasHit(false);
   // Hit-test the regular on-scene actors
@@ -1009,7 +748,11 @@ bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& laye
   hitTestResults.eventTime = 0u; ///< Unused
 
   HitTestFunctionWrapper hitTestFunctionWrapper(func);
-  if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper, propagationType))
+  // TODO : It looks better to do this before hit test is required.
+  hitTestFunctionWrapper.SetPoint(hitTestResults.point);
+  hitTestFunctionWrapper.SetTimeStamp(hitTestResults.eventTime);
+  hitTestFunctionWrapper.SetPropagationType(propagationType);
+  if(HitTestRenderTaskList(sceneSize, renderTaskList, layerList, screenCoordinates, hitTestFunctionWrapper, propagationType, hitTestResults))
   {
     results.actor            = hitTestResults.actor;
     results.actorCoordinates = hitTestResults.actorCoordinates;
@@ -1020,14 +763,13 @@ bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& laye
 
 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface, const Integration::Scene::TouchPropagationType propagationType)
 {
-  bool wasHit(false);
+  // TODO : It looks better to do this before hit test is required.
+  hitTestInterface.SetPoint(results.point);
+  hitTestInterface.SetTimeStamp(results.eventTime);
+  hitTestInterface.SetPropagationType(propagationType);
 
   // Hit-test the regular on-scene actors
-  if(!wasHit)
-  {
-    wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface, propagationType);
-  }
-  return wasHit;
+  return HitTestRenderTaskList(sceneSize, renderTaskList, layerList, screenCoordinates, hitTestInterface, propagationType, results);
 }
 
 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor, const Integration::Scene::TouchPropagationType propagationType)
index ccb023f241632b14be6b5b48144a0054718f0443..a9ec6b471c350682d99ec582cd9c5ceabfa8a1d6 100644 (file)
@@ -92,20 +92,53 @@ struct HitTestInterface
    *       If false is returend, then this actor passes the hit-test and the next actor performs the hit-test.
    *
    * @param[in] actor The hit actor.
-   * @param[in] point The point of event touched.
    * @param[in] hitPointLocal The hit point in the Actor's local reference system.
-   * @param[in] timeStamp The time the event occurred.
    * @param[in] propagationType If Integration::Scene::TouchPropagationType::GEOMETRY, hittest works in a geometry way.
    *
    * @return true if the actor should be the hit, false otherwise.
    */
-  virtual bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) = 0;
+  virtual bool ActorRequiresHitResultCheck(Actor* actor, Vector2 hitPointLocal) = 0;
+
+  void SetPoint(const Integration::Point& point)
+  {
+    mPoint = point;
+  }
+
+  const Integration::Point& GetPoint() const
+  {
+    return mPoint;
+  }
+
+  void SetTimeStamp(uint32_t timeStamp)
+  {
+    mTimeStamp = timeStamp;
+  }
+
+  uint32_t GetTimeStamp() const
+  {
+    return mTimeStamp;
+  }
+
+  void SetPropagationType(const Integration::Scene::TouchPropagationType propagationtType)
+  {
+    mPropagationType = propagationtType;
+  }
+
+  Integration::Scene::TouchPropagationType GetPropagationType() const
+  {
+    return mPropagationType;
+  }
 
 protected:
   /**
    * Virtual destructor, no deletion through this interface
    */
   virtual ~HitTestInterface();
+
+private:
+  Integration::Point                       mPoint;
+  uint32_t                                 mTimeStamp{0u};
+  Integration::Scene::TouchPropagationType mPropagationType{Integration::Scene::TouchPropagationType::PARENT};
 };
 
 /**
index f3d89bb02f56e9f9f215bf5be87ae7985113179e..6f6cfacd6066f2954ec6b8a563a43bd48db22055 100644 (file)
@@ -251,7 +251,7 @@ struct ActorHoverableCheck : public HitTestAlgorithm::HitTestInterface
     return layer->IsHoverConsumed();
   }
 
-  bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
+  bool ActorRequiresHitResultCheck(Actor* actor, Vector2 hitPointLocal) override
   {
     // Hover event is always hit.
     return true;
index a78cc614534d903b0d9e6ab581d0a56a99c49704..71ac62ef1499649805a356b9dd66d59fafa4a9e1 100644 (file)
@@ -54,11 +54,8 @@ namespace Dali
 {
 namespace Internal
 {
-RayTest::RayTest()
-{
-}
 
-bool RayTest::SphereTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir) const
+bool RayTest::SphereTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir)
 {
   /*
    http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection
@@ -152,7 +149,7 @@ bool RayTest::SphereTest(const Internal::Actor& actor, const Vector4& rayOrigin,
   return (b2 * b2 - a * c) >= 0.0f;
 }
 
-bool RayTest::ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance) const
+bool RayTest::ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance)
 {
   bool hit = false;
 
@@ -194,6 +191,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 911ba159511d2320f2952da982f69983a8126c52..840ddfa686df2afc0cd6bdddf0d65bdbfd32685e 100644 (file)
@@ -37,15 +37,8 @@ class EventThreadServices;
  * Stores a reference to the EventThreadServices so limit the number of times this is created
  * to avoid repeated calls to EventThreadServices::Get().
  */
-class RayTest
+namespace RayTest
 {
-public:
-  /// Constructor
-  RayTest();
-
-  /// Default Destructor
-  ~RayTest() = default;
-
   /**
    * Performs a ray-sphere test with the given pick-ray and the given actor's bounding sphere.
    *
@@ -56,7 +49,7 @@ public:
    *
    * @note The actor coordinates are relative to the top-left (0.0, 0.0, 0.5)
    */
-  bool SphereTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir) const;
+  bool SphereTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir);
 
   /**
    * Performs a ray-actor test with the given pick-ray and the given actor's geometry.
@@ -70,7 +63,21 @@ public:
    *
    * @note The actor coordinates are relative to the top-left (0.0, 0.0, 0.5)
    */
-  bool ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance) const;
+  bool ActorTest(const Internal::Actor& actor, const Vector4& rayOrigin, const Vector4& rayDir, Vector2& hitPointLocal, float& distance);
+
+  /**
+   * 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);
 };
 
 } // namespace Internal
index c894cf75e13436df5d75bca35d5828cf109ee21a..4eba296709b50aec19efa608480323db2f9347b6 100644 (file)
@@ -224,7 +224,30 @@ RenderTask::ScreenToFrameBufferFunction RenderTask::GetScreenToFrameBufferFuncti
 
 void RenderTask::SetScreenToFrameBufferMappingActor(Dali::Actor& mappingActor)
 {
+  if(mInputMappingActor == mappingActor)
+  {
+    return;
+  }
+
+  if(!mappingActor)
+  {
+    Dali::Actor actor = mInputMappingActor.GetHandle();
+    if(actor)
+    {
+      GetImplementation(actor).SetIsRenderTaskMappingActor(false);
+    }
+    mInputMappingActor.Reset();
+    return;
+  }
+
+  if(GetImplementation(mappingActor).IsRenderTaskMappingActor())
+  {
+    DALI_LOG_ERROR("The actor is already a mapping actor of another RenderTask\n");
+    return;
+  }
+
   mInputMappingActor = WeakHandle<Dali::Actor>(mappingActor);
+  GetImplementation(mappingActor).SetIsRenderTaskMappingActor(true);
 }
 
 Dali::Actor RenderTask::GetScreenToFrameBufferMappingActor() const
@@ -472,11 +495,8 @@ uint32_t RenderTask::GetRefreshRate() const
   return mRefreshRate;
 }
 
-bool RenderTask::IsHittable(Vector2& screenCoords) const
+bool RenderTask::IsInputAvailable() const
 {
-  // True when input is enabled, source & camera actor are valid
-  bool inputEnabled(false);
-
   Actor*       sourceActor = GetSourceActor();
   CameraActor* cameraActor = GetCameraActor();
 
@@ -485,17 +505,26 @@ bool RenderTask::IsHittable(Vector2& screenCoords) const
      sourceActor->OnScene() &&
      nullptr != cameraActor &&
      cameraActor->OnScene())
+  {
+    return true;
+  }
+  return false;
+}
+
+bool RenderTask::IsHittable(Vector2& screenCoords) const
+{
+  if(IsInputAvailable())
   {
     // If the actors are rendered off-screen, then the screen coordinates must be converted
     // and the conversion function will tell us if they are inside or outside
     if(TranslateCoordinates(screenCoords))
     {
       // This is a suitable render-task for input handling
-      inputEnabled = true;
+      return true;
     }
   }
 
-  return inputEnabled;
+  return false;
 }
 
 bool RenderTask::TranslateCoordinates(Vector2& screenCoords) const
@@ -1053,6 +1082,13 @@ RenderTask::~RenderTask()
   // scene object deletion is handled by our parent
   // scene object handles observation of source and camera
 
+  Dali::Actor actor = mInputMappingActor.GetHandle();
+  if(actor)
+  {
+    GetImplementation(actor).SetIsRenderTaskMappingActor(false);
+  }
+  mInputMappingActor.Reset();
+
   ClearRenderResult();
 }
 
index d9855412f7831d5aef764d5f50e7a0586ba36446..6f3ed9b0be25a185bd85215cd2424ab7201a9ee0 100644 (file)
@@ -234,6 +234,12 @@ public:
    */
   uint32_t GetRefreshRate() const;
 
+  /**
+   * Retrieves whether the RenderTask is input available or not
+   * @return True if the RenderTask is input available.
+   */
+  bool IsInputAvailable() const;
+
   /**
    * Check if the render-task is hittable. If render task is offscreen, the screen coordinates may be translated.
    * @param[in,out] screenCoords The screen coordinate, which may be converted (for hit-testing actors which are rendered off-screen).
index f9a0a08acecf9eefa0300cfe410836b1acf1d4cf..54902f9f4dac6f713047ad699550031525538e82 100644 (file)
@@ -349,6 +349,9 @@ public:
    * @SINCE_1_0.0
    * @param[in] mappingActor The actor used for conversion
    * @note The mapping actor needs to be rendered by the default render task to make the mapping work properly.
+   * @note An Actor can be MappingActor for only one RenderTask.
+   * @note If input mappingActor is null, then this RenderTask become to do not use mapping Actor.
+   * @note The mapping actor cannot be used as a Source Actor of any RenderTask.
    */
   void SetScreenToFrameBufferMappingActor(Actor mappingActor);