Added bullet-physics-demo
[platform/core/uifw/dali-demo.git] / examples / chipmunk-physics / physics-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
18 #include "physics-impl.h"
19 #include "physics-actor.h"
20
21 #include <devel-api/common/stage.h>
22 #include <iostream>
23 #include <map>
24 #include <utility>
25
26 using Dali::Actor;
27 using Dali::Layer;
28 using Dali::Stage;
29 using Dali::Vector2;
30 using Dali::Vector3;
31 using Dali::Window;
32 using namespace Dali::DevelStage;
33
34 #define GRABBABLE_MASK_BIT (1u << 31)
35 cpShapeFilter GRAB_FILTER          = {CP_NO_GROUP, GRABBABLE_MASK_BIT, GRABBABLE_MASK_BIT};
36 cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT};
37
38 Actor PhysicsImpl::Initialize(Window window)
39 {
40   mWindow = window;
41   mSpace  = cpSpaceNew();
42   cpSpaceSetIterations(mSpace, 30);
43   cpSpaceSetSleepTimeThreshold(mSpace, 0.5f);
44   cpSpaceSetGravity(mSpace, cpv(0, -200));
45
46   auto windowSize = window.GetSize();
47   CreateWorldBounds(windowSize);
48
49   // Create an actor that can handle mouse events.
50   mPhysicsRoot                                 = Layer::New();
51   mPhysicsRoot[Actor::Property::SIZE]          = Vector2(windowSize.GetWidth(), windowSize.GetHeight());
52   mPhysicsRoot[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
53   mPhysicsRoot[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::CENTER;
54
55   mFrameCallback = new FrameCallback(*this);
56   AddFrameCallback(Stage::GetCurrent(), *mFrameCallback, window.GetRootLayer());
57   Stage::GetCurrent().KeepRendering(30);
58
59   return mPhysicsRoot;
60 }
61
62 Layer PhysicsImpl::CreateDebug(Vector2 windowSize)
63 {
64   return Layer();
65 }
66
67 void PhysicsImpl::CreateWorldBounds(Window::WindowSize size)
68 {
69   // Physics origin is 0,0,0 in DALi coords.
70   // But, Y is inverted, so bottom is -ve, top is +ve.
71   // Perform this correction when applying position to actor.
72   // But, can't use actors in update, so cache transform.
73   SetTransform(Vector2(size.GetWidth(), size.GetHeight()));
74
75   int xBound = size.GetWidth() / 2;
76   int yBound = size.GetHeight() / 2;
77
78   cpBody* staticBody = cpSpaceGetStaticBody(mSpace);
79
80   if(mLeftBound)
81   {
82     cpSpaceRemoveShape(mSpace, mLeftBound);
83     cpSpaceRemoveShape(mSpace, mRightBound);
84     cpSpaceRemoveShape(mSpace, mTopBound);
85     cpSpaceRemoveShape(mSpace, mBottomBound);
86     cpShapeFree(mLeftBound);
87     cpShapeFree(mRightBound);
88     cpShapeFree(mTopBound);
89     cpShapeFree(mBottomBound);
90   }
91   mLeftBound   = AddBound(staticBody, cpv(-xBound, -yBound), cpv(-xBound, yBound));
92   mRightBound  = AddBound(staticBody, cpv(xBound, -yBound), cpv(xBound, yBound));
93   mTopBound    = AddBound(staticBody, cpv(-xBound, -yBound), cpv(xBound, -yBound));
94   mBottomBound = AddBound(staticBody, cpv(-xBound, yBound), cpv(xBound, yBound));
95 }
96
97 void PhysicsImpl::SetTransform(Vector2 worldSize)
98 {
99   mWorldOffset.x = worldSize.x * 0.5f;
100   mWorldOffset.y = worldSize.y * 0.5f;
101   // y is always inverted.
102 }
103
104 cpShape* PhysicsImpl::AddBound(cpBody* staticBody, cpVect start, cpVect end)
105 {
106   cpShape* shape = cpSpaceAddShape(mSpace, cpSegmentShapeNew(staticBody, start, end, 0.0f));
107   cpShapeSetElasticity(shape, 1.0f);
108   cpShapeSetFriction(shape, 1.0f);
109   cpShapeSetFilter(shape, NOT_GRABBABLE_FILTER);
110   return shape;
111 }
112
113 PhysicsActor& PhysicsImpl::AddBall(::Actor actor, float mass, float radius, float elasticity, float friction)
114 {
115   Dali::Mutex::ScopedLock lock(mMutex);
116   cpBody*                 body = cpSpaceAddBody(mSpace, cpBodyNew(mass, cpMomentForCircle(mass, 0.0f, radius, cpvzero)));
117   cpBodySetPosition(body, cpv(0, 0));
118   cpBodySetVelocity(body, cpv(0, 0));
119
120   cpShape* shape = cpSpaceAddShape(mSpace, cpCircleShapeNew(body, radius, cpvzero));
121   cpShapeSetElasticity(shape, elasticity);
122   cpShapeSetFriction(shape, friction);
123
124   int                   id    = actor[Actor::Property::ID];
125   Dali::Property::Index index = actor.RegisterProperty("uBrightness", 0.0f);
126   mPhysicsActors.insert(std::make_pair(id, PhysicsActor{actor, body, this, index}));
127   actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::TOP_LEFT;
128   actor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
129   mPhysicsRoot.Add(actor);
130   return mPhysicsActors.at(id);
131 }
132
133 PhysicsActor& PhysicsImpl::AddBrick(Dali::Actor actor, float mass, float elasticity, float friction, Vector3 size)
134 {
135   Dali::Mutex::ScopedLock lock(mMutex);
136   cpBody*                 body = cpSpaceAddBody(mSpace, cpBodyNew(mass, cpMomentForBox(mass, size.width, size.height)));
137   cpBodySetPosition(body, cpv(0, 0));
138   cpBodySetVelocity(body, cpv(0, 0));
139
140   cpShape* shape = cpSpaceAddShape(mSpace, cpBoxShapeNew(body, size.width, size.height, 0.0f));
141   cpShapeSetFriction(shape, friction);
142   cpShapeSetElasticity(shape, elasticity);
143
144   int                   id    = actor[Actor::Property::ID];
145   Dali::Property::Index index = actor.RegisterProperty("uBrightness", 0.0f);
146   mPhysicsActors.insert(std::make_pair(id, PhysicsActor{actor, body, this, index}));
147   actor[Actor::Property::PARENT_ORIGIN] = Dali::ParentOrigin::TOP_LEFT;
148   actor[Actor::Property::ANCHOR_POINT]  = Dali::AnchorPoint::CENTER;
149   mPhysicsRoot.Add(actor);
150   return mPhysicsActors.at(id);
151 }
152
153 cpBody* PhysicsImpl::AddMouseBody()
154 {
155   Dali::Mutex::ScopedLock lock(mMutex);
156   auto                    kinematicBody = cpBodyNewKinematic(); // Mouse actor is a kinematic body that is not integrated
157   return kinematicBody;
158 }
159
160 PhysicsActor* PhysicsImpl::GetPhysicsActor(cpBody* body)
161 {
162   return reinterpret_cast<PhysicsActor*>(cpBodyGetUserData(body));
163 }
164
165 void PhysicsImpl::HighlightBody(cpBody* body, bool highlight)
166 {
167   auto physicsActor = GetPhysicsActor(body);
168   if(physicsActor)
169   {
170     Actor actor = mPhysicsRoot.FindChildById(physicsActor->GetId());
171     if(actor)
172     {
173       actor[physicsActor->GetBrightnessIndex()] = highlight ? 1.0f : 0.0f;
174     }
175   }
176 }
177
178 // Convert from root actor local space to physics space
179 Vector3 PhysicsImpl::TranslateToPhysicsSpace(Vector3 vector)
180 {
181   // root actor origin is top left, DALi Y is inverted.
182   // Physics origin is center. Y: 0->1 => 0.5=>-0.5
183   return Vector3(vector.x - mWorldOffset.x, mWorldOffset.y - vector.y, vector.z);
184 }
185
186 // Convert from physics space to root actor local space
187 Vector3 PhysicsImpl::TranslateFromPhysicsSpace(Vector3 vector)
188 {
189   return Vector3(vector.x + mWorldOffset.x, mWorldOffset.y - vector.y, vector.z);
190 }
191
192 // Convert a vector from dali space to physics space
193 Vector3 PhysicsImpl::ConvertVectorToPhysicsSpace(Vector3 vector)
194 {
195   // root actor origin is top left, DALi Y is inverted.
196   // @todo Add space config scale.
197   return Vector3(vector.x, -vector.y, vector.z);
198 }
199
200 // Convert a vector physics space to root actor local space
201 Vector3 PhysicsImpl::ConvertVectorFromPhysicsSpace(Vector3 vector)
202 {
203   return Vector3(vector.x, -vector.y, vector.z);
204 }
205
206 void PhysicsImpl::Integrate(float timestep)
207 {
208   if(mPhysicsIntegrateState)
209   {
210     cpSpaceStep(mSpace, timestep);
211   }
212   //  if(mDynamicsWorld->getDebugDrawer() && mPhysicsDebugState)
213   //  {
214   //    mDynamicsWorld->debugDrawWorld();
215   //  }
216 }
217
218 cpBody* PhysicsImpl::HitTest(Vector2 screenCoords, Vector3 origin, Vector3 direction, Vector3& localPivot, float& distanceFromCamera)
219 {
220   Vector3          spacePosition = TranslateToPhysicsSpace(Vector3{screenCoords});
221   cpVect           mousePosition = cpv(spacePosition.x, spacePosition.y);
222   cpFloat          radius        = 5.0f;
223   cpPointQueryInfo info          = {0};
224   cpShape*         shape         = cpSpacePointQueryNearest(mSpace, mousePosition, radius, GRAB_FILTER, &info);
225
226   cpBody* body{nullptr};
227
228   if(shape && cpBodyGetMass(cpShapeGetBody(shape)) < INFINITY)
229   {
230     // Use the closest point on the surface if the click is outside the shape.
231     cpVect nearest = (info.distance > 0.0f ? info.point : mousePosition);
232     body           = cpShapeGetBody(shape);
233     cpVect local   = cpBodyWorldToLocal(body, nearest);
234     localPivot.x   = local.x;
235     localPivot.y   = local.y;
236     localPivot.z   = 0.0;
237   }
238   return body;
239 }
240
241 cpConstraint* PhysicsImpl::AddPivotJoint(cpBody* body1, cpBody* body2, Vector3 localPivot)
242 {
243   cpVect        pivot{localPivot.x, localPivot.y};
244   cpConstraint* joint = cpPivotJointNew2(body2, body1, cpvzero, pivot);
245   cpConstraintSetMaxForce(joint, 50000.0f); // Magic numbers for mouse feedback.
246   cpConstraintSetErrorBias(joint, cpfpow(1.0f - 0.15f, 60.0f));
247   cpConstraint* constraint = cpSpaceAddConstraint(mSpace, joint);
248   return constraint; // Constraint & joint are the same...
249 }
250
251 void PhysicsImpl::MoveMouseBody(cpBody* mouseBody, Vector3 position)
252 {
253   cpVect cpPosition = cpv(position.x, position.y);
254   cpVect newPoint   = cpvlerp(cpBodyGetPosition(mouseBody), cpPosition, 0.25f);
255   cpBodySetVelocity(mouseBody, cpvmult(cpvsub(newPoint, cpBodyGetPosition(mouseBody)), 60.0f));
256   // Normally, kinematic body's position would be calculated by engine.
257   // For mouse, though, we want to set it.
258   cpBodySetPosition(mouseBody, newPoint);
259 }
260
261 void PhysicsImpl::MoveConstraint(cpConstraint* constraint, Vector3 newPosition)
262 {
263 }
264
265 void PhysicsImpl::ReleaseConstraint(cpConstraint* constraint)
266 {
267   cpSpaceRemoveConstraint(mSpace, constraint);
268   cpConstraintFree(constraint);
269 }
270
271 int PhysicsImpl::ActivateBody(cpBody* body)
272 {
273   int oldState = cpBodyIsSleeping(body);
274   cpBodyActivate(body);
275
276   return oldState;
277 }
278
279 void PhysicsImpl::RestoreBodyState(cpBody* body, int oldState)
280 {
281   if(oldState)
282   {
283     cpBodyActivate(body);
284   }
285   else
286   {
287     cpBodySleep(body);
288   }
289 }