Fix viewport size when off-screen actor hittest 27/297627/6
authorEunki, Hong <eunkiki.hong@samsung.com>
Tue, 22 Aug 2023 02:37:11 +0000 (11:37 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Tue, 22 Aug 2023 12:26:47 +0000 (21:26 +0900)
There was some miss-conversion of screen coordinate
when we use offscreen rendering.

Since hit-test algorithm use converted-screen-coordinate
by inputMappingActor, we also need to use inputMappingActor
when we use viewport value.

Change-Id: I2b1995a1b8e47b600239dcfb6e0380124e484f2f
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-HitTestAlgorithm.cpp
dali/devel-api/events/hit-test-algorithm.cpp
dali/internal/event/events/hit-test-algorithm-impl.cpp
dali/internal/event/render-tasks/render-task-impl.cpp
dali/internal/event/render-tasks/render-task-impl.h

index a9167e0..20279f7 100644 (file)
@@ -700,3 +700,171 @@ int UtcDaliHitTestAlgorithmBuildPickingRay01(void)
 
   END_TEST;
 }
+
+int UtcDaliHitTestAlgorithmBuildPickingRay02(void)
+{
+  TestApplication application;
+  tet_infoline("Testing Dali::HitTestAlgorithm::BuildPickingRay positive test for offscreen");
+
+  Stage          stage             = Stage::GetCurrent();
+  RenderTaskList renderTaskList    = stage.GetRenderTaskList();
+  RenderTask     defaultRenderTask = renderTaskList.GetTask(0u);
+  RenderTask     offRenderTask     = renderTaskList.CreateTask();
+
+  Dali::CameraActor defaultCameraActor = defaultRenderTask.GetCameraActor();
+
+  Vector2 stageSize(stage.GetSize());
+
+  Vector2 actorSize(stageSize * 0.5f);
+  Vector2 offscreenSize(1920.0f, 1080.0f); // Quit big size.
+
+  // Create two actors with half the size of the stage and set them to be partial-overlapping
+  Actor blue = Actor::New();
+  blue.SetProperty(Actor::Property::NAME, "Blue");
+  blue.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  blue.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER);
+  blue.SetProperty(Actor::Property::SIZE, actorSize);
+  blue.SetProperty(Actor::Property::POSITION, -actorSize * 0.25f);
+
+  Actor green = Actor::New();
+  green.SetProperty(Actor::Property::NAME, "Green");
+  green.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  green.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER);
+  green.SetProperty(Actor::Property::SIZE, actorSize);
+  green.SetProperty(Actor::Property::POSITION, actorSize * 0.25f);
+
+  Actor red = Actor::New();
+  red.SetProperty(Actor::Property::NAME, "Red");
+  red.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  red.SetProperty(Actor::Property::PARENT_ORIGIN, AnchorPoint::CENTER);
+  red.SetProperty(Actor::Property::SIZE, offscreenSize * 0.5f);
+
+  Dali::CameraActor offscreenCameraActor                     = Dali::CameraActor::New(offscreenSize);
+  offscreenCameraActor[Dali::Actor::Property::ANCHOR_POINT]  = AnchorPoint::CENTER;
+  offscreenCameraActor[Dali::Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER;
+  stage.Add(offscreenCameraActor);
+
+  offRenderTask.SetExclusive(true);
+  offRenderTask.SetInputEnabled(true);
+  offRenderTask.SetCameraActor(offscreenCameraActor);
+  offRenderTask.SetSourceActor(red);
+  offRenderTask.SetScreenToFrameBufferMappingActor(green);
+
+  Dali::Texture texture      = Dali::Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, unsigned(actorSize.width), unsigned(actorSize.height));
+  FrameBuffer   renderTarget = FrameBuffer::New(actorSize.width, actorSize.height, FrameBuffer::Attachment::DEPTH_STENCIL);
+  renderTarget.AttachColorTexture(texture);
+  offRenderTask.SetFrameBuffer(renderTarget);
+
+  // Add the actors to the view
+  stage.Add(blue);
+  stage.Add(green);
+  stage.Add(red);
+
+  // Render and notify
+  application.SendNotification();
+  application.Render(0);
+
+  Vector2 screenCoords(stageSize * 0.5f); // touch center of screen
+  Vector3 origin;
+  Vector3 direction;
+  bool    built = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction);
+
+  Vector3 camPos = defaultCameraActor[Actor::Property::POSITION];
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, -Vector3::ZAXIS, 0.01f, TEST_LOCATION);
+
+  screenCoords.x = stageSize.width * 0.75f;
+  built          = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(0.075f, 0.0f, -1.0f), 0.01f, TEST_LOCATION);
+
+  screenCoords.x = 0.0f;
+  screenCoords.y = 0.0f;
+  built          = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(-0.144f, -0.24f, -0.96f), 0.01f, TEST_LOCATION);
+
+  screenCoords.x = stageSize.width;
+  screenCoords.y = stageSize.height;
+  built          = HitTestAlgorithm::BuildPickingRay(defaultRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(0.144f, 0.24f, -0.96f), 0.01f, TEST_LOCATION);
+
+  // For offscreen picking ray
+  camPos = Vector3(offscreenCameraActor[Actor::Property::POSITION]);
+
+  const float ELLIPSION = 0.001f; ///< tiny margin to avoid non-hitting cases
+
+  // Center of green
+  screenCoords = stageSize * 0.5f + actorSize * 0.25f;
+  built        = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, -Vector3::ZAXIS, 0.01f, TEST_LOCATION);
+
+  // Center right of green
+  screenCoords.x = stageSize.width * 0.5f + actorSize.width * 0.75f - ELLIPSION;
+  built          = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(0.242533f, 0.0f, -0.970143f), 0.01f, TEST_LOCATION);
+
+  // Top left of green
+  screenCoords = stageSize * 0.5f - actorSize * 0.25f + Vector2(ELLIPSION, ELLIPSION);
+  built        = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(-0.240308f, -0.135174f, -0.961239f), 0.01f, TEST_LOCATION);
+
+  // Bottom right of green
+  screenCoords = stageSize * 0.5f + actorSize * 0.75f - Vector2(ELLIPSION, ELLIPSION);
+  built        = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(0.240308f, 0.135174f, -0.961239f), 0.01f, TEST_LOCATION);
+
+  // Rotate green
+  green.SetProperty(Actor::Property::ORIENTATION, Quaternion(Radian(Degree(90.0f)), Vector3::ZAXIS));
+
+  // Render and notify
+  application.SendNotification();
+  application.Render(0);
+
+  // Top left of green, but ray directoin is bottom left
+  screenCoords.x = stageSize.width * 0.5f + actorSize.width * 0.25f - actorSize.height * 0.5f + ELLIPSION;
+  screenCoords.y = stageSize.height * 0.5f + actorSize.height * 0.25f - actorSize.width * 0.5f + ELLIPSION;
+  built          = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(-0.240308f, 0.135174f, -0.961239f), 0.01f, TEST_LOCATION);
+
+  // Bottom right of green, but ray direction is top right
+  screenCoords.x = stageSize.width * 0.5f + actorSize.width * 0.25f + actorSize.height * 0.5f - ELLIPSION;
+  screenCoords.y = stageSize.height * 0.5f + actorSize.height * 0.25f + actorSize.width * 0.5f - ELLIPSION;
+  built          = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(camPos, origin, TEST_LOCATION);
+  direction.Normalize();
+  DALI_TEST_EQUALS(direction, Vector3(0.240308f, -0.135174f, -0.961239f), 0.01f, TEST_LOCATION);
+
+  // Out of green. BuildPickingRay failed.
+  screenCoords = stageSize * 0.5f - actorSize * 0.5f;
+  built        = HitTestAlgorithm::BuildPickingRay(offRenderTask, screenCoords, origin, direction);
+  DALI_TEST_EQUALS(built, false, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index e9e3e7f..b8714de 100644 (file)
@@ -37,20 +37,30 @@ bool HitTest(Stage stage, const Vector2& screenCoordinates, Results& results, Hi
 
 bool BuildPickingRay(RenderTask renderTask, const Vector2& screenCoordinates, Vector3& origin, Vector3& direction)
 {
-  Viewport viewport = renderTask.GetViewport();
-  if(screenCoordinates.x < static_cast<float>(viewport.x) ||
-     screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
-     screenCoordinates.y < static_cast<float>(viewport.y) ||
-     screenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
+  Internal::RenderTask& renderTaskImpl             = GetImplementation(renderTask);
+  Vector2               convertedScreenCoordinates = screenCoordinates;
+
+  if(!renderTaskImpl.IsHittable(convertedScreenCoordinates))
+  {
+    return false;
+  }
+
+  Viewport viewport;
+  renderTaskImpl.GetHittableViewport(viewport);
+  if(convertedScreenCoordinates.x < static_cast<float>(viewport.x) ||
+     convertedScreenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
+     convertedScreenCoordinates.y < static_cast<float>(viewport.y) ||
+     convertedScreenCoordinates.y > static_cast<float>(viewport.y + viewport.height))
   {
     // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
     return false;
   }
-  CameraActor            cameraActor     = renderTask.GetCameraActor();
-  Internal::CameraActor& cameraActorImpl = GetImplementation(cameraActor);
+  DALI_ASSERT_ALWAYS(renderTaskImpl.GetCameraActor());
+
+  Internal::CameraActor& cameraActorImpl = *renderTaskImpl.GetCameraActor();
   Vector4                rayOrigin;
   Vector4                rayDirection;
-  bool                   success = cameraActorImpl.BuildPickingRay(screenCoordinates, viewport, rayOrigin, rayDirection);
+  bool                   success = cameraActorImpl.BuildPickingRay(convertedScreenCoordinates, viewport, rayOrigin, rayDirection);
   if(success)
   {
     origin    = Vector3(rayOrigin.x, rayOrigin.y, rayOrigin.z);
index 5409ed0..77adf67 100644 (file)
@@ -446,7 +446,8 @@ bool HitTestRenderTask(const RenderTaskList::ExclusivesContainer& exclusives,
   if(renderTask.IsHittable(screenCoordinates))
   {
     Viewport viewport;
-    renderTask.GetViewport(viewport);
+    renderTask.GetHittableViewport(viewport);
+
     if(screenCoordinates.x < static_cast<float>(viewport.x) ||
        screenCoordinates.x > static_cast<float>(viewport.x + viewport.width) ||
        screenCoordinates.y < static_cast<float>(viewport.y) ||
index 51853be..c292d96 100644 (file)
@@ -519,7 +519,7 @@ bool RenderTask::TranslateCoordinates(Vector2& screenCoords) const
 
         float localX, localY;
         inside            = inputMappingActor->ScreenToLocal(defaultCamera->GetViewMatrix(), defaultCamera->GetProjectionMatrix(), viewport, localX, localY, screenCoords.x, screenCoords.y);
-        Vector3 actorSize = inputMappingActor->GetCurrentSize();
+        Vector3 actorSize = inputMappingActor->GetCurrentSize() * inputMappingActor->GetCurrentWorldScale();
         if(inside && localX >= 0.f && localX <= actorSize.x && localY >= 0.f && localY <= actorSize.y)
         {
           screenCoords.x = localX;
@@ -543,6 +543,34 @@ bool RenderTask::TranslateCoordinates(Vector2& screenCoords) const
   return inside;
 }
 
+void RenderTask::GetHittableViewport(Viewport& viewPort) const
+{
+  if(GetRenderTaskSceneObject())
+  {
+    if(GetInputEnabled())
+    {
+      if(mFrameBuffer)
+      {
+        auto mappingActor = GetScreenToFrameBufferMappingActor();
+        if(mappingActor)
+        {
+          Actor& inputMappingActor = GetImplementation(mappingActor);
+
+          Vector3 actorSize = inputMappingActor.GetCurrentSize() * inputMappingActor.GetCurrentWorldScale();
+
+          viewPort.x = viewPort.y = 0;
+          viewPort.width          = static_cast<int32_t>(actorSize.x + 0.5f); // rounded
+          viewPort.height         = static_cast<int32_t>(actorSize.y + 0.5f); // rounded
+        }
+      }
+      else
+      {
+        GetViewport(viewPort);
+      }
+    }
+  }
+}
+
 bool RenderTask::WorldToViewport(const Vector3& position, float& viewportX, float& viewportY) const
 {
   CameraActor* cam = GetCameraActor();
index 2e49900..4be6c7f 100644 (file)
@@ -243,6 +243,13 @@ public:
   bool TranslateCoordinates(Vector2& screenCoords) const;
 
   /**
+   * @brief Get Viewport when we need to be used with translated screen coordinated when render task is offscreen.
+   * It will be used when we hit-test.
+   * @param[out] viewPort instance to copy the values into
+   */
+  void GetHittableViewport(Viewport& viewPort) const;
+
+  /**
    * @copydoc Dali::RenderTask::WorldToViewport()
    */
   bool WorldToViewport(const Vector3& position, float& viewportX, float& viewportY) const;
@@ -358,8 +365,8 @@ protected:
   ~RenderTask() override;
 
 private: // not copyable
-  RenderTask()                             = delete;
-  RenderTask(const RenderTask&)            = delete;
+  RenderTask()                  = delete;
+  RenderTask(const RenderTask&) = delete;
   RenderTask& operator=(const RenderTask&) = delete;
 
 private:
@@ -369,12 +376,12 @@ private:
   WeakHandle<Dali::Actor> mInputMappingActor;  /// used to mapping screen to frame buffer coordinate, not kept alive by rendertask
   RenderTaskList&         mRenderTaskList;     ///< The render task list
 
-  Vector4 mClearColor;                         ///< Optional clear color
+  Vector4 mClearColor; ///< Optional clear color
 
-  Vector2 mViewportPosition;                   ///< The cached viewport position
-  Vector2 mViewportSize;                       ///< The cached viewport size
+  Vector2 mViewportPosition; ///< The cached viewport position
+  Vector2 mViewportSize;     ///< The cached viewport size
 
-  uint32_t mRefreshRate;                       ///< Determines how often the task is processed.
+  uint32_t mRefreshRate; ///< Determines how often the task is processed.
 
   uint32_t mRefreshOnceCounter;