Bug fixes for 2d physics
[platform/core/uifw/dali-toolkit.git] / dali-physics / internal / physics-world-impl.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 // Class Header
18 #include <dali-physics/internal/physics-world-impl.h>
19
20 // External Headers
21
22 // Internal Headers
23 #include <dali/dali.h>
24 #include <dali/devel-api/common/stage-devel.h>
25 #include <dali/devel-api/update/frame-callback-interface.h>
26
27 namespace Dali::Toolkit::Physics::Internal
28 {
29 /**
30  * FrameCallback implementation. Will run the OnUpdate method.
31  */
32 class FrameCallback : public Dali::FrameCallbackInterface
33 {
34 public:
35   /**
36    * Constructor
37    */
38   explicit FrameCallback(PhysicsWorld& physicsWorld)
39   : mPhysicsWorld(physicsWorld)
40   {
41   }
42
43 private:
44   /**
45    * Called each frame.
46    * @param[in] updateProxy Used to set world matrix and size
47    * @param[in] elapsedSeconds Time since last frame
48    * @return Whether we should keep rendering.
49    */
50   bool Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds) override
51   {
52     return mPhysicsWorld.OnUpdate(updateProxy, elapsedSeconds);
53   }
54
55 private: // Member variables
56   PhysicsWorld& mPhysicsWorld;
57 };
58
59 PhysicsWorld::PhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
60 : mUpdateCallback(updateCallback),
61   mRootActor(rootActor)
62 {
63 }
64
65 void PhysicsWorld::Initialize()
66 {
67   // Call derived class's initializer
68   OnInitialize();
69
70   // Automatically start the frame callback. This means everything should
71   // be accessed with a mutex lock, which is automatically locked when
72   // ScopedAccessor is used.
73   mFrameCallback = std::make_unique<FrameCallback>(*this);
74   Dali::DevelStage::AddFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback, mRootActor);
75   Dali::Stage::GetCurrent().KeepRendering(30); // @todo Remove!
76 }
77
78 PhysicsWorld::~PhysicsWorld()
79 {
80   // Derived class's destructor should clean down physics objects under mutex lock
81   // On completion, can remove the callback.
82
83   Dali::DevelStage::RemoveFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
84 }
85
86 Dali::Mutex& PhysicsWorld::GetMutex()
87 {
88   return mMutex;
89 }
90
91 bool PhysicsWorld::OnUpdate(Dali::UpdateProxy& updateProxy, float elapsedSeconds)
92 {
93   Dali::Mutex::ScopedLock lock(mMutex);
94
95   // Process command queue
96   if(mNotifySyncPoint != Dali::UpdateProxy::INVALID_SYNC &&
97      mNotifySyncPoint == updateProxy.PopSyncPoint())
98   {
99     while(!commandQueue.empty())
100     {
101       commandQueue.front()(); // Execute the queued methods
102       commandQueue.pop();
103     }
104
105     mNotifySyncPoint = Dali::UpdateProxy::INVALID_SYNC;
106   }
107
108   // Perform as many integration steps as needed to handle elapsed time
109   static float frameTime = 0;
110   frameTime += elapsedSeconds;
111   do
112   {
113     Integrate(mPhysicsTimeStep);
114     frameTime -= mPhysicsTimeStep;
115   } while(frameTime > 0);
116
117   // Update the corresponding actors to their physics spaces
118   if(mUpdateCallback)
119   {
120     Dali::CallbackBase::Execute(*mUpdateCallback, &updateProxy); // Don't care about actor update return
121   }
122
123   // @todo Check physics world to see if everything is at rest
124   return true;
125 }
126
127 void PhysicsWorld::SetTimestep(float timeStep)
128 {
129   mPhysicsTimeStep = timeStep;
130 }
131
132 float PhysicsWorld::GetTimestep()
133 {
134   return mPhysicsTimeStep;
135 }
136
137 void PhysicsWorld::Queue(std::function<void(void)> function)
138 {
139   if(!mMutex.IsLocked()) // Annoyingly, the dali mutex scoped lock doesn't prevent relocking in the same thread.
140   {
141     Dali::Mutex::ScopedLock lock(mMutex);
142     commandQueue.push(function);
143   }
144   else
145   {
146     commandQueue.push(function);
147   }
148 }
149
150 void PhysicsWorld::CreateSyncPoint()
151 {
152   mNotifySyncPoint = Dali::DevelStage::NotifyFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
153 }
154
155 void PhysicsWorld::SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state)
156 {
157   mPhysicsIntegrateState = state;
158 }
159
160 Physics::PhysicsAdaptor::IntegrationState PhysicsWorld::GetIntegrationState()
161 {
162   return mPhysicsIntegrateState;
163 }
164
165 void PhysicsWorld::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
166 {
167   mPhysicsDebugState = state;
168 }
169
170 Physics::PhysicsAdaptor::DebugState PhysicsWorld::GetDebugState()
171 {
172   return mPhysicsDebugState;
173 }
174
175 } // namespace Dali::Toolkit::Physics::Internal