[dali_2.2.38] Merge branch 'devel/master' 41/296841/1
authorDavid Steele <david.steele@samsung.com>
Fri, 4 Aug 2023 12:43:56 +0000 (13:43 +0100)
committerDavid Steele <david.steele@samsung.com>
Fri, 4 Aug 2023 12:43:56 +0000 (13:43 +0100)
Change-Id: Ifcfde121f7566ebde7b00e5e5b38fbf1f65176e9

19 files changed:
build/tizen/CMakeLists.txt
build/tizen/examples/CMakeLists.txt
com.samsung.dali-demo.xml
examples-reel/dali-examples-reel.cpp
examples/chipmunk-physics/README.md [new file with mode: 0644]
examples/chipmunk-physics/chipmunk.gif [new file with mode: 0644]
examples/chipmunk-physics/frame-callback.cpp [new file with mode: 0644]
examples/chipmunk-physics/frame-callback.h [new file with mode: 0644]
examples/chipmunk-physics/physics-actor.cpp [new file with mode: 0644]
examples/chipmunk-physics/physics-actor.h [new file with mode: 0644]
examples/chipmunk-physics/physics-demo-controller.cpp [new file with mode: 0644]
examples/chipmunk-physics/physics-impl.cpp [new file with mode: 0644]
examples/chipmunk-physics/physics-impl.h [new file with mode: 0644]
examples/chipmunk-physics/shaders/rendering-textured-shape.frag [new file with mode: 0644]
examples/chipmunk-physics/shaders/rendering-textured-shape.vert [new file with mode: 0644]
packaging/com.samsung.dali-demo.spec
resources/po/en_GB.po
resources/po/en_US.po
shared/dali-demo-strings.h

index 2c6425f..5cb6012 100644 (file)
@@ -243,6 +243,21 @@ IF( ENABLE_PKG_CONFIGURE )
     SET( ENABLE_SCENE3D "ON" )
   ENDIF()
 
+  pkg_check_modules(DALI_PHYSICS_2D dali2-physics-2d)
+  IF( DALI_PHYSICS_2D_FOUND )
+    FOREACH(flag ${DALI_PHYSICS_2D_CFLAGS})
+      SET(REQUIRED_CFLAGS "${REQUIRED_CFLAGS} ${flag}")
+    ENDFOREACH(flag)
+
+    SET( REQUIRED_CFLAGS "${REQUIRED_CFLAGS} -DDALI_PHYSICS_2D_AVAILABLE" )
+
+    FOREACH(flag ${DALI_PHYSICS_2D_LDFLAGS})
+      SET(REQUIRED_PKGS_LDFLAGS "${REQUIRED_PKGS_LDFLAGS} ${flag}")
+    ENDFOREACH(flag)
+
+    SET( ENABLE_PHYSICS_2D "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)
@@ -292,6 +307,8 @@ IF( WIN32 OR APPLE ) # WIN32 includes x64 as well according to the cmake doc.
 
   FIND_PACKAGE( dali2-scene3d )
 
+  FIND_PACKAGE( chipmunk )
+
   # Set up the include dir
   SET( INCLUDE_DIR $ENV{includedir} )
   IF( NOT INCLUDE_DIR )
@@ -346,6 +363,15 @@ IF( WIN32 OR APPLE ) # WIN32 includes x64 as well according to the cmake doc.
     )
     SET( ENABLE_SCENE3D "ON" )
   ENDIF()
+
+  IF (chipmunk_FOUND)
+    SET(REQUIRED_LIBS
+      ${REQUIRED_LIBS}
+      -lchipmunk
+    )
+    SET( ENABLE_PHYSICS_2D "ON" )
+  ENDIF()
+
 ELSEIF( UNIX )
   SET( REQUIRED_LIBS
     ${REQUIRED_PKGS_LDFLAGS}
@@ -374,6 +400,10 @@ IF( ENABLE_SCENE3D )
   SET(DALI_DEMO_CFLAGS "${DALI_DEMO_CFLAGS} -DDALI_SCENE3D_AVAILABLE")
 ENDIF()
 
+IF( ENABLE_PHYSICS_2D )
+  SET(DALI_DEMO_CFLAGS "${DALI_DEMO_CFLAGS} -DDALI_PHYSICS_2D_AVAILABLE")
+ENDIF()
+
 IF( UNIX )
   IF( NOT ${ENABLE_EXPORTALL} )
     ADD_DEFINITIONS( "-DHIDE_DALI_INTERNALS" )
@@ -491,3 +521,4 @@ MESSAGE( " Folder        DEMO_LANG : [" ${DEMO_LANG} "]" )
 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} "]" )
index 05ebbdf..6a458b3 100644 (file)
@@ -20,6 +20,13 @@ IF (NOT "${ENABLE_SCENE3D}" )
   ENDIF()
 ENDIF()
 
+SET(PHYSICS_2D_DIR "chipmunk")
+IF (NOT "${ENABLE_PHYSICS_2D}" )
+  IF ( ${PHYSICS_2D_DIR} IN_LIST SUBDIRS )
+    LIST( REMOVE_ITEM SUBDIRS ${PHYSICS_2D_DIR} )
+  ENDIF()
+ENDIF()
+
 FIND_PROGRAM( SHADER_GENERATOR "dali-shader-generator" )
 IF( NOT SHADER_GENERATOR )
   MESSAGE( FATAL_ERROR "dali-shader-generator not found!" )
index 4d2e012..4c12d91 100644 (file)
@@ -67,6 +67,9 @@
        <ui-application appid="canvas-view.example" exec="/usr/apps/com.samsung.dali-demo/bin/canvas-view.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Canvas View</label>
        </ui-application>
+       <ui-application appid="chipmunk-physics.example" exec="/usr/apps/com.samsung.dali-demo/bin/chipmunk-physics.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
+               <label>Chipmunk Physics</label>
+       </ui-application>
        <ui-application appid="clipping-draw-order.example" exec="/usr/apps/com.samsung.dali-demo/bin/clipping-draw-order.example" nodisplay="true" multiple="false" type="c++app" taskmanage="true">
                <label>Clipping Draw Order</label>
        </ui-application>
