Merge "Remove SystemOverlay." into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
1 /*
2  * Copyright (c) 2018 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/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/stage-impl.h>
31 #include <dali/internal/event/common/projection.h>
32 #include <dali/internal/event/images/frame-buffer-image-impl.h>
33 #include <dali/internal/event/render-tasks/render-task-impl.h>
34 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
35
36 namespace Dali
37 {
38
39 namespace Internal
40 {
41
42 namespace HitTestAlgorithm
43 {
44
45 namespace
46 {
47
48 struct HitActor
49 {
50   HitActor()
51   : actor( NULL ),
52     distance( std::numeric_limits<float>::max() ),
53     depth( std::numeric_limits<int>::min() )
54   {
55   }
56
57   Actor *actor;        ///< The actor hit (if actor is hit, then this is initialised).
58   Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
59   float distance;      ///< Distance from ray origin to hit actor.
60   int32_t depth;       ///< Depth index of this actor.
61 };
62
63 /**
64  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
65  */
66 struct HitTestFunctionWrapper : public HitTestInterface
67 {
68   /**
69    * Constructor
70    *
71    * @param[in] func HitTestFunction to call with an Actor handle.
72    */
73   HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
74   : mFunc( func )
75   {
76   }
77
78   virtual bool IsActorHittable( Actor* actor )
79   {
80     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
81   }
82
83   virtual bool DescendActorHierarchy( Actor* actor )
84   {
85     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
86   }
87
88   virtual bool DoesLayerConsumeHit( Layer* layer )
89   {
90     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
91     // where the caller may want to check for something completely different.
92     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
93     return false;
94   }
95
96   Dali::HitTestAlgorithm::HitTestFunction mFunc;
97 };
98
99 /**
100  * Used in the hit-test algorithm to check whether the actor is touchable.
101  * It is used by the touch event processor.
102  */
103 struct ActorTouchableCheck : public HitTestInterface
104 {
105   virtual bool IsActorHittable( Actor* actor )
106   {
107     return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
108            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
109   }
110
111   virtual bool DescendActorHierarchy( Actor* actor )
112   {
113     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
114            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
115   }
116
117   virtual bool DoesLayerConsumeHit( Layer* layer )
118   {
119     return layer->IsTouchConsumed();
120   }
121 };
122
123 /**
124  * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
125  */
126 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
127                                           const RenderTask& renderTask,
128                                           const Vector< RenderTaskList::Exclusive >& exclusives )
129
130 {
131   if( exclusives.Size() )
132   {
133     for( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
134     {
135       if( ( exclusiveIt->renderTaskPtr != &renderTask ) && ( exclusiveIt->actorPtr == &actor ) )
136       {
137         return true;
138       }
139     }
140   }
141   return false;
142 }
143
144 /**
145  * Recursively hit test all the actors, without crossing into other layers.
146  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
147  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
148  * of touch vector). The closest Hit-Tested Actor is that which is returned.
149  * Exceptions to this rule are:
150  * - When comparing against renderable parents, if Actor is the same distance
151  * or closer than it's renderable parent, then it takes priority.
152  */
153 HitActor HitTestWithinLayer( Actor& actor,
154                              const RenderTask& renderTask,
155                              const Vector< RenderTaskList::Exclusive >& exclusives,
156                              const Vector4& rayOrigin,
157                              const Vector4& rayDir,
158                              float& nearClippingPlane,
159                              float& farClippingPlane,
160                              HitTestInterface& hitCheck,
161                              bool& overlayHit,
162                              bool layerIs3d,
163                              uint32_t clippingDepth,
164                              uint32_t clippingBitPlaneMask )
165 {
166   HitActor hit;
167
168   if( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
169   {
170     return hit;
171   }
172
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;
179   if( clippingActor )
180   {
181     ++newClippingDepth;
182   }
183
184   // If we are a clipping actor or hittable...
185   if( clippingActor || hitCheck.IsActorHittable( &actor ) )
186   {
187     Vector3 size( actor.GetCurrentSize() );
188
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 && actor.RaySphereTest( rayOrigin, rayDir ) )
192     {
193       Vector2 hitPointLocal;
194       float distance;
195
196       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
197       if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
198       {
199         if( distance >= nearClippingPlane && distance <= farClippingPlane )
200         {
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.
203           if( clippingActor )
204           {
205             clippingBitPlaneMask |= 1u << clippingDepth;
206           }
207
208           if( overlayHit && !actor.IsOverlay() )
209           {
210             // If we have already hit an overlay and current actor is not an overlay ignore current actor.
211           }
212           else
213           {
214             if( actor.IsOverlay() )
215             {
216               overlayHit = true;
217             }
218
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;
223
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 )
227             {
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;
236
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 )
240               {
241                 haveHitActor = false;
242               }
243             }
244
245             if( haveHitActor )
246             {
247               hit.actor = &actor;
248               hit.hitPosition = hitPointLocal;
249               hit.distance = distance;
250               hit.depth = actor.GetSortingDepth() ;
251
252               if( actor.GetRendererCount() > 0 )
253               {
254                 //Get renderer with maximum depth
255                 int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
256                 for( uint32_t i(1); i < actor.GetRendererCount(); ++i )
257                 {
258                   int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
259                   if( depth > rendererMaxDepth )
260                   {
261                     rendererMaxDepth = depth;
262                   }
263                 }
264                 hit.depth += rendererMaxDepth;
265               }
266             }
267           }
268         }
269       }
270     }
271   }
272
273   // Find a child hit, until we run out of actors in the current layer.
274   HitActor childHit;
275   if( actor.GetChildCount() > 0 )
276   {
277     childHit.distance = std::numeric_limits<float>::max();
278     childHit.depth = std::numeric_limits<int32_t>::min();
279     ActorContainer& children = actor.GetChildrenInternal();
280
281     // Hit test ALL children and calculate their distance.
282     bool parentIsRenderable = actor.IsRenderable();
283
284     for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
285     {
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
289       {
290         HitActor currentHit( HitTestWithinLayer( ( *iter->Get() ),
291                                                  renderTask,
292                                                  exclusives,
293                                                  rayOrigin,
294                                                  rayDir,
295                                                  nearClippingPlane,
296                                                  farClippingPlane,
297                                                  hitCheck,
298                                                  overlayHit,
299                                                  layerIs3d,
300                                                  newClippingDepth,
301                                                  clippingBitPlaneMask ) );
302
303         bool updateChildHit = false;
304         if( currentHit.distance >= 0.0f )
305         {
306           if( layerIs3d )
307           {
308             updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
309                                ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
310           }
311           else
312           {
313             updateChildHit = currentHit.depth >= childHit.depth;
314           }
315         }
316
317         if( updateChildHit )
318         {
319           if( !parentIsRenderable || currentHit.depth > hit.depth ||
320             ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
321           {
322             childHit = currentHit;
323           }
324         }
325       }
326     }
327   }
328
329   return ( childHit.actor ) ? childHit : hit;
330 }
331
332 /**
333  * Return true if actor is sourceActor or a descendent of sourceActor
334  */
335 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
336 {
337   if ( &sourceActor == &actor )
338   {
339     return true;
340   }
341
342   Actor* parent = actor.GetParent();
343   if ( parent )
344   {
345     return IsWithinSourceActors( sourceActor, *parent );
346   }
347
348   // Not within source actors
349   return false;
350 }
351
352 /**
353  * Returns true if the layer and all of the layer's parents are visible and sensitive.
354  */
355 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
356 {
357   bool hittable( true );
358
359   if( layer.IsClipping() )
360   {
361     ClippingBox box = layer.GetClippingBox();
362
363     if( screenCoordinates.x < static_cast<float>( box.x )||
364         screenCoordinates.x > static_cast<float>( box.x + box.width )||
365         screenCoordinates.y < stageSize.y - static_cast<float>( box.y + box.height ) ||
366         screenCoordinates.y > stageSize.y - static_cast<float>( box.y ) )
367     {
368       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
369       hittable = false;
370     }
371   }
372
373   if( hittable )
374   {
375     Actor* actor( &layer );
376
377     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
378     while( actor && hittable )
379     {
380       if( ! hitCheck.DescendActorHierarchy( actor ) )
381       {
382         hittable = false;
383         break;
384       }
385       actor = actor->GetParent();
386     }
387   }
388
389   return hittable;
390 }
391
392 /**
393  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
394  */
395 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
396 {
397   CameraActor* cameraActor = renderTask.GetCameraActor();
398   nearClippingPlane = cameraActor->GetNearClippingPlane();
399   farClippingPlane = cameraActor->GetFarClippingPlane();
400 }
401
402 /**
403  * Hit test a RenderTask
404  */
405 bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
406                         Stage& stage,
407                         LayerList& layers,
408                         RenderTask& renderTask,
409                         Vector2 screenCoordinates,
410                         Results& results,
411                         HitTestInterface& hitCheck )
412 {
413   if ( renderTask.IsHittable( screenCoordinates ) )
414   {
415     Viewport viewport;
416     renderTask.GetViewport( viewport );
417     if( screenCoordinates.x < static_cast<float>( viewport.x ) ||
418         screenCoordinates.x > static_cast<float>( viewport.x + viewport.width ) ||
419         screenCoordinates.y < static_cast<float>( viewport.y ) ||
420         screenCoordinates.y > static_cast<float>( viewport.y + viewport.height ) )
421     {
422       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
423       return false;
424     }
425
426     float nearClippingPlane, farClippingPlane;
427     GetCameraClippingPlane( renderTask, nearClippingPlane, farClippingPlane );
428
429     // Determine the layer depth of the source actor
430     Actor* sourceActor( renderTask.GetSourceActor() );
431     if( sourceActor )
432     {
433       Dali::Layer layer( sourceActor->GetLayer() );
434       if( layer )
435       {
436         const uint32_t sourceActorDepth( layer.GetDepth() );
437
438         CameraActor* cameraActor = renderTask.GetCameraActor();
439         bool pickingPossible = cameraActor->BuildPickingRay(
440             screenCoordinates,
441             viewport,
442             results.rayOrigin,
443             results.rayDirection );
444         if( !pickingPossible )
445         {
446           return false;
447         }
448
449         // Hit test starting with the top layer, working towards the bottom layer.
450         HitActor hit;
451         bool overlayHit = false;
452         bool layerConsumesHit = false;
453         const Vector2& stageSize = stage.GetSize();
454
455         for( int32_t i = layers.GetLayerCount() - 1; i >= 0 && !( hit.actor ); --i )
456         {
457           Layer* layer( layers.GetLayer( i ) );
458           overlayHit = false;
459
460           // Ensure layer is touchable (also checks whether ancestors are also touchable)
461           if( IsActuallyHittable( *layer, screenCoordinates, stageSize, hitCheck ) )
462           {
463             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
464             if( sourceActorDepth == static_cast<uint32_t>( i ) )
465             {
466               // Recursively hit test the source actor & children, without crossing into other layers.
467               hit = HitTestWithinLayer( *sourceActor,
468                                         renderTask,
469                                         exclusives,
470                                         results.rayOrigin,
471                                         results.rayDirection,
472                                         nearClippingPlane,
473                                         farClippingPlane,
474                                         hitCheck,
475                                         overlayHit,
476                                         layer->GetBehavior() == Dali::Layer::LAYER_3D,
477                                         0u,
478                                         0u );
479             }
480             else if( IsWithinSourceActors( *sourceActor, *layer ) )
481             {
482               // Recursively hit test all the actors, without crossing into other layers.
483               hit = HitTestWithinLayer( *layer,
484                                         renderTask,
485                                         exclusives,
486                                         results.rayOrigin,
487                                         results.rayDirection,
488                                         nearClippingPlane,
489                                         farClippingPlane,
490                                         hitCheck,
491                                         overlayHit,
492                                         layer->GetBehavior() == Dali::Layer::LAYER_3D,
493                                         0u,
494                                         0u );
495             }
496
497             // If this layer is set to consume the hit, then do not check any layers behind it
498             if( hitCheck.DoesLayerConsumeHit( layer ) )
499             {
500               layerConsumesHit = true;
501               break;
502             }
503           }
504         }
505
506         if( hit.actor )
507         {
508           results.renderTask = RenderTaskPtr( &renderTask );
509           results.actor = Dali::Actor( hit.actor );
510           results.actorCoordinates = hit.hitPosition;
511
512           return true; // Success
513         }
514
515         if( layerConsumesHit )
516         {
517           return true; // Also success if layer is consuming the hit
518         }
519       }
520     }
521   }
522   return false;
523 }
524
525 /**
526  * Iterate through the RenderTaskList and perform hit testing.
527  *
528  * @param[in] stage The stage the tests will be performed in
529  * @param[in] layers The list of layers to test
530  * @param[in] taskList The list of render tasks
531  * @param[out] results Ray information calculated by the camera
532  * @param[in] hitCheck The hit testing interface object to use
533  * @param[in] onScreen True to test on-screen, false to test off-screen
534  * @return True if we have a hit, false otherwise
535  */
536 bool HitTestRenderTaskList( Stage& stage,
537                             LayerList& layers,
538                             RenderTaskList& taskList,
539                             const Vector2& screenCoordinates,
540                             Results& results,
541                             HitTestInterface& hitCheck,
542                             bool onScreen )
543 {
544   RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
545   RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
546   const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
547
548   for( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
549   {
550     RenderTask& renderTask = *iter->Get();
551     bool isOffscreenRenderTask = ( renderTask.GetTargetFrameBuffer() || renderTask.GetFrameBuffer() );
552     if( (onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask) )
553     {
554       // Skip to next task
555       continue;
556     }
557
558     if( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
559     {
560       // Return true when an actor is hit (or layer in our render-task consumes the hit)
561       return true; // don't bother checking off screen tasks
562     }
563   }
564
565   return false;
566 }
567
568 /**
569  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
570  *
571  * @param[in] stage The stage the tests will be performed in
572  * @param[in] layers The list of layers to test
573  * @param[in] taskList The list of render tasks
574  * @param[out] results Ray information calculated by the camera
575  * @param[in] hitCheck The hit testing interface object to use
576  * @param[in] onScreen True to test on-screen, false to test off-screen
577  * @return True if we have a hit, false otherwise
578  */
579 bool HitTestForEachRenderTask( Stage& stage,
580                                LayerList& layers,
581                                RenderTaskList& taskList,
582                                const Vector2& screenCoordinates,
583                                Results& results,
584                                HitTestInterface& hitCheck )
585 {
586   bool result = false;
587
588   // Check on-screen tasks before off-screen ones.
589   // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
590   if( HitTestRenderTaskList( stage, layers, taskList, screenCoordinates, results, hitCheck, true  ) ||
591       HitTestRenderTaskList( stage, layers, taskList, screenCoordinates, results, hitCheck, false ) )
592   {
593     // Found hit.
594     result = true;
595   }
596
597   return result;
598 }
599
600 } // unnamed namespace
601
602 HitTestInterface::~HitTestInterface()
603 {
604 }
605
606 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
607 {
608   bool wasHit( false );
609   // Hit-test the regular on-stage actors
610   RenderTaskList& taskList = stage.GetRenderTaskList();
611   LayerList& layerList = stage.GetLayerList();
612
613   Results hitTestResults;
614   HitTestFunctionWrapper hitTestFunctionWrapper( func );
615   if( HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
616   {
617     results.actor = hitTestResults.actor;
618     results.actorCoordinates = hitTestResults.actorCoordinates;
619     wasHit = true;
620   }
621   return wasHit;
622 }
623
624 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
625 {
626   bool wasHit( false );
627
628   // Hit-test the regular on-stage actors
629   if( !wasHit )
630   {
631     RenderTaskList& taskList = stage.GetRenderTaskList();
632     LayerList& layerList = stage.GetLayerList();
633
634     wasHit = HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, results, hitTestInterface );
635   }
636   return wasHit;
637 }
638
639 bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
640 {
641   ActorTouchableCheck actorTouchableCheck;
642   return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
643 }
644
645 bool HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
646               Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
647 {
648   bool wasHit( false );
649   Results hitTestResults;
650
651   const Vector< RenderTaskList::Exclusive >& exclusives = stage.GetRenderTaskList().GetExclusivesList();
652   HitTestFunctionWrapper hitTestFunctionWrapper( func );
653   if( HitTestRenderTask( exclusives, stage, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
654   {
655     results.actor = hitTestResults.actor;
656     results.actorCoordinates = hitTestResults.actorCoordinates;
657     wasHit = true;
658   }
659   return wasHit;
660 }
661
662
663 } // namespace HitTestAlgorithm
664
665 } // namespace Internal
666
667 } // namespace Dali