From 0fb7eb9844f083c4edb33d1fe1ae3585cbb58659 Mon Sep 17 00:00:00 2001 From: David Steele Date: Fri, 11 Aug 2023 18:23:09 +0100 Subject: [PATCH] Added bullet-physics-demo Added mechanism to handle optional dependencies Change-Id: Ib24df94c68bb1cca58ff8fbb2be3d32cc5668ed0 --- .gitignore | 4 + build/tizen/CMakeLists.txt | 51 +- build/tizen/examples/CMakeLists.txt | 6 +- com.samsung.dali-demo.xml | 3 + examples-reel/dali-examples-reel.cpp | 1 + examples/bullet-physics/README.md | 20 + examples/bullet-physics/ball-renderer.cpp | 218 +++++++ examples/bullet-physics/ball-renderer.h | 43 ++ examples/bullet-physics/cube-renderer.cpp | 183 ++++++ examples/bullet-physics/cube-renderer.h | 44 ++ examples/bullet-physics/dependencies.cmake | 5 + .../bullet-physics/physics-demo-controller.cpp | 649 +++++++++++++++++++++ .../shaders/rendering-textured-shape.frag | 32 + .../shaders/rendering-textured-shape.vert | 26 + examples/chipmunk-physics/dependencies.cmake | 5 + examples/drawable-actor/drawable-actor-example.cpp | 20 +- resources/po/en_GB.po | 3 + resources/po/en_US.po | 3 + shared/dali-demo-strings.h | 2 + 19 files changed, 1297 insertions(+), 21 deletions(-) create mode 100644 examples/bullet-physics/README.md create mode 100644 examples/bullet-physics/ball-renderer.cpp create mode 100644 examples/bullet-physics/ball-renderer.h create mode 100644 examples/bullet-physics/cube-renderer.cpp create mode 100644 examples/bullet-physics/cube-renderer.h create mode 100644 examples/bullet-physics/dependencies.cmake create mode 100644 examples/bullet-physics/physics-demo-controller.cpp create mode 100644 examples/bullet-physics/shaders/rendering-textured-shape.frag create mode 100644 examples/bullet-physics/shaders/rendering-textured-shape.vert create mode 100644 examples/chipmunk-physics/dependencies.cmake 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" -- 2.7.4