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