diff --git a/.gitignore b/.gitignore
index 3d47500..001916b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/build/tizen/CMakeLists.txt b/build/tizen/CMakeLists.txt
index 5cb6012..46c389c 100644
--- a/build/tizen/CMakeLists.txt
+++ b/build/tizen/CMakeLists.txt
@@ -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,8 @@ 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-3d )
# Set up the include dir
SET( INCLUDE_DIR $ENV{includedir} )
@@ -365,11 +385,17 @@ 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-3d_FOUND)
+ SET(DALI_PHYSICS_3D_LDFLAGS ${DALI_PHYSICS_3D_LDFLAGS} dali2-physics-3d::dali2-physics-3d)
ENDIF()
ELSEIF( UNIX )
@@ -404,6 +430,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 +552,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} "]" )
diff --git a/build/tizen/examples/CMakeLists.txt b/build/tizen/examples/CMakeLists.txt
index 6a458b3..7d02b15 100644
--- a/build/tizen/examples/CMakeLists.txt
+++ b/build/tizen/examples/CMakeLists.txt
@@ -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()
-
diff --git a/com.samsung.dali-demo.xml b/com.samsung.dali-demo.xml
index 4c12d91..17fba75 100644
--- a/com.samsung.dali-demo.xml
+++ b/com.samsung.dali-demo.xml
@@ -58,6 +58,9 @@
+
+
+
diff --git a/examples-reel/dali-examples-reel.cpp b/examples-reel/dali-examples-reel.cpp
index a697899..7d5af30 100644
--- a/examples-reel/dali-examples-reel.cpp
+++ b/examples-reel/dali-examples-reel.cpp
@@ -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
index 0000000..e2abee1
--- /dev/null
+++ b/examples/bullet-physics/README.md
@@ -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
index 0000000..6c4f852
--- /dev/null
+++ b/examples/bullet-physics/ball-renderer.cpp
@@ -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
+#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& vertices, std::vector& 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 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& 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 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 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
index 0000000..b106cec
--- /dev/null
+++ b/examples/bullet-physics/ball-renderer.h
@@ -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
+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
index 0000000..a5b79d4
--- /dev/null
+++ b/examples/bullet-physics/cube-renderer.cpp
@@ -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
+#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
index 0000000..8e6c42d
--- /dev/null
+++ b/examples/bullet-physics/cube-renderer.h
@@ -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
+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
index 0000000..e938380
--- /dev/null
+++ b/examples/bullet-physics/dependencies.cmake
@@ -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
index 0000000..4052f28
--- /dev/null
+++ b/examples/bullet-physics/physics-demo-controller.cpp
@@ -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
+#include
+#include "dali-physics/public-api/physics-actor.h"
+#include "dali-physics/public-api/physics-adaptor.h"
+
+#include
+#include
+
+#include
+#include
+
+#include
+
+#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();
+ 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();
+ 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();
+ 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();
+
+ 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();
+ 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(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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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();
+ 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;
+}
diff --git a/examples/bullet-physics/shaders/rendering-textured-shape.frag b/examples/bullet-physics/shaders/rendering-textured-shape.frag
new file mode 100644
index 0000000..1f7bac2
--- /dev/null
+++ b/examples/bullet-physics/shaders/rendering-textured-shape.frag
@@ -0,0 +1,32 @@
+uniform sampler2D uTexture;
+uniform mediump float uBrightness;
+varying mediump vec2 vTexCoord;
+varying mediump vec3 vIllumination;
+
+mediump vec3 redistribute_rgb(mediump vec3 color)
+{
+ mediump float threshold = 0.9999999;
+ mediump float m = max(max(color.r, color.g), color.b);
+ if(m <= threshold)
+ {
+ return color;
+ }
+ mediump float total = color.r + color.g + color.b;
+ if( total >= 3.0 * threshold)
+ {
+ return vec3(threshold);
+ }
+ mediump float x = (3.0 * threshold - total) / (3.0 * m - total);
+ mediump float gray = threshold - x * m;
+ return vec3(gray) + vec3(x)*color;
+}
+
+void main()
+{
+ mediump vec4 texColor = texture2D( uTexture, vTexCoord );
+ //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);
+}
diff --git a/examples/bullet-physics/shaders/rendering-textured-shape.vert b/examples/bullet-physics/shaders/rendering-textured-shape.vert
new file mode 100644
index 0000000..02b1edb
--- /dev/null
+++ b/examples/bullet-physics/shaders/rendering-textured-shape.vert
@@ -0,0 +1,26 @@
+attribute mediump vec3 aPosition; // 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
+uniform mediump vec3 uSize; // DALi shader builtin
+varying mediump vec3 vIllumination;
+varying mediump vec2 vTexCoord;
+
+void main()
+{
+ mediump vec4 vertexPosition = vec4(aPosition, 1.0);
+ mediump vec3 normal = normalize(vertexPosition.xyz);
+
+ vertexPosition.xyz *= uSize;
+ vec4 pos = uModelView * vertexPosition;
+
+ vec4 lightPosition = vec4(400.0, 0.0, 100.0, 1.0);
+ vec4 mvLightPos = uViewMatrix * lightPosition;
+ vec3 vectorToLight = normalize(mvLightPos.xyz - pos.xyz);
+ float lightDiffuse = max(dot(vectorToLight, normal), 0.0);
+
+ vIllumination = vec3(lightDiffuse * 0.5 + 0.5);
+ vTexCoord = aTexCoord;
+ gl_Position = uMvpMatrix * vertexPosition;
+}
diff --git a/examples/chipmunk-physics/dependencies.cmake b/examples/chipmunk-physics/dependencies.cmake
new file mode 100644
index 0000000..b30b385
--- /dev/null
+++ b/examples/chipmunk-physics/dependencies.cmake
@@ -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/drawable-actor/drawable-actor-example.cpp b/examples/drawable-actor/drawable-actor-example.cpp
index f8fc3db..f72ae53 100644
--- a/examples/drawable-actor/drawable-actor-example.cpp
+++ b/examples/drawable-actor/drawable-actor-example.cpp
@@ -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(window.GetSize().GetWidth(), window.GetSize().GetHeight());
// Create render callback
- mRenderCallback = RenderCallback::New( mRenderer.get(), &NativeRenderer::OnRender );
+ mRenderCallback = RenderCallback::New(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 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;
diff --git a/resources/po/en_GB.po b/resources/po/en_GB.po
index d35f25d..89b8938 100755
--- a/resources/po/en_GB.po
+++ b/resources/po/en_GB.po
@@ -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"
diff --git a/resources/po/en_US.po b/resources/po/en_US.po
index 9a5c9b8..6ffb4b4 100755
--- a/resources/po/en_US.po
+++ b/resources/po/en_US.po
@@ -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"
diff --git a/shared/dali-demo-strings.h b/shared/dali-demo-strings.h
index 3c31c54..3e8f37d 100644
--- a/shared/dali-demo-strings.h
+++ b/shared/dali-demo-strings.h
@@ -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"