2 * Copyright (c) 2014 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
57 distance( std::numeric_limits<float>::max() ),
58 depth( std::numeric_limits<int>::min() )
62 Actor *actor; ///< the actor hit. (if actor hit, then initialised)
63 float x; ///< x position of hit (only valid if actor valid)
64 float y; ///< y position of hit (only valid if actor valid)
65 float distance; ///< distance from ray origin to hit actor
66 int depth; ///< depth index of this actor
71 * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
73 struct HitTestFunctionWrapper : public HitTestInterface
78 * @param[in] func HitTestFunction to call with an Actor handle.
80 HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
85 virtual bool IsActorHittable( Actor* actor )
87 return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
90 virtual bool DescendActorHierarchy( Actor* actor )
92 return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
95 virtual bool DoesLayerConsumeHit( Layer* layer )
97 // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
98 // where the caller may want to check for something completely different.
99 // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
103 Dali::HitTestAlgorithm::HitTestFunction mFunc;
107 * Used in the hit-test algorithm to check whether the actor is touchable.
108 * It is used by the touch event processor.
110 struct ActorTouchableCheck : public HitTestInterface
112 virtual bool IsActorHittable( Actor* actor )
114 return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
115 actor->IsHittable(); // Is actor sensitive, visible and on the scene?
118 virtual bool DescendActorHierarchy( Actor* actor )
120 return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
121 actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
124 virtual bool DoesLayerConsumeHit( Layer* layer )
126 return layer->IsTouchConsumed();
131 * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
133 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
134 const RenderTask& renderTask,
135 const Vector< RenderTaskList::Exclusive >& exclusives )
138 if ( exclusives.Size() )
140 for ( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
142 if ( exclusiveIt->renderTaskPtr != &renderTask )
144 if ( exclusiveIt->actorPtr == &actor )
155 * Recursively hit test all the actors, without crossing into other layers.
156 * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
157 * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
158 * of touch vector). The closest Hit-Tested Actor is that which is returned.
159 * Exceptions to this rule are:
160 * - When comparing against renderable parents, if Actor is the same distance
161 * or closer than it's renderable parent, then it takes priority.
163 HitActor HitTestWithinLayer( Actor& actor,
164 const RenderTask& renderTask,
165 const Vector< RenderTaskList::Exclusive >& exclusives,
166 const Vector4& rayOrigin,
167 const Vector4& rayDir,
168 float& nearClippingPlane,
169 float& farClippingPlane,
170 HitTestInterface& hitCheck,
171 bool& stencilOnLayer,
174 bool parentIsStencil,
179 if ( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
184 // Children should inherit the stencil draw mode
185 bool isStencil = parentIsStencil;
187 if ( actor.GetDrawMode() == DrawMode::STENCIL && actor.IsVisible() )
190 stencilOnLayer = true;
193 // If we are a stencil or hittable...
194 if ( isStencil || hitCheck.IsActorHittable( &actor ) )
196 Vector3 size( actor.GetCurrentSize() );
198 if ( size.x > 0.0f && size.y > 0.0f && // Ensure the actor has a valid size.
199 actor.RaySphereTest( rayOrigin, rayDir ) ) // Perform quicker ray sphere test to see if our ray is close to the actor.
201 Vector4 hitPointLocal;
204 // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
205 if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
207 if( distance >= nearClippingPlane && distance <= farClippingPlane )
209 // If the hit has happened on a stencil then register, but don't record as hit result
216 if( overlayHit && !actor.IsOverlay() )
218 //If we have already hit an overlay and current actor is not an overlay
219 //ignore current actor
223 if( actor.IsOverlay() )
229 hit.x = hitPointLocal.x;
230 hit.y = hitPointLocal.y;
231 hit.distance = distance;
232 hit.depth = actor.GetHierarchyDepth() * Dali::Layer::TREE_DEPTH_MULTIPLIER;
234 if ( actor.GetRendererCount() > 0 )
236 //Get renderer with maximum depth
237 int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
238 for( unsigned int i(1); i<actor.GetRendererCount(); ++i)
240 int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
241 if( depth > rendererMaxDepth )
243 rendererMaxDepth = depth;
246 hit.depth += rendererMaxDepth;
255 // If we are a stencil (or a child of a stencil) and we have already ascertained that the stencil has been hit then there is no need to hit-test the children of this stencil-actor
256 if ( isStencil && stencilHit )
261 // Find a child hit, until we run out of actors in the current layer.
263 if( actor.GetChildCount() > 0 )
265 childHit.distance = std::numeric_limits<float>::max();
266 childHit.depth = std::numeric_limits<int>::min();
267 ActorContainer& children = actor.GetChildrenInternal();
269 // Hit test ALL children and calculate their distance.
270 bool parentIsRenderable = actor.IsRenderable();
272 for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
274 // Descend tree only if...
275 if ( !(*iter)->IsLayer() && // Child is NOT a layer, hit testing current layer only or Child is not a layer and we've inherited the stencil draw mode
276 ( isStencil || hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We are a stencil OR we can descend into child hierarchy
278 HitActor currentHit( HitTestWithinLayer( (*iter->Get()),
292 bool updateChildHit = false;
293 if ( currentHit.distance >= 0.0f )
297 updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
298 ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
302 updateChildHit = currentHit.depth >= childHit.depth;
306 if ( updateChildHit )
308 if( !parentIsRenderable || currentHit.depth > hit.depth ||
309 ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
311 childHit = currentHit;
317 return ( childHit.actor ) ? childHit : hit;
321 * Return true if actor is sourceActor or a descendent of sourceActor
323 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
325 if ( &sourceActor == &actor )
331 Actor* parent = actor.GetParent();
334 return IsWithinSourceActors( sourceActor, *parent );
338 // Not within source actors
343 * Returns true if the layer and all of the layer's parents are visible and sensitive.
345 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
347 bool hittable( true );
349 if(layer.IsClipping())
351 ClippingBox box = layer.GetClippingBox();
353 if( screenCoordinates.x < box.x ||
354 screenCoordinates.x > box.x + box.width ||
355 screenCoordinates.y < stageSize.y - (box.y + box.height) ||
356 screenCoordinates.y > stageSize.y - box.y)
358 // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
365 Actor* actor( &layer );
367 // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
368 while ( actor && hittable )
370 if ( ! hitCheck.DescendActorHierarchy( actor ) )
375 actor = actor->GetParent();
383 * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
385 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
387 CameraActor* cameraActor = renderTask.GetCameraActor();
388 nearClippingPlane = cameraActor->GetNearClippingPlane();
389 farClippingPlane = cameraActor->GetFarClippingPlane();
393 * Hit test a RenderTask
395 bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
398 RenderTask& renderTask,
399 Vector2 screenCoordinates,
401 HitTestInterface& hitCheck )
403 if ( renderTask.IsHittable( screenCoordinates ) )
406 renderTask.GetViewport( viewport );
407 if( screenCoordinates.x < viewport.x ||
408 screenCoordinates.x > viewport.x + viewport.width ||
409 screenCoordinates.y < viewport.y ||
410 screenCoordinates.y > viewport.y + viewport.height )
412 // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
416 float nearClippingPlane, farClippingPlane;
417 GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
419 // Determine the layer depth of the source actor
420 Actor* sourceActor( renderTask.GetSourceActor() );
423 Dali::Layer layer( sourceActor->GetLayer() );
426 const unsigned int sourceActorDepth( layer.GetDepth() );
428 CameraActor* cameraActor = renderTask.GetCameraActor();
429 bool pickingPossible = cameraActor->BuildPickingRay(
433 results.rayDirection );
434 if( !pickingPossible )
439 // Hit test starting with the top layer, working towards the bottom layer.
441 bool stencilOnLayer = false;
442 bool stencilHit = false;
443 bool overlayHit = false;
444 bool layerConsumesHit = false;
446 const Vector2& stageSize = stage.GetSize();
448 for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
450 Layer* layer( layers.GetLayer(i) );
451 HitActor previousHit = hit;
452 stencilOnLayer = false;
456 // Ensure layer is touchable (also checks whether ancestors are also touchable)
457 if ( IsActuallyHittable ( *layer, screenCoordinates, stageSize, hitCheck ) )
459 // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
460 if ( sourceActorDepth == static_cast<unsigned int>(i) )
462 // Recursively hit test the source actor & children, without crossing into other layers.
463 hit = HitTestWithinLayer( *sourceActor,
467 results.rayDirection,
475 layer->GetBehavior() == Dali::Layer::LAYER_3D);
477 else if ( IsWithinSourceActors( *sourceActor, *layer ) )
479 // Recursively hit test all the actors, without crossing into other layers.
480 hit = HitTestWithinLayer( *layer,
484 results.rayDirection,
492 layer->GetBehavior() == Dali::Layer::LAYER_3D);
495 // If a stencil on this layer hasn't been hit, then discard hit results for this layer if our current hit actor is renderable
496 if ( stencilOnLayer && !stencilHit &&
497 hit.actor && hit.actor->IsRenderable() )
502 // If this layer is set to consume the hit, then do not check any layers behind it
503 if ( hitCheck.DoesLayerConsumeHit( layer ) )
505 layerConsumesHit = true;
512 results.renderTask = Dali::RenderTask(&renderTask);
513 results.actor = Dali::Actor(hit.actor);
514 results.actorCoordinates.x = hit.x;
515 results.actorCoordinates.y = hit.y;
516 return true; // Success
518 else if ( layerConsumesHit )
520 return true; // Also success if layer is consuming the hit
529 * Iterate through RenderTaskList and perform hit test.
531 * @return true if we have a hit, false otherwise
533 bool HitTestForEachRenderTask( Stage& stage,
535 RenderTaskList& taskList,
536 const Vector2& screenCoordinates,
538 HitTestInterface& hitCheck )
540 RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
541 RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
543 const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
545 // Check onscreen tasks before offscreen ones, hit test order should be reverse of draw order (see ProcessRenderTasks() where offscreen tasks are drawn first).
548 for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
550 RenderTask& renderTask = GetImplementation( *iter );
551 Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
553 // Note that if frameBufferImage is NULL we are using the default (on screen) render target
556 ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
566 if ( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
568 // Return true when an actor is hit (or layer in our render-task consumes the hit)
569 return true; // don't bother checking off screen tasks
574 for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
576 RenderTask& renderTask = GetImplementation( *iter );
577 Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
579 // Note that if frameBufferImage is NULL we are using the default (on screen) render target
582 ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
591 if ( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
593 // Return true when an actor is hit (or a layer in our render-task consumes the hit)
601 } // unnamed namespace
603 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
605 bool wasHit( false );
606 // Hit-test the regular on-stage actors
607 RenderTaskList& taskList = stage.GetRenderTaskList();
608 LayerList& layerList = stage.GetLayerList();
610 Results hitTestResults;
611 HitTestFunctionWrapper hitTestFunctionWrapper( func );
612 if ( HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
614 results.actor = hitTestResults.actor;
615 results.actorCoordinates = hitTestResults.actorCoordinates;
621 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
623 bool wasHit( false );
625 // Hit-test the system-overlay actors first
626 SystemOverlay* systemOverlay = stage.GetSystemOverlayInternal();
630 RenderTaskList& overlayTaskList = systemOverlay->GetOverlayRenderTasks();
631 LayerList& overlayLayerList = systemOverlay->GetLayerList();
633 wasHit = HitTestForEachRenderTask( stage, overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
636 // Hit-test the regular on-stage actors
639 RenderTaskList& taskList = stage.GetRenderTaskList();
640 LayerList& layerList = stage.GetLayerList();
642 wasHit = HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, results, hitTestInterface );
647 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
649 ActorTouchableCheck actorTouchableCheck;
650 return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
653 bool HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
654 Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
656 bool wasHit( false );
657 Results hitTestResults;
659 const Vector< RenderTaskList::Exclusive >& exclusives = stage.GetRenderTaskList().GetExclusivesList();
660 HitTestFunctionWrapper hitTestFunctionWrapper( func );
661 if ( HitTestRenderTask( exclusives, stage, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
663 results.actor = hitTestResults.actor;
664 results.actorCoordinates = hitTestResults.actorCoordinates;
670 } // namespace HitTestAlgorithm
672 } // namespace Internal