Adding bullet physics files 64/297064/23
authorDavid Steele <david.steele@samsung.com>
Wed, 9 Aug 2023 17:21:38 +0000 (18:21 +0100)
committerDavid Steele <david.steele@samsung.com>
Tue, 29 Aug 2023 13:54:29 +0000 (14:54 +0100)
Created DALi version of bullet-physics-demo from
github.sec.samsung.net/dalihub/dali-physics-demo

Hidden details of ScopedAccessor
Changed how transform works
Updated debug renderer
Added test cases for PhysicsAdaptor
Added test cases for PhysicsActor
Fixed quaternion handling to work with mirrored x/y/z coord system

Change-Id: I7292a2fbf73ef82a8b5e43701489b460ea36b27d

35 files changed:
.gitignore
automated-tests/patch-coverage.pl
automated-tests/src/dali-physics/CMakeLists.txt [new file with mode: 0644]
automated-tests/src/dali-physics/tct-dali-physics-core.cpp [new file with mode: 0644]
automated-tests/src/dali-physics/utc-Dali-PhysicsActor.cpp [new file with mode: 0644]
automated-tests/src/dali-physics/utc-Dali-PhysicsAdaptor.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/CMakeLists.txt
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/test-harness.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-direct-rendering-egl.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.h
automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp [deleted file]
build/tizen/.gitignore
build/tizen/dali-physics/CMakeLists.txt
build/tizen/dali-physics/dali2-physics-3d.pc.in
dali-physics/README.md [new file with mode: 0644]
dali-physics/dali-physics.h [new file with mode: 0644]
dali-physics/internal/file.list [new file with mode: 0644]
dali-physics/internal/physics-actor-impl.cpp [new file with mode: 0644]
dali-physics/internal/physics-actor-impl.h [new file with mode: 0644]
dali-physics/internal/physics-adaptor-impl.cpp [new file with mode: 0644]
dali-physics/internal/physics-adaptor-impl.h [new file with mode: 0644]
dali-physics/internal/physics-debug-renderer.cpp [new file with mode: 0644]
dali-physics/internal/physics-debug-renderer.h [new file with mode: 0644]
dali-physics/internal/physics-world-impl.cpp [new file with mode: 0644]
dali-physics/internal/physics-world-impl.h [new file with mode: 0644]
dali-physics/public-api/file.list [new file with mode: 0644]
dali-physics/public-api/physics-actor.cpp [new file with mode: 0644]
dali-physics/public-api/physics-actor.h [new file with mode: 0644]
dali-physics/public-api/physics-adaptor.cpp [new file with mode: 0644]
dali-physics/public-api/physics-adaptor.h [new file with mode: 0644]
dali-scene3d/internal/loader/gltf2-util.cpp
dali-scene3d/public-api/loader/camera-parameters.cpp
dali-scene3d/public-api/loader/camera-parameters.h
packaging/dali-toolkit.spec

index 2c8d0d1..4555b92 100644 (file)
@@ -41,7 +41,20 @@ install_manifest.txt
 /build/tizen/doc
 /build/tizen/.cov
 /build/tizen/CPack*.cmake
