2 * Copyright (c) 2020 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/public-api/actors/layer.h>
23 #include <dali/public-api/math/vector2.h>
24 #include <dali/public-api/math/vector4.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/internal/event/actors/actor-impl.h>
27 #include <dali/internal/event/actors/camera-actor-impl.h>
28 #include <dali/internal/event/actors/layer-impl.h>
29 #include <dali/internal/event/actors/layer-list.h>
30 #include <dali/internal/event/common/projection.h>
31 #include <dali/internal/event/events/ray-test.h>
32 #include <dali/internal/event/render-tasks/render-task-impl.h>
33 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
41 namespace HitTestAlgorithm
51 distance( std::numeric_limits<float>::max() ),
52 depth( std::numeric_limits<int>::min() )
56 Actor *actor; ///< The actor hit (if actor is hit, then this is initialised).
57 Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
58 float distance; ///< Distance from ray origin to hit actor.
59 int32_t depth; ///< Depth index of this actor.
63 * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
65 struct HitTestFunctionWrapper : public HitTestInterface
70 * @param[in] func HitTestFunction to call with an Actor handle.
72 HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
77 bool IsActorHittable( Actor* actor ) override
79 return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
82 bool DescendActorHierarchy( Actor* actor ) override
84 return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
87 bool DoesLayerConsumeHit( Layer* layer ) override
89 // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
90 // where the caller may want to check for something completely different.
91 // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
95 Dali::HitTestAlgorithm::HitTestFunction mFunc;
99 * Used in the hit-test algorithm to check whether the actor is touchable.
100 * It is used by the touch event processor.
102 struct ActorTouchableCheck : public HitTestInterface
104 bool IsActorHittable( Actor* actor ) override
106 return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
107 actor->IsHittable(); // Is actor sensitive, visible and on the scene?
110 bool DescendActorHierarchy( Actor* actor ) override
112 return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
113 actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
116 bool DoesLayerConsumeHit( Layer* layer ) override
118 return layer->IsTouchConsumed();
123 * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
125 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
126 const RenderTask& renderTask,
127 const RenderTaskList::ExclusivesContainer& exclusives )
130 if( exclusives.size() )
132 for( const auto& exclusive : exclusives )
134 if( ( exclusive.renderTaskPtr != &renderTask ) && ( exclusive.actor.GetActor() == &actor ) )
144 * Recursively hit test all the actors, without crossing into other layers.
145 * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
146 * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
147 * of touch vector). The closest Hit-Tested Actor is that which is returned.
148 * Exceptions to this rule are:
149 * - When comparing against renderable parents, if Actor is the same distance
150 * or closer than it's renderable parent, then it takes priority.
152 HitActor HitTestWithinLayer( Actor& actor,
153 const RenderTask& renderTask,
154 const RenderTaskList::ExclusivesContainer& exclusives,
155 const Vector4& rayOrigin,
156 const Vector4& rayDir,
157 float& nearClippingPlane,
158 float& farClippingPlane,
159 HitTestInterface& hitCheck,
162 uint32_t clippingDepth,
163 uint32_t clippingBitPlaneMask,
164 const RayTest& rayTest )
168 if( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
173 // For clipping, regardless of whether we have hit this actor or not,
174 // we increase the clipping depth if we have hit a clipping actor.
175 // This is used later to ensure all nested clipped children have hit
176 // all clipping actors also for them to be counted as hit.
177 uint32_t newClippingDepth = clippingDepth;
178 bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
184 // If we are a clipping actor or hittable...
185 if( clippingActor || hitCheck.IsActorHittable( &actor ) )
187 Vector3 size( actor.GetCurrentSize() );
189 // Ensure the actor has a valid size.
190 // If so, perform a quick ray sphere test to see if our ray is close to the actor.
191 if( size.x > 0.0f && size.y > 0.0f && rayTest.SphereTest( actor, rayOrigin, rayDir ) )
193 Vector2 hitPointLocal;
196 // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
197 if( rayTest.ActorTest( actor, rayOrigin, rayDir, hitPointLocal, distance ) )
199 if( distance >= nearClippingPlane && distance <= farClippingPlane )
201 // If the hit has happened on a clipping actor, then add this clipping depth to the mask of hit clipping depths.
202 // This mask shows all the actors that have been hit at different clipping depths.
205 clippingBitPlaneMask |= 1u << clippingDepth;
208 if( overlayHit && !actor.IsOverlay() )
210 // If we have already hit an overlay and current actor is not an overlay ignore current actor.
214 if( actor.IsOverlay() )
219 // At this point we have hit an actor.
220 // Now perform checks for clipping.
221 // Assume we have hit the actor first as if it is not clipped this would be the case.
222 bool haveHitActor = true;
224 // Check if we are performing clipping. IE. if any actors so far have clipping enabled - not necessarily this one.
225 // We can do this by checking the clipping depth has a value 1 or above.
226 if( newClippingDepth >= 1u )
228 // Now for us to count this actor as hit, we must have also hit
229 // all CLIPPING actors up to this point in the hierarchy as well.
230 // This information is stored in the clippingBitPlaneMask we updated above.
231 // Here we calculate a comparison mask by setting all the bits up to the current depth value.
232 // EG. a depth of 4 (10000 binary) = a mask of 1111 binary.
233 // This allows us a fast way of comparing all bits are set up to this depth.
234 // Note: If the current Actor has clipping, that is included in the depth mask too.
235 uint32_t clippingDepthMask = ( 1u << newClippingDepth ) - 1u;
237 // The two masks must be equal to be a hit, as we are already assuming a hit
238 // (for non-clipping mode) then they must be not-equal to disqualify the hit.
239 if( clippingBitPlaneMask != clippingDepthMask )
241 haveHitActor = false;
248 hit.hitPosition = hitPointLocal;
249 hit.distance = distance;
250 hit.depth = actor.GetSortingDepth() ;
252 if( actor.GetRendererCount() > 0 )
254 //Get renderer with maximum depth
255 int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
256 for( uint32_t i(1); i < actor.GetRendererCount(); ++i )
258 int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
259 if( depth > rendererMaxDepth )
261 rendererMaxDepth = depth;
264 hit.depth += rendererMaxDepth;
273 // Find a child hit, until we run out of actors in the current layer.
275 if( actor.GetChildCount() > 0 )
277 childHit.distance = std::numeric_limits<float>::max();
278 childHit.depth = std::numeric_limits<int32_t>::min();
279 ActorContainer& children = actor.GetChildrenInternal();
281 // Hit test ALL children and calculate their distance.
282 bool parentIsRenderable = actor.IsRenderable();
284 for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
286 // Descend tree only if...
287 if ( !( *iter )->IsLayer() && // Child is NOT a layer, hit testing current layer only
288 ( hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We can descend into child hierarchy
290 HitActor currentHit( HitTestWithinLayer( ( *iter->Get() ),
301 clippingBitPlaneMask,
304 // Make sure the set hit actor is actually hittable. This is usually required when we have some
305 // clipping as we need to hit-test all actors as we descend the tree regardless of whether they
306 // are hittable or not.
307 if(currentHit.actor && !hitCheck.IsActorHittable(currentHit.actor))
312 bool updateChildHit = false;
313 if( currentHit.distance >= 0.0f )
317 updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
318 ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
322 updateChildHit = currentHit.depth >= childHit.depth;
328 if( !parentIsRenderable || currentHit.depth > hit.depth ||
329 ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
331 childHit = currentHit;
338 return ( childHit.actor ) ? childHit : hit;
342 * Return true if actor is sourceActor or a descendent of sourceActor
344 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
346 if ( &sourceActor == &actor )
351 Actor* parent = actor.GetParent();
354 return IsWithinSourceActors( sourceActor, *parent );
357 // Not within source actors
362 * Returns true if the layer and all of the layer's parents are visible and sensitive.
364 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
366 bool hittable( true );
368 if( layer.IsClipping() )
370 ClippingBox box = layer.GetClippingBox();
372 if( screenCoordinates.x < static_cast<float>( box.x )||
373 screenCoordinates.x > static_cast<float>( box.x + box.width )||
374 screenCoordinates.y < stageSize.y - static_cast<float>( box.y + box.height ) ||
375 screenCoordinates.y > stageSize.y - static_cast<float>( box.y ) )
377 // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
384 Actor* actor( &layer );
386 // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
387 while( actor && hittable )
389 if( ! hitCheck.DescendActorHierarchy( actor ) )
394 actor = actor->GetParent();
402 * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
404 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
406 CameraActor* cameraActor = renderTask.GetCameraActor();
407 nearClippingPlane = cameraActor->GetNearClippingPlane();
408 farClippingPlane = cameraActor->GetFarClippingPlane();
412 * Hit test a RenderTask
414 bool HitTestRenderTask( const RenderTaskList::ExclusivesContainer& exclusives,
415 const Vector2& sceneSize,
417 RenderTask& renderTask,
418 Vector2 screenCoordinates,
420 HitTestInterface& hitCheck,
421 const RayTest& rayTest )
423 if ( renderTask.IsHittable( screenCoordinates ) )
426 renderTask.GetViewport( viewport );
427 if( screenCoordinates.x < static_cast<float>( viewport.x ) ||
428 screenCoordinates.x > static_cast<float>( viewport.x + viewport.width ) ||
429 screenCoordinates.y < static_cast<float>( viewport.y ) ||
430 screenCoordinates.y > static_cast<float>( viewport.y + viewport.height ) )
432 // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
436 float nearClippingPlane, farClippingPlane;
437 GetCameraClippingPlane( renderTask, nearClippingPlane, farClippingPlane );
439 // Determine the layer depth of the source actor
440 Actor* sourceActor( renderTask.GetSourceActor() );
443 Dali::Layer layer( sourceActor->GetLayer() );
446 const uint32_t sourceActorDepth( layer.GetProperty< bool >( Dali::Layer::Property::DEPTH ) );
448 CameraActor* cameraActor = renderTask.GetCameraActor();
449 bool pickingPossible = cameraActor->BuildPickingRay(
453 results.rayDirection );
454 if( !pickingPossible )
459 // Hit test starting with the top layer, working towards the bottom layer.
461 bool overlayHit = false;
462 bool layerConsumesHit = false;
464 for( int32_t i = layers.GetLayerCount() - 1; i >= 0 && !( hit.actor ); --i )
466 Layer* layer( layers.GetLayer( i ) );
469 // Ensure layer is touchable (also checks whether ancestors are also touchable)
470 if( IsActuallyHittable( *layer, screenCoordinates, sceneSize, hitCheck ) )
472 // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
473 if( sourceActorDepth == static_cast<uint32_t>( i ) )
475 // Recursively hit test the source actor & children, without crossing into other layers.
476 hit = HitTestWithinLayer( *sourceActor,
480 results.rayDirection,
485 layer->GetBehavior() == Dali::Layer::LAYER_3D,
490 else if( IsWithinSourceActors( *sourceActor, *layer ) )
492 // Recursively hit test all the actors, without crossing into other layers.
493 hit = HitTestWithinLayer( *layer,
497 results.rayDirection,
502 layer->GetBehavior() == Dali::Layer::LAYER_3D,
508 // If this layer is set to consume the hit, then do not check any layers behind it
509 if( hitCheck.DoesLayerConsumeHit( layer ) )
511 layerConsumesHit = true;
519 results.renderTask = RenderTaskPtr( &renderTask );
520 results.actor = Dali::Actor( hit.actor );
521 results.actorCoordinates = hit.hitPosition;
523 return true; // Success
526 if( layerConsumesHit )
528 return true; // Also success if layer is consuming the hit
537 * Iterate through the RenderTaskList and perform hit testing.
539 * @param[in] sceneSize The scene size the tests will be performed in
540 * @param[in] layers The list of layers to test
541 * @param[in] taskList The list of render tasks
542 * @param[out] results Ray information calculated by the camera
543 * @param[in] hitCheck The hit testing interface object to use
544 * @param[in] onScreen True to test on-screen, false to test off-screen
545 * @return True if we have a hit, false otherwise
547 bool HitTestRenderTaskList( const Vector2& sceneSize,
549 RenderTaskList& taskList,
550 const Vector2& screenCoordinates,
552 HitTestInterface& hitCheck,
555 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
556 RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
557 const auto& exclusives = taskList.GetExclusivesList();
560 for( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
562 RenderTask& renderTask = *iter->Get();
563 const bool isOffscreenRenderTask = renderTask.GetFrameBuffer();
564 if( (onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask) )
570 if( HitTestRenderTask( exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest ) )
572 // Return true when an actor is hit (or layer in our render-task consumes the hit)
573 return true; // don't bother checking off screen tasks
581 * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
583 * @param[in] sceneSize The scene size the tests will be performed in
584 * @param[in] layers The list of layers to test
585 * @param[in] taskList The list of render tasks
586 * @param[out] results Ray information calculated by the camera
587 * @param[in] hitCheck The hit testing interface object to use
588 * @param[in] onScreen True to test on-screen, false to test off-screen
589 * @return True if we have a hit, false otherwise
591 bool HitTestForEachRenderTask( const Vector2& sceneSize,
593 RenderTaskList& taskList,
594 const Vector2& screenCoordinates,
596 HitTestInterface& hitCheck )
600 // Check on-screen tasks before off-screen ones.
601 // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
602 if( HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, true ) ||
603 HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, false ) )
612 } // unnamed namespace
614 HitTestInterface::~HitTestInterface() = default;
616 bool HitTest( const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
618 bool wasHit( false );
619 // Hit-test the regular on-scene actors
620 Results hitTestResults;
621 HitTestFunctionWrapper hitTestFunctionWrapper( func );
622 if( HitTestForEachRenderTask( sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
624 results.actor = hitTestResults.actor;
625 results.actorCoordinates = hitTestResults.actorCoordinates;
631 bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
633 bool wasHit( false );
635 // Hit-test the regular on-scene actors
638 wasHit = HitTestForEachRenderTask( sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface );
643 bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results )
645 ActorTouchableCheck actorTouchableCheck;
646 return HitTest( sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck );
649 } // namespace HitTestAlgorithm
651 } // namespace Internal