index b44ab48..a697899 100644 (file)
@@ -47,6 +47,7 @@ int DALI_EXPORT_API main(int argc, char** argv)
   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("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));
   demo.AddExample(Example("clipping-draw-order.example", DALI_DEMO_STR_TITLE_CLIPPING_DRAW_ORDER));
   demo.AddExample(Example("color-transition.example", DALI_DEMO_STR_TITLE_COLOR_TRANSITION));
diff --git a/examples/chipmunk-physics/README.md b/examples/chipmunk-physics/README.md
new file mode 100644 (file)
index 0000000..78adff1
--- /dev/null
@@ -0,0 +1,6 @@
+# Chipmunk Physics Example
+
+This is an example showing how to use Chipmunk2D physics library to create and control physics objects in DALi.
+It creates a ball and a pyramid brick wall which can be moved using touch and key events.
+
+![](./chipmunk.gif)
diff --git a/examples/chipmunk-physics/chipmunk.gif b/examples/chipmunk-physics/chipmunk.gif
new file mode 100644 (file)
index 0000000..7777a98
Binary files /dev/null and b/examples/chipmunk-physics/chipmunk.gif differ
diff --git a/examples/chipmunk-physics/frame-callback.cpp b/examples/chipmunk-physics/frame-callback.cpp
new file mode 100644 (file)
index 0000000..c6ef8a3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "frame-callback.h"
+#include "physics-impl.h"
+#include <dali/public-api/math/vector3.h>
+#include <dali/devel-api/update/update-proxy.h>
+#include <dali/devel-api/threading/mutex.h>
+
+using Dali::Vector3;
+using Dali::Quaternion;
+
+FrameCallback::FrameCallback(PhysicsImpl& physicsImpl)
+: mPhysicsImpl(physicsImpl)
+{
+}
+
+bool FrameCallback::Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds)
+{
+  Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
+  static float frameTime=0;
+  frameTime+=elapsedSeconds;
+  do
+  {
+    mPhysicsImpl.Integrate(mPhysicsTimeStep);
+    frameTime-=mPhysicsTimeStep;
+  } while (frameTime>0);
+
+  for(auto&& actor : mPhysicsImpl.mPhysicsActors)
+  {
+    // Get position, orientation from physics world.
+    Vector3 position = actor.second.GetActorPosition();
+    updateProxy.BakePosition(actor.first, position);
+    Quaternion rotation = actor.second.GetActorRotation();
+    updateProxy.BakeOrientation(actor.first, rotation);
+  }
+
+  return true;
+}
diff --git a/examples/chipmunk-physics/frame-callback.h b/examples/chipmunk-physics/frame-callback.h
new file mode 100644 (file)
index 0000000..c45f019
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef PHYSICS_DEMO_FRAME_CALLBACK_H
+#define PHYSICS_DEMO_FRAME_CALLBACK_H
+
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali/public-api/adaptor-framework/window.h>
+#include <dali/devel-api/update/frame-callback-interface.h>
+#include <map>
+#include <chrono>
+
+class PhysicsImpl;
+
+class FrameCallback : public Dali::FrameCallbackInterface
+{
+public:
+  /**
+   * Constructor
+   */
+  explicit FrameCallback(PhysicsImpl& physicsImpl);
+
+  /**
+   * Set the physics time step
+   * @param timeStep (in seconds)
+   */
+  void SetPhysicsTimeStep(float timeStep)
+  {
+    mPhysicsTimeStep = timeStep;
+  }
+
+private:
+  /**
+   * Called each frame.
+   * @param[in] updateProxy Used to set world matrix and size
+   * @param[in] elapsedSeconds Time since last frame
+   * @return Whether we should keep rendering.
+   */
+  bool Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds) override;
+
+private: // Member variables
+  PhysicsImpl& mPhysicsImpl;
+  float mPhysicsTimeStep{1.0/180.0};
+};
+
+#endif //PHYSICS_DEMO_FRAME_CALLBACK_H
diff --git a/examples/chipmunk-physics/physics-actor.cpp b/examples/chipmunk-physics/physics-actor.cpp
new file mode 100644 (file)
index 0000000..07f14a3
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "physics-actor.h"
+#include "physics-impl.h"
+#include <dali/public-api/common/constants.h>
+#include <dali/public-api/math/vector3.h>
+#include <dali/public-api/math/quaternion.h>
+
+using Dali::Vector3;
+using Dali::Quaternion;
+using Dali::Radian;
+
+void PhysicsActor::ClearForces()
+{
+  printf("Not Implemented\n");
+  //mBody->clearForces();
+  // No similar API
+}
+
+Dali::Vector3 PhysicsActor::GetPhysicsPosition()
+{
+  cpVect cpPosition = cpBodyGetPosition(mBody);
+  return Vector3(cpPosition.x, cpPosition.y, 0.0f);
+}
+
+void PhysicsActor::SetPhysicsPosition(Dali::Vector3 actorPosition)
+{
+  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
+  Vector3 physicsPosition = mImpl->TranslateToPhysicsSpace(actorPosition);
+  cpBodySetPosition(mBody, cpv(physicsPosition.x, physicsPosition.y));
+}
+
+void PhysicsActor::SetPhysicsVelocity(Dali::Vector3 actorVelocity)
+{
+  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
+  Vector3 physicsVelocity = mImpl->ConvertVectorToPhysicsSpace(actorVelocity);
+  cpBodySetVelocity(mBody, cpv(physicsVelocity.x, physicsVelocity.y));
+}
+
+void PhysicsActor::SetPhysicsAngularVelocity(Dali::Vector3 velocity)
+{
+  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
+  printf("Not Implemented\n");
+  //mBody->setAngularVelocity(btVector3(velocity.x, velocity.y, velocity.z));
+}
+
+Quaternion PhysicsActor::GetPhysicsRotation()
+{
+  return Quaternion{};
+}
+
+void PhysicsActor::SetPhysicsRotation(Dali::Quaternion rotation)
+{
+  Dali::Mutex::ScopedLock lock(mImpl->mMutex);
+
+  Vector3 axis;
+  Radian angle;
+  rotation.ToAxisAngle(axis, angle);
+
+  //btQuaternion orn = btQuaternion(btVector3(axis.x, -axis.y, axis.z), btScalar(float(-angle)));
+  //btTransform& transform = mBody->getWorldTransform();
+  //transform.setRotation(orn);
+  printf("Not Implemented\n");
+}
+
+
+Vector3 PhysicsActor::GetActorPosition()
+{
+  cpVect cpPosition = cpBodyGetPosition(mBody);
+  return mImpl->TranslateFromPhysicsSpace(Vector3(cpPosition.x, cpPosition.y, 0.0f));
+}
+
+Vector3 PhysicsActor::GetActorVelocity()
+{
+  cpVect cpVelocity = cpBodyGetVelocity(mBody);
+  return mImpl->ConvertVectorFromPhysicsSpace(Vector3(cpVelocity.x, cpVelocity.y, 0.0f));
+}
+
+Quaternion PhysicsActor::GetActorRotation()
+{
+  cpFloat angle = cpBodyGetAngle(mBody);
+  return Quaternion(Radian(angle), -Vector3::ZAXIS);
+}
diff --git a/examples/chipmunk-physics/physics-actor.h b/examples/chipmunk-physics/physics-actor.h
new file mode 100644 (file)
index 0000000..aeaa612
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef DALI_PHYSICS_DEMO_PHYSICS_ACTOR_H
+#define DALI_PHYSICS_DEMO_PHYSICS_ACTOR_H
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <chipmunk/chipmunk.h>
+#include <cstdint>
+#include <dali/dali.h>
+#include <iosfwd>
+
+// Forward declarations
+class PhysicsImpl;
+
+/**
+ * Class that associates an actor with a physics body. (Initially, rigid body)
+ */
+class PhysicsActor
+{
+public:
+  PhysicsActor() = default;
+  PhysicsActor(Dali::Actor& actor, cpBody* body, PhysicsImpl* impl, Dali::Property::Index brightnessId)
+  : mImpl(impl),
+    mActorId(actor.GetProperty<int>(Dali::Actor::Property::ID)),
+    mBody(body),
+    mBrightnessIndex(brightnessId)
+  {
+    cpBodySetUserData(mBody, this);
+  }
+
+  PhysicsActor(const PhysicsActor& rhs)=delete;
+  PhysicsActor& operator=(const PhysicsActor& rhs)=delete;
+
+  PhysicsActor(const PhysicsActor&& rhs)
+  {
+    if(this != &rhs)
+    {
+      mImpl = rhs.mImpl;
+      mActorId = rhs.mActorId;
+      mBody = rhs.mBody;
+      cpBodySetUserData(mBody, this);
+      mBrightnessIndex = rhs.mBrightnessIndex;
+    }
+  }
+
+  PhysicsActor& operator=(const PhysicsActor&& rhs)
+  {
+    if(this != &rhs)
+    {
+      mActorId = rhs.mActorId;
+      mBody    = rhs.mBody;
+      mImpl = rhs.mImpl;
+      mBrightnessIndex = rhs.mBrightnessIndex;
+      cpBodySetUserData(mBody, this);
+    }
+    return *this;
+  }
+
+  uint32_t GetId()
+  {
+    return mActorId;
+  }
+
+  cpBody* GetBody()
+  {
+    return mBody;
+  }
+
+  Dali::Property::Index GetBrightnessIndex()
+  {
+    return mBrightnessIndex;
+  }
+
+  Dali::Vector3 GetPhysicsPosition();
+  Dali::Quaternion GetPhysicsRotation();
+
+  void SetPhysicsPosition(Dali::Vector3 actorPosition);
+  void SetPhysicsVelocity(Dali::Vector3 actorVelocity);
+  void SetPhysicsAngularVelocity(Dali::Vector3 actorVelocity);
+  void SetPhysicsRotation(Dali::Quaternion actorRotation);
+  Dali::Vector3 GetActorPosition();
+  Dali::Vector3 GetActorVelocity();
+  Dali::Quaternion GetActorRotation();
+  void ClearForces();
+
+private:
+  PhysicsImpl* mImpl{nullptr};
+  uint32_t mActorId{0};
+  cpBody* mBody{nullptr};
+  Dali::Property::Index mBrightnessIndex{Dali::Property::INVALID_INDEX};
+};
+
+#endif // DALI_PHYSICS_DEMO_PHYSICS_ACTOR_H
diff --git a/examples/chipmunk-physics/physics-demo-controller.cpp b/examples/chipmunk-physics/physics-demo-controller.cpp
new file mode 100644 (file)
index 0000000..14a9b0f
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dali-toolkit/dali-toolkit.h>
+#include <dali/dali.h>
+
+#include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
+#include <dali-toolkit/devel-api/visuals/visual-properties-devel.h>
+#include <dali/devel-api/adaptor-framework/key-devel.h>
+#include <dali/devel-api/events/hit-test-algorithm.h>
+
+#include <iostream>
+#include <string>
+
+#include "generated/rendering-textured-shape-frag.h"
+#include "generated/rendering-textured-shape-vert.h"
+#include "physics-actor.h"
+#include "physics-impl.h"
+
+using namespace Dali;
+
+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"};
+
+/**
+ * @brief The physics demo using Chipmunk2D APIs.
+ */
+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();
+
+    mPhysicsRoot = mPhysicsImpl.Initialize(mWindow);
+    mPhysicsRoot.TouchedSignal().Connect(this, &PhysicsDemoController::OnTouched);
+
+    mWindow.Add(mPhysicsRoot);
+
+    CreateBall();
+    CreateBrickPyramid(windowSize);
+
+    // For funky mouse drag
+    mMouseBody = mPhysicsImpl.AddMouseBody();
+  }
+
+  void CreateBall()
+  {
+    const float BALL_MASS       = 10.0f;
+    const float BALL_RADIUS     = 26.0f;
+    const float BALL_ELASTICITY = 0.5f;
+    const float BALL_FRICTION   = 0.5f;
+
+    Property::Value v{std::string{SHADER_RENDERING_TEXTURED_SHAPE_VERT}};
+    Property::Value f{std::string{SHADER_RENDERING_TEXTURED_SHAPE_FRAG}};
+
+    auto image = Property::Map{{Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE},
+                               {Toolkit::ImageVisual::Property::URL, BALL_IMAGE},
+                               {Toolkit::Visual::Property::SHADER, {{Toolkit::Visual::Shader::Property::FRAGMENT_SHADER, f}}}};
+
+    auto ball                                 = Toolkit::ImageView::New();
+    ball[Toolkit::ImageView::Property::IMAGE] = image;
+    mPhysicsImpl.AddBall(ball, BALL_MASS, BALL_RADIUS, BALL_ELASTICITY, BALL_FRICTION);
+  }
+
+  void CreateBrickPyramid(Dali::Window::WindowSize windowSize)
+  {
+    const float BRICK_MASS       = 30.0f;
+    const float BRICK_ELASTICITY = 0.0f;
+    const float BRICK_FRICTION   = 0.9f;
+    const int   BRICK_WIDTH      = 128;
+    const int   BRICK_HEIGHT     = 64;
+    const int   BRICK_GAP        = 8;
+
+    Property::Value v{std::string{SHADER_RENDERING_TEXTURED_SHAPE_VERT}};
+    Property::Value f{std::string{SHADER_RENDERING_TEXTURED_SHAPE_FRAG}};
+
+    int uriIndex     = 0;
+    int numberOfRows = windowSize.GetWidth() / (BRICK_WIDTH + BRICK_GAP) - 2;
+    int oY           = windowSize.GetHeight() - (1 + numberOfRows) * BRICK_HEIGHT;
+    for(int i = 0; i < numberOfRows; ++i)
+    {
+      // Row start: i+1 is brick number. i is gap#
+      int w  = (i + 1) * BRICK_WIDTH + i * BRICK_GAP;
+      int oX = (windowSize.GetWidth() - w) / 2;
+      for(int j = 0; j < i + 1; ++j)
+      {
+        auto image = Property::Map{{Toolkit::Visual::Property::TYPE, Toolkit::Visual::IMAGE},
+                                   {Toolkit::ImageVisual::Property::URL, BRICK_URIS[uriIndex]},
+                                   {Toolkit::Visual::Property::SHADER, {{Toolkit::Visual::Shader::Property::FRAGMENT_SHADER, f}}}};
+
+        auto brick                                 = Toolkit::ImageView::New();
+        brick[Toolkit::ImageView::Property::IMAGE] = image;
+        auto& physicsActor                         = mPhysicsImpl.AddBrick(brick, BRICK_MASS, BRICK_ELASTICITY, BRICK_FRICTION, Vector3(BRICK_WIDTH, BRICK_HEIGHT, BRICK_HEIGHT));
+        physicsActor.SetPhysicsPosition(Vector3(oX + j * (BRICK_WIDTH + BRICK_GAP), oY + i * BRICK_HEIGHT, 0.0f));
+        uriIndex += 1;
+        uriIndex %= 4;
+      }
+    }
+  }
+  void OnTerminate(Application& application)
+  {
+    UnparentAndReset(mPhysicsRoot);
+  }
+
+  void OnWindowResize(Window window, Window::WindowSize newSize)
+  {
+    mPhysicsImpl.CreateWorldBounds(newSize);
+  }
+
+  bool OnTouched(Dali::Actor actor, const Dali::TouchEvent& touch)
+  {
+    static enum {
+      None,
+      MoveCameraXZ,
+      MovePivot,
+    } state = None;
+
+    auto    renderTask   = mWindow.GetRenderTaskList().GetTask(0);
+    auto    screenCoords = touch.GetScreenPosition(0);
+    Vector3 origin, direction;
+    Dali::HitTestAlgorithm::BuildPickingRay(renderTask, screenCoords, origin, direction);
+
+    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;
+            Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
+
+            Vector3 localPivot;
+            float   pickingDistance;
+            auto    body = mPhysicsImpl.HitTest(screenCoords, origin, direction, localPivot, pickingDistance);
+            if(body)
+            {
+              mPickedBody = body;
+              mPhysicsImpl.HighlightBody(mPickedBody, true);
+              mPickedSavedState = mPhysicsImpl.ActivateBody(mPickedBody);
+              mPickedConstraint = mPhysicsImpl.AddPivotJoint(mPickedBody, mMouseBody, localPivot);
+            }
+          }
+        }
+        break;
+      }
+      case MovePivot:
+      {
+        if(touch.GetState(0) == Dali::PointState::MOTION)
+        {
+          if(mPickedBody && mPickedConstraint)
+          {
+            if(!mShiftDown)
+            {
+              // Move point in XY plane, projected into scene
+              Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
+
+              Vector3 position = mPhysicsImpl.TranslateToPhysicsSpace(Vector3(screenCoords));
+              mPhysicsImpl.MoveMouseBody(mMouseBody, position);
+            }
+            else
+            {
+              // Move point in XZ plane
+              // Above vanishing pt, it's on top plane of frustum; below vanishing pt it's on bottom plane.
+              // Kind of want to project onto the plane using initial touch xy, rather than top/bottom.
+              // Whole new projection code needed.
+
+              // Cheat!
+            }
+          }
+        }
+        else if(touch.GetState(0) == Dali::PointState::FINISHED ||
+                touch.GetState(0) == Dali::PointState::INTERRUPTED)
+        {
+          if(mPickedConstraint)
+          {
+            mPhysicsImpl.HighlightBody(mPickedBody, false);
+
+            Dali::Mutex::ScopedLock lock(mPhysicsImpl.mMutex);
+            mPhysicsImpl.RestoreBodyState(mPickedBody, mPickedSavedState);
+            mPhysicsImpl.ReleaseConstraint(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;
+      }
+    }
+
+    //std::cout<<"Touch State: "<<state<<std::endl;
+    Stage::GetCurrent().KeepRendering(30.0f);
+
+    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(" "))
+          {
+            mPhysicsImpl.ToggleIntegrateState();
+          }
+          else if(!event.GetKeyString().compare("m"))
+          {
+            mPhysicsImpl.ToggleDebugState();
+          }
+          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;
+        }
+      }
+    }
+  }
+
+private:
+  Application& mApplication;
+  Window       mWindow;
+
+  PhysicsImpl   mPhysicsImpl;
+  Actor         mPhysicsRoot;
+  cpBody*       mMouseBody{nullptr};
+  cpBody*       mPickedBody{nullptr};
+  cpConstraint* mPickedConstraint{nullptr};
+  int           mPickedSavedState = -1; /// 0 : Active, 1 : Sleeping
+
+  bool mCtrlDown{false};
+  bool mAltDown{false};
+  bool mShiftDown{false};
+};
+
+int DALI_EXPORT_API main(int argc, char** argv)
+{
+  Application           application = Application::New(&argc, &argv);
+  PhysicsDemoController controller(application);
+  application.MainLoop();
+  return 0;
+}
diff --git a/examples/chipmunk-physics/physics-impl.cpp b/examples/chipmunk-physics/physics-impl.cpp
new file mode 100644 (file)
index 0000000..12e94a8
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "physics-impl.h"
+#include "physics-actor.h"
+
+#include <devel-api/common/stage.h>
+#include <map>
+#include <utility>
+#include <iostream>
+
+using Dali::Layer;
+using Dali::Actor;
+using Dali::Window;
+using Dali::Vector2;
+using Dali::Vector3;
+using Dali::Stage;
+using namespace Dali::DevelStage;
+
+#define GRABBABLE_MASK_BIT (1u<<31)
+cpShapeFilter GRAB_FILTER = {CP_NO_GROUP, GRABBABLE_MASK_BIT, GRABBABLE_MASK_BIT};
+cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT};
+
+Actor PhysicsImpl::Initialize(Window window)
+{
+  mWindow = window;
+  mSpace = cpSpaceNew();
+  cpSpaceSetIterations(mSpace, 30);
+  cpSpaceSetSleepTimeThreshold(mSpace, 0.5f);
+  cpSpaceSetGravity(mSpace, cpv(0, -200));
+
+  auto windowSize = window.GetSize();
+  CreateWorldBounds(windowSize);
+
+  // Create an actor that can handle mouse events.
+  mPhysicsRoot = Layer::New();
+  mPhysicsRoot[Actor::Property::SIZE] = Vector2(windowSize.GetWidth(), windowSize.GetHeight());
+  mPhysicsRoot[Actor::Property::ANCHOR_POINT] = Dali::AnchorPoint::CENTER;
+  mPhysicsRoot[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
+
+  mFrameCallback = new FrameCallback(*this);
+  AddFrameCallback(Stage::GetCurrent(), *mFrameCallback, window.GetRootLayer());
+  Stage::GetCurrent().KeepRendering(30);
+
+
+  return mPhysicsRoot;
+}
+
+Layer PhysicsImpl::CreateDebug(Vector2 windowSize)
+{
+  return Layer();
+}
+
+void PhysicsImpl::CreateWorldBounds(Window::WindowSize size)
+{
+  // Physics origin is 0,0,0 in DALi coords.
+  // But, Y is inverted, so bottom is -ve, top is +ve.
+  // Perform this correction when applying position to actor.
+  // But, can't use actors in update, so cache transform.
+  SetTransform(Vector2(size.GetWidth(), size.GetHeight()));
+
+  int xBound=size.GetWidth()/2;
+  int yBound=size.GetHeight()/2;
+
+  cpBody *staticBody = cpSpaceGetStaticBody(mSpace);
+
+  if(mLeftBound)
+  {
+    cpSpaceRemoveShape(mSpace, mLeftBound);
+    cpSpaceRemoveShape(mSpace, mRightBound);
+    cpSpaceRemoveShape(mSpace, mTopBound);
+    cpSpaceRemoveShape(mSpace, mBottomBound);
+    cpShapeFree(mLeftBound);
+    cpShapeFree(mRightBound);
+    cpShapeFree(mTopBound);
+    cpShapeFree(mBottomBound);
+  }
+  mLeftBound   = AddBound(staticBody, cpv(-xBound, -yBound), cpv(-xBound,  yBound));
+  mRightBound  = AddBound(staticBody, cpv( xBound, -yBound), cpv( xBound,  yBound));
+  mTopBound    = AddBound(staticBody, cpv(-xBound, -yBound), cpv( xBound, -yBound));
+  mBottomBound = AddBound(staticBody, cpv(-xBound,  yBound), cpv( xBound,  yBound));
+}
+
+void PhysicsImpl::SetTransform(Vector2 worldSize)
+{
+  mWorldOffset.x = worldSize.x * 0.5f;
+  mWorldOffset.y = worldSize.y * 0.5f;
+  // y is always inverted.
+}
+
+cpShape* PhysicsImpl::AddBound(cpBody* staticBody, cpVect start, cpVect end)
+{
+  cpShape* shape = cpSpaceAddShape(mSpace, cpSegmentShapeNew(staticBody,start, end,0.0f));
+  cpShapeSetElasticity(shape, 1.0f);
+  cpShapeSetFriction(shape, 1.0f);
+  cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER);
+  return shape;
+}
+
+PhysicsActor& PhysicsImpl::AddBall(::Actor actor, float mass, float radius, float elasticity, float friction)
+{
+  Dali::Mutex::ScopedLock lock(mMutex);
+  cpBody* body = cpSpaceAddBody(mSpace, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
+  cpBodySetPosition(body, cpv(0, 0));
+  cpBodySetVelocity(body, cpv(0, 0));
+
+  cpShape* shape = cpSpaceAddShape(mSpace, cpCircleShapeNew(body, radius, cpvzero));
+  cpShapeSetElasticity(shape, elasticity);
+  cpShapeSetFriction(shape, friction);
+
+  int id = actor[Actor::Property::ID];
+  Dali::Property::Index index = actor.RegisterProperty("uBrightness", 0.0f);
+  mPhysicsActors.insert(std::make_pair(id, PhysicsActor{actor, body, this, index}));
+  actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::TOP_LEFT;
+  actor[Actor::Property::ANCHOR_POINT] = Dali::AnchorPoint::CENTER;
+  mPhysicsRoot.Add(actor);
+  return mPhysicsActors.at(id);
+}
+
+PhysicsActor& PhysicsImpl::AddBrick(Dali::Actor actor, float mass, float elasticity, float friction, Vector3 size)
+{
+  Dali::Mutex::ScopedLock lock(mMutex);
+  cpBody* body = cpSpaceAddBody(mSpace, cpBodyNew(mass, cpMomentForBox(mass, size.width, size.height)));
+  cpBodySetPosition(body, cpv(0, 0));
+  cpBodySetVelocity(body, cpv(0, 0));
+
+  cpShape* shape = cpSpaceAddShape(mSpace, cpBoxShapeNew(body, size.width, size.height, 0.0f));
+  cpShapeSetFriction(shape, friction);
+  cpShapeSetElasticity(shape, elasticity);
+
+  int id = actor[Actor::Property::ID];
+  Dali::Property::Index index = actor.RegisterProperty("uBrightness", 0.0f);
+  mPhysicsActors.insert(std::make_pair(id, PhysicsActor{actor, body, this, index}));
+  actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::TOP_LEFT;
+  actor[Actor::Property::ANCHOR_POINT] = Dali::AnchorPoint::CENTER;
+  mPhysicsRoot.Add(actor);
+  return mPhysicsActors.at(id);
+}
+
+cpBody* PhysicsImpl::AddMouseBody()
+{
+  Dali::Mutex::ScopedLock lock(mMutex);
+  auto kinematicBody = cpBodyNewKinematic(); // Mouse actor is a kinematic body that is not integrated
+  return kinematicBody;
+}
+
+PhysicsActor* PhysicsImpl::GetPhysicsActor(cpBody* body)
+{
+  return reinterpret_cast<PhysicsActor*>(cpBodyGetUserData(body));
+}
+
+void PhysicsImpl::HighlightBody(cpBody* body, bool highlight)
+{
+  auto physicsActor = GetPhysicsActor(body);
+  if(physicsActor)
+  {
+    Actor actor = mPhysicsRoot.FindChildById(physicsActor->GetId());
+    if(actor)
+    {
+      actor[physicsActor->GetBrightnessIndex()] = highlight?1.0f:0.0f;
+    }
+  }
+}
+
+// Convert from root actor local space to physics space
+Vector3 PhysicsImpl::TranslateToPhysicsSpace(Vector3 vector)
+{
+  // root actor origin is top left, DALi Y is inverted.
+  // Physics origin is center. Y: 0->1 => 0.5=>-0.5
+  return Vector3(vector.x-mWorldOffset.x, mWorldOffset.y-vector.y, vector.z);
+}
+
+// Convert from physics space to root actor local space
+Vector3 PhysicsImpl::TranslateFromPhysicsSpace(Vector3 vector)
+{
+  return Vector3(vector.x+mWorldOffset.x, mWorldOffset.y-vector.y, vector.z);
+}
+
+// Convert a vector from dali space to physics space
+Vector3 PhysicsImpl::ConvertVectorToPhysicsSpace(Vector3 vector)
+{
+  // root actor origin is top left, DALi Y is inverted.
+  // @todo Add space config scale.
+  return Vector3(vector.x, -vector.y, vector.z);
+}
+
+// Convert a vector physics space to root actor local space
+Vector3 PhysicsImpl::ConvertVectorFromPhysicsSpace(Vector3 vector)
+{
+  return Vector3(vector.x, -vector.y, vector.z);
+}
+
+void PhysicsImpl::Integrate(float timestep)
+{
+  if(mPhysicsIntegrateState)
+  {
+    cpSpaceStep(mSpace, timestep);
+  }
+//  if(mDynamicsWorld->getDebugDrawer() && mPhysicsDebugState)
+//  {
+//    mDynamicsWorld->debugDrawWorld();
+//  }
+}
+
+cpBody* PhysicsImpl::HitTest(Vector2 screenCoords, Vector3 origin, Vector3 direction, Vector3& localPivot, float& distanceFromCamera)
+{
+  Vector3 spacePosition = TranslateToPhysicsSpace(Vector3{screenCoords});
+  cpVect mousePosition = cpv(spacePosition.x, spacePosition.y);
+  cpFloat radius = 5.0f;
+  cpPointQueryInfo info = {0};
+  cpShape *shape = cpSpacePointQueryNearest(mSpace, mousePosition, radius, GRAB_FILTER, &info);
+
+  cpBody *body{nullptr};
+
+  if(shape && cpBodyGetMass(cpShapeGetBody(shape)) < INFINITY)
+  {
+    // Use the closest point on the surface if the click is outside the shape.
+    cpVect nearest = (info.distance > 0.0f ? info.point : mousePosition);
+    body = cpShapeGetBody(shape);
+    cpVect local = cpBodyWorldToLocal(body, nearest);
+    localPivot.x = local.x;
+    localPivot.y = local.y;
+    localPivot.z = 0.0;
+  }
+  return body;
+}
+
+
+cpConstraint* PhysicsImpl::AddPivotJoint(cpBody* body1, cpBody* body2, Vector3 localPivot)
+{
+  cpVect pivot{localPivot.x, localPivot.y};
+  cpConstraint* joint = cpPivotJointNew2(body2, body1, cpvzero, pivot);
+  cpConstraintSetMaxForce(joint, 50000.0f); // Magic numbers for mouse feedback.
+  cpConstraintSetErrorBias(joint, cpfpow(1.0f - 0.15f, 60.0f));
+  cpConstraint* constraint = cpSpaceAddConstraint(mSpace, joint);
+  return constraint; // Constraint & joint are the same...
+}
+
+void PhysicsImpl::MoveMouseBody(cpBody* mouseBody, Vector3 position)
+{
+  cpVect cpPosition = cpv(position.x, position.y);
+  cpVect newPoint = cpvlerp(cpBodyGetPosition(mouseBody), cpPosition, 0.25f);
+  cpBodySetVelocity(mouseBody, cpvmult(cpvsub(newPoint, cpBodyGetPosition(mouseBody)), 60.0f));
+  // Normally, kinematic body's position would be calculated by engine.
+  // For mouse, though, we want to set it.
+  cpBodySetPosition(mouseBody, newPoint);
+}
+
+void PhysicsImpl::MoveConstraint(cpConstraint* constraint, Vector3 newPosition)
+{
+}
+
+void PhysicsImpl::ReleaseConstraint(cpConstraint* constraint)
+{
+  cpSpaceRemoveConstraint(mSpace, constraint);
+  cpConstraintFree(constraint);
+}
+
+int PhysicsImpl::ActivateBody(cpBody* body)
+{
+  int oldState = cpBodyIsSleeping(body);
+  cpBodyActivate(body);
+
+  return oldState;
+}
+
+void PhysicsImpl::RestoreBodyState(cpBody* body, int oldState)
+{
+  if(oldState)
+  {
+    cpBodyActivate(body);
+  }
+  else
+  {
+    cpBodySleep(body);
+  }
+}
diff --git a/examples/chipmunk-physics/physics-impl.h b/examples/chipmunk-physics/physics-impl.h
new file mode 100644 (file)
index 0000000..9420fa4
--- /dev/null
@@ -0,0 +1,175 @@
+#ifndef DALI_PHYSICS_DEMO_PHYSICS_IMPL_H
+#define DALI_PHYSICS_DEMO_PHYSICS_IMPL_H
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/dali.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/common/stage-devel.h>
+
+#include <map>
+#include <chipmunk/chipmunk.h>
+
+#include "physics-actor.h"
+#include "frame-callback.h"
+
+
+class PhysicsImpl : public Dali::ConnectionTracker
+{
+public:
+  Dali::Actor Initialize(Dali::Window window);
+
+  /**
+   * Create a layer & debug renderer
+   */
+  Dali::Layer CreateDebug(Dali::Vector2 windowSize);
+
+  /**
+   * Converts a point in RootActor local coords (e.g. gesture)
+   * into physics space coords.
+   * @param vector The point to convert
+   * @return The converted point
+   */
+  Dali::Vector3 TranslateToPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Converts a point in physics space coords.
+   * into RootActor local coords
+   * @param vector The point to convert
+   * @return The converted point
+   */
+  Dali::Vector3 TranslateFromPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Converts a vector in DALi space into physics space.
+   * @param vector The vector to convert
+   * @return The converted vector
+   */
+  Dali::Vector3 ConvertVectorToPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Converts a vector in physics space to DALi space
+   * @param vector The vector to convert
+   * @return The converted vector
+   */
+  Dali::Vector3 ConvertVectorFromPhysicsSpace(Dali::Vector3 vector);
+
+  /**
+   * Set up the transform from world space to physics space
+   * @param[in] worldSize The 2d bounding box of the world in screen space
+   */
+  void SetTransform(Dali::Vector2 worldSize);
+
+  /**
+   * Run the physics integration over the given timestep.
+   * @param timeStep
+   */
+  void Integrate(float timeStep);
+
+  /**
+   * Toggle the integration state. If it's turned on, physics will run
+   * during the frame callback.
+   */
+  void ToggleIntegrateState()
+  {
+    mPhysicsIntegrateState ^= true;
+  }
+
+  /**
+   * Toggle the debug state. If debug is turned on, use the physics engine
+   * debug to show wireframes.
+   */
+  void ToggleDebugState()
+  {
+    mPhysicsDebugState ^= true;
+  }
+
+  void CreateWorldBounds(Dali::Window::WindowSize size);
+  cpShape* AddBound(cpBody* staticBody, cpVect start, cpVect end);
+  PhysicsActor& AddBall(Dali::Actor actor, float mass, float radius, float elasticity, float friction);
+  PhysicsActor& AddBrick(Dali::Actor actor,float mass, float elasticity, float friction, Dali::Vector3 size);
+
+  cpBody* AddMouseBody();
+
+  /**
+   * @param[in] screenCoords The touch point in screen coordinates
+   * @param[in] origin The camera origin in DALi world space
+   * @param[in] direction The ray direction in DALi world space
+   * @param[out] localPivot The hit point local to the body
+   * @param[out] distanceFromCamera The distance of the pick point from the camera
+   * @return nullptr if no dynamic body found, otherwise a valid ptr to the hit body.
+   */
+  cpBody* HitTest(Dali::Vector2 screenCoords, Dali::Vector3 origin, Dali::Vector3 direction,
+                  Dali::Vector3& localPivot, float& distanceFromCamera);
+
+  cpConstraint* AddPivotJoint(cpBody* body1, cpBody* body2, Dali::Vector3 localPivot);
+
+  void MoveMouseBody(cpBody* mouseBody, Dali::Vector3 position);
+
+  void MoveConstraint(cpConstraint* constraint, Dali::Vector3 newPosition);
+
+  void ReleaseConstraint(cpConstraint* constraint);
+
+  /**
+   * Ensure that the physics body does not go to sleep
+   * @param[in] body The physics body
+   * @return The old state
+   */
+  int ActivateBody(cpBody* body);
+
+  /**
+   * Restore the state of the physics body
+   * @param[in] body The physics body
+   * @param[in] oldState The previous state to restore
+   */
+  void RestoreBodyState(cpBody* body, int oldState);
+
+  /**
+   * Get the physics actor associated with the given body
+   * @param[in] body The physics body
+   * @return the associated physics actor
+   */
+  PhysicsActor* GetPhysicsActor(cpBody* body);
+
+  /**
+   * Set the highlight state of the actor associated with the physics body
+   * @param[in] body The physics body
+   * @param[in] highlight Whether to turn the highlight on or off.
+   */
+  void HighlightBody(cpBody* body, bool highlight);
+
+
+public:
+  std::map<uint32_t, PhysicsActor> mPhysicsActors;
+  bool mPhysicsIntegrateState{true};
+  bool mPhysicsDebugState{true};
+
+  cpSpace* mSpace{nullptr};
+  cpShape* mLeftBound{nullptr};
+  cpShape* mRightBound{nullptr};
+  cpShape* mTopBound{nullptr};
+  cpShape* mBottomBound{nullptr};
+
+  Dali::Window mWindow;
+  Dali::Mutex mMutex;
+
+  Dali::Actor mPhysicsRoot;
+  Dali::Vector2 mWorldOffset;
+  FrameCallback* mFrameCallback{nullptr};
+};
+
+#endif // DALI_PHYSICS_DEMO_PHYSICS_IMPL_H
diff --git a/examples/chipmunk-physics/shaders/rendering-textured-shape.frag b/examples/chipmunk-physics/shaders/rendering-textured-shape.frag
new file mode 100644 (file)
index 0000000..42bf947
--- /dev/null
@@ -0,0 +1,29 @@
+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 vec3 pcol=texColor.rgb*(1.0+uBrightness);
+  gl_FragColor = vec4( redistribute_rgb(pcol), texColor.a);
+}
\ No newline at end of file
diff --git a/examples/chipmunk-physics/shaders/rendering-textured-shape.vert b/examples/chipmunk-physics/shaders/rendering-textured-shape.vert
new file mode 100644 (file)
index 0000000..4c41f54
--- /dev/null
@@ -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 = aPosition.xy*2.0;
+  gl_Position = uMvpMatrix * vertexPosition;
+}
\ No newline at end of file
index 1cd4339..ea7a0d1 100755 (executable)
@@ -2,7 +2,7 @@
 
 Name:       com.samsung.dali-demo
 Summary:    The OpenGLES Canvas Core Demo
