2 * Copyright (c) 2016 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/system-overlay.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <dali/public-api/math/vector2.h>
25 #include <dali/public-api/math/vector4.h>
26 #include <dali/integration-api/debug.h>
27 #include <dali/internal/event/actors/actor-impl.h>
28 #include <dali/internal/event/actors/camera-actor-impl.h>
29 #include <dali/internal/event/actors/image-actor-impl.h>
30 #include <dali/internal/event/actors/layer-impl.h>
31 #include <dali/internal/event/actors/layer-list.h>
32 #include <dali/internal/event/common/system-overlay-impl.h>
33 #include <dali/internal/event/common/stage-impl.h>
34 #include <dali/internal/event/common/projection.h>
35 #include <dali/internal/event/images/frame-buffer-image-impl.h>
36 #include <dali/internal/event/render-tasks/render-task-impl.h>
37 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
45 namespace HitTestAlgorithm
55 distance( std::numeric_limits<float>::max() ),
56 depth( std::numeric_limits<int>::min() )
60 Actor *actor; ///< The actor hit (if actor is hit, then this is initialised).
61 Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
62 float distance; ///< Distance from ray origin to hit actor.
63 int depth; ///< Depth index of this actor.
67 * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
69 struct HitTestFunctionWrapper : public HitTestInterface
74 * @param[in] func HitTestFunction to call with an Actor handle.
76 HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
81 virtual bool IsActorHittable( Actor* actor )
83 return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
86 virtual bool DescendActorHierarchy( Actor* actor )
88 return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
91 virtual bool DoesLayerConsumeHit( Layer* layer )
93 // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
94 // where the caller may want to check for something completely different.
95 // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
99 Dali::HitTestAlgorithm::HitTestFunction mFunc;
103 * Used in the hit-test algorithm to check whether the actor is touchable.
104 * It is used by the touch event processor.
106 struct ActorTouchableCheck : public HitTestInterface
108 virtual bool IsActorHittable( Actor* actor )
110 return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
111 actor->IsHittable(); // Is actor sensitive, visible and on the scene?
114 virtual bool DescendActorHierarchy( Actor* actor )
116 return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
117 actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
120 virtual bool DoesLayerConsumeHit( Layer* layer )
122 return layer->IsTouchConsumed();
127 * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
129 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
130 const RenderTask& renderTask,
131 const Vector< RenderTaskList::Exclusive >& exclusives )
134 if( exclusives.Size() )
136 for( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
138 if( ( exclusiveIt->renderTaskPtr != &renderTask ) && ( exclusiveIt->actorPtr == &actor ) )
148 * Recursively hit test all the actors, without crossing into other layers.
149 * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
150 * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
151 * of touch vector). The closest Hit-Tested Actor is that which is returned.
152 * Exceptions to this rule are:
153 * - When comparing against renderable parents, if Actor is the same distance
154 * or closer than it's renderable parent, then it takes priority.
156 HitActor HitTestWithinLayer( Actor& actor,
157 const RenderTask& renderTask,
158 const Vector< RenderTaskList::Exclusive >& exclusives,
159 const Vector4& rayOrigin,
160 const Vector4& rayDir,
161 float& nearClippingPlane,
162 float& farClippingPlane,
163 HitTestInterface& hitCheck,
166 unsigned int clippingDepth,
167 unsigned int clippingBitPlaneMask )
171 if( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
176 // For clipping, regardless of whether we have hit this actor or not,
177 // we increase the clipping depth if we have hit a clipping actor.
178 // This is used later to ensure all nested clipped children have hit
179 // all clipping actors also for them to be counted as hit.
180 unsigned int newClippingDepth = clippingDepth;
181 bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
187 // If we are a clipping actor or hittable...
188 if( clippingActor || hitCheck.IsActorHittable( &actor ) )
190 Vector3 size( actor.GetCurrentSize() );
192 // Ensure the actor has a valid size.
193 // If so, perform a quick ray sphere test to see if our ray is close to the actor.
194 if( size.x > 0.0f && size.y > 0.0f && actor.RaySphereTest( rayOrigin, rayDir ) )
196 Vector2 hitPointLocal;
199 // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
200 if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
202 if( distance >= nearClippingPlane && distance <= farClippingPlane )
204 // If the hit has happened on a clipping actor, then add this clipping depth to the mask of hit clipping depths.
205 // This mask shows all the actors that have been hit at different clipping depths.
208 clippingBitPlaneMask |= 1u << clippingDepth;
211 if( overlayHit && !actor.IsOverlay() )
213 // If we have already hit an overlay and current actor is not an overlay ignore current actor.
217 if( actor.IsOverlay() )
222 // At this point we have hit an actor.
223 // Now perform checks for clipping.
224 // Assume we have hit the actor first as if it is not clipped this would be the case.
225 bool haveHitActor = true;
227 // Check if we are performing clipping. IE. if any actors so far have clipping enabled - not necessarily this one.
228 // We can do this by checking the clipping depth has a value 1 or above.
229 if( newClippingDepth >= 1u )
231 // Now for us to count this actor as hit, we must have also hit
232 // all CLIPPING actors up to this point in the hierarchy as well.
233 // This information is stored in the clippingBitPlaneMask we updated above.
234 // Here we calculate a comparison mask by setting all the bits up to the current depth value.
235 // EG. a depth of 4 (10000 binary) = a mask of 1111 binary.
236 // This allows us a fast way of comparing all bits are set up to this depth.
237 unsigned int clippingDepthMask = ( 1u << clippingDepth ) - 1u;
239 // The two masks must be equal to be a hit, as we are already assuming a hit
240 // (for non-clipping mode) then they must be not-equal to disqualify the hit.
241 if( clippingBitPlaneMask != clippingDepthMask )
243 haveHitActor = false;
250 hit.hitPosition = hitPointLocal;
251 hit.distance = distance;
252 hit.depth = actor.GetHierarchyDepth() * Dali::Layer::TREE_DEPTH_MULTIPLIER;
254 if( actor.GetRendererCount() > 0 )
256 //Get renderer with maximum depth
257 int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
258 for( unsigned int i(1); i < actor.GetRendererCount(); ++i )
260 int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
261 if( depth > rendererMaxDepth )
263 rendererMaxDepth = depth;
266 hit.depth += rendererMaxDepth;
275 // Find a child hit, until we run out of actors in the current layer.
277 if( actor.GetChildCount() > 0 )
279 childHit.distance = std::numeric_limits<float>::max();
280 childHit.depth = std::numeric_limits<int>::min();
281 ActorContainer& children = actor.GetChildrenInternal();
283 // Hit test ALL children and calculate their distance.
284 bool parentIsRenderable = actor.IsRenderable();
286 for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
288 // Descend tree only if...
289 if ( !( *iter )->IsLayer() && // Child is NOT a layer, hit testing current layer only
290 ( hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We can descend into child hierarchy
292 HitActor currentHit( HitTestWithinLayer( ( *iter->Get() ),
303 clippingBitPlaneMask ) );
305 bool updateChildHit = false;
306 if( currentHit.distance >= 0.0f )
310 updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
311 ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
315 updateChildHit = currentHit.depth >= childHit.depth;
321 if( !parentIsRenderable || currentHit.depth > hit.depth ||
322 ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
324 childHit = currentHit;
331 return ( childHit.actor ) ? childHit : hit;
335 * Return true if actor is sourceActor or a descendent of sourceActor
337 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
339 if ( &sourceActor == &actor )
344 Actor* parent = actor.GetParent();
347 return IsWithinSourceActors( sourceActor, *parent );
350 // Not within source actors
355 * Returns true if the layer and all of the layer's parents are visible and sensitive.
357 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
359 bool hittable( true );
361 if( layer.IsClipping() )
363 ClippingBox box = layer.GetClippingBox();
365 if( screenCoordinates.x < box.x ||
366 screenCoordinates.x > box.x + box.width ||
367 screenCoordinates.y < stageSize.y - (box.y + box.height) ||
368 screenCoordinates.y > stageSize.y - box.y)
370 // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
377 Actor* actor( &layer );
379 // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
380 while( actor && hittable )
382 if( ! hitCheck.DescendActorHierarchy( actor ) )
387 actor = actor->GetParent();
395 * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
397 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
399 CameraActor* cameraActor = renderTask.GetCameraActor();
400 nearClippingPlane = cameraActor->GetNearClippingPlane();
401 farClippingPlane = cameraActor->GetFarClippingPlane();
405 * Hit test a RenderTask
407 bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
410 RenderTask& renderTask,
411 Vector2 screenCoordinates,
413 HitTestInterface& hitCheck )
415 if ( renderTask.IsHittable( screenCoordinates ) )
418 renderTask.GetViewport( viewport );
419 if( screenCoordinates.x < viewport.x ||
420 screenCoordinates.x > viewport.x + viewport.width ||
421 screenCoordinates.y < viewport.y ||
422 screenCoordinates.y > viewport.y + viewport.height )
424 // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
428 float nearClippingPlane, farClippingPlane;
429 GetCameraClippingPlane( renderTask, nearClippingPlane, farClippingPlane );
431 // Determine the layer depth of the source actor
432 Actor* sourceActor( renderTask.GetSourceActor() );
435 Dali::Layer layer( sourceActor->GetLayer() );
438 const unsigned int sourceActorDepth( layer.GetDepth() );
440 CameraActor* cameraActor = renderTask.GetCameraActor();
441 bool pickingPossible = cameraActor->BuildPickingRay(
445 results.rayDirection );
446 if( !pickingPossible )
451 // Hit test starting with the top layer, working towards the bottom layer.
453 bool overlayHit = false;
454 bool layerConsumesHit = false;
455 const Vector2& stageSize = stage.GetSize();
457 for( int i = layers.GetLayerCount() - 1; i >= 0 && !( hit.actor ); --i )
459 Layer* layer( layers.GetLayer( i ) );
462 // Ensure layer is touchable (also checks whether ancestors are also touchable)
463 if( IsActuallyHittable( *layer, screenCoordinates, stageSize, hitCheck ) )
465 // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
466 if( sourceActorDepth == static_cast<unsigned int>( i ) )
468 // Recursively hit test the source actor & children, without crossing into other layers.
469 hit = HitTestWithinLayer( *sourceActor,
473 results.rayDirection,
478 layer->GetBehavior() == Dali::Layer::LAYER_3D,
482 else if( IsWithinSourceActors( *sourceActor, *layer ) )
484 // Recursively hit test all the actors, without crossing into other layers.
485 hit = HitTestWithinLayer( *layer,
489 results.rayDirection,
494 layer->GetBehavior() == Dali::Layer::LAYER_3D,
499 // If this layer is set to consume the hit, then do not check any layers behind it
500 if( hitCheck.DoesLayerConsumeHit( layer ) )
502 layerConsumesHit = true;
510 results.renderTask = Dali::RenderTask( &renderTask );
511 results.actor = Dali::Actor( hit.actor );
512 results.actorCoordinates = hit.hitPosition;
514 return true; // Success
517 if( layerConsumesHit )
519 return true; // Also success if layer is consuming the hit
528 * Iterate through the RenderTaskList and perform hit testing.
530 * @param[in] stage The stage the tests will be performed in
531 * @param[in] layers The list of layers to test
532 * @param[in] taskList The list of render tasks
533 * @param[out] results Ray information calculated by the camera
534 * @param[in] hitCheck The hit testing interface object to use
535 * @param[in] onScreen True to test on-screen, false to test off-screen
536 * @return True if we have a hit, false otherwise
538 bool HitTestRenderTaskList( Stage& stage,
540 RenderTaskList& taskList,
541 const Vector2& screenCoordinates,
543 HitTestInterface& hitCheck,
546 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
547 RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
548 const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
550 for( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
552 RenderTask& renderTask = GetImplementation( *iter );
553 Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
555 // Note that if frameBufferImage is NULL we are using the default (on screen) render target
556 if( frameBufferImage )
558 ResourceId id = GetImplementation( frameBufferImage ).GetResourceId();
560 // Change comparison depending on if on-screen or off-screen.
561 if( onScreen ? ( 0 != id ) : ( 0 == id ) )
568 if( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
570 // Return true when an actor is hit (or layer in our render-task consumes the hit)
571 return true; // don't bother checking off screen tasks
579 * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
581 * @param[in] stage The stage the tests will be performed in
582 * @param[in] layers The list of layers to test
583 * @param[in] taskList The list of render tasks
584 * @param[out] results Ray information calculated by the camera
585 * @param[in] hitCheck The hit testing interface object to use
586 * @param[in] onScreen True to test on-screen, false to test off-screen
587 * @return True if we have a hit, false otherwise
589 bool HitTestForEachRenderTask( Stage& stage,
591 RenderTaskList& taskList,
592 const Vector2& screenCoordinates,
594 HitTestInterface& hitCheck )
598 // Check on-screen tasks before off-screen ones.
599 // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
600 if( HitTestRenderTaskList( stage, layers, taskList, screenCoordinates, results, hitCheck, true ) ||
601 HitTestRenderTaskList( stage, layers, taskList, screenCoordinates, results, hitCheck, false ) )
610 } // unnamed namespace
612 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
614 bool wasHit( false );
615 // Hit-test the regular on-stage actors
616 RenderTaskList& taskList = stage.GetRenderTaskList();
617 LayerList& layerList = stage.GetLayerList();
619 Results hitTestResults;
620 HitTestFunctionWrapper hitTestFunctionWrapper( func );
621 if( HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
623 results.actor = hitTestResults.actor;
624 results.actorCoordinates = hitTestResults.actorCoordinates;
630 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
632 bool wasHit( false );
634 // Hit-test the system-overlay actors first
635 SystemOverlay* systemOverlay = stage.GetSystemOverlayInternal();
639 RenderTaskList& overlayTaskList = systemOverlay->GetOverlayRenderTasks();
640 LayerList& overlayLayerList = systemOverlay->GetLayerList();
642 wasHit = HitTestForEachRenderTask( stage, overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
645 // Hit-test the regular on-stage actors
648 RenderTaskList& taskList = stage.GetRenderTaskList();
649 LayerList& layerList = stage.GetLayerList();
651 wasHit = HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, results, hitTestInterface );
656 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
658 ActorTouchableCheck actorTouchableCheck;
659 return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
662 bool HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
663 Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
665 bool wasHit( false );
666 Results hitTestResults;
668 const Vector< RenderTaskList::Exclusive >& exclusives = stage.GetRenderTaskList().GetExclusivesList();
669 HitTestFunctionWrapper hitTestFunctionWrapper( func );
670 if( HitTestRenderTask( exclusives, stage, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
672 results.actor = hitTestResults.actor;
673 results.actorCoordinates = hitTestResults.actorCoordinates;
680 } // namespace HitTestAlgorithm
682 } // namespace Internal