1/**
2 * Copyright (c) 2006-2023 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
33namespace love
34{
35namespace physics
36{
37namespace box2d
38{
39
40Body::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
56Body::~Body()
57{
58 if (!udata)
59 return;
60
61 if (udata->ref)
62 delete udata->ref;
63
64 delete udata;
65}
66
67float Body::getX()
68{
69 return Physics::scaleUp(body->GetPosition().x);
70}
71
72float Body::getY()
73{
74 return Physics::scaleUp(body->GetPosition().y);
75}
76
77void 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
84void 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
91float Body::getAngle()
92{
93 return body->GetAngle();
94}
95
96void 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
103void 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
110float Body::getAngularVelocity() const
111{
112 return body->GetAngularVelocity();
113}
114
115float Body::getMass() const
116{
117 return body->GetMass();
118}
119
120float Body::getInertia() const
121{
122 return Physics::scaleUp(Physics::scaleUp(body->GetInertia()));
123}
124
125int 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
137float Body::getAngularDamping() const
138{
139 return body->GetAngularDamping();
140}
141
142float Body::getLinearDamping() const
143{
144 return body->GetLinearDamping();
145}
146
147float Body::getGravityScale() const
148{
149 return body->GetGravityScale();
150}
151
152Body::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
171void Body::applyLinearImpulse(float jx, float jy, bool wake)
172{
173 body->ApplyLinearImpulse(Physics::scaleDown(b2Vec2(jx, jy)), body->GetWorldCenter(), wake);
174}
175
176void 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
181void 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
187void 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
193void 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
198void Body::applyForce(float fx, float fy, bool wake)
199{
200 body->ApplyForceToCenter(Physics::scaleDown(b2Vec2(fx, fy)), wake);
201}
202
203void Body::setX(float x)
204{
205 body->SetTransform(Physics::scaleDown(b2Vec2(x, getY())), getAngle());
206}
207
208void Body::setY(float y)
209{
210 body->SetTransform(Physics::scaleDown(b2Vec2(getX(), y)), getAngle());
211}
212
213void Body::setLinearVelocity(float x, float y)
214{
215 body->SetLinearVelocity(Physics::scaleDown(b2Vec2(x, y)));
216}
217
218void Body::setAngle(float d)
219{
220 body->SetTransform(body->GetPosition(), d);
221}
222
223void Body::setAngularVelocity(float r)
224{
225 body->SetAngularVelocity(r);
226}
227
228void Body::setPosition(float x, float y)
229{
230 body->SetTransform(Physics::scaleDown(b2Vec2(x, y)), body->GetAngle());
231}
232
233void Body::setAngularDamping(float d)
234{
235 body->SetAngularDamping(d);
236}
237
238void Body::setLinearDamping(float d)
239{
240 body->SetLinearDamping(d);
241}
242
243void Body::resetMassData()
244{
245 body->ResetMassData();
246}
247
248void 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
257void Body::setMass(float m)
258{
259 b2MassData data;
260 body->GetMassData(&data);
261 data.mass = m;
262 body->SetMassData(&data);
263}
264
265void 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
274void Body::setGravityScale(float scale)
275{
276 body->SetGravityScale(scale);
277}
278
279void 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
297void 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
304void 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
311int 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
335void 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
342void 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
349int Body::getLocalPoints(lua_State *L)
350{
351 int argc = lua_gettop(L);
352 int vcount = (int)argc/2;
353 // at least one point
354 love::luax_assert_argc(L, 2);
355
356 for (int i = 0; i<vcount; i++)
357 {
358 float x = (float)lua_tonumber(L, 1);
359 float y = (float)lua_tonumber(L, 2);
360 // Remove them, so we don't run out of stack space
361 lua_remove(L, 1);
362 lua_remove(L, 1);
363 // Time for scaling
364 b2Vec2 point = Physics::scaleUp(body->GetLocalPoint(Physics::scaleDown(b2Vec2(x, y))));
365 // And then we push the result
366 lua_pushnumber(L, point.x);
367 lua_pushnumber(L, point.y);
368 }
369
370 return argc;
371}
372
373void Body::getLinearVelocityFromWorldPoint(float x, float y, float &x_o, float &y_o)
374{
375 b2Vec2 v = Physics::scaleUp(body->GetLinearVelocityFromWorldPoint(Physics::scaleDown(b2Vec2(x, y))));
376 x_o = v.x;
377 y_o = v.y;
378}
379
380void Body::getLinearVelocityFromLocalPoint(float x, float y, float &x_o, float &y_o)
381{
382 b2Vec2 v = Physics::scaleUp(body->GetLinearVelocityFromLocalPoint(Physics::scaleDown(b2Vec2(x, y))));
383 x_o = v.x;
384 y_o = v.y;
385}
386
387bool Body::isBullet() const
388{
389 return body->IsBullet();
390}
391
392void Body::setBullet(bool bullet)
393{
394 return body->SetBullet(bullet);
395}
396
397bool Body::isActive() const
398{
399 return body->IsActive();
400}
401
402bool Body::isAwake() const
403{
404 return body->IsAwake();
405}
406
407void Body::setSleepingAllowed(bool allow)
408{
409 body->SetSleepingAllowed(allow);
410}
411
412bool Body::isSleepingAllowed() const
413{
414 return body->IsSleepingAllowed();
415}
416
417void Body::setActive(bool active)
418{
419 body->SetActive(active);
420}
421
422void Body::setAwake(bool awake)
423{
424 body->SetAwake(awake);
425}
426
427void Body::setFixedRotation(bool fixed)
428{
429 body->SetFixedRotation(fixed);
430}
431
432bool Body::isFixedRotation() const
433{
434 return body->IsFixedRotation();
435}
436
437bool Body::isTouching(Body *other) const
438{
439 const b2ContactEdge *ce = body->GetContactList();
440 b2Body *otherbody = other->body;
441
442 while (ce != nullptr)
443 {
444 if (ce->other == otherbody && ce->contact != nullptr && ce->contact->IsTouching())
445 return true;
446
447 ce = ce->next;
448 }
449
450 return false;
451}
452
453World *Body::getWorld() const
454{
455 return world;
456}
457
458int Body::getFixtures(lua_State *L) const
459{
460 lua_newtable(L);
461 b2Fixture *f = body->GetFixtureList();
462 int i = 1;
463 do
464 {
465 if (!f)
466 break;
467 Fixture *fixture = (Fixture *)world->findObject(f);
468 if (!fixture)
469 throw love::Exception("A fixture has escaped Memoizer!");
470 luax_pushtype(L, fixture);
471 lua_rawseti(L, -2, i);
472 i++;
473 }
474 while ((f = f->GetNext()));
475 return 1;
476}
477
478int Body::getJoints(lua_State *L) const
479{
480 lua_newtable(L);
481 const b2JointEdge *je = body->GetJointList();
482 int i = 1;
483
484 do
485 {
486 if (!je)
487 break;
488
489 Joint *joint = (Joint *) world->findObject(je->joint);
490 if (!joint)
491 throw love::Exception("A joint has escaped Memoizer!");
492
493 luax_pushjoint(L, joint);
494 lua_rawseti(L, -2, i);
495 i++;
496 }
497 while ((je = je->next));
498
499 return 1;
500}
501
502int Body::getContacts(lua_State *L) const
503{
504 lua_newtable(L);
505 const b2ContactEdge *ce = body->GetContactList();
506 int i = 1;
507 do
508 {
509 if (!ce)
510 break;
511
512 Contact *contact = (Contact *) world->findObject(ce->contact);
513 if (!contact)
514 contact = new Contact(world, ce->contact);
515 else
516 contact->retain();
517
518 luax_pushtype(L, contact);
519 contact->release();
520 lua_rawseti(L, -2, i);
521 i++;
522 }
523 while ((ce = ce->next));
524 return 1;
525}
526
527void Body::destroy()
528{
529 if (world->world->IsLocked())
530 {
531 // Called during time step. Save reference for destruction afterwards.
532 this->retain();
533 world->destructBodies.push_back(this);
534 return;
535 }
536
537 world->world->DestroyBody(body);
538 world->unregisterObject(body);
539 body = NULL;
540
541 // Remove userdata reference to avoid it sticking around after GC
542 if (udata && udata->ref)
543 udata->ref->unref();
544
545 // Box2D body destroyed. Release its reference to the love Body.
546 this->release();
547}
548
549int Body::setUserData(lua_State *L)
550{
551 love::luax_assert_argc(L, 1, 1);
552
553 if (udata == nullptr)
554 {
555 udata = new bodyudata();
556 body->SetUserData((void *) udata);
557 }
558
559 if(!udata->ref)
560 udata->ref = new Reference();
561
562 udata->ref->ref(L);
563
564 return 0;
565}
566
567int Body::getUserData(lua_State *L)
568{
569 if (udata != nullptr && udata->ref != nullptr)
570 udata->ref->push(L);
571 else
572 lua_pushnil(L);
573
574 return 1;
575}
576
577} // box2d
578} // physics
579} // love
580