-/build/tizen/dali-physics/*
+
+/build/tizen/.ninja_deps
+/build/tizen/.ninja_log
+/build/tizen/build.ninja
+/build/tizen/rules.ninja
+
+/build/tizen/dali-physics/*.cmake
+/build/tizen/dali-physics/Makefile
+/build/tizen/dali-physics/CMakeCache.txt
+/build/tizen/dali-physics/CMakeFiles/
+/build/tizen/dali-physics/cmake_install.cmake
+/build/tizen/dali-physics/bullet3/
+/build/tizen/dali-physics/chipmunk2d/
+
 /build/desktop
 /packaging/home*
 compile_commands.json
index 2f8bdb4..38fc2b0 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/perl
 #
-# Copyright (c) 2020 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.
@@ -985,7 +985,7 @@ sub parse_diff
     $files{$file}->{"patch"} = [@checklines];
     $files{$file}->{"b_lines"} = {%b_lines};
 
-    my %filter = map { $_ => $files{$_} } grep {m!^dali(-toolkit|-scene3d)?/!} (keys(%files));
+    my %filter = map { $_ => $files{$_} } grep {m!^dali(-toolkit|-scene3d|-physics)?/!} (keys(%files));
 
     if($pd_debug)
     {
diff --git a/automated-tests/src/dali-physics/CMakeLists.txt b/automated-tests/src/dali-physics/CMakeLists.txt
new file mode 100644 (file)
index 0000000..b6beab1
--- /dev/null
@@ -0,0 +1,99 @@
+SET(PKG_NAME "dali-physics")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+SET(RPM_NAME "core-${PKG_NAME}-tests")
+
+SET(CAPI_LIB "dali-physics")
+
+# List of test case sources (Only these get parsed for test cases)
+SET(TC_SOURCES
+  utc-Dali-PhysicsAdaptor.cpp
+  utc-Dali-PhysicsActor.cpp
+  )
+
+# List of test harness files (Won't get parsed for test cases)
+SET(TEST_HARNESS_DIR "../dali-toolkit/dali-toolkit-test-utils")
+
+SET(TEST_HARNESS_SOURCES
+  ${TEST_HARNESS_DIR}/toolkit-adaptor.cpp
+  ${TEST_HARNESS_DIR}/toolkit-application.cpp
+  ${TEST_HARNESS_DIR}/toolkit-direct-rendering-egl.cpp
+  ${TEST_HARNESS_DIR}/toolkit-event-thread-callback.cpp
+  ${TEST_HARNESS_DIR}/toolkit-environment-variable.cpp
+  ${TEST_HARNESS_DIR}/toolkit-input-method-context.cpp
+  ${TEST_HARNESS_DIR}/toolkit-input-method-options.cpp
+  ${TEST_HARNESS_DIR}/toolkit-lifecycle-controller.cpp
+  ${TEST_HARNESS_DIR}/toolkit-orientation.cpp
+  ${TEST_HARNESS_DIR}/toolkit-style-monitor.cpp
+  ${TEST_HARNESS_DIR}/toolkit-test-application.cpp
+  ${TEST_HARNESS_DIR}/toolkit-timer.cpp
+  ${TEST_HARNESS_DIR}/toolkit-trigger-event-factory.cpp
+  ${TEST_HARNESS_DIR}/toolkit-window.cpp
+  ${TEST_HARNESS_DIR}/toolkit-scene-holder.cpp
+  ${TEST_HARNESS_DIR}/dali-test-suite-utils.cpp
+  ${TEST_HARNESS_DIR}/dali-toolkit-test-suite-utils.cpp
+  ${TEST_HARNESS_DIR}/dummy-control.cpp
+  ${TEST_HARNESS_DIR}/mesh-builder.cpp
+  ${TEST_HARNESS_DIR}/test-actor-utils.cpp
+  ${TEST_HARNESS_DIR}/test-animation-data.cpp
+  ${TEST_HARNESS_DIR}/test-application.cpp
+  ${TEST_HARNESS_DIR}/test-button.cpp
+  ${TEST_HARNESS_DIR}/test-harness.cpp
+  ${TEST_HARNESS_DIR}/test-gesture-generator.cpp
+  ${TEST_HARNESS_DIR}/test-gl-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-sync-impl.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-sync-object.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-buffer.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-command-buffer.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-framebuffer.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-controller.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-texture.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-sampler.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-program.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-pipeline.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-shader.cpp
+  ${TEST_HARNESS_DIR}/test-graphics-reflection.cpp
+  ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
+)
+
+PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
+  dali2-core
+  dali2-adaptor
+  dali2-toolkit
+  dali2-physics-3d
+  bullet3
+)
+
+ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED)
+ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} )
+
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
+FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
+    SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
+ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
+
+INCLUDE_DIRECTORIES(
+    ../../../
+    ${${CAPI_LIB}_INCLUDE_DIRS}
+    ../dali-toolkit/dali-toolkit-test-utils
+)
+
+ADD_CUSTOM_COMMAND(
+  COMMAND ${SCRIPT_DIR}/tcheadgen.sh ${EXEC_NAME}.h ${TC_SOURCES}
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  OUTPUT ${EXEC_NAME}.h
+  COMMENT "Generating test tables"
+  )
+
+ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.h ${EXEC_NAME}.cpp ${TC_SOURCES} ${TEST_HARNESS_SOURCES})
+TARGET_LINK_LIBRARIES(${EXEC_NAME}
+    ${${CAPI_LIB}_LIBRARIES}
+    -lpthread -ldl --coverage
+)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+    DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
diff --git a/automated-tests/src/dali-physics/tct-dali-physics-core.cpp b/automated-tests/src/dali-physics/tct-dali-physics-core.cpp
new file mode 100644 (file)
index 0000000..64550a1
--- /dev/null
@@ -0,0 +1,7 @@
+#include <test-harness.h>
+#include "tct-dali-physics-core.h"
+
+int main(int argc, char * const argv[])
+{
+  return TestHarness::RunTests(argc, argv, tc_array);
+}
diff --git a/automated-tests/src/dali-physics/utc-Dali-PhysicsActor.cpp b/automated-tests/src/dali-physics/utc-Dali-PhysicsActor.cpp
new file mode 100644 (file)
index 0000000..e1266e8
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <bullet/btBulletDynamicsCommon.h>
+#include <stdlib.h>
+#include <iostream>
+
+// Need to override adaptor classes for toolkit test harness, so include
+// test harness headers before dali headers.
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+
+#include <dali-physics/dali-physics.h>
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali-toolkit/devel-api/controls/alignment/alignment.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit::Physics;
+
+extern btRigidBody* CreateBody(btDiscreteDynamicsWorld* bulletWorld);
+
+const char* BALL_IMAGE = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
+
+int UtcDaliPhysicsActorNew(void)
+{
+  ToolkitTestApplication application;
+
+  btRigidBody* body{nullptr};
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  auto accessor         = adaptor.GetPhysicsAccessor();
+  auto bulletWorld      = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                  = CreateBody(bulletWorld);
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+
+  DALI_TEST_CHECK(physicsActor);
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorDownCastP(void)
+{
+  ToolkitTestApplication application;
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  btRigidBody* body{nullptr};
+  auto         accessor     = adaptor.GetPhysicsAccessor();
+  auto         bulletWorld  = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                      = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New(BALL_IMAGE);
+  PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+  BaseHandle   handle(physicsActor);
+
+  PhysicsActor actor2 = PhysicsActor::DownCast(handle);
+  DALI_TEST_CHECK(actor2);
+  DALI_TEST_EQUALS(physicsActor.GetId(), actor2.GetId(), TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorDownCastN(void)
+{
+  BaseHandle   uninitializedHandle;
+  PhysicsActor actor = PhysicsActor::DownCast(uninitializedHandle);
+  DALI_TEST_CHECK(!actor);
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorMoveConstructor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the move constructor");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  btRigidBody* body{nullptr};
+  auto         accessor     = adaptor.GetPhysicsAccessor();
+  auto         bulletWorld  = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                      = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New(BALL_IMAGE);
+  PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+
+  DALI_TEST_CHECK(physicsActor);
+  uint32_t id = physicsActor.GetId();
+
+  PhysicsActor moved = std::move(physicsActor);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_CHECK(!physicsActor);
+  DALI_TEST_CHECK(moved != physicsActor);
+  DALI_TEST_EQUALS(moved.GetId(), id, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorCopyConstructor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the move constructor");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  btRigidBody* body{nullptr};
+  auto         accessor     = adaptor.GetPhysicsAccessor();
+  auto         bulletWorld  = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                      = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New(BALL_IMAGE);
+  PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+
+  DALI_TEST_CHECK(physicsActor);
+  uint32_t id = physicsActor.GetId();
+
+  PhysicsActor selectedActor(physicsActor);
+  DALI_TEST_CHECK(selectedActor);
+  DALI_TEST_CHECK(physicsActor);
+  DALI_TEST_CHECK(selectedActor == physicsActor); // should point at same object
+  DALI_TEST_EQUALS(selectedActor.GetId(), id, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorCopyAssign(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the copy assign");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  btRigidBody* body{nullptr};
+  auto         accessor     = adaptor.GetPhysicsAccessor();
+  auto         bulletWorld  = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                      = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New(BALL_IMAGE);
+  PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+
+  DALI_TEST_CHECK(physicsActor);
+  uint32_t id = physicsActor.GetId();
+
+  PhysicsActor selectedActor = physicsActor;
+  DALI_TEST_CHECK(selectedActor);
+  DALI_TEST_CHECK(physicsActor);
+  DALI_TEST_CHECK(selectedActor == physicsActor); // should point at same object
+  DALI_TEST_EQUALS(selectedActor.GetId(), id, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the move constructor");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  btRigidBody* body{nullptr};
+  auto         accessor     = adaptor.GetPhysicsAccessor();
+  auto         bulletWorld  = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                      = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New(BALL_IMAGE);
+  PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+
+  DALI_TEST_CHECK(physicsActor);
+  uint32_t id = physicsActor.GetId();
+
+  PhysicsActor moved;
+  moved = std::move(physicsActor);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_CHECK(!physicsActor);
+  DALI_TEST_EQUALS(moved.GetId(), id, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetIdP(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the ID Getter");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  btRigidBody* body{nullptr};
+  auto         accessor     = adaptor.GetPhysicsAccessor();
+  auto         bulletWorld  = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+  body                      = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New(BALL_IMAGE);
+  PhysicsActor physicsActor = adaptor.AddActorBody(ballActor, body);
+  int          id           = physicsActor.GetId();
+  int          actorId      = ballActor[Actor::Property::ID];
+  DALI_TEST_EQUALS(id, actorId, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetIdN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the ID Getter");
+
+  PhysicsActor physicsActor;
+  try
+  {
+    uint32_t id __attribute__((unused)) = physicsActor.GetId();
+    tet_result(TET_FAIL);
+  }
+  catch(DaliException e)
+  {
+    DALI_TEST_ASSERT(e, "Physics actor handle is empty", TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetBodyP(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the body Getter");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  PhysicsActor physicsActor;
+  btRigidBody* body{nullptr};
+  {
+    auto accessor         = adaptor.GetPhysicsAccessor();
+    auto bulletWorld      = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body                  = CreateBody(bulletWorld);
+    Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+    physicsActor = adaptor.AddActorBody(ballActor, body);
+  }
+
+  application.Render();
+  Test::WaitForEventThreadTrigger(1);
+
+  Dali::Any any = physicsActor.GetBody();
+  DALI_TEST_EQUALS(any.Get<btRigidBody*>(), body, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetBodyN(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the ID Getter");
+
+  PhysicsActor physicsActor;
+  try
+  {
+    Dali::Any any __attribute__((unused)) = physicsActor.GetBody();
+    tet_result(TET_FAIL);
+  }
+  catch(DaliException e)
+  {
+    DALI_TEST_ASSERT(e, "Physics actor handle is empty", TEST_LOCATION);
+  }
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorSetPosition(void)
+{
+  tet_infoline("Test the AsyncSetPhysicsPosition() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsPosition(Vector3(10, 20, -30));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+    auto actor    = rootActor.FindChildById(physicsActor.GetId());
+    // Warning - physics properties are never reflected in the event size cache.
+    // Have to use GetCurrentProperty to see the updated values.
+    DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), Vector3(10, 20, -30), 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorSetRotation1(void)
+{
+  tet_infoline("Test the AsyncSetPhysicsRotation() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsRotation(Quaternion(Degree(30), Vector3::YAXIS));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+    auto actor    = rootActor.FindChildById(physicsActor.GetId());
+    // Warning - physics properties are never reflected in the event size cache.
+    // Have to use GetCurrentProperty to see the updated values.
+    Quaternion q = actor.GetCurrentProperty<Quaternion>(Actor::Property::ORIENTATION);
+    Quaternion expected(Degree(30), Vector3::YAXIS);
+    DALI_TEST_EQUALS(q, expected, 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorSetRotation2(void)
+{
+  tet_infoline("Test the AsyncSetPhysicsRotation() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsRotation(Quaternion(Degree(30), Vector3::ZAXIS));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+    auto actor    = rootActor.FindChildById(physicsActor.GetId());
+    // Warning - physics properties are never reflected in the event size cache.
+    // Have to use GetCurrentProperty to see the updated values.
+    Quaternion q = actor.GetCurrentProperty<Quaternion>(Actor::Property::ORIENTATION);
+    Quaternion expected(Degree(30), Vector3::ZAXIS);
+    DALI_TEST_EQUALS(q, expected, 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetActorPosition(void)
+{
+  tet_infoline("Test the GetActorPosition() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsPosition(Vector3(10, 20, -30));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+    DALI_TEST_EQUALS(physicsActor.GetActorPosition(), Vector3(10, 20, -30), 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetActorRotation(void)
+{
+  tet_infoline("Test the GetActorRotation() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsRotation(Quaternion(Degree(30), Vector3::ZAXIS));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+    DALI_TEST_EQUALS(physicsActor.GetActorRotation(), Quaternion(Degree(30), Vector3::ZAXIS), 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetPhysicsPosition(void)
+{
+  tet_infoline("Test the GetPhysicsPosition() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsPosition(Vector3(10, 20, -30));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto    accessor = adaptor.GetPhysicsAccessor();
+    Vector4 pos      = transform * Vector4(10, 20, -30, 1);
+    DALI_TEST_EQUALS(physicsActor.GetPhysicsPosition(), Vector3(pos), 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsActorGetPhysicsRotation(void)
+{
+  tet_infoline("Test the GetPhysicsRotation() function");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+  btRigidBody* body{nullptr};
+  PhysicsActor physicsActor;
+  {
+    auto accessor    = adaptor.GetPhysicsAccessor();
+    auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body             = CreateBody(bulletWorld);
+    physicsActor     = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsRotation(Quaternion(Degree(30), Vector3::ZAXIS));
+  }
+
+  Test::WaitForEventThreadTrigger(1);
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  // Run 2 frames to ensure both buffers are set.
+  application.SendNotification();
+  application.Render();
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+    DALI_TEST_EQUALS(physicsActor.GetPhysicsRotation(), Quaternion(Degree(-30), Vector3::ZAXIS), 0.0001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-physics/utc-Dali-PhysicsAdaptor.cpp b/automated-tests/src/dali-physics/utc-Dali-PhysicsAdaptor.cpp
new file mode 100644 (file)
index 0000000..01dae19
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <iostream>
+#include <typeinfo>
+
+// Need to override adaptor classes for toolkit test harness, so include
+// test harness headers before dali headers.
+#include <dali-physics/dali-physics.h>
+#include <dali-toolkit-test-suite-utils.h>
+#include <toolkit-event-thread-callback.h>
+
+#include <dali-toolkit/devel-api/controls/alignment/alignment.h>
+#include <dali-toolkit/public-api/controls/image-view/image-view.h>
+#include <dali/devel-api/adaptor-framework/window-devel.h>
+#include <dali/devel-api/events/hit-test-algorithm.h>
+
+#include <bullet/btBulletDynamicsCommon.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit::Physics;
+
+void utc_dali_physics3d_startup(void)
+{
+  test_return_value = TET_UNDEF;
+}
+
+void utc_dali_physics3d_cleanup(void)
+{
+  test_return_value = TET_PASS;
+}
+
+btRigidBody* CreateBody(btDiscreteDynamicsWorld* bulletWorld)
+{
+  btSphereShape* ball = new btSphereShape(30);
+  btVector3      localInertia(0.f, 0.f, 0.f);
+  ball->calculateLocalInertia(10, localInertia);
+  btTransform transform;
+  transform.setIdentity();
+  auto* motionState = new btDefaultMotionState(transform);
+
+  btRigidBody::btRigidBodyConstructionInfo ci(10, motionState, ball, localInertia);
+
+  btRigidBody* body = new btRigidBody(ci);
+  body->setFriction(0.5f);
+  body->setRestitution(0.5f);
+  bulletWorld->addRigidBody(body);
+  return body;
+}
+
+int UtcDaliPhysicsCreateAdaptorP1(void)
+{
+  ToolkitTestApplication application;
+
+  Matrix     transform(true);
+  Uint16Pair size(640, 480);
+
+  PhysicsAdaptor handle = PhysicsAdaptor::New(transform, size);
+  DALI_TEST_CHECK(handle);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsCreateAdaptorN1(void)
+{
+  ToolkitTestApplication application;
+
+  PhysicsAdaptor handle;
+  DALI_TEST_CHECK(!handle);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsDowncastP1(void)
+{
+  ToolkitTestApplication application;
+
+  Matrix     transform(true);
+  Uint16Pair size(640, 480);
+
+  BaseHandle handle = PhysicsAdaptor::New(transform, size);
+
+  auto adaptor = PhysicsAdaptor::DownCast(handle);
+  DALI_TEST_CHECK(adaptor);
+  //Following only works if type is registered
+  //DALI_TEST_EQUALS("PhysicsAdaptor", adaptor.GetTypeName(), TEST_LOCATION);
+  END_TEST;
+}
+
+int UtcDaliPhysicsDowncastN1(void)
+{
+  BaseHandle handle;
+  auto       adaptor = PhysicsAdaptor::DownCast(handle);
+  DALI_TEST_CHECK(!adaptor);
+
+  DALI_TEST_CHECK(typeid(PhysicsAdaptor) == typeid(decltype(adaptor)));
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorMoveConstructor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the move constructor");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  DALI_TEST_CHECK(adaptor);
+
+  PhysicsAdaptor moved = std::move(adaptor);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_CHECK(!adaptor);
+  DALI_TEST_CHECK(moved != adaptor);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorCopyConstructor(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the move constructor");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  DALI_TEST_CHECK(adaptor);
+
+  PhysicsAdaptor altAdaptor = adaptor;
+  DALI_TEST_CHECK(altAdaptor);
+  DALI_TEST_CHECK(adaptor);
+  DALI_TEST_CHECK(altAdaptor == adaptor); // should point at same object
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorCopyAssign(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the copy assign");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  DALI_TEST_CHECK(adaptor);
+
+  PhysicsAdaptor altAdaptor = adaptor;
+  DALI_TEST_CHECK(altAdaptor);
+  DALI_TEST_CHECK(adaptor);
+  DALI_TEST_CHECK(altAdaptor == adaptor); // should point at same object
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorMoveAssignment(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("Testing the move constructor");
+
+  Matrix         transform(true);
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  DALI_TEST_CHECK(adaptor);
+
+  PhysicsAdaptor moved;
+  moved = std::move(adaptor);
+  DALI_TEST_CHECK(moved);
+  DALI_TEST_CHECK(!adaptor);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsSetTimestep(void)
+{
+  ToolkitTestApplication application;
+
+  Matrix     transform(true);
+  Uint16Pair size(640, 480);
+
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  adaptor.SetTimestep(1.0f / 60.0f);
+
+  DALI_TEST_EQUALS(adaptor.GetTimestep(), 1.0f / 60.0f, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsGetTimestep(void)
+{
+  ToolkitTestApplication application;
+
+  Matrix     transform(true);
+  Uint16Pair size(640, 480);
+
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  adaptor.SetTimestep(1.0f / 60.0f);
+  float timestep = adaptor.GetTimestep();
+  float expected = 1.0f / 60.0f;
+  DALI_TEST_EQUALS(timestep, expected, 0.0001f, TEST_LOCATION);
+
+  adaptor.SetTimestep(1.0f / 120.0f);
+  timestep = adaptor.GetTimestep();
+  expected = 1.0f / 120.0f;
+  DALI_TEST_EQUALS(timestep, expected, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsGetPhysicsAccessorP1(void)
+{
+  ToolkitTestApplication application;
+
+  Matrix     transform(true);
+  Uint16Pair size(640, 480);
+
+  PhysicsAdaptor                           adaptor  = PhysicsAdaptor::New(transform, size);
+  PhysicsAdaptor::ScopedPhysicsAccessorPtr accessor = adaptor.GetPhysicsAccessor();
+  DALI_TEST_CHECK(accessor.get() != nullptr);
+
+  Dali::Any world = accessor->GetNative();
+  DALI_TEST_CHECK(!world.Empty());
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsGetPhysicsAccessorN1(void)
+{
+  ToolkitTestApplication application;
+
+  PhysicsAdaptor handle;
+  DALI_TEST_CHECK(!handle);
+
+  try
+  {
+    auto ptr = handle.GetPhysicsAccessor();
+    DALI_TEST_CHECK(ptr == nullptr);
+
+    tet_result(TET_FAIL);
+  }
+  catch(DaliException& e)
+  {
+    DALI_TEST_ASSERT(e, "Physics adaptor handle is empty", TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorGetRootActor(void)
+{
+  tet_infoline("Test that the root actor can be retrieved");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  DALI_TEST_CHECK(rootActor);
+  DALI_TEST_EQUALS(rootActor.GetProperty<Vector2>(Actor::Property::SIZE), Vector2(640.0f, 480.0f), 0.001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorCreateDebugLayer(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(true);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair size(640, 480);
+  auto       scene = application.GetScene();
+
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+  Window window = DevelWindow::Get(rootActor);
+
+  Layer layer = adaptor.CreateDebugLayer(window);
+  DALI_TEST_CHECK(layer);
+
+  adaptor.SetDebugState(PhysicsAdaptor::DebugState::ON);
+
+  btRigidBody* body{nullptr};
+  {
+    auto accessor            = adaptor.GetPhysicsAccessor();
+    auto bulletWorld         = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body                     = CreateBody(bulletWorld);
+    Dali::Actor ballActor    = Toolkit::ImageView::New(TEST_RESOURCE_DIR "/gallery-small-1.jpg");
+    auto        physicsActor = adaptor.AddActorBody(ballActor, body);
+    physicsActor.AsyncSetPhysicsPosition(Vector3(0.f, 0.f, 0.f));
+  }
+  Test::WaitForEventThreadTrigger(1);
+  application.SendNotification();
+  application.Render();
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorTranslateToPhysicsSpace1(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Vector3 a(30, 20, 10);
+  Vector3 expected = a * 2.0f;
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(a), expected, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorTranslateToPhysicsSpace2(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using an alternative scale doesn't change rotation");
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  // Rotation shouldn't change under this scale
+  Quaternion q(Degree(30.0f), Vector3::XAXIS);
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(q), q, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorTranslateToPhysicsSpace3(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using an inverted Y scale also inverts quaternions");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Quaternion q(Degree(30.0f), Vector3::ZAXIS);
+  Quaternion qp(Degree(-30.0f), Vector3::ZAXIS); // We have mirrored along Y axis, so Z rot is opposite.
+
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(q), qp, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorTranslateToPhysicsSpace4(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using an inverted Y scale also inverts quaternions");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Quaternion q(Degree(30.0f), Vector3::XAXIS);
+  Quaternion qp(Degree(-30.0f), Vector3::XAXIS); // We have mirrored along Y axis, so Z rot is opposite.
+
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(q), qp, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorTranslateToPhysicsSpace5(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using an inverted Y scale also inverts quaternions, except along Y axis");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Quaternion q(Degree(30.0f), Vector3::YAXIS);
+  Quaternion qp(Degree(30.0f), Vector3::YAXIS);
+
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(q), qp, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorTranslateFromPhysicsSpace1(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using a double scale halves position");
+
+  transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Vector3 position(20.0f, 20.0f, 0.0f);
+  Vector3 expected(10.0f, -10.0f, 0.0f);
+
+  DALI_TEST_EQUALS(adaptor.TranslateFromPhysicsSpace(position), expected, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorConvertVectorToPhysicsSpace01(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using a translation does not translate vector");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, 1.0f, 1.0f));
+  transform.SetTranslation(Vector3(0.0f, 100.0f, 0.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  Vector3        vector(20.0f, 20.0f, 0.0f);
+  DALI_TEST_EQUALS(adaptor.ConvertVectorToPhysicsSpace(vector), vector, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorConvertVectorToPhysicsSpace02(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using a translation with inverse Y does not translate vector");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+  transform.SetTranslation(Vector3(0.0f, 100.0f, 0.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  Vector3        vector(20.0f, 20.0f, 0.0f);
+  Vector3        expected(20.0f, -20.0f, 0.0f);
+  DALI_TEST_EQUALS(adaptor.ConvertVectorToPhysicsSpace(vector), expected, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorConvertVectorFromPhysicsSpace01(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using a translation does not translate vector");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, 1.0f, 1.0f));
+  transform.SetTranslation(Vector3(0.0f, 100.0f, 0.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  Vector3        vector(20.0f, 20.0f, 0.0f);
+  DALI_TEST_EQUALS(adaptor.ConvertVectorFromPhysicsSpace(vector), vector, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorConvertVectorFromPhysicsSpace02(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  tet_infoline("Test that using a translation with inverse Y does not translate vector");
+
+  transform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+  transform.SetTranslation(Vector3(0.0f, 100.0f, 0.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+  Vector3        vector(20.0f, 20.0f, 0.0f);
+  Vector3        expected(20.0f, -20.0f, 0.0f);
+  DALI_TEST_EQUALS(adaptor.ConvertVectorFromPhysicsSpace(vector), expected, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorSetTransformAndSize(void)
+{
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Vector3 a(30, 20, 10);
+  Vector3 expected = a * 2.0f;
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(a), expected, 0.0001f, TEST_LOCATION);
+
+  transform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+  transform.SetTranslation(Vector3(0.0f, 100.0f, 0.0f));
+  adaptor.SetTransformAndSize(transform, size);
+
+  Vector3 expect2(30, 80, 10);
+  DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(a), expect2, 0.0001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorSetIntegrationState(void)
+{
+  tet_infoline("Test that changing the integration state is reflected");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  DALI_TEST_CHECK(adaptor.GetIntegrationState() == PhysicsAdaptor::IntegrationState::ON);
+
+  adaptor.SetIntegrationState(PhysicsAdaptor::IntegrationState::OFF);
+  DALI_TEST_CHECK(adaptor.GetIntegrationState() == PhysicsAdaptor::IntegrationState::OFF);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorGetIntegrationState(void)
+{
+  tet_infoline("Test that changing the integration state is reflected");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  adaptor.SetIntegrationState(PhysicsAdaptor::IntegrationState::OFF);
+  DALI_TEST_CHECK(adaptor.GetIntegrationState() == PhysicsAdaptor::IntegrationState::OFF);
+
+  adaptor.SetIntegrationState(PhysicsAdaptor::IntegrationState::ON);
+  DALI_TEST_CHECK(adaptor.GetIntegrationState() == PhysicsAdaptor::IntegrationState::ON);
+
+  // Can't test actual integration step runs without adding actors - see utc-Dali-PhysicsActor.cpp.
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorSetDebugState(void)
+{
+  tet_infoline("Test that changing the debug state is reflected");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  DALI_TEST_CHECK(adaptor.GetDebugState() == PhysicsAdaptor::DebugState::OFF);
+
+  adaptor.SetDebugState(PhysicsAdaptor::DebugState::ON);
+  DALI_TEST_CHECK(adaptor.GetDebugState() == PhysicsAdaptor::DebugState::ON);
+
+  adaptor.SetDebugState(PhysicsAdaptor::DebugState::OFF);
+  DALI_TEST_CHECK(adaptor.GetDebugState() == PhysicsAdaptor::DebugState::OFF);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorGetDebugState(void)
+{
+  tet_infoline("Test that changing the debug state is reflected");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  adaptor.SetDebugState(PhysicsAdaptor::DebugState::OFF);
+  DALI_TEST_CHECK(adaptor.GetDebugState() == PhysicsAdaptor::DebugState::OFF);
+
+  adaptor.SetDebugState(PhysicsAdaptor::DebugState::ON);
+  DALI_TEST_CHECK(adaptor.GetDebugState() == PhysicsAdaptor::DebugState::ON);
+
+  // Can't test actual debug step runs without adding actors - see utc-Dali-PhysicsActor.cpp.
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorAddActorBody(void)
+{
+  tet_infoline("Test that an actor/body pair can be added");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  auto accessor    = adaptor.GetPhysicsAccessor();
+  auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+
+  btRigidBody* body         = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New("gallery-small-1.jpg");
+  auto         physicsActor = adaptor.AddActorBody(ballActor, body);
+
+  DALI_TEST_CHECK(physicsActor);
+  int id = ballActor[Actor::Property::ID];
+
+  DALI_TEST_EQUALS(physicsActor.GetId(), id, TEST_LOCATION);
+  DALI_TEST_EQUALS(physicsActor.GetBody().Get<btRigidBody*>(), body, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorGetPhysicsActor(void)
+{
+  tet_infoline("Test that an actor/body pair can be retrieved");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  auto           scene     = application.GetScene();
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  scene.Add(rootActor);
+
+  auto accessor    = adaptor.GetPhysicsAccessor();
+  auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+
+  btRigidBody* body         = CreateBody(bulletWorld);
+  Dali::Actor  ballActor    = Toolkit::ImageView::New("gallery-small-1.jpg");
+  auto         physicsActor = adaptor.AddActorBody(ballActor, body);
+
+  DALI_TEST_CHECK(physicsActor);
+
+  PhysicsActor testActor = adaptor.GetPhysicsActor(body);
+  DALI_TEST_CHECK(testActor);
+  DALI_TEST_CHECK(physicsActor == testActor);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorBuildPickingRay(void)
+{
+  tet_infoline("Test that a touch can be converted to a picking ray");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  Vector3 from, to;
+  adaptor.BuildPickingRay(Vector3(), -Vector3::ZAXIS, from, to);
+
+  DALI_TEST_EQUALS(from, Vector3(), 0.001f, TEST_LOCATION);
+  DALI_TEST_EQUALS(to, Vector3(0.0f, 0.0f, -20000.0f), 0.001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorProjectPoint(void)
+{
+  tet_infoline("Test that a point is projected into physics space");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+  // distance is in physics units, not DALi units!
+  Vector3 projectedPoint = adaptor.ProjectPoint(Vector3(), -Vector3::ZAXIS, 200);
+
+  DALI_TEST_EQUALS(projectedPoint, Vector3(0.0f, 0.0f, -200.0f), 0.001f, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorQueue(void)
+{
+  tet_infoline("Test that Queue and CreateSyncPoint both work");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  btRigidBody* body{nullptr};
+  {
+    auto accessor            = adaptor.GetPhysicsAccessor();
+    auto bulletWorld         = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body                     = CreateBody(bulletWorld);
+    Dali::Actor ballActor    = Toolkit::ImageView::New("gallery-small-1.jpg");
+    auto        physicsActor = adaptor.AddActorBody(ballActor, body);
+  }
+
+  tet_infoline("Test that Queue works without accessor");
+  adaptor.Queue([body]() {
+    body->getWorldTransform().setOrigin(btVector3(100.0f, 20.0f, 20.0f));
+  });
+  adaptor.CreateSyncPoint();
+
+  application.SendNotification();
+  application.Render();
+  // Should trigger an Update
+
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+
+    btVector3 origin = body->getWorldTransform().getOrigin();
+    DALI_TEST_EQUALS(origin.x(), 100.0f, 0.001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(origin.y(), 20.0f, 0.001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(origin.z(), 20.0f, 0.001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorCreateSyncPoint(void)
+{
+  tet_infoline("Test that a delayed CreateSyncPoint delays update");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 2.0f));
+  Uint16Pair     size(640, 480);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  btRigidBody* body{nullptr};
+  {
+    auto accessor            = adaptor.GetPhysicsAccessor();
+    auto bulletWorld         = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    body                     = CreateBody(bulletWorld);
+    Dali::Actor ballActor    = Toolkit::ImageView::New("gallery-small-1.jpg");
+    auto        physicsActor = adaptor.AddActorBody(ballActor, body);
+
+    tet_infoline("Test that Queue works with accessor");
+    adaptor.Queue([body]() {
+      body->getWorldTransform().setOrigin(btVector3(100.0f, 20.0f, 20.0f));
+    });
+  }
+
+  // Should trigger an Update without processing queue
+  application.SendNotification();
+  application.Render();
+
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+
+    btVector3 origin = body->getWorldTransform().getOrigin();
+    DALI_TEST_EQUALS(origin.x(), 0.0f, 0.001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(origin.y(), 0.0f, 0.001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(origin.z(), 0.0f, 0.001f, TEST_LOCATION);
+  }
+
+  // Should now execute queue
+  adaptor.CreateSyncPoint();
+  application.SendNotification();
+  application.Render();
+
+  {
+    auto accessor = adaptor.GetPhysicsAccessor();
+
+    btVector3 origin = body->getWorldTransform().getOrigin();
+    DALI_TEST_EQUALS(origin.x(), 100.0f, 0.001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(origin.y(), 20.0f, 0.001f, TEST_LOCATION);
+    DALI_TEST_EQUALS(origin.z(), 20.0f, 0.001f, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliPhysicsAdaptorHitTestP(void)
+{
+  tet_infoline("Test that hit testing finds a body");
+
+  ToolkitTestApplication application;
+  Matrix                 transform(false);
+  transform.SetIdentityAndScale(Vector3(1.0f, 1.0f, 1.0f));
+  Uint16Pair     size(TestApplication::DEFAULT_SURFACE_WIDTH, TestApplication::DEFAULT_SURFACE_HEIGHT);
+  const Vector2  center(TestApplication::DEFAULT_SURFACE_WIDTH * 0.5f, TestApplication::DEFAULT_SURFACE_HEIGHT * 0.5f);
+  PhysicsAdaptor adaptor   = PhysicsAdaptor::New(transform, size);
+  Actor          rootActor = adaptor.GetRootActor();
+  auto           scene     = application.GetScene();
+  scene.Add(rootActor);
+
+  {
+    auto         accessor    = adaptor.GetPhysicsAccessor(); // Prevent integration
+    auto         bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    Dali::Actor  ballActor   = Toolkit::ImageView::New(TEST_RESOURCE_DIR "/gallery-small-1.jpg");
+    btRigidBody* body        = CreateBody(bulletWorld);
+    body->getWorldTransform().setOrigin(btVector3(0.f, 0.f, 0.f));
+
+    ballActor[Actor::Property::PARENT_ORIGIN] = ParentOrigin::CENTER;
+    ballActor[Actor::Property::ANCHOR_POINT]  = AnchorPoint::CENTER;
+
+    auto physicsActor = adaptor.AddActorBody(ballActor, body);
+  }
+  Test::WaitForEventThreadTrigger(1);
+
+  // Should trigger an Update without processing queue
+  application.SendNotification();
+  application.Render();
+
+  Vector3 origin, direction;
+  Dali::HitTestAlgorithm::BuildPickingRay(scene.GetRenderTaskList().GetTask(0), center, origin, direction);
+  Vector3 from, to;
+  adaptor.BuildPickingRay(origin, direction, from, to); // Hit test centre of screen
+
+  {
+    auto    accessor = adaptor.GetPhysicsAccessor();
+    Vector3 localPivot;
+    float   distanceFromCamera;
+    auto    body = accessor->HitTest(from, to, localPivot, distanceFromCamera);
+
+    DALI_TEST_CHECK(!body.Empty());
+  }
+
+  END_TEST;
+}
+
+// todo:
+// Hit Test
+// PhysicsDebugRenderer.... Elide?!
index f11bfbf..ec10a33 100755 (executable)
@@ -7,7 +7,6 @@ SET(CAPI_LIB "dali-toolkit")
 
 # List of test case sources (Only these get parsed for test cases)
 SET(TC_SOURCES
-  toolkit-direct-rendering-egl.cpp
   utc-Dali-Alignment.cpp
   utc-Dali-AnimatedImageVisual.cpp
   utc-Dali-AnimatedVectorImageVisual.cpp
@@ -103,6 +102,7 @@ SET(TEST_HARNESS_SOURCES
   dali-toolkit-test-utils/toolkit-application.cpp
   dali-toolkit-test-utils/toolkit-canvas-renderer.cpp
   dali-toolkit-test-utils/toolkit-clipboard.cpp
+  dali-toolkit-test-utils/toolkit-direct-rendering-egl.cpp
   dali-toolkit-test-utils/toolkit-event-thread-callback.cpp
   dali-toolkit-test-utils/toolkit-environment-variable.cpp
   dali-toolkit-test-utils/toolkit-input-method-context.cpp
index 31080a4..4237327 100644 (file)
@@ -510,7 +510,11 @@ int32_t RunAllInParallel(const char* processName, ::testcase tc_array[], std::st
         std::chrono::steady_clock::duration timeSpan = endTime - tc.second.startTime;
         double                              seconds  = double(timeSpan.count()) * std::chrono::steady_clock::period::num / std::chrono::steady_clock::period::den;
 
-        if(seconds > MAXIMUM_CHILD_LIFETIME)
+        if(4.9999 < seconds && seconds < 5 && !tc.second.finished)
+        {
+          printf("Child process %s is delayed: WCHAN:%s\n", tc.second.name, GetWChan(tc.first).c_str());
+        }
+        else if(seconds > MAXIMUM_CHILD_LIFETIME)
         {
           // Kill the child process. A subsequent call to waitpid will process signal result below.
           if(!tc.second.finished)
diff --git a/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-direct-rendering-egl.cpp b/automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-direct-rendering-egl.cpp
new file mode 100644 (file)
index 0000000..198c849
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+* 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.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+#include <EGL/egl.h>
+#include <GLES3/gl3.h>
+extern "C"
+{
+  // Flag to be set when we want shader compilation fail
+  bool gDirectRenderingFailCreateShader = false;
+
+  // Flag to be set when we want program linking fail
+  bool gDirectRenderingFailCreateProgram = false;
+
+  /**
+ * To test the multithreaded variant we need override EGL api
+ *
+ * The Direct Rendering uses GL directly and it's needed to override certain funtions in order
+ * to force code execution.
+ */
+  EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list)
+  {
+    return EGLContext(0x12345678);
+  }
+
+  EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config)
+  {
+    static EGLConfig config;
+    if(num_config)
+    {
+      *num_config = 1;
+    }
+    if(configs)
+    {
+      configs[0] = config;
+    }
+
+    return EGL_TRUE;
+  }
+
+  GLuint glCreateProgram(void)
+  {
+    static uint32_t programId = 1;
+    return programId++;
+  }
+
+  GLuint glCreateShader(GLenum type)
+  {
+    static uint32_t shaderId = 1;
+    return shaderId++;
+  }
+
+  void glCompileShader(GLuint shader)
+  {
+  }
+
+  void glLinkProgram(GLuint program)
+  {
+  }
+
+  void glGenTextures(GLsizei n, GLuint* textures)
+  {
+    static GLuint texId = 1u;
+    for(auto i = 0; i < n; ++i)
+    {
+      textures[i] = texId++;
+    }
+  }
+
+  void glGetShaderiv(GLuint shader, GLenum pname, GLint* params)
+  {
+    if(pname == GL_COMPILE_STATUS)
+    {
+      params[0] = gDirectRenderingFailCreateShader ? GL_FALSE : GL_TRUE;
+    }
+    else if(pname == GL_INFO_LOG_LENGTH)
+    {
+      params[0] = 4;
+    }
+  }
+
+  void glGetProgramiv(GLuint shader, GLenum pname, GLint* params)
+  {
+    if(pname == GL_LINK_STATUS)
+    {
+      params[0] = gDirectRenderingFailCreateProgram ? GL_FALSE : GL_TRUE;
+    }
+    else if(pname == GL_INFO_LOG_LENGTH)
+    {
+      params[0] = 4;
+    }
+  }
+
+  void glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog)
+  {
+    infoLog[0] = '0';
+    infoLog[1] = '\n';
+  }
+
+  void glGetProgramInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog)
+  {
+    infoLog[0] = '0';
+    infoLog[1] = '\n';
+  }
+
+  void glDeleteSync(GLsync sync)
+  {
+  }
+
+  GLenum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout)
+  {
+    return GL_CONDITION_SATISFIED;
+  }
+
+  GLsync glFenceSync(GLenum condition, GLbitfield flags)
+  {
+    static uint32_t syncId = 0;
+    return reinterpret_cast<GLsync>(++syncId);
+  }
+
+  GLenum glCheckFramebufferStatus(GLenum target)
+  {
+    return GL_FRAMEBUFFER_COMPLETE;
+  }
+}
\ No newline at end of file
index 1e36828..ac967f8 100644 (file)
@@ -33,7 +33,7 @@ using AdaptorImpl = Dali::Internal::Adaptor::Adaptor;
 
 ToolkitTestApplication::ToolkitTestApplication(size_t surfaceWidth, size_t surfaceHeight, float horizontalDpi, float verticalDpi)
 : TestApplication(surfaceWidth, surfaceHeight, horizontalDpi, verticalDpi, false /* Do not Initialize Core */),