-Version:    2.2.37
+Version:    2.2.38
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0
@@ -23,6 +23,7 @@ BuildRequires:  pkgconfig(dali2-core)
 BuildRequires:  pkgconfig(dali2-adaptor)
 BuildRequires:  pkgconfig(dali2-toolkit)
 BuildRequires:  pkgconfig(dali2-scene3d)
+BuildRequires:  pkgconfig(dali2-physics-2d)
 BuildRequires:  pkgconfig(libtzplatform-config)
 BuildRequires:  pkgconfig(gles20)
 BuildRequires:  pkgconfig(glesv2)
index c7abd09..d35f25d 100755 (executable)
@@ -43,6 +43,9 @@ msgstr "Card Active"
 msgid "DALI_DEMO_STR_TITLE_COMPRESSED_TEXTURE_FORMATS"
 msgstr "Compressed Texture Formats"
 
+msgid "DALI_DEMO_STR_TITLE_CHIPMUNK_PHYSICS"
+msgstr "Chipmunk Physics"
+
 msgid "DALI_DEMO_STR_TITLE_CLIPPING"
 msgstr "Clipping"
 
index 266c445..9a5c9b8 100755 (executable)
@@ -46,6 +46,9 @@ msgstr "Card Active"
 msgid "DALI_DEMO_STR_TITLE_COMPRESSED_TEXTURE_FORMATS"
 msgstr "Compressed Texture Formats"
 
