/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
#!/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.
$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)
{
--- /dev/null
+SET(PKG_NAME "dali-physics2d")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+SET(RPM_NAME "core-${PKG_NAME}-tests")
+
+# 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(${PKG_NAME} REQUIRED
+ dali2-core
+ dali2-adaptor
+ dali2-toolkit
+ dali2-physics-2d
+ chipmunk2d
+)
+
+ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED)
+ADD_COMPILE_OPTIONS( ${${PKG_NAME}_CFLAGS_OTHER} )
+
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
+FOREACH(directory ${${PKG_NAME}_LIBRARY_DIRS})
+ SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
+ENDFOREACH(directory ${PKG_NAME_LIBRARY_DIRS})
+
+INCLUDE_DIRECTORIES(
+ ../../../
+ ${${PKG_NAME}_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}
+ ${${PKG_NAME}_LIBRARIES}
+ -lpthread -ldl --coverage
+)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+ DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
--- /dev/null
+#include <test-harness.h>
+
+#include "tct-dali-physics2d-core.h"
+
+int main(int argc, char* const argv[])
+{
+ return TestHarness::RunTests(argc, argv, tc_array);
+}
--- /dev/null
+/*
+ * 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 <chipmunk/chipmunk.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 cpBody* CreateBody(cpSpace* space);
+
+const char* BALL_IMAGE = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
+
+int UtcDaliPhysics2DActorNew(void)
+{
+ ToolkitTestApplication application;
+
+ cpBody* body{nullptr};
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ Dali::Actor ballActor = Toolkit::ImageView::New(BALL_IMAGE);
+
+ PhysicsActor physicsActor = PhysicsActor::New(ballActor, body, adaptor);
+
+ DALI_TEST_CHECK(physicsActor);
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorDownCastP(void)
+{
+ ToolkitTestApplication application;
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ cpBody* body{nullptr};
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorDownCastN(void)
+{
+ BaseHandle uninitializedHandle;
+ PhysicsActor actor = PhysicsActor::DownCast(uninitializedHandle);
+ DALI_TEST_CHECK(!actor);
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorMoveConstructor(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Testing the move constructor");
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ cpBody* body{nullptr};
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorCopyConstructor(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Testing the move constructor");
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ cpBody* body{nullptr};
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorCopyAssign(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Testing the copy assign");
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ cpBody* body{nullptr};
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorMoveAssignment(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Testing the move constructor");
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ cpBody* body{nullptr};
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorGetIdP(void)
+{
+ ToolkitTestApplication application;
+ tet_infoline("Testing the ID Getter");
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ cpBody* body{nullptr};
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorGetIdN(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 UtcDaliPhysics2DActorGetBodyP(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;
+ cpBody* body{nullptr};
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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<cpBody*>(), body, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorGetBodyN(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 UtcDaliPhysics2DActorSetPosition(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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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, 0), 0.01f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorSetRotation1(void)
+{
+ tet_infoline("Test the AsyncSetPhysicsRotation() function");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 1.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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorSetRotation2(void)
+{
+ tet_infoline("Test the AsyncSetPhysicsRotation() function");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 1.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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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.001f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorGetActorPosition(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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ physicsActor = adaptor.AddActorBody(ballActor, body);
+ tet_infoline("Test that Z is ignored");
+ 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();
+ tet_infoline("Test that Z is ignored. Note, error is quite high, so make epsilon low");
+ DALI_TEST_EQUALS(physicsActor.GetActorPosition(), Vector3(10, 20, 0), 0.01f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorGetActorRotation(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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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 UtcDaliPhysics2DActorGetPhysicsPosition(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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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, 0, 1);
+ DALI_TEST_EQUALS(physicsActor.GetPhysicsPosition(), Vector3(pos), 0.01f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DActorGetPhysicsRotation(void)
+{
+ tet_infoline("Test the GetPhysicsRotation() function");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 1.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);
+
+ cpBody* body{nullptr};
+ PhysicsActor physicsActor;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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();
+ {
+ tet_infoline("Check that actor and physics rotations are identical");
+ auto accessor = adaptor.GetPhysicsAccessor();
+ DALI_TEST_EQUALS(physicsActor.GetPhysicsRotation(), Quaternion(Degree(30), Vector3::ZAXIS), 0.0001f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
--- /dev/null
+/*
+ * 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 <chipmunk/chipmunk.h>
+
+using namespace Dali;
+using namespace Dali::Toolkit::Physics;
+
+void utc_dali_physics2d_startup(void)
+{
+ test_return_value = TET_UNDEF;
+}
+
+void utc_dali_physics2d_cleanup(void)
+{
+ test_return_value = TET_PASS;
+}
+
+cpBody* CreateBody(cpSpace* space)
+{
+ const float BALL_MASS = 10.0f;
+ const float BALL_RADIUS = 26.0f;
+ const float BALL_ELASTICITY = 0.5f;
+ const float BALL_FRICTION = 0.5f;
+
+ cpBody* body = cpSpaceAddBody(space, cpBodyNew(BALL_MASS, cpMomentForCircle(BALL_MASS, 0.0f, BALL_RADIUS, cpvzero)));
+
+ cpShape* shape = cpSpaceAddShape(space, cpCircleShapeNew(body, BALL_RADIUS, cpvzero));
+ cpShapeSetElasticity(shape, BALL_ELASTICITY);
+ cpShapeSetFriction(shape, BALL_FRICTION);
+
+ return body;
+}
+
+int UtcDaliPhysics2DCreateAdaptorP1(void)
+{
+ ToolkitTestApplication application;
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+
+ PhysicsAdaptor handle = PhysicsAdaptor::New(transform, size);
+ DALI_TEST_CHECK(handle);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DCreateAdaptorN1(void)
+{
+ ToolkitTestApplication application;
+
+ PhysicsAdaptor handle;
+ DALI_TEST_CHECK(!handle);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DDowncastP1(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 UtcDaliPhysics2DDowncastN1(void)
+{
+ BaseHandle handle;
+ auto adaptor = PhysicsAdaptor::DownCast(handle);
+ DALI_TEST_CHECK(!adaptor);
+
+ DALI_TEST_CHECK(typeid(PhysicsAdaptor) == typeid(decltype(adaptor)));
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorMoveConstructor(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 UtcDaliPhysics2DAdaptorCopyConstructor(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 UtcDaliPhysics2DAdaptorCopyAssign(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 UtcDaliPhysics2DAdaptorMoveAssignment(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 UtcDaliPhysics2DSetTimestep(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 UtcDaliPhysics2DGetTimestep(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 UtcDaliPhysics2DGetPhysicsAccessorP1(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 UtcDaliPhysics2DGetPhysicsAccessorN1(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 UtcDaliPhysics2DAdaptorGetRootActor(void)
+{
+ tet_infoline("Test that the root actor can be retrieved");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 UtcDaliPhysics2DAdaptorCreateDebugLayer(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(true);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorTranslateToPhysicsSpace1(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.0f));
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ Vector3 a(30, 20, 10);
+ Vector3 expected(60, 40, 10);
+ DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(a), expected, 0.0001f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorTranslateToPhysicsSpace2(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, 1.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 UtcDaliPhysics2DAdaptorTranslateToPhysicsSpace3(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ tet_infoline("Test that using an inverted Y scale does nothing to rotation");
+
+ 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);
+
+ DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(q), qp, 0.0001f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorTranslateToPhysicsSpace4(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ tet_infoline("Test that using an inverted Y scale does nothing to rotation");
+
+ 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);
+
+ DALI_TEST_EQUALS(adaptor.TranslateToPhysicsSpace(q), qp, 0.0001f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorTranslateToPhysicsSpace5(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ tet_infoline("Test that using an inverted Y scale does nothing to rotation");
+
+ 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 UtcDaliPhysics2DAdaptorTranslateFromPhysicsSpace1(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ tet_infoline("Test that using a double scale halves position");
+
+ transform.SetIdentityAndScale(Vector3(2.0f, -2.0f, 1.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 UtcDaliPhysics2DAdaptorConvertVectorToPhysicsSpace01(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 UtcDaliPhysics2DAdaptorConvertVectorToPhysicsSpace02(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 UtcDaliPhysics2DAdaptorConvertVectorFromPhysicsSpace01(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 UtcDaliPhysics2DAdaptorConvertVectorFromPhysicsSpace02(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 UtcDaliPhysics2DAdaptorSetTransformAndSize(void)
+{
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.0f));
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ Vector3 a(30, 20, 10);
+ Vector3 expected(60, 40, 10);
+ 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 UtcDaliPhysics2DAdaptorSetIntegrationState(void)
+{
+ tet_infoline("Test that changing the integration state is reflected");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 UtcDaliPhysics2DAdaptorGetIntegrationState(void)
+{
+ tet_infoline("Test that changing the integration state is reflected");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 UtcDaliPhysics2DAdaptorSetDebugState(void)
+{
+ tet_infoline("Test that changing the debug state is reflected");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 UtcDaliPhysics2DAdaptorGetDebugState(void)
+{
+ tet_infoline("Test that changing the debug state is reflected");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 UtcDaliPhysics2DAdaptorAddActorBody(void)
+{
+ tet_infoline("Test that an actor/body pair can be added");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 space = accessor->GetNative().Get<cpSpace*>();
+
+ cpBody* body = CreateBody(space);
+ 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<cpBody*>(), body, TEST_LOCATION);
+
+ END_TEST;
+}
+
+void removeShape(cpBody* body, cpShape* shape, void* data)
+{
+ cpSpace* space = static_cast<cpSpace*>(data);
+ cpSpaceRemoveShape(space, shape);
+ cpShapeSetBody(shape, nullptr);
+ cpShapeFree(shape);
+}
+
+int UtcDaliPhysics2DAdaptorRemoveActorBodyP01(void)
+{
+ tet_infoline("Test that an actor/body pair can be removed");
+
+ 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);
+
+ cpBody* body;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+
+ body = CreateBody(space);
+ }
+ Dali::Actor ballActor = Toolkit::ImageView::New("gallery-small-1.jpg");
+ auto physicsActor = adaptor.AddActorBody(ballActor, body);
+
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ adaptor.RemoveActorBody(physicsActor);
+ DALI_TEST_CHECK(!ballActor.GetParent());
+
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+
+ try
+ {
+ cpBodyEachShape(body, removeShape, space);
+ cpSpaceRemoveBody(space, body);
+ tet_result(TET_PASS);
+ }
+ catch(std::exception& e)
+ {
+ tet_result(TET_FAIL);
+ }
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorRemoveActorBodyN01(void)
+{
+ 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);
+
+ tet_infoline("Test that removing a physics actor that hasn't been created with AddActorBody does nothing");
+ Dali::Actor actor = Dali::Actor::New();
+ cpBody* body = cpBodyNew(1.0f, 1.0f);
+ PhysicsActor physicsActor = PhysicsActor::New(actor, body, adaptor);
+ ;
+ try
+ {
+ adaptor.RemoveActorBody(physicsActor);
+ tet_result(TET_PASS);
+ }
+ catch(std::exception& e)
+ {
+ // Should fail silently, without exception!
+ tet_result(TET_FAIL);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorRemoveActorBodyN02(void)
+{
+ tet_infoline("Test that an empty actor/body pair doesn't break adaptor");
+
+ 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);
+
+ PhysicsActor physicsActor;
+ try
+ {
+ adaptor.RemoveActorBody(physicsActor);
+ tet_result(TET_FAIL);
+ }
+ catch(DaliException& e)
+ {
+ DALI_TEST_ASSERT(e, "Physics actor handle is empty", TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorGetPhysicsActor(void)
+{
+ tet_infoline("Test that an actor/body pair can be retrieved");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.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 space = accessor->GetNative().Get<cpSpace*>();
+
+ cpBody* body = CreateBody(space);
+ 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 UtcDaliPhysics2DAdaptorBuildPickingRay(void)
+{
+ tet_infoline("Test that picking ray converts screen coords");
+
+ 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);
+
+ Vector3 from, to;
+ adaptor.BuildPickingRay(Vector3(center), Vector3(center), from, to); // Hit test centre of screen
+ Vector3 physCenter = adaptor.TranslateToPhysicsSpace(Vector3(center));
+ DALI_TEST_EQUALS(from, physCenter, 0.001f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorProjectPoint(void)
+{
+ tet_infoline("Test that a point is projected into physics space");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.0f));
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+
+ Vector3 projectedPoint = adaptor.ProjectPoint(Vector3(), -Vector3::ZAXIS, 200);
+
+ DALI_TEST_EQUALS(projectedPoint, Vector3(0.0f, 0.0f, 0.0f), 0.001f, TEST_LOCATION);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorQueue(void)
+{
+ tet_infoline("Test that Queue and CreateSyncPoint both work");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.0f));
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+ Actor rootActor = adaptor.GetRootActor();
+ auto scene = application.GetScene();
+ scene.Add(rootActor);
+
+ cpBody* body{nullptr};
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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]() {
+ cpBodySetPosition(body, cpv(100.0f, 20.0f));
+ });
+ adaptor.CreateSyncPoint();
+
+ application.SendNotification();
+ application.Render();
+ // Should trigger an Update
+
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ cpVect origin = cpBodyGetPosition(body);
+
+ DALI_TEST_EQUALS(origin.x, cpFloat(100.0f), 0.001f, TEST_LOCATION);
+ DALI_TEST_EQUALS(origin.y, cpFloat(20.0f), 0.001f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorCreateSyncPoint(void)
+{
+ tet_infoline("Test that a delayed CreateSyncPoint delays update");
+
+ ToolkitTestApplication application;
+ Matrix transform(false);
+ transform.SetIdentityAndScale(Vector3(2.0f, 2.0f, 1.0f));
+ Uint16Pair size(640, 480);
+ PhysicsAdaptor adaptor = PhysicsAdaptor::New(transform, size);
+ Actor rootActor = adaptor.GetRootActor();
+ auto scene = application.GetScene();
+ scene.Add(rootActor);
+
+ cpBody* body{nullptr};
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto space = accessor->GetNative().Get<cpSpace*>();
+ body = CreateBody(space);
+ 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]() {
+ cpBodySetPosition(body, cpv(100.0f, 20.0f));
+ });
+ }
+
+ // Should trigger an Update without processing queue
+ application.SendNotification();
+ application.Render();
+
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+
+ cpVect origin = cpBodyGetPosition(body);
+ DALI_TEST_EQUALS(origin.x, cpFloat(0.0f), 0.01f, TEST_LOCATION);
+ DALI_TEST_EQUALS(origin.y, cpFloat(0.0f), 0.01f, TEST_LOCATION);
+ }
+
+ // Should now execute queue
+ adaptor.CreateSyncPoint();
+ application.SendNotification();
+ application.Render();
+
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+
+ cpVect origin = cpBodyGetPosition(body);
+ DALI_TEST_EQUALS(origin.x, cpFloat(100.0f), 0.01f, TEST_LOCATION);
+ DALI_TEST_EQUALS(origin.y, cpFloat(20.0f), 0.01f, TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics2DAdaptorHitTestP(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 space = accessor->GetNative().Get<cpSpace*>();
+ Dali::Actor ballActor = Toolkit::ImageView::New(TEST_RESOURCE_DIR "/gallery-small-1.jpg");
+ cpBody* body = CreateBody(space);
+ cpBodySetPosition(body, cpv(center.x, center.y));
+
+ 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 from, to;
+ adaptor.BuildPickingRay(Vector3(center), Vector3(center), from, to); // Hit test centre of screen
+
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ Vector3 localPivot;
+ float distanceFromCamera;
+ auto body = accessor->HitTest(from, from, localPivot, distanceFromCamera);
+
+ DALI_TEST_CHECK(!body.Empty());
+ }
+
+ END_TEST;
+}
--- /dev/null
+SET(PKG_NAME "dali-physics3d")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+SET(RPM_NAME "core-${PKG_NAME}-tests")
+
+# 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(${PKG_NAME} REQUIRED
+ dali2-core
+ dali2-adaptor
+ dali2-toolkit
+ dali2-physics-3d
+ bullet3
+)
+
+ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED)
+ADD_COMPILE_OPTIONS( ${${PKG_NAME}_CFLAGS_OTHER} )
+
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
+FOREACH(directory ${${PKG_NAME}_LIBRARY_DIRS})
+ SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
+ENDFOREACH(directory ${PKG_NAME_LIBRARY_DIRS})
+
+INCLUDE_DIRECTORIES(
+ ../../../
+ ${${PKG_NAME}_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}
+ ${${PKG_NAME}_LIBRARIES}
+ -lpthread -ldl --coverage
+)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+ DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
--- /dev/null
+#include <test-harness.h>
+
+// Must come second
+#include "tct-dali-physics3d-core.h"
+
+int main(int argc, char* const argv[])
+{
+ return TestHarness::RunTests(argc, argv, tc_array);
+}
--- /dev/null
+/*
+ * 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 UtcDaliPhysics3DActorNew(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 UtcDaliPhysics3DActorDownCastP(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 UtcDaliPhysics3DActorDownCastN(void)
+{
+ BaseHandle uninitializedHandle;
+ PhysicsActor actor = PhysicsActor::DownCast(uninitializedHandle);
+ DALI_TEST_CHECK(!actor);
+ END_TEST;
+}
+
+int UtcDaliPhysics3DActorMoveConstructor(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 UtcDaliPhysics3DActorCopyConstructor(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 UtcDaliPhysics3DActorCopyAssign(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 UtcDaliPhysics3DActorMoveAssignment(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 UtcDaliPhysics3DActorGetIdP(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 UtcDaliPhysics3DActorGetIdN(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 UtcDaliPhysics3DActorGetBodyP(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 UtcDaliPhysics3DActorGetBodyN(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 UtcDaliPhysics3DActorSetPosition(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 UtcDaliPhysics3DActorSetRotation1(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 UtcDaliPhysics3DActorSetRotation2(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 UtcDaliPhysics3DActorGetActorPosition(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 UtcDaliPhysics3DActorGetActorRotation(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 UtcDaliPhysics3DActorGetPhysicsPosition(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 UtcDaliPhysics3DActorGetPhysicsRotation(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;
+}
--- /dev/null
+/*
+ * 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 UtcDaliPhysics3DCreateAdaptorP1(void)
+{
+ ToolkitTestApplication application;
+
+ Matrix transform(true);
+ Uint16Pair size(640, 480);
+
+ PhysicsAdaptor handle = PhysicsAdaptor::New(transform, size);
+ DALI_TEST_CHECK(handle);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics3DCreateAdaptorN1(void)
+{
+ ToolkitTestApplication application;
+
+ PhysicsAdaptor handle;
+ DALI_TEST_CHECK(!handle);
+
+ END_TEST;
+}
+
+int UtcDaliPhysics3DDowncastP1(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 UtcDaliPhysics3DDowncastN1(void)
+{
+ BaseHandle handle;
+ auto adaptor = PhysicsAdaptor::DownCast(handle);
+ DALI_TEST_CHECK(!adaptor);
+
+ DALI_TEST_CHECK(typeid(PhysicsAdaptor) == typeid(decltype(adaptor)));
+ END_TEST;
+}
+
+int UtcDaliPhysics3DAdaptorMoveConstructor(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 UtcDaliPhysics3DAdaptorCopyConstructor(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 UtcDaliPhysics3DAdaptorCopyAssign(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 UtcDaliPhysics3DAdaptorMoveAssignment(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 UtcDaliPhysics3DSetTimestep(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 UtcDaliPhysics3DGetTimestep(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 UtcDaliPhysics3DGetPhysicsAccessorP1(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 UtcDaliPhysics3DGetPhysicsAccessorN1(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 UtcDaliPhysics3DAdaptorGetRootActor(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 UtcDaliPhysics3DAdaptorCreateDebugLayer(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 UtcDaliPhysics3DAdaptorTranslateToPhysicsSpace1(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 UtcDaliPhysics3DAdaptorTranslateToPhysicsSpace2(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 UtcDaliPhysics3DAdaptorTranslateToPhysicsSpace3(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 UtcDaliPhysics3DAdaptorTranslateToPhysicsSpace4(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 UtcDaliPhysics3DAdaptorTranslateToPhysicsSpace5(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 UtcDaliPhysics3DAdaptorTranslateFromPhysicsSpace1(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 UtcDaliPhysics3DAdaptorConvertVectorToPhysicsSpace01(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 UtcDaliPhysics3DAdaptorConvertVectorToPhysicsSpace02(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 UtcDaliPhysics3DAdaptorConvertVectorFromPhysicsSpace01(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 UtcDaliPhysics3DAdaptorConvertVectorFromPhysicsSpace02(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 UtcDaliPhysics3DAdaptorSetTransformAndSize(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 UtcDaliPhysics3DAdaptorSetIntegrationState(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 UtcDaliPhysics3DAdaptorGetIntegrationState(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 UtcDaliPhysics3DAdaptorSetDebugState(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 UtcDaliPhysics3DAdaptorGetDebugState(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 UtcDaliPhysics3DAdaptorAddActorBody(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 UtcDaliPhysics3DAdaptorRemoveActorBodyP01(void)
+{
+ tet_infoline("Test that an actor/body pair can be removed");
+
+ 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);
+
+ btRigidBody* body;
+ {
+ 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);
+
+ application.SendNotification();
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ adaptor.RemoveActorBody(physicsActor);
+ DALI_TEST_CHECK(!ballActor.GetParent());
+
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+
+ bulletWorld->removeRigidBody(body);
+ try
+ {
+ delete body;
+ tet_result(TET_PASS);
+ }
+ catch(std::exception& e)
+ {
+ tet_result(TET_FAIL);
+ }
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics3DAdaptorRemoveActorBodyN01(void)
+{
+ tet_infoline("Test that an empty actor/body pair doesn't break adaptor");
+
+ 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);
+
+ tet_infoline("Test that removing a physics actor that hasn't been created with AddActorBody does nothing");
+
+ Dali::Actor actor = Dali::Actor::New();
+ btRigidBody* body;
+ {
+ auto accessor = adaptor.GetPhysicsAccessor();
+ auto bulletWorld = accessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+
+ body = CreateBody(bulletWorld);
+ }
+
+ PhysicsActor physicsActor = PhysicsActor::New(actor, body, adaptor);
+ ;
+ try
+ {
+ adaptor.RemoveActorBody(physicsActor);
+ tet_result(TET_PASS);
+ }
+ catch(std::exception& e)
+ {
+ tet_result(TET_FAIL);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics3DAdaptorRemoveActorBodyN02(void)
+{
+ tet_infoline("Test that an empty actor/body pair doesn't break adaptor");
+
+ 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);
+
+ PhysicsActor physicsActor;
+ try
+ {
+ adaptor.RemoveActorBody(physicsActor);
+ tet_result(TET_FAIL);
+ }
+ catch(DaliException& e)
+ {
+ DALI_TEST_ASSERT(e, "Physics actor handle is empty", TEST_LOCATION);
+ }
+
+ END_TEST;
+}
+
+int UtcDaliPhysics3DAdaptorGetPhysicsActor(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 UtcDaliPhysics3DAdaptorBuildPickingRay(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 UtcDaliPhysics3DAdaptorProjectPoint(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 UtcDaliPhysics3DAdaptorQueue(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 UtcDaliPhysics3DAdaptorCreateSyncPoint(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 UtcDaliPhysics3DAdaptorHitTestP(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?!
# 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
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
#define DALI_TEST_COMPARE_TYPES_H
/*
- * Copyright (c) 2022 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.
}
/**
- * A helper for fuzzy-comparing Vector2 objects
- * @param[in] vector1 the first object
- * @param[in] vector2 the second object
+ * A helper for matching floats
+ * @param[in] value1 the first object
+ * @param[in] value2 the second object
* @param[in] epsilon difference threshold
* @returns true if difference is smaller than epsilon threshold, false otherwise
*/
}
/**
+ * A helper for matching doubles
+ * @param[in] value1 the first object
+ * @param[in] value2 the second object
+ * @param[in] epsilon difference threshold
+ * @returns true if difference is smaller than epsilon threshold, false otherwise
+ */
+template<>
+inline bool CompareType<double>(double value1, double value2, float epsilon)
+{
+ return fabs(value1 - value2) < double(epsilon);
+}
+
+/**
* A helper for fuzzy-comparing Vector2 objects
* @param[in] vector1 the first object
* @param[in] vector2 the second object
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)
--- /dev/null
+/*
+* 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
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
// 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
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();
#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.
*/
void RunIdles();
-private:
+ Dali::Window GetWindow()
+ {
+ return mMainWindow;
+ }
- std::unique_ptr<Dali::Window> mMainWindow;
+private:
+ Dali::Window mMainWindow;
std::unique_ptr< Adaptor > mAdaptor;
};
+++ /dev/null
-/*
-* 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
END_TEST;
}
+
+int UtcDaliAnimatedVectorImageVisualFlushAction(void)
+{
+ ToolkitTestApplication application;
+
+ tet_infoline("UtcDaliAnimatedVectorImageVisualFlushAction");
+
+ int startFrame = 1;
+ int endFrame = 2;
+
+ int totalFrameCount = 0;
+
+ Property::Array playRange;
+ playRange.PushBack(startFrame);
+ playRange.PushBack(endFrame);
+
+ Property::Map resultMap;
+ Property::Value* value = nullptr;
+
+ // request AnimatedVectorImageVisual with a property map
+ VisualFactory factory = VisualFactory::Get();
+ Visual::Base visual = factory.CreateVisual(
+ Property::Map()
+ .Add(Toolkit::Visual::Property::TYPE, DevelVisual::ANIMATED_VECTOR_IMAGE)
+ .Add(ImageVisual::Property::URL, TEST_VECTOR_IMAGE_FILE_NAME)
+ .Add(DevelImageVisual::Property::PLAY_RANGE, playRange)
+ .Add(ImageVisual::Property::SYNCHRONOUS_LOADING, true));
+
+ DummyControl dummyControl = DummyControl::New(true);
+ Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+ dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, visual);
+ dummyControl.SetResizePolicy(ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS);
+
+ application.GetScene().Add(dummyControl);
+
+ // Retry function to get playrange until expect values comes.
+ auto CheckAndRetryPlayRange = [&](int expectStartFrame, int expectEndFrame, std::vector<std::pair<int, int>> retrialFrames) {
+ int tryCount = 0;
+ int tryCountMax = 30;
+ while(++tryCount <= tryCountMax)
+ {
+ Property::Map resultMap = dummyControl.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+
+ Property::Value* value = resultMap.Find(DevelImageVisual::Property::PLAY_RANGE, Property::ARRAY);
+ DALI_TEST_CHECK(value);
+
+ Property::Array* result = value->GetArray();
+ DALI_TEST_CHECK(result);
+ DALI_TEST_EQUALS(result->Count(), 2, TEST_LOCATION);
+
+ bool tryAgain = false;
+ for(auto& framePair : retrialFrames)
+ {
+ if(result->GetElementAt(0).Get<int>() == framePair.first && result->GetElementAt(1).Get<int>() == framePair.second)
+ {
+ tryAgain = true;
+ break;
+ }
+ }
+ if(tryAgain)
+ {
+ tet_printf("Retry to get value again! [%d]\n", tryCount);
+ // Dummy sleep 1 second.
+ Test::WaitForEventThreadTrigger(1, 1);
+ continue;
+ }
+
+ DALI_TEST_EQUALS(result->GetElementAt(0).Get<int>(), expectStartFrame, TEST_LOCATION);
+ DALI_TEST_EQUALS(result->GetElementAt(1).Get<int>(), expectEndFrame, TEST_LOCATION);
+ break;
+ }
+ DALI_TEST_CHECK(tryCount <= tryCountMax);
+ };
+
+ tet_printf("Pause lottie first.\n");
+
+ Property::Map attributes;
+ DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::PAUSE, attributes);
+
+ application.SendNotification();
+ application.Render(16);
+
+ do
+ {
+ resultMap = dummyControl.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+
+ value = resultMap.Find(DevelImageVisual::Property::TOTAL_FRAME_NUMBER, Property::INTEGER);
+ DALI_TEST_CHECK(value);
+ totalFrameCount = value->Get<int>();
+ } while(totalFrameCount == 0);
+
+ // Ensure that vector data sended well.
+ CheckAndRetryPlayRange(startFrame, endFrame, {{0, 0}, {0, totalFrameCount - 1}});
+
+ resultMap = dummyControl.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+
+ value = resultMap.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER, Property::INTEGER);
+ DALI_TEST_CHECK(value);
+ DALI_TEST_EQUALS(value->Get<int>(), startFrame, TEST_LOCATION);
+
+ tet_printf("Now logically, range : [%d~%d], current : %d\n", startFrame, endFrame, startFrame);
+
+ int changedStartFrame1 = startFrame + 2;
+ int changedEndFrame1 = endFrame + 2;
+
+ playRange.Clear();
+ playRange.Add(changedStartFrame1);
+ playRange.Add(changedEndFrame1);
+
+ tet_printf("Change play range\n");
+ attributes.Add(DevelImageVisual::Property::PLAY_RANGE, playRange);
+ DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+ tet_printf("Jump to changedEndFrame!\n");
+ DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::JUMP_TO, changedEndFrame1);
+
+ attributes.Clear();
+ tet_infoline("Flush Action!");
+ tet_printf("Now logically, range : [%d~%d], current : %d\n", changedStartFrame1, changedEndFrame1, changedEndFrame1);
+ DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelAnimatedVectorImageVisual::Action::FLUSH, attributes);
+
+ int changedStartFrame2 = startFrame + 1;
+ int changedEndFrame2 = endFrame + 1;
+
+ playRange.Clear();
+ playRange.Add(changedStartFrame2);
+ playRange.Add(changedEndFrame2);
+
+ tet_printf("Change play range again\n");
+ tet_printf("Now logically, range : [%d~%d], current : %d\n", changedStartFrame2, changedEndFrame2, changedEndFrame2);
+ attributes.Add(DevelImageVisual::Property::PLAY_RANGE, playRange);
+ DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, Dali::Toolkit::DevelVisual::Action::UPDATE_PROPERTY, attributes);
+
+ application.SendNotification();
+ application.Render(16);
+
+ // Ensure that vector data sended well.
+ CheckAndRetryPlayRange(changedStartFrame2, changedEndFrame2, {{changedStartFrame1, changedEndFrame1}, {startFrame, endFrame}});
+
+ resultMap = dummyControl.GetProperty<Property::Map>(DummyControl::Property::TEST_VISUAL);
+
+ tet_printf("Test whether current frame number changed well. If Flush not works, current frame become startFrame.");
+ value = resultMap.Find(DevelImageVisual::Property::CURRENT_FRAME_NUMBER, Property::INTEGER);
+ DALI_TEST_CHECK(value);
+ DALI_TEST_EQUALS(value->Get<int>(), changedEndFrame2, TEST_LOCATION);
+
+ dummyControl.Unparent();
+
+ END_TEST;
+}
\ No newline at end of file
const char* TEST_BROKEN_IMAGE_01 = TEST_RESOURCE_DIR "/button-up.9.png";
const char* TEST_BROKEN_IMAGE_02 = TEST_RESOURCE_DIR "/heartsframe.9.png";
+const char* TEST_INVALID_NPATCH_FILE_NAME_01 = "invalid1.9.png";
+
// resolution: 34*34, pixel format: RGBA8888
static const char* gImage_34_RGBA = TEST_RESOURCE_DIR "/icon-edit.png";
// resolution: 600*600, pixel format: RGB888
}
}
+void OnResourceReadySignal08(Control control)
+{
+ gResourceReadySignalCounter++;
+
+ if(gImageView1)
+ {
+ gImageView1.Unparent();
+ gImageView1.Reset();
+ }
+ if(gImageView2)
+ {
+ gImageView2.Unparent();
+ gImageView2.Reset();
+ }
+}
+
+std::size_t gResourceReadySignal09Emitted = false;
+
+void OnResourceReadySignal09(Control control)
+{
+ gResourceReadySignalCounter++;
+
+ if(gImageView1 && !gResourceReadySignal09Emitted)
+ {
+ gResourceReadySignal09Emitted = true;
+ gImageView1.ResourceReadySignal().Disconnect(&OnResourceReadySignal09);
+
+ // Try to load cached invalid nine patch image. It will request load now.
+ gImageView1.SetImage(TEST_INVALID_NPATCH_FILE_NAME_01);
+ gImageView2.SetImage(TEST_INVALID_NPATCH_FILE_NAME_01);
+
+ // Destroy all visuals immediatly.
+ gImageView1.Unparent();
+ gImageView1.Reset();
+ gImageView2.Unparent();
+ gImageView2.Reset();
+ }
+}
+
} // namespace
int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
END_TEST;
}
+int UtcDaliImageViewSetImageOnResourceReadySignal08(void)
+{
+ tet_infoline("Test remove npatch images during resource ready");
+
+ ToolkitTestApplication application;
+
+ gResourceReadySignalCounter = 0;
+
+ Property::Map map;
+ map[Toolkit::ImageVisual::Property::URL] = TEST_BROKEN_IMAGE_M;
+
+ // Clear image view for clear test
+
+ if(gImageView1)
+ {
+ gImageView1.Reset();
+ }
+ if(gImageView2)
+ {
+ gImageView2.Reset();
+ }
+
+ // Case 1 : Remove all images during resource ready.
+ try
+ {
+ gImageView1 = ImageView::New();
+ gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+ gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal08);
+ application.GetScene().Add(gImageView1);
+
+ application.SendNotification();
+ application.Render();
+
+ // Load gImageView1
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK(true);
+ }
+ catch(...)
+ {
+ // Exception should not happened
+ DALI_TEST_CHECK(false);
+ }
+
+ // Clear cache.
+ application.SendNotification();
+ application.Render();
+
+ gResourceReadySignalCounter = 0;
+
+ // Case 2 : Remove all images when we use cached resource.
+ try
+ {
+ gImageView1 = ImageView::New();
+ gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+ gImageView1.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+ application.GetScene().Add(gImageView1);
+
+ application.SendNotification();
+ application.Render();
+
+ // Load gImageView1
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ DALI_TEST_EQUALS(gResourceReadySignalCounter, 1, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ gImageView2 = ImageView::New();
+ gImageView2.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+ gImageView2.ResourceReadySignal().Connect(&OnResourceReadySignal08);
+ application.GetScene().Add(gImageView2);
+ DALI_TEST_EQUALS(gResourceReadySignalCounter, 2, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK(true);
+ }
+ catch(...)
+ {
+ // Exception should not happened
+ DALI_TEST_CHECK(false);
+ }
+ gResourceReadySignalCounter = 0;
+
+ // Clear image view for clear test
+
+ if(gImageView1)
+ {
+ gImageView1.Reset();
+ }
+ if(gImageView2)
+ {
+ gImageView2.Reset();
+ }
+
+ END_TEST;
+}
+
+int UtcDaliImageViewSetImageOnResourceReadySignal09(void)
+{
+ tet_infoline("Test load invalid npatch images during invalid resource ready");
+
+ ToolkitTestApplication application;
+
+ gResourceReadySignalCounter = 0;
+
+ Property::Map map;
+ map[Toolkit::ImageVisual::Property::URL] = TEST_INVALID_NPATCH_FILE_NAME_01;
+
+ // Clear image view for clear test
+
+ if(gImageView1)
+ {
+ gImageView1.Reset();
+ }
+ if(gImageView2)
+ {
+ gImageView2.Reset();
+ }
+ if(gImageView3)
+ {
+ gImageView3.Reset();
+ }
+
+ // Dummy view with npatch image
+ ImageView dummyView = ImageView::New(TEST_BROKEN_IMAGE_M);
+ application.GetScene().Add(dummyView);
+
+ application.SendNotification();
+ application.Render();
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+ application.SendNotification();
+ application.Render();
+
+ // Case 1 : Reload images during resource ready.
+ try
+ {
+ gResourceReadySignal09Emitted = false;
+
+ gImageView1 = ImageView::New();
+ gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+ gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal09);
+ application.GetScene().Add(gImageView1);
+
+ gImageView2 = ImageView::New();
+ application.GetScene().Add(gImageView2);
+
+ // Load TEST_INVALID_NPATCH_FILE_NAME_01
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ // Load TEST_INVALID_NPATCH_FILE_NAME_01 one more times.
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK(true);
+ }
+ catch(...)
+ {
+ // Exception should not happened
+ DALI_TEST_CHECK(false);
+ }
+
+ // Clear cache.
+ application.SendNotification();
+ application.Render();
+
+ gResourceReadySignalCounter = 0;
+
+ // Case 2 : Remove all images when we use cached resource.
+ try
+ {
+ gResourceReadySignal09Emitted = false;
+
+ gImageView3 = ImageView::New();
+ gImageView3.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+ gImageView3.ResourceReadySignal().Connect(&OnSimpleResourceReadySignal);
+ application.GetScene().Add(gImageView3);
+
+ gImageView2 = ImageView::New();
+ application.GetScene().Add(gImageView2);
+
+ // Load gImageView2
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ gImageView1 = ImageView::New();
+ gImageView1.SetProperty(Toolkit::ImageView::Property::IMAGE, map);
+ gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal09);
+ application.GetScene().Add(gImageView1);
+
+ // Load gImageView1
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ // Load TEST_INVALID_NPATCH_FILE_NAME_01 one more times.
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_CHECK(true);
+ }
+ catch(...)
+ {
+ // Exception should not happened
+ DALI_TEST_CHECK(false);
+ }
+ gResourceReadySignalCounter = 0;
+
+ // Clear image view for clear test
+
+ if(gImageView1)
+ {
+ gImageView1.Reset();
+ }
+ if(gImageView2)
+ {
+ gImageView2.Reset();
+ }
+ if(gImageView3)
+ {
+ gImageView3.Reset();
+ }
+
+ END_TEST;
+}
+
int UtcDaliImageViewUseSameUrlWithAnimatedImageVisual(void)
{
tet_infoline("Test multiple views with same image in animated image visual");
{
imageView[index].Unparent();
}
+
+ // Ensure remove npatch cache if required.
+ application.SendNotification();
+ application.Render();
+
imageView[index] = ImageView::New(nPatchImageUrl);
imageView[index].SetProperty(Actor::Property::SIZE, Vector2(100.0f, 200.0f));
application.GetScene().Add(imageView[index]);
dali2-*-config.cmake
libdali2-scene3d.so*
dali-shader-generator
+build.ninja
+rules.ninja
+.ninja_deps
+.ninja_log
set(prefix ${CMAKE_INSTALL_PREFIX})
-set(repo_root_dir "${CMAKE_CURRENT_LIST_DIR}/../../../")
+set(repo_root_dir "${CMAKE_CURRENT_LIST_DIR}/../../..")
set(physics_dir "${repo_root_dir}/dali-physics")
option(ENABLE_PKG_CONFIGURE "Use pkgconfig" ON)
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
+set(physics_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 "2D sources: ${physics2d_src_files}")
+MESSAGE(STATUS "3D sources: ${physics3d_src_files}")
+
+ADD_LIBRARY("${name}-2d" SHARED ${physics2d_src_files} )
+TARGET_LINK_LIBRARIES("${name}-2d" ${DALICORE_LDFLAGS}
+ dali2-toolkit
+ chipmunk
+ ${COVERAGE})
+TARGET_COMPILE_OPTIONS("${name}-2d" PUBLIC "-I${repo_root_dir}/dali-physics/third-party/chipmunk2d/include")
+
+ADD_LIBRARY("${name}-3d" SHARED ${physics3d_src_files} )
+TARGET_LINK_LIBRARIES("${name}-3d" ${DALICORE_LDFLAGS}
+ dali2-toolkit
+ bullet3
+ ${COVERAGE})
+TARGET_COMPILE_OPTIONS("${name}-3d" PUBLIC "-I${repo_root_dir}/dali-physics/third-party/bullet3/src")
+
+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}-2d
+ PROPERTIES
+ VERSION ${DALI_PHYSICS_VERSION}
+ SOVERSION ${${name}_VERSION_MAJOR}
+ CLEAN_DIRECT_OUPUT 1
)
-endif()
-if( INSTALL_CMAKE_MODULES )
+ SET_TARGET_PROPERTIES( ${name}-3d
+ PROPERTIES
+ VERSION ${DALI_PHYSICS_VERSION}
+ SOVERSION ${${name}_VERSION_MAJOR}
+ CLEAN_DIRECT_OUPUT 1
+ )
- 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}-2d
+ EXPORT ${name}-2d-targets
+ LIBRARY DESTINATION ${LIB_DIR}
+ ARCHIVE DESTINATION ${LIB_DIR}
+ RUNTIME DESTINATION ${BIN_DIR}
+ )
+
+ 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}-2d-targets
+ NAMESPACE ${name}-2d::
+ FILE ${name}-2d-targets.cmake
+ DESTINATION share/${name}-2d
+ )
+
+ 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}-2d DESTINATION ${LIB_DIR} )
+ INSTALL( TARGETS ${name}-3d DESTINATION ${LIB_DIR} )
+
+ENDIF()
+# Install headers
+install( FILES ${physics_public_api_header_files}
+ DESTINATION "${INCLUDE_DIR}/dali-physics/public-api"
+ )
+install( FILES ${physics_dir}/dali-physics.h
+ DESTINATION "${INCLUDE_DIR}/dali-physics"
+ )
Description: Dali Physics 2D library
Version: ${apiversion}
Requires: chipmunk2d
-Libs: -L${libdir}
+Libs: -L${libdir} -ldali2-physics-2d
Cflags: -I${includedir}
Description: Dali Physics 3D library
Version: ${apiversion}
Requires: bullet3
-Libs: -L${libdir}
+Libs: -L${libdir} -ldali2-physics-3d
Cflags: -I${includedir}
--- /dev/null
+# 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.
+
--- /dev/null
+#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>
--- /dev/null
+/*
+ * 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 <btBulletDynamicsCommon.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)
+{
+}
+
+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
--- /dev/null
+/*
+ * 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/bullet-impl/bullet-physics-adaptor-impl.h>
+
+// External Headers
+#include <btBulletDynamicsCommon.h>
+#include <utility>
+
+// Internal Headers
+#include <dali-physics/internal/bullet-impl/bullet-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
+
+} // namespace
+
+namespace Dali::Toolkit::Physics::Internal
+{
+PhysicsAdaptorPtr CreateNewPhysicsAdaptor(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+ PhysicsAdaptorPtr adaptor(new BulletPhysicsAdaptor());
+ adaptor->Initialize(transform, worldSize);
+ return adaptor;
+}
+
+BulletPhysicsAdaptor::BulletPhysicsAdaptor()
+: PhysicsAdaptor()
+{
+}
+
+BulletPhysicsAdaptor::~BulletPhysicsAdaptor()
+{
+ // @todo Ensure physics bodies don't leak
+}
+
+void BulletPhysicsAdaptor::OnInitialize(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+ mTransform = transform;
+ mInverseTransform = transform;
+ mInverseTransform.Invert();
+ mSize = worldSize;
+
+ mPhysicsWorld = BulletPhysicsWorld::New(mRootActor,
+ Dali::MakeCallback(mSlotDelegate.GetSlot(),
+ &PhysicsAdaptor::OnUpdateActors));
+}
+
+Layer BulletPhysicsAdaptor::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::NAME] = "PhysicsDebugLayer";
+ 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 BulletPhysicsAdaptor::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);
+ }
+}
+
+PhysicsActorPtr BulletPhysicsAdaptor::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);
+}
+
+void BulletPhysicsAdaptor::RemoveActorBody(PhysicsActor& physicsActor)
+{
+ auto iter = mPhysicsActors.find(physicsActor.GetId());
+ if(iter != mPhysicsActors.end())
+ {
+ mPhysicsActors.erase(iter);
+ }
+ Dali::Actor actor = mRootActor.FindChildById(physicsActor.GetId());
+ if(actor)
+ {
+ actor.Unparent();
+ }
+
+ auto body = physicsActor.GetBody();
+ btRigidBody* btBody = body.Get<btRigidBody*>();
+ if(btBody)
+ {
+ btBody->setUserIndex(-1);
+ }
+}
+
+PhysicsActorPtr BulletPhysicsAdaptor::GetPhysicsActor(Dali::Any body) const
+{
+ 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;
+}
+
+// Convert a position from root actor local space to physics space
+Vector3 BulletPhysicsAdaptor::TranslateToPhysicsSpace(Vector3 vector) const
+{
+ 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 BulletPhysicsAdaptor::TranslateFromPhysicsSpace(Vector3 vector) const
+{
+ Vector4 position = mInverseTransform * Vector4(vector.x, vector.y, vector.z, 1.0f);
+ return Vector3(position);
+}
+
+Quaternion BulletPhysicsAdaptor::TranslateToPhysicsSpace(Quaternion orientation) const
+{
+ // 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 BulletPhysicsAdaptor::TranslateFromPhysicsSpace(Quaternion orientation) const
+{
+ // Mirroring conversion is identical in both transforms
+ return TranslateToPhysicsSpace(orientation);
+}
+
+// Convert a vector from dali space to physics space
+Vector3 BulletPhysicsAdaptor::ConvertVectorToPhysicsSpace(Vector3 vector) const
+{
+ 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 BulletPhysicsAdaptor::ConvertVectorFromPhysicsSpace(Vector3 vector) const
+{
+ Vector4 otherVector(mInverseTransform * Vector4(vector.x, vector.y, vector.z, 0.0f));
+ return Vector3(otherVector);
+}
+
+void BulletPhysicsAdaptor::BuildPickingRay(Vector3 origin, Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld)
+{
+ rayFromWorld = TranslateToPhysicsSpace(origin);
+ rayToWorld = TranslateToPhysicsSpace(origin + direction * 10000.0f);
+}
+
+Vector3 BulletPhysicsAdaptor::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;
+ return (rayFromWorld + dir);
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+#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 adaptoried.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+
+// INTERNAL INCLUDES
+#include <dali-physics/internal/bullet-impl/bullet-physics-debug-renderer.h>
+#include <dali-physics/internal/physics-adaptor-impl.h>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class PhysicsDebugRenderer;
+
+class BulletPhysicsAdaptor : public PhysicsAdaptor
+{
+public:
+ BulletPhysicsAdaptor();
+
+ /**
+ * A reference counted object may only be deleted by calling Unreference()
+ */
+ ~BulletPhysicsAdaptor() override;
+
+ // Remove copy constructor and copy assignment
+ BulletPhysicsAdaptor(const PhysicsAdaptor& handle) = delete;
+ BulletPhysicsAdaptor& 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);
+ void OnInitialize(const Dali::Matrix& transform, Uint16Pair size) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::CreateDebugLayer
+ */
+ Dali::Layer CreateDebugLayer(Dali::Window window) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetTransformAndSize
+ */
+ void SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair size) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+ */
+ Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+ */
+ Dali::Quaternion TranslateToPhysicsSpace(Dali::Quaternion rotation) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+ */
+ Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+ */
+ Dali::Quaternion TranslateFromPhysicsSpace(Quaternion rotation) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorToPhysicsSpace
+ */
+ Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorFromPhysicsSpace
+ */
+ Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::AddActorBody
+ */
+ PhysicsActorPtr AddActorBody(Dali::Actor actor, Dali::Any body) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::RemoveActorBody
+ */
+ void RemoveActorBody(PhysicsActor& physicsActor) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetPhysicsActor
+ */
+ PhysicsActorPtr GetPhysicsActor(Dali::Any body) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::BuildPickingRay
+ */
+ void BuildPickingRay(Dali::Vector3 origin, Dali::Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ProjectPoint
+ */
+ Dali::Vector3 ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance) override;
+
+private:
+ Actor mDebugActor;
+ std::unique_ptr<PhysicsDebugRenderer> mDebugRenderer;
+};
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+/*
+ * 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/bullet-impl/bullet-physics-debug-renderer.h>
+
+// External Includes
+#include <dali/dali.h>
+
+// Internal Includes
+#include <dali-physics/internal/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),
+ mVertexLocation(-1),
+ mVertexColourLocation(-1),
+ mProjectionLocation(-1),
+ mModelViewLocation(-1),
+ mBufferId(0u),
+ mProgramId(0u)
+{
+}
+
+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
--- /dev/null
+#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.
+ */
+
+// External includes
+#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
--- /dev/null
+/*
+ * 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/bullet-impl/bullet-physics-world-impl.h>
+
+// External Headers
+#include <BulletCollision/NarrowPhaseCollision/btRaycastCallback.h>
+#include <btBulletCollisionCommon.h>
+#include <memory>
+
+// 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
+{
+std::unique_ptr<PhysicsWorld> BulletPhysicsWorld::New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+{
+ std::unique_ptr<BulletPhysicsWorld> world = std::make_unique<BulletPhysicsWorld>(rootActor, updateCallback);
+ world->Initialize();
+ return world;
+}
+
+BulletPhysicsWorld::BulletPhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+: PhysicsWorld(rootActor, updateCallback)
+{
+}
+
+void BulletPhysicsWorld::OnInitialize(/*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);
+}
+
+BulletPhysicsWorld::~BulletPhysicsWorld()
+{
+ 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::Any BulletPhysicsWorld::GetNative()
+{
+ return mDynamicsWorld;
+}
+
+void BulletPhysicsWorld::Integrate(float timestep)
+{
+ if(mPhysicsIntegrateState == Physics::PhysicsAdaptor::IntegrationState::ON)
+ {
+ mDynamicsWorld->stepSimulation(timestep);
+ }
+ if(mDynamicsWorld->getDebugDrawer() && mPhysicsDebugState == Physics::PhysicsAdaptor::DebugState::ON)
+ {
+ mDynamicsWorld->debugDrawWorld();
+ }
+}
+
+inline btVector3 ConvertVector(Dali::Vector3 vector)
+{
+ return btVector3(vector.x, vector.y, vector.z);
+}
+
+Dali::Any BulletPhysicsWorld::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;
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+#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/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/internal/physics-world-impl.h>
+#include <dali-physics/public-api/physics-adaptor.h>
+
+#include <btBulletDynamicsCommon.h>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class PhysicsWorld;
+class FrameCallback;
+
+class BulletPhysicsWorld : public PhysicsWorld
+{
+public:
+ static std::unique_ptr<PhysicsWorld> New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+
+ BulletPhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+ ~BulletPhysicsWorld();
+
+ void OnInitialize(/*void* dynamicsWorld*/) override;
+
+ Dali::Any GetNative() override;
+
+ /**
+ * 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) override;
+
+ void Integrate(float timestep) override;
+
+private:
+ btDiscreteDynamicsWorld* mDynamicsWorld{nullptr};
+ btCollisionDispatcher* mDispatcher{nullptr};
+ btDefaultCollisionConfiguration* mCollisionConfiguration{nullptr};
+ btBroadphaseInterface* mBroadphase{nullptr};
+ btSequentialImpulseConstraintSolver* mSolver{nullptr};
+};
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+/*
+ * 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>
+
+#include <chipmunk/chipmunk.h>
+
+namespace
+{
+inline cpVect fromVec3(Dali::Vector3 vec3)
+{
+ return cpv(vec3.x, vec3.y);
+}
+
+inline Dali::Vector3 toVec3(cpVect vec)
+{
+ return Dali::Vector3(vec.x, vec.y, 0.0f);
+}
+
+} //Anonymous namespace
+
+namespace Dali::Toolkit::Physics::Internal
+{
+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)
+{
+ cpBodySetUserData(mBody.Get<cpBody*>(), this);
+
+ // RegisterObject?
+}
+
+void PhysicsActor::AsyncSetPhysicsPosition(Dali::Vector3 actorPosition)
+{
+ // Queue task
+ cpBody* body = mBody.Get<cpBody*>();
+ cpVect pos = fromVec3(mAdaptor.TranslateToPhysicsSpace(actorPosition));
+ mAdaptor.Queue([body, pos] { cpBodySetPosition(body, pos); });
+}
+
+void PhysicsActor::AsyncSetPhysicsRotation(Dali::Quaternion rotation)
+{
+ // Queue task
+ cpBody* body = mBody.Get<cpBody*>();
+ auto q = mAdaptor.TranslateToPhysicsSpace(rotation);
+ Vector3 axis;
+ Radian angle;
+ q.ToAxisAngle(axis, angle);
+ mAdaptor.Queue([body, angle]() { cpBodySetAngle(body, angle); });
+}
+
+Dali::Vector3 PhysicsActor::GetPhysicsPosition() const
+{
+ cpBody* body = mBody.Get<cpBody*>();
+ return toVec3(cpBodyGetPosition(body));
+}
+
+Dali::Quaternion PhysicsActor::GetPhysicsRotation() const
+{
+ cpBody* body = mBody.Get<cpBody*>();
+ cpFloat angle = cpBodyGetAngle(body);
+ return Quaternion(Radian(angle), Vector3::ZAXIS);
+}
+
+Dali::Vector3 PhysicsActor::GetActorPosition() const
+{
+ cpBody* body = mBody.Get<cpBody*>();
+ cpVect cpPosition = cpBodyGetPosition(body);
+ return mAdaptor.TranslateFromPhysicsSpace(Vector3(cpPosition.x, cpPosition.y, 0.0f));
+}
+
+Dali::Quaternion PhysicsActor::GetActorRotation() const
+{
+ cpBody* body = mBody.Get<cpBody*>();
+ cpFloat angle = cpBodyGetAngle(body);
+ return mAdaptor.TranslateFromPhysicsSpace(Quaternion(Radian(angle), Vector3::ZAXIS));
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+/*
+ * 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/chipmunk-impl/chipmunk-physics-adaptor-impl.h>
+
+// External Headers
+#include <utility>
+
+// Internal Headers
+#include <dali-physics/internal/chipmunk-impl/chipmunk-physics-world-impl.h>
+#include <dali/dali.h>
+#include <dali/integration-api/debug.h>
+
+namespace
+{
+#if defined(DEBUG_ENABLED)
+Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, false, "LOG_PHYSICS");
+#endif
+
+inline cpVect ConvertVector(Dali::Vector3 vector)
+{
+ return cpv(vector.x, vector.y);
+}
+
+} // namespace
+
+namespace Dali::Toolkit::Physics::Internal
+{
+PhysicsAdaptorPtr CreateNewPhysicsAdaptor(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+ PhysicsAdaptorPtr adaptor(new ChipmunkPhysicsAdaptor());
+ adaptor->Initialize(transform, worldSize);
+ return adaptor;
+}
+
+ChipmunkPhysicsAdaptor::ChipmunkPhysicsAdaptor()
+: PhysicsAdaptor()
+{
+}
+
+ChipmunkPhysicsAdaptor::~ChipmunkPhysicsAdaptor()
+{
+ // @todo Ensure physics bodies don't leak
+}
+
+void ChipmunkPhysicsAdaptor::OnInitialize(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+ mTransform = transform;
+ mInverseTransform = transform;
+ mInverseTransform.Invert();
+ mSize = worldSize;
+
+ mPhysicsWorld = ChipmunkPhysicsWorld::New(mRootActor,
+ Dali::MakeCallback(mSlotDelegate.GetSlot(),
+ &PhysicsAdaptor::OnUpdateActors));
+}
+
+Layer ChipmunkPhysicsAdaptor::CreateDebugLayer(Dali::Window window)
+{
+ return Layer();
+}
+
+void ChipmunkPhysicsAdaptor::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);
+}
+
+PhysicsActorPtr ChipmunkPhysicsAdaptor::AddActorBody(Dali::Actor actor, Dali::Any body)
+{
+ uint32_t id = static_cast<uint32_t>(actor.GetProperty<int>(Actor::Property::ID));
+ cpBody* cBody = body.Get<cpBody*>();
+ cpBodySetUserData(cBody, this);
+
+ 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);
+}
+
+void ChipmunkPhysicsAdaptor::RemoveActorBody(PhysicsActor& physicsActor)
+{
+ auto iter = mPhysicsActors.find(physicsActor.GetId());
+ if(iter != mPhysicsActors.end())
+ {
+ mPhysicsActors.erase(iter);
+ }
+ Dali::Actor actor = mRootActor.FindChildById(physicsActor.GetId());
+ if(actor)
+ {
+ actor.Unparent();
+ }
+ auto body = physicsActor.GetBody();
+ cpBody* cBody = body.Get<cpBody*>();
+ if(cBody)
+ {
+ cpBodySetUserData(cBody, nullptr);
+ }
+}
+
+PhysicsActorPtr ChipmunkPhysicsAdaptor::GetPhysicsActor(Dali::Any body) const
+{
+ cpBody* cBody = body.Get<cpBody*>();
+ if(cBody)
+ {
+ return reinterpret_cast<PhysicsActor*>(cpBodyGetUserData(cBody));
+ }
+ DALI_LOG_ERROR("Body not found in physics actors");
+ return nullptr;
+}
+
+// Convert a position from root actor local space to physics space
+Vector3 ChipmunkPhysicsAdaptor::TranslateToPhysicsSpace(Vector3 vector) const
+{
+ 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 ChipmunkPhysicsAdaptor::TranslateFromPhysicsSpace(Vector3 vector) const
+{
+ Vector4 position = mInverseTransform * Vector4(vector.x, vector.y, vector.z, 1.0f);
+ return Vector3(position);
+}
+
+Quaternion ChipmunkPhysicsAdaptor::TranslateToPhysicsSpace(Quaternion orientation) const
+{
+ // It's complicated.
+ return orientation;
+}
+
+Quaternion ChipmunkPhysicsAdaptor::TranslateFromPhysicsSpace(Quaternion orientation) const
+{
+ // Mirroring conversion is identical in both transforms
+ return TranslateToPhysicsSpace(orientation);
+}
+
+// Convert a vector from dali space to physics space
+Vector3 ChipmunkPhysicsAdaptor::ConvertVectorToPhysicsSpace(Vector3 vector) const
+{
+ 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 ChipmunkPhysicsAdaptor::ConvertVectorFromPhysicsSpace(Vector3 vector) const
+{
+ Vector4 otherVector(mInverseTransform * Vector4(vector.x, vector.y, vector.z, 0.0f));
+ return Vector3(otherVector);
+}
+
+void ChipmunkPhysicsAdaptor::BuildPickingRay(Vector3 origin, Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld)
+{
+ rayFromWorld = TranslateToPhysicsSpace(origin);
+ rayToWorld = TranslateToPhysicsSpace(origin); // rayToWorld is identical - there's no depth
+}
+
+Vector3 ChipmunkPhysicsAdaptor::ProjectPoint(Vector3 origin, Vector3 direction, float distance)
+{
+ // Ignore direction & distance.
+ return TranslateToPhysicsSpace(origin);
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+#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 adaptoried.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// External headers
+
+// Internal headers
+#include <dali-physics/internal/physics-adaptor-impl.h>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class ChipmunkPhysicsAdaptor : public PhysicsAdaptor
+{
+public:
+ ChipmunkPhysicsAdaptor();
+
+ /**
+ * A reference counted object may only be deleted by calling Unreference()
+ */
+ ~ChipmunkPhysicsAdaptor() override;
+
+ // Remove copy constructor and copy assignment
+ ChipmunkPhysicsAdaptor(const PhysicsAdaptor& handle) = delete;
+ ChipmunkPhysicsAdaptor& operator=(const PhysicsAdaptor& handle) = delete;
+
+ /**
+ * 2nd stage initialization
+ */
+ void OnInitialize(const Dali::Matrix& transform, Uint16Pair size) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::CreateDebugLayer
+ */
+ Dali::Layer CreateDebugLayer(Dali::Window window) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetTransformAndSize
+ */
+ void SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair size) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+ */
+ Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+ */
+ Dali::Quaternion TranslateToPhysicsSpace(Dali::Quaternion rotation) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+ */
+ Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+ */
+ Dali::Quaternion TranslateFromPhysicsSpace(Quaternion rotation) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorToPhysicsSpace
+ */
+ Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorFromPhysicsSpace
+ */
+ Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::AddActorBody
+ */
+ PhysicsActorPtr AddActorBody(Dali::Actor actor, Dali::Any body) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::RemoveActorBody
+ */
+ void RemoveActorBody(PhysicsActor& physicsActor) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetPhysicsActor
+ */
+ PhysicsActorPtr GetPhysicsActor(Dali::Any body) const override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::BuildPickingRay
+ */
+ void BuildPickingRay(Dali::Vector3 origin, Dali::Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld) override;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ProjectPoint
+ */
+ Dali::Vector3 ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance) override;
+};
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+/*
+ * 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/chipmunk-impl/chipmunk-physics-world-impl.h>
+
+// External Headers
+
+// Internal Headers
+#include <dali/dali.h>
+#include <dali/devel-api/common/stage-devel.h>
+#include <dali/devel-api/update/frame-callback-interface.h>
+
+namespace
+{
+#define GRABBABLE_MASK_BIT (1u << 31)
+cpShapeFilter GRAB_FILTER = {CP_NO_GROUP, GRABBABLE_MASK_BIT, GRABBABLE_MASK_BIT};
+
+inline cpVect ConvertVector(Dali::Vector3 vector)
+{
+ return cpv(vector.x, vector.y);
+}
+
+static void ShapeFreeWrap(cpSpace* space, cpShape* shape, void* unused)
+{
+ cpSpaceRemoveShape(space, shape);
+ cpShapeFree(shape);
+}
+
+static void PostShapeFree(cpShape* shape, cpSpace* space)
+{
+ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ShapeFreeWrap, shape, NULL);
+}
+
+static void ConstraintFreeWrap(cpSpace* space, cpConstraint* constraint, void* unused)
+{
+ cpSpaceRemoveConstraint(space, constraint);
+ cpConstraintFree(constraint);
+}
+
+static void PostConstraintFree(cpConstraint* constraint, cpSpace* space)
+{
+ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ConstraintFreeWrap, constraint, NULL);
+}
+
+static void BodyFreeWrap(cpSpace* space, cpBody* body, void* unused)
+{
+ cpSpaceRemoveBody(space, body);
+ cpBodyFree(body);
+}
+
+static void PostBodyFree(cpBody* body, cpSpace* space)
+{
+ cpSpaceAddPostStepCallback(space, (cpPostStepFunc)BodyFreeWrap, body, NULL);
+}
+} // namespace
+
+namespace Dali::Toolkit::Physics::Internal
+{
+std::unique_ptr<PhysicsWorld> ChipmunkPhysicsWorld::New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+{
+ std::unique_ptr<ChipmunkPhysicsWorld> world = std::make_unique<ChipmunkPhysicsWorld>(rootActor, updateCallback);
+ world->Initialize();
+ return world;
+}
+
+ChipmunkPhysicsWorld::ChipmunkPhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+: PhysicsWorld(rootActor, updateCallback)
+{
+}
+
+void ChipmunkPhysicsWorld::OnInitialize(/*void* dynamicsWorld*/)
+{
+ // @todo Should enable developer to optionally supply their own created cpSpace.
+ mSpace = cpSpaceNew();
+ cpSpaceSetIterations(mSpace, 30);
+ cpSpaceSetSleepTimeThreshold(mSpace, 0.5f);
+ cpSpaceSetGravity(mSpace, cpv(0, -200));
+}
+
+ChipmunkPhysicsWorld::~ChipmunkPhysicsWorld()
+{
+ Dali::Mutex::ScopedLock lock(mMutex);
+ if(mSpace)
+ {
+ cpSpaceEachShape(mSpace, (cpSpaceShapeIteratorFunc)PostShapeFree, mSpace);
+ cpSpaceEachConstraint(mSpace, (cpSpaceConstraintIteratorFunc)PostConstraintFree, mSpace);
+ cpSpaceEachBody(mSpace, (cpSpaceBodyIteratorFunc)PostBodyFree, mSpace);
+ cpSpaceFree(mSpace);
+ mSpace = nullptr;
+ }
+}
+
+Dali::Any ChipmunkPhysicsWorld::GetNative()
+{
+ return mSpace;
+}
+
+void ChipmunkPhysicsWorld::Integrate(float timestep)
+{
+ if(mPhysicsIntegrateState == Physics::PhysicsAdaptor::IntegrationState::ON)
+ {
+ cpSpaceStep(mSpace, timestep);
+ }
+}
+
+Dali::Any ChipmunkPhysicsWorld::HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera)
+{
+ cpVect spacePosition = cpv(rayFromWorld.x, rayFromWorld.y);
+ cpFloat radius = 5.0f;
+ cpPointQueryInfo info = {0};
+ cpShape* shape = cpSpacePointQueryNearest(mSpace, spacePosition, radius, GRAB_FILTER, &info);
+ cpBody* hitBody{nullptr};
+
+ if(shape && cpBodyGetMass(cpShapeGetBody(shape)) < INFINITY)
+ {
+ // Use the closest point on the surface if the click is outside the shape.
+ cpVect nearest = (info.distance > 0.0f ? info.point : spacePosition);
+ hitBody = cpShapeGetBody(shape);
+ cpVect local = cpBodyWorldToLocal(hitBody, nearest);
+ localPivot.x = local.x;
+ localPivot.y = local.y;
+ localPivot.z = 0.0;
+ }
+
+ Dali::Any bodyPtr;
+ // Only set non-null ptr into bodyPtr, leave empty if null.
+ if(hitBody)
+ {
+ bodyPtr = hitBody;
+ }
+ return bodyPtr;
+}
+
+} // namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+#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/internal/physics-world-impl.h>
+
+#include <chipmunk/chipmunk.h>
+#include <memory>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class ChipmunkPhysicsWorld : public PhysicsWorld
+{
+public:
+ static std::unique_ptr<PhysicsWorld> New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+
+ ChipmunkPhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+ ~ChipmunkPhysicsWorld() override;
+
+ void OnInitialize(/*void* dynamicsWorld*/) override;
+
+ Dali::Any GetNative() override;
+
+ void Integrate(float timestep) override;
+
+ Dali::Any HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera) override;
+
+private:
+ cpSpace* mSpace{nullptr};
+};
+
+} //namespace Dali::Toolkit::Physics::Internal
--- /dev/null
+set(physics_internal_dir ${physics_dir}/internal)
+set(physics2d_internal_dir ${physics_internal_dir}/chipmunk-impl)
+set(physics3d_internal_dir ${physics_internal_dir}/bullet-impl)
+
+set(physics2d_src_files ${physics_src_files}
+ ${physics2d_internal_dir}/chipmunk-physics-actor-impl.cpp
+ ${physics2d_internal_dir}/chipmunk-physics-adaptor-impl.cpp
+ ${physics2d_internal_dir}/chipmunk-physics-world-impl.cpp
+ ${physics_internal_dir}/physics-adaptor-impl.cpp
+ ${physics_internal_dir}/physics-world-impl.cpp
+)
+
+set(physics3d_src_files ${physics_src_files}
+ ${physics3d_internal_dir}/bullet-physics-actor-impl.cpp
+ ${physics3d_internal_dir}/bullet-physics-adaptor-impl.cpp
+ ${physics3d_internal_dir}/bullet-physics-debug-renderer.cpp
+ ${physics3d_internal_dir}/bullet-physics-world-impl.cpp
+ ${physics_internal_dir}/physics-adaptor-impl.cpp
+ ${physics_internal_dir}/physics-world-impl.cpp
+)
--- /dev/null
+#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 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
--- /dev/null
+/*
+ * 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 <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
+
+} // namespace
+
+namespace Dali::Toolkit::Physics::Internal
+{
+PhysicsAdaptor::PhysicsAdaptor()
+: mSlotDelegate(this)
+{
+}
+
+PhysicsAdaptor::~PhysicsAdaptor()
+{
+}
+
+void PhysicsAdaptor::Initialize(const Dali::Matrix& transform, Uint16Pair worldSize)
+{
+ // Create an actor that can handle mouse events.
+ // @todo Enable this to be fully configured / provided
+ mRootActor = Layer::New();
+ mRootActor[Actor::Property::NAME] = "PhysicsRootLayer";
+ mRootActor[Layer::Property::BEHAVIOR] = Layer::LAYER_3D;
+ mRootActor[Layer::Property::DEPTH_TEST] = true;
+ mRootActor[Actor::Property::SIZE] = Vector2(worldSize.GetWidth(), worldSize.GetHeight());
+ mRootActor[Actor::Property::ANCHOR_POINT] = Dali::AnchorPoint::CENTER;
+ mRootActor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
+
+ // Initialize derived adaptor (and world)
+ OnInitialize(transform, worldSize);
+}
+
+void PhysicsAdaptor::SetTimestep(float timestep)
+{
+ mPhysicsWorld->SetTimestep(timestep);
+}
+
+float PhysicsAdaptor::GetTimestep() const
+{
+ return mPhysicsWorld->GetTimestep();
+}
+
+Physics::PhysicsAdaptor::ScopedPhysicsAccessorPtr PhysicsAdaptor::GetPhysicsAccessor()
+{
+ return std::unique_ptr<Physics::PhysicsAdaptor::ScopedPhysicsAccessor>(new Physics::PhysicsAdaptor::ScopedPhysicsAccessor(*mPhysicsWorld.get()));
+}
+
+void PhysicsAdaptor::SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state)
+{
+ mPhysicsWorld->SetIntegrationState(state);
+}
+
+Physics::PhysicsAdaptor::IntegrationState PhysicsAdaptor::GetIntegrationState() const
+{
+ return mPhysicsWorld->GetIntegrationState();
+}
+
+void PhysicsAdaptor::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
+{
+ mPhysicsWorld->SetDebugState(state);
+}
+
+Physics::PhysicsAdaptor::DebugState PhysicsAdaptor::GetDebugState() const
+{
+ return mPhysicsWorld->GetDebugState();
+}
+
+Dali::Actor PhysicsAdaptor::GetRootActor() const
+{
+ return mRootActor;
+}
+
+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 Dali::Toolkit::Physics::Internal
--- /dev/null
+#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-world-impl.h>
+#include <dali-physics/public-api/physics-adaptor.h>
+
+namespace Dali::Toolkit::Physics
+{
+namespace Internal
+{
+class PhysicsAdaptor;
+class PhysicsDebugRenderer;
+
+using PhysicsAdaptorPtr = IntrusivePtr<PhysicsAdaptor>;
+
+// Declaration of factory function, implemented by derived class
+PhysicsAdaptorPtr CreateNewPhysicsAdaptor(const Dali::Matrix& transform, Uint16Pair worldSize);
+
+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;
+
+ /**
+ * 2nd stage initialization
+ */
+ void Initialize(const Dali::Matrix& transform, Uint16Pair size);
+ virtual void OnInitialize(const Dali::Matrix& transform, Uint16Pair size) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetTimestep
+ */
+ void SetTimestep(float timestep);
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetTimestep
+ */
+ float GetTimestep() const;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetPhysicsAccessor
+ */
+ Physics::PhysicsAdaptor::ScopedPhysicsAccessorPtr GetPhysicsAccessor();
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::CreateDebugLayer
+ */
+ virtual Dali::Layer CreateDebugLayer(Dali::Window window) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+ */
+ virtual Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateToPhysicsSpace
+ */
+ virtual Dali::Quaternion TranslateToPhysicsSpace(Dali::Quaternion rotation) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+ */
+ virtual Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::TranslateFromPhysicsSpace
+ */
+ virtual Dali::Quaternion TranslateFromPhysicsSpace(Quaternion rotation) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorToPhysicsSpace
+ */
+ virtual Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ConvertVectorFromPhysicsSpace
+ */
+ virtual Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetTransformAndSize
+ */
+ virtual void SetTransformAndSize(const Dali::Matrix& transform, Uint16Pair size) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetIntegrationState
+ */
+ void SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state);
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetIntegrationState
+ */
+ Physics::PhysicsAdaptor::IntegrationState GetIntegrationState() const;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::SetDebugState
+ */
+ void SetDebugState(Physics::PhysicsAdaptor::DebugState state);
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetDebugState
+ */
+ Physics::PhysicsAdaptor::DebugState GetDebugState() const;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::AddActorBody
+ */
+ virtual PhysicsActorPtr AddActorBody(Dali::Actor actor, Dali::Any body) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::RemoveActorBody
+ */
+ virtual void RemoveActorBody(PhysicsActor& physicsActor) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetPhysicsActor
+ */
+ virtual PhysicsActorPtr GetPhysicsActor(Dali::Any body) const = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::GetRootActor
+ */
+ Dali::Actor GetRootActor() const;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::BuildPickingRay
+ */
+ virtual void BuildPickingRay(Dali::Vector3 origin, Dali::Vector3 direction, Dali::Vector3& rayFromWorld, Dali::Vector3& rayToWorld) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::ProjectPoint
+ */
+ virtual Dali::Vector3 ProjectPoint(Dali::Vector3 origin, Dali::Vector3 direction, float distance) = 0;
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::Queue
+ */
+ void Queue(std::function<void(void)> function);
+
+ /**
+ * @copydoc Dali::Toolkit::Physics::PhysicsAdaptor::Queue
+ */
+ void CreateSyncPoint();
+
+ /**
+ * Handle the update of all of the known bound actors
+ */
+ void OnUpdateActors(Dali::UpdateProxy* updateProxy);
+
+protected:
+ std::unique_ptr<PhysicsWorld> mPhysicsWorld;
+ std::unordered_map<uint32_t, PhysicsActorPtr> mPhysicsActors;
+ Dali::Actor mRootActor;
+
+ Dali::Matrix mTransform;
+ Dali::Matrix mInverseTransform;
+ Dali::Uint16Pair mSize;
+
+ 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
--- /dev/null
+/*
+ * 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
+
+// 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;
+};
+
+PhysicsWorld::PhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
+: mUpdateCallback(updateCallback),
+ mRootActor(rootActor)
+{
+}
+
+void PhysicsWorld::Initialize()
+{
+ // Call derived class's initializer
+ OnInitialize();
+
+ // 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!
+}
+
+PhysicsWorld::~PhysicsWorld()
+{
+ // Derived class's destructor should clean down physics objects under mutex lock
+ // On completion, can remove the callback.
+
+ Dali::DevelStage::RemoveFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
+}
+
+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::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);
+}
+
+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
--- /dev/null
+#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 <functional>
+#include <queue>
+
+namespace Dali::Toolkit::Physics::Internal
+{
+class PhysicsWorld;
+class FrameCallback;
+
+/**
+ * Abstract class that handles the update frame callback, queuing and calling
+ * functions before the integration step ; calling the integration step,
+ * and owning the mutex for the update callback.
+ *
+ * Implementing classes should also hold the physics world.
+ */
+class PhysicsWorld
+{
+public:
+ /**
+ * Create a new physics world.
+ */
+ static std::unique_ptr<PhysicsWorld> New(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+
+ /**
+ * Constructor which takes the root actor and a callback from the PhysicsAdaptor
+ * @param[in] rootActor The root actor that physics actors will be added to
+ * @param[in] updateCallback A callback from the PhysicsAdaptor which updates the physics actors after the integration step
+ */
+ PhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback);
+
+ /**
+ * Virtual destructor.
+ * Note, removes the frame callback.
+ */
+ virtual ~PhysicsWorld();
+
+ /**
+ * Initialize derived classes and creates the frame callback
+ */
+ void Initialize();
+
+ /**
+ * Initialize the derived class
+ */
+ virtual void OnInitialize() = 0;
+
+ /**
+ * Get the native physics world / space.
+ * @return A pointer to the physics world / space
+ */
+ virtual Dali::Any GetNative() = 0;
+
+ /**
+ * 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.
+ */
+ virtual Dali::Any HitTest(Dali::Vector3 rayFromWorld, Dali::Vector3 rayToWorld, Dali::Vector3& localPivot, float& distanceFromCamera) = 0;
+
+ /**
+ * @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);
+
+protected:
+ virtual void Integrate(float timestep) = 0;
+
+protected:
+ Dali::Mutex mMutex;
+ std::queue<std::function<void(void)>> commandQueue;
+ Dali::UpdateProxy::NotifySyncPoint mNotifySyncPoint{Dali::UpdateProxy::INVALID_SYNC};
+ 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
--- /dev/null
+set(physics_public_api_dir "${physics_dir}/public-api")
+
+set(physics_src_files ${physics_src_files}
+ ${physics_public_api_dir}/physics-actor.cpp
+ ${physics_public_api_dir}/physics-adaptor.cpp
+ ${physics_public_api_dir}/scoped-physics-accessor.cpp
+)
+
+set(physics_public_api_header_files
+ ${physics_public_api_dir}/physics-actor.h
+ ${physics_public_api_dir}/physics-adaptor.h
+)
--- /dev/null
+/*
+ * 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
--- /dev/null
+#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:
+ /**
+ * @brief Constructor
+ *
+ * @SINCE_2_2.43
+ */
+ PhysicsActor();
+
+ /**
+ * @brief Destructor.
+ *
+ * @SINCE_2_2.43
+ * This is non-virtual since derived Handle types must not contain data or virtual methods
+ */
+ ~PhysicsActor();
+
+ /**
+ * @brief Copy Constructor.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle The handle to copy
+ * @note This creates a new handle, but does not create a new implementation object.
+ */
+ PhysicsActor(const PhysicsActor& handle);
+
+ /**
+ * @brief Move Constructor.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle A reference to the handle to move
+ */
+ PhysicsActor(PhysicsActor&& rhs) noexcept;
+
+ /**
+ * @brief Assignment operator.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle A reference to the handle to move
+ * @return a reference to this handle
+ */
+ PhysicsActor& operator=(const PhysicsActor& handle);
+
+ /**
+ * @brief Move Assignment operator.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle A reference to the handle to move
+ * @return a reference to this handle
+ */
+ PhysicsActor& operator=(PhysicsActor&& handle) noexcept;
+
+ /**
+ * New method.
+ * @SINCE_2_2.43
+ *
+ * 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.43
+ * @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.
+ *
+ * @SINCE_2_2.43
+ */
+ uint32_t GetId() const;
+
+ /**
+ * @brief Get the actual physics body of this object.
+ *
+ * @SINCE_2_2.43
+ * 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;
+
+ /**
+ * @brief Queue a method to set the position on the associated physics body
+ * in the update thread before the next integration.
+ *
+ * @SINCE_2_2.43
+ * @param[in] actorPosition The position of the actor in DALi space
+ */
+ void AsyncSetPhysicsPosition(Dali::Vector3 actorPosition);
+
+ /**
+ * @brief Queue a method to set the rotation of the associated physics body
+ * in the update thread before the next integration.
+ *
+ * @SINCE_2_2.43
+ * @param[in] actorRotation The orientation of the actor in DALi space
+ */
+ void AsyncSetPhysicsRotation(Dali::Quaternion actorRotation);
+
+ /**
+ * @brief Get the current position of the physics body in Physics space.
+ *
+ * @SINCE_2_2.43
+ * @return the current position of the physics body in Physics space.
+ */
+ Dali::Vector3 GetPhysicsPosition() const;
+
+ /**
+ * @brief Get the current rotation of the physics body in Physics space.
+ *
+ * @SINCE_2_2.43
+ * @return the current rotation of the physics body in Physics space.
+ */
+ Dali::Quaternion GetPhysicsRotation() const;
+
+ /**
+ * @brief Get the current position of the physics body in DALi space.
+ *
+ * @SINCE_2_2.43
+ * @return the current position of the physics body in DALi space.
+ */
+ Dali::Vector3 GetActorPosition() const;
+
+ /**
+ * @brief Get the current rotation of the physics body in DALi space.
+ *
+ * @SINCE_2_2.43
+ * @return the current rotation of the physics body in DALi space.
+ */
+ Dali::Quaternion GetActorRotation() const;
+
+public: // Not intended for developer use
+ /// @cond internal
+ /**
+ * @brief This constructor is used by PhysicsActor::New() methods.
+ *
+ * @SINCE_2_2.43
+ * @param[in] impl A pointer to a newly allocated Dali resource.
+ * @note Not intended for application developers
+ */
+ explicit DALI_INTERNAL PhysicsActor(Internal::PhysicsActor* impl);
+ /// @endcond
+};
+
+} // namespace Dali::Toolkit::Physics
+
+#endif //DALI_TOOLKIT_PHYSICS_ACTOR_H
--- /dev/null
+/*
+ * 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::CreateNewPhysicsAdaptor(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() const
+{
+ 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) const
+{
+ return GetImplementation(*this).TranslateToPhysicsSpace(vector);
+}
+
+Dali::Quaternion PhysicsAdaptor::TranslateToPhysicsSpace(Dali::Quaternion rotation) const
+{
+ return GetImplementation(*this).TranslateToPhysicsSpace(rotation);
+}
+
+Dali::Vector3 PhysicsAdaptor::TranslateFromPhysicsSpace(Dali::Vector3 vector) const
+{
+ return GetImplementation(*this).TranslateFromPhysicsSpace(vector);
+}
+
+Dali::Vector3 PhysicsAdaptor::ConvertVectorToPhysicsSpace(Dali::Vector3 vector) const
+{
+ return GetImplementation(*this).ConvertVectorToPhysicsSpace(vector);
+}
+
+Dali::Vector3 PhysicsAdaptor::ConvertVectorFromPhysicsSpace(Dali::Vector3 vector) const
+{
+ 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() const
+{
+ return GetImplementation(*this).GetIntegrationState();
+}
+
+void PhysicsAdaptor::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
+{
+ GetImplementation(*this).SetDebugState(state);
+}
+
+Physics::PhysicsAdaptor::DebugState PhysicsAdaptor::GetDebugState() const
+{
+ 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());
+}
+
+void PhysicsAdaptor::RemoveActorBody(PhysicsActor physicsActor)
+{
+ GetImplementation(*this).RemoveActorBody(GetImplementation(physicsActor));
+}
+
+PhysicsActor PhysicsAdaptor::GetPhysicsActor(Dali::Any body) const
+{
+ Internal::PhysicsActorPtr physicsActor = GetImplementation(*this).GetPhysicsActor(body);
+ return PhysicsActor(physicsActor.Get());
+}
+
+Dali::Actor PhysicsAdaptor::GetRootActor() const
+{
+ 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
--- /dev/null
+#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>
+#include <functional> ///< for std::function
+
+// 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:
+ /**
+ * @brief Enumeration to turn the integration step on or off.
+ */
+ enum class IntegrationState
+ {
+ OFF,
+ ON
+ };
+
+ /**
+ * @brief Enumeration to turn the debug rendering on or off
+ */
+ enum class DebugState
+ {
+ OFF,
+ ON
+ };
+
+ /**
+ * @brief Scoped accessor to the physics world.
+ *
+ * @SINCE_2_2.43
+ * 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:
+ /**
+ * @brief Get a pointer to the native world.
+ *
+ * @SINCE_2_2.43
+ * 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();
+
+ /**
+ * @brief Hit test the physics world and return the nearest body.
+ *
+ * @SINCE_2_2.43
+ * @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;
+
+ /**
+ * @brief Destructor.
+ *
+ * On leaving scope, the mutex is unlocked and the physics integration step
+ * can resume.
+ */
+ ~ScopedPhysicsAccessor();
+
+ private:
+ /**
+ * @brief Private constructor.
+ *
+ * It is created by the physics adaptor.
+ */
+ ScopedPhysicsAccessor(Internal::PhysicsWorld& world);
+ friend Internal::PhysicsAdaptor;
+
+ struct Impl; ///< Opaque implementation structure
+ Impl* mImpl;
+ };
+
+ /**
+ * @brief Creates an uninitalized PhysicsAdaptor, this can be initialized with PhysicsAdaptor::New().
+ *
+ * @SINCE_2_2.43
+ * Calling member functions with an uninitialized PhysicsAdaptor handle is not allowed.
+ */
+ PhysicsAdaptor();
+
+ /**
+ * @brief Destructor
+ *
+ * This is non-virtual since derived Handle types must not contain data or virtual methods.
+ * @SINCE_2_2.43
+ */
+ ~PhysicsAdaptor();
+
+ /**
+ * @brief Copy Constructor.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle The handle to copy
+ * @note This creates a new handle, but does not create a new implementation object.
+ */
+ PhysicsAdaptor(const PhysicsAdaptor& handle);
+
+ /**
+ * @brief Move Constructor.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle A reference to the handle to move
+ */
+ PhysicsAdaptor(PhysicsAdaptor&& rhs) noexcept;
+
+ /**
+ * @brief Assignment operator.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle A reference to the handle to move
+ * @return a reference to this handle
+ */
+ PhysicsAdaptor& operator=(const PhysicsAdaptor& handle);
+
+ /**
+ * @brief Move Assignment operator.
+ *
+ * @SINCE_2_2.43
+ * @param[in] handle A reference to the handle to move
+ * @return a reference to this handle
+ */
+ PhysicsAdaptor& operator=(PhysicsAdaptor&& handle) noexcept;
+
+ /**
+ * @brief Initialize the physics system.
+ *
+ * @SINCE_2_2.43
+ * @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.43
+ * @param[in] handle to an object
+ * @return handle to a PhysicsAdaptor object or an uninitialized handle
+ */
+ static PhysicsAdaptor DownCast(BaseHandle handle);
+
+ /**
+ * @brief Set how long the integration should take.
+ *
+ * @SINCE_2_2.43
+ * @param[in] timestep The length of time that the physics integration should take.
+ */
+ void SetTimestep(float timestep);
+
+ /**
+ * @brief Get the current physics integration timestep.
+ *
+ * @SINCE_2_2.43
+ * @return the current physics integration timestep
+ */
+ float GetTimestep() const;
+
+ /**
+ * @brief Type to represent a pointer to a scoped accessor.
+ *
+ * @SINCE_2_2.43
+ */
+ using ScopedPhysicsAccessorPtr = std::unique_ptr<PhysicsAdaptor::ScopedPhysicsAccessor>;
+
+ /**
+ * @brief Returns an accessor to the physics world.
+ *
+ * @SINCE_2_2.43
+ * 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.
+ * @return the scoped accessor
+ */
+ ScopedPhysicsAccessorPtr GetPhysicsAccessor();
+
+ /**
+ * @brief Create a layer & debug renderer.
+ *
+ * @SINCE_2_2.43
+ * The debug renderer may utilize the debug features of the native physics
+ * engine.
+ *
+ * @param[in] window The window to draw in (requires camera)
+ * @return The debug layer
+ */
+ Dali::Layer CreateDebugLayer(Dali::Window window);
+
+ /**
+ * @brief Converts a point in RootActor local coords (e.g. gesture)
+ * into physics space coords.
+ *
+ * @SINCE_2_2.43
+ * @param vector The point to convert
+ * @return The converted point
+ */
+ Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector) const;
+
+ /**
+ * @brief Convert a rotation in DALi coordinate system into physics space.
+ *
+ * @SINCE_2_2.43
+ * @param[in] rotation The rotation to convert
+ * @return the converted rotation.
+ */
+ Dali::Quaternion TranslateToPhysicsSpace(Dali::Quaternion rotation) const;
+
+ /**
+ * @brief Converts a point in physics space coords into RootActor local coords.
+ *
+ * @SINCE_2_2.43
+ * @param vector The point to convert
+ * @return The converted point
+ */
+ Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector) const;
+
+ /**
+ * @brief Convert a rotation in physics coordinate system into DALi space.
+ *
+ * @SINCE_2_2.43
+ * @param[in] rotation The rotation to convert
+ * @return the converted rotation.
+ */
+ Dali::Quaternion TranslateFromPhysicsSpace(Dali::Quaternion rotation) const;
+
+ /**
+ * @brief Converts a vector (not a point) in DALi space into physics space.
+ *
+ * @SINCE_2_2.43
+ * @param vector The vector to convert
+ * @return The converted vector
+ */
+ Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector) const;
+
+ /**
+ * @brief Converts a vector (not a point) in physics space to DALi space.
+ *
+ * @SINCE_2_2.43
+ * @param vector The vector to convert
+ * @return The converted vector
+ */
+ Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector) const;
+
+ /**
+ * @brief Set up the transform from world space to physics space.
+ *
+ * @SINCE_2_2.43
+ * @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);
+
+ /**
+ * @brief Set the integration state.
+ *
+ * @SINCE_2_2.43
+ * If it's turned off, physics will be paused.
+ * @note This is ON by default
+ * @param[in] state the new integration state
+ */
+ void SetIntegrationState(IntegrationState state);
+
+ /**
+ * @brief Get the integration state.
+ *
+ * @SINCE_2_2.43
+ * @return the new integration state
+ */
+ IntegrationState GetIntegrationState() const;
+
+ /**
+ * @brief Set the debug state.
+ *
+ * @SINCE_2_2.43
+ * 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);
+
+ /**
+ * @brief Get the debug state.
+ *
+ * @SINCE_2_2.43
+ * @return the new debug state
+ */
+ DebugState GetDebugState() const;
+
+ /**
+ * @brief Add an actor / body pair.
+ * @pre It's expected that the client has added the body to the physics world.
+ *
+ * The adaptor does not "take ownership" of the actor or the physics body.
+ * @SINCE_2_2.43
+ * @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);
+
+ /**
+ * @brief Remove the actor / body.
+ *
+ * This will unparent the actor from the root actor and disassociate it from
+ * the body.
+ *
+ * It is the responsibility of the client to remove the body from the physics world
+ * and destroy it at an appropriate time. Create a scoped accessor to ensure
+ * that the integration step isn't being run when doing so.
+ *
+ * If the root actor is holding the last reference to the actor, it will be
+ * destroyed automatically, otherwise it is the responsibility of the client to
+ * dereference the actor.
+ *
+ * @SINCE_2_2.43
+ * @param[in] physicsActor The actor / body pair to remove.
+ */
+ void RemoveActorBody(PhysicsActor physicsActor);
+
+ /**
+ * @brief Get the physics actor associated with the given body.
+ *
+ * @SINCE_2_2.43
+ * @param[in] body The physics body
+ * @return the associated physics actor
+ */
+ PhysicsActor GetPhysicsActor(Dali::Any body) const;
+
+ /**
+ * @brief Get the root actor (which holds all the actor/body pairs).
+ *
+ * @SINCE_2_2.43
+ * @return the root actor.
+ */
+ Dali::Actor GetRootActor() const;
+
+ /**
+ * @brief Convert DALi touch point into a picking ray in the physics world.
+ *
+ * These can then be used to hit test the PhysicsWorld
+ *
+ * @SINCE_2_2.43
+ * @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);
+
+ /**
+ * @brief 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.
+ *
+ * @SINCE_2_2.43
+ * @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);
+
+ /**
+ * @brief Queue a function to be executed before the physics integration in the update thread.
+ *
+ * @SINCE_2_2.43
+ * Multiple functions can be queued up. They are executed in the update frame
+ * callback when the next sync point is seen, so CreateSyncPoint() should be called
+ * afterwards for this to be executed.
+ *
+ * @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);
+
+ /**
+ * @brief Create a sync point for queued functions.
+ *
+ * @SINCE_2_2.43
+ * Ensures that any previously queued functions are processed
+ * in the Update::FrameCallback during the same frame as other
+ * DALi properties set during this event handler invocation.
+ * For example,
+ * boxActor = Actor::New();
+ * //... Create box actor renderer ...
+ * btRigidBodyConstructionInfo info;
+ * //... set construction properties
+ * boxBody = new btRigidBody(info);
+ * auto boxPhysicsActor = physicsAdaptor.AddActorBody(boxActor, boxBody);
+ * boxActor.SetProperty(Actor::Property::VISIBLE, true);
+ * boxActor.SetProperty(Actor::Property::OPACITY, 0.5f);
+ * physicsAdaptor.Queue([boxBody](){ boxBody->activate(true);});
+ * btVector3 impulse(4, 5, 6);
+ * btVector3 position();
+ * physicsAdaptor.Queue([boxBody](){ boxBody->applyImpulse(impulse, position);});
+ * physicsAdaptor.CreateSyncPoint();
+ *
+ * Ensures that the box has both render properties and physics properties applied
+ * during the same frame.
+ */
+ void CreateSyncPoint();
+
+public: // Not intended for developer use
+ /// @cond internal
+ /**
+ * @brief This constructor is used by PhysicsAdaptor::New() methods.
+ *
+ * @SINCE_2_2.43
+ * @param[in] impl A pointer to a newly allocated Dali resource.
+ * @note Not intended for application developers
+ */
+ explicit DALI_INTERNAL PhysicsAdaptor(Internal::PhysicsAdaptor* impl);
+ /// @endcond
+};
+
+} // namespace Dali::Toolkit::Physics
+
+#endif //DALI_TOOLKIT_PHYSICS_ADAPTOR_H
--- /dev/null
+/*
+ * 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-adaptor.h>
+
+#include <dali-physics/internal/physics-adaptor-impl.h>
+#include <dali-physics/internal/physics-world-impl.h>
+
+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 Dali::Toolkit::Physics
auto& node = (*i0).second;
ShaderDefinition shaderDef;
ReadStringVector(node.GetChild("defines"), shaderDef.mDefines);
+ auto sssIter = std::find_if(shaderDef.mDefines.begin(), shaderDef.mDefines.end(), [](std::string& item)
+ { return (item == "SSS"); });
+ if(sssIter != shaderDef.mDefines.end())
+ {
+ shaderDef.mDefines.erase(sssIter);
+ }
// Read shader hints. Possible values are:
// Don't define for No hints.
materialDef.mFlags |= semantic;
}
- if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
- {
- ToUnixFileSeparators(texturePath);
-
- const auto semantic = MaterialDefinition::SUBSURFACE;
- materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
- materialDef.mFlags |= semantic;
- }
+/// @TODO : Some dli shader don't implement this subsurfaceMp usage.
+/// To make visual test pass, Skip subsurfaceMap texture using
+/// until dli shaders are support it.
+// if(ReadString(node.GetChild("subsurfaceMap"), texturePath))
+// {
+// ToUnixFileSeparators(texturePath);
+//
+// const auto semantic = MaterialDefinition::SUBSURFACE;
+// materialDef.mTextureStages.push_back({semantic, TextureDefinition{std::move(texturePath)}});
+// materialDef.mFlags |= semantic;
+// }
if(ReadString(node.GetChild("occlusionMap"), texturePath))
{
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)
} // namespace Gltf2Util
-} // namespace Dali::Scene3D::Loader::Internal
\ No newline at end of file
+} // namespace Dali::Scene3D::Loader::Internal
bool CameraParameters::ConfigureCamera(CameraActor& camera, bool invertY) const
{
+ camera[Actor::Property::NAME] = name;
+
if(isPerspective)
{
if(Dali::Equals(zNear, gltf2::UNDEFINED_FLOAT_VALUE) ||
return true;
}
-} // namespace Dali::Scene3D::Loader
\ No newline at end of file
+} // namespace Dali::Scene3D::Loader
#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
{
{
struct DALI_SCENE3D_API CameraParameters
{
+ std::string name;
+
// TODO : Is these default value has is meaning?
Matrix matrix = Matrix::IDENTITY;
float orthographicSize = 1.f;
// EXTERNAL INCLUDES
#include <dali-toolkit/devel-api/toolkit-action-index-ranges.h>
-#include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
#include <dali-toolkit/devel-api/visuals/animated-image-visual-actions-devel.h>
+#include <dali-toolkit/devel-api/visuals/image-visual-actions-devel.h>
#include <dali/public-api/signals/callback.h>
#include <string>
JUMP_TO = DevelAnimatedImageVisual::Action::JUMP_TO, ///< Jump to the specified frame. Property::INTEGER value should be passed.
// AnimatedVectorImageVisual only actions
- SET_DYNAMIC_PROPERTY = DevelAnimatedImageVisual::Action::ANIMATED_IMAGE_VISUAL_ACTION_END_INDEX ///< Set the dynamic property.
+ SET_DYNAMIC_PROPERTY = DevelAnimatedImageVisual::Action::ANIMATED_IMAGE_VISUAL_ACTION_END_INDEX, ///< Set the dynamic property.
+
+ FLUSH, ///< Flush animation data. It will make ensure that changeness of animated vector image properties flushed.
};
} // namespace Action
}
break;
}
+ case DevelAnimatedVectorImageVisual::Action::FLUSH:
+ {
+ if(DALI_LIKELY(!mCoreShutdown))
+ {
+ SendAnimationData();
+ }
+ break;
+ }
}
TriggerVectorRasterization();
uint32_t index = mAnimationDataIndex == 0 ? 1 : 0; // Use the other buffer
- mAnimationData[index] = data;
+ mAnimationData[index].push_back(data);
mAnimationDataUpdated = true;
if(data.resendFlag & VectorAnimationTask::RESEND_SIZE)
{
ConditionalWait::ScopedLock lock(mConditionalWait);
- if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].resendFlag != 0)
+ if(!mAnimationDataUpdated || mAnimationData[mAnimationDataIndex].size() != 0)
{
// Data is not updated or the previous data is not applied yet.
return;
index = mAnimationDataIndex;
}
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_LOOP_COUNT)
+ for(const auto& animationData : mAnimationData[index])
{
- SetLoopCount(mAnimationData[index].loopCount);
- }
-
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_RANGE)
- {
- SetPlayRange(mAnimationData[index].playRange);
- }
-
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_STOP_BEHAVIOR)
- {
- SetStopBehavior(mAnimationData[index].stopBehavior);
- }
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_LOOP_COUNT)
+ {
+ SetLoopCount(animationData.loopCount);
+ }
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_LOOPING_MODE)
- {
- SetLoopingMode(mAnimationData[index].loopingMode);
- }
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_RANGE)
+ {
+ SetPlayRange(animationData.playRange);
+ }
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_CURRENT_FRAME)
- {
- SetCurrentFrameNumber(mAnimationData[index].currentFrame);
- }
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_STOP_BEHAVIOR)
+ {
+ SetStopBehavior(animationData.stopBehavior);
+ }
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
- {
- mVectorRenderer.InvalidateBuffer();
- }
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_LOOPING_MODE)
+ {
+ SetLoopingMode(animationData.loopingMode);
+ }
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_DYNAMIC_PROPERTY)
- {
- for(auto&& iter : mAnimationData[index].dynamicProperties)
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_CURRENT_FRAME)
{
- mVectorRenderer.AddPropertyValueCallback(iter.keyPath, static_cast<VectorAnimationRenderer::VectorProperty>(iter.property), iter.callback, iter.id);
+ SetCurrentFrameNumber(animationData.currentFrame);
}
- }
- if(mAnimationData[index].resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
- {
- if(mAnimationData[index].playState == DevelImageVisual::PlayState::PLAYING)
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_NEED_RESOURCE_READY)
{
- PlayAnimation();
+ mVectorRenderer.InvalidateBuffer();
}
- else if(mAnimationData[index].playState == DevelImageVisual::PlayState::PAUSED)
+
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_DYNAMIC_PROPERTY)
{
- PauseAnimation();
+ for(auto&& iter : animationData.dynamicProperties)
+ {
+ mVectorRenderer.AddPropertyValueCallback(iter.keyPath, static_cast<VectorAnimationRenderer::VectorProperty>(iter.property), iter.callback, iter.id);
+ }
}
- else if(mAnimationData[index].playState == DevelImageVisual::PlayState::STOPPED)
+
+ if(animationData.resendFlag & VectorAnimationTask::RESEND_PLAY_STATE)
{
- StopAnimation();
+ if(animationData.playState == DevelImageVisual::PlayState::PLAYING)
+ {
+ PlayAnimation();
+ }
+ else if(animationData.playState == DevelImageVisual::PlayState::PAUSED)
+ {
+ PauseAnimation();
+ }
+ else if(animationData.playState == DevelImageVisual::PlayState::STOPPED)
+ {
+ StopAnimation();
+ }
}
}
- // reset data
- mAnimationData[index].dynamicProperties.clear();
- mAnimationData[index].resendFlag = 0;
+ // reset data list
+ mAnimationData[index].clear();
}
void VectorAnimationTask::OnUploadCompleted()
std::string mUrl;
VectorAnimationRenderer mVectorRenderer;
- AnimationData mAnimationData[2];
+ std::vector<AnimationData> mAnimationData[2];
VectorAnimationThread& mVectorAnimationThread;
ConditionalWait mConditionalWait;
ResourceReadySignalType mResourceReadySignal;
/*
- * Copyright (c) 2022 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.
mCroppedHeight(0),
mBorder(0, 0, 0, 0),
mLoadingState(LoadingState::NOT_STARTED),
+ mRenderingMap{nullptr},
mPreMultiplyOnLoad(false),
- mRenderingMap{nullptr}
+ mObserverNotifying(false)
{
}
{
RenderingAddOn::Get().DestroyNPatch(mRenderingMap);
}
+ mObserverList.Clear();
+ mQueuedObservers.Clear();
}
void NPatchData::SetId(const NPatchDataId id)
void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
{
- mObserverList.PushBack(textureObserver);
+ if(mObserverNotifying)
+ {
+ // Do not add it into observer list during observer notifying.
+ mQueuedObservers.PushBack(textureObserver);
+ }
+ else
+ {
+ mObserverList.PushBack(textureObserver);
+ }
+ textureObserver->DestructionSignal().Connect(this, &NPatchData::ObserverDestroyed);
}
void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver)
{
if(textureObserver == mObserverList[index])
{
+ textureObserver->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
mObserverList.Erase(mObserverList.begin() + index);
break;
}
}
}
- for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+ mObserverNotifying = true;
+
+ // Reverse observer list that we can pop_back the observer.
+ std::reverse(mObserverList.Begin(), mObserverList.End());
+
+ while(mObserverList.Count() > 0u)
{
- TextureUploadObserver* observer = mObserverList[index];
+ TextureUploadObserver* observer = *(mObserverList.End() - 1u);
+ mObserverList.Erase(mObserverList.End() - 1u);
+
+ observer->DestructionSignal().Disconnect(this, &NPatchData::ObserverDestroyed);
+
NotifyObserver(observer, loadSuccess);
}
- mObserverList.Clear();
+ mObserverNotifying = false;
+
+ // Swap observer list what we queued during notify observer.
+ // If mQueuedObserver is not empty, it mean mLoadingState was LOAD_FAILED, and we try to re-load for this data.
+ // (If mLoadingState was LOAD_COMPLETE, NotifyObserver will be called directly. @todo : Should we fix this logic, matched with texture manager?)
+ // So LoadComplete will be called.
+ mObserverList.Swap(mQueuedObservers);
+}
+
+void NPatchData::ObserverDestroyed(TextureUploadObserver* observer)
+{
+ for(auto iter = mObserverList.Begin(); iter != mObserverList.End();)
+ {
+ if(observer == (*iter))
+ {
+ iter = mObserverList.Erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ if(mObserverNotifying)
+ {
+ for(auto iter = mQueuedObservers.Begin(); iter != mQueuedObservers.End();)
+ {
+ if(observer == (*iter))
+ {
+ iter = mQueuedObservers.Erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+ }
}
} // namespace Internal
#define DALI_TOOLKIT_NPATCH_DATA_H
/*
- * Copyright (c) 2022 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.
{
namespace Internal
{
-class NPatchData : public Dali::Toolkit::TextureUploadObserver
+class NPatchData : public ConnectionTracker, public Dali::Toolkit::TextureUploadObserver
{
public:
- typedef int32_t NPatchDataId; ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data.
- static const int INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error
+ typedef int32_t NPatchDataId; ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data.
+ static const NPatchDataId INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error
/**
* @brief Loading State of the NPatch image.
*/
void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override;
+ /**
+ * This is called by the TextureUploadObserver when an observer is destroyed.
+ * We use the callback to know when to remove an observer from our notify list.
+ * @param[in] observer The observer that generated the callback
+ */
+ void ObserverDestroyed(TextureUploadObserver* observer);
+
private:
using ObserverListType = Dali::Vector<TextureUploadObserver*>;
NPatchDataId mId;
- ObserverListType mObserverList; ///< Container used to store all observer clients of this Texture
- VisualUrl mUrl; ///< Url of the N-Patch
- TextureSet mTextureSet; ///< Texture containing the cropped image
- NPatchUtility::StretchRanges mStretchPixelsX; ///< X stretch pixels
- NPatchUtility::StretchRanges mStretchPixelsY; ///< Y stretch pixels
- std::size_t mHash; ///< Hash code for the Url
- uint32_t mCroppedWidth; ///< Width of the cropped middle part of N-patch
- uint32_t mCroppedHeight; ///< Height of the cropped middle part of N-patch
- Rect<int> mBorder; ///< The size of the border
- LoadingState mLoadingState; ///< True if the data loading is completed
- bool mPreMultiplyOnLoad; ///< Whether to multiply alpha into color channels on load
- void* mRenderingMap; ///< NPatch rendering data
+ ObserverListType mObserverList; ///< Container used to store all observer clients of this Texture
+ ObserverListType mQueuedObservers; ///< Container observers when user try to add during notify observers
+ VisualUrl mUrl; ///< Url of the N-Patch
+ TextureSet mTextureSet; ///< Texture containing the cropped image
+ NPatchUtility::StretchRanges mStretchPixelsX; ///< X stretch pixels
+ NPatchUtility::StretchRanges mStretchPixelsY; ///< Y stretch pixels
+ std::size_t mHash; ///< Hash code for the Url
+ uint32_t mCroppedWidth; ///< Width of the cropped middle part of N-patch
+ uint32_t mCroppedHeight; ///< Height of the cropped middle part of N-patch
+ Rect<int> mBorder; ///< The size of the border
+ LoadingState mLoadingState; ///< True if the data loading is completed
+ void* mRenderingMap; ///< NPatch rendering data
+
+ bool mPreMultiplyOnLoad : 1; ///< Whether to multiply alpha into color channels on load
+ bool mObserverNotifying : 1; ///< Whether this NPatchData notifying observers or not.
};
} // namespace Internal
/*
- * Copyright (c) 2022 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.
// EXTERNAL HEADERS
#include <dali/devel-api/common/hash.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
#include <dali/integration-api/debug.h>
namespace Dali
} // Anonymous namespace
NPatchLoader::NPatchLoader()
-: mCurrentNPatchDataId(0)
+: mCurrentNPatchDataId(0),
+ mRemoveProcessorRegistered(false)
{
}
NPatchLoader::~NPatchLoader()
{
+ if(mRemoveProcessorRegistered && Adaptor::IsAvailable())
+ {
+ Adaptor::Get().UnregisterProcessor(*this, true);
+ mRemoveProcessorRegistered = false;
+ }
}
NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
{
+ // Skip invalid id generation.
+ if(DALI_UNLIKELY(mCurrentNPatchDataId == NPatchData::INVALID_NPATCH_DATA_ID))
+ {
+ mCurrentNPatchDataId = 0;
+ }
return mCurrentNPatchDataId++;
}
-std::size_t NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
+NPatchData::NPatchDataId NPatchLoader::Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading)
{
- NPatchData* data = GetNPatchData(url, border, preMultiplyOnLoad);
+ std::shared_ptr<NPatchData> data = GetNPatchData(url, border, preMultiplyOnLoad);
- DALI_ASSERT_ALWAYS(data && "NPatchData creation failed!");
+ DALI_ASSERT_ALWAYS(data.get() && "NPatchData creation failed!");
if(data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
{
auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
: TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
- Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data, true, preMultiplyOnLoading);
+ Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer(url, Dali::ImageDimensions(), FittingMode::DEFAULT, SamplingMode::BOX_THEN_LINEAR, synchronousLoading, data.get(), true, preMultiplyOnLoading);
if(pixelBuffer)
{
return false;
}
-void NPatchLoader::Remove(std::size_t id, TextureUploadObserver* textureObserver)
+void NPatchLoader::RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
+{
+ mRemoveQueue.push_back({id, textureObserver});
+
+ if(!mRemoveProcessorRegistered && Adaptor::IsAvailable())
+ {
+ mRemoveProcessorRegistered = true;
+ Adaptor::Get().RegisterProcessor(*this, true);
+ }
+}
+
+void NPatchLoader::Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver)
{
int32_t cacheIndex = GetCacheIndexFromId(id);
if(cacheIndex == INVALID_CACHE_INDEX)
}
}
-NPatchData* NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
+void NPatchLoader::Process(bool postProcessor)
+{
+ for(auto& iter : mRemoveQueue)
+ {
+ Remove(iter.first, iter.second);
+ }
+ mRemoveQueue.clear();
+
+ if(Adaptor::IsAvailable())
+ {
+ Adaptor::Get().UnregisterProcessor(*this, true);
+ mRemoveProcessorRegistered = false;
+ }
+}
+
+std::shared_ptr<NPatchData> NPatchLoader::GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad)
{
std::size_t hash = CalculateHash(url.GetUrl());
std::vector<NPatchInfo>::size_type index = UNINITIALIZED_ID;
if(mCache[index].mData->GetBorder() == border)
{
mCache[index].mReferenceCount++;
- return mCache[index].mData.get();
+ return mCache[index].mData;
}
else
{
// If this is new image loading, make new cache data
if(infoPtr == nullptr)
{
- NPatchInfo info(new NPatchData());
+ NPatchInfo info(std::make_shared<NPatchData>());
info.mData->SetId(GenerateUniqueNPatchDataId());
info.mData->SetHash(hash);
info.mData->SetUrl(url);
// Else if LOAD_COMPLETE, Same url but border is different - use the existing texture
else if(infoPtr->mData->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE)
{
- NPatchInfo info(new NPatchData());
+ NPatchInfo info(std::make_shared<NPatchData>());
info.mData->SetId(GenerateUniqueNPatchDataId());
info.mData->SetHash(hash);
DALI_ASSERT_ALWAYS(infoPtr && "NPatchInfo creation failed!");
- return infoPtr->mData.get();
+ return infoPtr->mData;
}
} // namespace Internal
#define DALI_TOOLKIT_NPATCH_LOADER_H
/*
- * Copyright (c) 2022 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.
// EXTERNAL INCLUDES
#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/processor-interface.h>
#include <dali/public-api/rendering/texture-set.h>
-#include <memory> // for std::unique_ptr
+#include <memory> // for std::shared_ptr
#include <string>
+#include <utility> // for std::pair
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/utility/npatch-utilities.h>
* small space and there's not usually a lot of them. Usually N patches are specified in
* toolkit default style and there is 1-2 per control that are shared across the whole application.
*/
-class NPatchLoader
+class NPatchLoader : public Integration::Processor
{
public:
/**
* @param [in] synchronousLoading True if the image will be loaded in synchronous time.
* @return id of the texture.
*/
- std::size_t Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading);
-
- /**
- * @brief Set loaded PixelBuffer and its information
- *
- * @param [in] id cache data id
- * @param [in] pixelBuffer of loaded image
- * @param [in] preMultiplied True if the image had pre-multiplied alpha applied
- */
- void SetNPatchData(std::size_t id, Devel::PixelBuffer& pixelBuffer, bool preMultiplied);
+ NPatchData::NPatchDataId Load(TextureManager& textureManager, TextureUploadObserver* textureObserver, const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad, bool synchronousLoading);
/**
* @brief Retrieve N patch data matching to an id
bool GetNPatchData(const NPatchData::NPatchDataId id, const NPatchData*& data);
/**
- * @brief Remove a texture matching id.
+ * @brief Request remove a texture matching id.
* Erase the observer from the observer list of cache if we need.
- * This API decrease cached NPatchInfo reference.
- * If the NPatchInfo reference become 0, the textureSet will be reset.
*
* @param [in] id cache data id
* @param [in] textureObserver The NPatchVisual that requested loading.
*/
- void Remove(std::size_t id, TextureUploadObserver* textureObserver);
+ void RequestRemove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver);
+
+protected: // Implementation of Processor
+ /**
+ * @copydoc Dali::Integration::Processor::Process()
+ */
+ void Process(bool postProcessor) override;
private:
NPatchData::NPatchDataId GenerateUniqueNPatchDataId();
int32_t GetCacheIndexFromId(const NPatchData::NPatchDataId id);
+ /**
+ * @brief Remove a texture matching id.
+ * Erase the observer from the observer list of cache if we need.
+ * This API decrease cached NPatchInfo reference.
+ * If the NPatchInfo reference become 0, the textureSet will be reset.
+ *
+ * @param [in] id cache data id
+ * @param [in] textureObserver The NPatchVisual that requested loading.
+ */
+ void Remove(NPatchData::NPatchDataId id, TextureUploadObserver* textureObserver);
+
private:
/**
* @brief Information of NPatchData
*/
struct NPatchInfo
{
- NPatchInfo(NPatchData* data)
+ NPatchInfo(std::shared_ptr<NPatchData> data)
: mData(data),
mReferenceCount(1u)
{
NPatchInfo(const NPatchInfo& info) = delete; // Do not use copy constructor
NPatchInfo& operator=(const NPatchInfo& info) = delete; // Do not use copy assign
- std::unique_ptr<NPatchData> mData;
+ std::shared_ptr<NPatchData> mData;
std::int16_t mReferenceCount; ///< The number of N-patch visuals that use this data.
};
* image has no alpha channel
* @return NPatchData pointer that Load function will used.
*/
- NPatchData* GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad);
+ std::shared_ptr<NPatchData> GetNPatchData(const VisualUrl& url, const Rect<int>& border, bool& preMultiplyOnLoad);
protected:
/**
private:
NPatchData::NPatchDataId mCurrentNPatchDataId;
std::vector<NPatchInfo> mCache;
+
+ std::vector<std::pair<NPatchData::NPatchDataId, TextureUploadObserver*>> mRemoveQueue; ///< Queue of textures to remove at PostProcess. It will be cleared after PostProcess.
+
+ bool mRemoveProcessorRegistered : 1; ///< Flag if remove processor registered or not.
};
} // namespace Internal
{
if(mId != NPatchData::INVALID_NPATCH_DATA_ID)
{
- mLoader.Remove(mId, this);
+ mLoader.RequestRemove(mId, this);
mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
mId = NPatchData::INVALID_NPATCH_DATA_ID;
}
{
if(mId != NPatchData::INVALID_NPATCH_DATA_ID)
{
- mLoader.Remove(mId, this);
+ mLoader.RequestRemove(mId, this);
mId = NPatchData::INVALID_NPATCH_DATA_ID;
}
if(mAuxiliaryTextureId != TextureManager::INVALID_TEXTURE_ID)
{
const unsigned int TOOLKIT_MAJOR_VERSION = 2;
const unsigned int TOOLKIT_MINOR_VERSION = 2;
-const unsigned int TOOLKIT_MICRO_VERSION = 42;
+const unsigned int TOOLKIT_MICRO_VERSION = 43;
const char* const TOOLKIT_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
Name: dali2-toolkit
Summary: Dali 3D engine Toolkit
-Version: 2.2.42
+Version: 2.2.43
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT
%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}/dali-physics/dali-physics.h
%{_includedir}/chipmunk/*
-%{_libdir}/pkgconfig/dali2-physics-2d.pc
%{_libdir}/pkgconfig/chipmunk2d.pc
+%{_libdir}/pkgconfig/dali2-physics-2d.pc
%files -n %{dali2_physics3d}
%if 0%{?enable_dali_smack_rules}
%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}/dali-physics/dali-physics.h
%{_includedir}/bullet/*
%{_libdir}/pkgconfig/dali2-physics-3d.pc
%{_libdir}/pkgconfig/bullet3.pc
-