-  mMainWindow(new Dali::Window),
+  mMainWindow(),
   mAdaptor(&AdaptorImpl::New()) // Need to create Adaptor first as many singletons in dali-adaptor need it
 {
   // Create Core next
@@ -41,8 +41,8 @@ ToolkitTestApplication::ToolkitTestApplication(size_t surfaceWidth, size_t surfa
 
   // Override Scene creation in TestApplication by creating a window.
   // The window will create a Scene & surface and set up the scene's surface appropriately.
-  *mMainWindow = Window::New(PositionSize(0, 0, surfaceWidth, surfaceHeight), "");
-  mScene       = AdaptorImpl::GetScene(*mMainWindow);
+  mMainWindow = Window::New(PositionSize(0, 0, surfaceWidth, surfaceHeight), "");
+  mScene       = AdaptorImpl::GetScene(mMainWindow);
   mScene.SetDpi(Vector2(horizontalDpi, verticalDpi));
 
   // Create render target for the scene
@@ -57,7 +57,7 @@ ToolkitTestApplication::ToolkitTestApplication(size_t surfaceWidth, size_t surfa
   Accessibility::Accessible::SetObjectRegistry(mCore->GetObjectRegistry());
 
   // This will also emit the window created signals
-  AdaptorImpl::GetImpl(*mAdaptor).Start(*mMainWindow);
+  AdaptorImpl::GetImpl(*mAdaptor).Start(mMainWindow);
   AdaptorImpl::GetImpl(*mAdaptor).SetApplication(*this);
 
   Dali::LifecycleController lifecycleController = Dali::LifecycleController::Get();
index 49a4c6f..6d3c511 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TOOLKIT_TEST_APPLICATION_H
 
 /*
- * Copyright (c) 2019 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.
@@ -61,9 +61,13 @@ public:
    */
   void RunIdles();
 
-private:
+  Dali::Window GetWindow()
+  {
+    return mMainWindow;
+  }
 
-  std::unique_ptr<Dali::Window> mMainWindow;
+private:
+  Dali::Window mMainWindow;
   std::unique_ptr< Adaptor > mAdaptor;
 };
 
diff --git a/automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp b/automated-tests/src/dali-toolkit/toolkit-direct-rendering-egl.cpp
deleted file mode 100644 (file)
index 35ee43e..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
-* 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.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*
-*/
-
-#include <EGL/egl.h>
-#include <GLES3/gl3.h>
-extern "C"
-{
-
-// Flag to be set when we want shader compilation fail
-bool gDirectRenderingFailCreateShader = false;
-
-// Flag to be set when we want program linking fail
-bool gDirectRenderingFailCreateProgram = false;
-
-/**
- * To test the multithreaded variant we need override EGL api
- *
- * The Direct Rendering uses GL directly and it's needed to override certain funtions in order
- * to force code execution.
- */
-EGLContext eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list)
-{
- return EGLContext(0x12345678);
-}
-
-EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config)
-{
-  static EGLConfig config;
-  if(num_config)
-  {
-    *num_config = 1;
-  }
-  if(configs)
-  {
-    configs[0] = config;
-  }
-
-  return EGL_TRUE;
-}
-
-GLuint glCreateProgram (void)
-{
-  static uint32_t programId = 1;
-  return programId++;
-}
-
-GLuint glCreateShader(GLenum type)
-{
-  static uint32_t shaderId = 1;
-  return shaderId++;
-}
-
-void glCompileShader(GLuint shader)
-{
-}
-
-void glLinkProgram (GLuint program)
-{
-}
-
-void glGenTextures(GLsizei n, GLuint *textures)
-{
-  static GLuint texId = 1u;
-  for(auto i = 0; i < n; ++i)
-  {
-    textures[i] = texId++;
-  }
-}
-
-void glGetShaderiv(GLuint shader, GLenum pname, GLint *params)
-{
-  if(pname == GL_COMPILE_STATUS)
-  {
-    params[0] = gDirectRenderingFailCreateShader ? GL_FALSE : GL_TRUE;
-  }
-  else if(pname == GL_INFO_LOG_LENGTH)
-  {
-    params[0] = 4;
-  }
-}
-
-void glGetProgramiv(GLuint shader, GLenum pname, GLint *params)
-{
-  if(pname == GL_LINK_STATUS)
-  {
-    params[0] = gDirectRenderingFailCreateProgram ? GL_FALSE : GL_TRUE;
-  }
-  else if(pname == GL_INFO_LOG_LENGTH)
-  {
-    params[0] = 4;
-  }
-}
-
-void glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
-{
-  infoLog[0] = '0';
-  infoLog[1] = '\n';
-}
-
-void glGetProgramInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)
-{
-  infoLog[0] = '0';
-  infoLog[1] = '\n';
-}
-
-void glDeleteSync (GLsync sync)
-{
-}
-
-GLenum glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout)
-{
-  return GL_CONDITION_SATISFIED;
-}
-
-GLsync glFenceSync (GLenum condition, GLbitfield flags)
-{
-  static uint32_t syncId = 0;
-  return reinterpret_cast<GLsync>(++syncId);
-}
-
-GLenum glCheckFramebufferStatus (GLenum target)
-{
-  return GL_FRAMEBUFFER_COMPLETE;
-}
-
-}
\ No newline at end of file
index f09a06e..26782ba 100644 (file)
@@ -4,3 +4,7 @@ dali.info
 dali2-*-config.cmake
 libdali2-scene3d.so*
 dali-shader-generator
