1 /**
2 * Copyright (c) 2006-2019 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21 #include "Body.h"
22
23 #include "common/math.h"
24
25 #include "Shape.h"
26 #include "Fixture.h"
27 #include "World.h"
28 #include "Physics.h"
29
30 // Needed for luax_pushjoint.
31 #include "wrap_Joint.h"
32
33 namespace love
34 {
35 namespace physics
36 {
37 namespace box2d
38 {
39
Body(World * world,b2Vec2 p,Body::Type type)40 Body::Body(World *world, b2Vec2 p, Body::Type type)
41 : world(world)
42 , udata(nullptr)
43 {
44 udata = new bodyudata();
45 udata->ref = nullptr;
46 b2BodyDef def;
47 def.position = Physics::scaleDown(p);
48 def.userData = (void *) udata;
49 body = world->world->CreateBody(&def);
50 // Box2D body holds a reference to the love Body.
51 this->retain();
52 this->setType(type);
53 world->registerObject(body, this);
54 }
55
~Body()56 Body::~Body()
57 {
58 if (!udata)
59 return;
60
61 if (udata->ref)
62 delete udata->ref;
63
64 delete udata;
65 }
66
getX()67 float Body::getX()
68 {
69 return Physics::scaleUp(body->GetPosition().x);
70 }
71
getY()72 float Body::getY()
73 {
74 return Physics::scaleUp(body->GetPosition().y);
75 }
76
getPosition(float & x_o,float & y_o)77 void Body::getPosition(float &x_o, float &y_o)
78 {
79 b2Vec2 v = Physics::scaleUp(body->GetPosition());
80 x_o = v.x;
81 y_o = v.y;
82 }
83
getLinearVelocity(float & x_o,float & y_o)84 void Body::getLinearVelocity(float &x_o, float &y_o)
85 {
86 b2Vec2 v = Physics::scaleUp(body->GetLinearVelocity());
87 x_o = v.x;
88 y_o = v.y;
89 }
90
getAngle()91 float Body::getAngle()
92 {
93 return body->GetAngle();
94 }
95
getWorldCenter(float & x_o,float & y_o)96 void Body::getWorldCenter(float &x_o, float &y_o)
97 {
98 b2Vec2 v = Physics::scaleUp(body->GetWorldCenter());
99 x_o = v.x;
100 y_o = v.y;
101 }
102
getLocalCenter(float & x_o,float & y_o)103 void Body::getLocalCenter(float &x_o, float &y_o)
104 {
105 b2Vec2 v = Physics::scaleUp(body->GetLocalCenter());
106 x_o = v.x;
107 y_o = v.y;
108 }
109
getAngularVelocity() const110 float Body::getAngularVelocity() const
111 {
112 return body->GetAngularVelocity();
113 }
114
getMass() const115 float Body::getMass() const
116 {
117 return body->GetMass();
118 }
119
getInertia() const120 float Body::getInertia() const
121 {
122 return Physics::scaleUp(Physics::scaleUp(body->GetInertia()));
123 }
124
getMassData(lua_State * L)125 int Body::getMassData(lua_State *L)
126 {
127 b2MassData data;
128 body->GetMassData(&data);
129 b2Vec2 center = Physics::scaleUp(data.center);
130 lua_pushnumber(L, center.x);
131 lua_pushnumber(L, center.y);
132 lua_pushnumber(L, data.mass);
133 lua_pushnumber(L, Physics::scaleUp(Physics::scaleUp(data.I)));
134 return 4;
135 }
136
getAngularDamping() const137 float Body::getAngularDamping() const
138 {
139 return body->GetAngularDamping();
140 }
141
getLinearDamping() const142 float Body::getLinearDamping() const
143 {
144 return body->GetLinearDamping();
145 }
146
getGravityScale() const147 float Body::getGravityScale() const
148 {
149 return body->GetGravityScale();
150 }
151
getType() const152 Body::Type Body::getType() const
153 {
154 switch (body->GetType())
155 {
156 case b2_staticBody:
157 return BODY_STATIC;
158 break;
159 case b2_dynamicBody:
160 return BODY_DYNAMIC;
161 break;
162 case b2_kinematicBody:
163 return BODY_KINEMATIC;
164 break;
165 default:
166 return BODY_INVALID;
167 break;
168 }
169 }
170
applyLinearImpulse(float jx,float jy,bool wake)171 void Body::applyLinearImpulse(float jx, float jy, bool wake)
172 {
173 body->ApplyLinearImpulse(Physics::scaleDown(b2Vec2(jx, jy)), body->GetWorldCenter(), wake);
174 }
175
applyLinearImpulse(float jx,float jy,float rx,float ry,bool wake)176 void Body::applyLinearImpulse(float jx, float jy, float rx, float ry, bool wake)
177 {
178 body->ApplyLinearImpulse(Physics::scaleDown(b2Vec2(jx, jy)), Physics::scaleDown(b2Vec2(rx, ry)), wake);
179 }
180
applyAngularImpulse(float impulse,bool wake)181 void Body::applyAngularImpulse(float impulse, bool wake)
182 {
183 // Angular impulse is in kg*m^2/s, meaning it needs to be scaled twice
184 body->ApplyAngularImpulse(Physics::scaleDown(Physics::scaleDown(impulse)), wake);
185 }
186
applyTorque(float t,bool wake)187 void Body::applyTorque(float t, bool wake)
188 {
189 // Torque is in N*m, or kg*m^2/s^2, meaning it also needs to be scaled twice
190 body->ApplyTorque(Physics::scaleDown(Physics::scaleDown(t)), wake);
191 }
192
applyForce(float fx,float fy,float rx,float ry,bool wake)193 void Body::applyForce(float fx, float fy, float rx, float ry, bool wake)
194 {
195 body->ApplyForce(Physics::scaleDown(b2Vec2(fx, fy)), Physics::scaleDown(b2Vec2(rx, ry)), wake);
196 }
197
applyForce(float fx,float fy,bool wake)198 void Body::applyForce(float fx, float fy, bool wake)
199 {
200 body->ApplyForceToCenter(Physics::scaleDown(b2Vec2(fx, fy)), wake);
201 }
202
setX(float x)203 void Body::setX(float x)
204 {
205 body->SetTransform(Physics::scaleDown(b2Vec2(x, getY())), getAngle());
206 }
207
setY(float y)208 void Body::setY(float y)
209 {
210 body->SetTransform(Physics::scaleDown(b2Vec2(getX(), y)), getAngle());
211 }
212
setLinearVelocity(float x,float y)213 void Body::setLinearVelocity(float x, float y)
214 {
215 body->SetLinearVelocity(Physics::scaleDown(b2Vec2(x, y)));
216 }
217
setAngle(float d)218 void Body::setAngle(float d)
219 {
220 body->SetTransform(body->GetPosition(), d);
221 }
222
setAngularVelocity(float r)223 void Body::setAngularVelocity(float r)
224 {
225 body->SetAngularVelocity(r);
226 }
227
setPosition(float x,float y)228 void Body::setPosition(float x, float y)
229 {
230 body->SetTransform(Physics::scaleDown(b2Vec2(x, y)), body->GetAngle());
231 }
232
setAngularDamping(float d)233 void Body::setAngularDamping(float d)
234 {
235 body->SetAngularDamping(d);
236 }
237
setLinearDamping(float d)238 void Body::setLinearDamping(float d)
239 {
240 body->SetLinearDamping(d);
241 }
242
resetMassData()243 void Body::resetMassData()
244 {
245 body->ResetMassData();
246 }
247
setMassData(float x,float y,float m,float i)248 void Body::setMassData(float x, float y, float m, float i)
249 {
250 b2MassData massData;
251 massData.center = Physics::scaleDown(b2Vec2(x, y));
252 massData.mass = m;
253 massData.I = Physics::scaleDown(Physics::scaleDown(i));
254 body->SetMassData(&massData);
255 }
256
setMass(float m)257 void Body::setMass(float m)
258 {
259 b2MassData data;
260 body->GetMassData(&data);
261 data.mass = m;
262 body->SetMassData(&data);
263 }
264
setInertia(float i)265 void Body::setInertia(float i)
266 {
267 b2MassData massData;
268 massData.center = body->GetLocalCenter();
269 massData.mass = body->GetMass();
270 massData.I = Physics::scaleDown(Physics::scaleDown(i));
271 body->SetMassData(&massData);
272 }
273
setGravityScale(float scale)274 void Body::setGravityScale(float scale)
275 {
276 body->SetGravityScale(scale);
277 }
278
setType(Body::Type type)279 void Body::setType(Body::Type type)
280 {
281 switch (type)
282 {
283 case Body::BODY_STATIC:
284 body->SetType(b2_staticBody);
285 break;
286 case Body::BODY_DYNAMIC:
287 body->SetType(b2_dynamicBody);
288 break;
289 case Body::BODY_KINEMATIC:
290 body->SetType(b2_kinematicBody);
291 break;
292 default:
293 break;
294 }
295 }
296
getWorldPoint(float x,float y,float & x_o,float & y_o)297 void Body::getWorldPoint(float x, float y, float &x_o, float &y_o)
298 {
299 b2Vec2 v = Physics::scaleUp(body->GetWorldPoint(Physics::scaleDown(b2Vec2(x, y))));
300 x_o = v.x;
301 y_o = v.y;
302 }
303
getWorldVector(float x,float y,float & x_o,float & y_o)304 void Body::getWorldVector(float x, float y, float &x_o, float &y_o)
305 {
306 b2Vec2 v = Physics::scaleUp(body->GetWorldVector(Physics::scaleDown(b2Vec2(x, y))));
307 x_o = v.x;
308 y_o = v.y;
309 }
310
getWorldPoints(lua_State * L)311 int Body::getWorldPoints(lua_State *L)
312 {
313 int argc = lua_gettop(L);
314 int vcount = (int)argc/2;
315 // at least one point
316 love::luax_assert_argc(L, 2);
317
318 for (int i = 0; i<vcount; i++)
319 {
320 float x = (float)lua_tonumber(L, 1);
321 float y = (float)lua_tonumber(L, 2);
322 // Remove them, so we don't run out of stack space
323 lua_remove(L, 1);
324 lua_remove(L, 1);
325 // Time for scaling
326 b2Vec2 point = Physics::scaleUp(body->GetWorldPoint(Physics::scaleDown(b2Vec2(x, y))));
327 // And then we push the result
328 lua_pushnumber(L, point.x);
329 lua_pushnumber(L, point.y);
330 }
331
332 return argc;
333 }
334
getLocalPoint(float x,float y,float & x_o,float & y_o)335 void Body::getLocalPoint(float x, float y, float &x_o, float &y_o)
336 {
337 b2Vec2 v = Physics::scaleUp(body->GetLocalPoint(Physics::scaleDown(b2Vec2(x, y))));
338 x_o = v.x;
339 y_o = v.y;
340 }
341
getLocalVector(float x,float y,float & x_o,float & y_o)342 void Body::getLocalVector(float x, float y, float &x_o, float &y_o)
343 {
344 b2Vec2 v = Physics::scaleUp(body->GetLocalVector(Physics::scaleDown(b2Vec2(x, y))));
345 x_o = v.x;
346 y_o = v.y;
347 }
348
getLinearVelocityFromWorldPoint(float x,float y,float & x_o,float & y_o)349 void Body::getLinearVelocityFromWorldPoint(float x, float y, float &x_o, float &y_o)
350 {
351 b2Vec2 v = Physics::scaleUp(body->GetLinearVelocityFromWorldPoint(Physics::scaleDown(b2Vec2(x, y))));
352 x_o = v.x;
353 y_o = v.y;
354 }
355
getLinearVelocityFromLocalPoint(float x,float y,float & x_o,float & y_o)356 void Body::getLinearVelocityFromLocalPoint(float x, float y, float &x_o, float &y_o)
357 {
358 b2Vec2 v = Physics::scaleUp(body->GetLinearVelocityFromLocalPoint(Physics::scaleDown(b2Vec2(x, y))));
359 x_o = v.x;
360 y_o = v.y;
361 }
362
isBullet() const363 bool Body::isBullet() const
364 {
365 return body->IsBullet();
366 }
367
setBullet(bool bullet)368 void Body::setBullet(bool bullet)
369 {
370 return body->SetBullet(bullet);
371 }
372
isActive() const373 bool Body::isActive() const
374 {
375 return body->IsActive();
376 }
377
isAwake() const378 bool Body::isAwake() const
379 {
380 return body->IsAwake();
381 }
382
setSleepingAllowed(bool allow)383 void Body::setSleepingAllowed(bool allow)
384 {
385 body->SetSleepingAllowed(allow);
386 }
387
isSleepingAllowed() const388 bool Body::isSleepingAllowed() const
389 {
390 return body->IsSleepingAllowed();
391 }
392
setActive(bool active)393 void Body::setActive(bool active)
394 {
395 body->SetActive(active);
396 }
397
setAwake(bool awake)398 void Body::setAwake(bool awake)
399 {
400 body->SetAwake(awake);
401 }
402
setFixedRotation(bool fixed)403 void Body::setFixedRotation(bool fixed)
404 {
405 body->SetFixedRotation(fixed);
406 }
407
isFixedRotation() const408 bool Body::isFixedRotation() const
409 {
410 return body->IsFixedRotation();
411 }
412
isTouching(Body * other) const413 bool Body::isTouching(Body *other) const
414 {
415 const b2ContactEdge *ce = body->GetContactList();
416 b2Body *otherbody = other->body;
417
418 while (ce != nullptr)
419 {
420 if (ce->other == otherbody && ce->contact != nullptr && ce->contact->IsTouching())
421 return true;
422
423 ce = ce->next;
424 }
425
426 return false;
427 }
428
getWorld() const429 World *Body::getWorld() const
430 {
431 return world;
432 }
433
getFixtures(lua_State * L) const434 int Body::getFixtures(lua_State *L) const
435 {
436 lua_newtable(L);
437 b2Fixture *f = body->GetFixtureList();
438 int i = 1;
439 do
440 {
441 if (!f)
442 break;
443 Fixture *fixture = (Fixture *)world->findObject(f);
444 if (!fixture)
445 throw love::Exception("A fixture has escaped Memoizer!");
446 luax_pushtype(L, fixture);
447 lua_rawseti(L, -2, i);
448 i++;
449 }
450 while ((f = f->GetNext()));
451 return 1;
452 }
453
getJoints(lua_State * L) const454 int Body::getJoints(lua_State *L) const
455 {
456 lua_newtable(L);
457 const b2JointEdge *je = body->GetJointList();
458 int i = 1;
459
460 do
461 {
462 if (!je)
463 break;
464
465 Joint *joint = (Joint *) world->findObject(je->joint);
466 if (!joint)
467 throw love::Exception("A joint has escaped Memoizer!");
468
469 luax_pushjoint(L, joint);
470 lua_rawseti(L, -2, i);
471 i++;
472 }
473 while ((je = je->next));
474
475 return 1;
476 }
477
getContacts(lua_State * L) const478 int Body::getContacts(lua_State *L) const
479 {
480 lua_newtable(L);
481 const b2ContactEdge *ce = body->GetContactList();
482 int i = 1;
483 do
484 {
485 if (!ce)
486 break;
487
488 Contact *contact = (Contact *) world->findObject(ce->contact);
489 if (!contact)
490 contact = new Contact(world, ce->contact);
491 else
492 contact->retain();
493
494 luax_pushtype(L, contact);
495 contact->release();
496 lua_rawseti(L, -2, i);
497 i++;
498 }
499 while ((ce = ce->next));
500 return 1;
501 }
502
destroy()503 void Body::destroy()
504 {
505 if (world->world->IsLocked())
506 {
507 // Called during time step. Save reference for destruction afterwards.
508 this->retain();
509 world->destructBodies.push_back(this);
510 return;
511 }
512
513 world->world->DestroyBody(body);
514 world->unregisterObject(body);
515 body = NULL;
516
517 // Remove userdata reference to avoid it sticking around after GC
518 if (udata && udata->ref)
519 udata->ref->unref();
520
521 // Box2D body destroyed. Release its reference to the love Body.
522 this->release();
523 }
524
setUserData(lua_State * L)525 int Body::setUserData(lua_State *L)
526 {
527 love::luax_assert_argc(L, 1, 1);
528
529 if (udata == nullptr)
530 {
531 udata = new bodyudata();
532 body->SetUserData((void *) udata);
533 }
534
535 if(!udata->ref)
536 udata->ref = new Reference();
537
538 udata->ref->ref(L);
539
540 return 0;
541 }
542
getUserData(lua_State * L)543 int Body::getUserData(lua_State *L)
544 {
545 if (udata != nullptr && udata->ref != nullptr)
546 udata->ref->push(L);
547 else
548 lua_pushnil(L);
549
550 return 1;
551 }
552
553 } // box2d
554 } // physics
555 } // love
556