Merge changes Ifaf5dd7d,Ib24df94c into devel/master
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 1 Sep 2023 14:02:53 +0000 (14:02 +0000)
committerGerrit Code Review <gerrit@review>
Fri, 1 Sep 2023 14:02:53 +0000 (14:02 +0000)
* changes:
  Updated chipmunk demo to use Dali physics adaptor
  Added bullet-physics-demo

28 files changed:
.gitignore
build/tizen/CMakeLists.txt
build/tizen/examples/CMakeLists.txt
com.samsung.dali-demo.xml
examples-reel/dali-examples-reel.cpp
examples/bullet-physics/README.md [new file with mode: 0644]
examples/bullet-physics/ball-renderer.cpp [new file with mode: 0644]
examples/bullet-physics/ball-renderer.h [new file with mode: 0644]
examples/bullet-physics/cube-renderer.cpp [new file with mode: 0644]
examples/bullet-physics/cube-renderer.h [new file with mode: 0644]
examples/bullet-physics/dependencies.cmake [new file with mode: 0644]
examples/bullet-physics/physics-demo-controller.cpp [new file with mode: 0644]
examples/bullet-physics/shaders/rendering-textured-shape.frag [moved from examples/chipmunk-physics/shaders/rendering-textured-shape.frag with 75% similarity]
examples/bullet-physics/shaders/rendering-textured-shape.vert [moved from examples/chipmunk-physics/shaders/rendering-textured-shape.vert with 90% similarity]
examples/chipmunk-physics/README.md
examples/chipmunk-physics/dependencies.cmake [new file with mode: 0644]
examples/chipmunk-physics/frame-callback.cpp [deleted file]
examples/chipmunk-physics/frame-callback.h [deleted file]
examples/chipmunk-physics/physics-actor.cpp [deleted file]
examples/chipmunk-physics/physics-actor.h [deleted file]
examples/chipmunk-physics/physics-demo-controller.cpp
examples/chipmunk-physics/physics-impl.cpp [deleted file]
examples/chipmunk-physics/physics-impl.h [deleted file]
examples/drawable-actor/drawable-actor-example.cpp
packaging/com.samsung.dali-demo.spec
resources/po/en_GB.po
resources/po/en_US.po
shared/dali-demo-strings.h