+build.ninja
+rules.ninja
+.ninja_deps
+.ninja_log
index d6dbad2..5bf2ff8 100644 (file)
@@ -70,25 +70,86 @@ endif()
 add_subdirectory("${physics_dir}/third-party/chipmunk2d" chipmunk2d)
 add_subdirectory("${physics_dir}/third-party/bullet3" bullet3)
 
-if (ENABLE_PKG_CONFIGURE)
-    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${core_pkg_cfg_file_2d} ${CMAKE_CURRENT_BINARY_DIR}/${core_pkg_cfg_file_3d}
-        DESTINATION ${LIB_DIR}/pkgconfig
+# TODO: Split into 2 separate targets
+set(physics3d_src_files "")
+include("${physics_dir}/public-api/file.list")
+include("${physics_dir}/internal/file.list")
+
+set(prefix_include_dir "${prefix}/include")
+include_directories(${repo_root_dir}
+  "${prefix_include_dir}"
+  "${repo_root_dir}/dali-physics/third-party/bullet3/src"
+)
+
+MESSAGE( STATUS "SOURCES: " ${physics3d_src_files})
+
+ADD_LIBRARY("${name}-3d" SHARED ${physics3d_src_files} )
+TARGET_LINK_LIBRARIES("${name}-3d" ${DALICORE_LDFLAGS}
+  dali2-toolkit
+  bullet3
+  ${COVERAGE})
+
+IF (ENABLE_PKG_CONFIGURE)
+  INSTALL(FILES
+    ${CMAKE_CURRENT_BINARY_DIR}/${core_pkg_cfg_file_2d}
+    ${CMAKE_CURRENT_BINARY_DIR}/${core_pkg_cfg_file_3d}
+    DESTINATION ${LIB_DIR}/pkgconfig )
+ENDIF()
+
+
+IF( INSTALL_CMAKE_MODULES )
+  MESSAGE(STATUS "Installing cmake modules & libs")
+  SET_TARGET_PROPERTIES( ${name}-3d
+    PROPERTIES
+    VERSION ${DALI_PHYSICS_VERSION}
+    SOVERSION ${${name}_VERSION_MAJOR}
+    CLEAN_DIRECT_OUPUT 1
     )
-endif()
-
-if( INSTALL_CMAKE_MODULES )
 
-  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}-2d-config.cmake "
+  IF( ENABLE_DEBUG )
+    SET( BIN_DIR "${BIN_DIR}/debug" )
+    SET( LIB_DIR "${LIB_DIR}/debug" )
+  ENDIF()
+
+  # Install library
+  INSTALL( TARGETS ${name}-3d
+    EXPORT ${name}-3d-targets
+    LIBRARY DESTINATION ${LIB_DIR}
+    ARCHIVE DESTINATION ${LIB_DIR}
+    RUNTIME DESTINATION ${BIN_DIR}
+  )
+
+  # Install the cmake modules.
+  INSTALL(
+    EXPORT ${name}-3d-targets
+    NAMESPACE ${name}-3d::
+    FILE ${name}-3d-targets.cmake
+    DESTINATION share/${name}-3d
+  )
+
+  FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}-2d-config.cmake "
     include(CMakeFindDependencyMacro)
     include(\${CMAKE_CURRENT_LIST_DIR}/${name}-2d-targets.cmake)
   ")
-  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}-2d-config.cmake DESTINATION share/${name}-2d)
+  INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}-2d-config.cmake DESTINATION share/${name}-2d)
 
-  file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}-3d-config.cmake "
+  FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}-3d-config.cmake "
     include(CMakeFindDependencyMacro)
     include(\${CMAKE_CURRENT_LIST_DIR}/${name}-3d-targets.cmake)
   ")
-  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}-3d-config.cmake DESTINATION share/${name}-3d)
-endif()
+  INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}-3d-config.cmake DESTINATION share/${name}-3d)
+
+ELSE()
+  MESSAGE(STATUS "Installing libs")
+  INSTALL( TARGETS ${name}-3d DESTINATION ${LIB_DIR} )
+
+ENDIF()
 
+# Install headers
+install( FILES ${physics3d_public_api_header_files}
+  DESTINATION "${INCLUDE_DIR}/dali-physics/public-api"
+  )
 
+install( FILES ${physics_dir}/dali-physics.h
+  DESTINATION "${INCLUDE_DIR}/dali-physics"
+  )
index 739026b..b5e7001 100644 (file)
@@ -8,5 +8,5 @@ Name: DALi Engine Physics Library
 Description: Dali Physics 3D library
 Version: ${apiversion}
 Requires: bullet3
-Libs: -L${libdir}
+Libs: -L${libdir} -ldali2-physics-3d
 Cflags: -I${includedir}
