Calculrate screen position with RenderTask 13/280813/16
authorEunki, Hong <eunkiki.hong@samsung.com>
Mon, 5 Sep 2022 10:14:39 +0000 (19:14 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Wed, 28 Sep 2022 08:49:12 +0000 (17:49 +0900)
Previous ScreenPosition / ScreenExtents consider
only the scene's default camera.
Now we make a new API that consider Custom camera's
View/Projection matrix.

Calculate screen position & extents is quite heavy
operation. So we need to seperate it for
simple 2D layer or 3D layer.

So, we add mLayer3DParentsCount value in actor.
It will be generated when actor is scene on,
or some layer's behaviour changed during on scene.

Change-Id: I7a6162bea84965f70800a6e7cbf2332e9933e20d
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-Actor.cpp
dali/internal/event/actors/actor-coords.cpp
dali/internal/event/actors/actor-coords.h
dali/internal/event/actors/actor-impl.cpp
dali/internal/event/actors/actor-impl.h
dali/internal/event/actors/actor-parent-impl.cpp
dali/internal/event/actors/actor-parent-impl.h
dali/internal/event/actors/layer-impl.cpp

index 33d9536..3574fba 100644 (file)
@@ -1354,10 +1354,12 @@ int UtcDaliActorCalculateScreenExtents(void)
   actor.SetProperty(Actor::Property::POSITION, Vector3(2.0f, 2.0f, 16.0f));
   actor.SetProperty(Actor::Property::SIZE, Vector3{1.0f, 1.0f, 1.0f});
 
+  application.GetScene().Add(actor);
+
   application.SendNotification();
   application.Render();
 
-  auto expectedExtent = Rect<>{-0.5f, -0.5f, 1.0f, 1.0f};
+  auto expectedExtent = Rect<>{1.5f, 1.5f, 1.0f, 1.0f};
   auto actualExtent   = DevelActor::CalculateScreenExtents(actor);
   DALI_TEST_EQUALS(expectedExtent.x, actualExtent.x, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
   DALI_TEST_EQUALS(expectedExtent.y, actualExtent.y, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
@@ -1368,6 +1370,240 @@ int UtcDaliActorCalculateScreenExtents(void)
   END_TEST;
 }
 
+int UtcDaliActorCalculateScreenExtentsInCustomCameraAndLayer3D(void)
+{
+  TestApplication    application;
+  Integration::Scene scene = application.GetScene();
+
+  // Make 3D Layer
+  Layer layer = Layer::New();
+  layer.SetProperty(Layer::Property::BEHAVIOR, Layer::Behavior::LAYER_3D);
+  layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+  scene.Add(layer);
+
+  // Build custom camera with top-view
+  CameraActor cameraActor = scene.GetRenderTaskList().GetTask(0).GetCameraActor();
+  {
+    // Default camera position at +z and looking -z axis. (orientation is [ Axis: [0, 1, 0], Angle: 180 degrees ])
+    Vector3    cameraPos    = cameraActor.GetProperty<Vector3>(Actor::Property::POSITION);
+    Quaternion cameraOrient = cameraActor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+
+    {
+      std::ostringstream oss;
+      oss << cameraPos << "\n";
+      oss << cameraOrient << "\n";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+
+    cameraActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, -cameraPos.z, 0.0f));
+    cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(90.0f), Vector3::XAXIS) * cameraOrient);
+
+    // Now, upside : -Z, leftside : -X, foward : +Y
+
+    cameraPos    = cameraActor.GetProperty<Vector3>(Actor::Property::POSITION);
+    cameraOrient = cameraActor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+    {
+      std::ostringstream oss;
+      oss << cameraPos << "\n";
+      oss << cameraOrient << "\n";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+  }
+
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(2.0f, 0.0f, 16.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3{1.0f, 0.0f, 3.0f});
+
+  layer.Add(actor);
+
+  application.SendNotification();
+  application.Render();
+
+  Vector2 sceneSize = scene.GetSize();
+
+  auto expectedExtent = Rect<>{sceneSize.x * 0.5f + 1.5f, sceneSize.y * 0.5f + 14.5f, 1.0f, 3.0f};
+  auto actualExtent   = DevelActor::CalculateScreenExtents(actor);
+  {
+    std::ostringstream oss;
+    oss << expectedExtent << "\n";
+    oss << actualExtent << "\n";
+    tet_printf("%s\n", oss.str().c_str());
+  }
+
+  DALI_TEST_EQUALS(expectedExtent.x, actualExtent.x, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.y, actualExtent.y, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.width, actualExtent.width, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.height, actualExtent.height, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliActorCalculateScreenInCustomCameraAndOffscreenLayer3D(void)
+{
+  // TODO : Need to make it works well
+  TestApplication    application;
+  Integration::Scene scene     = application.GetScene();
+  Vector2            sceneSize = scene.GetSize();
+
+  // Make 3D Layer
+  Layer layer = Layer::New();
+  layer.SetProperty(Layer::Property::BEHAVIOR, Layer::Behavior::LAYER_3D);
+  layer.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  layer.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  layer.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
+  layer.SetProperty(Actor::Property::SIZE, sceneSize);
+
+  scene.Add(layer);
+
+  // Build custom camera with top-view
+  CameraActor offscreenCameraActor = CameraActor::New(Size(TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT));
+
+  offscreenCameraActor.SetPerspectiveProjection(sceneSize);
+  offscreenCameraActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  offscreenCameraActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+
+  scene.Add(offscreenCameraActor);
+  {
+    // Default camera position at +z and looking -z axis. (orientation is [ Axis: [0, 1, 0], Angle: 180 degrees ])
+    Vector3    cameraPos    = offscreenCameraActor.GetProperty<Vector3>(Actor::Property::POSITION);
+    Quaternion cameraOrient = offscreenCameraActor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+
+    {
+      std::ostringstream oss;
+      oss << cameraPos << "\n";
+      oss << cameraOrient << "\n";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+
+    offscreenCameraActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, -cameraPos.z, 0.0f));
+    offscreenCameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(90.0f), Vector3::XAXIS) * cameraOrient);
+
+    // Now, upside : -Z, leftside : -X, foward : +Y
+
+    cameraPos    = offscreenCameraActor.GetProperty<Vector3>(Actor::Property::POSITION);
+    cameraOrient = offscreenCameraActor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+    {
+      std::ostringstream oss;
+      oss << cameraPos << "\n";
+      oss << cameraOrient << "\n";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+  }
+  Vector3 sourcePosition{2.0f, 0.0f, 16.0f};
+  Vector3 sourceSize{1.0f, 0.0f, 3.0f};
+
+  Actor sourceActor = Actor::New();
+  sourceActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  sourceActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  sourceActor.SetProperty(Actor::Property::POSITION, sourcePosition);
+  sourceActor.SetProperty(Actor::Property::SIZE, sourceSize);
+
+  layer.Add(sourceActor);
+
+  // Create framebuffer
+  unsigned int width(64);
+  unsigned int height(64);
+  Texture      texture     = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, width, height);
+  FrameBuffer  frameBuffer = FrameBuffer::New(width, height, FrameBuffer::Attachment::DEPTH_STENCIL);
+  frameBuffer.AttachColorTexture(texture);
+
+  Actor rootActor = Actor::New();
+  rootActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  rootActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  rootActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
+  rootActor.SetProperty(Actor::Property::SIZE, sceneSize);
+  scene.Add(rootActor);
+
+  RenderTaskList taskList = scene.GetRenderTaskList();
+  RenderTask     newTask  = taskList.CreateTask();
+  newTask.SetCameraActor(offscreenCameraActor);
+  newTask.SetSourceActor(layer);
+  newTask.SetInputEnabled(false);
+  newTask.SetClearColor(Vector4(0.f, 0.f, 0.f, 0.f));
+  newTask.SetClearEnabled(true);
+  newTask.SetExclusive(true);
+  newTask.SetFrameBuffer(frameBuffer);
+  newTask.SetScreenToFrameBufferMappingActor(rootActor);
+
+  application.SendNotification();
+  application.Render(16u);
+
+  auto expectedExtent = Rect<>{sceneSize.x * 0.5f + sourcePosition.x - sourceSize.x * 0.5f,
+                               sceneSize.y * 0.5f + sourcePosition.z - sourceSize.z * 0.5f,
+                               sourceSize.x,
+                               sourceSize.z};
+  auto actualExtent   = DevelActor::CalculateScreenExtents(sourceActor);
+  {
+    std::ostringstream oss;
+    oss << expectedExtent << "\n";
+    oss << actualExtent << "\n";
+    tet_printf("%s\n", oss.str().c_str());
+  }
+
+  auto expectedScreen = Vector2{sceneSize.x * 0.5f + sourcePosition.x, sceneSize.y * 0.5f + sourcePosition.z};
+  auto actualScreen   = sourceActor.GetProperty<Vector2>(Actor::Property::SCREEN_POSITION);
+  {
+    std::ostringstream oss;
+    oss << expectedScreen << "\n";
+    oss << actualScreen << "\n";
+    tet_printf("%s\n", oss.str().c_str());
+  }
+
+  DALI_TEST_EQUALS(expectedExtent.x, actualExtent.x, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.y, actualExtent.y, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.width, actualExtent.width, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.height, actualExtent.height, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(expectedScreen.x, actualScreen.x, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedScreen.y, actualScreen.y, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+
+  // Change rootActor's size and position
+
+  Vector3 rootPosition{100.0f, 200.0f, 0.0f};
+  Vector3 rootSize{200.0f, 100.0f, 0.0f};
+
+  rootActor.SetProperty(Actor::Property::POSITION, rootPosition);
+  rootActor.SetProperty(Actor::Property::SIZE, rootSize);
+
+  application.SendNotification();
+  application.Render(16u);
+
+  expectedExtent = Rect<>{sceneSize.x * 0.5f + rootPosition.x + (sourcePosition.x - sourceSize.x * 0.5f) * rootSize.x / sceneSize.x,
+                          sceneSize.y * 0.5f + rootPosition.y + (sourcePosition.z - sourceSize.z * 0.5f) * rootSize.y / sceneSize.y,
+                          sourceSize.x * rootSize.x / sceneSize.x,
+                          sourceSize.z * rootSize.y / sceneSize.y};
+  actualExtent   = DevelActor::CalculateScreenExtents(sourceActor);
+  {
+    std::ostringstream oss;
+    oss << expectedExtent << "\n";
+    oss << actualExtent << "\n";
+    tet_printf("%s\n", oss.str().c_str());
+  }
+
+  expectedScreen = Vector2{sceneSize.x * 0.5f + rootPosition.x + sourcePosition.x * rootSize.x / sceneSize.x, sceneSize.y * 0.5f + rootPosition.y + sourcePosition.z * rootSize.y / sceneSize.y};
+  actualScreen   = sourceActor.GetProperty<Vector2>(Actor::Property::SCREEN_POSITION);
+  {
+    std::ostringstream oss;
+    oss << expectedScreen << "\n";
+    oss << actualScreen << "\n";
+    tet_printf("%s\n", oss.str().c_str());
+  }
+
+  DALI_TEST_EQUALS(expectedExtent.x, actualExtent.x, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.y, actualExtent.y, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.width, actualExtent.width, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedExtent.height, actualExtent.height, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(expectedScreen.x, actualScreen.x, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+  DALI_TEST_EQUALS(expectedScreen.y, actualScreen.y, Math::MACHINE_EPSILON_10000, TEST_LOCATION);
+
+  END_TEST;
+}
+
 // SetPosition(float x, float y)
 int UtcDaliActorSetPosition01(void)
 {
@@ -7088,6 +7324,76 @@ int UtcDaliActorGetScreenPositionResizeScene(void)
   END_TEST;
 }
 
+int UtcDaliActorGetScreenPositionInCustomCameraAndLayer3D(void)
+{
+  tet_infoline("UtcDaliActorGetScreenPositionInCustomCameraAndLayer3D Check screen position under LAYER_3D and custom camera");
+
+  TestApplication    application;
+  Integration::Scene scene = application.GetScene();
+
+  // Make 3D Layer
+  Layer layer = scene.GetRootLayer();
+  layer.SetProperty(Layer::Property::BEHAVIOR, Layer::Behavior::LAYER_3D);
+
+  // Build custom camera with top-view
+  CameraActor cameraActor = scene.GetRenderTaskList().GetTask(0).GetCameraActor();
+  {
+    // Default camera position at +z and looking -z axis. (orientation is [ Axis: [0, 1, 0], Angle: 180 degrees ])
+    Vector3    cameraPos    = cameraActor.GetProperty<Vector3>(Actor::Property::POSITION);
+    Quaternion cameraOrient = cameraActor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+
+    {
+      std::ostringstream oss;
+      oss << cameraPos << "\n";
+      oss << cameraOrient << "\n";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+
+    cameraActor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, -cameraPos.z, 0.0f));
+    cameraActor.SetProperty(Actor::Property::ORIENTATION, Quaternion(Degree(90.0f), Vector3::XAXIS) * cameraOrient);
+
+    // Now, upside : -Z, leftside : -X, foward : +Y
+
+    cameraPos    = cameraActor.GetProperty<Vector3>(Actor::Property::POSITION);
+    cameraOrient = cameraActor.GetProperty<Quaternion>(Actor::Property::ORIENTATION);
+    {
+      std::ostringstream oss;
+      oss << cameraPos << "\n";
+      oss << cameraOrient << "\n";
+      tet_printf("%s\n", oss.str().c_str());
+    }
+  }
+
+  Actor actorA = Actor::New();
+  actorA.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actorA.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actorA.SetProperty(Actor::Property::SIZE, Vector3(10.0f, 10.0f, 10.0f));
+  actorA.SetProperty(Actor::Property::POSITION, Vector3(20.0f, 0.0f, 10.0f));
+
+  Actor actorB = Actor::New();
+  actorB.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actorB.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actorB.SetProperty(Actor::Property::SIZE, Vector3(10.0f, 10.0f, 10.0f));
+  actorB.SetProperty(Actor::Property::POSITION, Vector3(-20.0f, 0.0f, -10.0f));
+
+  scene.Add(actorA);
+  scene.Add(actorB);
+
+  application.SendNotification();
+  application.Render();
+
+  Vector2 sceneSize           = scene.GetSize();
+  Vector2 actorScreenPosition = actorA.GetProperty(Actor::Property::SCREEN_POSITION).Get<Vector2>();
+
+  DALI_TEST_EQUALS(actorScreenPosition, sceneSize / 2 + Vector2(20.0f, 10.0f), TEST_LOCATION);
+
+  actorScreenPosition = actorB.GetProperty(Actor::Property::SCREEN_POSITION).Get<Vector2>();
+
+  DALI_TEST_EQUALS(actorScreenPosition, sceneSize / 2 - Vector2(20.0f, 10.0f), TEST_LOCATION);
+
+  END_TEST;
+}
+
 int utcDaliActorPositionUsesAnchorPoint(void)
 {
   TestApplication application;
index ce8b7a6..bad1233 100644 (file)
 
 namespace Dali::Internal
 {
+namespace
+{
+/**
+ * @brief Get the Viewport Extents from RenderTask
+ *
+ * @param[in] renderTask RenderTask what we want to get viewport.
+ * @param[out] viewportExtent Calculated extent by renderTask.
+ * @return True if we success to get viewports. False otherwise
+ */
+bool GetViewportExtentsFromRenderTask(const RenderTask& renderTask, Rect<float>& viewportExtent)
+{
+  if(renderTask.GetFrameBuffer())
+  {
+    Dali::Actor mappingActor = renderTask.GetScreenToFrameBufferMappingActor();
+    if(mappingActor)
+    {
+      // NOTE : We will assume that mapping actor always use default camera.
+      Vector2 screenPosition    = mappingActor.GetProperty<Vector2>(Dali::Actor::Property::SCREEN_POSITION);
+      Vector3 size              = mappingActor.GetCurrentProperty<Vector3>(Dali::Actor::Property::SIZE) * mappingActor.GetCurrentProperty<Vector3>(Dali::Actor::Property::WORLD_SCALE);
+      Vector3 anchorPointOffSet = size * GetImplementation(mappingActor).GetAnchorPointForPosition();
+      Vector2 position          = Vector2(screenPosition.x - anchorPointOffSet.x, screenPosition.y - anchorPointOffSet.y);
+      viewportExtent.x          = position.x;
+      viewportExtent.y          = position.y;
+      viewportExtent.width      = size.x;
+      viewportExtent.height     = size.y;
+    }
+    else
+    {
+      return false;
+    }
+  }
+  else
+  {
+    Viewport viewport;
+    renderTask.GetViewport(viewport);
+    viewportExtent.x      = viewport.x;
+    viewportExtent.y      = viewport.y;
+    viewportExtent.width  = viewport.width;
+    viewportExtent.height = viewport.height;
+  }
+  return true;
+}
+} // namespace
 bool ConvertScreenToLocal(
   const Matrix&   viewMatrix,
   const Matrix&   projectionMatrix,
@@ -155,6 +198,252 @@ Rect<> CalculateActorScreenExtents(const Actor& actor, const Vector2& screenPosi
   return {position.x, position.y, size.x, size.y};
 }
 
+bool ConvertLocalToScreen(
+  const Matrix&  viewMatrix,
+  const Matrix&  projectionMatrix,
+  const Matrix&  worldMatrix,
+  const Rect<>&  viewportExtent,
+  const Vector3& localPosition,
+  float&         screenX,
+  float&         screenY)
+{
+  bool success = false;
+
+  // Convert local to projection coordinates
+  // note, P*(V*(M*pos))) is faster than (P*V*M)*pos
+  Vector4 mvpPos(localPosition.x, localPosition.y, localPosition.z, 1.0f);
+
+  mvpPos = worldMatrix * mvpPos;
+  mvpPos = viewMatrix * mvpPos;
+  mvpPos = projectionMatrix * mvpPos;
+
+  if(DALI_LIKELY(!EqualsZero(mvpPos.w)))
+  {
+    success = true;
+    screenX = viewportExtent.x + (mvpPos.x + mvpPos.w) * viewportExtent.width * 0.5f / mvpPos.w;
+    screenY = viewportExtent.y + (-mvpPos.y + mvpPos.w) * viewportExtent.height * 0.5f / mvpPos.w;
+  }
+  return success;
+}
+
+bool ConvertLocalToScreenRenderTask(
+  const RenderTask& renderTask,
+  const Actor&      actor,
+  const Matrix&     worldMatrix,
+  const Vector3&    localPosition,
+  float&            screenX,
+  float&            screenY)
+{
+  bool         success     = false;
+  const Actor* sourceActor = renderTask.GetSourceActor();
+  if(sourceActor == nullptr)
+  {
+    return success;
+  }
+
+  // Check whether current actor is in this rendertask.
+  bool         actorInRendertask = false;
+  const Actor* targetActor       = &actor;
+  while(targetActor)
+  {
+    if(sourceActor == targetActor)
+    {
+      actorInRendertask = true;
+      break;
+    }
+    targetActor = targetActor->GetParent();
+  }
+  if(!actorInRendertask)
+  {
+    return success;
+  }
+
+  CameraActor* camera = renderTask.GetCameraActor();
+  if(camera)
+  {
+    Rect<float> viewportExtent = {0.f, 0.f, 0.f, 0.f};
+    if(!GetViewportExtentsFromRenderTask(renderTask, viewportExtent))
+    {
+      return success;
+    }
+
+    if(ConvertLocalToScreen(camera->GetViewMatrix(), camera->GetProjectionMatrix(), worldMatrix, viewportExtent, localPosition, screenX, screenY))
+    {
+      success = true;
+    }
+  }
+  return success;
+}
+
+bool ConvertLocalToScreenRenderTaskList(
+  const RenderTaskList& renderTaskList,
+  const Actor&          actor,
+  const Matrix&         worldMatrix,
+  const Vector3&        localPosition,
+  float&                screenX,
+  float&                screenY)
+{
+  // do a reverse traversal of all lists (as the default onscreen one is typically the last one)
+  uint32_t taskCount = renderTaskList.GetTaskCount();
+  for(uint32_t i = taskCount; i > 0; --i)
+  {
+    RenderTaskPtr task = renderTaskList.GetTask(i - 1);
+    if(ConvertLocalToScreenRenderTask(*task, actor, worldMatrix, localPosition, screenX, screenY))
+    {
+      // found a task where this conversion was ok so return
+      return true;
+    }
+  }
+  return false;
+}
+
+const Vector2 CalculateActorScreenPositionRenderTaskList(const Actor& actor, BufferIndex bufferIndex)
+{
+  if(actor.OnScene())
+  {
+    const auto& node  = actor.GetNode();
+    Scene&      scene = actor.GetScene();
+
+    Vector2 result;
+
+    auto        worldMatrix    = node.GetWorldMatrix(bufferIndex);
+    const auto& renderTaskList = scene.GetRenderTaskList();
+    ConvertLocalToScreenRenderTaskList(renderTaskList, actor, worldMatrix, node.GetSize(bufferIndex) * (actor.GetAnchorPointForPosition() - Vector3(0.5f, 0.5f, 0.5f)), result.x, result.y);
+
+    return result;
+  }
+  return Vector2::ZERO;
+}
+
+bool ConvertLocalToScreenExtentRenderTask(
+  const RenderTask& renderTask,
+  const Actor&      actor,
+  const Matrix&     worldMatrix,
+  const Vector3&    currentSize,
+  Rect<>&           screenExtent)
+{
+  bool         success     = false;
+  const Actor* sourceActor = renderTask.GetSourceActor();
+  if(sourceActor == nullptr)
+  {
+    return success;
+  }
+
+  // Check whether current actor is in this rendertask.
+  bool         actorInRendertask = false;
+  const Actor* targetActor       = &actor;
+  while(targetActor)
+  {
+    if(sourceActor == targetActor)
+    {
+      actorInRendertask = true;
+      break;
+    }
+    targetActor = targetActor->GetParent();
+  }
+  if(!actorInRendertask)
+  {
+    return success;
+  }
+
+  CameraActor* camera = renderTask.GetCameraActor();
+  if(camera)
+  {
+    Rect<float> viewportExtent = {0.f, 0.f, 0.f, 0.f};
+    if(!GetViewportExtentsFromRenderTask(renderTask, viewportExtent))
+    {
+      return success;
+    }
+
+    constexpr uint32_t BOX_POINT_COUNT           = 8;
+    const Vector3      BBOffset[BOX_POINT_COUNT] = {
+      Vector3(-currentSize.width * 0.5f, -currentSize.height * 0.5f, -currentSize.depth * 0.5f),
+      Vector3(-currentSize.width * 0.5f, currentSize.height * 0.5f, -currentSize.depth * 0.5f),
+      Vector3(currentSize.width * 0.5f, -currentSize.height * 0.5f, -currentSize.depth * 0.5f),
+      Vector3(currentSize.width * 0.5f, currentSize.height * 0.5f, -currentSize.depth * 0.5f),
+      Vector3(-currentSize.width * 0.5f, -currentSize.height * 0.5f, currentSize.depth * 0.5f),
+      Vector3(-currentSize.width * 0.5f, currentSize.height * 0.5f, currentSize.depth * 0.5f),
+      Vector3(currentSize.width * 0.5f, -currentSize.height * 0.5f, currentSize.depth * 0.5f),
+      Vector3(currentSize.width * 0.5f, currentSize.height * 0.5f, currentSize.depth * 0.5f),
+    };
+
+    float minScreenX = 0.0f;
+    float minScreenY = 0.0f;
+    float maxScreenX = 0.0f;
+    float maxScreenY = 0.0f;
+
+    const auto& viewMatrix       = camera->GetViewMatrix();
+    const auto& projectionMatrix = camera->GetProjectionMatrix();
+
+    success = ConvertLocalToScreen(viewMatrix, projectionMatrix, worldMatrix, viewportExtent, BBOffset[0], minScreenX, minScreenY);
+    if(success)
+    {
+      maxScreenX = minScreenX;
+      maxScreenY = minScreenY;
+      for(uint32_t i = 1; i < BOX_POINT_COUNT; ++i)
+      {
+        float   screenX       = 0.0f;
+        float   screenY       = 0.0f;
+        Vector3 localPosition = BBOffset[i];
+        if(DALI_UNLIKELY(!ConvertLocalToScreen(viewMatrix, projectionMatrix, worldMatrix, viewportExtent, localPosition, screenX, screenY)))
+        {
+          success = false;
+          break;
+        }
+        minScreenX = std::min(minScreenX, screenX);
+        maxScreenX = std::max(maxScreenX, screenX);
+        minScreenY = std::min(minScreenY, screenY);
+        maxScreenY = std::max(maxScreenY, screenY);
+      }
+      if(success)
+      {
+        screenExtent.x      = minScreenX;
+        screenExtent.y      = minScreenY;
+        screenExtent.width  = maxScreenX - minScreenX;
+        screenExtent.height = maxScreenY - minScreenY;
+      }
+    }
+  }
+  return success;
+}
+
+bool ConvertLocalToScreenExtentRenderTaskList(
+  const RenderTaskList& renderTaskList,
+  const Actor&          actor,
+  const Matrix&         worldMatrix,
+  const Vector3&        currentSize,
+  Rect<>&               screenExtent)
+{
+  // do a reverse traversal of all lists (as the default onscreen one is typically the last one)
+  uint32_t taskCount = renderTaskList.GetTaskCount();
+  for(uint32_t i = taskCount; i > 0; --i)
+  {
+    RenderTaskPtr task = renderTaskList.GetTask(i - 1);
+    if(ConvertLocalToScreenExtentRenderTask(*task, actor, worldMatrix, currentSize, screenExtent))
+    {
+      // found a task where this conversion was ok so return
+      return true;
+    }
+  }
+  return false;
+}
+
+Rect<> CalculateActorScreenExtentsRenderTaskList(const Actor& actor, BufferIndex bufferIndex)
+{
+  Rect<> result = {0.0f, 0.0f, 0.0f, 0.0f};
+
+  if(actor.OnScene())
+  {
+    const auto& node  = actor.GetNode();
+    Scene&      scene = actor.GetScene();
+
+    auto        worldMatrix    = node.GetWorldMatrix(bufferIndex);
+    const auto& renderTaskList = scene.GetRenderTaskList();
+    ConvertLocalToScreenExtentRenderTaskList(renderTaskList, actor, worldMatrix, node.GetSize(bufferIndex), result);
+  }
+  return result;
+}
+
 /**
  * @brief Computes and center position by using transform properties.
  * @param[in] anchorPoint anchorPoint of an actor.
index 1c7d092..af37dfa 100644 (file)
@@ -32,8 +32,8 @@ namespace Dali::Internal
  * @param[in] viewMatrix The view matrix used to display this entity
  * @param[in] projectionMatrix The projection matrix used to display this entity
  * @param[in] worldMatrix The world matrix of this entity
- * @param[in] viewport The viewport used for drawing
  * @param[in] currentSize The 2d bounding box for this entity
+ * @param[in] viewport The viewport used for drawing
  * @param[out] localX The local X coordinate
  * @param[out] localY The local Y coordinate
  * @param[in] screenX The screen X coordinate
@@ -113,6 +113,119 @@ const Vector2 CalculateActorScreenPosition(const Actor& actor, BufferIndex buffe
 Rect<> CalculateActorScreenExtents(const Actor& actor, const Vector2& screenPosition, BufferIndex bufferIndex);
 
 /**
+ * Convert local coordinates to screen coordinates
+ *
+ * @param[in] viewMatrix The view matrix used to display this entity
+ * @param[in] projectionMatrix The projection matrix used to display this entity
+ * @param[in] worldMatrix The world matrix of this entity
+ * @param[in] viewport The viewport used for drawing
+ * @param[in] localPosition The local position
+ * @param[out] screenX The screen X coordinate
+ * @param[out] screenY The screen Y coordinate
+ * @return true if the conversion was successful
+ */
+bool ConvertLocalToScreen(const Matrix&   viewMatrix,
+                          const Matrix&   projectionMatrix,
+                          const Matrix&   worldMatrix,
+                          const Viewport& viewport,
+                          const Vector3&  localPosition,
+                          float&          screenX,
+                          float&          screenY);
+
+/**
+ * Convert local coordinates to screen coordinates
+ *
+ * @param[in] renderTask The render task used to display this entity
+ * @param[in] actor The actor of this entity
+ * @param[in] worldMatrix The world matrix of this entity
+ * @param[in] localPosition The local position
+ * @param[out] screenX The screen X coordinate
+ * @param[out] screenY The screen Y coordinate
+ * @return true if the conversion was successful
+ */
+bool ConvertLocalToScreenRenderTask(
+  const RenderTask& renderTask,
+  const Actor&      actor,
+  const Matrix&     worldMatrix,
+  const Vector3&    localPosition,
+  float&            screenX,
+  float&            screenY);
+
+/**
+ * Convert local coordinates to screen coordinates
+ * Search through the given renderTaskList to check if this entity can be converted
+ *
+ * @param[in] renderTaskList The render task list to search
+ * @param[in] actor The actor of this entity
+ * @param[in] worldMatrix The world matrix of this entity
+ * @param[in] localPosition The local position
+ * @param[out] screenX The screen X coordinate
+ * @param[out] screenY The screen Y coordinate
+ * @return true if the conversion was successful
+ */
+bool ConvertLocalToScreenRenderTaskList(
+  const RenderTaskList& renderTaskList,
+  const Actor&          actor,
+  const Matrix&         worldMatrix,
+  const Vector3&        localPosition,
+  float&                screenX,
+  float&                screenY);
+
+/**
+ * Calculate the screen position of the actor from it's node transform and anchor point.
+ * It will consider rendertasklist.
+ *
+ * @param[in] actor The actor
+ * @param[in] bufferIndex The current event buffer index
+ * @return the screen extents of the actor consider camera.
+ */
+const Vector2 CalculateActorScreenPositionRenderTaskList(const Actor& actor, BufferIndex bufferIndex);
+
+/**
+ * Convert local coordinates to screen coordinates extent
+ *
+ * @param[in] renderTask The render task used to display this entity
+ * @param[in] actor The actor of this entity
+ * @param[in] worldMatrix The world matrix of this entity
+ * @param[in] localPosition The local position
+ * @param[out] screenExtent The screen Extent
+ * @return true if the conversion was successful
+ */
+bool ConvertLocalToScreenExtentRenderTask(
+  const RenderTask& renderTask,
+  const Actor&      actor,
+  const Matrix&     worldMatrix,
+  const Vector3&    currentSize,
+  Rect<>&           screenExtent);
+
+/**
+ * Convert local coordinates to screen coordinates extent
+ * Search through the given renderTaskList to check if this entity can be converted
+ *
+ * @param[in] renderTaskList The render task list to search
+ * @param[in] actor The actor of this entity
+ * @param[in] worldMatrix The world matrix of this entity
+ * @param[in] localPosition The local position
+ * @param[out] screenExtent The screen Extent
+ * @return true if the conversion was successful
+ */
+bool ConvertLocalToScreenExtentRenderTaskList(
+  const RenderTaskList& renderTaskList,
+  const Actor&          actor,
+  const Matrix&         worldMatrix,
+  const Vector3&        currentSize,
+  Rect<>&               screenExtent);
+/**
+ * Calculate the screen extents of the actor from its node transform, anchor point and size.
+ * It will consider rendertasklist.
+ *
+ * @param[in] actor The actor
+ * @param[in] bufferIndex The current event buffer index
+ * @return the screen extents of the actor consider camera.
+ */
+Rect<> CalculateActorScreenExtentsRenderTaskList(const Actor& actor, BufferIndex bufferIndex);
+
+/**
  * Get the world transform of the actor.
  *
  * This calculates the world transform from scratch using only event
index 4414f1d..bf91a94 100644 (file)
@@ -540,7 +540,15 @@ const Vector2 Actor::GetCurrentScreenPosition() const
   if(mScene)
   {
     BufferIndex bufferIndex = GetEventThreadServices().GetEventBufferIndex();
-    return CalculateActorScreenPosition(*this, bufferIndex);
+    if(mLayer3DParentsCount == 0)
+    {
+      // We can assume that this actor is under 2d layer. Use faster, but imprecise algorithm
+      return CalculateActorScreenPosition(*this, bufferIndex);
+    }
+    else
+    {
+      return CalculateActorScreenPositionRenderTaskList(*this, bufferIndex);
+    }
   }
   return Vector2::ZERO;
 }
@@ -1076,6 +1084,7 @@ Actor::Actor(DerivedType derivedType, const SceneGraph::Node& node)
   mName(),
   mSortedDepth(0u),
   mDepth(0u),
+  mLayer3DParentsCount(0),
   mIsRoot(ROOT_LAYER == derivedType),
   mIsLayer(LAYER == derivedType || ROOT_LAYER == derivedType),
   mIsOnScene(false),
@@ -1195,7 +1204,7 @@ void Actor::UnparentChildren()
   mParentImpl.UnparentChildren();
 }
 
-void Actor::ConnectToScene(uint32_t parentDepth, bool notify)
+void Actor::ConnectToScene(uint32_t parentDepth, uint32_t layer3DParentsCount, bool notify)
 {
   // This container is used instead of walking the Actor hierarchy.
   // It protects us when the Actor hierarchy is modified during OnSceneConnectionExternal callbacks.
@@ -1207,7 +1216,7 @@ void Actor::ConnectToScene(uint32_t parentDepth, bool notify)
   }
 
   // This stage is not interrupted by user callbacks.
-  mParentImpl.RecursiveConnectToScene(connectionList, parentDepth + 1);
+  mParentImpl.RecursiveConnectToScene(connectionList, layer3DParentsCount, parentDepth + 1);
 
   // Notify applications about the newly connected actors.
   for(const auto& actor : connectionList)
@@ -1476,7 +1485,7 @@ void Actor::SetParent(ActorParent* parent, bool notify)
        parentActor->OnScene())
     {
       // Instruct each actor to create a corresponding node in the scene graph
-      ConnectToScene(parentActor->GetHierarchyDepth(), notify);
+      ConnectToScene(parentActor->GetHierarchyDepth(), parentActor->GetLayer3DParentCount(), notify);
     }
 
     // Resolve the name and index for the child properties if any
@@ -1504,9 +1513,18 @@ void Actor::SetParent(ActorParent* parent, bool notify)
 
 Rect<> Actor::CalculateScreenExtents() const
 {
-  auto        screenPosition = GetCurrentScreenPosition();
-  BufferIndex bufferIndex    = GetEventThreadServices().GetEventBufferIndex();
-  return CalculateActorScreenExtents(*this, screenPosition, bufferIndex);
+  if(mLayer3DParentsCount == 0)
+  {
+    // We can assume that this actor is under 2d layer. Use faster, but imprecise algorithm
+    auto        screenPosition = GetCurrentScreenPosition();
+    BufferIndex bufferIndex    = GetEventThreadServices().GetEventBufferIndex();
+    return CalculateActorScreenExtents(*this, screenPosition, bufferIndex);
+  }
+  else
+  {
+    BufferIndex bufferIndex = GetEventThreadServices().GetEventBufferIndex();
+    return CalculateActorScreenExtentsRenderTaskList(*this, bufferIndex);
+  }
 }
 
 Vector3 Actor::GetAnchorPointForPosition() const
index d0d985f..4557a03 100644 (file)
@@ -767,6 +767,21 @@ public:
   }
 
   /**
+   * @brief Get the number of layer with 3D behaviour in ancestors include this. It will be 0 if actor is not on scene.
+   *
+   * @return currently the number of layer with 3D behaviour in ancestors.
+   */
+  inline int32_t GetLayer3DParentCount() const
+  {
+    if(mIsOnScene)
+    {
+      return mLayer3DParentsCount;
+    }
+
+    return 0;
+  }
+
+  /**
    * Get the actor's sorting depth
    *
    * @return The depth used for hit-testing and renderer sorting
@@ -1538,9 +1553,10 @@ protected:
   /**
    * Called on a child during Add() when the parent actor is connected to the Scene.
    * @param[in] parentDepth The depth of the parent in the hierarchy.
+   * @param[in] layer3DParentsCount The number of 3d layers in the hierarchy.
    * @param[in] notify Emits notification if set to true.
    */
-  void ConnectToScene(uint32_t parentDepth, bool notify);
+  void ConnectToScene(uint32_t parentDepth, uint32_t layer3DParentsCount, bool notify);
 
   /**
    * Connect the Node associated with this Actor to the scene-graph.
@@ -1914,6 +1930,8 @@ protected:
   uint32_t    mSortedDepth; ///< The sorted depth index. A combination of tree traversal and sibling order.
   int16_t     mDepth;       ///< The depth in the hierarchy of the actor. Only 32,767 levels of depth are supported
 
+  int16_t mLayer3DParentsCount; ///< The number of layer with 3D behaviour in ancestors include this. It will be 0 if actor is not on scene.
+
   const bool               mIsRoot : 1;                    ///< Flag to identify the root actor
   const bool               mIsLayer : 1;                   ///< Flag to identify that this is a layer
   bool                     mIsOnScene : 1;                 ///< Flag to identify whether the actor is on-scene
index f5311df..781b1b7 100644 (file)
@@ -433,12 +433,22 @@ void ActorParentImpl::DepthTraverseActorTree(OwnerPointer<SceneGraph::NodeDepths
   }
 }
 
-void ActorParentImpl::RecursiveConnectToScene(ActorContainer& connectionList, uint32_t depth)
+void ActorParentImpl::RecursiveConnectToScene(ActorContainer& connectionList, uint32_t layer3DParentsCount, uint32_t depth)
 {
   DALI_ASSERT_ALWAYS(!mOwner.OnScene());
 
-  mOwner.mIsOnScene = true;
-  mOwner.mDepth     = static_cast<uint16_t>(depth); // overflow ignored, not expected in practice
+  if(mOwner.mIsLayer)
+  {
+    if(static_cast<Dali::Internal::Layer*>(&mOwner)->GetBehavior() == Dali::Layer::Behavior::LAYER_3D)
+    {
+      // This is 3d layer. Propagate it to all children.
+      ++layer3DParentsCount;
+    }
+  }
+
+  mOwner.mIsOnScene           = true;
+  mOwner.mDepth               = static_cast<uint16_t>(depth);               // overflow ignored, not expected in practice
+  mOwner.mLayer3DParentsCount = static_cast<uint16_t>(layer3DParentsCount); // overflow ignored, not expected in practice
   mOwner.ConnectToSceneGraph();
 
   // Notification for internal derived classes
@@ -453,7 +463,7 @@ void ActorParentImpl::RecursiveConnectToScene(ActorContainer& connectionList, ui
     for(const auto& actor : *mChildren)
     {
       actor->SetScene(*mOwner.mScene);
-      actor->mParentImpl.RecursiveConnectToScene(connectionList, depth + 1);
+      actor->mParentImpl.RecursiveConnectToScene(connectionList, layer3DParentsCount, depth + 1);
     }
   }
 }
@@ -461,8 +471,9 @@ void ActorParentImpl::RecursiveConnectToScene(ActorContainer& connectionList, ui
 void ActorParentImpl::RecursiveDisconnectFromScene(ActorContainer& disconnectionList)
 {
   // need to change state first so that internals relying on IsOnScene() inside OnSceneDisconnectionInternal() get the correct value
-  mOwner.mIsOnScene = false;
-  mOwner.mScene     = nullptr;
+  mOwner.mIsOnScene           = false;
+  mOwner.mScene               = nullptr;
+  mOwner.mLayer3DParentsCount = 0;
 
   // Recursively disconnect children
   if(mChildren)
@@ -481,6 +492,20 @@ void ActorParentImpl::RecursiveDisconnectFromScene(ActorContainer& disconnection
   mOwner.DisconnectFromSceneGraph();
 }
 
+void ActorParentImpl::RecursiveChangeLayer3dCount(int32_t layer3DParentsCountDiff)
+{
+  mOwner.mLayer3DParentsCount += layer3DParentsCountDiff; // overflow ignored, not expected in practice
+
+  // Recursively change the value
+  if(mChildren)
+  {
+    for(const auto& actor : *mChildren)
+    {
+      actor->mParentImpl.RecursiveChangeLayer3dCount(layer3DParentsCountDiff);
+    }
+  }
+}
+
 void ActorParentImpl::InheritLayoutDirectionRecursively(Dali::LayoutDirection::Type direction, bool set)
 {
   if(mOwner.mInheritLayoutDirection || set)
index 5dff226..c284953 100644 (file)
@@ -1,7 +1,7 @@
 #ifndef DALI_INTERNAL_ACTOR_PARENT_IMPL_H
 #define DALI_INTERNAL_ACTOR_PARENT_IMPL_H
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use actor file except in compliance with the License.
@@ -205,9 +205,10 @@ public:
    * Helper to recursively connect a tree of actors.
    * This is not interrupted by user callbacks
    * @param[in]  depth The depth in the hierarchy of the actor
+   * @param[in]  layer3DParentsCount The number of 3d layers in the hierarchy.
    * @param[out] connectionList On return, the list of connected actors which require notification.
    */
-  void RecursiveConnectToScene(ActorContainer& connectionList, uint32_t depth);
+  void RecursiveConnectToScene(ActorContainer& connectionList, uint32_t layer3DParentsCount, uint32_t depth);
 
   /**
    * Helper to recursively disconnect a tree of actors.
@@ -217,6 +218,13 @@ public:
   void RecursiveDisconnectFromScene(ActorContainer& disconnectionList);
 
   /**
+   * Helper to recursively change the number of 3d layers in a tree of actors.
+   * This is not interrupted by user callbacks.
+   * @param[in] layer3DParentsCountDiff Difference valaue of 3d layers count.
+   */
+  void RecursiveChangeLayer3dCount(int32_t layer3DParentsCountDiff);
+
+  /**
    * @brief Propagates layout direction recursively.
    * @param[in] direction New layout direction.
    */
index bc0ad28..d0d7e23 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -239,12 +239,22 @@ void Layer::MoveBelow(const Internal::Layer& target)
 
 void Layer::SetBehavior(Dali::Layer::Behavior behavior)
 {
-  mBehavior = behavior;
+  if(mBehavior != behavior)
+  {
+    mBehavior = behavior;
 
-  // Notify update side object.
-  SetBehaviorMessage(GetEventThreadServices(), GetSceneGraphLayer(), behavior);
-  // By default, disable depth test for LAYER_UI, and enable for LAYER_3D.
-  SetDepthTestDisabled(mBehavior == Dali::Layer::LAYER_UI);
+    if(mIsOnScene)
+    {
+      // If current layer is on scene, and it's behavior changed,
+      // Change the mLayer3DParentsCount value recursively.
+      mParentImpl.RecursiveChangeLayer3dCount(mBehavior == Dali::Layer::LAYER_3D ? 1 : -1);
+    }
+
+    // Notify update side object.
+    SetBehaviorMessage(GetEventThreadServices(), GetSceneGraphLayer(), behavior);
+    // By default, disable depth test for LAYER_UI, and enable for LAYER_3D.
+    SetDepthTestDisabled(mBehavior == Dali::Layer::LAYER_UI);
+  }
 }
 
 void Layer::SetClipping(bool enabled)