2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
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>
40 namespace HitTestAlgorithm
48 distance(std::numeric_limits<float>::max()),
49 depth(std::numeric_limits<int>::min())
53 Actor* actor; ///< The actor hit (if actor is hit, then this is initialised).
54 Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
55 float distance; ///< Distance from ray origin to hit actor.
56 int32_t depth; ///< Depth index of this actor.
60 * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
62 struct HitTestFunctionWrapper : public HitTestInterface
67 * @param[in] func HitTestFunction to call with an Actor handle.
69 HitTestFunctionWrapper(Dali::HitTestAlgorithm::HitTestFunction func)
74 bool IsActorHittable(Actor* actor) override
76 return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::CHECK_ACTOR);
79 bool DescendActorHierarchy(Actor* actor) override
81 return mFunc(Dali::Actor(actor), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE);
84 bool DoesLayerConsumeHit(Layer* layer) override
86 // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
87 // where the caller may want to check for something completely different.
88 // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
92 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
94 return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
97 Dali::HitTestAlgorithm::HitTestFunction mFunc;
101 * Used in the hit-test algorithm to check whether the actor is touchable.
102 * It is used by the touch event processor.
104 struct ActorTouchableCheck : public HitTestInterface
106 bool IsActorHittable(Actor* actor) override
108 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?
109 actor->IsHittable(); // Is actor sensitive, visible and on the scene?
112 bool DescendActorHierarchy(Actor* actor) override
114 return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
115 actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
118 bool DoesLayerConsumeHit(Layer* layer) override
120 return layer->IsTouchConsumed();
123 bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
125 if(point.GetState() != PointState::STARTED && actor->IsAllowedOnlyOwnTouch() && ownActor != actor)
129 return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
132 void SetOwnActor(const Actor* actor)
136 const Actor* ownActor;
140 * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
142 bool IsActorExclusiveToAnotherRenderTask(const Actor& actor,
143 const RenderTask& renderTask,
144 const RenderTaskList::ExclusivesContainer& exclusives)
147 if(exclusives.size())
149 for(const auto& exclusive : exclusives)
151 if((exclusive.renderTaskPtr != &renderTask) && (exclusive.actor.GetActor() == &actor))
161 * Hit tests the given actor and updates the in/out variables appropriately
163 void HitTestActor(const RenderTask& renderTask,
164 const Vector4& rayOrigin,
165 const Vector4& rayDir,
166 const float& nearClippingPlane,
167 const float& farClippingPlane,
168 HitTestInterface& hitCheck,
169 const RayTest& rayTest,
170 const Integration::Point& point,
171 const uint32_t eventTime,
178 if(clippingActor || hitCheck.IsActorHittable(&actor))
180 Vector3 size(actor.GetCurrentSize());
182 // Ensure the actor has a valid size.
183 // If so, perform a quick ray sphere test to see if our ray is close to the actor.
184 if(size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest(actor, rayOrigin, rayDir))
186 Vector2 hitPointLocal;
189 // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
190 if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
192 // Calculate z coordinate value in Camera Space.
193 const Matrix& viewMatrix = renderTask.GetCameraActor()->GetViewMatrix();
194 const Vector4& hitDir = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
195 const float cameraDepthDistance = (viewMatrix * hitDir).z;
197 // Check if cameraDepthDistance is between clipping plane
198 if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
200 if(overlayHit && !overlayedActor)
202 // If we have already hit an overlay and current actor is not an overlay ignore current actor.
211 // If the hit actor does not want to hit, the hit-test continues.
212 if(hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime))
215 hit.hitPosition = hitPointLocal;
216 hit.distance = distance;
217 hit.depth = actor.GetSortingDepth();
219 if(actor.GetRendererCount() > 0)
221 //Get renderer with maximum depth
222 int rendererMaxDepth(actor.GetRendererAt(0).Get()->GetDepthIndex());
223 for(uint32_t i(1); i < actor.GetRendererCount(); ++i)
225 int depth = actor.GetRendererAt(i).Get()->GetDepthIndex();
226 if(depth > rendererMaxDepth)
228 rendererMaxDepth = depth;
231 hit.depth += rendererMaxDepth;
242 * When iterating through the children of an actor, this method updates the child-hit-data.
244 void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
246 bool updateChildHit = false;
247 if(currentHit.distance >= 0.0f)
251 updateChildHit = ((currentHit.depth > childHit.depth) ||
252 ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
256 updateChildHit = currentHit.depth >= childHit.depth;
262 if(!parentIsRenderable || currentHit.depth > hit.depth ||
263 (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
265 childHit = currentHit;
271 * Recursively hit test all the actors, without crossing into other layers.
272 * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
273 * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
274 * of touch vector). The closest Hit-Tested Actor is that which is returned.
275 * Exceptions to this rule are:
276 * - When comparing against renderable parents, if Actor is the same distance
277 * or closer than it's renderable parent, then it takes priority.
279 HitActor HitTestWithinLayer(Actor& actor,
280 const RenderTask& renderTask,
281 const RenderTaskList::ExclusivesContainer& exclusives,
282 const Vector4& rayOrigin,
283 const Vector4& rayDir,
284 const float& nearClippingPlane,
285 const float& farClippingPlane,
286 HitTestInterface& hitCheck,
287 const bool& overlayed,
290 const RayTest& rayTest,
291 const Integration::Point& point,
292 const uint32_t eventTime)
296 if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
301 // For clipping, regardless of whether we have hit this actor or not.
302 // This is used later to ensure all nested clipped children have hit
303 // all clipping actors also for them to be counted as hit.
304 bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
305 bool overlayedActor = overlayed || actor.IsOverlay();
307 // If we are a clipping actor or hittable...
308 HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit);
310 // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
311 if(clippingActor && !(hit.actor))
316 // Find a child hit, until we run out of actors in the current layer.
318 if(actor.GetChildCount() > 0)
320 childHit.distance = std::numeric_limits<float>::max();
321 childHit.depth = std::numeric_limits<int32_t>::min();
322 ActorContainer& children = actor.GetChildrenInternal();
324 // Hit test ALL children and calculate their distance.
325 bool parentIsRenderable = actor.IsRenderable();
327 for(ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter)
329 // Descend tree only if...
330 if(!(*iter)->IsLayer() && // Child is NOT a layer, hit testing current layer only
331 (hitCheck.DescendActorHierarchy((*iter).Get()))) // We can descend into child hierarchy
333 HitActor currentHit(HitTestWithinLayer((*iter->Get()),
348 // Make sure the set hit actor is actually hittable. This is usually required when we have some
349 // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
350 // are hittable or not.
351 if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
356 UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
361 return (childHit.actor) ? childHit : hit;
365 * Return true if actor is sourceActor or a descendent of sourceActor
367 bool IsWithinSourceActors(const Actor& sourceActor, const Actor& actor)
369 if(&sourceActor == &actor)
374 Actor* parent = actor.GetParent();
377 return IsWithinSourceActors(sourceActor, *parent);
380 // Not within source actors
385 * Returns true if the layer and all of the layer's parents are visible and sensitive.
387 inline bool IsActuallyHittable(Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck)
391 if(layer.IsClipping())
393 ClippingBox box = layer.GetClippingBox();
395 if(screenCoordinates.x < static_cast<float>(box.x) ||
396 screenCoordinates.x > static_cast<float>(box.x + box.width) ||
397 screenCoordinates.y < stageSize.y - static_cast<float>(box.y + box.height) ||
398 screenCoordinates.y > stageSize.y - static_cast<float>(box.y))
400 // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
407 Actor* actor(&layer);
409 // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
410 while(actor && hittable)
412 if(!hitCheck.DescendActorHierarchy(actor))
417 actor = actor->GetParent();
425 * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
427 void GetCameraClippingPlane(RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane)
429 CameraActor* cameraActor = renderTask.GetCameraActor();
430 nearClippingPlane = cameraActor->GetNearClippingPlane();
431 farClippingPlane = cameraActor->GetFarClippingPlane();
435 * Hit test a RenderTask
437 bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
438 const Vector2& sceneSize,
440 RenderTask& renderTask,
441 Vector2 screenCoordinates,
443 HitTestInterface& hitCheck,
444 const RayTest& rayTest)
446 if(renderTask.IsHittable(screenCoordinates))
449 renderTask.GetHittableViewport(viewport);
451 if(screenCoordinates.x < static_cast<float>(viewport.x) ||
452 screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
453 screenCoordinates.y < static_cast<float>(viewport.y) ||
454 screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
456 // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
460 float nearClippingPlane, farClippingPlane;
461 GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
463 // Determine the layer depth of the source actor
464 Actor* sourceActor(renderTask.GetSourceActor());
467 Dali::Layer sourceLayer(sourceActor->GetLayer());
470 const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
472 CameraActor* cameraActor = renderTask.GetCameraActor();
473 bool pickingPossible = cameraActor->BuildPickingRay(
477 results.rayDirection);
483 // Hit test starting with the top layer, working towards the bottom layer.
485 bool overlayHit = false;
486 bool layerConsumesHit = false;
488 // Be used when we decide to consume layer.
489 // We should not consume hit if sourceLayer is above on consumable layer. Otherwise, we should consume. So just initialize it as 0.
490 // sourceLayerIndex can be a relative value to calculate the relationship with the layer.
491 // 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.
492 // If there is a sourceLayer above the consumable layer, the sourceLayerIndex is determined and the index of the consumable layer is also determined.
493 // Then we can calculate the relationship between the two layers.
494 bool IsHitTestWithinLayer = false;
495 int32_t sourceLayerIndex = 0;
496 int32_t consumedLayerIndex = -1;
498 for(int32_t i = layers.GetLayerCount() - 1; i >= 0 && !(hit.actor); --i)
500 Layer* layer(layers.GetLayer(i));
502 IsHitTestWithinLayer = false;
504 if(sourceLayer == layer)
506 sourceLayerIndex = i;
509 // Ensure layer is touchable (also checks whether ancestors are also touchable)
510 if(IsActuallyHittable(*layer, screenCoordinates, sceneSize, hitCheck))
512 // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
513 if(sourceActorDepth == static_cast<uint32_t>(i))
515 IsHitTestWithinLayer = true;
516 // Recursively hit test the source actor & children, without crossing into other layers.
517 hit = HitTestWithinLayer(*sourceActor,
521 results.rayDirection,
527 layer->GetBehavior() == Dali::Layer::LAYER_3D,
532 else if(IsWithinSourceActors(*sourceActor, *layer))
534 IsHitTestWithinLayer = true;
535 // Recursively hit test all the actors, without crossing into other layers.
536 hit = HitTestWithinLayer(*layer,
540 results.rayDirection,
546 layer->GetBehavior() == Dali::Layer::LAYER_3D,
552 // If this layer is set to consume the hit, then do not check any layers behind it
553 if(IsHitTestWithinLayer && hitCheck.DoesLayerConsumeHit(layer))
555 consumedLayerIndex = i;
556 layerConsumesHit = true;
564 results.renderTask = RenderTaskPtr(&renderTask);
565 results.actor = Dali::Actor(hit.actor);
566 results.actorCoordinates = hit.hitPosition;
568 return true; // Success
573 // Consumes if the hitted layer is above the SourceActor's layer.
574 return sourceLayerIndex <= consumedLayerIndex;
583 * Iterate through the RenderTaskList and perform hit testing.
585 * @param[in] sceneSize The scene size the tests will be performed in
586 * @param[in] layers The list of layers to test
587 * @param[in] taskList The list of render tasks
588 * @param[out] results Ray information calculated by the camera
589 * @param[in] hitCheck The hit testing interface object to use
590 * @return True if we have a hit, false otherwise
592 bool HitTestRenderTaskList(const Vector2& sceneSize,
594 RenderTaskList& taskList,
595 const Vector2& screenCoordinates,
597 HitTestInterface& hitCheck)
599 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
600 RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
601 const auto& exclusives = taskList.GetExclusivesList();
604 // Hit test order should be reverse of draw order
605 for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
607 RenderTask& renderTask = *iter->Get();
608 if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
610 // Return true when an actor is hit (or layer in our render-task consumes the hit)
619 * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
621 * @param[in] sceneSize The scene size the tests will be performed in
622 * @param[in] layers The list of layers to test
623 * @param[in] taskList The list of render tasks
624 * @param[out] results Ray information calculated by the camera
625 * @param[in] hitCheck The hit testing interface object to use
626 * @return True if we have a hit, false otherwise
628 bool HitTestForEachRenderTask(const Vector2& sceneSize,
630 RenderTaskList& taskList,
631 const Vector2& screenCoordinates,
633 HitTestInterface& hitCheck)
637 if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck))
646 } // unnamed namespace
648 HitTestInterface::~HitTestInterface() = default;
650 bool HitTest(const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func)
653 // Hit-test the regular on-scene actors
654 Results hitTestResults;
655 HitTestFunctionWrapper hitTestFunctionWrapper(func);
656 if(HitTestForEachRenderTask(sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper))
658 results.actor = hitTestResults.actor;
659 results.actorCoordinates = hitTestResults.actorCoordinates;
665 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface)
669 // Hit-test the regular on-scene actors
672 wasHit = HitTestForEachRenderTask(sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface);
677 bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor)
679 ActorTouchableCheck actorTouchableCheck;
680 actorTouchableCheck.SetOwnActor(ownActor);
681 return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck);
684 } // namespace HitTestAlgorithm
686 } // namespace Internal