[dali_2.3.21] Merge branch 'devel/master'
[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 thread_local int gLocked{0};
28
29 namespace Dali::Toolkit::Physics::Internal
30 {
31 /**
32  * FrameCallback implementation. Will run the OnUpdate method.
33  */
34 class FrameCallback : public Dali::FrameCallbackInterface
35 {
36 public:
37   /**
38    * Constructor
39    */
40   explicit FrameCallback(PhysicsWorld& physicsWorld)
41   : mPhysicsWorld(physicsWorld)
42   {
43   }
44
45 private:
46   /**
47    * Called each frame.
48    * @param[in] updateProxy Used to set world matrix and size
49    * @param[in] elapsedSeconds Time since last frame
50    * @return Whether we should keep rendering.
51    */
52   bool Update(Dali::UpdateProxy& updateProxy, float elapsedSeconds) override
53   {
54     return mPhysicsWorld.OnUpdate(updateProxy, elapsedSeconds);
55   }
56
57 private: // Member variables
58   PhysicsWorld& mPhysicsWorld;
59 };
60
61 PhysicsWorld::PhysicsWorld(Dali::Actor rootActor, Dali::CallbackBase* updateCallback)
62 : mUpdateCallback(updateCallback),
63   mRootActor(rootActor)
64 {
65 }
66
67 void PhysicsWorld::Initialize()
68 {
69   // Call derived class's initializer
70   OnInitialize();
71
72   // Automatically start the frame callback. This means everything should
73   // be accessed with a mutex lock, which is automatically locked when
74   // ScopedAccessor is used.
75   mFrameCallback = std::make_unique<FrameCallback>(*this);
76   Dali::DevelStage::AddFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback, mRootActor);
77   Dali::Stage::GetCurrent().KeepRendering(30); // @todo Remove!
78 }
79
80 PhysicsWorld::~PhysicsWorld()
81 {
82   // Derived class's destructor should clean down physics objects under mutex lock
83   // On completion, can remove the callback.
84
85   Dali::DevelStage::RemoveFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
86 }
87
88 bool PhysicsWorld::OnUpdate(Dali::UpdateProxy& updateProxy, float elapsedSeconds)
89 {
90   ScopedLock lock(*this);
91
92   // Process command queue
93   if(mNotifySyncPoint != Dali::UpdateProxy::INVALID_SYNC &&
94      mNotifySyncPoint == updateProxy.PopSyncPoint())
95   {
96     while(!commandQueue.empty())
97     {
98       commandQueue.front()(); // Execute the queued methods
99       commandQueue.pop();
100     }
101
102     mNotifySyncPoint = Dali::UpdateProxy::INVALID_SYNC;
103   }
104
105   // Perform as many integration steps as needed to handle elapsed time
106   static float frameTime = 0;
107   frameTime += elapsedSeconds;
108   do
109   {
110     Integrate(mPhysicsTimeStep);
111     frameTime -= mPhysicsTimeStep;
112   } while(frameTime > 0);
113
114   // Update the corresponding actors to their physics spaces
115   if(mUpdateCallback)
116   {
117     Dali::CallbackBase::Execute(*mUpdateCallback, &updateProxy); // Don't care about actor update return
118   }
119
120   // @todo Check physics world to see if everything is at rest
121   return true;
122 }
123
124 void PhysicsWorld::SetTimestep(float timeStep)
125 {
126   mPhysicsTimeStep = timeStep;
127 }
128
129 float PhysicsWorld::GetTimestep()
130 {
131   return mPhysicsTimeStep;
132 }
133
134 /**
135  * Lock the mutex.
136  */
137 void PhysicsWorld::Lock()
138 {
139   //@todo Could replace the mutex with an atomic flag, if it's not set,
140   // the queue and integration step could be skipped
141   if(!gLocked)
142   {
143     gLocked = true;
144     mMutex.lock();
145   }
146 }
147
148 /**
149  * Unlock the mutex
150  */
151 void PhysicsWorld::Unlock()
152 {
153   mMutex.unlock();
154   gLocked = false;
155 }
156
157 void PhysicsWorld::Queue(std::function<void(void)> function)
158 {
159   ScopedLock lock(*this);
160   commandQueue.push(function);
161 }
162
163 void PhysicsWorld::CreateSyncPoint()
164 {
165   mNotifySyncPoint = Dali::DevelStage::NotifyFrameCallback(Dali::Stage::GetCurrent(), *mFrameCallback);
166 }
167
168 void PhysicsWorld::SetIntegrationState(Physics::PhysicsAdaptor::IntegrationState state)
169 {
170   mPhysicsIntegrateState = state;
171 }
172
173 Physics::PhysicsAdaptor::IntegrationState PhysicsWorld::GetIntegrationState()
174 {
175   return mPhysicsIntegrateState;
176 }
177
178 void PhysicsWorld::SetDebugState(Physics::PhysicsAdaptor::DebugState state)
179 {
180   mPhysicsDebugState = state;
181 }
182
183 Physics::PhysicsAdaptor::DebugState PhysicsWorld::GetDebugState()
184 {
185   return mPhysicsDebugState;
186 }
187
188 } // namespace Dali::Toolkit::Physics::Internal