diff --git a/dali-physics/README.md b/dali-physics/README.md
new file mode 100644 (file)
index 0000000..a473a61
--- /dev/null
@@ -0,0 +1,13 @@
+# Overview
+
+The physics library is a small frontend to Chipmunk or Bullet libraries. It
+provides a safe mechanism to initialize and access the physics world from the
+event thread, whilst running the integration step in the Update thread.
+
+A queueing mechanism is provided to execute native functions on the Update
+thread prior to running the integration step. (This enables DALi properties
+and physics properties (e.g. position or orientation) to be synced in the same frame)
+
+DALi actors that are mapped to physics bodies are automatically updated after
+the integration step.
+
diff --git a/dali-physics/dali-physics.h b/dali-physics/dali-physics.h
new file mode 100644 (file)
index 0000000..7e8135b
--- /dev/null
@@ -0,0 +1,20 @@
+#pragma once
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali-physics/public-api/physics-actor.h>
+#include <dali-physics/public-api/physics-adaptor.h>
diff --git a/dali-physics/internal/file.list b/dali-physics/internal/file.list
new file mode 100644 (file)
index 0000000..9b2cf6e
--- /dev/null
@@ -0,0 +1,8 @@
+set(physics3d_internal_dir "${physics_dir}/internal")
+
+set(physics3d_src_files ${physics3d_src_files}
+  ${physics3d_internal_dir}/physics-actor-impl.cpp
+  ${physics3d_internal_dir}/physics-adaptor-impl.cpp
+  ${physics3d_internal_dir}/physics-debug-renderer.cpp
+  ${physics3d_internal_dir}/physics-world-impl.cpp
+)
diff --git a/dali-physics/internal/physics-actor-impl.cpp b/dali-physics/internal/physics-actor-impl.cpp
new file mode 100644 (file)
index 0000000..9f9d467
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Class Header
+#include <dali-physics/internal/physics-actor-impl.h>
+#include <dali-physics/internal/physics-adaptor-impl.h>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+namespace
+{
+inline btVector3 fromVec3(Dali::Vector3 vec3)
+{
+  return btVector3(vec3.x, vec3.y, vec3.z);
+}
+inline Dali::Vector3 toVec3(btVector3 vec3)
+{
+  return Dali::Vector3(vec3.x(), vec3.y(), vec3.z());
+}
+
+inline btQuaternion fromQuat(Dali::Quaternion q)
+{
+  return btQuaternion(q.mVector.x, q.mVector.y, q.mVector.z, q.mVector.w);
+}
+inline Dali::Quaternion toQuat(btQuaternion q)
+{
+  return Dali::Quaternion(q.w(), q.x(), q.y(), q.z());
+}
+
+} //Anonymous namespace
+
+PhysicsActorPtr PhysicsActor::New(Dali::Actor actor, Dali::Any body, Dali::Toolkit::Physics::Internal::PhysicsAdaptor& adaptor)
+{
+  PhysicsActorPtr physicsActor(new Internal::PhysicsActor(actor, body, adaptor));
+  physicsActor->Initialize();
+  return physicsActor;
+}
+
+PhysicsActor::PhysicsActor(Dali::Actor actor, Dali::Any body, PhysicsAdaptor& adaptor)
+: mAdaptor(adaptor),
+  mActorId(actor.GetProperty<int>(Dali::Actor::Property::ID)),
+  mBody(body)
+{
+}
+
+PhysicsActor::~PhysicsActor() = default;
+
+void PhysicsActor::Initialize(void)
+{
+  // RegisterObject?
+}
+
+void PhysicsActor::AsyncSetPhysicsPosition(Dali::Vector3 actorPosition)
+{
+  // Queue task
+  btRigidBody* body = mBody.Get<btRigidBody*>();
+  auto         pos  = fromVec3(mAdaptor.TranslateToPhysicsSpace(actorPosition));
+  mAdaptor.Queue([body, pos] { body->getWorldTransform().setOrigin(pos); });
+}
+
+void PhysicsActor::AsyncSetPhysicsRotation(Dali::Quaternion rotation)
+{
+  // Queue task
+  btRigidBody* body = mBody.Get<btRigidBody*>();
+  auto         q    = fromQuat(mAdaptor.TranslateToPhysicsSpace(rotation));
+  mAdaptor.Queue([body, q]() { body->getWorldTransform().setRotation(q); });
+}
+
+Dali::Vector3 PhysicsActor::GetPhysicsPosition() const
+{
+  btRigidBody* body = mBody.Get<btRigidBody*>();
+  return toVec3(body->getWorldTransform().getOrigin());
+}
+
+Dali::Quaternion PhysicsActor::GetPhysicsRotation() const
+{
+  btRigidBody* body = mBody.Get<btRigidBody*>();
+  return toQuat(body->getWorldTransform().getRotation());
+}
+
+Dali::Vector3 PhysicsActor::GetActorPosition() const
+{
+  btRigidBody*       body      = mBody.Get<btRigidBody*>();
+  const btTransform& transform = body->getWorldTransform();
+  const btVector3&   position  = transform.getOrigin();
+  return mAdaptor.TranslateFromPhysicsSpace(Vector3(position.getX(), position.getY(), position.getZ()));
+}
+
+Dali::Quaternion PhysicsActor::GetActorRotation() const
+{
+  btRigidBody*       body      = mBody.Get<btRigidBody*>();
+  const btTransform& transform = body->getWorldTransform();
+  btQuaternion       q         = transform.getRotation();
+  return mAdaptor.TranslateFromPhysicsSpace(Quaternion(q.w(), q.x(), q.y(), q.z()));
+
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
diff --git a/dali-physics/internal/physics-actor-impl.h b/dali-physics/internal/physics-actor-impl.h
new file mode 100644 (file)
index 0000000..583bbfa
--- /dev/null
@@ -0,0 +1,123 @@
+#ifndef DALI_TOOLKIT_PHYSICS_INTERNAL_ACTOR_H
+#define DALI_TOOLKIT_PHYSICS_INTERNAL_ACTOR_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/object/any.h>
+#include <dali/public-api/object/base-object.h>
+
+// INTERNAL INCLUDES
+#include <dali-physics/public-api/physics-actor.h>
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
+
+namespace Dali::Toolkit::Physics
+{
+namespace Internal
+{
+class PhysicsActor;
+class PhysicsAdaptor;
+
+using PhysicsActorPtr = Dali::IntrusivePtr<PhysicsActor>;
+
+class DALI_TOOLKIT_API PhysicsActor : public Dali::BaseObject
+{
+public:
+  PhysicsActor(Dali::Actor actor, Dali::Any body, PhysicsAdaptor& adaptor);
+  ~PhysicsActor() override;
+  PhysicsActor(const PhysicsActor& handle) = delete;
+  PhysicsActor& operator=(const PhysicsActor& handle) = delete;
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::New()
+   */
+  static PhysicsActorPtr New(Dali::Actor actor, Dali::Any body, PhysicsAdaptor& adaptor);
+
+  void Initialize(void);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::GetId
+   */
+  uint32_t GetId() const
+  {
+    return mActorId;
+  }
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::GetBody
+   */
+  Dali::Any GetBody() const
+  {
+    return mBody;
+  }
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::AsyncSetPhysicsPosition
+   */
+  void AsyncSetPhysicsPosition(Dali::Vector3 actorPosition);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::AsyncSetPhysicsRotation
+   */
+  void AsyncSetPhysicsRotation(Dali::Quaternion actorRotation);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::GetPhysicsPosition
+   */
+  Dali::Vector3 GetPhysicsPosition() const;
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::GetPhysicsRotation
+   */
+  Dali::Quaternion GetPhysicsRotation() const;
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::GetActorPosition
+   */
+  Dali::Vector3 GetActorPosition() const;
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsActor::GetActorRotation
+   */
+  Dali::Quaternion GetActorRotation() const;
+
+private:
+  PhysicsAdaptor& mAdaptor;
+  uint32_t        mActorId{0};
+  Dali::Any       mBody;
+};
+
+} // namespace Internal
+
+inline Internal::PhysicsActor& GetImplementation(Physics::PhysicsActor& physics)
+{
+  DALI_ASSERT_ALWAYS(physics && "Physics actor handle is empty");
+  BaseObject& handle = physics.GetBaseObject();
+  return static_cast<Internal::PhysicsActor&>(handle);
+}
+
+inline const Internal::PhysicsActor& GetImplementation(const Physics::PhysicsActor& physics)
+{
+  DALI_ASSERT_ALWAYS(physics && "Physics actor handle is empty");
+  const BaseObject& handle = physics.GetBaseObject();
+  return static_cast<const Internal::PhysicsActor&>(handle);
+}
+
+} // namespace Dali::Toolkit::Physics
+
+#endif //DALI_TOOLKIT_PHYSICS_INTERNAL_ACTOR_H
diff --git a/dali-physics/internal/physics-adaptor-impl.cpp b/dali-physics/internal/physics-adaptor-impl.cpp
new file mode 100644 (file)
index 0000000..dd20c0b
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Class Header
+#include <dali-physics/internal/physics-adaptor-impl.h>
+
+// External Headers
+#include <iostream>
+#include <map>
+#include <memory>
+#include <utility>
+
+// Internal Headers
+#include <dali-physics/internal/physics-world-impl.h>
+#include <dali/dali.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/integration-api/debug.h>
+#include <dali/public-api/actors/drawable-actor.h>
+
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_PHYSICS");
+#endif
+
+inline btVector3 ConvertVector(Dali::Vector3 vector)
+{
+  return btVector3(vector.x, vector.y, vector.z);
+}
+
+} // namespace
+
+namespace Dali::Toolkit::Physics
+{
+struct PhysicsAdaptor::ScopedPhysicsAccessor::Impl
+{
+  Impl(Internal::PhysicsWorld& world)
+  : mLock(world.GetMutex()),
+    mPhysicsWorld(world)
+  {
+  }
+  Impl(Impl&)         = delete;
+  const Impl& operator=(const Impl&) = delete;
+
+  Dali::Mutex::ScopedLock mLock;
+  Internal::PhysicsWorld& mPhysicsWorld;
+  friend Internal::PhysicsAdaptor;
+};
+
+PhysicsAdaptor::ScopedPhysicsAccessor::ScopedPhysicsAccessor(Internal::PhysicsWorld& world)
+: mImpl(new Impl(world))
+{
+}
+PhysicsAdaptor::ScopedPhysicsAccessor::~ScopedPhysicsAccessor()
+{
+  delete mImpl;
+}
+
+Dali::Any PhysicsAdaptor::ScopedPhysicsAccessor::GetNative()
+{
+  return mImpl->mPhysicsWorld.GetNative();
+}
+
+Dali::Any PhysicsAdaptor::ScopedPhysicsAccessor::HitTest(
+  Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera)
+{
+  return mImpl->mPhysicsWorld.HitTest(rayFromWorld, rayToWorld, localPivot, distanceFromCamera);
+}
+
+namespace Internal
+{
+PhysicsAdaptorPtr PhysicsAdaptor::New(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+  PhysicsAdaptorPtr adaptor(new PhysicsAdaptor());
+  adaptor->Initialize(transform, worldSize);
+  return adaptor;
+}
+
+PhysicsAdaptor::PhysicsAdaptor()
+: mSlotDelegate(this)
+{
+}
+
+PhysicsAdaptor::~PhysicsAdaptor()
+{
+  // @todo Ensure physics bodies don't leak
+}
+
+void PhysicsAdaptor::Initialize(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+  mTransform        = transform;
+  mInverseTransform = transform;
+  mInverseTransform.Invert();
+  mSize = worldSize;
+
+  // Create an actor that can handle mouse events.
+  // @todo Enable this to be fully configured / provided
+  mRootActor                                 = Layer::New();
+  mRootActor[Layer::Property::BEHAVIOR]      = Layer::LAYER_3D;
+  mRootActor[Layer::Property::DEPTH_TEST]    = true;
+  mRootActor[Actor::Property::SIZE]          = Vector2(mSize.GetWidth(), mSize.GetHeight());
+  mRootActor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
+  mRootActor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
+
+  mPhysicsWorld = PhysicsWorld::New(mRootActor,
+                                    Dali::MakeCallback(mSlotDelegate.GetSlot(),
+                                                       &PhysicsAdaptor::OnUpdateActors));
+}
+
+void PhysicsAdaptor::SetTimestep(float timestep)
+{
+  mPhysicsWorld->SetTimestep(timestep);
+}
+
+float PhysicsAdaptor::GetTimestep()
+{
+  return mPhysicsWorld->GetTimestep();
+}
+
+Physics::PhysicsAdaptor::ScopedPhysicsAccessorPtr PhysicsAdaptor::GetPhysicsAccessor()
+{
+  return std::unique_ptr<Physics::PhysicsAdaptor::ScopedPhysicsAccessor>(new Physics::PhysicsAdaptor::ScopedPhysicsAccessor(*mPhysicsWorld.get()));
+}
+
+Layer PhysicsAdaptor::CreateDebugLayer(Dali::Window window)
+{
+  Layer debugLayer;
+
+  auto renderTaskList = window.GetRenderTaskList();
+  auto renderTask     = renderTaskList.GetTask(0);
+  auto windowSize     = window.GetSize();
+
+  debugLayer                                 = Layer::New();
+  debugLayer[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
+  debugLayer[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
+
+  Constraint positionConstraint = Constraint::New<Vector3>(debugLayer, Actor::Property::POSITION, EqualToConstraint());
+  positionConstraint.AddSource(Source(mRootActor, Actor::Property::POSITION));
+  positionConstraint.Apply();
+  Constraint sizeConstraint = Constraint::New<Vector2>(debugLayer, Actor::Property::SIZE, EqualToConstraint());
+  sizeConstraint.AddSource(Source(mRootActor, Actor::Property::SIZE));
+  sizeConstraint.Apply();
+
+  mDebugRenderer                              = PhysicsDebugRenderer::New(windowSize.GetWidth(), windowSize.GetHeight(), renderTask.GetCameraActor(), this);
+  mDebugActor                                 = DrawableActor::New(*(mDebugRenderer->GetCallback().get()));
+  mDebugActor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
+  mDebugActor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
+
+  Constraint sizeConstraint2 = Constraint::New<Vector2>(mDebugActor, Actor::Property::SIZE, EqualToConstraint());
+  sizeConstraint2.AddSource(ParentSource(Actor::Property::SIZE));
+  sizeConstraint2.Apply();
+
+  debugLayer.Add(mDebugActor);
+
+  auto bulletWorld = mPhysicsWorld->GetNative().Get<btDiscreteDynamicsWorld*>();
+
+  bulletWorld->setDebugDrawer(mDebugRenderer.get());
+  mDebugRenderer->setDebugMode(btIDebugDraw::DBG_DrawWireframe |
+                               btIDebugDraw::DBG_DrawContactPoints |
+                               btIDebugDraw::DBG_DrawNormals);
+
+  window.Add(debugLayer);
+  return debugLayer;
+}
+
+void PhysicsAdaptor::SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+  mTransform        = transform;
+  mInverseTransform = transform;
+  mInverseTransform.Invert();
+  mSize = worldSize;
+
+  GetRootActor()[Actor::Property::SIZE] = Vector3(worldSize.GetWidth(), worldSize.GetHeight(), 0);
+
+  if(mDebugRenderer)
+  {
+    Actor layer                  = mDebugActor.GetParent();
+    layer[Actor::Property::SIZE] = Vector3(worldSize);
+    mDebugRenderer->UpdateWindowSize(worldSize);
+  }
+}
+
+void PhysicsAdaptor::SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state)
+{
+  mPhysicsWorld->SetIntegrationState(state);
+}
+
+Physics::PhysicsAdaptor::IntegrationState PhysicsAdaptor::GetIntegrationState()
+{
+  return mPhysicsWorld->GetIntegrationState();
+}
+
+void PhysicsAdaptor::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
+{
+  mPhysicsWorld->SetDebugState(state);
+}
+
+Physics::PhysicsAdaptor::DebugState PhysicsAdaptor::GetDebugState()
+{
+  return mPhysicsWorld->GetDebugState();
+}
+
+PhysicsActorPtr PhysicsAdaptor::AddActorBody(Dali::Actor actor, Dali::Any body)
+{
+  uint32_t     id     = static_cast<uint32_t>(actor.GetProperty<int>(Actor::Property::ID));
+  btRigidBody* btBody = body.Get<btRigidBody*>();
+
+  btBody->setUserIndex(id);
+  mPhysicsActors.insert(std::make_pair(id, PhysicsActor::New(actor, body, *this)));
+  actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
+  actor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
+  mRootActor.Add(actor);
+  return mPhysicsActors.at(id);
+}
+
+PhysicsActorPtr PhysicsAdaptor::GetPhysicsActor(Dali::Any body)
+{
+  btRigidBody* btBody = body.Get<btRigidBody*>();
+  if(btBody)
+  {
+    int  id   = btBody->getUserIndex();
+    auto iter = mPhysicsActors.find(id);
+    if(iter != mPhysicsActors.end())
+    {
+      return iter->second;
+    }
+  }
+  DALI_LOG_ERROR("Body not found in physics actors");
+  return nullptr;
+}
+
+Dali::Actor PhysicsAdaptor::GetRootActor()
+{
+  return mRootActor;
+}
+
+// Convert a position from root actor local space to physics space
+Vector3 PhysicsAdaptor::TranslateToPhysicsSpace(Vector3 vector)
+{
+  Vector4 position = mTransform * Vector4(vector.x, vector.y, vector.z, 1.0f);
+  return Vector3(position);
+}
+
+// Convert a position from physics space to root actor local space
+Vector3 PhysicsAdaptor::TranslateFromPhysicsSpace(Vector3 vector)
+{
+  Vector4 position = mInverseTransform * Vector4(vector.x, vector.y, vector.z, 1.0f);
+  return Vector3(position);
+}
+
+Quaternion PhysicsAdaptor::TranslateToPhysicsSpace(Quaternion orientation)
+{
+  // Naive check for speed. Should pass scale in constructor/transform setter
+
+  if(std::signbit(mTransform.AsFloat()[0])) // mirrored in x
+  {
+    return Quaternion(orientation.mVector.w, orientation.mVector.x, -orientation.mVector.y, -orientation.mVector.z);
+  }
+  else if(std::signbit(mTransform.AsFloat()[5])) // mirrored in y
+  {
+    return Quaternion(orientation.mVector.w, -orientation.mVector.x, orientation.mVector.y, -orientation.mVector.z);
+  }
+  else if(std::signbit(mTransform.AsFloat()[10])) // mirrored in z
+  {
+    return Quaternion(orientation.mVector.w, -orientation.mVector.x, -orientation.mVector.y, orientation.mVector.z);
+  }
+
+  // No mirror, so rotation is invariant.
+  return orientation;
+}
+
+Quaternion PhysicsAdaptor::TranslateFromPhysicsSpace(Quaternion orientation)
+{
+  // Mirroring conversion is identical in both transforms
+  return TranslateToPhysicsSpace(orientation);
+}
+
+// Convert a vector from dali space to physics space
+Vector3 PhysicsAdaptor::ConvertVectorToPhysicsSpace(Vector3 vector)
+{
+  Vector4 otherVector(mTransform * Vector4(vector.x, vector.y, vector.z, 0.0f));
+  return Vector3(otherVector);
+}
+
+// Convert a vector from physics space to root actor local space
+Vector3 PhysicsAdaptor::ConvertVectorFromPhysicsSpace(Vector3 vector)
+{
+  Vector4 otherVector(mInverseTransform * Vector4(vector.x, vector.y, vector.z, 0.0f));
+  return Vector3(otherVector);
+}
+
+void PhysicsAdaptor::BuildPickingRay(Vector3 origin, Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld)
+{
+  rayFromWorld = TranslateToPhysicsSpace(origin);
+  // direction from DALI touch is normalized.
+  // Multiply it up so that it goes through the visible world. (@todo use Space config?)
+  rayToWorld = TranslateToPhysicsSpace(origin + direction * 10000.0f);
+}
+
+Vector3 PhysicsAdaptor::ProjectPoint(Vector3 origin, Vector3 direction, float distance)
+{
+  Vector3 rayFromWorld = TranslateToPhysicsSpace(origin);
+  Vector3 rayToWorld   = TranslateToPhysicsSpace(origin + direction * 10000.0f);
+
+  Vector3 dir = rayToWorld - rayFromWorld;
+  dir.Normalize();
+  dir *= distance; // Should compute with scale?
+  return (rayFromWorld + dir);
+}
+
+void PhysicsAdaptor::OnUpdateActors(Dali::UpdateProxy* updateProxy)
+{
+  for(auto&& actor : mPhysicsActors)
+  {
+    // Get position, orientation from physics world.
+    Vector3 position = actor.second->GetActorPosition();
+    updateProxy->BakePosition(actor.first, position);
+    Quaternion rotation = actor.second->GetActorRotation();
+    updateProxy->BakeOrientation(actor.first, rotation);
+  }
+}
+
+void PhysicsAdaptor::Queue(std::function<void()> function)
+{
+  mPhysicsWorld->Queue(function);
+}
+
+void PhysicsAdaptor::CreateSyncPoint()
+{
+  mPhysicsWorld->CreateSyncPoint();
+}
+
+} // namespace Internal
+} // namespace Dali::Toolkit::Physics
diff --git a/dali-physics/internal/physics-adaptor-impl.h b/dali-physics/internal/physics-adaptor-impl.h
new file mode 100644 (file)
index 0000000..9c1644a
--- /dev/null
@@ -0,0 +1,207 @@
+#ifndef DALI_TOOLKIT_PHYSICS_INTERNAL_ADAPTOR_H
+#define DALI_TOOLKIT_PHYSICS_INTERNAL_ADAPTOR_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or adaptoried.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/object/base-object.h>
+#include <memory>
+#include <unordered_map>
+
+// INTERNAL INCLUDES
+#include <dali-physics/internal/physics-actor-impl.h>
+#include <dali-physics/internal/physics-debug-renderer.h>
+#include <dali-physics/internal/physics-world-impl.h>
+#include <dali-physics/public-api/physics-adaptor.h>
+
+namespace Dali::Toolkit::Physics
+{
+namespace Internal
+{
+class PhysicsAdaptor;
+using PhysicsAdaptorPtr = IntrusivePtr<PhysicsAdaptor>;
+
+class PhysicsAdaptor : public BaseObject
+{
+public:
+  PhysicsAdaptor();
+
+  /**
+   * A reference counted object may only be deleted by calling Unreference()
+   */
+  ~PhysicsAdaptor() override;
+
+  // Remove copy constructor and copy assignment
+  PhysicsAdaptor(const PhysicsAdaptor& handle) = delete;
+  PhysicsAdaptor& operator=(const PhysicsAdaptor& handle) = delete;
+
+  static PhysicsAdaptorPtr New(const Dali::Matrix& transform, Uint16Pair size);
+
+  /**
+   * 2nd stage initialization
+   */
+  void Initialize(const Dali::Matrix& transform, Uint16Pair size);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetTimestep
+   */
+  void SetTimestep(float timestep);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetTimestep
+   */
+  float GetTimestep();
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetPhysicsAccessor
+   */
+  Physics::PhysicsAdaptor::ScopedPhysicsAccessorPtr GetPhysicsAccessor();
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::CreateDebugLayer
+   */
+  Dali::Layer CreateDebugLayer(Dali::Window window);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+   */
+  Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+   */
+  Dali::Quaternion TranslateToPhysicsSpace(Dali::Quaternion rotation);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+   */
+  Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+   */
+  Dali::Quaternion TranslateFromPhysicsSpace(Quaternion rotation);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorToPhysicsSpace
+   */
+  Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorFromPhysicsSpace
+   */
+  Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetTransformAndSize
+   */
+  void SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair size);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetIntegrationState
+   */
+  void SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetIntegrationState
+   */
+  Physics::PhysicsAdaptor::IntegrationState GetIntegrationState();
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetDebugState
+   */
+  void SetDebugState(Physics::PhysicsAdaptor::DebugState state);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetDebugState
+   */
+  Physics::PhysicsAdaptor::DebugState GetDebugState();
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::AddActorBody
+   */
+  PhysicsActorPtr AddActorBody(Dali::Actor actor, Dali::Any body);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetPhysicsActor
+   */
+  PhysicsActorPtr GetPhysicsActor(Dali::Any body);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetRootActor
+   */
+  Dali::Actor GetRootActor();
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::BuildPickingRay
+   */
+  void BuildPickingRay(Dali::Vector3 origin, Dali::Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ProjectPoint
+   */
+  Dali::Vector3 ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::Queue
+   */
+  void Queue(std::function<void(void)> function);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::Queue
+   */
+  void CreateSyncPoint();
+
+private:
+  /**
+   * Handle the update of all of the known bound actors
+   */
+  void OnUpdateActors(Dali::UpdateProxy* updateProxy);
+
+private:
+  std::unique_ptr<PhysicsWorld>                 mPhysicsWorld;
+  std::unordered_map<uint32_t, PhysicsActorPtr> mPhysicsActors;
+  Dali::Actor                                   mRootActor;
+  Dali::Actor                                   mDebugActor;
+
+  Dali::Matrix     mTransform;
+  Dali::Matrix     mInverseTransform;
+  Dali::Uint16Pair mSize;
+
+  std::unique_ptr<PhysicsDebugRenderer> mDebugRenderer;
+  Dali::SlotDelegate<PhysicsAdaptor>    mSlotDelegate;
+};
+
+} //namespace Internal
+
+inline Internal::PhysicsAdaptor& GetImplementation(Dali::Toolkit::Physics::PhysicsAdaptor& handle)
+{
+  DALI_ASSERT_ALWAYS(handle && "Physics adaptor handle is empty");
+  BaseObject& object = handle.GetBaseObject();
+  return static_cast<Internal::PhysicsAdaptor&>(object);
+}
+
+inline const Internal::PhysicsAdaptor& GetImplementation(const Dali::Toolkit::Physics::PhysicsAdaptor& handle)
+{
+  DALI_ASSERT_ALWAYS(handle && "Physics adaptor handle is empty");
+  const BaseObject& object = handle.GetBaseObject();
+  return static_cast<const Internal::PhysicsAdaptor&>(object);
+}
+
+} // namespace Dali::Toolkit::Physics
+
+#endif //DALI_TOOLKIT_PHYSICS_INTERNAL_ADAPTOR_H
diff --git a/dali-physics/internal/physics-debug-renderer.cpp b/dali-physics/internal/physics-debug-renderer.cpp
new file mode 100644 (file)
index 0000000..c6a1421
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "physics-debug-renderer.h"
+
+#include <dali/dali.h>
+#include "physics-adaptor-impl.h"
+
+using Dali::Degree;
+using Dali::Matrix;
+using Dali::Quaternion;
+using Dali::Radian;
+using Dali::Vector3;
+using Dali::Vector4;
+
+namespace
+{
+GLuint LoadShader(GLenum shaderType, const char* shaderSource)
+{
+  GLuint shader = glCreateShader(shaderType);
+  if(shader != 0)
+  {
+    glShaderSource(shader, 1, &shaderSource, NULL);
+    glCompileShader(shader);
+    GLint compiled = 0;
+    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+    if(compiled != GL_TRUE)
+    {
+      GLint infoLen = 0;
+      glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+
+      if(infoLen > 0)
+      {
+        std::vector<char> logBuffer;
+        logBuffer.resize(infoLen + 1);
+        glGetShaderInfoLog(shader, infoLen, NULL, &logBuffer[0]);
+        fprintf(stderr, "%s\n", &logBuffer[0]);
+        fflush(stderr);
+
+        glDeleteShader(shader);
+        shader = 0;
+      }
+    }
+  }
+  return shader;
+}
+
+GLuint CreateProgram(const char* vertexSource, const char* fragmentSource)
+{
+  GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
+  if(!vertexShader)
+  {
+    return 0;
+  }
+  GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
+  if(!fragmentShader)
+  {
+    return 0;
+  }
+  GLuint program = glCreateProgram();
+  if(program)
+  {
+    glAttachShader(program, vertexShader);
+    glAttachShader(program, fragmentShader);
+    glLinkProgram(program);
+    GLint linkStatus = GL_FALSE;
+    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+    if(linkStatus != GL_TRUE)
+    {
+      GLint bufLength = 0;
+      glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+      if(bufLength)
+      {
+        std::vector<char> logBuffer;
+        logBuffer.resize(bufLength + 1);
+        glGetProgramInfoLog(program, bufLength, NULL, &logBuffer[0]);
+        fprintf(stderr, "%s\n", &logBuffer[0]);
+        fflush(stderr);
+      }
+      glDeleteProgram(program);
+      program = 0;
+    }
+  }
+  return program;
+}
+} // namespace
+
+namespace Dali::Toolkit::Physics::Internal
+{
+std::unique_ptr<PhysicsDebugRenderer> PhysicsDebugRenderer::New(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor)
+{
+  auto renderer             = std::make_unique<PhysicsDebugRenderer>(width, height, camera, adaptor);
+  renderer->mRenderCallback = Dali::RenderCallback::New<PhysicsDebugRenderer>(renderer.get(), &PhysicsDebugRenderer::OnRender);
+  return renderer;
+}
+
+PhysicsDebugRenderer::PhysicsDebugRenderer(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor)
+: mCamera(camera),
+  mWidth(width),
+  mHeight(height),
+  mAdaptor(*adaptor)
+{
+}
+
+bool PhysicsDebugRenderer::OnRender(const Dali::RenderCallbackInput& input)
+{
+  if(mState == State::INIT)
+  {
+    Setup();
+    mState = State::RENDER;
+  }
+  glViewport(0, 0, mWidth, mHeight);
+
+  RenderLines(input);
+
+  return false;
+}
+
+// Run on first invocation of callback
+void PhysicsDebugRenderer::Setup()
+{
+  PrepareShader();
+  mVertexLocation       = glGetAttribLocation(mProgramId, "vertexPosition");
+  mVertexColourLocation = glGetAttribLocation(mProgramId, "vertexColour");
+  mProjectionLocation   = glGetUniformLocation(mProgramId, "projection");
+  mModelViewLocation    = glGetUniformLocation(mProgramId, "modelView");
+
+  glEnable(GL_DEPTH_TEST);
+  glViewport(0, 0, mWidth, mHeight);
+
+  glGenBuffers(1, &mBufferId);
+}
+
+void PhysicsDebugRenderer::UpdateWindowSize(Dali::Vector2 size)
+{
+  mWidth  = size.width;
+  mHeight = size.height;
+}
+
+void PhysicsDebugRenderer::PrepareShader()
+{
+  static const char glVertexShader[] =
+    "attribute vec4 vertexPosition;\n"
+    "attribute vec3 vertexColour;\n"
+    "varying vec3 fragColour;\n"
+    "uniform mat4 projection;\n"
+    "uniform mat4 modelView;\n"
+    "void main()\n"
+    "{\n"
+    "    gl_Position = projection * modelView * vertexPosition;\n"
+    "    fragColour = vertexColour;\n"
+    "}\n";
+
+  static const char glFragmentShader[] =
+    "precision mediump float;\n"
+    "varying vec3 fragColour;\n"
+    "void main()\n"
+    "{\n"
+    "    gl_FragColor = vec4(fragColour, 1.0);\n"
+    "}\n";
+
+  mProgramId = CreateProgram(glVertexShader, glFragmentShader);
+}
+
+void PhysicsDebugRenderer::RenderLines(const Dali::RenderCallbackInput& input)
+{
+  mModelViewMatrix.SetIdentity();
+  mProjectionMatrix = input.projection;
+
+  Matrix::Multiply(mModelViewMatrix, mModelViewMatrix, input.view);
+  glUseProgram(mProgramId);
+
+  // In theory, input.clippingBox should tell us the actor position in clip-space.
+  // But, it appears to be bugged.
+
+  glBindBuffer(GL_ARRAY_BUFFER, mBufferId);
+  glBufferData(GL_ARRAY_BUFFER, mLines.size() * sizeof(VertexLine), &mLines[0], GL_STATIC_DRAW);
+
+  glVertexAttribPointer(mVertexLocation, 3, GL_FLOAT, GL_FALSE, 24, 0);
+  glEnableVertexAttribArray(mVertexLocation);
+  glVertexAttribPointer(mVertexColourLocation, 3, GL_FLOAT, GL_FALSE, 24, reinterpret_cast<const void*>(12));
+  glEnableVertexAttribArray(mVertexColourLocation);
+  glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix.AsFloat());
+  glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix.AsFloat());
+
+  glDrawArrays(GL_LINES, 0, mLines.size());
+  mLines.clear();
+}
+
+void PhysicsDebugRenderer::drawLine(const btVector3& from, const btVector3& to, const btVector3& color)
+{
+  mLines.push_back(VertexLine{mAdaptor.TranslateFromPhysicsSpace(Vector3(from.x(), from.y(), from.z())),
+                              Vector3(color.x(), color.y(), color.z())});
+  mLines.push_back(VertexLine{mAdaptor.TranslateFromPhysicsSpace(Vector3(to.x(), to.y(), to.z())), Vector3(color.x(), color.y(), color.z())});
+}
+
+void PhysicsDebugRenderer::drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor)
+{
+  mLines.push_back(VertexLine{mAdaptor.TranslateFromPhysicsSpace(Vector3(from.x(), from.y(), from.z())), Vector3(fromColor.x(), fromColor.y(), fromColor.z())});
+  mLines.push_back(VertexLine{mAdaptor.TranslateFromPhysicsSpace(Vector3(to.x(), to.y(), to.z())), Vector3(toColor.x(), toColor.y(), toColor.z())});
+}
+
+void PhysicsDebugRenderer::drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color)
+{
+}
+
+void PhysicsDebugRenderer::reportErrorWarning(const char* warningString)
+{
+}
+
+void PhysicsDebugRenderer::draw3dText(const btVector3& location, const char* textString)
+{
+}
+
+void PhysicsDebugRenderer::setDebugMode(int debugMode)
+{
+}
+
+int PhysicsDebugRenderer::getDebugMode() const
+{
+  return true;
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
diff --git a/dali-physics/internal/physics-debug-renderer.h b/dali-physics/internal/physics-debug-renderer.h
new file mode 100644 (file)
index 0000000..b7909e2
--- /dev/null
@@ -0,0 +1,106 @@
+#pragma once
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES3/gl3.h>
+#include <LinearMath/btIDebugDraw.h>
+#include <dali/dali.h>
+
+using Dali::Actor;
+using Dali::CameraActor;
+using Dali::Geometry;
+using Dali::Renderer;
+using Dali::Shader;
+using Dali::TextureSet;
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class PhysicsAdaptor;
+
+class PhysicsDebugRenderer : public btIDebugDraw
+{
+public:
+  // Creates and initializes a new renderer
+  static std::unique_ptr<PhysicsDebugRenderer> New(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor);
+
+  /**
+   * Get the callback (for actor creation)
+   */
+  std::unique_ptr<Dali::RenderCallback>& GetCallback()
+  {
+    return mRenderCallback;
+  }
+
+  void UpdateWindowSize(Dali::Vector2 size);
+
+  /**
+   * Constructor.
+   * @param[in] width Width of the renderer - viewport
+   * @param[in] height Height of the renderer - viewport
+   */
+  PhysicsDebugRenderer(uint32_t width, uint32_t height, Dali::CameraActor camera, PhysicsAdaptor* adaptor);
+
+public: // Inherited from btIDebugDraw
+  // Assume this is called during FrameCallback (i.e. in update manager, rather than during render...)
+  // Generate stack of lines... render, then clear stack.
+  void drawLine(const btVector3& from, const btVector3& to, const btVector3& color) override;
+  void drawLine(const btVector3& from, const btVector3& to, const btVector3& fromColor, const btVector3& toColor) override;
+  void drawContactPoint(const btVector3& PointOnB, const btVector3& normalOnB, btScalar distance, int lifeTime, const btVector3& color) override;
+  void reportErrorWarning(const char* warningString) override;
+  void draw3dText(const btVector3& location, const char* textString) override;
+  void setDebugMode(int debugMode) override;
+  int  getDebugMode() const override;
+
+private:
+  bool OnRender(const Dali::RenderCallbackInput& input);
+  void Setup();
+  void PrepareShader();
+  void RenderLines(const Dali::RenderCallbackInput& input);
+
+private:
+  CameraActor                           mCamera;
+  Renderer                              mDebugRenderer;
+  std::unique_ptr<Dali::RenderCallback> mRenderCallback;
+
+  enum class State
+  {
+    INIT,
+    RENDER
+  } mState{State::INIT};
+
+  struct VertexLine
+  {
+    Dali::Vector3 position;
+    Dali::Vector3 color;
+  };
+  std::vector<VertexLine> mLines;
+
+  Dali::Matrix    mModelViewMatrix;
+  Dali::Matrix    mViewMatrix;
+  Dali::Matrix    mProjectionMatrix;
+  int             mWidth;
+  int             mHeight;
+  PhysicsAdaptor& mAdaptor;
+  GLint           mVertexLocation;
+  GLint           mVertexColourLocation;
+  GLint           mProjectionLocation;
+  GLint           mModelViewLocation;
+  GLuint          mBufferId;
+  GLuint          mProgramId;
+};
+
+} // namespace Dali::Toolkit::Physics::Internal
diff --git a/dali-physics/internal/physics-world-impl.cpp b/dali-physics/internal/physics-world-impl.cpp
new file mode 100644 (file)
index 0000000..d8cd6af
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Class Header
+#include <dali-physics/internal/physics-world-impl.h>
+
+// External Headers
+#include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h>
+#include <btBulletCollisionCommon.h>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <utility>
+
+// Internal Headers
+#include <dali/dali.h>
+#include <dali/devel-api/common/stage-devel.h>
+#include <dali/devel-api/update/frame-callback-interface.h>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+/**
+ * FrameCallback implementation. Will run the OnUpdate method.
+ */
+class FrameCallback : public Dali::FrameCallbackInterface
+{
+public:
+  /**
+   * Constructor
+   */
+  explicit FrameCallback(PhysicsWorld& physicsWorld)
+  : mPhysicsWorld(physicsWorld)
+  {
+  }
+
+private:
+  /**
+   * Called each frame.
+   * @param[in] updateProxy Used to set world matrix and size
+   * @param[in] elapsedSeconds Time since last frame
+   * @return Whether we should keep rendering.
+   */
+  bool Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds) override
+  {
+    return mPhysicsWorld.OnUpdate(updateProxy, elapsedSeconds);
+  }
+
+private: // Member variables
+  PhysicsWorld& mPhysicsWorld;
+};
+
+using NativeWorld = btDiscreteDynamicsWorld*;
+
+std::unique_ptr<PhysicsWorld> PhysicsWorld::New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+{
+  std::unique_ptr<PhysicsWorld> world = std::make_unique<PhysicsWorld>(rootActor, updateCallback);
+  world->Initialize();
+  return world;
+}
+
+PhysicsWorld::PhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+: mUpdateCallback(updateCallback),
+  mRootActor(rootActor)
+{
+  Initialize();
+}
+
+void PhysicsWorld::Initialize(/*void* dynamicsWorld*/)
+{
+  // @todo Should enable developer to supply their own created DynamicsWorld.
+
+  mCollisionConfiguration = new btDefaultCollisionConfiguration();
+  mDispatcher             = new btCollisionDispatcher(mCollisionConfiguration);
+  mBroadphase             = new btDbvtBroadphase();
+  mSolver                 = new btSequentialImpulseConstraintSolver;
+  mDynamicsWorld          = new btDiscreteDynamicsWorld(mDispatcher, mBroadphase, mSolver, mCollisionConfiguration);
+
+  // Automatically start the frame callback. This means everything should
+  // be accessed with a mutex lock, which is automatically locked when
+  // ScopedAccessor is used.
+  mFrameCallback = std::make_unique<FrameCallback>(*this);
+  Dali::DevelStage::AddFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback, mRootActor);
+  Dali::Stage::GetCurrent().KeepRendering(30); // @todo Remove!
+}
+
+Dali::Any PhysicsWorld::GetNative()
+{
+  return mDynamicsWorld;
+}
+
+PhysicsWorld::~PhysicsWorld()
+{
+  Dali::DevelStage::RemoveFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
+
+  Dali::Mutex::ScopedLock lock(mMutex);
+
+  if(mDynamicsWorld)
+  {
+    int i;
+    for(i = mDynamicsWorld->getNumConstraints() - 1; i >= 0; i--)
+    {
+      mDynamicsWorld->removeConstraint(mDynamicsWorld->getConstraint(i));
+    }
+    for(i = mDynamicsWorld->getNumCollisionObjects() - 1; i >= 0; i--)
+    {
+      btCollisionObject* obj  = mDynamicsWorld->getCollisionObjectArray()[i];
+      btRigidBody*       body = btRigidBody::upcast(obj);
+      if(body && body->getMotionState())
+      {
+        delete body->getMotionState();
+      }
+      mDynamicsWorld->removeCollisionObject(obj);
+      delete obj;
+    }
+  }
+
+  /*
+  for (int j = 0; j < m_collisionShapes.size(); j++)
+  {
+    btCollisionShape* shape = mCollisionShapes[j];
+    delete shape;
+  }
+  mCollisionShapes.clear();
+  */
+
+  delete mDynamicsWorld;
+  delete mSolver;
+  delete mBroadphase;
+  delete mDispatcher;
+  delete mCollisionConfiguration;
+}
+
+Dali::Mutex& PhysicsWorld::GetMutex()
+{
+  return mMutex;
+}
+
+bool PhysicsWorld::OnUpdate(Dali::UpdateProxy& updateProxy, float elapsedSeconds)
+{
+  Dali::Mutex::ScopedLock lock(mMutex);
+
+  // Process command queue
+  if(mNotifySyncPoint != Dali::UpdateProxy::INVALID_SYNC &&
+     mNotifySyncPoint == updateProxy.PopSyncPoint())
+  {
+    do
+    {
+      commandQueue.front()(); // Execute the queued methods
+      commandQueue.pop();
+    } while(!commandQueue.empty());
+
+    mNotifySyncPoint = Dali::UpdateProxy::INVALID_SYNC;
+  }
+
+  // Perform as many integration steps as needed to handle elapsed time
+  static float frameTime = 0;
+  frameTime += elapsedSeconds;
+  do
+  {
+    Integrate(mPhysicsTimeStep);
+    frameTime -= mPhysicsTimeStep;
+  } while(frameTime > 0);
+
+  // Update the corresponding actors to their physics spaces
+  if(mUpdateCallback)
+  {
+    Dali::CallbackBase::Execute(*mUpdateCallback, &updateProxy); // Don't care about actor update return
+  }
+
+  // @todo Check physics world to see if everything is at rest
+  return true;
+}
+
+void PhysicsWorld::Integrate(float timestep)
+{
+  if(mPhysicsIntegrateState == Physics::PhysicsAdaptor::IntegrationState::ON)
+  {
+    mDynamicsWorld->stepSimulation(timestep);
+  }
+  if(mDynamicsWorld->getDebugDrawer() && mPhysicsDebugState == Physics::PhysicsAdaptor::DebugState::ON)
+  {
+    mDynamicsWorld->debugDrawWorld();
+  }
+}
+
+void PhysicsWorld::SetTimestep(float timeStep)
+{
+  mPhysicsTimeStep = timeStep;
+}
+
+float PhysicsWorld::GetTimestep()
+{
+  return mPhysicsTimeStep;
+}
+
+void PhysicsWorld::Queue(std::function<void(void)> function)
+{
+  if(!mMutex.IsLocked()) // Annoyingly, the dali mutex scoped lock doesn't prevent relocking in the same thread.
+  {
+    Dali::Mutex::ScopedLock lock(mMutex);
+    commandQueue.push(function);
+  }
+  else
+  {
+    commandQueue.push(function);
+  }
+}
+
+void PhysicsWorld::CreateSyncPoint()
+{
+  mNotifySyncPoint = Dali::DevelStage::NotifyFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
+}
+
+inline btVector3 ConvertVector(Dali::Vector3 vector)
+{
+  return btVector3(vector.x, vector.y, vector.z);
+}
+
+Dali::Any PhysicsWorld::HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera)
+{
+  btRigidBody* hitBody{nullptr};
+
+  btVector3                                  origin = ConvertVector(rayFromWorld);
+  btVector3                                  end    = ConvertVector(rayToWorld);
+  btCollisionWorld::ClosestRayResultCallback rayResultCallback(origin, end);
+  rayResultCallback.m_flags |= btTriangleRaycastCallback::kF_UseGjkConvexCastRaytest;
+
+  mDynamicsWorld->rayTest(origin, end, rayResultCallback);
+  if(rayResultCallback.hasHit())
+  {
+    auto         pickPos = rayResultCallback.m_hitPointWorld;
+    btRigidBody* body    = (btRigidBody*)btRigidBody::upcast(rayResultCallback.m_collisionObject);
+    if(body)
+    {
+      if(!(body->isStaticObject() || body->isKinematicObject()))
+      {
+        hitBody            = body; // Found a dynamic body.
+        distanceFromCamera = (pickPos - origin).length();
+
+        btVector3 pivot = body->getCenterOfMassTransform().inverse() * pickPos;
+        localPivot.x    = pivot.x();
+        localPivot.y    = pivot.y();
+        localPivot.z    = pivot.z();
+      }
+    }
+  }
+  Dali::Any bodyPtr;
+  if(hitBody)
+  {
+    bodyPtr = hitBody;
+  }
+
+  return bodyPtr;
+}
+
+void PhysicsWorld::SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state)
+{
+  mPhysicsIntegrateState = state;
+}
+
+Physics::PhysicsAdaptor::IntegrationState PhysicsWorld::GetIntegrationState()
+{
+  return mPhysicsIntegrateState;
+}
+
+void PhysicsWorld::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
+{
+  mPhysicsDebugState = state;
+}
+
+Physics::PhysicsAdaptor::DebugState PhysicsWorld::GetDebugState()
+{
+  return mPhysicsDebugState;
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
diff --git a/dali-physics/internal/physics-world-impl.h b/dali-physics/internal/physics-world-impl.h
new file mode 100644 (file)
index 0000000..fa6108f
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef DALI_TOOLKIT_PHYSICS_INTERNAL_PHYSICS_WORLD_H
+#define DALI_TOOLKIT_PHYSICS_INTERNAL_PHYSICS_WORLD_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/dali.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/update/frame-callback-interface.h>
+#include <dali/devel-api/update/update-proxy.h>
+
+#include <dali-physics/public-api/physics-adaptor.h>
+
+#include <btBulletDynamicsCommon.h>
+#include <functional>
+#include <queue>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class PhysicsWorld;
+class FrameCallback;
+
+class PhysicsWorld /* : public BaseObject */
+{
+public:
+  static std::unique_ptr<PhysicsWorld> New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+
+  PhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+  ~PhysicsWorld();
+
+  void Initialize(/*void* dynamicsWorld*/);
+
+  Dali::Any GetNative();
+
+  /**
+   * Set how long the integration should take.
+   * @param[in] timestep The length of time that the physics integration should take.
+   */
+  void SetTimestep(float timestep);
+
+  /**
+   * Get the current physics integration timestep
+   * @return the current physics integration timestep
+   */
+  float GetTimestep();
+
+  /**
+   * Queue a function for execution in the update thread, prior to the physics integration.
+   * Enables syncronization of DALi properties and physics controlled properties.
+   */
+
+  void Queue(std::function<void(void)> function);
+
+  /**
+   * Create a sync point for queued functions.
+   *
+   * Ensures that any queued functions are processed after this sync
+   * point is seen in the Update::FrameCallback, which will be in the
+   * same frame as any other DALi properties set during this event
+   * handler invocation.
+   *
+   * @param[in] None
+   */
+  void CreateSyncPoint();
+
+  /**
+   * Hit test the physics world and return the nearest body.
+   *
+   * @param[in] rayFromWorld The origin in physics world space
+   * @param[in] rayToWorld A point along the direction on the far side of the physics world
+   * @param[out] localPivot The hit point local to the body
+   * @param[out] distanceFromCamera The distance of the pick point from the camera
+   * @return Empty value if no dynamic body found, otherwise a valid ptr to the hit body.
+   */
+  Dali::Any HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetIntegrationState
+   */
+  void SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetIntegrationState
+   */
+  Physics::PhysicsAdaptor::IntegrationState GetIntegrationState();
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetDebugState
+   */
+  void SetDebugState(Physics::PhysicsAdaptor::DebugState state);
+
+  /**
+   * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetDebugState
+   */
+  Physics::PhysicsAdaptor::DebugState GetDebugState();
+
+public:
+  Dali::Mutex& GetMutex(); // Only for use by adaptor in creating scoped accessor
+
+  bool OnUpdate(Dali::UpdateProxy& updateProxy, float elapsedSeconds);
+
+private:
+  void Integrate(float timestep);
+
+private:
+  Dali::Mutex mMutex;
+
+  btDiscreteDynamicsWorld*             mDynamicsWorld{nullptr};
+  btCollisionDispatcher*               mDispatcher{nullptr};
+  btDefaultCollisionConfiguration*     mCollisionConfiguration{nullptr};
+  btBroadphaseInterface*               mBroadphase{nullptr};
+  btSequentialImpulseConstraintSolver* mSolver{nullptr};
+
+  std::queue<std::function<void(void)>> commandQueue;
+  Dali::UpdateProxy::NotifySyncPoint    mNotifySyncPoint;
+  Dali::CallbackBase*                   mUpdateCallback{nullptr};
+  std::unique_ptr<FrameCallback>        mFrameCallback;
+  Dali::Actor                           mRootActor;
+  float                                 mPhysicsTimeStep{1.0 / 180.0};
+
+  Physics::PhysicsAdaptor::IntegrationState mPhysicsIntegrateState{Physics::PhysicsAdaptor::IntegrationState::ON};
+  Physics::PhysicsAdaptor::DebugState       mPhysicsDebugState{Physics::PhysicsAdaptor::DebugState::OFF};
+};
+
+} // namespace Dali::Toolkit::Physics::Internal
+
+#endif //DALI_TOOLKIT_PHYSICS_INTERNAL_PHYSICS_WORLD_H
diff --git a/dali-physics/public-api/file.list b/dali-physics/public-api/file.list
new file mode 100644 (file)
index 0000000..0f454c7
--- /dev/null
@@ -0,0 +1,11 @@
+set(physics3d_public_api_dir "${physics_dir}/public-api")
+
+set(physics3d_src_files ${physics3d_src_files}
+       ${physics3d_public_api_dir}/physics-actor.cpp
+       ${physics3d_public_api_dir}/physics-adaptor.cpp
+)
+
+set(physics3d_public_api_header_files
+       ${physics3d_public_api_dir}/physics-actor.h
+       ${physics3d_public_api_dir}/physics-adaptor.h
+)
diff --git a/dali-physics/public-api/physics-actor.cpp b/dali-physics/public-api/physics-actor.cpp
new file mode 100644 (file)
index 0000000..5058520
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Class Header
+#include <dali-physics/public-api/physics-actor.h>
+
+// Internal headers
+#include <dali-physics/internal/physics-actor-impl.h>
+#include <dali-physics/internal/physics-adaptor-impl.h>
+
+namespace Dali::Toolkit::Physics
+{
+PhysicsActor::PhysicsActor()                            = default;
+PhysicsActor::~PhysicsActor()                           = default;
+PhysicsActor::PhysicsActor(const PhysicsActor& handle)  = default;
+PhysicsActor::PhysicsActor(PhysicsActor&& rhs) noexcept = default;
+PhysicsActor& PhysicsActor::operator=(const PhysicsActor& handle) = default;
+PhysicsActor& PhysicsActor::operator=(PhysicsActor&& handle) noexcept = default;
+
+PhysicsActor PhysicsActor::New(Dali::Actor actor, Dali::Any body, PhysicsAdaptor adaptor)
+{
+  Dali::Toolkit::Physics::Internal::PhysicsActorPtr internal = Internal::PhysicsActor::New(actor, body, GetImplementation(adaptor));
+  return PhysicsActor(internal.Get());
+}
+
+PhysicsActor PhysicsActor::DownCast(BaseHandle handle)
+{
+  return PhysicsActor(dynamic_cast<Dali::Toolkit::Physics::Internal::PhysicsActor*>(handle.GetObjectPtr()));
+}
+
+uint32_t PhysicsActor::GetId() const
+{
+  return GetImplementation(*this).GetId();
+}
+
+Dali::Any PhysicsActor::GetBody() const
+{
+  return GetImplementation(*this).GetBody();
+}
+
+void PhysicsActor::AsyncSetPhysicsPosition(Dali::Vector3 actorPosition)
+{
+  GetImplementation(*this).AsyncSetPhysicsPosition(actorPosition);
+}
+
+void PhysicsActor::AsyncSetPhysicsRotation(Dali::Quaternion actorRotation)
+{
+  GetImplementation(*this).AsyncSetPhysicsRotation(actorRotation);
+}
+
+Dali::Vector3 PhysicsActor::GetPhysicsPosition() const
+{
+  return GetImplementation(*this).GetPhysicsPosition();
+}
+
+Dali::Quaternion PhysicsActor::GetPhysicsRotation() const
+{
+  return GetImplementation(*this).GetPhysicsRotation();
+}
+
+Dali::Vector3 PhysicsActor::GetActorPosition() const
+{
+  return GetImplementation(*this).GetActorPosition();
+}
+
+Dali::Quaternion PhysicsActor::GetActorRotation() const
+{
+  return GetImplementation(*this).GetActorRotation();
+}
+
+PhysicsActor::PhysicsActor(Internal::PhysicsActor* impl)
+: BaseHandle(impl)
+{
+}
+
+} // namespace Dali::Toolkit::Physics
diff --git a/dali-physics/public-api/physics-actor.h b/dali-physics/public-api/physics-actor.h
new file mode 100644 (file)
index 0000000..e4514bd
--- /dev/null
@@ -0,0 +1,163 @@
+#ifndef DALI_TOOLKIT_PHYSICS_ACTOR_H
+#define DALI_TOOLKIT_PHYSICS_ACTOR_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/object/any.h>
+#include <dali/public-api/object/base-handle.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
+
+namespace Dali::Toolkit::Physics
+{
+class PhysicsAdaptor;
+
+namespace Internal
+{
+class PhysicsActor;
+}
+
+/**
+ * Class to associate a physics body (such as btRigidBody*) with a DALi
+ * actor for rendering.
+ *
+ * This object offers methods to modify basic physics properties
+ * asynchronously, that is, on the Update thread. It is also possible
+ * for the developer to queue other physics functions on the body by
+ * using PhysicsWorld::Queue() and passing in a lambda that captures the
+ * body.
+ *
+ * For example:
+ *   btRigidBody* body = physicsActor.GetBody().Get<btRigidBody*>();
+ *   mPhysicsImpl.Queue([body](){ body->clearForces(); });
+ * This enables the developer to synchronize setting physics properties
+ * and setting DALi actor properties.
+ */
+class DALI_TOOLKIT_API PhysicsActor : public Dali::BaseHandle
+{
+public:
+  PhysicsActor();
+  ~PhysicsActor();
+  PhysicsActor(const PhysicsActor& handle);
+  PhysicsActor(PhysicsActor&& rhs) noexcept;
+  PhysicsActor& operator=(const PhysicsActor& handle);
+  PhysicsActor& operator=(PhysicsActor&& handle) noexcept;
+
+  /**
+   * New method.
+   * @SINCE_2.2.40
+   *
+   * Binds the actor to the given body. This should be a body that has
+   * been added to the physics world, and has physical postion and
+   * rotation in that space. The Actor is used to render that object in
+   * DALi space.
+   *
+   * @note This object only stores the actor ID, which is used
+   * internally in the FrameCallback. It is up to the caller to ensure
+   * there is a reference to the actor (e.g. by parenting into the scene)
+   *
+   * @note This API is used internally, and is of little benefit to the developer.
+   *
+   * @param[in] actor The DALi actor used to render this object
+   * @param[in] body The physics body used in the physics simulation
+   * @param[in] adaptor The physics adaptor to use. @todo remove?
+   */
+  static PhysicsActor New(Dali::Actor actor, Dali::Any body, PhysicsAdaptor adaptor);
+
+  /**
+   * @brief Downcasts a handle to PhysicsActor handle.
+   *
+   * If handle points to an PhysicsActor object, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2.2.40
+   * @param[in] handle to an object
+   * @return handle to a PhysicsActor object or an uninitialized handle
+   */
+  static PhysicsActor DownCast(BaseHandle handle);
+
+  /**
+   * @brief Get the actor ID of the associated actor.
+   */
+  uint32_t GetId() const;
+
+  /**
+   * Using ANY wrapper to enable this interface to be used for any
+   * types of physics bodies from either 2d or 3d physics.
+   * @return The physics body. It can be cast to an appropriate type,
+   * for example:
+   *   btRigidBody* body = actor.GetBody().Get<btRigidBody*>();
+   *
+   * @note Please ensure to get a scoped accessor first to avoid modifying
+   * physics properties during integration step, or use the Async methods
+   * or the Queue.
+   */
+  Dali::Any GetBody() const;
+
+  /**
+   * Queue a method to set the position on the associated physics body
+   * in the update thread before the next integration.
+   * @param[in] actorPosition The position of the actor in DALi space
+   */
+  void AsyncSetPhysicsPosition(Dali::Vector3 actorPosition);
+
+  /**
+   * Queue a method to set the rotation of the associated physics body
+   * in the update thread before the next integration.
+   * @param[in] actorRotation The orientation of the actor in DALi space
+   */
+  void AsyncSetPhysicsRotation(Dali::Quaternion actorRotation);
+
+  /**
+   * Get the current position of the physics body in Physics space.
+   * @return the current position of the physics body in Physics space.
+   */
+  Dali::Vector3 GetPhysicsPosition() const;
+
+  /**
+   * Get the current rotation of the physics body in Physics space.
+   * @return the current rotation of the physics body in Physics space.
+   */
+  Dali::Quaternion GetPhysicsRotation() const;
+
+  /**
+   * Get the current position of the physics body in DALi space.
+   * @return the current position of the physics body in DALi space.
+   */
+  Dali::Vector3 GetActorPosition() const;
+
+  /**
+   * Get the current rotation of the physics body in DALi space.
+   * @return the current rotation of the physics body in DALi space.
+   */
+  Dali::Quaternion GetActorRotation() const;
+
+public: // Not intended for developer use
+  /// @cond internal
+  /**
+   * @note Not intented for application developers
+   */
+  explicit DALI_INTERNAL PhysicsActor(Internal::PhysicsActor* impl);
+  /// @endcond
+};
+
+} // namespace Dali::Toolkit::Physics
+
+#endif //DALI_TOOLKIT_PHYSICS_ACTOR_H
diff --git a/dali-physics/public-api/physics-adaptor.cpp b/dali-physics/public-api/physics-adaptor.cpp
new file mode 100644 (file)
index 0000000..c5cf51f
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Class Header
+#include <dali-physics/public-api/physics-adaptor.h>
+
+// Internal headers
+#include <dali-physics/internal/physics-actor-impl.h>
+#include <dali-physics/internal/physics-adaptor-impl.h>
+#include <dali-physics/public-api/physics-actor.h>
+
+namespace Dali::Toolkit::Physics
+{
+PhysicsAdaptor::PhysicsAdaptor() = default;
+
+PhysicsAdaptor::~PhysicsAdaptor() = default;
+
+PhysicsAdaptor::PhysicsAdaptor(const PhysicsAdaptor& handle) = default;
+
+PhysicsAdaptor::PhysicsAdaptor(PhysicsAdaptor&& rhs) noexcept = default;
+
+PhysicsAdaptor& PhysicsAdaptor::operator=(const PhysicsAdaptor& handle) = default;
+
+PhysicsAdaptor& PhysicsAdaptor::operator=(PhysicsAdaptor&& handle) noexcept = default;
+
+PhysicsAdaptor PhysicsAdaptor::New(const Dali::Matrix& transform, Uint16Pair size)
+{
+  Internal::PhysicsAdaptorPtr internal = Internal::PhysicsAdaptor::New(transform, size);
+  return PhysicsAdaptor(internal.Get());
+}
+
+PhysicsAdaptor PhysicsAdaptor::DownCast(BaseHandle handle)
+{
+  return PhysicsAdaptor(dynamic_cast<Dali::Toolkit::Physics::Internal::PhysicsAdaptor*>(handle.GetObjectPtr()));
+}
+
+void PhysicsAdaptor::SetTimestep(float timestep)
+{
+  GetImplementation(*this).SetTimestep(timestep);
+}
+
+float PhysicsAdaptor::GetTimestep()
+{
+  return GetImplementation(*this).GetTimestep();
+}
+
+PhysicsAdaptor::ScopedPhysicsAccessorPtr PhysicsAdaptor::GetPhysicsAccessor()
+{
+  return GetImplementation(*this).GetPhysicsAccessor();
+}
+
+Dali::Layer PhysicsAdaptor::CreateDebugLayer(Dali::Window window)
+{
+  return GetImplementation(*this).CreateDebugLayer(window);
+}
+
+Dali::Vector3 PhysicsAdaptor::TranslateToPhysicsSpace(Dali::Vector3 vector)
+{
+  return GetImplementation(*this).TranslateToPhysicsSpace(vector);
+}
+
+Dali::Quaternion PhysicsAdaptor::TranslateToPhysicsSpace(Dali::Quaternion rotation)
+{
+  return GetImplementation(*this).TranslateToPhysicsSpace(rotation);
+}
+
+Dali::Vector3 PhysicsAdaptor::TranslateFromPhysicsSpace(Dali::Vector3 vector)
+{
+  return GetImplementation(*this).TranslateFromPhysicsSpace(vector);
+}
+
+Dali::Vector3 PhysicsAdaptor::ConvertVectorToPhysicsSpace(Dali::Vector3 vector)
+{
+  return GetImplementation(*this).ConvertVectorToPhysicsSpace(vector);
+}
+
+Dali::Vector3 PhysicsAdaptor::ConvertVectorFromPhysicsSpace(Dali::Vector3 vector)
+{
+  return GetImplementation(*this).ConvertVectorFromPhysicsSpace(vector);
+}
+
+void PhysicsAdaptor::SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair size)
+{
+  GetImplementation(*this).SetTransformAndSize(transform, size);
+}
+
+void PhysicsAdaptor::SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state)
+{
+  GetImplementation(*this).SetIntegrationState(state);
+}
+
+Physics::PhysicsAdaptor::IntegrationState PhysicsAdaptor::GetIntegrationState()
+{
+  return GetImplementation(*this).GetIntegrationState();
+}
+
+void PhysicsAdaptor::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
+{
+  GetImplementation(*this).SetDebugState(state);
+}
+
+Physics::PhysicsAdaptor::DebugState PhysicsAdaptor::GetDebugState()
+{
+  return GetImplementation(*this).GetDebugState();
+}
+
+PhysicsActor PhysicsAdaptor::AddActorBody(Dali::Actor actor, Dali::Any body)
+{
+  Internal::PhysicsActorPtr physicsActor = GetImplementation(*this).AddActorBody(actor, body);
+  return PhysicsActor(physicsActor.Get());
+}
+
+PhysicsActor PhysicsAdaptor::GetPhysicsActor(Dali::Any body)
+{
+  Internal::PhysicsActorPtr physicsActor = GetImplementation(*this).GetPhysicsActor(body);
+  return PhysicsActor(physicsActor.Get());
+}
+
+Dali::Actor PhysicsAdaptor::GetRootActor()
+{
+  return GetImplementation(*this).GetRootActor();
+}
+
+void PhysicsAdaptor::BuildPickingRay(Dali::Vector3 origin, Dali::Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld)
+{
+  GetImplementation(*this).BuildPickingRay(origin, direction, rayFromWorld, rayToWorld);
+}
+
+Dali::Vector3 PhysicsAdaptor::ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance)
+{
+  return GetImplementation(*this).ProjectPoint(origin, direction, distance);
+}
+
+void PhysicsAdaptor::Queue(std::function<void(void)> function)
+{
+  GetImplementation(*this).Queue(function);
+}
+
+void PhysicsAdaptor::CreateSyncPoint()
+{
+  GetImplementation(*this).CreateSyncPoint();
+}
+
+PhysicsAdaptor::PhysicsAdaptor(Internal::PhysicsAdaptor* impl)
+: BaseHandle(impl)
+{
+}
+
+} // namespace Dali::Toolkit::Physics
diff --git a/dali-physics/public-api/physics-adaptor.h b/dali-physics/public-api/physics-adaptor.h
new file mode 100644 (file)
index 0000000..e39945b
--- /dev/null
@@ -0,0 +1,339 @@
+#ifndef DALI_TOOLKIT_PHYSICS_ADAPTOR_H
+#define DALI_TOOLKIT_PHYSICS_ADAPTOR_H
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or adaptoried.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/adaptor-framework/window.h>
+#include <dali/public-api/object/any.h>
+#include <dali/public-api/object/base-handle.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/public-api/dali-toolkit-common.h>
+
+namespace Dali::Toolkit::Physics
+{
+class PhysicsActor;
+
+namespace Internal
+{
+class PhysicsAdaptor;
+class PhysicsWorld;
+} // namespace Internal
+
+/**
+ * Adaptor to manage access to the physics world and pairing actors and physics
+ * bodies, plus some translation methods to/from the physics space and dali space.
+ *
+ * Also manages a debug renderer that may utilize the physics engine debug.
+ * It is up to the developer to retrieve the root actor and parent it into the scene.
+ */
+class DALI_TOOLKIT_API PhysicsAdaptor : public BaseHandle
+{
+public:
+  enum class IntegrationState
+  {
+    OFF,
+    ON
+  };
+  enum class DebugState
+  {
+    OFF,
+    ON
+  };
+
+  /**
+   * Scoped accessor to the physics world. Automatically locks the physics world
+   * with a mutex to prevent the integration step from running whilst the
+   * developer is accessing the world, e.g. to add/remove bodies or constraints,
+   * or to perform hit-test.
+   *
+   * When it goes out of scope, the mutex is unlocked, and the integration step
+   * can resume.
+   */
+  struct ScopedPhysicsAccessor
+  {
+  public:
+    /**
+     * Get a pointer to the native world. This uses DALi::Any wrapper to ensure
+     * that the same interface can be used for both 2d and 3d physics. It can be
+     * cast to the right type using the following construct:
+     *   auto accessor = PhysicsAdaptor.GetPhysicsAccessor();
+     *   auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamiscWorld*>();
+     */
+    Dali::Any GetNative();
+
+    /**
+     * Hit test the physics world and return the nearest body.
+     *
+     * @param[in] rayFromWorld The origin in physics world space
+     * @param[in] rayToWorld A point along the direction on the far side of the physics world
+     * @param[out] localPivot The hit point local to the body
+     * @param[out] distanceFromCamera The distance of the pick point from the camera
+     * @return Empty value if no dynamic body found, otherwise a valid ptr to the hit body.
+     */
+    Dali::Any HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera);
+
+    // Not copyable
+    ScopedPhysicsAccessor(ScopedPhysicsAccessor&) = delete;
+    const ScopedPhysicsAccessor& operator=(const ScopedPhysicsAccessor&) = delete;
+
+    /**
+     * Destructor
+     */
+    ~ScopedPhysicsAccessor();
+
+  private:
+    ScopedPhysicsAccessor(Internal::PhysicsWorld& world);
+    friend Internal::PhysicsAdaptor;
+
+    struct Impl;
+    Impl* mImpl;
+  };
+
+  PhysicsAdaptor();
+  ~PhysicsAdaptor();
+  PhysicsAdaptor(const PhysicsAdaptor& handle);
+  PhysicsAdaptor(PhysicsAdaptor&& rhs) noexcept;
+  PhysicsAdaptor& operator=(const PhysicsAdaptor& handle);
+  PhysicsAdaptor& operator=(PhysicsAdaptor&& handle) noexcept;
+
+  /**
+   * Initialize the physics system.
+   *
+   * @todo Consider allowing developer to create the physics world and pass it in here.
+   *
+   * @param[in] transform The transform matrix for DALi to Physics world space
+   * @param[in] size The size of the layer the physics actors will be drawn in
+   */
+  static PhysicsAdaptor New(const Dali::Matrix& transform, Uint16Pair size);
+
+  /**
+   * @brief Downcasts a handle to PhysicsAdaptor handle.
+   *
+   * If handle points to an PhysicsAdaptor object, the downcast produces valid handle.
+   * If not, the returned handle is left uninitialized.
+   *
+   * @SINCE_2.2.40
+   * @param[in] handle to an object
+   * @return handle to a PhysicsAdaptor object or an uninitialized handle
+   */
+  static PhysicsAdaptor DownCast(BaseHandle handle);
+
+  /**
+   * Set how long the integration should take.
+   * @param[in] timestep The length of time that the physics integration should take.
+   */
+  void SetTimestep(float timestep);
+
+  /**
+   * Get the current physics integration timestep
+   * @return the current physics integration timestep
+   */
+  float GetTimestep();
+
+  /**
+   * Returns an accessor pointer to the physics world. It automatically locks a mutex
+   * to prevent the integration step from running whilst the world is being modified.
+   *
+   * When the pointer goes out of scope, the mutex is unlocked and the physics world
+   * can run again.
+   */
+  using ScopedPhysicsAccessorPtr = std::unique_ptr<PhysicsAdaptor::ScopedPhysicsAccessor>;
+  ScopedPhysicsAccessorPtr GetPhysicsAccessor();
+
+  /**
+   * Create a layer & debug renderer
+   * The debug renderer may utilize the debug features of the native physics
+   * engine.
+   *
+   * @param[in] window The window to draw in (requires camera)
+   */
+  Dali::Layer CreateDebugLayer(Dali::Window window);
+
+  /**
+   * Converts a point in RootActor local coords (e.g. gesture)
+   * into physics space coords.
+   * @param vector The point to convert
+   * @return The converted point
+   */
+  Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Convert a rotation in DALi coordinate system into physics space.
+   * @param[in] rotation The rotation to convert
+   * @return the converted rotation.
+   */
+  Dali::Quaternion TranslateToPhysicsSpace(Dali::Quaternion rotation);
+
+  /**
+   * Converts a point in physics space coords.
+   * into RootActor local coords
+   * @param vector The point to convert
+   * @return The converted point
+   */
+  Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Convert a rotation in physics coordinate system into DALi space.
+   * @param[in] rotation The rotation to convert
+   * @return the converted rotation.
+   */
+  Dali::Quaternion TranslateFromPhysicsSpace(Dali::Quaternion rotation);
+
+  /**
+   * Converts a vector (not a point) in DALi space into physics space.
+   * @param vector The vector to convert
+   * @return The converted vector
+   */
+  Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Converts a vector (not a point) in physics space to DALi space
+   * @param vector The vector to convert
+   * @return The converted vector
+   */
+  Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Set up the transform from world space to physics space
+   * @param[in] transform The transform matrix for DALi to Physics world space
+   * @param[in] size The size of the layer the physics actors will be drawn in
+   */
+  void SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair size);
+
+  /**
+   * Set the integration state. If it's turned on, physics will run
+   * during the update frame callback.
+   * @note This is ON by default
+   * @param[in] state the new integration state
+   */
+  void SetIntegrationState(IntegrationState state);
+
+  /**
+   * Get the integration state.
+   * @return the new integration state
+   */
+  IntegrationState GetIntegrationState();
+
+  /**
+   * Set the debug state. If debug is turned on, use the physics engine
+   * debug to show wireframes in a layer above the root actor.
+   * @note This is OFF by default
+   * @param[in] state the new debug state
+   */
+  void SetDebugState(DebugState state);
+
+  /**
+   * Get the debug state.
+   * @return the new debug state
+   */
+  DebugState GetDebugState();
+
+  /**
+   * Add an actor / body pair.
+   * @pre It's expected that the client has added the body to the physics world.
+   *
+   * @param[in] actor The actor used for rendering the physics object
+   * @param[in] body The physics object
+   * @return a handle to the actor / body pair.
+   */
+  PhysicsActor AddActorBody(Dali::Actor actor, Dali::Any body);
+
+  /**
+   * Get the physics actor associated with the given body
+   * @param[in] body The physics body
+   * @return the associated physics actor
+   */
+  PhysicsActor GetPhysicsActor(Dali::Any body);
+
+  /**
+   * Get the root actor (which holds all the actor/body pairs)
+   */
+  Dali::Actor GetRootActor();
+
+  /**
+   * Convert DALi touch point into a picking ray in the physics world.
+   *
+   * These can then be used to hit test the PhysicsWorld
+   *
+   * @param[in] origin The origin in DALi world space
+   * @param[in] direction The direction of the picking ray
+   * @param[out] rayFromWorld The origin in physics world space
+   * @param[out] rayToWorld A point along the direction on the far side of the
+   * physics world
+   *
+   * Example:
+   *   OnTouched(Dali::Actor actor, Dali::TouchEvent& touch)
+   *   {
+   *     ..
+   *     Vector3 origin, direction;
+   *     Dali::HitTestAlgorithm::BuildPickingRay(renderTask, touch.GetScreenPosition(0), origin, direction);
+   *     btVector3 rayFromWorld, rayToWorld;
+   *     physicsAdaptor.BuildPickingRay(origin, direction, rayFromWorld, rayToWorld);
+   *     auto scopedAccessor = physicsAdaptor.GetPhysicsAccessor();
+   *     body = scopedAccessor->Get().HitTest(rayFromWorld, rayToWorld, ..);
+   *   }
+   */
+  void BuildPickingRay(Dali::Vector3 origin, Dali::Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld);
+
+  /**
+   * Project a point from the origin (in DALi space) a distance along
+   * the direction vector (in DALi space), and return the projected
+   * point in Physics space.
+   *
+   * @param[in] origin    Origin in DALi world space
+   * @param[in] direction Direction in DALi world space
+   * @param[in] distance  Distance along the direction vector
+   * @return the projected point, converted to the Physics space.
+   */
+  Dali::Vector3 ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance);
+
+  /**
+   * Queue a function to be executed before the physics integration in the update thread.
+   *
+   * @param[in] function to execute. This can be any method, and it can work fine with
+   * physics bodies etc, but it must not be used with DALI event side objects, as this
+   * will very likely cause the update thread to crash.
+   */
+  void Queue(std::function<void(void)> function);
+
+  /**
+   * Create a sync point for queued functions.
+   *
+   * Ensures that any queued functions are processed after this sync
+   * point is seen in the Update::FrameCallback, which will be in the
+   * same frame as any other DALi properties set during this event
+   * handler invocation.
+   *
+   * @param[in] None
+   */
+  void CreateSyncPoint();
+
+public: // Not intended for developer use
+  /// @cond internal
+  /**
+   * @note Not intented for application developers
+   */
+  explicit DALI_INTERNAL PhysicsAdaptor(Internal::PhysicsAdaptor* impl);
+  /// @endcond
+};
+
+} // namespace Dali::Toolkit::Physics
+
+#endif //DALI_TOOLKIT_PHYSICS_ADAPTOR_H
index ef3f0ce..8cdbecf 100644 (file)
@@ -948,6 +948,8 @@ void ConvertCamera(const gltf2::Camera& camera, CameraParameters& cameraParamete
     cameraParameters.zNear = ortho.mZNear;
     cameraParameters.zFar  = ortho.mZFar;
   }
