[dali_2.3.30] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
1 /*
2  * Copyright (c) 2024 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/internal/event/actors/actor-impl.h>
24 #include <dali/internal/event/actors/camera-actor-impl.h>
25 #include <dali/internal/event/actors/layer-impl.h>
26 #include <dali/internal/event/actors/layer-list.h>
27 #include <dali/internal/event/common/projection.h>
28 #include <dali/internal/event/events/ray-test.h>
29 #include <dali/internal/event/render-tasks/render-task-impl.h>
30 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
31 #include <dali/internal/event/rendering/renderer-impl.h>
32 #include <dali/public-api/actors/layer.h>
33 #include <dali/public-api/math/vector2.h>
34 #include <dali/public-api/math/vector4.h>
35
36 namespace Dali::Internal::HitTestAlgorithm
37 {
38 namespace
39 {
40 struct HitActor
41 {
42   HitActor()
43   : actor(nullptr),
44     distance(std::numeric_limits<float>::max()),
45     depth(std::numeric_limits<int>::min())
46   {
47   }
48
49   Actor*  actor;       ///< The actor hit (if actor is hit, then this is initialised).
50   Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
51   float   distance;    ///< Distance from ray origin to hit actor.
52   int32_t depth;       ///< Depth index of this actor.
53 };
54
55 /**
56  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
57  */
58 struct HitTestFunctionWrapper : public HitTestInterface
59 {
60   /**
61    * Constructor
62    *
63    * @param[in] func HitTestFunction to call with an Actor handle.
64    */
65   HitTestFunctionWrapper(Dali::HitTestAlgorithm::HitTestFunction func)
66   : mFunc(func)
67   {
68   }
69
70   bool IsActorHittable(Actor* actor) override
71   {
72     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::CHECK_ACTOR);
73   }
74
75   bool DescendActorHierarchy(Actor* actor) override
76   {
77     return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE);
78   }
79
80   bool DoesLayerConsumeHit(Layer* layer) override
81   {
82     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
83     // where the caller may want to check for something completely different.
84     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
85     return false;
86   }
87
88   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
89   {
90     // Geometry way does not require Hittest from the client.
91     if(propagationType == Integration::Scene::TouchPropagationType::PARENT)
92     {
93       return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
94     }
95     return true;
96   }
97
98   Dali::HitTestAlgorithm::HitTestFunction mFunc;
99 };
100
101 /**
102  * Used in the hit-test algorithm to check whether the actor is touchable.
103  * It is used by the touch event processor.
104  */
105 struct ActorTouchableCheck : public HitTestInterface
106 {
107   bool IsActorHittable(Actor* actor) override
108   {
109     return (actor->GetTouchRequired() || actor->GetInterceptTouchRequired() || actor->IsTouchFocusable()) && // Does the Application or derived actor type require a touch event or a intercept touch event? or focusable by touch?
110            actor->IsHittable();                                                                              // Is actor sensitive, visible and on the scene?
111   }
112
113   bool DescendActorHierarchy(Actor* actor) override
114   {
115     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
116            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
117   }
118
119   bool DoesLayerConsumeHit(Layer* layer) override
120   {
121     return layer->IsTouchConsumed();
122   }
123
124   bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp, const Integration::Scene::TouchPropagationType propagationType) override
125   {
126     // The Geometry way behaves like AllowedOnlyOwnTouch is enabled.
127     if(point.GetState() != PointState::STARTED && (propagationType == Integration::Scene::TouchPropagationType::GEOMETRY || actor->IsAllowedOnlyOwnTouch()) && ownActor != actor)
128     {
129       return false;
130     }
131     // Geometry way does not require Hittest from the client.
132     if(propagationType == Integration::Scene::TouchPropagationType::PARENT)
133     {
134       return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
135     }
136     return true;
137   }
138
139   void SetOwnActor(const Actor* actor)
140   {
141     ownActor = actor;
142   }
143   const Actor* ownActor;
144 };
145
146 /**
147  * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
148  */
149 bool IsActorExclusiveToAnotherRenderTask(const Actor&                               actor,
150                                          const RenderTask&                          renderTask,
151                                          const RenderTaskList::ExclusivesContainer& exclusives)
152
153 {
154   bool exclusiveByOtherTask = false;
155   if(exclusives.size())
156   {
157     for(const auto& exclusive : exclusives)
158     {
159       if(exclusive.actor.GetActor() == &actor)
160       {
161         if(exclusive.renderTaskPtr != &renderTask)
162         {
163           exclusiveByOtherTask = true;
164         }
165         else
166         {
167           // Fast-out if render task is itself
168           return false;
169         }
170       }
171     }
172   }
173   return exclusiveByOtherTask;
174 }
175
176 /**
177  * Checks if actor or anyone of it's parents are an overlay, until either the currentActor is reached or the root actor
178  * @param actor The child-actor and it's parents to check
179  * @param currentActor The top actor of this current branch which we should not go above
180  * @return True if the actor or a parent is an overlay, false otherwise
181  */
182 inline bool IsOnOverlay(Actor* actor, Actor* currentActor)
183 {
184   while(actor && actor != currentActor)
185   {
186     if(actor->IsOverlay())
187     {
188       return true;
189     }
190     actor = actor->GetParent();
191   }
192   return false;
193 }
194
195 /**
196  * Hit tests the given actor and updates the in/out variables appropriately
197  */
198 bool HitTestActor(const RenderTask&                              renderTask,
199                   const Vector4&                                 rayOrigin,
200                   const Vector4&                                 rayDir,
201                   const float&                                   nearClippingPlane,
202                   const float&                                   farClippingPlane,
203                   HitTestInterface&                              hitCheck,
204                   const RayTest&                                 rayTest,
205                   const Integration::Point&                      point,
206                   const uint32_t                                 eventTime,
207                   bool                                           clippingActor,
208                   bool                                           overlayedActor,
209                   Actor&                                         actor,
210                   bool&                                          overlayHit,
211                   HitActor&                                      hit,
212                   const Integration::Scene::TouchPropagationType propagationType)
213 {
214   bool isClippingOrHittable = clippingActor || hitCheck.IsActorHittable(&actor);
215   bool isGeometry = propagationType == Integration::Scene::TouchPropagationType::GEOMETRY;
216   if(isClippingOrHittable || isGeometry)
217   {
218     Vector3 size(actor.GetCurrentSize());
219
220     // Ensure the actor has a valid size.
221     // If so, perform a quick ray sphere test to see if our ray is close to the actor.
222     if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
223     {
224       Vector2 hitPointLocal;
225       float   distance;
226
227       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
228       if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
229       {
230         // Calculate z coordinate value in Camera Space.
231         const Matrix&  viewMatrix          = renderTask.GetCameraActor()->GetViewMatrix();
232         const Vector4& hitDir              = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
233         const float    cameraDepthDistance = (viewMatrix * hitDir).z;
234
235         // Check if cameraDepthDistance is between clipping plane
236         if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
237         {
238           if(isGeometry && actor.GetParent())
239           {
240             // If the child touches outside the parent's size boundary, it should not be hit.
241             if(!overlayedActor && !clippingActor && !actor.GetParent()->IsLayer())
242             {
243               Vector2 hitPointLocal;
244               float   distance;
245               if(!(rayTest.SphereTest(*actor.GetParent(), rayOrigin, rayDir) &&
246                     rayTest.ActorTest(*actor.GetParent(), rayOrigin, rayDir, hitPointLocal, distance)))
247               {
248                 return false;
249               }
250             }
251           }
252
253           if(overlayHit && !overlayedActor)
254           {
255             // If we have already hit an overlay and current actor is not an overlay ignore current actor.
256           }
257           else
258           {
259             if(overlayedActor)
260             {
261               overlayHit = true;
262             }
263
264             // If the hit actor does not want to hit, the hit-test continues.
265             if(isClippingOrHittable && hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime, propagationType))
266             {
267               hit.actor       = &actor;
268               hit.hitPosition = hitPointLocal;
269               hit.distance    = distance;
270               hit.depth       = actor.GetSortingDepth();
271
272               if(actor.GetRendererCount() > 0)
273               {
274                 // Get renderer with maximum depth
275                 int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
276                 for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
277                 {
278                   int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
279                   if(depth > rendererMaxDepth)
280                   {
281                     rendererMaxDepth = depth;
282                   }
283                 }
284                 hit.depth += rendererMaxDepth;
285               }
286             }
287           }
288         }
289       }
290     }
291   }
292   return true;
293 }
294
295 /**
296  * When iterating through the children of an actor, this method updates the child-hit-data.
297  */
298 void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
299 {
300   bool updateChildHit = false;
301   if(currentHit.distance >= 0.0f)
302   {
303     if(layerIs3d)
304     {
305       updateChildHit = ((currentHit.depth > childHit.depth) ||
306                         ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
307     }
308     else
309     {
310       updateChildHit = currentHit.depth >= childHit.depth;
311     }
312   }
313
314   if(updateChildHit)
315   {
316     if(!parentIsRenderable || currentHit.depth > hit.depth ||
317        (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
318     {
319       childHit = currentHit;
320     }
321   }
322 }
323
324 /**
325  * Recursively hit test all the actors, without crossing into other layers.
326  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
327  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
328  * of touch vector). The closest Hit-Tested Actor is that which is returned.
329  * Exceptions to this rule are:
330  * - When comparing against renderable parents, if Actor is the same distance
331  * or closer than it's renderable parent, then it takes priority.
332  */
333 HitActor HitTestWithinLayer(Actor&                                           actor,
334                             const RenderTask&                                renderTask,
335                             const RenderTaskList::ExclusivesContainer&       exclusives,
336                             const Vector4&                                   rayOrigin,
337                             const Vector4&                                   rayDir,
338                             const float&                                     nearClippingPlane,
339                             const float&                                     farClippingPlane,
340                             HitTestInterface&                                hitCheck,
341                             const bool&                                      overlayed,
342                             bool&                                            overlayHit,
343                             bool                                             layerIs3d,
344                             const RayTest&                                   rayTest,
345                             const Integration::Point&                        point,
346                             const uint32_t                                   eventTime,
347                             std::list<Dali::Internal::Actor*>&               actorLists,
348                             const Integration::Scene::TouchPropagationType   propagationType)
349 {
350   HitActor hit;
351
352   if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
353   {
354     return hit;
355   }
356
357   // For clipping, regardless of whether we have hit this actor or not.
358   // This is used later to ensure all nested clipped children have hit
359   // all clipping actors also for them to be counted as hit.
360   const ClippingMode::Type clippingMode   = actor.GetClippingMode();
361   bool                     clippingActor  = clippingMode != ClippingMode::DISABLED;
362   bool                     overlayedActor = overlayed || actor.IsOverlay();
363
364   // If we are a clipping actor or hittable...
365   if(!HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit, propagationType))
366   {
367     return hit;
368   }
369
370   // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
371   // 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.
372   if(clippingActor && !(hit.actor) && (clippingMode == ClippingMode::CLIP_CHILDREN))
373   {
374     return hit;
375   }
376   else if(propagationType == Integration::Scene::TouchPropagationType::GEOMETRY && hit.actor)
377   {
378     // Saves the actors that can be hit as a list
379     actorLists.push_back(hit.actor);
380   }
381
382   // Find a child hit, until we run out of actors in the current layer.
383   HitActor childHit;
384   if(actor.GetChildCount() > 0)
385   {
386     childHit.distance        = std::numeric_limits<float>::max();
387     childHit.depth           = std::numeric_limits<int32_t>::min();
388     ActorContainer& children = actor.GetChildrenInternal();
389
390     // Hit test ALL children and calculate their distance.
391     bool parentIsRenderable = actor.IsRenderable();
392
393     for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
394     {
395       // Descend tree only if...
396       if(!(*iter)->IsLayer() &&                           // Child is NOT a layer, hit testing current layer only
397          (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
398       {
399         HitActor currentHit(HitTestWithinLayer((*iter->Get()),
400                                                renderTask,
401                                                exclusives,
402                                                rayOrigin,
403                                                rayDir,
404                                                nearClippingPlane,
405                                                farClippingPlane,
406                                                hitCheck,
407                                                overlayedActor,
408                                                overlayHit,
409                                                layerIs3d,
410                                                rayTest,
411                                                point,
412                                                eventTime,
413                                                actorLists,
414                                                propagationType));
415         // Make sure the set hit actor is actually hittable. This is usually required when we have some
416         // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
417         // are hittable or not.
418         if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
419         {
420           continue;
421         }
422
423         UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
424       }
425     }
426   }
427
428   if(childHit.actor)
429   {
430     // If child has been hit & current actor is clipping to bounding box...
431     if(clippingMode == ClippingMode::CLIP_TO_BOUNDING_BOX)
432     {
433       // ...then make sure the clipping actor has actually been hit unless the child hit actor is on a child overlay.
434       if(hit.actor || IsOnOverlay(childHit.actor, &actor))
435       {
436         // Only then should we return the child hit in this scenario.
437         return childHit;
438       }
439     }
440     else
441     {
442       // no clipping concerns, return child hit.
443       return childHit;
444     }
445   }
446
447   return hit;
448 }
449
450 /**
451  * Return true if actor is sourceActor or a descendent of sourceActor
452  */
453 bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
454 {
455   if(&sourceActor == &actor)
456   {
457     return true;
458   }
459
460   Actor* parent = actor.GetParent();
461   if(parent)
462   {
463     return IsWithinSourceActors(sourceActor, *parent);
464   }
465
466   // Not within source actors
467   return false;
468 }
469
470 /**
471  * Returns true if the actor and all of the actor's parents are hittable.
472  */
473 bool IsActorActuallyHittable(Actor* actor, HitTestInterface& hitCheck)
474 {
475   Actor* currentActor = actor;
476   // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
477   while(currentActor)
478   {
479     if(!hitCheck.DescendActorHierarchy(currentActor))
480     {
481       return false;
482     }
483     currentActor = currentActor->GetParent();
484   }
485
486   return true;
487 }
488
489 /**
490  * Returns true if the layer and all of the layer's parents are hittable.
491  */
492 inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
493 {
494   bool hittable(true);
495
496   if(layer.IsClipping())
497   {
498     ClippingBox box = layer.GetClippingBox();
499
500     if(screenCoordinates.x < static_cast<float>(box.x) ||
501        screenCoordinates.x > static_cast<float>(box.x + box.width) ||
502        screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
503        screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
504     {
505       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
506       hittable = false;
507     }
508   }
509
510   if(hittable)
511   {
512     Actor* actor(&layer);
513     hittable = IsActorActuallyHittable(actor, hitCheck);
514   }
515
516   return hittable;
517 }
518
519 /**
520  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
521  */
522 void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
523 {
524   CameraActor* cameraActor = renderTask.GetCameraActor();
525   nearClippingPlane        = cameraActor->GetNearClippingPlane();
526   farClippingPlane         = cameraActor->GetFarClippingPlane();
527 }
528
529 void GeoHitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
530                           const Vector2&                             sceneSize,
531                           LayerList&                                 layers,
532                           RenderTask&                                renderTask,
533                           Vector2                                    screenCoordinates,
534                           Results&                                   results,
535                           HitTestInterface&                          hitCheck,
536                           const RayTest&                             rayTest)
537 {
538   if(renderTask.IsHittable(screenCoordinates))
539   {
540     Viewport viewport;
541     renderTask.GetHittableViewport(viewport);
542
543     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
544        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
545        screenCoordinates.y < static_cast<float>(viewport.y) ||
546        screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
547     {
548       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
549       return;
550     }
551
552     float nearClippingPlane, farClippingPlane;
553     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
554
555     // Determine the layer depth of the source actor
556     Actor* sourceActor(renderTask.GetSourceActor());
557     if(sourceActor)
558     {
559       Dali::Layer sourceLayer(sourceActor->GetLayer());
560       if(sourceLayer)
561       {
562         const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
563         CameraActor*   cameraActor     = renderTask.GetCameraActor();
564         bool           pickingPossible = cameraActor->BuildPickingRay(screenCoordinates,
565                                                             viewport,
566                                                             results.rayOrigin,
567                                                             results.rayDirection);
568         if(!pickingPossible)
569         {
570           return;
571         }
572
573         // Hit test starting with the top layer, working towards the bottom layer.
574         bool overlayHit = false;
575
576         for(uint32_t i = 0; i < layers.GetLayerCount(); ++i)
577         {
578           Layer* layer(layers.GetLayer(i));
579           overlayHit = false;
580           HitActor hit;
581
582           // Ensure layer is touchable (also checks whether ancestors are also touchable)
583           if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
584           {
585             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
586             if(sourceActorDepth == i)
587             {
588               // Recursively hit test the source actor & children, without crossing into other layers.
589               hit = HitTestWithinLayer(*sourceActor,
590                                        renderTask,
591                                        exclusives,
592                                        results.rayOrigin,
593                                        results.rayDirection,
594                                        nearClippingPlane,
595                                        farClippingPlane,
596                                        hitCheck,
597                                        overlayHit,
598                                        overlayHit,
599                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
600                                        rayTest,
601                                        results.point,
602                                        results.eventTime,
603                                        results.actorLists,
604                                        Integration::Scene::TouchPropagationType::GEOMETRY);
605             }
606             else if(IsWithinSourceActors(*sourceActor, *layer))
607             {
608               // Recursively hit test all the actors, without crossing into other layers.
609               hit = HitTestWithinLayer(*layer,
610                                        renderTask,
611                                        exclusives,
612                                        results.rayOrigin,
613                                        results.rayDirection,
614                                        nearClippingPlane,
615                                        farClippingPlane,
616                                        hitCheck,
617                                        overlayHit,
618                                        overlayHit,
619                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
620                                        rayTest,
621                                        results.point,
622                                        results.eventTime,
623                                        results.actorLists,
624                                        Integration::Scene::TouchPropagationType::GEOMETRY);
625             }
626           }
627
628           if(hit.actor)
629           {
630             results.renderTask       = RenderTaskPtr(&renderTask);
631             results.actor            = Dali::Actor(hit.actor);
632             results.actorCoordinates = hit.hitPosition;
633           }
634         }
635       }
636     }
637   }
638   return;
639 }
640
641 /**
642  * Hit test a RenderTask
643  */
644 bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
645                        const Vector2&                             sceneSize,
646                        LayerList&                                 layers,
647                        RenderTask&                                renderTask,
648                        Vector2                                    screenCoordinates,
649                        Results&                                   results,
650                        HitTestInterface&                          hitCheck,
651                        const RayTest&                             rayTest)
652 {
653   if(renderTask.IsHittable(screenCoordinates))
654   {
655     Viewport viewport;
656     renderTask.GetHittableViewport(viewport);
657
658     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
659        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
660        screenCoordinates.y < static_cast<float>(viewport.y) ||
661        screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
662     {
663       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
664       return false;
665     }
666
667     float nearClippingPlane, farClippingPlane;
668     GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
669
670     // Determine the layer depth of the source actor
671     Actor* sourceActor(renderTask.GetSourceActor());
672
673     // Check the source actor is actually hittable or not.
674     if(sourceActor && IsActorActuallyHittable(sourceActor, hitCheck))
675     {
676       Dali::Layer sourceLayer(sourceActor->GetLayer());
677       if(sourceLayer)
678       {
679         const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
680
681         CameraActor* cameraActor     = renderTask.GetCameraActor();
682         bool         pickingPossible = cameraActor->BuildPickingRay(
683           screenCoordinates,
684           viewport,
685           results.rayOrigin,
686           results.rayDirection);
687         if(!pickingPossible)
688         {
689           return false;
690         }
691
692         // Hit test starting with the top layer, working towards the bottom layer.
693         HitActor hit;
694         bool     overlayHit       = false;
695         bool     layerConsumesHit = false;
696
697         // Be used when we decide to consume layer.
698         // We should not consume hit if sourceLayer is above on consumable layer. Otherwise, we should consume. So just initialize it as 0.
699         // sourceLayerIndex can be a relative value to calculate the relationship with the layer.
700         // 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.
701         // If there is a sourceLayer above the consumable layer, the sourceLayerIndex is determined and the index of the consumable layer is also determined.
702         // Then we can calculate the relationship between the two layers.
703         bool    IsHitTestWithinLayer = false;
704         int32_t sourceLayerIndex     = 0;
705         int32_t consumedLayerIndex   = -1;
706
707         for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
708         {
709           Layer* layer(layers.GetLayer(i));
710           overlayHit           = false;
711           IsHitTestWithinLayer = false;
712
713           if(sourceLayer == layer)
714           {
715             sourceLayerIndex = i;
716           }
717
718           // Ensure layer is touchable (also checks whether ancestors are also touchable)
719           if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
720           {
721             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
722             if(sourceActorDepth == static_cast<uint32_t>(i))
723             {
724               IsHitTestWithinLayer = true;
725               // Recursively hit test the source actor & children, without crossing into other layers.
726               hit = HitTestWithinLayer(*sourceActor,
727                                        renderTask,
728                                        exclusives,
729                                        results.rayOrigin,
730                                        results.rayDirection,
731                                        nearClippingPlane,
732                                        farClippingPlane,
733                                        hitCheck,
734                                        overlayHit,
735                                        overlayHit,
736                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
737                                        rayTest,
738                                        results.point,
739                                        results.eventTime,
740                                        results.actorLists,
741                                        Integration::Scene::TouchPropagationType::PARENT);
742             }
743             else if(IsWithinSourceActors(*sourceActor, *layer))
744             {
745               IsHitTestWithinLayer = true;
746               // Recursively hit test all the actors, without crossing into other layers.
747               hit = HitTestWithinLayer(*layer,
748                                        renderTask,
749                                        exclusives,
750                                        results.rayOrigin,
751                                        results.rayDirection,
752                                        nearClippingPlane,
753                                        farClippingPlane,
754                                        hitCheck,
755                                        overlayHit,
756                                        overlayHit,
757                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
758                                        rayTest,
759                                        results.point,
760                                        results.eventTime,
761                                        results.actorLists,
762                                        Integration::Scene::TouchPropagationType::PARENT);
763             }
764
765             // If this layer is set to consume the hit, then do not check any layers behind it
766             if(IsHitTestWithinLayer && hitCheck.DoesLayerConsumeHit(layer))
767             {
768               consumedLayerIndex = i;
769               layerConsumesHit   = true;
770               break;
771             }
772           }
773         }
774
775         if(hit.actor)
776         {
777           results.renderTask       = RenderTaskPtr(&renderTask);
778           results.actor            = Dali::Actor(hit.actor);
779           results.actorCoordinates = hit.hitPosition;
780
781           return true; // Success
782         }
783
784         if(layerConsumesHit)
785         {
786           // Consumes if the hitted layer is above the SourceActor's layer.
787           bool ret = sourceLayerIndex <= consumedLayerIndex;
788           if(ret)
789           {
790             DALI_LOG_RELEASE_INFO("layer is set to consume the hit\n");
791             results.renderTask = RenderTaskPtr(&renderTask);
792             results.actor      = Dali::Layer(layers.GetLayer(consumedLayerIndex));
793           }
794           return ret;
795         }
796       }
797     }
798   }
799   return false;
800 }
801
802 /**
803  * Selects Prior Actor that is rendered later between firstActor and secondActor in the layer of rootActor.
804  * if only one of Actor is included in the layer, returns the Actor.
805  * if both of the firstActor and secondActor are not included in the layer, returns empty Actor.
806  */
807 Dali::Actor FindPriorActorInLayer(Dali::Actor rootActor, Dali::Actor firstActor, Dali::Actor secondActor)
808 {
809   Dali::Actor priorActor;
810   Dali::Layer layer = rootActor.GetLayer();
811   bool firstActorIncluded = firstActor.GetLayer() == layer;
812   bool secondActorIncluded = secondActor.GetLayer() == layer;
813
814   if(firstActorIncluded && !secondActorIncluded)
815   {
816     priorActor = firstActor;
817   }
818   else if(!firstActorIncluded && secondActorIncluded)
819   {
820     priorActor = secondActor;
821   }
822   else if(firstActorIncluded && secondActorIncluded)
823   {
824     priorActor = (GetImplementation(firstActor).GetSortingDepth() < GetImplementation(secondActor).GetSortingDepth()) ? secondActor : firstActor;
825   }
826
827   return priorActor;
828 }
829
830 /**
831  * Selects Prior Actor that is rendered later between firstActor and secondActor from child scene tree of rootActor.
832  */
833 Dali::Actor FindPriorActorInLayers(const LayerList& layers, Dali::Actor rootActor, Dali::Actor firstActor, Dali::Actor secondActor)
834 {
835   Dali::Layer sourceLayer = rootActor.GetLayer();
836   const uint32_t sourceActorDepth(sourceLayer.GetProperty<int>(Dali::Layer::Property::DEPTH));
837
838   Dali::Actor priorActor;
839   uint32_t layerCount = layers.GetLayerCount();
840   if(layerCount > 0)
841   {
842     for(int32_t i = layerCount - 1; i >= 0; --i)
843     {
844       Layer* layer(layers.GetLayer(i));
845       if(sourceActorDepth == static_cast<uint32_t>(i))
846       {
847         priorActor = FindPriorActorInLayer(rootActor, firstActor, secondActor);
848       }
849       else if(IsWithinSourceActors(GetImplementation(rootActor), *layer))
850       {
851         Dali::Actor layerRoot = Dali::Actor(layer);
852         priorActor            = FindPriorActorInLayer(layerRoot, firstActor, secondActor);
853       }
854
855       if(priorActor)
856       {
857         break;
858       }
859     }
860   }
861   return priorActor;
862 }
863
864 /**
865  * Iterate through the RenderTaskList and perform hit testing.
866  *
867  * @param[in] sceneSize The scene size the tests will be performed in
868  * @param[in] layers The list of layers to test
869  * @param[in] taskList The list of render tasks
870  * @param[out] results Ray information calculated by the camera
871  * @param[in] hitCheck The hit testing interface object to use
872  * @param[in] propagationType Whether the scene using geometry event propagation touch and hover events.
873  * @return True if we have a hit, false otherwise
874  */
875 bool HitTestRenderTaskList(const Vector2&                                  sceneSize,
876                            LayerList&                                      layers,
877                            RenderTaskList&                                 taskList,
878                            const Vector2&                                  screenCoordinates,
879                            Results&                                        results,
880                            HitTestInterface&                               hitCheck,
881                            const Integration::Scene::TouchPropagationType  propagationType)
882 {
883   if(propagationType == Integration::Scene::TouchPropagationType::GEOMETRY)
884   {
885     RenderTaskList::RenderTaskContainer&          tasks      = taskList.GetTasks();
886     RenderTaskList::RenderTaskContainer::iterator endIter    = tasks.end();
887     const auto&                                   exclusives = taskList.GetExclusivesList();
888     RayTest                                       rayTest;
889
890     // Hit test order should be of draw order
891     for(RenderTaskList::RenderTaskContainer::iterator iter = tasks.begin(); endIter != iter; ++iter)
892     {
893       RenderTask& renderTask = *iter->Get();
894       GeoHitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest);
895     }
896
897     return !results.actorLists.empty();
898   }
899   else
900   {
901     RenderTaskList::RenderTaskContainer&                  tasks      = taskList.GetTasks();
902     RenderTaskList::RenderTaskContainer::reverse_iterator endIter    = tasks.rend();
903     const auto&                                           exclusives = taskList.GetExclusivesList();
904     RayTest                                               rayTest;
905
906     Results                                      storedResults = results;
907     std::vector<std::pair<Dali::Actor, Results>> offScreenHitResults;
908     // Hit test order should be reverse of draw order
909     for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
910     {
911       RenderTask& renderTask = *iter->Get();
912       if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
913       {
914         if(renderTask.GetFrameBuffer())
915         {
916           Results result = results;
917           offScreenHitResults.push_back(std::make_pair(renderTask.GetScreenToFrameBufferMappingActor(), std::move(result)));
918           continue;
919         }
920
921         if(offScreenHitResults.empty())
922         {
923           return true;
924         }
925
926         Actor* sourceActor(renderTask.GetSourceActor());
927         for(auto&& pair : offScreenHitResults)
928         {
929           Dali::Actor mappingActor = pair.first;
930           if(!mappingActor || !IsWithinSourceActors(*sourceActor, GetImplementation(mappingActor)))
931           {
932             continue;
933           }
934
935           bool mappingActorInsideHitConsumingLayer = false;
936           if(GetImplementation(results.actor).IsLayer())
937           {
938             Dali::Layer resultLayer = Dali::Layer::DownCast(results.actor);
939             // Check the resultLayer is consuming hit even though the layer is not hittable.
940             // And check the resultLayer is the layer of mappingActor too.
941             if(hitCheck.DoesLayerConsumeHit(&GetImplementation(resultLayer)) && !hitCheck.IsActorHittable(&GetImplementation(results.actor)) && results.actor == mappingActor.GetLayer())
942             {
943               mappingActorInsideHitConsumingLayer = true;
944             }
945           }
946           if(mappingActorInsideHitConsumingLayer || mappingActor == FindPriorActorInLayers(layers, Dali::Actor(sourceActor), mappingActor, results.actor))
947           {
948             results = pair.second;
949             break;
950           }
951         }
952         // Return true when an actor is hit (or layer in our render-task consumes the hit)
953         return true;
954       }
955     }
956
957     // When no OnScreen Actor is hitted but there are hit results from OffScreen RenderTasks
958     // those use ScreenToFrameBufferFunction, simply returns first hitted result.
959     if(!offScreenHitResults.empty())
960     {
961       results = offScreenHitResults.front().second;
962       return true;
963     }
964
965     results = storedResults;
966   }
967   return false;
968 }
969
970 /**
971  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
972  *
973  * @param[in] sceneSize The scene size the tests will be performed in
974  * @param[in] layers The list of layers to test
975  * @param[in] taskList The list of render tasks
976  * @param[out] results Ray information calculated by the camera
977  * @param[in] hitCheck The hit testing interface object to use
978  * @param[in] propagationType Whether the scene using geometry event propagation touch and hover events.
979  * @return True if we have a hit, false otherwise
980  */
981 bool HitTestForEachRenderTask(const Vector2&                                 sceneSize,
982                               LayerList&                                     layers,
983                               RenderTaskList&                                taskList,
984                               const Vector2&                                 screenCoordinates,
985                               Results&                                       results,
986                               HitTestInterface&                              hitCheck,
987                               const Integration::Scene::TouchPropagationType propagationType)
988 {
989   bool result = false;
990
991   if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, propagationType))
992   {
993     // Found hit.
994     result = true;
995   }
996
997   return result;
998 }
999
1000 } // unnamed namespace
1001
1002 HitTestInterface::~HitTestInterface() = default;
1003
1004 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)
1005 {
1006   bool wasHit(false);
1007   // Hit-test the regular on-scene actors
1008   Results                hitTestResults;
1009   HitTestFunctionWrapper hitTestFunctionWrapper(func);
1010   if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper, propagationType))
1011   {
1012     results.actor            = hitTestResults.actor;
1013     results.actorCoordinates = hitTestResults.actorCoordinates;
1014     wasHit                   = true;
1015   }
1016   return wasHit;
1017 }
1018
1019 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface, const Integration::Scene::TouchPropagationType propagationType)
1020 {
1021   bool wasHit(false);
1022
1023   // Hit-test the regular on-scene actors
1024   if(!wasHit)
1025   {
1026     wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface, propagationType);
1027   }
1028   return wasHit;
1029 }
1030
1031 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor, const Integration::Scene::TouchPropagationType propagationType)
1032 {
1033   ActorTouchableCheck actorTouchableCheck;
1034   actorTouchableCheck.SetOwnActor(ownActor);
1035   return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck, propagationType);
1036 }
1037
1038 } // namespace Dali::Internal::HitTestAlgorithm