+msgid "DALI_DEMO_STR_TITLE_CHIPMUNK_PHYSICS"
+msgstr "Chipmunk Physics"
+
 msgid "DALI_DEMO_STR_TITLE_CLIPPING"
 msgstr "Clipping"
 
index a371246..3c31c54 100644 (file)
@@ -50,6 +50,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_CANVAS_VIEW dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CANVAS_VIEW")
 #define DALI_DEMO_STR_TITLE_CALL_ACTIVE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CALL_ACTIVE")
 #define DALI_DEMO_STR_TITLE_CARD_ACTIVE dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CARD_ACTIVE")
+#define DALI_DEMO_STR_TITLE_CHIPMUNK_PHYSICS dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CHIPMUNK_PHYSICS")
 #define DALI_DEMO_STR_TITLE_CLIPPING dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CLIPPING")
 #define DALI_DEMO_STR_TITLE_CLIPPING_DRAW_ORDER dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_CLIPPING_DRAW_ORDER")
 #define DALI_DEMO_STR_TITLE_COLOR_TRANSITION dgettext(DALI_DEMO_DOMAIN_LOCAL, "DALI_DEMO_STR_TITLE_COLOR_TRANSITION")
@@ -169,6 +170,7 @@ extern "C"
 #define DALI_DEMO_STR_TITLE_CANVAS_VIEW "Canvas View"
 #define DALI_DEMO_STR_TITLE_CALL_ACTIVE "Call Active"
 #define DALI_DEMO_STR_TITLE_CARD_ACTIVE "Card Active"
+#define DALI_DEMO_STR_TITLE_CHIPMUNK_PHYSICS "Chipmunk Physics"
 #define DALI_DEMO_STR_TITLE_CLIPPING "Clipping"
 #define DALI_DEMO_STR_TITLE_CLIPPING_DRAW_ORDER "Clipping Draw Order"
 #define DALI_DEMO_STR_TITLE_COLOR_TRANSITION "Color Transition"