+
+  cameraParameters.name = std::string(camera.mName);
 }
 
 void ConvertNode(gltf2::Node const& node, const Index gltfIndex, Index parentIndex, ConversionContext& context, bool isMRendererModel)
@@ -1472,4 +1474,4 @@ void ConvertGltfToContext(gt::Document& document, Gltf2Util::ConversionContext&
 
 } // namespace Gltf2Util
 
-} // namespace Dali::Scene3D::Loader::Internal
\ No newline at end of file
+} // namespace Dali::Scene3D::Loader::Internal
index 05bcef1..cf7e498 100644 (file)
@@ -197,6 +197,8 @@ void CameraParameters::CalculateTransformComponents(Vector3& position, Quaternio
 
 bool CameraParameters::ConfigureCamera(CameraActor& camera, bool invertY) const
 {
+  camera[Actor::Property::NAME] = name;
+
   if(isPerspective)
   {
     if(Dali::Equals(zNear, gltf2::UNDEFINED_FLOAT_VALUE) ||
@@ -266,4 +268,4 @@ bool CameraParameters::ConfigureCamera(CameraActor& camera, bool invertY) const
   return true;
 }
 
-} // namespace Dali::Scene3D::Loader
\ No newline at end of file
+} // namespace Dali::Scene3D::Loader
index 9fde071..b0b88b3 100644 (file)
@@ -25,6 +25,7 @@
 #include <dali/public-api/math/degree.h>
 #include <dali/public-api/math/matrix.h>
 #include <dali/public-api/math/vector3.h>
+#include <string>
 
 namespace Dali
 {
@@ -36,6 +37,8 @@ namespace Loader
 {
 struct DALI_SCENE3D_API CameraParameters
 {
+  std::string name;
+
   // TODO : Is these default value has is meaning?
   Matrix matrix           = Matrix::IDENTITY;
   float  orthographicSize = 1.f;
index 203dffd..78611e8 100644 (file)
@@ -544,10 +544,12 @@ esac
 %endif
 %defattr(-,root,root,-)
 %{_libdir}/libchipmunk.so*
+#%{_libdir}/libdali2-physics-2d.so*
 %license LICENSE
 
 %files -n %{dali2_physics2d}-devel
 %defattr(-,root,root,-)
+%{_includedir}/dali-physics/public-api/*
 %{_includedir}/chipmunk/*
 %{_libdir}/pkgconfig/dali2-physics-2d.pc
 %{_libdir}/pkgconfig/chipmunk2d.pc
@@ -560,11 +562,12 @@ esac
 %endif
 %defattr(-,root,root,-)
 %{_libdir}/libbullet3.so*
+%{_libdir}/libdali2-physics-3d.so*
 %license LICENSE
 
 %files -n %{dali2_physics3d}-devel
 %defattr(-,root,root,-)
+%{_includedir}/dali-physics/public-api/*
 %{_includedir}/bullet/*
 %{_libdir}/pkgconfig/dali2-physics-3d.pc
 %{_libdir}/pkgconfig/bullet3.pc
-