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 "Fixture.h"
22
23// Module
24#include "Body.h"
25#include "World.h"
26#include "Physics.h"
27
28// STD
29#include <bitset>
30
31namespace love
32{
33namespace physics
34{
35namespace box2d
36{
37
38love::Type Fixture::type("Fixture", &Object::type);
39
40Fixture::Fixture(Body *body, Shape *shape, float density)
41 : body(body)
42 , fixture(nullptr)
43{
44 udata = new fixtureudata();
45 udata->ref = nullptr;
46 b2FixtureDef def;
47 def.shape = shape->shape;
48 def.userData = (void *)udata;
49 def.density = density;
50 fixture = body->body->CreateFixture(&def);
51 this->retain();
52 body->world->registerObject(fixture, this);
53}
54
55Fixture::~Fixture()
56{
57 if (!udata)
58 return;
59
60 if (udata->ref)
61 delete udata->ref;
62
63 delete udata;
64}
65
66void Fixture::checkCreateShape()
67{
68 if (shape.get() != nullptr || fixture == nullptr || fixture->GetShape() == nullptr)
69 return;
70
71 b2Shape *bshape = fixture->GetShape();
72
73 switch (bshape->GetType())
74 {
75 case b2Shape::e_circle:
76 shape.set(new CircleShape((b2CircleShape *) bshape, false), Acquire::NORETAIN);
77 break;
78 case b2Shape::e_edge:
79 shape.set(new EdgeShape((b2EdgeShape *) bshape, false), Acquire::NORETAIN);
80 break;
81 case b2Shape::e_polygon:
82 shape.set(new PolygonShape((b2PolygonShape *) bshape, false), Acquire::NORETAIN);
83 break;
84 case b2Shape::e_chain:
85 shape.set(new ChainShape((b2ChainShape *) bshape, false), Acquire::NORETAIN);
86 break;
87 default:
88 break;
89 }
90}
91
92Shape::Type Fixture::getType()
93{
94 checkCreateShape();
95 if (shape.get() == nullptr)
96 return Shape::SHAPE_INVALID;
97 else
98 return shape->getType();
99}
100
101void Fixture::setFriction(float friction)
102{
103 fixture->SetFriction(friction);
104}
105
106void Fixture::setRestitution(float restitution)
107{
108 fixture->SetRestitution(restitution);
109}
110
111void Fixture::setDensity(float density)
112{
113 fixture->SetDensity(density);
114}
115
116void Fixture::setSensor(bool sensor)
117{
118 fixture->SetSensor(sensor);
119}
120
121float Fixture::getFriction() const
122{
123 return fixture->GetFriction();
124}
125
126float Fixture::getRestitution() const
127{
128 return fixture->GetRestitution();
129}
130
131float Fixture::getDensity() const
132{
133 return fixture->GetDensity();
134}
135
136bool Fixture::isSensor() const
137{
138 return fixture->IsSensor();
139}
140
141Body *Fixture::getBody() const
142{
143 return body;
144}
145
146Shape *Fixture::getShape()
147{
148 checkCreateShape();
149 return shape;
150}
151
152bool Fixture::isValid() const
153{
154 return fixture != nullptr;
155}
156
157void Fixture::setFilterData(int *v)
158{
159 b2Filter f;
160 f.categoryBits = (uint16) v[0];
161 f.maskBits = (uint16) v[1];
162 f.groupIndex = (int16) v[2];
163 fixture->SetFilterData(f);
164}
165
166void Fixture::getFilterData(int *v)
167{
168 b2Filter f = fixture->GetFilterData();
169 v[0] = (int) f.categoryBits;
170 v[1] = (int) f.maskBits;
171 v[2] = (int) f.groupIndex;
172}
173
174int Fixture::setCategory(lua_State *L)
175{
176 b2Filter f = fixture->GetFilterData();
177 f.categoryBits = (uint16)getBits(L);
178 fixture->SetFilterData(f);
179 return 0;
180}
181
182int Fixture::setMask(lua_State *L)
183{
184 b2Filter f = fixture->GetFilterData();
185 f.maskBits = ~(uint16)getBits(L);
186 fixture->SetFilterData(f);
187 return 0;
188}
189
190void Fixture::setGroupIndex(int index)
191{
192 b2Filter f = fixture->GetFilterData();
193 f.groupIndex = (uint16)index;
194 fixture->SetFilterData(f);
195}
196
197int Fixture::getGroupIndex() const
198{
199 b2Filter f = fixture->GetFilterData();
200 return f.groupIndex;
201}
202
203int Fixture::getCategory(lua_State *L)
204{
205 return pushBits(L, fixture->GetFilterData().categoryBits);
206}
207
208int Fixture::getMask(lua_State *L)
209{
210 return pushBits(L, ~(fixture->GetFilterData().maskBits));
211}
212
213uint16 Fixture::getBits(lua_State *L)
214{
215 // Get number of args.
216 bool istable = lua_istable(L, 1);
217 int argc = istable ? (int) luax_objlen(L, 1) : lua_gettop(L);
218
219 // The new bitset.
220 std::bitset<16> b;
221
222 for (int i = 1; i <= argc; i++)
223 {
224 size_t bpos = 0;
225
226 if (istable)
227 {
228 lua_rawgeti(L, 1, i);
229 bpos = (size_t) (lua_tointeger(L, -1) - 1);
230 lua_pop(L, 1);
231 }
232 else
233 bpos = (size_t) (lua_tointeger(L, i) - 1);
234
235 if (bpos >= 16)
236 luaL_error(L, "Values must be in range 1-16.");
237
238 b.set(bpos, true);
239 }
240
241 return (uint16)b.to_ulong();
242}
243
244int Fixture::pushBits(lua_State *L, uint16 bits)
245{
246 // Create a bitset.
247 std::bitset<16> b((int)bits);
248
249 // Push all set bits.
250 for (int i = 0; i<16; i++)
251 if (b.test(i))
252 lua_pushinteger(L, i+1);
253
254 // Count number of set bits.
255 return (int)b.count();
256}
257
258int Fixture::setUserData(lua_State *L)
259{
260 love::luax_assert_argc(L, 1, 1);
261
262 if (udata == nullptr)
263 {
264 udata = new fixtureudata();
265 fixture->SetUserData((void *) udata);
266 }
267
268 if(!udata->ref)
269 udata->ref = new Reference();
270
271 udata->ref->ref(L);
272
273 return 0;
274}
275
276int Fixture::getUserData(lua_State *L)
277{
278 if (udata->ref != nullptr)
279 udata->ref->push(L);
280 else
281 lua_pushnil(L);
282
283 return 1;
284}
285
286bool Fixture::testPoint(float x, float y) const
287{
288 return fixture->TestPoint(Physics::scaleDown(b2Vec2(x, y)));
289}
290
291int Fixture::rayCast(lua_State *L) const
292{
293 float p1x = Physics::scaleDown((float)luaL_checknumber(L, 1));
294 float p1y = Physics::scaleDown((float)luaL_checknumber(L, 2));
295 float p2x = Physics::scaleDown((float)luaL_checknumber(L, 3));
296 float p2y = Physics::scaleDown((float)luaL_checknumber(L, 4));
297 float maxFraction = (float)luaL_checknumber(L, 5);
298 int childIndex = (int) luaL_optinteger(L, 6, 1) - 1; // Convert from 1-based index
299 b2RayCastInput input;
300 input.p1.Set(p1x, p1y);
301 input.p2.Set(p2x, p2y);
302 input.maxFraction = maxFraction;
303 b2RayCastOutput output;
304 if (!fixture->RayCast(&output, input, childIndex))
305 return 0; // Nothing hit.
306 lua_pushnumber(L, output.normal.x);
307 lua_pushnumber(L, output.normal.y);
308 lua_pushnumber(L, output.fraction);
309 return 3;
310}
311
312int Fixture::getBoundingBox(lua_State *L) const
313{
314 int childIndex = (int) luaL_optinteger(L, 1, 1) - 1; // Convert from 1-based index
315 b2AABB box;
316 luax_catchexcept(L, [&]() { box = fixture->GetAABB(childIndex); });
317 box = Physics::scaleUp(box);
318 lua_pushnumber(L, box.lowerBound.x);
319 lua_pushnumber(L, box.lowerBound.y);
320 lua_pushnumber(L, box.upperBound.x);
321 lua_pushnumber(L, box.upperBound.y);
322 return 4;
323}
324
325int Fixture::getMassData(lua_State *L) const
326{
327 b2MassData data;
328 fixture->GetMassData(&data);
329 b2Vec2 center = Physics::scaleUp(data.center);
330 lua_pushnumber(L, center.x);
331 lua_pushnumber(L, center.y);
332 lua_pushnumber(L, data.mass);
333 lua_pushnumber(L, data.I);
334 return 4;
335}
336
337void Fixture::destroy(bool implicit)
338{
339 if (body->world->world->IsLocked())
340 {
341 // Called during time step. Save reference for destruction afterwards.
342 this->retain();
343 body->world->destructFixtures.push_back(this);
344 return;
345 }
346
347 shape.set(nullptr);
348
349 if (!implicit && fixture != nullptr)
350 body->body->DestroyFixture(fixture);
351 body->world->unregisterObject(fixture);
352 fixture = nullptr;
353
354 // Remove userdata reference to avoid it sticking around after GC
355 if (udata && udata->ref)
356 udata->ref->unref();
357
358 // Box2D fixture destroyed. Release its reference to the love Fixture.
359 this->release();
360}
361
362} // box2d
363} // physics
364} // love
365