DALI_TEST_CHECK( results.actor == actor[1]);
DALI_TEST_EQUALS( screenCoordinates - position, results.actorCoordinates, 0.1f, TEST_LOCATION );
-
screenCoordinates.x = 120.f;
screenCoordinates.y = 130.f;
results.actor = Actor();
results.actorCoordinates = Vector2::ZERO;
Dali::HitTestAlgorithm::HitTest( renderTask[0], screenCoordinates, results, IsActorHittableFunction );
- DALI_TEST_CHECK( results.actor == actor[1] );
- DALI_TEST_EQUALS( screenCoordinates - position, results.actorCoordinates, 0.1f, TEST_LOCATION );
+ DALI_TEST_CHECK( !results.actor );
+ DALI_TEST_EQUALS( Vector2::ZERO, results.actorCoordinates, 0.1f, TEST_LOCATION );
results.actor = Actor();
results.actorCoordinates = Vector2::ZERO;
namespace
{
bool gTouchCallBackCalled=false;
-bool gTouchCallBack2Called=false;
bool gHoverCallBackCalled=false;
/**
app.Render(1);
gTouchCallBackCalled = false;
- gTouchCallBack2Called = false;
// simulate a touch event
Dali::TouchPoint point( 0, TouchPoint::Down, 25.0f, 25.0f );
END_TEST;
}
-static bool TestCallback2(Actor actor, const TouchEvent& event)
-{
- gTouchCallBack2Called = true;
- return false;
- END_TEST;
-}
-
static bool TestCallback3(Actor actor, const HoverEvent& event)
{
gHoverCallBackCalled = true;
END_TEST;
}
-
-int UtcDaliActorSetDrawModeOverlayHitTest(void)
-{
- TestApplication app;
- tet_infoline(" UtcDaliActorSetDrawModeOverlayHitTest");
-
- BufferImage imageA = BufferImage::New(16, 16);
- BufferImage imageB = BufferImage::New(16, 16);
- ImageActor a = ImageActor::New( imageA );
- ImageActor b = ImageActor::New( imageB );
-
- // Render a,b as regular non-overlays. so order will be:
- Stage::GetCurrent().Add(a);
- Stage::GetCurrent().Add(b);
-
- a.SetSize( 100.0f, 100.0f );
- b.SetSize( 100.0f, 100.0f );
-
- // position b overlapping a. (regular non-overlays)
- // hit test at point 'x'
- // --------
- // | |
- // | a |
- // | --------
- // | |x |
- // | | |
- // ----| |
- // | b |
- // | |
- // --------
- // note: b is on top, because it's Z position is higher.
- a.SetPosition(Vector3(0.0f, 0.0f, 0.0f));
- b.SetPosition(Vector3(50.0f, 50.0f, 1.0f));
-
- // connect to their touch signals
- a.TouchedSignal().Connect(TestCallback);
- b.TouchedSignal().Connect(TestCallback2);
-
- a.SetDrawMode( DrawMode::NORMAL );
- b.SetDrawMode( DrawMode::NORMAL );
- SimulateTouchForSetOverlayHitTest(app);
-
- DALI_TEST_CHECK( gTouchCallBackCalled == false );
- DALI_TEST_CHECK( gTouchCallBack2Called == true );
- // Make Actor a an overlay.
- // --------
- // | |
- // | a |
- // | |----
- // | x | |
- // | | |
- // -------- |
- // | b |
- // | |
- // --------
- // note: a is on top, because it is an overlay.
- a.SetDrawMode( DrawMode::OVERLAY );
- b.SetDrawMode( DrawMode::NORMAL );
- SimulateTouchForSetOverlayHitTest(app);
-
- DALI_TEST_CHECK( gTouchCallBackCalled == true );
- DALI_TEST_CHECK( gTouchCallBack2Called == false );
- // Make both Actors as overlays
- // --------
- // | |
- // | a |
- // | --------
- // | |x |
- // | | |
- // ----| |
- // | b |
- // | |
- // --------
- // note: b is on top, because it is the 2nd child in the hierarchy.
- a.SetDrawMode( DrawMode::OVERLAY );
- b.SetDrawMode( DrawMode::OVERLAY );
- SimulateTouchForSetOverlayHitTest(app);
-
- DALI_TEST_CHECK( gTouchCallBackCalled == false );
- DALI_TEST_CHECK( gTouchCallBack2Called == true );
- END_TEST;
-}
-
int UtcDaliActorGetCurrentWorldMatrix(void)
{
TestApplication app;
Stage stage ( Stage::GetCurrent() );
Vector2 stageSize ( stage.GetSize() );
- Actor parent = ImageActor::New();
+ ImageActor parent = ImageActor::New();
parent.SetSize( 100.0f, 100.0f );
parent.SetAnchorPoint(AnchorPoint::TOP_LEFT);
stage.Add(parent);
- Actor actor = ImageActor::New();
+ ImageActor actor = ImageActor::New();
actor.SetSize( 100.0f, 100.0f );
actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+ actor.SetDepthIndex( 1 );
parent.Add(actor);
// Render and notify
Stage stage ( Stage::GetCurrent() );
Vector2 stageSize ( stage.GetSize() );
- Actor parent = ImageActor::New();
+ ImageActor parent = ImageActor::New();
parent.SetSize(100.0f, 100.0f);
parent.SetAnchorPoint(AnchorPoint::TOP_LEFT);
stage.Add(parent);
- Actor actor = ImageActor::New();
+ ImageActor actor = ImageActor::New();
actor.SetSize(100.0f, 100.0f);
actor.SetAnchorPoint(AnchorPoint::TOP_LEFT);
+ actor.SetDepthIndex( 1 );
parent.Add(actor);
// Render and notify
void ImageActor::SetSortModifier(float modifier)
{
- mImageAttachment->SetSortModifier(modifier);
+ mImageAttachment->SetSortModifier( modifier );
}
float ImageActor::GetSortModifier() const
return mImageAttachment->GetSortModifier();
}
+void ImageActor::SetDepthIndex( int depthIndex )
+{
+ mImageAttachment->SetSortModifier( depthIndex );
+}
+
+int ImageActor::GetDepthIndex() const
+{
+ return static_cast< int >( GetSortModifier() );
+}
+
void ImageActor::SetCullFace(CullFaceMode mode)
{
mImageAttachment->SetCullFace( mode );
float GetSortModifier() const;
/**
+ * @copydoc Dali::RenderableActor::SetDepthIndex()
+ */
+ void SetDepthIndex( int depthIndex );
+
+ /**
+ * @copydoc Dali::RenderableActor::GetDepthIndex()
+ */
+ int GetDepthIndex() const;
+
+ /**
* @copydoc Dali::RenderableActor::SetCullFace()
*/
void SetCullFace(CullFaceMode mode);
#include <dali/integration-api/debug.h>
#include <dali/internal/event/actors/actor-impl.h>
#include <dali/internal/event/actors/camera-actor-impl.h>
+#include <dali/internal/event/actors/image-actor-impl.h>
#include <dali/internal/event/actors/layer-impl.h>
#include <dali/internal/event/actors/layer-list.h>
#include <dali/internal/event/actors/renderable-actor-impl.h>
x( 0 ),
y( 0 ),
distance( std::numeric_limits<float>::max() ),
- overlay( false )
+ depth( std::numeric_limits<int>::min() )
{
-
}
Actor *actor; ///< the actor hit. (if actor hit, then initialised)
float x; ///< x position of hit (only valid if actor valid)
float y; ///< y position of hit (only valid if actor valid)
float distance; ///< distance from ray origin to hit actor
- bool overlay; ///< true if the hit actor is an overlay
+ int depth; ///< depth index of this actor
};
};
/**
+ * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
+ */
+bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
+ const RenderTask& renderTask,
+ const Vector< RenderTaskList::Exclusive >& exclusives )
+
+{
+ if ( exclusives.Size() )
+ {
+ for ( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
+ {
+ if ( exclusiveIt->renderTaskPtr != &renderTask )
+ {
+ if ( exclusiveIt->actorPtr == &actor )
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
* Recursively hit test all the actors, without crossing into other layers.
* This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
* Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
* of touch vector). The closest Hit-Tested Actor is that which is returned.
* Exceptions to this rule are:
- * - If the Actor is an overlay then it is considered closer than all previous
- * overlays encountered in the hit test traversal.
* - When comparing against renderable parents, if Actor is the same distance
* or closer than it's renderable parent, then it takes priority.
*/
HitActor HitTestWithinLayer( Actor& actor,
+ const RenderTask& renderTask,
+ const Vector< RenderTaskList::Exclusive >& exclusives,
const Vector4& rayOrigin,
const Vector4& rayDir,
- bool worldOverlay,
float& nearClippingPlane,
float& farClippingPlane,
HitTestInterface& hitCheck,
bool& stencilHit,
bool parentIsStencil )
{
- worldOverlay |= actor.IsOverlay();
-
HitActor hit;
+ if ( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
+ {
+ return hit;
+ }
+
// Children should inherit the stencil draw mode
bool isStencil = parentIsStencil;
hit.x = hitPointLocal.x;
hit.y = hitPointLocal.y;
hit.distance = distance;
- hit.overlay = worldOverlay;
+
+ // Is this actor an Image Actor or contains a renderer?
+ if ( ImageActor* imageActor = dynamic_cast< ImageActor* >( &actor ) )
+ {
+ hit.depth = imageActor->GetDepthIndex();
+ }
+ else
+ {
+ if ( actor.GetRendererCount() )
+ {
+ hit.depth = actor.GetRendererAt( 0 ).GetCurrentDepthIndex();
+ }
+ else
+ {
+ hit.depth = 0;
+ }
+ }
}
}
}
if( actor.GetChildCount() > 0 )
{
childHit.distance = std::numeric_limits<float>::max();
+ childHit.depth = std::numeric_limits<int>::min();
ActorContainer& children = actor.GetChildrenInternal();
// Hit test ALL children and calculate their distance.
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
( isStencil || hitCheck.DescendActorHierarchy( &GetImplementation( *iter ) ) ) ) // We are a stencil OR we can descend into child hierarchy
{
- HitActor currentHit( HitTestWithinLayer( GetImplementation(*iter), rayOrigin, rayDir, worldOverlay, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, isStencil ) );
-
- // If Current child is an overlay, then it takes priority.
- // If it is not an overlay, and the previously hit sibling is also not an overlay, then closest takes priority.
- // (last overlay sibling has priority as is rendered on top)
- if ( currentHit.distance >= 0.f && (currentHit.overlay || (!childHit.overlay && currentHit.distance < childHit.distance) ) )
+ HitActor currentHit( HitTestWithinLayer( GetImplementation(*iter),
+ renderTask,
+ exclusives,
+ rayOrigin,
+ rayDir,
+ nearClippingPlane,
+ farClippingPlane,
+ hitCheck,
+ stencilOnLayer,
+ stencilHit,
+ isStencil ) );
+
+ bool updateChildHit = false;
+ // If our ray casting hit, then check then if the hit actor's depth is greater that the favorite, it will be preferred
+ if ( currentHit.distance >= 0.0f )
{
- if ( !parentIsRenderable )
+ if ( currentHit.depth > childHit.depth )
{
- // If our parent is not renderable, then child should be hit regardless of distance.
- childHit = currentHit;
+ updateChildHit = true;
}
- else if ( currentHit.overlay || (!hit.overlay && currentHit.distance <= hit.distance) )
+
+ // If the hit actor's depth is equal to current favorite, then we check the distance and prefer the closer
+ else if ( currentHit.depth == childHit.depth )
{
- // If our parent is renderable, then child should only be hit if it is an overlay, or if it is closer than a non-overlay.
- // (child overlay has priority as is rendered on top of it's parent)
- childHit = currentHit;
+ if ( currentHit.distance < childHit.distance )
+ {
+ updateChildHit = true;
+ }
}
}
+
+ if ( updateChildHit )
+ {
+ if( !parentIsRenderable || currentHit.depth > hit.depth ||
+ ( currentHit.depth == hit.depth && currentHit.distance < hit.distance ) )
+ {
+ childHit = currentHit;
+ }
+ }
}
}
}
/**
* Hit test a RenderTask
*/
-bool HitTestRenderTask( LayerList& layers,
+bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
+ LayerList& layers,
RenderTask& renderTask,
Vector2 screenCoordinates,
Results& results,
for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
{
Layer* layer( layers.GetLayer(i) );
-
HitActor previousHit = hit;
stencilOnLayer = false;
stencilHit = false;
if ( sourceActorDepth == static_cast<unsigned int>(i) )
{
// Recursively hit test the source actor & children, without crossing into other layers.
- hit = HitTestWithinLayer( *sourceActor, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
+ hit = HitTestWithinLayer( *sourceActor,
+ renderTask,
+ exclusives,
+ results.rayOrigin,
+ results.rayDirection,
+ nearClippingPlane,
+ farClippingPlane,
+ hitCheck,
+ stencilOnLayer,
+ stencilHit,
+ false );
}
else if ( IsWithinSourceActors( *sourceActor, *layer ) )
{
// Recursively hit test all the actors, without crossing into other layers.
- hit = HitTestWithinLayer( *layer, results.rayOrigin, results.rayDirection, false, nearClippingPlane, farClippingPlane, hitCheck, stencilOnLayer, stencilHit, false );
+ hit = HitTestWithinLayer( *layer,
+ renderTask,
+ exclusives,
+ results.rayOrigin,
+ results.rayDirection,
+ nearClippingPlane,
+ farClippingPlane,
+ hitCheck,
+ stencilOnLayer,
+ stencilHit,
+ false );
}
// If a stencil on this layer hasn't been hit, then discard hit results for this layer if our current hit actor is renderable
RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
+ const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
+
// Check onscreen tasks before offscreen ones, hit test order should be reverse of draw order (see ProcessRenderTasks() where offscreen tasks are drawn first).
// on screen
}
}
- if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
+ if ( HitTestRenderTask( exclusives, layers, renderTask, screenCoordinates, results, hitCheck ) )
{
// Return true when an actor is hit (or layer in our render-task consumes the hit)
return true; // don't bother checking off screen tasks
continue;
}
- if ( HitTestRenderTask( layers, renderTask, screenCoordinates, results, hitCheck ) )
+ if ( HitTestRenderTask( exclusives, layers, renderTask, screenCoordinates, results, hitCheck ) )
{
// Return true when an actor is hit (or a layer in our render-task consumes the hit)
return true;
bool wasHit( false );
Results hitTestResults;
+ const Vector< RenderTaskList::Exclusive >& exclusives = Stage::GetCurrent()->GetRenderTaskList().GetExclusivesList();
HitTestFunctionWrapper hitTestFunctionWrapper( func );
- if ( HitTestRenderTask( stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
+ if ( HitTestRenderTask( exclusives, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
{
results.actor = hitTestResults.actor;
results.actorCoordinates = hitTestResults.actorCoordinates;
#include <dali/internal/event/common/stage-impl.h>
#include <dali/internal/event/images/frame-buffer-image-impl.h>
#include <dali/internal/update/nodes/node.h>
+#include <dali/internal/event/render-tasks/render-task-list-impl.h>
#include <dali/internal/update/render-tasks/scene-graph-render-task.h>
#if defined(DEBUG_ENABLED)
void RenderTask::SetSourceActor( Actor* actor )
{
+ const Stage* stage = Stage::GetCurrent();
+ if ( stage )
+ {
+ stage->GetRenderTaskList().SetExclusive( this, mExclusive );
+ }
mSourceConnector.SetActor( actor );
}
{
mExclusive = exclusive;
+ const Stage* stage = Stage::GetCurrent();
+ if ( stage )
+ {
+ stage->GetRenderTaskList().SetExclusive( this, exclusive );
+ }
+
if ( mSceneObject )
{
// mSceneObject is being used in a separate thread; queue a message to set the value
{
if ( *iter == task )
{
+ RenderTask& taskImpl = GetImplementation( task );
if ( mSceneObject )
{
- RenderTask& taskImpl = GetImplementation( task );
-
SceneGraph::RenderTask* sceneObject = taskImpl.GetRenderTaskSceneObject();
DALI_ASSERT_DEBUG( NULL != sceneObject );
}
mTasks.erase( iter );
+
+ for ( Vector< Exclusive >::Iterator exclusiveIt = mExclusives.Begin(); exclusiveIt != mExclusives.End(); ++exclusiveIt )
+ {
+ if ( exclusiveIt->renderTaskPtr == &taskImpl )
+ {
+ mExclusives.Erase( exclusiveIt );
+ break;
+ }
+ }
break; // we're finished
}
}
return mTasks[index];
}
+void RenderTaskList::SetExclusive( RenderTask* task, bool exclusive )
+{
+ // Check to see if this rendertask has an entry?
+ for ( Vector< Exclusive >::Iterator exclusiveIt = mExclusives.Begin(); exclusiveIt != mExclusives.End(); ++exclusiveIt )
+ {
+ if ( exclusiveIt->renderTaskPtr == task )
+ {
+ if ( !exclusive )
+ {
+ mExclusives.Erase( exclusiveIt );
+ break;
+ }
+ else
+ {
+ exclusiveIt->actorPtr = task->GetSourceActor();
+ exclusive = false;
+ break;
+ }
+ }
+ }
+ if ( exclusive )
+ {
+ Exclusive exclusiveSlot;
+ exclusiveSlot.renderTaskPtr = task;
+ exclusiveSlot.actorPtr = task->GetSourceActor();
+ mExclusives.PushBack( exclusiveSlot );
+ }
+}
+
RenderTaskList::RenderTaskList( EventThreadServices& eventThreadServices, RenderTaskDefaults& defaults, bool systemLevel )
: mEventThreadServices( eventThreadServices ),
mDefaults( defaults ),
class EventThreadServices;
class RenderTaskDefaults;
+class Actor;
namespace SceneGraph
{
typedef std::vector< Dali::RenderTask > RenderTaskContainer;
+ struct Exclusive
+ {
+ RenderTask* renderTaskPtr; ///< Pointer for comparison with current rendertask.
+ Actor* actorPtr; ///< Pointer for comparison with current actor.
+ };
+
/**
* Create a RenderTaskList.
* @param[in] eventServices Used for sending message to the scene graph.
}
/**
+ * @brief Mark a rendertask as having exclusive access to its source actor.
+ *
+ * @param[in] task Pointer to the rendertask.
+ * @param[in] exclusive If a rendertask is to have exclusive acesss to its source actor.
+ */
+ void SetExclusive( RenderTask* task, bool exclusive );
+
+ /**
+ * @brief Return the list of rendertasks that exclusively own their source actor.
+ *
+ * @return [description]
+ */
+ const Vector< Exclusive >& GetExclusivesList() const
+ {
+ return mExclusives;
+ }
+
+ /**
* Provide notification signals for a "Finished" render task.
* This method should be called in the event-thread
* Queue NotifyFinishedMessage() from update-thread
SceneGraph::RenderTaskList* mSceneObject; ///< Raw-pointer to the scene-graph object; not owned.
- RenderTaskContainer mTasks; ///< Reference counted render-tasks
+ RenderTaskContainer mTasks; ///< Reference counted render-tasks
+ Vector< Exclusive > mExclusives; ///< List of rendertasks with exclusively owned source actors.
};
} // namespace Internal
return GetImplementation(*this).GetSortModifier();
}
+void ImageActor::SetDepthIndex( int depthIndex )
+{
+ GetImplementation(*this).SetDepthIndex( depthIndex );
+}
+
+int ImageActor::GetDepthIndex() const
+{
+ return GetImplementation(*this).GetDepthIndex();
+}
+
void ImageActor::SetCullFace(const CullFaceMode mode)
{
GetImplementation(*this).SetCullFace(mode);
float GetSortModifier() const;
/**
+ * @brief Sets the depth index within a layer for the depth sort algorithm.
+ *
+ * This is intended to replace SetSortModifier, which will be deprecated and remains currently for backwards compatibility.
+ *
+ * @param[in] depthIndex The depth index to use for sorting within a layer.
+ */
+ void SetDepthIndex( int depthIndex );
+
+ /**
+ * @brief Retrieves the depth index within a layer.
+ *
+ * This will replace GetSortModifier in the same manner as SetDepthIndex.
+ *
+ * @return The depth index used for sorting within a layer.
+ */
+ int GetDepthIndex() const;
+
+ /**
* @brief Set the face-culling mode for this actor.
*
* @param[in] mode The culling mode.