/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
return false;
}
+ bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
+ {
+ return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
+ }
+
Dali::HitTestAlgorithm::HitTestFunction mFunc;
};
{
bool IsActorHittable(Actor* actor) override
{
- return (actor->GetTouchRequired() || actor->IsTouchFocusable()) && // Does the Application or derived actor type require a touch event? or focusable by touch?
- actor->IsHittable(); // Is actor sensitive, visible and on the scene?
+ return (actor->GetTouchRequired() || actor->GetInterceptTouchRequired() || actor->IsTouchFocusable()) && // Does the Application or derived actor type require a touch event or a intercept touch event? or focusable by touch?
+ actor->IsHittable(); // Is actor sensitive, visible and on the scene?
}
bool DescendActorHierarchy(Actor* actor) override
{
return layer->IsTouchConsumed();
}
+
+ bool ActorRequiresHitResultCheck(Actor* actor, Integration::Point point, Vector2 hitPointLocal, uint32_t timeStamp) override
+ {
+ if(point.GetState() != PointState::STARTED && actor->IsAllowedOnlyOwnTouch() && ownActor != actor)
+ {
+ return false;
+ }
+ return actor->EmitHitTestResultSignal(point, hitPointLocal, timeStamp);
+ }
+
+ void SetOwnActor(const Actor* actor)
+ {
+ ownActor = actor;
+ }
+ const Actor* ownActor;
};
/**
}
/**
- * 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:
- * - When comparing against renderable parents, if Actor is the same distance
- * or closer than it's renderable parent, then it takes priority.
+ * Hit tests the given actor and updates the in/out variables appropriately
*/
-HitActor HitTestWithinLayer(Actor& actor,
- const RenderTask& renderTask,
- const RenderTaskList::ExclusivesContainer& exclusives,
- const Vector4& rayOrigin,
- const Vector4& rayDir,
- float& nearClippingPlane,
- float& farClippingPlane,
- HitTestInterface& hitCheck,
- bool& overlayHit,
- bool layerIs3d,
- uint32_t clippingDepth,
- uint32_t clippingBitPlaneMask,
- const RayTest& rayTest)
+void HitTestActor(const RenderTask& renderTask,
+ const Vector4& rayOrigin,
+ const Vector4& rayDir,
+ const float& nearClippingPlane,
+ const float& farClippingPlane,
+ HitTestInterface& hitCheck,
+ const RayTest& rayTest,
+ const Integration::Point& point,
+ const uint32_t eventTime,
+ bool clippingActor,
+ bool overlayedActor,
+ Actor& actor,
+ bool& overlayHit,
+ HitActor& hit)
{
- HitActor hit;
-
- if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
- {
- return hit;
- }
-
- // For clipping, regardless of whether we have hit this actor or not,
- // we increase the clipping depth if we have hit a clipping actor.
- // This is used later to ensure all nested clipped children have hit
- // all clipping actors also for them to be counted as hit.
- uint32_t newClippingDepth = clippingDepth;
- bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
- if(clippingActor)
- {
- ++newClippingDepth;
- }
-
- // If we are a clipping actor or hittable...
if(clippingActor || hitCheck.IsActorHittable(&actor))
{
Vector3 size(actor.GetCurrentSize());
// Finally, perform a more accurate ray test to see if our ray actually hits the actor.
if(rayTest.ActorTest(actor, rayOrigin, rayDir, hitPointLocal, distance))
{
- if(distance >= nearClippingPlane && distance <= farClippingPlane)
- {
- // If the hit has happened on a clipping actor, then add this clipping depth to the mask of hit clipping depths.
- // This mask shows all the actors that have been hit at different clipping depths.
- if(clippingActor)
- {
- clippingBitPlaneMask |= 1u << clippingDepth;
- }
+ // Calculate z coordinate value in Camera Space.
+ const Matrix& viewMatrix = renderTask.GetCameraActor()->GetViewMatrix();
+ const Vector4& hitDir = Vector4(rayDir.x * distance, rayDir.y * distance, rayDir.z * distance, 0.0f);
+ const float cameraDepthDistance = (viewMatrix * hitDir).z;
- if(overlayHit && !actor.IsOverlay())
+ // Check if cameraDepthDistance is between clipping plane
+ if(cameraDepthDistance >= nearClippingPlane && cameraDepthDistance <= farClippingPlane)
+ {
+ if(overlayHit && !overlayedActor)
{
// If we have already hit an overlay and current actor is not an overlay ignore current actor.
}
else
{
- if(actor.IsOverlay())
+ if(overlayedActor)
{
overlayHit = true;
}
- // At this point we have hit an actor.
- // Now perform checks for clipping.
- // Assume we have hit the actor first as if it is not clipped this would be the case.
- bool haveHitActor = true;
-
- // Check if we are performing clipping. IE. if any actors so far have clipping enabled - not necessarily this one.
- // We can do this by checking the clipping depth has a value 1 or above.
- if(newClippingDepth >= 1u)
- {
- // Now for us to count this actor as hit, we must have also hit
- // all CLIPPING actors up to this point in the hierarchy as well.
- // This information is stored in the clippingBitPlaneMask we updated above.
- // Here we calculate a comparison mask by setting all the bits up to the current depth value.
- // EG. a depth of 4 (10000 binary) = a mask of 1111 binary.
- // This allows us a fast way of comparing all bits are set up to this depth.
- // Note: If the current Actor has clipping, that is included in the depth mask too.
- uint32_t clippingDepthMask = (1u << newClippingDepth) - 1u;
-
- // The two masks must be equal to be a hit, as we are already assuming a hit
- // (for non-clipping mode) then they must be not-equal to disqualify the hit.
- if(clippingBitPlaneMask != clippingDepthMask)
- {
- haveHitActor = false;
- }
- }
-
- if(haveHitActor)
+ // If the hit actor does not want to hit, the hit-test continues.
+ if(hitCheck.ActorRequiresHitResultCheck(&actor, point, hitPointLocal, eventTime))
{
hit.actor = &actor;
hit.hitPosition = hitPointLocal;
}
}
}
+}
+
+/**
+ * When iterating through the children of an actor, this method updates the child-hit-data.
+ */
+void UpdateChildHitData(const HitActor& hit, const HitActor& currentHit, const bool layerIs3d, const bool parentIsRenderable, HitActor& childHit)
+{
+ bool updateChildHit = false;
+ if(currentHit.distance >= 0.0f)
+ {
+ if(layerIs3d)
+ {
+ updateChildHit = ((currentHit.depth > childHit.depth) ||
+ ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
+ }
+ else
+ {
+ updateChildHit = currentHit.depth >= childHit.depth;
+ }
+ }
+
+ if(updateChildHit)
+ {
+ if(!parentIsRenderable || currentHit.depth > hit.depth ||
+ (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
+ {
+ childHit = currentHit;
+ }
+ }
+}
+
+/**
+ * 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:
+ * - 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 RenderTaskList::ExclusivesContainer& exclusives,
+ const Vector4& rayOrigin,
+ const Vector4& rayDir,
+ const float& nearClippingPlane,
+ const float& farClippingPlane,
+ HitTestInterface& hitCheck,
+ const bool& overlayed,
+ bool& overlayHit,
+ bool layerIs3d,
+ const RayTest& rayTest,
+ const Integration::Point& point,
+ const uint32_t eventTime)
+{
+ HitActor hit;
+
+ if(IsActorExclusiveToAnotherRenderTask(actor, renderTask, exclusives))
+ {
+ return hit;
+ }
+
+ // For clipping, regardless of whether we have hit this actor or not.
+ // This is used later to ensure all nested clipped children have hit
+ // all clipping actors also for them to be counted as hit.
+ bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
+ bool overlayedActor = overlayed || actor.IsOverlay();
+
+ // If we are a clipping actor or hittable...
+ HitTestActor(renderTask, rayOrigin, rayDir, nearClippingPlane, farClippingPlane, hitCheck, rayTest, point, eventTime, clippingActor, overlayedActor, actor, overlayHit, hit);
+
+ // If current actor is clipping, and hit failed, We should not checkup child actors. Fast return
+ if(clippingActor && !(hit.actor))
+ {
+ return hit;
+ }
// Find a child hit, until we run out of actors in the current layer.
HitActor childHit;
nearClippingPlane,
farClippingPlane,
hitCheck,
+ overlayedActor,
overlayHit,
layerIs3d,
- newClippingDepth,
- clippingBitPlaneMask,
- rayTest));
+ rayTest,
+ point,
+ eventTime));
// Make sure the set hit actor is actually hittable. This is usually required when we have some
// clipping as we need to hit-test all actors as we descend the tree regardless of whether they
// are hittable or not.
- if(currentHit.actor && !hitCheck.IsActorHittable(currentHit.actor))
+ if(currentHit.actor && (!hitCheck.IsActorHittable(currentHit.actor)))
{
continue;
}
- bool updateChildHit = false;
- if(currentHit.distance >= 0.0f)
- {
- if(layerIs3d)
- {
- updateChildHit = ((currentHit.depth > childHit.depth) ||
- ((currentHit.depth == childHit.depth) && (currentHit.distance < childHit.distance)));
- }
- else
- {
- updateChildHit = currentHit.depth >= childHit.depth;
- }
- }
-
- if(updateChildHit)
- {
- if(!parentIsRenderable || currentHit.depth > hit.depth ||
- (layerIs3d && (currentHit.depth == hit.depth && currentHit.distance < hit.distance)))
- {
- childHit = currentHit;
- }
- }
+ UpdateChildHitData(hit, currentHit, layerIs3d, parentIsRenderable, childHit);
}
}
}
Actor* sourceActor(renderTask.GetSourceActor());
if(sourceActor)
{
- Dali::Layer layer(sourceActor->GetLayer());
- if(layer)
+ Dali::Layer sourceLayer(sourceActor->GetLayer());
+ if(sourceLayer)
{
- const uint32_t sourceActorDepth(layer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
+ const uint32_t sourceActorDepth(sourceLayer.GetProperty<bool>(Dali::Layer::Property::DEPTH));
CameraActor* cameraActor = renderTask.GetCameraActor();
bool pickingPossible = cameraActor->BuildPickingRay(
farClippingPlane,
hitCheck,
overlayHit,
+ overlayHit,
layer->GetBehavior() == Dali::Layer::LAYER_3D,
- 0u,
- 0u,
- rayTest);
+ rayTest,
+ results.point,
+ results.eventTime);
}
else if(IsWithinSourceActors(*sourceActor, *layer))
{
farClippingPlane,
hitCheck,
overlayHit,
+ overlayHit,
layer->GetBehavior() == Dali::Layer::LAYER_3D,
- 0u,
- 0u,
- rayTest);
+ rayTest,
+ results.point,
+ results.eventTime);
}
// If this layer is set to consume the hit, then do not check any layers behind it
if(hitCheck.DoesLayerConsumeHit(layer))
{
- layerConsumesHit = true;
+ // Consume the hit if this layer is same as SourceActor's layer
+ layerConsumesHit = (sourceLayer == Dali::Layer(layer));
break;
}
}
* @param[in] taskList The list of render tasks
* @param[out] results Ray information calculated by the camera
* @param[in] hitCheck The hit testing interface object to use
- * @param[in] onScreen True to test on-screen, false to test off-screen
* @return True if we have a hit, false otherwise
*/
bool HitTestRenderTaskList(const Vector2& sceneSize,
RenderTaskList& taskList,
const Vector2& screenCoordinates,
Results& results,
- HitTestInterface& hitCheck,
- bool onScreen)
+ HitTestInterface& hitCheck)
{
RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
const auto& exclusives = taskList.GetExclusivesList();
RayTest rayTest;
+ // Hit test order should be reverse of draw order
for(RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter)
{
- RenderTask& renderTask = *iter->Get();
- const bool isOffscreenRenderTask = renderTask.GetFrameBuffer();
- if((onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask))
- {
- // Skip to next task
- continue;
- }
-
+ RenderTask& renderTask = *iter->Get();
if(HitTestRenderTask(exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck, rayTest))
{
// 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
+ return true;
}
}
* @param[in] taskList The list of render tasks
* @param[out] results Ray information calculated by the camera
* @param[in] hitCheck The hit testing interface object to use
- * @param[in] onScreen True to test on-screen, false to test off-screen
* @return True if we have a hit, false otherwise
*/
bool HitTestForEachRenderTask(const Vector2& sceneSize,
{
bool result = false;
- // Check on-screen tasks before off-screen ones.
- // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
- if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, true) ||
- HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck, false))
+ if(HitTestRenderTaskList(sceneSize, layers, taskList, screenCoordinates, results, hitCheck))
{
// Found hit.
result = true;
return wasHit;
}
-bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results)
+bool HitTest(const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, const Actor* ownActor)
{
ActorTouchableCheck actorTouchableCheck;
+ actorTouchableCheck.SetOwnActor(ownActor);
return HitTest(sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck);
}