| 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 |  | 
|---|
| 31 | namespace love | 
|---|
| 32 | { | 
|---|
| 33 | namespace physics | 
|---|
| 34 | { | 
|---|
| 35 | namespace box2d | 
|---|
| 36 | { | 
|---|
| 37 |  | 
|---|
| 38 | love::Type Fixture::type( "Fixture", &Object::type); | 
|---|
| 39 |  | 
|---|
| 40 | Fixture::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 |  | 
|---|
| 55 | Fixture::~Fixture() | 
|---|
| 56 | { | 
|---|
| 57 | if (!udata) | 
|---|
| 58 | return; | 
|---|
| 59 |  | 
|---|
| 60 | if (udata->ref) | 
|---|
| 61 | delete udata->ref; | 
|---|
| 62 |  | 
|---|
| 63 | delete udata; | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | void 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 |  | 
|---|
| 92 | Shape::Type Fixture::getType() | 
|---|
| 93 | { | 
|---|
| 94 | checkCreateShape(); | 
|---|
| 95 | if (shape.get() == nullptr) | 
|---|
| 96 | return Shape::SHAPE_INVALID; | 
|---|
| 97 | else | 
|---|
| 98 | return shape->getType(); | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | void Fixture::setFriction(float friction) | 
|---|
| 102 | { | 
|---|
| 103 | fixture->SetFriction(friction); | 
|---|
| 104 | } | 
|---|
| 105 |  | 
|---|
| 106 | void Fixture::setRestitution(float restitution) | 
|---|
| 107 | { | 
|---|
| 108 | fixture->SetRestitution(restitution); | 
|---|
| 109 | } | 
|---|
| 110 |  | 
|---|
| 111 | void Fixture::setDensity(float density) | 
|---|
| 112 | { | 
|---|
| 113 | fixture->SetDensity(density); | 
|---|
| 114 | } | 
|---|
| 115 |  | 
|---|
| 116 | void Fixture::setSensor(bool sensor) | 
|---|
| 117 | { | 
|---|
| 118 | fixture->SetSensor(sensor); | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | float Fixture::getFriction() const | 
|---|
| 122 | { | 
|---|
| 123 | return fixture->GetFriction(); | 
|---|
| 124 | } | 
|---|
| 125 |  | 
|---|
| 126 | float Fixture::getRestitution() const | 
|---|
| 127 | { | 
|---|
| 128 | return fixture->GetRestitution(); | 
|---|
| 129 | } | 
|---|
| 130 |  | 
|---|
| 131 | float Fixture::getDensity() const | 
|---|
| 132 | { | 
|---|
| 133 | return fixture->GetDensity(); | 
|---|
| 134 | } | 
|---|
| 135 |  | 
|---|
| 136 | bool Fixture::isSensor() const | 
|---|
| 137 | { | 
|---|
| 138 | return fixture->IsSensor(); | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 | Body *Fixture::getBody() const | 
|---|
| 142 | { | 
|---|
| 143 | return body; | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | Shape *Fixture::getShape() | 
|---|
| 147 | { | 
|---|
| 148 | checkCreateShape(); | 
|---|
| 149 | return shape; | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | bool Fixture::isValid() const | 
|---|
| 153 | { | 
|---|
| 154 | return fixture != nullptr; | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | void 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 |  | 
|---|
| 166 | void 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 |  | 
|---|
| 174 | int 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 |  | 
|---|
| 182 | int 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 |  | 
|---|
| 190 | void Fixture::setGroupIndex(int index) | 
|---|
| 191 | { | 
|---|
| 192 | b2Filter f = fixture->GetFilterData(); | 
|---|
| 193 | f.groupIndex = (uint16)index; | 
|---|
| 194 | fixture->SetFilterData(f); | 
|---|
| 195 | } | 
|---|
| 196 |  | 
|---|
| 197 | int Fixture::getGroupIndex() const | 
|---|
| 198 | { | 
|---|
| 199 | b2Filter f = fixture->GetFilterData(); | 
|---|
| 200 | return f.groupIndex; | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | int Fixture::getCategory(lua_State *L) | 
|---|
| 204 | { | 
|---|
| 205 | return pushBits(L, fixture->GetFilterData().categoryBits); | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | int Fixture::getMask(lua_State *L) | 
|---|
| 209 | { | 
|---|
| 210 | return pushBits(L, ~(fixture->GetFilterData().maskBits)); | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | uint16 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 |  | 
|---|
| 244 | int 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 |  | 
|---|
| 258 | int 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 |  | 
|---|
| 276 | int 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 |  | 
|---|
| 286 | bool Fixture::testPoint(float x, float y) const | 
|---|
| 287 | { | 
|---|
| 288 | return fixture->TestPoint(Physics::scaleDown(b2Vec2(x, y))); | 
|---|
| 289 | } | 
|---|
| 290 |  | 
|---|
| 291 | int 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 |  | 
|---|
| 312 | int 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 |  | 
|---|
| 325 | int 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 |  | 
|---|
| 337 | void 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 |  | 
|---|