index 3d47500..001916b 100644 (file)
@@ -60,6 +60,10 @@ simple-image-wall.js
 /com.samsung.dali-demo-debugsource.manifest
 /build/tizen/*.vcxproj
 /build/tizen/*.vcxproj.filters
+/build/tizen/.ninja_deps
+/build/tizen/.ninja_log
+/build/tizen/build.ninja
+/build/tizen/rules.ninja
 /build/tizen/builder/Debug/*
 /build/tizen/builder/Release/*
 /build/tizen/builder/*.dir
index 5cb6012..5270d57 100644 (file)
@@ -243,21 +243,39 @@ IF( ENABLE_PKG_CONFIGURE )
     SET( ENABLE_SCENE3D "ON" )
   ENDIF()
 
-  pkg_check_modules(DALI_PHYSICS_2D dali2-physics-2d)
+  pkg_check_modules(DALI_PHYSICS_2D dali2-physics-2d chipmunk2d)
   IF( DALI_PHYSICS_2D_FOUND )
     FOREACH(flag ${DALI_PHYSICS_2D_CFLAGS})
-      SET(REQUIRED_CFLAGS "${REQUIRED_CFLAGS} ${flag}")
+      SET(OPTIONAL_PHYSICS_2D_CFLAGS "${OPTIONAL_PHYSICS_2D_CFLAGS} ${flag}")
     ENDFOREACH(flag)
-
-    SET( REQUIRED_CFLAGS "${REQUIRED_CFLAGS} -DDALI_PHYSICS_2D_AVAILABLE" )
+    SET(OPTIONAL_PHYSICS_2D_CFLAGS "${OPTIONAL_PHYSICS_2D_CFLAGS} -DDALI_PHYSICS_2D_AVAILABLE" )
+    STRING(STRIP ${OPTIONAL_PHYSICS_2D_CFLAGS} OPTIONAL_PHYSICS_2D_CFLAGS)
 
     FOREACH(flag ${DALI_PHYSICS_2D_LDFLAGS})
-      SET(REQUIRED_PKGS_LDFLAGS "${REQUIRED_PKGS_LDFLAGS} ${flag}")
+      SET(OPTIONAL_PHYSICS_2D_LDFLAGS "${OPTIONAL_PHYSICS_2D_LDFLAGS} ${flag}")
     ENDFOREACH(flag)
+    STRING(STRIP ${OPTIONAL_PHYSICS_2D_LDFLAGS} OPTIONAL_PHYSICS_2D_LDFLAGS)
 
     SET( ENABLE_PHYSICS_2D "ON" )
   ENDIF()
 
+  pkg_check_modules(DALI_PHYSICS_3D dali2-physics-3d bullet3)
+  IF( DALI_PHYSICS_3D_FOUND )
+    FOREACH(flag ${DALI_PHYSICS_3D_CFLAGS})
+      SET(OPTIONAL_PHYSICS_3D_CFLAGS "${OPTIONAL_PHYSICS_3D_CFLAGS} ${flag}")
+    ENDFOREACH(flag)
+
+    SET( OPTIONAL_PHYSICS_3D_CFLAGS "${OPTIONAL_PHYSICS_3D_CFLAGS} -DDALI_PHYSICS_3D_AVAILABLE" )
+    STRING(STRIP ${OPTIONAL_PHYSICS_3D_CFLAGS} OPTIONAL_PHYSICS_3D_CFLAGS)
+
+    FOREACH(flag ${DALI_PHYSICS_3D_LDFLAGS})
+      SET(OPTIONAL_PHYSICS_3D_LDFLAGS "${OPTIONAL_PHYSICS_3D_LDFLAGS} ${flag}")
+    ENDFOREACH(flag)
+    STRING(STRIP ${OPTIONAL_PHYSICS_3D_LDFLAGS} OPTIONAL_PHYSICS_3D_LDFLAGS)
+
+    SET( ENABLE_PHYSICS_3D "ON" )
+  ENDIF()
+
   # if build as tizen platform, use capi-appfw-app-control
   IF( TIZEN )
     pkg_check_modules(CAPI_APPFW_APP_CONTROL capi-appfw-app-control)
@@ -308,6 +326,9 @@ IF( WIN32 OR APPLE ) # WIN32 includes x64 as well according to the cmake doc.
   FIND_PACKAGE( dali2-scene3d )
 
   FIND_PACKAGE( chipmunk )
+  FIND_PACKAGE( bullet3 )
+  FIND_PACKAGE( dali2-physics-2d )
+  FIND_PACKAGE( dali2-physics-3d )
 
   # Set up the include dir
   SET( INCLUDE_DIR $ENV{includedir} )
@@ -365,11 +386,21 @@ IF( WIN32 OR APPLE ) # WIN32 includes x64 as well according to the cmake doc.
   ENDIF()
 
   IF (chipmunk_FOUND)
-    SET(REQUIRED_LIBS
-      ${REQUIRED_LIBS}
-      -lchipmunk
-    )
-    SET( ENABLE_PHYSICS_2D "ON" )
+    SET(DALI_PHYSICS_2D_LDFLAGS ${DALI_PHYSICS_2D_LDFLAGS} -lchipmunk)
+    SET(ENABLE_PHYSICS_2D "ON" )
+  ENDIF()
+
+  IF (bullet3_FOUND)
+    SET(DALI_PHYSICS_3D_LDFLAGS ${OPTIONAL_PHYSICS_3D_LDFLAGS} -lbullet3)
+    SET(ENABLE_PHYSICS_3D "ON" )
+  ENDIF()
+
+  IF (dali2-physics-2d_FOUND)
+    SET(DALI_PHYSICS_2D_LDFLAGS ${DALI_PHYSICS_2D_LDFLAGS} dali2-physics-2d::dali2-physics-2d)
+  ENDIF()
+
+  IF (dali2-physics-3d_FOUND)
+    SET(DALI_PHYSICS_3D_LDFLAGS ${DALI_PHYSICS_3D_LDFLAGS} dali2-physics-3d::dali2-physics-3d)
   ENDIF()
 
 ELSEIF( UNIX )
@@ -404,6 +435,10 @@ IF( ENABLE_PHYSICS_2D )
   SET(DALI_DEMO_CFLAGS "${DALI_DEMO_CFLAGS} -DDALI_PHYSICS_2D_AVAILABLE")
 ENDIF()
 
+IF( ENABLE_PHYSICS_3D )
+  SET(DALI_DEMO_CFLAGS "${DALI_DEMO_CFLAGS} -DDALI_PHYSICS_3D_AVAILABLE")
+ENDIF()
+
 IF( UNIX )
   IF( NOT ${ENABLE_EXPORTALL} )
     ADD_DEFINITIONS( "-DHIDE_DALI_INTERNALS" )
@@ -522,3 +557,4 @@ MESSAGE( " Current Build Platform  : [" ${CURRENT_BUILD_PLATFORM} "]" )
 MESSAGE( " Build example name      : [" ${CURRENT_BUILD_EXAMPLE_NAME} "]" )
 MESSAGE( " Scene3D Enabled         : [" ${ENABLE_SCENE3D} "]" )
 MESSAGE( " Physics 2D Enabled      : [" ${ENABLE_PHYSICS_2D} "]" )
+MESSAGE( " Physics 3D Enabled      : [" ${ENABLE_PHYSICS_3D} "]" )
index 6a458b3..7d02b15 100644 (file)
@@ -35,6 +35,7 @@ ENDIF()
 FUNCTION(INSTALL_EXAMPLES EXAMPLE)
   SET(PARENT_CMAKE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../")
   FILE(GLOB_RECURSE SRCS "${EXAMPLES_SRC_DIR}/${EXAMPLE}/*.cpp")
+
   SET(SRCS ${SRCS} "${ROOT_SRC_DIR}/shared/resources-location.cpp")
   IF(SHARED)
     ADD_LIBRARY(${EXAMPLE}.example SHARED ${SRCS})
@@ -42,6 +43,10 @@ FUNCTION(INSTALL_EXAMPLES EXAMPLE)
     ADD_EXECUTABLE(${EXAMPLE}.example ${SRCS})
   ENDIF()
 
+  IF(EXISTS ${EXAMPLES_SRC_DIR}/${EXAMPLE}/dependencies.cmake)
+    INCLUDE( ${EXAMPLES_SRC_DIR}/${EXAMPLE}/dependencies.cmake OPTIONAL)
+  endif()
+
   # Generate source files for shaders
   SET(SHADER_SOURCE_DIR "${EXAMPLES_SRC_DIR}/${EXAMPLE}/shaders/")
   IF (EXISTS ${SHADER_SOURCE_DIR})
@@ -64,4 +69,3 @@ IF( NOT BUILD_EXAMPLE_NAME )
 ELSE()
   INSTALL_EXAMPLES(${BUILD_EXAMPLE_NAME})
 ENDIF()
-
index 4c12d91..17fba75 100644 (file)
@@ -58,6 +58,9 @@
        <ui-application appid="builder.example" exec="/usr/apps/com.samsung.dali-demo/bin/builder.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Script Based UI</label>
        </ui-application>
+       <ui-application appid="bullet-physics.example" exec="/usr/apps/com.samsung.dali-demo/bin/bullet-physics.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
+               <label>Bullet Physics</label>
+       </ui-application>
        <ui-application appid="buttons.example" exec="/usr/apps/com.samsung.dali-demo/bin/buttons.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Buttons</label>
        </ui-application>
index a697899..7d5af30 100644 (file)
@@ -46,6 +46,7 @@ int DALI_EXPORT_API main(int argc, char** argv)
   demo.AddExample(Example("bloom-view.example", DALI_DEMO_STR_TITLE_BLOOM_VIEW));
   demo.AddExample(Example("builder.example", DALI_DEMO_STR_TITLE_SCRIPT_BASED_UI));
   demo.AddExample(Example("buttons.example", DALI_DEMO_STR_TITLE_BUTTONS));
+  demo.AddExample(Example("bullet-physics.example", DALI_DEMO_STR_TITLE_BULLET_PHYSICS));
   demo.AddExample(Example("canvas-view.example", DALI_DEMO_STR_TITLE_CANVAS_VIEW));
   demo.AddExample(Example("chipmunk-physics.example", DALI_DEMO_STR_TITLE_CHIPMUNK_PHYSICS));
   demo.AddExample(Example("clipping.example", DALI_DEMO_STR_TITLE_CLIPPING));
diff --git a/examples/bullet-physics/README.md b/examples/bullet-physics/README.md
new file mode 100644 (file)
index 0000000..e2abee1
--- /dev/null
@@ -0,0 +1,20 @@
+# Bullet physics example
+
+This is an example showing how to use the Bullet physics library to create and control 
+physics objects in DALi.
+
+It creates a pyramid of bricks and a ball falling onto a large block.
+
+The user can select and drag any of the actors with the mouse.
+The user can zoom in and out using the mouse wheel.
+
+"wasd" keys move the last touched actor up/down/left/right.
+"qe" keys rotate the last touched actor in Z axis
+"zx" keys rotate the last touched actor in Y axis
+"cv" keys rotate the last touched actor in X axis
+"p" key resets the position/forces on the last touched actor to the origin
+
+"m" key toggles the debug rendering
+Space key toggles the integration state.
+
+
diff --git a/examples/bullet-physics/ball-renderer.cpp b/examples/bullet-physics/ball-renderer.cpp
new file mode 100644 (file)
index 0000000..6c4f852
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * 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 "ball-renderer.h"
+#include <dali-toolkit/dali-toolkit.h>
+#include "cube-renderer.h"
+#include "generated/rendering-textured-shape-frag.h"
+#include "generated/rendering-textured-shape-vert.h"
+
+using namespace Dali;
+
+namespace
+{
+const char* TEXTURE_URL = DEMO_IMAGE_DIR "/background-3.jpg";
+} // namespace
+
+Dali::Geometry   BallRenderer::gBallGeometry;
+Dali::TextureSet BallRenderer::gBallTextureSet;
+
+struct Vertex
+{
+  Vector3 aPosition;
+  Vector2 aTexCoord;
+};
+
+void SubDivide(std::vector<Vertex>& vertices, std::vector<uint16_t>& indices)
+{
+  uint16_t triangleCount = indices.size() / 3;
+  for(uint16_t i = 0; i < triangleCount; ++i)
+  {
+    auto v1 = vertices[indices[i * 3]].aPosition;
+    auto v2 = vertices[indices[i * 3 + 1]].aPosition;
+    auto v3 = vertices[indices[i * 3 + 2]].aPosition;
+    // Triangle subdivision adds pts halfway along each edge.
+    auto     v4 = v1 + (v2 - v1) * 0.5f;
+    auto     v5 = v2 + (v3 - v2) * 0.5f;
+    auto     v6 = v3 + (v1 - v3) * 0.5f;
+    uint16_t j  = vertices.size();
+    vertices.emplace_back(Vertex{
+      {v4},
+    });
+    vertices.emplace_back(Vertex{
+      {v5},
+    });
+    vertices.emplace_back(Vertex{
+      {v6},
+    });
+    // Now, original tri breaks into 4, so replace this tri, and add 3 more
+    uint16_t i1        = indices[i * 3 + 1];
+    uint16_t i2        = indices[i * 3 + 2];
+    indices[i * 3 + 1] = j;
+    indices[i * 3 + 2] = j + 2;
+
+    std::vector<uint16_t> newTris = {j, i1, uint16_t(j + 1), j, uint16_t(j + 1), uint16_t(j + 2), uint16_t(j + 1), i2, uint16_t(j + 2)};
+    indices.insert(indices.end(), newTris.begin(), newTris.end());
+  }
+  for(auto& vertex : vertices)
+  {
+    vertex.aPosition.Normalize();
+  }
+}
+
+void MapUVsToSphere(std::vector<Vertex>& vertices)
+{
+  // Convert world coords to long-lat
+  // Assume radius=1;
+  // V=(cos(long)cos(lat), sin(long)cos(lat), sin(lat))
+  // => lat=arcsin(z), range (-PI/2, PI/2); => 0.5+(asin(z)/PI) range(0,1)
+  // => y/x = sin(long)/cos(long) => long = atan2(y/x), range(-pi, pi)
+  // But, rotate 90 deg for portrait texture!
+  for(auto& vertex : vertices)
+  {
+    vertex.aTexCoord.y = 1.0f + (atan2f(vertex.aPosition.y, vertex.aPosition.x) / (2.0f * Math::PI));
+    vertex.aTexCoord.x = 1.0f - (0.5f + (asinf(vertex.aPosition.z) / Math::PI));
+  }
+}
+
+/**
+ * @brief CreateBallGeometry
+ * This function creates a ball geometry including texture coordinates.
+ */
+Geometry BallRenderer::CreateBallGeometry()
+{
+  if(!gBallGeometry)
+  {
+    float phi = (1.0f + sqrt(5.0f)) * 0.5f; // golden ratio
+    float a   = 1.0f;
+    float b   = 1.0f / phi;
+
+    // add vertices
+    std::vector<Vertex> vertices;
+    vertices.emplace_back(Vertex{Vector3{0, b, -a}});
+    vertices.emplace_back(Vertex{Vector3{b, a, 0}});
+    vertices.emplace_back(Vertex{Vector3{-b, a, -a}});
+
+    vertices.emplace_back(Vertex{Vector3{0, b, a}});
+    vertices.emplace_back(Vertex{Vector3{0, -b, a}});
+    vertices.emplace_back(Vertex{Vector3{-a, 0, b}});
+
+    vertices.emplace_back(Vertex{Vector3{0, -b, -a}});
+    vertices.emplace_back(Vertex{Vector3{a, 0, -b}});
+    vertices.emplace_back(Vertex{Vector3{a, 0, b}});
+
+    vertices.emplace_back(Vertex{Vector3{-a, 0, -b}});
+    vertices.emplace_back(Vertex{Vector3{b, -a, 0}});
+    vertices.emplace_back(Vertex{Vector3{-b, -a, 0}});
+
+    for(auto vertex : vertices)
+    {
+      vertex.aPosition.Normalize();
+    }
+
+    // this dodgy code is not zero indexed but starts at 1.
+    std::vector<uint16_t> indices = {
+      3, 2, 1, 2, 3, 4, 6, 5, 4, 5, 9, 4, 8, 7, 1, 7, 10, 1, 12, 11, 5, 11, 12, 7, 10, 6, 3, 6, 10, 12, 9, 8, 2, 8, 9, 11, 3, 6, 4, 9, 2, 4, 10, 3, 1, 2, 8, 1, 12, 10, 7, 8, 11, 7, 6, 12, 5, 11, 9, 5};
+    // fix offset indices
+    for(auto& index : indices)
+    {
+      --index;
+    }
+
+    // 2 subdivisions gives a reasonably nice sphere
+    SubDivide(vertices, indices);
+    SubDivide(vertices, indices);
+
+    MapUVsToSphere(vertices);
+
+    VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
+                                                    .Add("aPosition", Property::VECTOR3)
+                                                    .Add("aTexCoord", Property::VECTOR2));
+    vertexBuffer.SetData(&vertices[0], vertices.size());
+
+    gBallGeometry = Geometry::New();
+    gBallGeometry.AddVertexBuffer(vertexBuffer);
+    gBallGeometry.SetIndexBuffer(&indices[0], indices.size());
+    gBallGeometry.SetType(Geometry::TRIANGLES);
+  }
+  return gBallGeometry;
+}
+
+TextureSet BallRenderer::CreateTexture(std::string url)
+{
+  // Load image from file
+  PixelData pixels = Dali::Toolkit::SyncImageLoader::Load(url);
+
+  Texture texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
+  texture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
+
+  // create TextureSet
+  auto textureSet = TextureSet::New();
+  textureSet.SetTexture(0, texture);
+
+  return textureSet;
+}
+
+/**
+ * Function creates renderer. It turns on depth test and depth write.
+ */
+Dali::Renderer BallRenderer::CreateRenderer(TextureSet textures)
+{
+  CreateBallGeometry();
+  Dali::Shader shader   = CreateShader();
+  Renderer     renderer = Renderer::New(gBallGeometry, shader);
+  renderer.SetTextures(textures);
+
+  // Face culling is enabled to hide the backwards facing sides of the ball
+  // This is sufficient to render a single object; for more complex scenes depth-testing might be required
+  renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
+  return renderer;
+}
+
+/**
+ * Creates new actor and renderer.
+ */
+Actor BallRenderer::CreateActor(Vector3 size, Vector4 color)
+{
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3(size.x, size.y, size.z) * 0.5f);
+  actor.SetProperty(Actor::Property::COLOR, color);
+  if(!gBallTextureSet)
+  {
+    gBallTextureSet = CreateTexture(TEXTURE_URL);
+  }
+  Renderer renderer = CreateRenderer(gBallTextureSet);
+  actor.AddRenderer(renderer);
+  return actor;
+}
+
+Actor BallRenderer::CreateActor(Vector3 size, std::string url)
+{
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
+  // Sphere is radius 1; so has natural diameter 2, so halve the size
+  actor.SetProperty(Actor::Property::SIZE, Vector3(size.x, size.y, size.z) * 0.5f);
+
+  TextureSet textures = CreateTexture(url);
+  Renderer   renderer = CreateRenderer(textures);
+  actor.AddRenderer(renderer);
+  return actor;
+}
diff --git a/examples/bullet-physics/ball-renderer.h b/examples/bullet-physics/ball-renderer.h
new file mode 100644 (file)
index 0000000..b106cec
--- /dev/null
@@ -0,0 +1,43 @@
+#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>
+using Dali::Actor;
+using Dali::Geometry;
+using Dali::Renderer;
+using Dali::Shader;
+using Dali::TextureSet;
+
+class BallRenderer
+{
+public:
+  static Dali::Geometry   CreateBallGeometry();
+  static Dali::Renderer   CreateRenderer(TextureSet textures);
+  static Dali::TextureSet CreateTexture(std::string url);
+
+  /**
+   * The size can control whether this is a ball or a cuboid. Note, textures
+   * will be stretched on non-square sides.
+   */
+  static Dali::Actor CreateActor(Dali::Vector3 size, Dali::Vector4 color);
+  static Dali::Actor CreateActor(Dali::Vector3 size, std::string url);
+
+  static Geometry   gBallGeometry;
+  static Shader     gBallShader;
+  static TextureSet gBallTextureSet;
+};
diff --git a/examples/bullet-physics/cube-renderer.cpp b/examples/bullet-physics/cube-renderer.cpp
new file mode 100644 (file)
index 0000000..a5b79d4
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * 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 "cube-renderer.h"
+#include <dali-toolkit/dali-toolkit.h>
+#include "generated/rendering-textured-shape-frag.h"
+#include "generated/rendering-textured-shape-vert.h"
+
+using namespace Dali;
+
+namespace
+{
+const char* TEXTURE_URL = DEMO_IMAGE_DIR "/wood.png";
+} // namespace
+
+Dali::Geometry   CubeRenderer::gCubeGeometry;
+Dali::TextureSet CubeRenderer::gCubeTextureSet;
+
+Dali::Shader CreateShader()
+{
+  static Dali::Shader gShapeShader;
+
+  if(!gShapeShader)
+  {
+    gShapeShader = Shader::New(SHADER_RENDERING_TEXTURED_SHAPE_VERT, SHADER_RENDERING_TEXTURED_SHAPE_FRAG);
+  }
+  return gShapeShader;
+}
+
+/**
+ * @brief CreateCubeGeometry
+ * This function creates a cube geometry including texture coordinates.
+ */
+Geometry CubeRenderer::CreateCubeGeometry()
+{
+  if(!gCubeGeometry)
+  {
+    struct Vertex
+    {
+      Vector3 aPosition;
+      Vector2 aTexCoord;
+    };
+
+    Vertex vertices[] = {
+      {Vector3(1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
+      {Vector3(-1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
+      {Vector3(1.0f, -1.0f, 1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, 1.0f, 1.0f), Vector2(0.0, 1.0)},
+      {Vector3(1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
+      {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
+      {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 1.0)},
+      {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
+      {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 1.0)},
+      {Vector3(1.0f, 1.0f, -1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, 1.0f, 1.0f), Vector2(0.0, 1.0)},
+      {Vector3(1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 0.0)},
+      {Vector3(-1.0f, 1.0f, -1.0f), Vector2(0.0, 0.0)},
+      {Vector3(-1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
+      {Vector3(1.0f, -1.0f, 1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, 1.0f, 1.0f), Vector2(1.0, 1.0)},
+      {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
+      {Vector3(1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, -1.0f, 1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
+      {Vector3(-1.0f, -1.0f, -1.0f), Vector2(0.0, 0.0)},
+      {Vector3(-1.0f, -1.0f, -1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, -1.0f, 1.0f), Vector2(1.0, 0.0)},
+      {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
+      {Vector3(1.0f, 1.0f, -1.0f), Vector2(1.0, 1.0)},
+      {Vector3(-1.0f, 1.0f, -1.0f), Vector2(1.0, 0.0)},
+      {Vector3(-1.0f, 1.0f, 1.0f), Vector2(0.0, 0.0)},
+    };
+
+    VertexBuffer vertexBuffer = VertexBuffer::New(Property::Map()
+                                                    .Add("aPosition", Property::VECTOR3)
+                                                    .Add("aTexCoord", Property::VECTOR2));
+    vertexBuffer.SetData(vertices, sizeof(vertices) / sizeof(Vertex));
+
+    // create indices
+    const unsigned short INDEX_CUBE[] = {
+      2, 1, 0, 5, 4, 3, 8, 7, 6, 11, 10, 9, 14, 13, 12, 17, 16, 15, 20, 19, 18, 23, 22, 21, 26, 25, 24, 29, 28, 27, 32, 31, 30, 35, 34, 33};
+
+    gCubeGeometry = Geometry::New();
+    gCubeGeometry.AddVertexBuffer(vertexBuffer);
+    gCubeGeometry.SetIndexBuffer(INDEX_CUBE,
+                                 sizeof(INDEX_CUBE) / sizeof(INDEX_CUBE[0]));
+    gCubeGeometry.SetType(Geometry::TRIANGLES);
+  }
+  return gCubeGeometry;
+}
+
+/**
+ * This function loads a pixel data from a file. In order to load it we use SyncImageLoader utility.
+ * If loading succeeds returned PixelData object can be used to create a texture.
+ * Texture must be uploaded. In the end the texture must be set on the TextureSet object.
+ */
+TextureSet CubeRenderer::CreateTexture(std::string url)
+{
+  // Load image from file
+  PixelData pixels = Dali::Toolkit::SyncImageLoader::Load(url);
+
+  Texture texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
+  texture.Upload(pixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
+
+  // create TextureSet
+  auto textureSet = TextureSet::New();
+  textureSet.SetTexture(0, texture);
+
+  return textureSet;
+}
+
+/**
+ * Function creates renderer. It turns on depth test and depth write.
+ */
+Dali::Renderer CubeRenderer::CreateRenderer(TextureSet textures)
+{
+  CreateCubeGeometry();
+  Dali::Shader shader   = CreateShader();
+  Renderer     renderer = Renderer::New(gCubeGeometry, shader);
+  renderer.SetTextures(textures);
+
+  // Face culling is enabled to hide the backwards facing sides of the cube
+  // This is sufficient to render a single object; for more complex scenes depth-testing might be required
+  renderer.SetProperty(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::BACK);
+  return renderer;
+}
+
+/**
+ * Creates new actor and renderer.
+ */
+Actor CubeRenderer::CreateActor(Vector3 size, Vector4 color)
+{
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
+  // Mesh is 2x2x2, so halve the size
+  actor.SetProperty(Actor::Property::SIZE, Vector3(size.x, size.y, size.z) * 0.5f);
+  actor.SetProperty(Actor::Property::COLOR, color);
+  if(!gCubeTextureSet)
+  {
+    gCubeTextureSet = CreateTexture(TEXTURE_URL);
+  }
+  Renderer renderer = CreateRenderer(gCubeTextureSet);
+  actor.AddRenderer(renderer);
+  return actor;
+}
+
+Actor CubeRenderer::CreateActor(Vector3 size, std::string url)
+{
+  Actor actor = Actor::New();
+  actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+  actor.SetProperty(Actor::Property::POSITION, Vector3(0.0f, 0.0f, 0.0f));
+  actor.SetProperty(Actor::Property::SIZE, Vector3(size.x, size.y, size.z) * 0.5f);
+
+  TextureSet textures = CreateTexture(url);
+  Renderer   renderer = CreateRenderer(textures);
+  actor.AddRenderer(renderer);
+  return actor;
+}
diff --git a/examples/bullet-physics/cube-renderer.h b/examples/bullet-physics/cube-renderer.h
new file mode 100644 (file)
index 0000000..8e6c42d
--- /dev/null
@@ -0,0 +1,44 @@
+#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>
+using Dali::Actor;
+using Dali::Geometry;
+using Dali::Renderer;
+using Dali::Shader;
+using Dali::TextureSet;
+
+Dali::Shader CreateShader();
+
+class CubeRenderer
+{
+public:
+  static Dali::Geometry   CreateCubeGeometry();
+  static Dali::Renderer   CreateRenderer(TextureSet textures);
+  static Dali::TextureSet CreateTexture(std::string url);
+
+  /**
+   * The size can control whether this is a cube or a cuboid. Note, textures
+   * will be stretched on non-square sides.
+   */
+  static Dali::Actor CreateActor(Dali::Vector3 size, Dali::Vector4 color);
+  static Dali::Actor CreateActor(Dali::Vector3 size, std::string url);
+
+  static Geometry   gCubeGeometry;
+  static TextureSet gCubeTextureSet;
+};
diff --git a/examples/bullet-physics/dependencies.cmake b/examples/bullet-physics/dependencies.cmake
new file mode 100644 (file)
index 0000000..e938380
--- /dev/null
@@ -0,0 +1,5 @@
+TARGET_COMPILE_OPTIONS(${EXAMPLE}.example PUBLIC ${DALI_PHYSICS_3D_CFLAGS})
+TARGET_LINK_LIBRARIES(${EXAMPLE}.example ${DALI_PHYSICS_3D_LDFLAGS})
+MESSAGE(STATUS "Included dependencies for ${EXAMPLE}")
+MESSAGE(STATUS " Compile options: ${DALI_PHYSICS_3D_CFLAGS}")
+MESSAGE(STATUS " Link options: ${DALI_PHYSICS_3D_LDFLAGS}")
diff --git a/examples/bullet-physics/physics-demo-controller.cpp b/examples/bullet-physics/physics-demo-controller.cpp
new file mode 100644 (file)
index 0000000..4052f28
--- /dev/null
@@ -0,0 +1,649 @@
+/*
+ * 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-toolkit/dali-toolkit.h>
+#include <dali/dali.h>
+#include "dali-physics/public-api/physics-actor.h"
+#include "dali-physics/public-api/physics-adaptor.h"
+
+#include <dali/devel-api/adaptor-framework/key-devel.h>
+#include <dali/devel-api/events/hit-test-algorithm.h>
+
+#include <iostream>
+#include <string>
+
+#include <btBulletDynamicsCommon.h>
+
+#include "ball-renderer.h"
+#include "cube-renderer.h"
+
+using namespace Dali;
+using namespace Dali::Toolkit::Physics;
+
+namespace KeyModifier
+{
+enum Key
+{
+  CONTROL_L = DevelKey::DALI_KEY_CONTROL_LEFT,
+  CONTROL_R = DevelKey::DALI_KEY_CONTROL_RIGHT,
+  SHIFT_L   = 50,
+  SHIFT_R   = 62,
+  ALT_L     = 64,
+  ALT_R     = 108,
+  SUPER_L   = 133,
+  SUPER_R   = 134,
+  MENU      = 135,
+};
+}
+
+const std::string BRICK_WALL    = DEMO_IMAGE_DIR "/brick-wall.jpg";
+const std::string BALL_IMAGE    = DEMO_IMAGE_DIR "/blocks-ball.png";
+const std::string BRICK_URIS[4] = {
+  DEMO_IMAGE_DIR "/blocks-brick-1.png", DEMO_IMAGE_DIR "/blocks-brick-2.png", DEMO_IMAGE_DIR "/blocks-brick-3.png", DEMO_IMAGE_DIR "/blocks-brick-4.png"};
+
+class PhysicsDemoController : public ConnectionTracker
+{
+public:
+  PhysicsDemoController(Application& app)
+  : mApplication(app)
+  {
+    app.InitSignal().Connect(this, &PhysicsDemoController::OnInit);
+    app.TerminateSignal().Connect(this, &PhysicsDemoController::OnTerminate);
+  }
+
+  ~PhysicsDemoController() override
+  {
+  }
+
+  void OnInit(Application& application)
+  {
+    mWindow = application.GetWindow();
+    mWindow.ResizeSignal().Connect(this, &PhysicsDemoController::OnWindowResize);
+    mWindow.KeyEventSignal().Connect(this, &PhysicsDemoController::OnKeyEv);
+    Stage::GetCurrent().KeepRendering(30);
+    mWindow.SetBackgroundColor(Color::DARK_SLATE_GRAY);
+    Window::WindowSize windowSize = mWindow.GetSize();
+
+    auto cameraActor = mWindow.GetRenderTaskList().GetTask(0).GetCameraActor();
+    cameraActor[CameraActor::Property::FIELD_OF_VIEW] = Math::PI/2.5f; // 72 degrees
+    cameraActor[Actor::Property::POSITION_X] = 500;
+    float z = cameraActor[Actor::Property::POSITION_Z];
+    cameraActor[Actor::Property::POSITION_Z] = z-100;
+    cameraActor[CameraActor::Property::TARGET_POSITION] = Vector3();
+    cameraActor[CameraActor::Property::TYPE] = Camera::LOOK_AT_TARGET;
+    mPhysicsTransform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+    mPhysicsTransform.SetTranslation(Vector3(windowSize.GetWidth() * 0.5f,
+                                             windowSize.GetHeight() * 0.5f,
+                                             -100.0f));
+
+    mPhysicsAdaptor = PhysicsAdaptor::New(mPhysicsTransform, windowSize);
+    mPhysicsRoot    = mPhysicsAdaptor.GetRootActor();
+
+    mPhysicsRoot.TouchedSignal().Connect(this, &PhysicsDemoController::OnTouched);
+    mPhysicsRoot.WheelEventSignal().Connect(this, &PhysicsDemoController::OnWheel);
+
+    mWindow.Add(mPhysicsRoot);
+
+    auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
+    auto bulletWorld = scopedAccessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    bulletWorld->setGravity(btVector3(0, -200, 0));
+
+    CreateGround(scopedAccessor, windowSize);
+    mBrick = CreateLargeBrick(scopedAccessor);
+    mSelectedActor = mBrick;
+
+    CreateBall(scopedAccessor);
+    CreateBrickPyramid(scopedAccessor, windowSize);
+
+    mPhysicsAdaptor.CreateSyncPoint();
+  }
+
+  btRigidBody* CreateRigidBody(btDiscreteDynamicsWorld* bulletWorld, float mass, const btTransform& bulletTransform, btCollisionShape* shape)
+  {
+    bool      isDynamic = (mass != 0.0);
+    btVector3 localInertia(0.0f, 0.0f, 0.0f);
+    if(isDynamic)
+    {
+      shape->calculateLocalInertia(mass, localInertia);
+    }
+
+    auto*                                    motionState = new btDefaultMotionState(bulletTransform);
+    btRigidBody::btRigidBodyConstructionInfo rigidBodyConstructionInfo(mass, motionState, shape, localInertia);
+    auto*                                    body = new btRigidBody(rigidBodyConstructionInfo);
+
+    bulletWorld->addRigidBody(body);
+    return body;
+  }
+
+  void CreateGround(PhysicsAdaptor::ScopedPhysicsAccessorPtr& scopedAccessor, Dali::Window::WindowSize windowSize)
+  {
+    Dali::Vector3 size(2 * windowSize.GetWidth(), 10.f, 2 * windowSize.GetWidth());
+    Actor         groundActor = CubeRenderer::CreateActor(size, BRICK_WALL);
+
+    auto physicsActor = CreateBrick(scopedAccessor, groundActor, 0.0f, 0.1f, 0.9f, size);
+    physicsActor.AsyncSetPhysicsPosition(Vector3(0.0f, 0.5f * windowSize.GetHeight(), 0.0f));
+  }
+
+  void CreateBall(PhysicsAdaptor::ScopedPhysicsAccessorPtr& scopedAccessor)
+  {
+    const float BALL_MASS       = 1.0f;
+    const float BALL_RADIUS     = 50.0f;
+    const float BALL_ELASTICITY = 0.5f;
+    const float BALL_FRICTION   = 0.5f;
+    auto        actor           = BallRenderer::CreateActor(Vector3(BALL_RADIUS * 2, BALL_RADIUS * 2, BALL_RADIUS * 2), Dali::Color::WHITE);
+
+    btSphereShape* ball = new btSphereShape(BALL_RADIUS); // @todo Fix leak
+    btTransform    transform;
+    transform.setIdentity();
+
+    auto         bulletWorld = scopedAccessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    btRigidBody* body        = CreateRigidBody(bulletWorld, BALL_MASS, transform, ball);
+
+    body->setFriction(BALL_FRICTION);
+    body->setRestitution(BALL_ELASTICITY);
+
+    auto physicsActor = mPhysicsAdaptor.AddActorBody(actor, body);
+    actor.RegisterProperty("uBrightness", 0.0f);
+
+    physicsActor.AsyncSetPhysicsPosition(Vector3(0.0f, -400.0f, -150.0f));
+  }
+
+  PhysicsActor CreateBrick(PhysicsAdaptor::ScopedPhysicsAccessorPtr& scopedAccessor,
+                           Dali::Actor                               actor,
+                           float                                     mass,
+                           float                                     elasticity,
+                           float                                     friction,
+                           Vector3                                   size)
+  {
+    btVector3   halfExtents(size.width * 0.5f, size.height * 0.5f, size.depth * 0.5f);
+    btBoxShape* shape = new btBoxShape(halfExtents); // @todo Fix leak
+    btVector3   inertia;
+    inertia.setZero();
+    shape->calculateLocalInertia(mass, inertia);
+
+    btTransform startTransform;
+    startTransform.setIdentity();
+    auto         bulletWorld = scopedAccessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+    btRigidBody* body        = CreateRigidBody(bulletWorld, mass, startTransform, shape);
+
+    body->setFriction(friction);
+    body->setRestitution(elasticity);
+
+    actor.RegisterProperty("uBrightness", 0.0f);
+
+    auto physicsActor = mPhysicsAdaptor.AddActorBody(actor, body);
+    return physicsActor;
+  }
+
+  PhysicsActor CreateLargeBrick(PhysicsAdaptor::ScopedPhysicsAccessorPtr& scopedAccessor)
+  {
+    const float BRICK_MASS       = 1.0f;
+    const float BRICK_ELASTICITY = 0.1f;
+    const float BRICK_FRICTION   = 0.6f;
+    const int   BRICK_WIDTH      = 400;
+    const int   BRICK_HEIGHT     = 150;
+    const int   BRICK_DEPTH      = 200;
+
+    auto brick        = CubeRenderer::CreateActor(Vector3(BRICK_WIDTH, BRICK_HEIGHT, BRICK_DEPTH), Dali::Color::WHITE);
+    auto physicsActor = CreateBrick(scopedAccessor, brick, BRICK_MASS, BRICK_ELASTICITY, BRICK_FRICTION, Vector3(BRICK_WIDTH, BRICK_HEIGHT, BRICK_DEPTH));
+
+    physicsActor.AsyncSetPhysicsPosition(Vector3(0, 0, -300));
+    return physicsActor;
+  }
+
+  void CreateBrickPyramid(PhysicsAdaptor::ScopedPhysicsAccessorPtr& scopedAccessor, Dali::Window::WindowSize windowSize)
+  {
+    const float BRICK_MASS       = 1.0f;
+    const float BRICK_ELASTICITY = 0.1f;
+    const float BRICK_FRICTION   = 0.6f;
+    const int   BRICK_WIDTH      = 60;
+    const int   BRICK_HEIGHT     = 30;
+    const int   BRICK_DEPTH      = 30;
+    const int   BRICK_GAP        = 12;
+
+    Dali::Vector4 colors[5] = {Dali::Color::AQUA_MARINE, Dali::Color::DARK_SEA_GREEN, Dali::Color::BLUE_VIOLET, Dali::Color::MISTY_ROSE, Dali::Color::ORCHID};
+
+    int numberOfRows = 10;
+    int oY           = -(1 + numberOfRows) * (BRICK_HEIGHT + BRICK_GAP);
+    for(int i = 0; i < numberOfRows; ++i)
+    {
+      int   w  = (i + 1) * BRICK_WIDTH + i * BRICK_GAP;
+      float oX = w * -0.5f;
+      for(int j = 0; j < i + 1; ++j)
+      {
+        auto brick        = CubeRenderer::CreateActor(Vector3(BRICK_WIDTH, BRICK_HEIGHT, BRICK_DEPTH), colors[(i + j) % 5]);
+        auto physicsActor = CreateBrick(scopedAccessor, brick, BRICK_MASS, BRICK_ELASTICITY, BRICK_FRICTION, Vector3(BRICK_WIDTH, BRICK_HEIGHT, BRICK_DEPTH));
+
+        physicsActor.AsyncSetPhysicsPosition(Vector3(oX + j * (BRICK_WIDTH + BRICK_GAP), oY + i * (BRICK_HEIGHT + BRICK_GAP), -300.0f));
+        // Create slight rotation offset to trigger automatic collapse
+        auto axis = Vector3(Random::Range(-0.2f, 0.2f), // Roughly the z axis
+                            Random::Range(-0.2f, 0.2f),
+                            Random::Range(0.8f, 1.2f));
+        axis.Normalize();
+        physicsActor.AsyncSetPhysicsRotation(Quaternion(Radian(Random::Range(-0.3f, 0.3f)), axis));
+      }
+    }
+  }
+
+  void HighlightBody(btRigidBody* body, bool highlight)
+  {
+    auto physicsActor = mPhysicsAdaptor.GetPhysicsActor(body);
+    if(physicsActor)
+    {
+      Actor actor = mPhysicsAdaptor.GetRootActor().FindChildById(physicsActor.GetId());
+      if(actor)
+      {
+        actor["uBrightness"] = highlight ? 1.0f : 0.0f;
+      }
+    }
+  }
+
+  void OnTerminate(Application& application)
+  {
+    UnparentAndReset(mPhysicsRoot);
+  }
+
+  void OnWindowResize(Window window, Window::WindowSize newSize)
+  {
+    Vector2 size(newSize.GetWidth(), newSize.GetHeight());
+    window.GetRenderTaskList().GetTask(0).GetCameraActor().SetPerspectiveProjection(size);
+    mPhysicsAdaptor.SetTransformAndSize(mPhysicsTransform, newSize);
+  }
+
+  bool OnTouched(Dali::Actor actor, const Dali::TouchEvent& touch)
+  {
+    static enum {
+      None,
+      MoveCameraXZ,
+      MovePivot,
+    } state = None;
+
+    const float MOUSE_CLAMPING(30.0f);
+    const float TAU(0.001f);
+    //static float cameraY{0.0f};
+    auto    renderTask   = mWindow.GetRenderTaskList().GetTask(0);
+    auto    screenCoords = touch.GetScreenPosition(0);
+    Vector3 origin, direction;
+    Dali::HitTestAlgorithm::BuildPickingRay(renderTask, screenCoords, origin, direction);
+
+    switch(state)
+    {
+      case None:
+      {
+        if(touch.GetState(0) == Dali::PointState::STARTED)
+        {
+          if(mCtrlDown)
+          {
+            state = MoveCameraXZ;
+            // local to top left
+            //cameraY = touch.GetLocalPosition(0).y;
+            // Could move on fixed plane, e.g. y=0.
+            // position.Y corresponds to a z value depending on perspective
+            // position.X scales to an x value depending on perspective
+          }
+          else
+          {
+            state               = MovePivot;
+            auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
+            auto bulletWorld    = scopedAccessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+
+            Vector3 localPivot;
+            Vector3 rayPhysicsOrigin;
+            Vector3 rayPhysicsEnd;
+            mPhysicsAdaptor.BuildPickingRay(origin, direction, rayPhysicsOrigin, rayPhysicsEnd);
+            auto body = scopedAccessor->HitTest(rayPhysicsOrigin, rayPhysicsEnd, localPivot, mOldPickingDistance);
+            if(!body.Empty())
+            {
+              mPickedBody = body.Get<btRigidBody*>();
+              HighlightBody(mPickedBody, true);
+              mSelectedActor = mPhysicsAdaptor.GetPhysicsActor(mPickedBody);
+
+              mPickedSavedState = mPickedBody->getActivationState();
+              mPickedBody->setActivationState(DISABLE_DEACTIVATION);
+
+              btVector3 pivot(localPivot.x, localPivot.y, localPivot.z);
+              auto      p2p = new btPoint2PointConstraint(*mPickedBody, pivot);
+              bulletWorld->addConstraint(p2p, true);
+              p2p->m_setting.m_impulseClamp = MOUSE_CLAMPING;
+              p2p->m_setting.m_tau          = TAU;
+
+              mPickedConstraint = p2p;
+            }
+          }
+        }
+        break;
+      }
+      case MovePivot:
+      {
+        if(touch.GetState(0) == Dali::PointState::MOTION)
+        {
+          if(mPickedBody && mPickedConstraint)
+          {
+            if(!mShiftDown)
+            {
+              // Move point in XY plane, projected into scene
+              auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor(); // Ensure we get a lock
+
+              Vector3                  newPosition = mPhysicsAdaptor.ProjectPoint(origin, direction, mOldPickingDistance);
+              btPoint2PointConstraint* p2p         = static_cast<btPoint2PointConstraint*>(mPickedConstraint);
+              if(p2p)
+              {
+                // @todo Add inline memory cast... (only viable if physics uses floats not doubles)
+                p2p->setPivotB(btVector3(newPosition.x, newPosition.y, newPosition.z));
+              }
+            }
+            // @todo Add code to move objects in XZ plane
+          }
+        }
+        else if(touch.GetState(0) == Dali::PointState::FINISHED ||
+                touch.GetState(0) == Dali::PointState::INTERRUPTED)
+        {
+          if(mPickedConstraint)
+          {
+            auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor(); // Ensure we get a lock
+            auto bulletWorld    = scopedAccessor->GetNative().Get<btDiscreteDynamicsWorld*>();
+            HighlightBody(mPickedBody, false);
+            mPickedBody->forceActivationState(mPickedSavedState);
+            mPickedBody->activate();
+            bulletWorld->removeConstraint(mPickedConstraint);
+
+            mPickedConstraint = nullptr;
+            mPickedBody       = nullptr;
+          }
+          state = None;
+        }
+        break;
+      }
+      case MoveCameraXZ:
+      {
+        if(touch.GetState(0) == Dali::PointState::MOTION)
+        {
+          // Move camera in XZ plane
+          //float y = cameraY; // touch point in Y. Move camera in an XZ plane on this point.
+        }
+        else if(touch.GetState(0) == Dali::PointState::FINISHED ||
+                touch.GetState(0) == Dali::PointState::INTERRUPTED)
+        {
+          state = None;
+        }
+        break;
+      }
+    }
+
+    static int reduceSpam = 0;
+    ++reduceSpam;
+    if(reduceSpam == 30)
+    {
+      reduceSpam = 0;
+      std::cout << "Dali scrnpos:" << screenCoords << "\nDali pos:    " << mBrick.GetActorPosition() << "\nDali rot:    " << mBrick.GetActorRotation() << "\nPhys pos:    " << mBrick.GetPhysicsPosition() << "\nPhys rot:    " << mBrick.GetPhysicsRotation() << "\n";
+    }
+
+    Stage::GetCurrent().KeepRendering(30.0f);
+
+    return true;
+  }
+
+  bool OnWheel(Actor actor, const WheelEvent& event)
+  {
+    // Move camera along it's fwd axis
+    auto            renderTask = mWindow.GetRenderTaskList().GetTask(0);
+    auto            camera     = renderTask.GetCameraActor();
+    static uint32_t lastTime   = 0;
+
+    int32_t delta = event.GetDelta();
+    if(lastTime > 0)
+    {
+      uint32_t timeDiff = event.GetTime() - lastTime;
+      ++timeDiff;       // Can be zero.
+      if(timeDiff < 50) // When it's less than some threshold
+      {
+        // Scale delta by speed (range: 0-6)
+        float scale = (50.0f - timeDiff) * 6.0f / 50.0f; // smaller times = bigger factor
+        scale *= (scale * scale);                        // Cube it. (Max - 360)
+        scale = std::max(1.0f, scale);
+        delta *= scale;
+      }
+    }
+    lastTime = event.GetTime();
+
+    float z = camera[Actor::Property::POSITION_Z];
+    z += delta;
+    camera[Actor::Property::POSITION_Z] = z;
+    return true;
+  }
+
+  void OnKeyEv(const Dali::KeyEvent& event)
+  {
+    if(event.GetState() == KeyEvent::DOWN)
+    {
+      switch(event.GetKeyCode())
+      {
+        case KeyModifier::CONTROL_L:
+        case KeyModifier::CONTROL_R:
+        {
+          mCtrlDown = true;
+          break;
+        }
+        case KeyModifier::ALT_L:
+        case KeyModifier::ALT_R:
+        {
+          mAltDown = true;
+          break;
+        }
+        case KeyModifier::SHIFT_L:
+        case KeyModifier::SHIFT_R:
+        {
+          mShiftDown = true;
+          break;
+        }
+        default:
+        {
+          if(IsKey(event, Dali::DALI_KEY_ESCAPE) || IsKey(event, Dali::DALI_KEY_BACK))
+          {
+            mApplication.Quit();
+          }
+          else if(!event.GetKeyString().compare(" "))
+          {
+            if(mIntegrationState == PhysicsAdaptor::IntegrationState::ON)
+            {
+              mIntegrationState = PhysicsAdaptor::IntegrationState::OFF;
+            }
+            else
+            {
+              mIntegrationState = PhysicsAdaptor::IntegrationState::ON;
+            }
+            mPhysicsAdaptor.SetIntegrationState(mIntegrationState);
+          }
+          else if(!event.GetKeyString().compare("m"))
+          {
+            if(mDebugState == PhysicsAdaptor::DebugState::ON)
+            {
+              mDebugState = PhysicsAdaptor::DebugState::OFF;
+            }
+            else
+            {
+              mDebugState = PhysicsAdaptor::DebugState::ON;
+              if(!mPhysicsDebugLayer)
+              {
+                mPhysicsDebugLayer = mPhysicsAdaptor.CreateDebugLayer(mWindow);
+              }
+            }
+            mPhysicsAdaptor.SetDebugState(mDebugState);
+          }
+          else if(!event.GetKeyString().compare("w"))
+          {
+            Vector3 pos = mSelectedActor.GetActorPosition();
+            mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(0, 10, 0));
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("s"))
+          {
+            Vector3 pos = mSelectedActor.GetActorPosition();
+            mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(0, -10, 0));
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("a"))
+          {
+            Vector3 pos = mSelectedActor.GetActorPosition();
+            mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(-10, 0, 0));
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("d"))
+          {
+            Vector3 pos = mSelectedActor.GetActorPosition();
+            mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(10, 0, 0));
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("q"))
+          {
+            Quaternion quat = mSelectedActor.GetActorRotation();
+            quat *= Quaternion(Radian(-0.1f), Vector3::ZAXIS);
+            mSelectedActor.AsyncSetPhysicsRotation(quat);
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("e"))
+          {
+            Quaternion quat = mSelectedActor.GetActorRotation();
+            quat *= Quaternion(Radian(0.1f), Vector3::ZAXIS);
+            mSelectedActor.AsyncSetPhysicsRotation(quat);
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("z"))
+          {
+            Quaternion quat = mSelectedActor.GetActorRotation();
+            quat *= Quaternion(Radian(-0.1f), Vector3::YAXIS);
+            mSelectedActor.AsyncSetPhysicsRotation(quat);
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("x"))
+          {
+            Quaternion quat = mSelectedActor.GetActorRotation();
+            quat *= Quaternion(Radian(0.1f), Vector3::YAXIS);
+            mSelectedActor.AsyncSetPhysicsRotation(quat);
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("c"))
+          {
+            Quaternion quat = mSelectedActor.GetActorRotation();
+            quat *= Quaternion(Radian(-0.1f), Vector3::XAXIS);
+            mSelectedActor.AsyncSetPhysicsRotation(quat);
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("v"))
+          {
+            Quaternion quat = mSelectedActor.GetActorRotation();
+            quat *= Quaternion(Radian(0.1f), Vector3::XAXIS);
+            mSelectedActor.AsyncSetPhysicsRotation(quat);
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->activate(true); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          else if(!event.GetKeyString().compare("p"))
+          {
+            mSelectedActor.AsyncSetPhysicsPosition(Vector3::ZERO);
+            mSelectedActor.AsyncSetPhysicsRotation(Quaternion(Radian(0.001f), Vector3::XAXIS));
+
+            // Example of calling specific function asyncronously:
+            btRigidBody* body = mSelectedActor.GetBody().Get<btRigidBody*>();
+            mPhysicsAdaptor.Queue([body]() { body->clearForces(); });
+            mPhysicsAdaptor.CreateSyncPoint();
+          }
+          break;
+        }
+      }
+    }
+    else if(event.GetState() == KeyEvent::UP)
+    {
+      switch(event.GetKeyCode())
+      {
+        case KeyModifier::CONTROL_L:
+        case KeyModifier::CONTROL_R:
+        {
+          mCtrlDown = false;
+          break;
+        }
+        case KeyModifier::ALT_L:
+        case KeyModifier::ALT_R:
+        {
+          mAltDown = false;
+          break;
+        }
+        case KeyModifier::SHIFT_L:
+        case KeyModifier::SHIFT_R:
+        {
+          mShiftDown = false;
+          break;
+        }
+      }
+    }
+
+    static int reduceSpam = 0;
+    ++reduceSpam;
+    if(reduceSpam == 2)
+    {
+      reduceSpam = 0;
+      std::cout << "Dali pos: " << mBrick.GetActorPosition() << "\nDali rot: " << mBrick.GetActorRotation() << "\nPhys pos: " << mBrick.GetPhysicsPosition() << "\nPhys rot: " << mBrick.GetPhysicsRotation() << "\n";
+    }
+  }
+
+private:
+  Application& mApplication;
+  Window       mWindow;
+
+  Matrix         mPhysicsTransform;
+  PhysicsAdaptor mPhysicsAdaptor;
+  Actor          mPhysicsRoot;
+  Layer          mPhysicsDebugLayer;
+  PhysicsActor   mBrick;
+  PhysicsActor   mSelectedActor;
+
+  btRigidBody*                     mPickedBody;
+  float                            mOldPickingDistance{0.0f};
+  int                              mPickedSavedState{0};
+  btTypedConstraint*               mPickedConstraint{nullptr};
+  PhysicsAdaptor::IntegrationState mIntegrationState{PhysicsAdaptor::IntegrationState::ON};
+  PhysicsAdaptor::DebugState       mDebugState{PhysicsAdaptor::DebugState::OFF};
+
+  bool mCtrlDown{false};
+  bool mAltDown{false};
+  bool mShiftDown{false};
+};
+
+int main(int argc, char** argv)
+{
+  Application           application = Application::New(&argc, &argv);
+  PhysicsDemoController controller(application);
+  application.MainLoop();
+  return 0;
+}
@@ -24,6 +24,9 @@ mediump vec3 redistribute_rgb(mediump vec3 color)
 void main()
 {
   mediump vec4 texColor = texture2D( uTexture, vTexCoord );
-  mediump vec3 pcol=texColor.rgb*(1.0+uBrightness);
-  gl_FragColor = vec4( redistribute_rgb(pcol), texColor.a);
-}
\ No newline at end of file
+  //mediump vec4 texColor = vec4(0.5,0.5,0.5,1.0);
+  //gl_FragColor = vec4(texColor.rgb, 1.0);
+
+  mediump vec3 pcol=vec3(vIllumination.rgb * texColor.rgb)*(1.0+uBrightness);
+  gl_FragColor = vec4( redistribute_rgb(pcol), 1.0);
+}
@@ -1,5 +1,5 @@
 attribute mediump vec3 aPosition;  // DALi shader builtin
-//attribute mediump vec2 aTexCoord;  // DALi shader builtin
+attribute mediump vec2 aTexCoord;  // DALi shader builtin
 uniform   mediump mat4 uMvpMatrix; // DALi shader builtin
 uniform   mediump mat4 uViewMatrix; // DALi shader builtin
 uniform   mediump mat4 uModelView; // DALi shader builtin
@@ -21,6 +21,6 @@ void main()
   float lightDiffuse = max(dot(vectorToLight, normal), 0.0);
 
   vIllumination = vec3(lightDiffuse * 0.5 + 0.5);
-  vTexCoord = aPosition.xy*2.0;
+  vTexCoord = aTexCoord;
   gl_Position = uMvpMatrix * vertexPosition;
-}
\ No newline at end of file
+}
index 78adff1..287fa27 100644 (file)
@@ -1,6 +1,11 @@
 # Chipmunk Physics Example
 
 This is an example showing how to use Chipmunk2D physics library to create and control physics objects in DALi.
-It creates a ball and a pyramid brick wall which can be moved using touch and key events.
+It creates a set of balls which act under gravity
 
 ![](./chipmunk.gif)
+
+"wasd" keys move the last touched actor up/down/left/right.
+"qe" keys rotate the last touched actor in Z axis
+"p" key resets the position/forces on the last touched actor to the origin
+Space key toggles the integration state.
diff --git a/examples/chipmunk-physics/dependencies.cmake b/examples/chipmunk-physics/dependencies.cmake
new file mode 100644 (file)
index 0000000..b30b385
--- /dev/null
@@ -0,0 +1,5 @@
+TARGET_COMPILE_OPTIONS(${EXAMPLE}.example PUBLIC ${DALI_PHYSICS_2D_CFLAGS})
+TARGET_LINK_LIBRARIES(${EXAMPLE}.example ${DALI_PHYSICS_2D_LDFLAGS})
+MESSAGE(STATUS "Included dependencies for ${EXAMPLE}")
+MESSAGE(STATUS " Compile options: ${DALI_PHYSICS_2D_CFLAGS}")
+MESSAGE(STATUS " Link options: ${DALI_PHYSICS_2D_LDFLAGS}")
diff --git a/examples/chipmunk-physics/frame-callback.cpp b/examples/chipmunk-physics/frame-callback.cpp
deleted file mode 100644 (file)
index 129b6d7..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 "frame-callback.h"
-#include <dali/devel-api/threading/mutex.h>
-#include <dali/devel-api/update/update-proxy.h>
-#include <dali/integration-api/debug.h>
-#include <dali/public-api/math/vector3.h>
-#include "physics-impl.h"
-
-using Dali::Quaternion;
-using Dali::Vector3;
-
-#if defined(DEBUG_ENABLED)
-extern Debug::Filter* gPhysicsDemo;
-#endif
-
-FrameCallback::FrameCallback(PhysicsImpl& physicsImpl)
-: mPhysicsImpl(physicsImpl)
-{
-}
-
-bool FrameCallback::Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds)
-{
-  static int numCalls = 0;
-
-  numCalls++;
-  if(numCalls % 30 == 0)
-  {
-    DALI_LOG_INFO(gPhysicsDemo, Debug::Concise, "Physics frame update\n");
-  }
-
-  Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
-  static float            frameTime = 0;
-  frameTime += elapsedSeconds;
-  do
-  {
-    mPhysicsImpl.Integrate(mPhysicsTimeStep);
-    frameTime -= mPhysicsTimeStep;
-  } while(frameTime > 0);
-
-  for(auto&& actor : mPhysicsImpl.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);
-  }
-
-  return true;
-}
diff --git a/examples/chipmunk-physics/frame-callback.h b/examples/chipmunk-physics/frame-callback.h
deleted file mode 100644 (file)
index c45f019..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef PHYSICS_DEMO_FRAME_CALLBACK_H
-#define PHYSICS_DEMO_FRAME_CALLBACK_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/public-api/adaptor-framework/window.h>
-#include <dali/devel-api/update/frame-callback-interface.h>
-#include <map>
-#include <chrono>
-
-class PhysicsImpl;
-
-class FrameCallback : public Dali::FrameCallbackInterface
-{
-public:
-  /**
-   * Constructor
-   */
-  explicit FrameCallback(PhysicsImpl& physicsImpl);
-
-  /**
-   * Set the physics time step
-   * @param timeStep (in seconds)
-   */
-  void SetPhysicsTimeStep(float timeStep)
-  {
-    mPhysicsTimeStep = timeStep;
-  }
-
-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;
-
-private: // Member variables
-  PhysicsImpl& mPhysicsImpl;
-  float mPhysicsTimeStep{1.0/180.0};
-};
-
-#endif //PHYSICS_DEMO_FRAME_CALLBACK_H
diff --git a/examples/chipmunk-physics/physics-actor.cpp b/examples/chipmunk-physics/physics-actor.cpp
deleted file mode 100644 (file)
index 07f14a3..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include "physics-actor.h"
-#include "physics-impl.h"
-#include <dali/public-api/common/constants.h>
-#include <dali/public-api/math/vector3.h>
-#include <dali/public-api/math/quaternion.h>
-
-using Dali::Vector3;
-using Dali::Quaternion;
-using Dali::Radian;
-
-void PhysicsActor::ClearForces()
-{
-  printf("Not Implemented\n");
-  //mBody->clearForces();
-  // No similar API
-}
-
-Dali::Vector3 PhysicsActor::GetPhysicsPosition()
-{
-  cpVect cpPosition = cpBodyGetPosition(mBody);
-  return Vector3(cpPosition.x, cpPosition.y, 0.0f);
-}
-
-void PhysicsActor::SetPhysicsPosition(Dali::Vector3 actorPosition)
-{
-  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
-  Vector3 physicsPosition = mImpl->TranslateToPhysicsSpace(actorPosition);
-  cpBodySetPosition(mBody, cpv(physicsPosition.x, physicsPosition.y));
-}
-
-void PhysicsActor::SetPhysicsVelocity(Dali::Vector3 actorVelocity)
-{
-  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
-  Vector3 physicsVelocity = mImpl->ConvertVectorToPhysicsSpace(actorVelocity);
-  cpBodySetVelocity(mBody, cpv(physicsVelocity.x, physicsVelocity.y));
-}
-
-void PhysicsActor::SetPhysicsAngularVelocity(Dali::Vector3 velocity)
-{
-  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
-  printf("Not Implemented\n");
-  //mBody->setAngularVelocity(btVector3(velocity.x, velocity.y, velocity.z));
-}
-
-Quaternion PhysicsActor::GetPhysicsRotation()
-{
-  return Quaternion{};
-}
-
-void PhysicsActor::SetPhysicsRotation(Dali::Quaternion rotation)
-{
-  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
-
-  Vector3 axis;
-  Radian angle;
-  rotation.ToAxisAngle(axis, angle);
-
-  //btQuaternion orn = btQuaternion(btVector3(axis.x, -axis.y, axis.z), btScalar(float(-angle)));
-  //btTransform& transform = mBody->getWorldTransform();
-  //transform.setRotation(orn);
-  printf("Not Implemented\n");
-}
-
-
-Vector3 PhysicsActor::GetActorPosition()
-{
-  cpVect cpPosition = cpBodyGetPosition(mBody);
-  return mImpl->TranslateFromPhysicsSpace(Vector3(cpPosition.x, cpPosition.y, 0.0f));
-}
-
-Vector3 PhysicsActor::GetActorVelocity()
-{
-  cpVect cpVelocity = cpBodyGetVelocity(mBody);
-  return mImpl->ConvertVectorFromPhysicsSpace(Vector3(cpVelocity.x, cpVelocity.y, 0.0f));
-}
-
-Quaternion PhysicsActor::GetActorRotation()
-{
-  cpFloat angle = cpBodyGetAngle(mBody);
-  return Quaternion(Radian(angle), -Vector3::ZAXIS);
-}
diff --git a/examples/chipmunk-physics/physics-actor.h b/examples/chipmunk-physics/physics-actor.h
deleted file mode 100644 (file)
index aeaa612..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-#ifndef DALI_PHYSICS_DEMO_PHYSICS_ACTOR_H
-#define DALI_PHYSICS_DEMO_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.
- *
- */
-
-#include <chipmunk/chipmunk.h>
-#include <cstdint>
-#include <dali/dali.h>
-#include <iosfwd>
-
-// Forward declarations
-class PhysicsImpl;
-
-/**
- * Class that associates an actor with a physics body. (Initially, rigid body)
- */
-class PhysicsActor
-{
-public:
-  PhysicsActor() = default;
-  PhysicsActor(Dali::Actor& actor, cpBody* body, PhysicsImpl* impl, Dali::Property::Index brightnessId)
-  : mImpl(impl),
-    mActorId(actor.GetProperty<int>(Dali::Actor::Property::ID)),
-    mBody(body),
-    mBrightnessIndex(brightnessId)
-  {
-    cpBodySetUserData(mBody, this);
-  }
-
-  PhysicsActor(const PhysicsActor& rhs)=delete;
-  PhysicsActor& operator=(const PhysicsActor& rhs)=delete;
-
-  PhysicsActor(const PhysicsActor&& rhs)
-  {
-    if(this != &rhs)
-    {
-      mImpl = rhs.mImpl;
-      mActorId = rhs.mActorId;
-      mBody = rhs.mBody;
-      cpBodySetUserData(mBody, this);
-      mBrightnessIndex = rhs.mBrightnessIndex;
-    }
-  }
-
-  PhysicsActor& operator=(const PhysicsActor&& rhs)
-  {
-    if(this != &rhs)
-    {
-      mActorId = rhs.mActorId;
-      mBody    = rhs.mBody;
-      mImpl = rhs.mImpl;
-      mBrightnessIndex = rhs.mBrightnessIndex;
-      cpBodySetUserData(mBody, this);
-    }
-    return *this;
-  }
-
-  uint32_t GetId()
-  {
-    return mActorId;
-  }
-
-  cpBody* GetBody()
-  {
-    return mBody;
-  }
-
-  Dali::Property::Index GetBrightnessIndex()
-  {
-    return mBrightnessIndex;
-  }
-
-  Dali::Vector3 GetPhysicsPosition();
-  Dali::Quaternion GetPhysicsRotation();
-
-  void SetPhysicsPosition(Dali::Vector3 actorPosition);
-  void SetPhysicsVelocity(Dali::Vector3 actorVelocity);
-  void SetPhysicsAngularVelocity(Dali::Vector3 actorVelocity);
-  void SetPhysicsRotation(Dali::Quaternion actorRotation);
-  Dali::Vector3 GetActorPosition();
-  Dali::Vector3 GetActorVelocity();
-  Dali::Quaternion GetActorRotation();
-  void ClearForces();
-
-private:
-  PhysicsImpl* mImpl{nullptr};
-  uint32_t mActorId{0};
-  cpBody* mBody{nullptr};
-  Dali::Property::Index mBrightnessIndex{Dali::Property::INVALID_INDEX};
-};
-
-#endif // DALI_PHYSICS_DEMO_PHYSICS_ACTOR_H
index 377322a..6baf71b 100644 (file)
@@ -15,7 +15,7 @@
  */
 
 #include <dali-toolkit/dali-toolkit.h>
-#include <dali/dali.h>
+#include <dali-physics/dali-physics.h>
 
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
 
 #include <iostream>
 #include <string>
-
-#include "generated/rendering-textured-shape-frag.h"
-#include "generated/rendering-textured-shape-vert.h"
-#include "physics-actor.h"
-#include "physics-impl.h"
+#include <chipmunk/chipmunk.h>
 
 using namespace Dali;
+using namespace Dali::Toolkit::Physics;
 
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gPhysicsDemo = Debug::Filter::New(Debug::Concise, false, "LOG_PHYSICS_EXAMPLE");
 #endif
 
+#define GRABBABLE_MASK_BIT (1u << 31)
+cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT};
+
 namespace KeyModifier
 {
 enum Key
@@ -84,25 +84,39 @@ public:
     mWindow.SetBackgroundColor(Color::DARK_SLATE_GRAY);
     Window::WindowSize windowSize = mWindow.GetSize();
 
-    mPhysicsRoot = mPhysicsImpl.Initialize(mWindow);
+    // Map Physics space (origin bottom left, +ve Y up)
+    // to DALi space (origin center, +ve Y down)
+    mPhysicsTransform.SetIdentityAndScale(Vector3(1.0f, -1.0f, 1.0f));
+    mPhysicsTransform.SetTranslation(Vector3(windowSize.GetWidth() * 0.5f,
+                                             windowSize.GetHeight() * 0.5f,
+                                             0.0f));
+
+    mPhysicsAdaptor = PhysicsAdaptor::New(mPhysicsTransform, windowSize);
+    mPhysicsRoot = mPhysicsAdaptor.GetRootActor();
     mPhysicsRoot.TouchedSignal().Connect(this, &PhysicsDemoController::OnTouched);
 
     mWindow.Add(mPhysicsRoot);
 
+    auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
+    cpSpace* space = scopedAccessor->GetNative().Get<cpSpace*>();
+
+    CreateBounds(space, windowSize);
     // Ball area = 2*PI*26^2 ~= 6.28*26*26 ~= 5400
     // Fill quarter of the screen...
     int numBalls = 10 + windowSize.GetWidth() * windowSize.GetHeight() / 20000;
-
     for(int i = 0; i < numBalls; ++i)
     {
-      CreateBall();
+      CreateBall(space);
     }
 
     // For funky mouse drag
-    mMouseBody = mPhysicsImpl.AddMouseBody();
+    mMouseBody = cpBodyNewKinematic(); // Mouse actor is a kinematic body that is not integrated
+
+    // Process any async queued methods next frame
+    mPhysicsAdaptor.CreateSyncPoint();
   }
 
-  void CreateBall()
+  void CreateBall(cpSpace* space)
   {
     const float BALL_MASS       = 10.0f;
     const float BALL_RADIUS     = 26.0f;
@@ -111,13 +125,81 @@ public:
 
     auto ball = Toolkit::ImageView::New(BALL_IMAGE);
 
-    auto&              physicsBall = mPhysicsImpl.AddBall(ball, BALL_MASS, BALL_RADIUS, BALL_ELASTICITY, BALL_FRICTION);
+    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);
+
+    ball.RegisterProperty("uBrightness", 0.0f);
+
+    PhysicsActor physicsBall = mPhysicsAdaptor.AddActorBody(ball, body);
+
     Window::WindowSize windowSize  = mWindow.GetSize();
-    const float        s           = BALL_RADIUS;
-    const float        fw          = windowSize.GetWidth() - BALL_RADIUS;
-    const float        fh          = windowSize.GetHeight() - BALL_RADIUS;
-    physicsBall.SetPhysicsPosition(Vector3(Random::Range(s, fw), Random::Range(s, fh), 0.0f));
-    physicsBall.SetPhysicsVelocity(Vector3(Random::Range(-100.0, 100.0), Random::Range(-100.0, 100.0), 0.0f));
+
+    const float        fw          = 0.5f*(windowSize.GetWidth() - BALL_RADIUS);
+    const float        fh          = 0.5f*(windowSize.GetHeight() - BALL_RADIUS);
+
+    // Example of setting physics property on update thread
+    physicsBall.AsyncSetPhysicsPosition(Vector3(Random::Range(-fw, fw), Random::Range(-fh, fh), 0.0f));
+
+    // Example of queuing a chipmunk method to run on the update thread
+    mPhysicsAdaptor.Queue([body](){
+      cpBodySetVelocity(body, cpv(Random::Range(-100.0, 100.0), Random::Range(-100.0, 100.0)));
+    });
+  }
+
+  void CreateBounds(cpSpace* space, Window::WindowSize size)
+  {
+    // We're working in physics space here - coords are: origin: bottom left, +ve Y: up
+    int xBound = size.GetWidth();
+    int yBound = size.GetHeight();
+
+    cpBody* staticBody = cpSpaceGetStaticBody(space);
+
+    if(mLeftBound)
+    {
+      cpSpaceRemoveShape(space, mLeftBound);
+      cpSpaceRemoveShape(space, mRightBound);
+      cpSpaceRemoveShape(space, mTopBound);
+      cpSpaceRemoveShape(space, mBottomBound);
+      cpShapeFree(mLeftBound);
+      cpShapeFree(mRightBound);
+      cpShapeFree(mTopBound);
+      cpShapeFree(mBottomBound);
+    }
+    mLeftBound   = AddBound(space, staticBody, cpv(0, 0), cpv(0, yBound));
+    mRightBound  = AddBound(space, staticBody, cpv(xBound, 0), cpv(xBound, yBound));
+    mTopBound    = AddBound(space, staticBody, cpv(0, 0), cpv(xBound, 0));
+    mBottomBound = AddBound(space, staticBody, cpv(0, yBound), cpv(xBound, yBound));
+  }
+
+  cpShape* AddBound(cpSpace* space, cpBody* staticBody, cpVect start, cpVect end)
+  {
+    cpShape* shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, start, end, 0.0f));
+    cpShapeSetElasticity(shape, 1.0f);
+    cpShapeSetFriction(shape, 1.0f);
+
+    cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER);
+    return shape;
+  }
+
+  void MoveMouseBody(cpBody* mouseBody, Vector3 position)
+  {
+    cpVect cpPosition = cpv(position.x, position.y);
+    cpVect newPoint   = cpvlerp(cpBodyGetPosition(mouseBody), cpPosition, 0.25f);
+    cpBodySetVelocity(mouseBody, cpvmult(cpvsub(newPoint, cpBodyGetPosition(mouseBody)), 60.0f));
+    cpBodySetPosition(mouseBody, newPoint);
+  }
+
+  cpConstraint* AddPivotJoint(cpSpace* space, cpBody* body1, cpBody* body2, Vector3 localPivot)
+  {
+    cpVect        pivot{localPivot.x, localPivot.y};
+    cpConstraint* joint = cpPivotJointNew2(body2, body1, cpvzero, pivot);
+    cpConstraintSetMaxForce(joint, 50000.0f); // Magic numbers for mouse feedback.
+    cpConstraintSetErrorBias(joint, cpfpow(1.0f - 0.15f, 60.0f));
+    cpConstraint* constraint = cpSpaceAddConstraint(space, joint);
+    return constraint; // Constraint & joint are the same...
   }
 
   void OnTerminate(Application& application)
@@ -127,21 +209,24 @@ public:
 
   void OnWindowResize(Window window, Window::WindowSize newSize)
   {
-    mPhysicsImpl.CreateWorldBounds(newSize);
+    auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
+    cpSpace* space = scopedAccessor->GetNative().Get<cpSpace*>();
+
+    CreateBounds(space, newSize);
   }
 
   bool OnTouched(Dali::Actor actor, const Dali::TouchEvent& touch)
   {
     static enum {
       None,
-      MoveCameraXZ,
       MovePivot,
     } state = None;
 
-    auto    renderTask   = mWindow.GetRenderTaskList().GetTask(0);
-    auto    screenCoords = touch.GetScreenPosition(0);
-    Vector3 origin, direction;
-    Dali::HitTestAlgorithm::BuildPickingRay(renderTask, screenCoords, origin, direction);
+    auto renderTask   = mWindow.GetRenderTaskList().GetTask(0);
+    auto screenCoords = touch.GetScreenPosition(0);
+    // In this demo, physics space is equivalent to screen space with y inverted
+    auto windowSize = mWindow.GetSize();
+    Vector3 rayPhysicsOrigin(screenCoords.x, windowSize.GetHeight() - screenCoords.y, 0.0f);
 
     switch(state)
     {
@@ -149,30 +234,23 @@ public:
       {
         if(touch.GetState(0) == Dali::PointState::STARTED)
         {
-          if(mCtrlDown)
-          {
-            state = MoveCameraXZ;
-            // local to top left
-            //cameraY = touch.GetLocalPosition(0).y;
-            // Could move on fixed plane, e.g. y=0.
-            // position.Y corresponds to a z value depending on perspective
-            // position.X scales to an x value depending on perspective
-          }
-          else
+          state = MovePivot;
+
+          auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
+          cpSpace* space = scopedAccessor->GetNative().Get<cpSpace*>();
+
+          Vector3 localPivot;
+          float   pickingDistance;
+
+          auto body = scopedAccessor->HitTest(rayPhysicsOrigin, rayPhysicsOrigin, localPivot, pickingDistance);
+          if(!body.Empty())
           {
-            state = MovePivot;
-            Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
+            mPickedBody = body.Get<cpBody*>();
+            mSelectedActor = mPhysicsAdaptor.GetPhysicsActor(mPickedBody);
 
-            Vector3 localPivot;
-            float   pickingDistance;
-            auto    body = mPhysicsImpl.HitTest(screenCoords, origin, direction, localPivot, pickingDistance);
-            if(body)
-            {
-              mPickedBody = body;
-              mPhysicsImpl.HighlightBody(mPickedBody, true);
-              mPickedSavedState = mPhysicsImpl.ActivateBody(mPickedBody);
-              mPickedConstraint = mPhysicsImpl.AddPivotJoint(mPickedBody, mMouseBody, localPivot);
-            }
+            mPickedSavedState = cpBodyIsSleeping(mPickedBody);
+            cpBodyActivate(mPickedBody);
+            mPickedConstraint = AddPivotJoint(space, mPickedBody, mMouseBody, localPivot);
           }
         }
         break;
@@ -183,23 +261,11 @@ public:
         {
           if(mPickedBody && mPickedConstraint)
           {
-            if(!mShiftDown)
-            {
-              // Move point in XY plane, projected into scene
-              Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
+            // Ensure we get a lock before altering constraints
+            auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
 
-              Vector3 position = mPhysicsImpl.TranslateToPhysicsSpace(Vector3(screenCoords));
-              mPhysicsImpl.MoveMouseBody(mMouseBody, position);
-            }
-            else
-            {
-              // Move point in XZ plane
-              // Above vanishing pt, it's on top plane of frustum; below vanishing pt it's on bottom plane.
-              // Kind of want to project onto the plane using initial touch xy, rather than top/bottom.
-              // Whole new projection code needed.
-
-              // Cheat!
-            }
+            // Move point in physics coords
+            MoveMouseBody(mMouseBody, rayPhysicsOrigin);
           }
         }
         else if(touch.GetState(0) == Dali::PointState::FINISHED ||
@@ -207,11 +273,20 @@ public:
         {
           if(mPickedConstraint)
           {
-            mPhysicsImpl.HighlightBody(mPickedBody, false);
+            auto scopedAccessor = mPhysicsAdaptor.GetPhysicsAccessor();
+            cpSpace* space = scopedAccessor->GetNative().Get<cpSpace*>();
 
-            Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
-            mPhysicsImpl.RestoreBodyState(mPickedBody, mPickedSavedState);
-            mPhysicsImpl.ReleaseConstraint(mPickedConstraint);
+            if(mPickedSavedState)
+            {
+              cpBodyActivate(mPickedBody);
+            }
+            else
+            {
+              cpBodySleep(mPickedBody);
+            }
+
+            cpSpaceRemoveConstraint(space, mPickedConstraint);
+            cpConstraintFree(mPickedConstraint);
             mPickedConstraint = nullptr;
             mPickedBody       = nullptr;
           }
@@ -219,23 +294,9 @@ public:
         }
         break;
       }
-      case MoveCameraXZ:
-      {
-        if(touch.GetState(0) == Dali::PointState::MOTION)
-        {
-          // Move camera in XZ plane
-          //float y = cameraY; // touch point in Y. Move camera in an XZ plane on this point.
-        }
-        else if(touch.GetState(0) == Dali::PointState::FINISHED ||
-                touch.GetState(0) == Dali::PointState::INTERRUPTED)
-        {
-          state = None;
-        }
-        break;
-      }
     }
 
-    //std::cout<<"Touch State: "<<state<<std::endl;
+
     Stage::GetCurrent().KeepRendering(30.0f);
 
     return true;
@@ -243,6 +304,8 @@ public:
 
   void OnKeyEv(const Dali::KeyEvent& event)
   {
+    static bool integrateState{true};
+
     if(event.GetState() == KeyEvent::DOWN)
     {
       switch(event.GetKeyCode())
@@ -273,11 +336,80 @@ public:
           }
           else if(!event.GetKeyString().compare(" "))
           {
-            mPhysicsImpl.ToggleIntegrateState();
+            integrateState = true^integrateState;
+            mPhysicsAdaptor.SetIntegrationState(integrateState?
+                                                PhysicsAdaptor::IntegrationState::ON:
+                                                PhysicsAdaptor::IntegrationState::OFF);
+
           }
-          else if(!event.GetKeyString().compare("m"))
+          else if(!event.GetKeyString().compare("w"))
           {
-            mPhysicsImpl.ToggleDebugState();
+            if(mSelectedActor)
+            {
+              Vector3 pos = mSelectedActor.GetActorPosition();
+              mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(0, -10, 0));
+              cpBody* body = mSelectedActor.GetBody().Get<cpBody*>();
+              mPhysicsAdaptor.Queue([body]() { cpBodyActivate(body); });
+              mPhysicsAdaptor.CreateSyncPoint();
+            }
+          }
+          else if(!event.GetKeyString().compare("s"))
+          {
+            if(mSelectedActor)
+            {
+              Vector3 pos = mSelectedActor.GetActorPosition();
+              mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(0, 10, 0));
+              cpBody* body = mSelectedActor.GetBody().Get<cpBody*>();
+              mPhysicsAdaptor.Queue([body]() { cpBodyActivate(body); });
+              mPhysicsAdaptor.CreateSyncPoint();
+            }
+          }
+          else if(!event.GetKeyString().compare("a"))
+          {
+            if(mSelectedActor)
+            {
+              Vector3 pos = mSelectedActor.GetActorPosition();
+              mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(-10, 0, 0));
+              cpBody* body = mSelectedActor.GetBody().Get<cpBody*>();
+              mPhysicsAdaptor.Queue([body]() { cpBodyActivate(body); });
+              mPhysicsAdaptor.CreateSyncPoint();
+            }
+          }
+          else if(!event.GetKeyString().compare("d"))
+          {
+            if(mSelectedActor)
+            {
+              Vector3 pos = mSelectedActor.GetActorPosition();
+              mSelectedActor.AsyncSetPhysicsPosition(pos + Vector3(10, 0, 0));
+              cpBody* body = mSelectedActor.GetBody().Get<cpBody*>();
+              mPhysicsAdaptor.Queue([body]() { cpBodyActivate(body); });
+              mPhysicsAdaptor.CreateSyncPoint();
+            }
+          }
+          else if(!event.GetKeyString().compare("q"))
+          {
+            // Rotate anti-clockwise
+            if(mSelectedActor)
+            {
+              Quaternion quaternion = mSelectedActor.GetActorRotation();
+              quaternion *= Quaternion(Degree(-15.0f), Vector3::ZAXIS);
+              mSelectedActor.AsyncSetPhysicsRotation(quaternion);
+              cpBody* body = mSelectedActor.GetBody().Get<cpBody*>();
+              mPhysicsAdaptor.Queue([body]() { cpBodyActivate(body); });
+              mPhysicsAdaptor.CreateSyncPoint();
+            }
+          }
+          else if(!event.GetKeyString().compare("e"))
+          {
+            // Rotate clockwise using native physics APIs
+            if(mSelectedActor)
+            {
+              cpBody* body = mSelectedActor.GetBody().Get<cpBody*>();
+              float angle = cpBodyGetAngle(body);
+              mPhysicsAdaptor.Queue([body, angle]() { cpBodySetAngle(body, angle-Math::PI/12.0f); });
+              mPhysicsAdaptor.Queue([body]() { cpBodyActivate(body); });
+              mPhysicsAdaptor.CreateSyncPoint();
+            }
           }
           break;
         }
@@ -313,13 +445,20 @@ private:
   Application& mApplication;
   Window       mWindow;
 
-  PhysicsImpl   mPhysicsImpl;
+  PhysicsAdaptor mPhysicsAdaptor;
+  PhysicsActor   mSelectedActor;
+  Matrix        mPhysicsTransform;
   Actor         mPhysicsRoot;
   cpBody*       mMouseBody{nullptr};
   cpBody*       mPickedBody{nullptr};
   cpConstraint* mPickedConstraint{nullptr};
   int           mPickedSavedState = -1; /// 0 : Active, 1 : Sleeping
 
+  cpShape* mLeftBound{nullptr};
+  cpShape* mRightBound{nullptr};
+  cpShape* mTopBound{nullptr};
+  cpShape* mBottomBound{nullptr};
+
   bool mCtrlDown{false};
   bool mAltDown{false};
   bool mShiftDown{false};
diff --git a/examples/chipmunk-physics/physics-impl.cpp b/examples/chipmunk-physics/physics-impl.cpp
deleted file mode 100644 (file)
index 1916ee0..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include "physics-impl.h"
-#include "physics-actor.h"
-
-#include <devel-api/common/stage.h>
-#include <iostream>
-#include <map>
-#include <utility>
-
-using Dali::Actor;
-using Dali::Layer;
-using Dali::Stage;
-using Dali::Vector2;
-using Dali::Vector3;
-using Dali::Window;
-using namespace Dali::DevelStage;
-
-#define GRABBABLE_MASK_BIT (1u << 31)
-cpShapeFilter GRAB_FILTER          = {CP_NO_GROUP, GRABBABLE_MASK_BIT, GRABBABLE_MASK_BIT};
-cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT};
-
-Actor PhysicsImpl::Initialize(Window window)
-{
-  mWindow = window;
-  mSpace  = cpSpaceNew();
-  cpSpaceSetIterations(mSpace, 30);
-  cpSpaceSetSleepTimeThreshold(mSpace, 0.5f);
-  cpSpaceSetGravity(mSpace, cpv(0, -200));
-
-  auto windowSize = window.GetSize();
-  CreateWorldBounds(windowSize);
-
-  // Create an actor that can handle mouse events.
-  mPhysicsRoot                                 = Layer::New();
-  mPhysicsRoot[Actor::Property::SIZE]          = Vector2(windowSize.GetWidth(), windowSize.GetHeight());
-  mPhysicsRoot[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
-  mPhysicsRoot[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
-
-  mFrameCallback = new FrameCallback(*this);
-  AddFrameCallback(Stage::GetCurrent(), *mFrameCallback, window.GetRootLayer());
-  Stage::GetCurrent().KeepRendering(30);
-
-  return mPhysicsRoot;
-}
-
-Layer PhysicsImpl::CreateDebug(Vector2 windowSize)
-{
-  return Layer();
-}
-
-void PhysicsImpl::CreateWorldBounds(Window::WindowSize size)
-{
-  // Physics origin is 0,0,0 in DALi coords.
-  // But, Y is inverted, so bottom is -ve, top is +ve.
-  // Perform this correction when applying position to actor.
-  // But, can't use actors in update, so cache transform.
-  SetTransform(Vector2(size.GetWidth(), size.GetHeight()));
-
-  int xBound = size.GetWidth() / 2;
-  int yBound = size.GetHeight() / 2;
-
-  cpBody* staticBody = cpSpaceGetStaticBody(mSpace);
-
-  if(mLeftBound)
-  {
-    cpSpaceRemoveShape(mSpace, mLeftBound);
-    cpSpaceRemoveShape(mSpace, mRightBound);
-    cpSpaceRemoveShape(mSpace, mTopBound);
-    cpSpaceRemoveShape(mSpace, mBottomBound);
-    cpShapeFree(mLeftBound);
-    cpShapeFree(mRightBound);
-    cpShapeFree(mTopBound);
-    cpShapeFree(mBottomBound);
-  }
-  mLeftBound   = AddBound(staticBody, cpv(-xBound, -yBound), cpv(-xBound, yBound));
-  mRightBound  = AddBound(staticBody, cpv(xBound, -yBound), cpv(xBound, yBound));
-  mTopBound    = AddBound(staticBody, cpv(-xBound, -yBound), cpv(xBound, -yBound));
-  mBottomBound = AddBound(staticBody, cpv(-xBound, yBound), cpv(xBound, yBound));
-}
-
-void PhysicsImpl::SetTransform(Vector2 worldSize)
-{
-  mWorldOffset.x = worldSize.x * 0.5f;
-  mWorldOffset.y = worldSize.y * 0.5f;
-  // y is always inverted.
-}
-
-cpShape* PhysicsImpl::AddBound(cpBody* staticBody, cpVect start, cpVect end)
-{
-  cpShape* shape = cpSpaceAddShape(mSpace, cpSegmentShapeNew(staticBody, start, end, 0.0f));
-  cpShapeSetElasticity(shape, 1.0f);
-  cpShapeSetFriction(shape, 1.0f);
-  cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER);
-  return shape;
-}
-
-PhysicsActor& PhysicsImpl::AddBall(::Actor actor, float mass, float radius, float elasticity, float friction)
-{
-  Dali::Mutex::ScopedLock lock(mMutex);
-  cpBody*                 body = cpSpaceAddBody(mSpace, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
-  cpBodySetPosition(body, cpv(0, 0));
-  cpBodySetVelocity(body, cpv(0, 0));
-
-  cpShape* shape = cpSpaceAddShape(mSpace, cpCircleShapeNew(body, radius, cpvzero));
-  cpShapeSetElasticity(shape, elasticity);
-  cpShapeSetFriction(shape, friction);
-
-  int                   id    = actor[Actor::Property::ID];
-  Dali::Property::Index index = actor.RegisterProperty("uBrightness", 0.0f);
-  mPhysicsActors.insert(std::make_pair(id, PhysicsActor{actor, body, this, index}));
-  actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::TOP_LEFT;
-  actor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
-  mPhysicsRoot.Add(actor);
-  return mPhysicsActors.at(id);
-}
-
-PhysicsActor& PhysicsImpl::AddBrick(Dali::Actor actor, float mass, float elasticity, float friction, Vector3 size)
-{
-  Dali::Mutex::ScopedLock lock(mMutex);
-  cpBody*                 body = cpSpaceAddBody(mSpace, cpBodyNew(mass, cpMomentForBox(mass, size.width, size.height)));
-  cpBodySetPosition(body, cpv(0, 0));
-  cpBodySetVelocity(body, cpv(0, 0));
-
-  cpShape* shape = cpSpaceAddShape(mSpace, cpBoxShapeNew(body, size.width, size.height, 0.0f));
-  cpShapeSetFriction(shape, friction);
-  cpShapeSetElasticity(shape, elasticity);
-
-  int                   id    = actor[Actor::Property::ID];
-  Dali::Property::Index index = actor.RegisterProperty("uBrightness", 0.0f);
-  mPhysicsActors.insert(std::make_pair(id, PhysicsActor{actor, body, this, index}));
-  actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::TOP_LEFT;
-  actor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
-  mPhysicsRoot.Add(actor);
-  return mPhysicsActors.at(id);
-}
-
-cpBody* PhysicsImpl::AddMouseBody()
-{
-  Dali::Mutex::ScopedLock lock(mMutex);
-  auto                    kinematicBody = cpBodyNewKinematic(); // Mouse actor is a kinematic body that is not integrated
-  return kinematicBody;
-}
-
-PhysicsActor* PhysicsImpl::GetPhysicsActor(cpBody* body)
-{
-  return reinterpret_cast<PhysicsActor*>(cpBodyGetUserData(body));
-}
-
-void PhysicsImpl::HighlightBody(cpBody* body, bool highlight)
-{
-  auto physicsActor = GetPhysicsActor(body);
-  if(physicsActor)
-  {
-    Actor actor = mPhysicsRoot.FindChildById(physicsActor->GetId());
-    if(actor)
-    {
-      actor[physicsActor->GetBrightnessIndex()] = highlight ? 1.0f : 0.0f;
-    }
-  }
-}
-
-// Convert from root actor local space to physics space
-Vector3 PhysicsImpl::TranslateToPhysicsSpace(Vector3 vector)
-{
-  // root actor origin is top left, DALi Y is inverted.
-  // Physics origin is center. Y: 0->1 => 0.5=>-0.5
-  return Vector3(vector.x - mWorldOffset.x, mWorldOffset.y - vector.y, vector.z);
-}
-
-// Convert from physics space to root actor local space
-Vector3 PhysicsImpl::TranslateFromPhysicsSpace(Vector3 vector)
-{
-  return Vector3(vector.x + mWorldOffset.x, mWorldOffset.y - vector.y, vector.z);
-}
-
-// Convert a vector from dali space to physics space
-Vector3 PhysicsImpl::ConvertVectorToPhysicsSpace(Vector3 vector)
-{
-  // root actor origin is top left, DALi Y is inverted.
-  // @todo Add space config scale.
-  return Vector3(vector.x, -vector.y, vector.z);
-}
-
-// Convert a vector physics space to root actor local space
-Vector3 PhysicsImpl::ConvertVectorFromPhysicsSpace(Vector3 vector)
-{
-  return Vector3(vector.x, -vector.y, vector.z);
-}
-
-void PhysicsImpl::Integrate(float timestep)
-{
-  if(mPhysicsIntegrateState)
-  {
-    cpSpaceStep(mSpace, timestep);
-  }
-  //  if(mDynamicsWorld->getDebugDrawer() && mPhysicsDebugState)
-  //  {
-  //    mDynamicsWorld->debugDrawWorld();
-  //  }
-}
-
-cpBody* PhysicsImpl::HitTest(Vector2 screenCoords, Vector3 origin, Vector3 direction, Vector3& localPivot, float& distanceFromCamera)
-{
-  Vector3          spacePosition = TranslateToPhysicsSpace(Vector3{screenCoords});
-  cpVect           mousePosition = cpv(spacePosition.x, spacePosition.y);
-  cpFloat          radius        = 5.0f;
-  cpPointQueryInfo info          = {0};
-  cpShape*         shape         = cpSpacePointQueryNearest(mSpace, mousePosition, radius, GRAB_FILTER, &info);
-
-  cpBody* body{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 : mousePosition);
-    body           = cpShapeGetBody(shape);
-    cpVect local   = cpBodyWorldToLocal(body, nearest);
-    localPivot.x   = local.x;
-    localPivot.y   = local.y;
-    localPivot.z   = 0.0;
-  }
-  return body;
-}
-
-cpConstraint* PhysicsImpl::AddPivotJoint(cpBody* body1, cpBody* body2, Vector3 localPivot)
-{
-  cpVect        pivot{localPivot.x, localPivot.y};
-  cpConstraint* joint = cpPivotJointNew2(body2, body1, cpvzero, pivot);
-  cpConstraintSetMaxForce(joint, 50000.0f); // Magic numbers for mouse feedback.
-  cpConstraintSetErrorBias(joint, cpfpow(1.0f - 0.15f, 60.0f));
-  cpConstraint* constraint = cpSpaceAddConstraint(mSpace, joint);
-  return constraint; // Constraint & joint are the same...
-}
-
-void PhysicsImpl::MoveMouseBody(cpBody* mouseBody, Vector3 position)
-{
-  cpVect cpPosition = cpv(position.x, position.y);
-  cpVect newPoint   = cpvlerp(cpBodyGetPosition(mouseBody), cpPosition, 0.25f);
-  cpBodySetVelocity(mouseBody, cpvmult(cpvsub(newPoint, cpBodyGetPosition(mouseBody)), 60.0f));
-  // Normally, kinematic body's position would be calculated by engine.
-  // For mouse, though, we want to set it.
-  cpBodySetPosition(mouseBody, newPoint);
-}
-
-void PhysicsImpl::MoveConstraint(cpConstraint* constraint, Vector3 newPosition)
-{
-}
-
-void PhysicsImpl::ReleaseConstraint(cpConstraint* constraint)
-{
-  cpSpaceRemoveConstraint(mSpace, constraint);
-  cpConstraintFree(constraint);
-}
-
-int PhysicsImpl::ActivateBody(cpBody* body)
-{
-  int oldState = cpBodyIsSleeping(body);
-  cpBodyActivate(body);
-
-  return oldState;
-}
-
-void PhysicsImpl::RestoreBodyState(cpBody* body, int oldState)
-{
-  if(oldState)
-  {
-    cpBodyActivate(body);
-  }
-  else
-  {
-    cpBodySleep(body);
-  }
-}
diff --git a/examples/chipmunk-physics/physics-impl.h b/examples/chipmunk-physics/physics-impl.h
deleted file mode 100644 (file)
index 9420fa4..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-#ifndef DALI_PHYSICS_DEMO_PHYSICS_IMPL_H
-#define DALI_PHYSICS_DEMO_PHYSICS_IMPL_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/common/stage-devel.h>
-
-#include <map>
-#include <chipmunk/chipmunk.h>
-
-#include "physics-actor.h"
-#include "frame-callback.h"
-
-
-class PhysicsImpl : public Dali::ConnectionTracker
-{
-public:
-  Dali::Actor Initialize(Dali::Window window);
-
-  /**
-   * Create a layer & debug renderer
-   */
-  Dali::Layer CreateDebug(Dali::Vector2 windowSize);
-
-  /**
-   * Converts a point in RootActor local coords (e.g. gesture)
-   * into physics space coords.
-   * @param vector The point to convert
-   * @return The converted point
-   */
-  Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector);
-
-  /**
-   * Converts a point in physics space coords.
-   * into RootActor local coords
-   * @param vector The point to convert
-   * @return The converted point
-   */
-  Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector);
-
-  /**
-   * Converts a vector in DALi space into physics space.
-   * @param vector The vector to convert
-   * @return The converted vector
-   */
-  Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector);
-
-  /**
-   * Converts a vector in physics space to DALi space
-   * @param vector The vector to convert
-   * @return The converted vector
-   */
-  Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector);
-
-  /**
-   * Set up the transform from world space to physics space
-   * @param[in] worldSize The 2d bounding box of the world in screen space
-   */
-  void SetTransform(Dali::Vector2 worldSize);
-
-  /**
-   * Run the physics integration over the given timestep.
-   * @param timeStep
-   */
-  void Integrate(float timeStep);
-
-  /**
-   * Toggle the integration state. If it's turned on, physics will run
-   * during the frame callback.
-   */
-  void ToggleIntegrateState()
-  {
-    mPhysicsIntegrateState ^= true;
-  }
-
-  /**
-   * Toggle the debug state. If debug is turned on, use the physics engine
-   * debug to show wireframes.
-   */
-  void ToggleDebugState()
-  {
-    mPhysicsDebugState ^= true;
-  }
-
-  void CreateWorldBounds(Dali::Window::WindowSize size);
-  cpShape* AddBound(cpBody* staticBody, cpVect start, cpVect end);
-  PhysicsActor& AddBall(Dali::Actor actor, float mass, float radius, float elasticity, float friction);
-  PhysicsActor& AddBrick(Dali::Actor actor,float mass, float elasticity, float friction, Dali::Vector3 size);
-
-  cpBody* AddMouseBody();
-
-  /**
-   * @param[in] screenCoords The touch point in screen coordinates
-   * @param[in] origin The camera origin in DALi world space
-   * @param[in] direction The ray direction in DALi world space
-   * @param[out] localPivot The hit point local to the body
-   * @param[out] distanceFromCamera The distance of the pick point from the camera
-   * @return nullptr if no dynamic body found, otherwise a valid ptr to the hit body.
-   */
-  cpBody* HitTest(Dali::Vector2 screenCoords, Dali::Vector3 origin, Dali::Vector3 direction,
-                  Dali::Vector3& localPivot, float& distanceFromCamera);
-
-  cpConstraint* AddPivotJoint(cpBody* body1, cpBody* body2, Dali::Vector3 localPivot);
-
-  void MoveMouseBody(cpBody* mouseBody, Dali::Vector3 position);
-
-  void MoveConstraint(cpConstraint* constraint, Dali::Vector3 newPosition);
-
-  void ReleaseConstraint(cpConstraint* constraint);
-
-  /**
-   * Ensure that the physics body does not go to sleep
-   * @param[in] body The physics body
-   * @return The old state
-   */
-  int ActivateBody(cpBody* body);
-
-  /**
-   * Restore the state of the physics body
-   * @param[in] body The physics body
-   * @param[in] oldState The previous state to restore
-   */
-  void RestoreBodyState(cpBody* body, int oldState);
-
-  /**
-   * Get the physics actor associated with the given body
-   * @param[in] body The physics body
-   * @return the associated physics actor
-   */
-  PhysicsActor* GetPhysicsActor(cpBody* body);
-
-  /**
-   * Set the highlight state of the actor associated with the physics body
-   * @param[in] body The physics body
-   * @param[in] highlight Whether to turn the highlight on or off.
-   */
-  void HighlightBody(cpBody* body, bool highlight);
-
-
-public:
-  std::map<uint32_t, PhysicsActor> mPhysicsActors;
-  bool mPhysicsIntegrateState{true};
-  bool mPhysicsDebugState{true};
-
-  cpSpace* mSpace{nullptr};
-  cpShape* mLeftBound{nullptr};
-  cpShape* mRightBound{nullptr};
-  cpShape* mTopBound{nullptr};
-  cpShape* mBottomBound{nullptr};
-
-  Dali::Window mWindow;
-  Dali::Mutex mMutex;
-
-  Dali::Actor mPhysicsRoot;
-  Dali::Vector2 mWorldOffset;
-  FrameCallback* mFrameCallback{nullptr};
-};
-
-#endif // DALI_PHYSICS_DEMO_PHYSICS_IMPL_H
index f8fc3db..f72ae53 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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.
@@ -35,9 +35,8 @@ using namespace Dali;
 class DrawableActorExampleController : public ConnectionTracker
 {
 public:
-
   explicit DrawableActorExampleController(Application& application)
-    : mApplication(application)
+  : mApplication(application)
   {
     // Connect to the Application's Init signal
     mApplication.InitSignal().Connect(this, &DrawableActorExampleController::Create);
@@ -56,16 +55,17 @@ public:
     mRenderer = std::make_unique<NativeRenderer>(window.GetSize().GetWidth(), window.GetSize().GetHeight());
 
     // Create render callback
-    mRenderCallback = RenderCallback::New<NativeRenderer>( mRenderer.get(), &NativeRenderer::OnRender );
+    mRenderCallback = RenderCallback::New<NativeRenderer>(mRenderer.get(), &NativeRenderer::OnRender);
 
     // Create drawable actor
-    mGLActor = DrawableActor::New( *mRenderCallback );
+    mGLActor = DrawableActor::New(*mRenderCallback);
 
-    mGLActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
-    mGLActor.SetProperty( Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER );
+    mGLActor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+    mGLActor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
 
     // Set size on the actor (half the window size to show that glClear() and scissor test work together)
-    mGLActor.SetProperty( Actor::Property::SIZE, Size( window.GetSize() ) * 0.5f);
+    mGLActor.SetProperty(Actor::Property::SIZE, Size(window.GetSize()) * 0.75f);
+    mGLActor[Actor::Property::POSITION] = Vector3(50.0f, 50.0f, 0.0f);
 
     // Add actor to the scene
     window.Add(mGLActor);
@@ -101,7 +101,7 @@ public:
     }
   }
 
-  TextLabel mTextLabel;
+  TextLabel     mTextLabel;
   DrawableActor mGLActor;
 
   std::unique_ptr<RenderCallback> mRenderCallback;
@@ -113,7 +113,7 @@ private:
 
 int DALI_EXPORT_API main(int argc, char** argv)
 {
-  Application          application = Application::New(&argc, &argv);
+  Application                    application = Application::New(&argc, &argv);
   DrawableActorExampleController test(application);
   application.MainLoop();
   return 0;
index 0edc431..85ce873 100755 (executable)
@@ -24,6 +24,7 @@ BuildRequires:  pkgconfig(dali2-adaptor)
 BuildRequires:  pkgconfig(dali2-toolkit)
 BuildRequires:  pkgconfig(dali2-scene3d)
 BuildRequires:  pkgconfig(dali2-physics-2d)
+BuildRequires:  pkgconfig(dali2-physics-3d)
 BuildRequires:  pkgconfig(libtzplatform-config)
 BuildRequires:  pkgconfig(gles20)
 BuildRequires:  pkgconfig(glesv2)
index d35f25d..89b8938 100755 (executable)
@@ -28,6 +28,9 @@ msgstr "Bloom"
 msgid "DALI_DEMO_STR_TITLE_BUBBLES"
 msgstr "Bubbles"
 
+msgid "DALI_DEMO_STR_TITLE_BULLET_PHYSICS"
+msgstr "Bullet Physics"
+
 msgid "DALI_DEMO_STR_TITLE_BUTTONS"
 msgstr "Buttons"
 
index 9a5c9b8..6ffb4b4 100755 (executable)
@@ -28,6 +28,9 @@ msgstr "Bloom"
 msgid "DALI_DEMO_STR_TITLE_BUBBLES"
 msgstr "Bubbles"
 
+msgid "DALI_DEMO_STR_TITLE_BULLET_PHYSICS"
+msgstr "Bullet Physics"
+
 msgid "DALI_DEMO_STR_TITLE_BUTTONS"
 msgstr "Buttons"
 
index 3c31c54..3e8f37d 100644 (file)
@@ -45,6 +45,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_BLOCKS dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_BLOCKS")
 #define DALI_DEMO_STR_TITLE_BLOOM_VIEW dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_BLOOM_VIEW")
 #define DALI_DEMO_STR_TITLE_BUBBLES dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_BUBBLES")
+#define DALI_DEMO_STR_TITLE_BULLET_PHYSICS dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_BULLET_PHYSICS")
 #define DALI_DEMO_STR_TITLE_BUTTONS dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_BUTTONS")
 #define DALI_DEMO_STR_TITLE_CAMERA_TEST dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CAMERA_TEST")
 #define DALI_DEMO_STR_TITLE_CANVAS_VIEW dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CANVAS_VIEW")
@@ -165,6 +166,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_BLOCKS "Blocks"
 #define DALI_DEMO_STR_TITLE_BLOOM_VIEW "Bloom"
 #define DALI_DEMO_STR_TITLE_BUBBLES "Bubbles"
+#define DALI_DEMO_STR_TITLE_BULLET_PHYSICS "Bullet Physics"
 #define DALI_DEMO_STR_TITLE_BUTTONS "Buttons"
 #define DALI_DEMO_STR_TITLE_CAMERA_TEST "Camera Test"
 #define DALI_DEMO_STR_TITLE_CANVAS_VIEW "Canvas View"