diff --git a/deps/jolt-physics-sharp b/deps/jolt-physics-sharp index 29fe07e8..2cda386d 160000 --- a/deps/jolt-physics-sharp +++ b/deps/jolt-physics-sharp @@ -1 +1 @@ -Subproject commit 29fe07e8088279c47d7108107856ec3c826d1817 +Subproject commit 2cda386d71174bec355888539679f7a4f7be1c2a diff --git a/src/api/api.h b/src/api/api.h index 527f4a6d..5593edf2 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -203,4 +203,5 @@ struct Shape* luax_newcapsuleshape(lua_State* L, int index); struct Shape* luax_newcylindershape(lua_State* L, int index); struct Shape* luax_newmeshshape(lua_State* L, int index); struct Shape* luax_newterrainshape(lua_State* L, int index); +struct Shape* luax_newcompoundshape(lua_State* L, int index); #endif diff --git a/src/api/l_physics.c b/src/api/l_physics.c index 4f484984..206bcf90 100644 --- a/src/api/l_physics.c +++ b/src/api/l_physics.c @@ -9,6 +9,7 @@ StringEntry lovrShapeType[] = { [SHAPE_CYLINDER] = ENTRY("cylinder"), [SHAPE_MESH] = ENTRY("mesh"), [SHAPE_TERRAIN] = ENTRY("terrain"), + [SHAPE_COMPOUND] = ENTRY("compound"), { 0 } }; @@ -178,6 +179,13 @@ static int l_lovrPhysicsNewTerrainShape(lua_State* L) { return 1; } +static int l_lovrPhysicsNewCompoundShape(lua_State* L) { + CompoundShape* shape = luax_newcompoundshape(L, 1); + luax_pushtype(L, CompoundShape, shape); + lovrRelease(shape, lovrShapeDestroy); + return 1; +} + static const luaL_Reg lovrPhysics[] = { { "newWorld", l_lovrPhysicsNewWorld }, { "newBallJoint", l_lovrPhysicsNewBallJoint }, @@ -190,6 +198,7 @@ static const luaL_Reg lovrPhysics[] = { { "newSliderJoint", l_lovrPhysicsNewSliderJoint }, { "newSphereShape", l_lovrPhysicsNewSphereShape }, { "newTerrainShape", l_lovrPhysicsNewTerrainShape }, + { "newCompoundShape", l_lovrPhysicsNewCompoundShape }, { NULL, NULL } }; @@ -205,6 +214,7 @@ extern const luaL_Reg lovrCapsuleShape[]; extern const luaL_Reg lovrCylinderShape[]; extern const luaL_Reg lovrMeshShape[]; extern const luaL_Reg lovrTerrainShape[]; +extern const luaL_Reg lovrCompoundShape[]; int luaopen_lovr_physics(lua_State* L) { lua_newtable(L); @@ -221,6 +231,7 @@ int luaopen_lovr_physics(lua_State* L) { luax_registertype(L, CylinderShape); luax_registertype(L, MeshShape); luax_registertype(L, TerrainShape); + luax_registertype(L, CompoundShape); lovrPhysicsInit(); luax_atexit(L, lovrPhysicsDestroy); return 1; diff --git a/src/api/l_physics_collider.c b/src/api/l_physics_collider.c index 52cd93e4..0b653fae 100644 --- a/src/api/l_physics_collider.c +++ b/src/api/l_physics_collider.c @@ -17,6 +17,20 @@ static int l_lovrColliderIsDestroyed(lua_State* L) { return 1; } +static int l_lovrColliderIsEnabled(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + bool enabled = lovrColliderIsEnabled(collider); + lua_pushboolean(L, enabled); + return 1; +} + +static int l_lovrColliderSetEnabled(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + bool enable = lua_toboolean(L, 2); + lovrColliderSetEnabled(collider, enable); + return 1; +} + static int l_lovrColliderGetWorld(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); World* world = lovrColliderGetWorld(collider); @@ -24,32 +38,50 @@ static int l_lovrColliderGetWorld(lua_State* L) { return 1; } -static int l_lovrColliderAddShape(lua_State* L) { +static int l_lovrColliderGetShape(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); - Shape* shape = luax_checkshape(L, 2); - lovrColliderAddShape(collider, shape); - return 0; -} - -static int l_lovrColliderRemoveShape(lua_State* L) { - Collider* collider = luax_checktype(L, 1, Collider); - Shape* shape = luax_checkshape(L, 2); - lovrColliderRemoveShape(collider, shape); - return 0; -} - -static int l_lovrColliderGetShapes(lua_State* L) { - Collider* collider = luax_checktype(L, 1, Collider); - size_t count; - Shape** shapes = lovrColliderGetShapes(collider, &count); - lua_createtable(L, (int) count, 0); - for (size_t i = 0; i < count; i++) { - luax_pushshape(L, shapes[i]); - lua_rawseti(L, -2, (int) i + 1); + uint32_t child = lua_gettop(L) == 1 ? ~0u : luax_checku32(L, 2) - 1; + Shape* shape = lovrColliderGetShape(collider, child); + if (shape) { + luax_pushshape(L, shape); + } else { + lua_pushnil(L); } return 1; } +static int l_lovrColliderSetShape(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + Shape* shape = lua_isnoneornil(L, 2) ? NULL : luax_checkshape(L, 2); + lovrColliderSetShape(collider, shape); + return 0; +} + +static int l_lovrColliderGetShapeOffset(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + float position[3], orientation[4], angle, ax, ay, az; + lovrColliderGetShapeOffset(collider, position, orientation); + quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); + lua_pushnumber(L, position[0]); + lua_pushnumber(L, position[1]); + lua_pushnumber(L, position[2]); + lua_pushnumber(L, angle); + lua_pushnumber(L, ax); + lua_pushnumber(L, ay); + lua_pushnumber(L, az); + return 7; +} + +static int l_lovrColliderSetShapeOffset(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + int index = 2; + float position[3], orientation[4]; + index = luax_readvec3(L, index, position, NULL); + index = luax_readquat(L, index, orientation, NULL); + lovrColliderSetShapeOffset(collider, position, orientation); + return 0; +} + static int l_lovrColliderGetJoints(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); size_t count; @@ -110,6 +142,19 @@ static int l_lovrColliderSetKinematic(lua_State* L) { return 0; } +static int l_lovrColliderIsSensor(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + lua_pushboolean(L, lovrColliderIsSensor(collider)); + return 1; +} + +static int l_lovrColliderSetSensor(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + bool sensor = lua_toboolean(L, 2); + lovrColliderSetSensor(collider, sensor); + return 0; +} + static int l_lovrColliderIsContinuous(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); bool continuous = lovrColliderIsContinuous(collider); @@ -544,6 +589,16 @@ static int l_lovrColliderSetTag(lua_State* L) { return 0; } +// Deprecated +static int l_lovrColliderGetShapes(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + Shape* shape = lovrColliderGetShape(collider, ~0u); + lua_createtable(L, 1, 0); + luax_pushshape(L, shape); + lua_rawseti(L, -2, 1); + return 1; +} + // Deprecated static int l_lovrColliderIsGravityIgnored(lua_State* L) { Collider* collider = luax_checktype(L, 1, Collider); @@ -562,15 +617,20 @@ static int l_lovrColliderSetGravityIgnored(lua_State* L) { const luaL_Reg lovrCollider[] = { { "destroy", l_lovrColliderDestroy }, { "isDestroyed", l_lovrColliderIsDestroyed }, + { "isEnabled", l_lovrColliderIsEnabled }, + { "setEnabled", l_lovrColliderSetEnabled }, { "getWorld", l_lovrColliderGetWorld }, - { "addShape", l_lovrColliderAddShape }, - { "removeShape", l_lovrColliderRemoveShape }, - { "getShapes", l_lovrColliderGetShapes }, + { "getShape", l_lovrColliderGetShape }, + { "setShape", l_lovrColliderSetShape }, + { "getShapeOffset", l_lovrColliderGetShapeOffset }, + { "setShapeOffset", l_lovrColliderSetShapeOffset }, { "getJoints", l_lovrColliderGetJoints }, { "getUserData", l_lovrColliderGetUserData }, { "setUserData", l_lovrColliderSetUserData }, { "isKinematic", l_lovrColliderIsKinematic }, { "setKinematic", l_lovrColliderSetKinematic }, + { "isSensor", l_lovrColliderIsSensor }, + { "setSensor", l_lovrColliderSetSensor }, { "isContinuous", l_lovrColliderIsContinuous }, { "setContinuous", l_lovrColliderSetContinuous }, { "getGravityScale", l_lovrColliderGetGravityScale }, @@ -617,6 +677,7 @@ const luaL_Reg lovrCollider[] = { { "setTag", l_lovrColliderSetTag }, // Deprecated + { "getShapes", l_lovrColliderGetShapes }, { "isGravityIgnored", l_lovrColliderIsGravityIgnored }, { "setGravityIgnored", l_lovrColliderSetGravityIgnored }, diff --git a/src/api/l_physics_shapes.c b/src/api/l_physics_shapes.c index c5f6970e..ced3443d 100644 --- a/src/api/l_physics_shapes.c +++ b/src/api/l_physics_shapes.c @@ -14,6 +14,7 @@ void luax_pushshape(lua_State* L, Shape* shape) { case SHAPE_CYLINDER: luax_pushtype(L, CylinderShape, shape); break; case SHAPE_MESH: luax_pushtype(L, MeshShape, shape); break; case SHAPE_TERRAIN: luax_pushtype(L, TerrainShape, shape); break; + case SHAPE_COMPOUND: luax_pushtype(L, CompoundShape, shape); break; default: lovrUnreachable(); } } @@ -28,7 +29,8 @@ Shape* luax_checkshape(lua_State* L, int index) { hash64("CapsuleShape", strlen("CapsuleShape")), hash64("CylinderShape", strlen("CylinderShape")), hash64("MeshShape", strlen("MeshShape")), - hash64("TerrainShape", strlen("TerrainShape")) + hash64("TerrainShape", strlen("TerrainShape")), + hash64("CompoundShape", strlen("CompoundShape")) }; for (size_t i = 0; i < COUNTOF(hashes); i++) { @@ -135,6 +137,85 @@ Shape* luax_newterrainshape(lua_State* L, int index) { } } +Shape* luax_newcompoundshape(lua_State* L, int index) { + if (lua_isnoneornil(L, index)) { + return lovrCompoundShapeCreate(NULL, NULL, NULL, 0, false); + } + + luaL_checktype(L, index, LUA_TTABLE); + int length = luax_len(L, index); + + uint32_t defer = lovrDeferPush(); + Shape** shapes = lovrMalloc(length * sizeof(Shape*)); + float* positions = lovrMalloc(length * 3 * sizeof(float)); + float* orientations = lovrMalloc(length * 4 * sizeof(float)); + lovrDefer(lovrFree, shapes); + lovrDefer(lovrFree, positions); + lovrDefer(lovrFree, orientations); + + for (int i = 0; i < length; i++) { + lua_rawgeti(L, index, i + 1); + lovrCheck(lua_istable(L, -1), "Expected table of tables for compound shape"); + + lua_rawgeti(L, -1, 1); + shapes[i] = luax_checkshape(L, -1); + lua_pop(L, 1); + + int index = 2; + lua_rawgeti(L, -1, index); + switch (lua_type(L, -1)) { + case LUA_TNIL: + vec3_set(&positions[3 * i], 0.f, 0.f, 0.f); + lua_pop(L, 1); + break; + case LUA_TNUMBER: + lua_rawgeti(L, -2, index + 1); + lua_rawgeti(L, -3, index + 2); + vec3_set(&positions[3 * i], luax_tofloat(L, -3), luax_tofloat(L, -2), luax_tofloat(L, -1)); + lua_pop(L, 3); + index += 3; + break; + default: { + float* v = luax_checkvector(L, -1, V_VEC3, "nil, number, or vec3"); + vec3_init(&positions[3 * i], v); + lua_pop(L, 1); + break; + } + } + + lua_rawgeti(L, -1, index); + switch (lua_type(L, -1)) { + case LUA_TNIL: + quat_identity(&orientations[4 * i]); + lua_pop(L, 1); + break; + case LUA_TNUMBER: + lua_rawgeti(L, -2, index); + lua_rawgeti(L, -3, index); + lua_rawgeti(L, -4, index); + quat_set(&orientations[4 * i], luax_tofloat(L, -4), luax_tofloat(L, -3), luax_tofloat(L, -2), luax_tofloat(L, -1)); + lua_pop(L, 4); + break; + default: { + float* q = luax_checkvector(L, -1, V_QUAT, "nil, number, or quat"); + quat_init(&positions[4 * i], q); + lua_pop(L, 1); + break; + } + } + + lua_pop(L, 1); + } + + lua_getfield(L, index, "freeze"); + bool freeze = lua_toboolean(L, -1); + lua_pop(L, 1); + + CompoundShape* shape = lovrCompoundShapeCreate(shapes, positions, orientations, length, freeze); + lovrDeferPop(defer); + return shape; +} + static int l_lovrShapeDestroy(lua_State* L) { Shape* shape = luax_checkshape(L, 1); lovrShapeDestroyData(shape); @@ -147,38 +228,6 @@ static int l_lovrShapeGetType(lua_State* L) { return 1; } -static int l_lovrShapeGetCollider(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - luax_pushtype(L, Collider, lovrShapeGetCollider(shape)); - return 1; -} - -static int l_lovrShapeIsEnabled(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - lua_pushboolean(L, lovrShapeIsEnabled(shape)); - return 1; -} - -static int l_lovrShapeSetEnabled(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - bool enabled = lua_toboolean(L, 2); - lovrShapeSetEnabled(shape, enabled); - return 0; -} - -static int l_lovrShapeIsSensor(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - lua_pushboolean(L, lovrShapeIsSensor(shape)); - return 1; -} - -static int l_lovrShapeSetSensor(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - bool sensor = lua_toboolean(L, 2); - lovrShapeSetSensor(shape, sensor); - return 0; -} - static void luax_pushshapestash(lua_State* L) { lua_getfield(L, LUA_REGISTRYINDEX, "_lovrshapestash"); @@ -214,74 +263,6 @@ static int l_lovrShapeSetUserData(lua_State* L) { return 0; } -static int l_lovrShapeGetPosition(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - float x, y, z; - lovrShapeGetPosition(shape, &x, &y, &z); - lua_pushnumber(L, x); - lua_pushnumber(L, y); - lua_pushnumber(L, z); - return 3; -} - -static int l_lovrShapeSetPosition(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - lovrCheck(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider"); - float position[3]; - luax_readvec3(L, 2, position, NULL); - lovrShapeSetPosition(shape, position[0], position[1], position[2]); - return 0; -} - -static int l_lovrShapeGetOrientation(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - float angle, x, y, z, orientation[4]; - lovrShapeGetOrientation(shape, orientation); - quat_getAngleAxis(orientation, &angle, &x, &y, &z); - lua_pushnumber(L, angle); - lua_pushnumber(L, x); - lua_pushnumber(L, y); - lua_pushnumber(L, z); - return 4; -} - -static int l_lovrShapeSetOrientation(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - lovrCheck(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider"); - float orientation[4]; - luax_readquat(L, 2, orientation, NULL); - lovrShapeSetOrientation(shape, orientation); - return 0; -} - -static int l_lovrShapeGetPose(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - float x, y, z; - lovrShapeGetPosition(shape, &x, &y, &z); - float angle, ax, ay, az, orientation[4]; - lovrShapeGetOrientation(shape, orientation); - quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); - lua_pushnumber(L, x); - lua_pushnumber(L, y); - lua_pushnumber(L, z); - lua_pushnumber(L, angle); - lua_pushnumber(L, ax); - lua_pushnumber(L, ay); - lua_pushnumber(L, az); - return 7; -} - -static int l_lovrShapeSetPose(lua_State* L) { - Shape* shape = luax_checkshape(L, 1); - lovrCheck(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider"); - float position[3], orientation[4]; - int index = luax_readvec3(L, 2, position, NULL); - luax_readquat(L, index, orientation, NULL); - lovrShapeSetPosition(shape, position[0], position[1], position[2]); - lovrShapeSetOrientation(shape, orientation); - return 0; -} - static int l_lovrShapeGetMass(lua_State* L) { Shape* shape = luax_checkshape(L, 1); float density = luax_checkfloat(L, 2); @@ -302,8 +283,15 @@ static int l_lovrShapeGetMass(lua_State* L) { static int l_lovrShapeGetAABB(lua_State* L) { Shape* shape = luax_checkshape(L, 1); - float aabb[6]; - lovrShapeGetAABB(shape, aabb); + float position[3], orientation[4], aabb[6]; + if (lua_gettop(L) >= 2) { + int index = 2; + index = luax_readvec3(L, index, position, NULL); + index = luax_readquat(L, index, orientation, NULL); + lovrShapeGetAABB(shape, position, orientation, aabb); + } else { + lovrShapeGetAABB(shape, NULL, NULL, aabb); + } for (int i = 0; i < 6; i++) { lua_pushnumber(L, aabb[i]); } @@ -313,19 +301,8 @@ static int l_lovrShapeGetAABB(lua_State* L) { #define lovrShape \ { "destroy", l_lovrShapeDestroy }, \ { "getType", l_lovrShapeGetType }, \ - { "getCollider", l_lovrShapeGetCollider }, \ - { "isEnabled", l_lovrShapeIsEnabled }, \ - { "setEnabled", l_lovrShapeSetEnabled }, \ - { "isSensor", l_lovrShapeIsSensor }, \ - { "setSensor", l_lovrShapeSetSensor }, \ { "getUserData", l_lovrShapeGetUserData }, \ { "setUserData", l_lovrShapeSetUserData }, \ - { "getPosition", l_lovrShapeGetPosition }, \ - { "setPosition", l_lovrShapeSetPosition }, \ - { "getOrientation", l_lovrShapeGetOrientation }, \ - { "setOrientation", l_lovrShapeSetOrientation }, \ - { "getPose", l_lovrShapeGetPose }, \ - { "setPose", l_lovrShapeSetPose }, \ { "getMass", l_lovrShapeGetMass }, \ { "getAABB", l_lovrShapeGetAABB } @@ -335,17 +312,9 @@ static int l_lovrSphereShapeGetRadius(lua_State* L) { return 1; } -static int l_lovrSphereShapeSetRadius(lua_State* L) { - SphereShape* sphere = luax_checktype(L, 1, SphereShape); - float radius = luax_checkfloat(L, 2); - lovrSphereShapeSetRadius(sphere, radius); - return 0; -} - const luaL_Reg lovrSphereShape[] = { lovrShape, { "getRadius", l_lovrSphereShapeGetRadius }, - { "setRadius", l_lovrSphereShapeSetRadius }, { NULL, NULL } }; @@ -359,18 +328,9 @@ static int l_lovrBoxShapeGetDimensions(lua_State* L) { return 3; } -static int l_lovrBoxShapeSetDimensions(lua_State* L) { - BoxShape* box = luax_checktype(L, 1, BoxShape); - float size[3]; - luax_readscale(L, 2, size, 3, NULL); - lovrBoxShapeSetDimensions(box, size[0], size[1], size[2]); - return 0; -} - const luaL_Reg lovrBoxShape[] = { lovrShape, { "getDimensions", l_lovrBoxShapeGetDimensions }, - { "setDimensions", l_lovrBoxShapeSetDimensions }, { NULL, NULL } }; @@ -380,32 +340,16 @@ static int l_lovrCapsuleShapeGetRadius(lua_State* L) { return 1; } -static int l_lovrCapsuleShapeSetRadius(lua_State* L) { - CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape); - float radius = luax_checkfloat(L, 2); - lovrCapsuleShapeSetRadius(capsule, radius); - return 0; -} - static int l_lovrCapsuleShapeGetLength(lua_State* L) { CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape); lua_pushnumber(L, lovrCapsuleShapeGetLength(capsule)); return 1; } -static int l_lovrCapsuleShapeSetLength(lua_State* L) { - CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape); - float length = luax_checkfloat(L, 2); - lovrCapsuleShapeSetLength(capsule, length); - return 0; -} - const luaL_Reg lovrCapsuleShape[] = { lovrShape, { "getRadius", l_lovrCapsuleShapeGetRadius }, - { "setRadius", l_lovrCapsuleShapeSetRadius }, { "getLength", l_lovrCapsuleShapeGetLength }, - { "setLength", l_lovrCapsuleShapeSetLength }, { NULL, NULL } }; @@ -415,32 +359,16 @@ static int l_lovrCylinderShapeGetRadius(lua_State* L) { return 1; } -static int l_lovrCylinderShapeSetRadius(lua_State* L) { - CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape); - float radius = luax_checkfloat(L, 2); - lovrCylinderShapeSetRadius(cylinder, radius); - return 0; -} - static int l_lovrCylinderShapeGetLength(lua_State* L) { CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape); lua_pushnumber(L, lovrCylinderShapeGetLength(cylinder)); return 1; } -static int l_lovrCylinderShapeSetLength(lua_State* L) { - CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape); - float length = luax_checkfloat(L, 2); - lovrCylinderShapeSetLength(cylinder, length); - return 0; -} - const luaL_Reg lovrCylinderShape[] = { lovrShape, { "getRadius", l_lovrCylinderShapeGetRadius }, - { "setRadius", l_lovrCylinderShapeSetRadius }, { "getLength", l_lovrCylinderShapeGetLength }, - { "setLength", l_lovrCylinderShapeSetLength }, { NULL, NULL } }; @@ -453,3 +381,109 @@ const luaL_Reg lovrTerrainShape[] = { lovrShape, { NULL, NULL } }; + +static int l_lovrCompoundShapeIsFrozen(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + bool frozen = lovrCompoundShapeIsFrozen(shape); + lua_pushboolean(L, frozen); + return 1; +} + +static int l_lovrCompoundShapeAddChild(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + Shape* child = luax_checkshape(L, 2); + float position[3], orientation[4]; + int index = 3; + index = luax_readvec3(L, index, position, NULL); + index = luax_readquat(L, index, orientation, NULL); + lovrCompoundShapeAddChild(shape, child, position, orientation); + return 0; +} + +static int l_lovrCompoundShapeReplaceChild(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + uint32_t index = luax_checku32(L, 2) - 1; + Shape* child = luax_checkshape(L, 3); + float position[3], orientation[4]; + int i = 4; + i = luax_readvec3(L, i, position, NULL); + i = luax_readquat(L, i, orientation, NULL); + lovrCompoundShapeReplaceChild(shape, index, child, position, orientation); + return 0; +} + +static int l_lovrCompoundShapeRemoveChild(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + uint32_t index = luax_checku32(L, 2) - 1; + lovrCompoundShapeRemoveChild(shape, index); + return 0; +} + +static int l_lovrCompoundShapeGetChild(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + uint32_t index = luax_checku32(L, 2) - 1; + Shape* child = lovrCompoundShapeGetChild(shape, index); + luax_pushshape(L, child); + return 1; +} + +static int l_lovrCompoundShapeGetChildren(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + int count = (int) lovrCompoundShapeGetChildCount(shape); + lua_createtable(L, count, 0); + for (int i = 0; i < count; i++) { + Shape* child = lovrCompoundShapeGetChild(shape, (uint32_t) i); + luax_pushshape(L, child); + lua_rawseti(L, -2, i + 1); + } + return 1; +} + +static int l_lovrCompoundShapeGetChildCount(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + uint32_t count = lovrCompoundShapeGetChildCount(shape); + lua_pushinteger(L, count); + return 1; +} + +static int l_lovrCompoundShapeGetChildOffset(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + uint32_t index = luax_checku32(L, 2) - 1; + float position[3], orientation[4], angle, ax, ay, az; + lovrCompoundShapeGetChildOffset(shape, index, position, orientation); + quat_getAngleAxis(orientation, &angle, &ax, &ay, &az); + lua_pushnumber(L, position[0]); + lua_pushnumber(L, position[1]); + lua_pushnumber(L, position[2]); + lua_pushnumber(L, angle); + lua_pushnumber(L, ax); + lua_pushnumber(L, ay); + lua_pushnumber(L, az); + return 7; +} + +static int l_lovrCompoundShapeSetChildOffset(lua_State* L) { + CompoundShape* shape = luax_checktype(L, 1, CompoundShape); + uint32_t index = luax_checku32(L, 2) - 1; + float position[3], orientation[4]; + int i = 3; + i = luax_readvec3(L, i, position, NULL); + i = luax_readquat(L, i, orientation, NULL); + lovrCompoundShapeSetChildOffset(shape, index, position, orientation); + return 0; +} + +const luaL_Reg lovrCompoundShape[] = { + lovrShape, + { "isFrozen", l_lovrCompoundShapeIsFrozen }, + { "addChild", l_lovrCompoundShapeAddChild }, + { "replaceChild", l_lovrCompoundShapeReplaceChild }, + { "removeChild", l_lovrCompoundShapeRemoveChild }, + { "getChild", l_lovrCompoundShapeGetChild }, + { "getChildren", l_lovrCompoundShapeGetChildren }, + { "getChildCount", l_lovrCompoundShapeGetChildCount }, + { "getChildOffset", l_lovrCompoundShapeGetChildOffset }, + { "setChildOffset", l_lovrCompoundShapeSetChildOffset }, + { "__len", l_lovrCompoundShapeGetChildCount }, // :) + { NULL, NULL } +}; diff --git a/src/api/l_physics_world.c b/src/api/l_physics_world.c index bf1a708b..8a036deb 100644 --- a/src/api/l_physics_world.c +++ b/src/api/l_physics_world.c @@ -27,17 +27,18 @@ static int nextOverlap(lua_State* L) { } } -static bool raycastCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) { +static bool raycastCallback(Collider* collider, float position[3], float normal[3], uint32_t shape, void* userdata) { lua_State* L = userdata; lua_pushvalue(L, -1); - luax_pushshape(L, shape); - lua_pushnumber(L, x); - lua_pushnumber(L, y); - lua_pushnumber(L, z); - lua_pushnumber(L, nx); - lua_pushnumber(L, ny); - lua_pushnumber(L, nz); - lua_call(L, 7, 1); + luax_pushtype(L, Collider, collider); + lua_pushnumber(L, position[0]); + lua_pushnumber(L, position[1]); + lua_pushnumber(L, position[2]); + lua_pushnumber(L, normal[0]); + lua_pushnumber(L, normal[1]); + lua_pushnumber(L, normal[2]); + lua_pushinteger(L, shape + 1); + lua_call(L, 8, 1); bool shouldStop = lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1); lua_pop(L, 1); return shouldStop; @@ -45,53 +46,55 @@ static bool raycastCallback(Shape* shape, float x, float y, float z, float nx, f typedef struct { const char* tag; - Shape* shape; + Collider* collider; + uint32_t shape; float distance; float origin[3]; float position[3]; float normal[3]; } RaycastData; -static bool raycastAnyCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) { +static bool raycastAnyCallback(Collider* collider, float position[3], float normal[3], uint32_t shape, void* userdata) { RaycastData* data = userdata; if (data->tag) { - const char* tag = lovrColliderGetTag(lovrShapeGetCollider(shape)); + const char* tag = lovrColliderGetTag(collider); if (!tag || strcmp(tag, data->tag)) { return false; } } + data->collider = collider; data->shape = shape; - vec3_set(data->position, x, y, z); - vec3_set(data->normal, nx, ny, nz); + vec3_init(data->position, position); + vec3_init(data->normal, normal); data->distance = vec3_distance(data->origin, data->position); return true; } -static bool raycastClosestCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) { +static bool raycastClosestCallback(Collider* collider, float position[3], float normal[3], uint32_t shape, void* userdata) { RaycastData* data = userdata; if (data->tag) { - const char* tag = lovrColliderGetTag(lovrShapeGetCollider(shape)); + const char* tag = lovrColliderGetTag(collider); if (!tag || strcmp(tag, data->tag)) { return false; } } - float position[3]; - vec3_set(position, x, y, z); float distance = vec3_distance(data->origin, position); if (distance < data->distance) { vec3_init(data->position, position); - vec3_set(data->normal, nx, ny, nz); + vec3_init(data->normal, normal); data->distance = distance; + data->collider = collider; data->shape = shape; } return false; } -static bool queryCallback(Shape* shape, void* userdata) { +static bool queryCallback(Collider* collider, uint32_t shape, void* userdata) { lua_State* L = userdata; lua_pushvalue(L, -1); - luax_pushshape(L, shape); - lua_call(L, 1, 1); + luax_pushtype(L, Collider, collider); + lua_pushinteger(L, shape + 1); + lua_call(L, 2, 1); bool shouldStop = lua_type(L, -1) == LUA_TBOOLEAN && !lua_toboolean(L, -1); lua_pop(L, 1); return shouldStop; @@ -99,9 +102,10 @@ static bool queryCallback(Shape* shape, void* userdata) { static int l_lovrWorldNewCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); + Shape* shape = luax_totype(L, 2, Shape); float position[3]; - luax_readvec3(L, 2, position, NULL); - Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]); + luax_readvec3(L, 2 + !!shape, position, NULL); + Collider* collider = lovrColliderCreate(world, shape, position[0], position[1], position[2]); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); return 1; @@ -111,9 +115,8 @@ static int l_lovrWorldNewBoxCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[3]; int index = luax_readvec3(L, 2, position, NULL); - Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]); BoxShape* shape = luax_newboxshape(L, index); - lovrColliderAddShape(collider, shape); + Collider* collider = lovrColliderCreate(world, shape, position[0], position[1], position[2]); lovrColliderInitInertia(collider, shape); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); @@ -125,9 +128,8 @@ static int l_lovrWorldNewCapsuleCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[3]; int index = luax_readvec3(L, 2, position, NULL); - Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]); CapsuleShape* shape = luax_newcapsuleshape(L, index); - lovrColliderAddShape(collider, shape); + Collider* collider = lovrColliderCreate(world, shape, position[0], position[1], position[2]); lovrColliderInitInertia(collider, shape); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); @@ -139,9 +141,8 @@ static int l_lovrWorldNewCylinderCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[3]; int index = luax_readvec3(L, 2, position, NULL); - Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]); CylinderShape* shape = luax_newcylindershape(L, index); - lovrColliderAddShape(collider, shape); + Collider* collider = lovrColliderCreate(world, shape, position[0], position[1], position[2]); lovrColliderInitInertia(collider, shape); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); @@ -153,9 +154,8 @@ static int l_lovrWorldNewSphereCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[3]; int index = luax_readvec3(L, 2, position, NULL); - Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]); SphereShape* shape = luax_newsphereshape(L, index); - lovrColliderAddShape(collider, shape); + Collider* collider = lovrColliderCreate(world, shape, position[0], position[1], position[2]); lovrColliderInitInertia(collider, shape); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); @@ -165,9 +165,8 @@ static int l_lovrWorldNewSphereCollider(lua_State* L) { static int l_lovrWorldNewMeshCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); - Collider* collider = lovrColliderCreate(world, 0.f, 0.f, 0.f); MeshShape* shape = luax_newmeshshape(L, 2); - lovrColliderAddShape(collider, shape); + Collider* collider = lovrColliderCreate(world, shape, 0.f, 0.f, 0.f); lovrColliderInitInertia(collider, shape); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); @@ -177,9 +176,8 @@ static int l_lovrWorldNewMeshCollider(lua_State* L) { static int l_lovrWorldNewTerrainCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); - Collider* collider = lovrColliderCreate(world, 0.f, 0.f, 0.f); TerrainShape* shape = luax_newterrainshape(L, 2); - lovrColliderAddShape(collider, shape); + Collider* collider = lovrColliderCreate(world, shape, 0.f, 0.f, 0.f); lovrColliderSetKinematic(collider, true); luax_pushtype(L, Collider, collider); lovrRelease(collider, lovrColliderDestroy); @@ -309,15 +307,16 @@ static int l_lovrWorldRaycastAny(lua_State* L) { RaycastData data = { 0 }; data.tag = lua_tostring(L, index); lovrWorldRaycast(world, start[0], start[1], start[2], end[0], end[1], end[2], raycastAnyCallback, &data); - if (data.shape) { - luax_pushshape(L, data.shape); + if (data.collider) { + luax_pushtype(L, Collider, data.collider); lua_pushnumber(L, data.position[0]); lua_pushnumber(L, data.position[1]); lua_pushnumber(L, data.position[2]); lua_pushnumber(L, data.normal[0]); lua_pushnumber(L, data.normal[1]); lua_pushnumber(L, data.normal[2]); - return 7; + lua_pushinteger(L, data.shape + 1); + return 8; } else { lua_pushnil(L); return 1; @@ -334,14 +333,15 @@ static int l_lovrWorldRaycastClosest(lua_State* L) { data.tag = lua_tostring(L, index); lovrWorldRaycast(world, start[0], start[1], start[2], end[0], end[1], end[2], raycastClosestCallback, &data); if (data.shape) { - luax_pushshape(L, data.shape); + luax_pushtype(L, Collider, data.collider); lua_pushnumber(L, data.position[0]); lua_pushnumber(L, data.position[1]); lua_pushnumber(L, data.position[2]); lua_pushnumber(L, data.normal[0]); lua_pushnumber(L, data.normal[1]); lua_pushnumber(L, data.normal[2]); - return 7; + lua_pushinteger(L, data.shape + 1); + return 8; } else { lua_pushnil(L); return 1; diff --git a/src/modules/physics/physics.h b/src/modules/physics/physics.h index 0a756f75..ebea6098 100644 --- a/src/modules/physics/physics.h +++ b/src/modules/physics/physics.h @@ -19,6 +19,7 @@ typedef Shape CapsuleShape; typedef Shape CylinderShape; typedef Shape MeshShape; typedef Shape TerrainShape; +typedef Shape CompoundShape; typedef Joint BallJoint; typedef Joint DistanceJoint; @@ -26,8 +27,8 @@ typedef Joint HingeJoint; typedef Joint SliderJoint; typedef void (*CollisionResolver)(World* world, void* userdata); -typedef bool (*RaycastCallback)(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata); -typedef bool (*QueryCallback)(Shape* shape, void* userdata); +typedef bool (*RaycastCallback)(Collider* collider, float position[3], float normal[3], uint32_t child, void* userdata); +typedef bool (*QueryCallback)(Collider* collider, uint32_t child, void* userdata); bool lovrPhysicsInit(void); void lovrPhysicsDestroy(void); @@ -85,16 +86,19 @@ void lovrWorldSetSleepingAllowed(World* world, bool allowed); // Collider -Collider* lovrColliderCreate(World* world, float x, float y, float z); +Collider* lovrColliderCreate(World* world, Shape* shape, float x, float y, float z); void lovrColliderDestroy(void* ref); void lovrColliderDestroyData(Collider* collider); bool lovrColliderIsDestroyed(Collider* collider); +bool lovrColliderIsEnabled(Collider* collider); +void lovrColliderSetEnabled(Collider* collider, bool enable); void lovrColliderInitInertia(Collider* collider, Shape* shape); World* lovrColliderGetWorld(Collider* collider); Collider* lovrColliderGetNext(Collider* collider); -void lovrColliderAddShape(Collider* collider, Shape* shape); -void lovrColliderRemoveShape(Collider* collider, Shape* shape); -Shape** lovrColliderGetShapes(Collider* collider, size_t* count); +Shape* lovrColliderGetShape(Collider* collider, uint32_t child); +void lovrColliderSetShape(Collider* collider, Shape* shape); +void lovrColliderGetShapeOffset(Collider* collider, float* position, float* orientation); +void lovrColliderSetShapeOffset(Collider* collider, float* position, float* orientation); Joint** lovrColliderGetJoints(Collider* collider, size_t* count); const char* lovrColliderGetTag(Collider* collider); bool lovrColliderSetTag(Collider* collider, const char* tag); @@ -104,6 +108,8 @@ float lovrColliderGetRestitution(Collider* collider); void lovrColliderSetRestitution(Collider* collider, float restitution); bool lovrColliderIsKinematic(Collider* collider); void lovrColliderSetKinematic(Collider* collider, bool kinematic); +bool lovrColliderIsSensor(Collider* collider); +void lovrColliderSetSensor(Collider* collider, bool sensor); bool lovrColliderIsContinuous(Collider* collider); void lovrColliderSetContinuous(Collider* collider, bool continuous); float lovrColliderGetGravityScale(Collider* collider); @@ -151,48 +157,44 @@ typedef enum { SHAPE_CAPSULE, SHAPE_CYLINDER, SHAPE_MESH, - SHAPE_TERRAIN + SHAPE_TERRAIN, + SHAPE_COMPOUND } ShapeType; void lovrShapeDestroy(void* ref); void lovrShapeDestroyData(Shape* shape); ShapeType lovrShapeGetType(Shape* shape); -Collider* lovrShapeGetCollider(Shape* shape); -bool lovrShapeIsEnabled(Shape* shape); -void lovrShapeSetEnabled(Shape* shape, bool enabled); -bool lovrShapeIsSensor(Shape* shape); -void lovrShapeSetSensor(Shape* shape, bool sensor); -void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z); -void lovrShapeSetPosition(Shape* shape, float x, float y, float z); -void lovrShapeGetOrientation(Shape* shape, float* orientation); -void lovrShapeSetOrientation(Shape* shape, float* orientation); void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* cz, float* mass, float inertia[6]); -void lovrShapeGetAABB(Shape* shape, float aabb[6]); +void lovrShapeGetAABB(Shape* shape, float position[3], float orientation[4], float aabb[6]); SphereShape* lovrSphereShapeCreate(float radius); float lovrSphereShapeGetRadius(SphereShape* sphere); -void lovrSphereShapeSetRadius(SphereShape* sphere, float radius); BoxShape* lovrBoxShapeCreate(float w, float h, float d); void lovrBoxShapeGetDimensions(BoxShape* box, float* w, float* h, float* d); -void lovrBoxShapeSetDimensions(BoxShape* box, float w, float h, float d); CapsuleShape* lovrCapsuleShapeCreate(float radius, float length); float lovrCapsuleShapeGetRadius(CapsuleShape* capsule); -void lovrCapsuleShapeSetRadius(CapsuleShape* capsule, float radius); float lovrCapsuleShapeGetLength(CapsuleShape* capsule); -void lovrCapsuleShapeSetLength(CapsuleShape* capsule, float length); CylinderShape* lovrCylinderShapeCreate(float radius, float length); float lovrCylinderShapeGetRadius(CylinderShape* cylinder); -void lovrCylinderShapeSetRadius(CylinderShape* cylinder, float radius); float lovrCylinderShapeGetLength(CylinderShape* cylinder); -void lovrCylinderShapeSetLength(CylinderShape* cylinder, float length); MeshShape* lovrMeshShapeCreate(int vertexCount, float vertices[], int indexCount, uint32_t indices[]); TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t n, float scaleXZ, float scaleY); +CompoundShape* lovrCompoundShapeCreate(Shape** shapes, float* positions, float* orientations, uint32_t count, bool freeze); +bool lovrCompoundShapeIsFrozen(CompoundShape* shape); +void lovrCompoundShapeAddChild(CompoundShape* shape, Shape* child, float* position, float* orientation); +void lovrCompoundShapeReplaceChild(CompoundShape* shape, uint32_t index, Shape* child, float* position, float* orientation); +void lovrCompoundShapeRemoveChild(CompoundShape* shape, uint32_t index); +Shape* lovrCompoundShapeGetChild(CompoundShape* shape, uint32_t index); +uint32_t lovrCompoundShapeGetChildCount(CompoundShape* shape); +void lovrCompoundShapeGetChildOffset(CompoundShape* shape, uint32_t index, float* position, float* orientation); +void lovrCompoundShapeSetChildOffset(CompoundShape* shape, uint32_t index, float* position, float* orientation); + // These tokens need to exist for Lua bindings #define lovrSphereShapeDestroy lovrShapeDestroy #define lovrBoxShapeDestroy lovrShapeDestroy @@ -200,6 +202,7 @@ TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t n, float scaleXZ, #define lovrCylinderShapeDestroy lovrShapeDestroy #define lovrMeshShapeDestroy lovrShapeDestroy #define lovrTerrainShapeDestroy lovrShapeDestroy +#define lovrCompoundShapeDestroy lovrShapeDestroy // Joints diff --git a/src/modules/physics/physics_jolt.c b/src/modules/physics/physics_jolt.c index 290b5d88..cea42c79 100644 --- a/src/modules/physics/physics_jolt.c +++ b/src/modules/physics/physics_jolt.c @@ -22,9 +22,9 @@ struct Collider { JPH_BodyID id; JPH_Body* body; World* world; + Shape* shape; Collider* prev; Collider* next; - arr_t(Shape*) shapes; arr_t(Joint*) joints; uint32_t tag; }; @@ -32,7 +32,6 @@ struct Collider { struct Shape { uint32_t ref; ShapeType type; - Collider* collider; JPH_Shape* shape; }; @@ -44,6 +43,7 @@ struct Joint { static struct { bool initialized; + Shape* pointShape; JPH_Shape* queryBox; JPH_Shape* querySphere; JPH_AllHit_CastShapeCollector* castShapeCollector; @@ -70,14 +70,16 @@ static uint32_t findTag(World* world, const char* name) { bool lovrPhysicsInit(void) { if (state.initialized) return false; JPH_Init(32 * 1024 * 1024); - state.castShapeCollector = JPH_AllHit_CastShapeCollector_Create(); + state.pointShape = lovrSphereShapeCreate(FLT_EPSILON); state.querySphere = (JPH_Shape*) JPH_SphereShape_Create(1.f); state.queryBox = (JPH_Shape*) JPH_BoxShape_Create(&(const JPH_Vec3) { .5, .5f, .5f }, 0.f); + state.castShapeCollector = JPH_AllHit_CastShapeCollector_Create(); return state.initialized = true; } void lovrPhysicsDestroy(void) { if (!state.initialized) return; + lovrRelease(state.pointShape, lovrShapeDestroy); JPH_Shutdown(); state.initialized = false; } @@ -173,32 +175,34 @@ void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, floa const JPH_Vec3 direction = { x2 - x1, y2 - y1, z2 - z1 }; JPH_AllHit_CastRayCollector* collector = JPH_AllHit_CastRayCollector_Create(); JPH_NarrowPhaseQuery_CastRayAll(query, &origin, &direction, collector, NULL, NULL, NULL); - size_t hit_count; - JPH_RayCastResult* hit_array = JPH_AllHit_CastRayCollector_GetHits(collector, &hit_count); - for (int i = 0; i < hit_count; i++) { - float x = x1 + hit_array[i].fraction * (x2 - x1); - float y = y1 + hit_array[i].fraction * (y2 - y1); - float z = z1 + hit_array[i].fraction * (z2 - z1); - // todo: assuming one shape per collider; doesn't support compound shape - Collider* collider = (Collider*) JPH_BodyInterface_GetUserData( - world->bodies, - hit_array[i].bodyID); - size_t count; - Shape** shape = lovrColliderGetShapes(collider, &count); - const JPH_RVec3 position = { x, y, z }; + + size_t count; + JPH_RayCastResult* hits = JPH_AllHit_CastRayCollector_GetHits(collector, &count); + + for (size_t i = 0; i < count; i++) { + Collider* collider = (Collider*) (uintptr_t) JPH_BodyInterface_GetUserData(world->bodies, hits[i].bodyID); + uint32_t child = 0; + + if (collider->shape->type == SHAPE_COMPOUND) { + JPH_SubShapeID id = hits[i].subShapeID2; + JPH_SubShapeID remainder; + child = JPH_CompoundShape_GetSubShapeIndexFromID((JPH_CompoundShape*) collider->shape, id, &remainder); + } + + JPH_RVec3 position = { + x1 + hits[i].fraction * (x2 - x1), + y1 + hits[i].fraction * (y2 - y1), + z1 + hits[i].fraction * (z2 - z1) + }; + JPH_Vec3 normal; - JPH_Body_GetWorldSpaceSurfaceNormal(collider->body, hit_array[i].subShapeID2, &position, &normal); + JPH_Body_GetWorldSpaceSurfaceNormal(collider->body, hits[i].subShapeID2, &position, &normal); - bool shouldStop = callback( - shape[0], // assumes one shape per collider; todo: compound shapes - x, y, z, - normal.x, normal.y, normal.z, - userdata); - - if (shouldStop) { + if (callback(collider, &position.x, &normal.x, child, userdata)) { break; } } + JPH_AllHit_CastRayCollector_Destroy(collector); } @@ -209,26 +213,25 @@ static bool lovrWorldQueryShape(World* world, JPH_Shape* shape, float position[3 mat4_translate(m, position[0], position[1], position[2]); mat4_scale(m, scale[0], scale[1], scale[2]); - JPH_Vec3 direction = { 0.f }; - JPH_RVec3 base_offset = { 0.f }; + JPH_Vec3 direction = { 0.f, 0.f, 0.f }; + JPH_RVec3 base_offset = { 0.f, 0.f, 0.f }; const JPH_NarrowPhaseQuery* query = JPC_PhysicsSystem_GetNarrowPhaseQueryNoLock(world->system); JPH_AllHit_CastShapeCollector_Reset(state.castShapeCollector); JPH_NarrowPhaseQuery_CastShape(query, shape, &transform, &direction, &base_offset, state.castShapeCollector); - size_t hit_count; - JPH_ShapeCastResult* hit_array = JPH_AllHit_CastShapeCollector_GetHits(state.castShapeCollector, &hit_count); - for (int i = 0; i < hit_count; i++) { + + size_t count; + JPH_AllHit_CastShapeCollector_GetHits(state.castShapeCollector, &count); + + for (size_t i = 0; i < count; i++) { JPH_BodyID id = JPH_AllHit_CastShapeCollector_GetBodyID2(state.castShapeCollector, i); - Collider* collider = (Collider*) JPH_BodyInterface_GetUserData( - world->bodies, - id); - size_t count; - Shape** shape = lovrColliderGetShapes(collider, &count); - bool shouldStop = callback(shape[0], userdata); - if (shouldStop) { + Collider* collider = (Collider*) (uintptr_t) JPH_BodyInterface_GetUserData(world->bodies, id); + + if (callback(collider, 0, userdata)) { break; } } - return hit_count > 0; + + return count > 0; } bool lovrWorldQueryBox(World* world, float position[3], float size[3], QueryCallback callback, void* userdata) { @@ -315,7 +318,7 @@ void lovrWorldSetAngularDamping(World* world, float damping, float threshold) { // Collider -Collider* lovrColliderCreate(World* world, float x, float y, float z) { +Collider* lovrColliderCreate(World* world, Shape* shape, float x, float y, float z) { uint32_t count = JPH_PhysicsSystem_GetNumBodies(world->system); uint32_t limit = JPH_PhysicsSystem_GetMaxBodies(world->system); lovrCheck(count < limit, "Too many colliders!"); @@ -323,25 +326,25 @@ Collider* lovrColliderCreate(World* world, float x, float y, float z) { Collider* collider = lovrCalloc(sizeof(Collider)); collider->ref = 1; collider->world = world; + collider->shape = shape ? shape : state.pointShape; collider->tag = UNTAGGED; - JPH_MotionType motionType = JPH_MotionType_Dynamic; - JPH_ObjectLayer objectLayer = UNTAGGED * 2 + 1; const JPH_RVec3 position = { x, y, z }; const JPH_Quat rotation = { 0.f, 0.f, 0.f, 1.f }; - // todo: a placeholder querySphere shape is used in collider, then replaced in lovrColliderAddShape - JPH_BodyCreationSettings* settings = JPH_BodyCreationSettings_Create3( - state.querySphere, &position, &rotation, motionType, objectLayer); + JPH_MotionType type = JPH_MotionType_Dynamic; + JPH_ObjectLayer objectLayer = UNTAGGED * 2 + 1; + JPH_BodyCreationSettings* settings = JPH_BodyCreationSettings_Create3(collider->shape->shape, &position, &rotation, type, objectLayer); collider->body = JPH_BodyInterface_CreateBody(world->bodies, settings); - JPH_BodyCreationSettings_Destroy(settings); collider->id = JPH_Body_GetID(collider->body); + JPH_BodyCreationSettings_Destroy(settings); + JPH_BodyInterface_AddBody(world->bodies, collider->id, JPH_Activation_Activate); JPH_BodyInterface_SetUserData(world->bodies, collider->id, (uint64_t) collider); + lovrColliderSetLinearDamping(collider, world->defaultLinearDamping, 0.f); lovrColliderSetAngularDamping(collider, world->defaultAngularDamping, 0.f); lovrColliderSetSleepingAllowed(collider, world->defaultIsSleepingAllowed); - arr_init(&collider->shapes); arr_init(&collider->joints); // Adjust the world's collider list @@ -353,15 +356,14 @@ Collider* lovrColliderCreate(World* world, float x, float y, float z) { world->head = collider; } - // The world owns a reference to the collider - lovrRetain(collider); + lovrRetain(collider->shape); + lovrRetain(collider); // The world owns a reference to the collider return collider; } void lovrColliderDestroy(void* ref) { Collider* collider = ref; lovrColliderDestroyData(collider); - arr_free(&collider->shapes); arr_free(&collider->joints); lovrFree(collider); } @@ -371,13 +373,9 @@ void lovrColliderDestroyData(Collider* collider) { return; } + lovrRelease(collider->shape, lovrShapeDestroy); + size_t count; - - Shape** shapes = lovrColliderGetShapes(collider, &count); - for (size_t i = 0; i < count; i++) { - lovrColliderRemoveShape(collider, shapes[i]); - } - Joint** joints = lovrColliderGetJoints(collider, &count); for (size_t i = 0; i < count; i++) { lovrRelease(joints[i], lovrJointDestroy); @@ -399,6 +397,18 @@ bool lovrColliderIsDestroyed(Collider* collider) { return !collider->body; } +bool lovrColliderIsEnabled(Collider* collider) { + return JPH_BodyInterface_IsAdded(collider->world->bodies, collider->id); +} + +void lovrColliderSetEnabled(Collider* collider, bool enable) { + if (enable && !lovrColliderIsEnabled(collider)) { + JPH_BodyInterface_AddBody(collider->world->bodies, collider->id, JPH_Activation_DontActivate); + } else if (!enable && lovrColliderIsEnabled(collider)) { + JPH_BodyInterface_RemoveBody(collider->world->bodies, collider->id); + } +} + void lovrColliderInitInertia(Collider* collider, Shape* shape) { // } @@ -411,29 +421,75 @@ Collider* lovrColliderGetNext(Collider* collider) { return collider->next; } -void lovrColliderAddShape(Collider* collider, Shape* shape) { +Shape* lovrColliderGetShape(Collider* collider, uint32_t child) { + if (collider->shape == state.pointShape) { + return NULL; + } + + if (child == ~0u || collider->shape->type != SHAPE_COMPOUND) { + return collider->shape; + } + + return lovrCompoundShapeGetChild(collider->shape, child); +} + +void lovrColliderSetShape(Collider* collider, Shape* shape) { + shape = shape ? shape : state.pointShape; + + if (shape == collider->shape) { + return; + } + + float position[3], orientation[4]; + const JPH_Shape* parent = JPH_BodyInterface_GetShape(collider->world->bodies, collider->id); + bool hasOffset = JPH_Shape_GetSubType(parent) == JPH_ShapeSubType_RotatedTranslated; + if (hasOffset) lovrColliderGetShapeOffset(collider, position, orientation); + + lovrRelease(collider->shape, lovrShapeDestroy); + collider->shape = shape; lovrRetain(shape); - shape->collider = collider; - arr_push(&collider->shapes, shape); - bool isMeshOrTerrain = (shape->type == SHAPE_TERRAIN) || (shape->type == SHAPE_MESH); - bool shouldUpdateMass = !isMeshOrTerrain; - if (isMeshOrTerrain) { - lovrColliderSetKinematic(shape->collider, true); - } - JPH_BodyInterface_SetShape(collider->world->bodies, collider->id, shape->shape, shouldUpdateMass, JPH_Activation_Activate); -} -void lovrColliderRemoveShape(Collider* collider, Shape* shape) { - if (shape->collider == collider) { - // todo: actions necessary for compound shapes - shape->collider = NULL; - lovrRelease(shape, lovrShapeDestroy); + bool updateMass = true; + if (shape->type == SHAPE_MESH || shape->type == SHAPE_TERRAIN) { + lovrColliderSetKinematic(collider, true); + updateMass = false; + } + + JPH_BodyInterface_SetShape(collider->world->bodies, collider->id, shape->shape, updateMass, JPH_Activation_Activate); + + if (hasOffset) { + lovrColliderSetShapeOffset(collider, position, orientation); } } -Shape** lovrColliderGetShapes(Collider* collider, size_t* count) { - *count = collider->shapes.length; - return collider->shapes.data; +void lovrColliderGetShapeOffset(Collider* collider, float* position, float* orientation) { + const JPH_Shape* shape = JPH_BodyInterface_GetShape(collider->world->bodies, collider->id); + + if (JPH_Shape_GetSubType(shape) == JPH_ShapeSubType_RotatedTranslated) { + JPH_Vec3 jposition; + JPH_Quat jrotation; + JPH_RotatedTranslatedShape_GetPosition((JPH_RotatedTranslatedShape*) shape, &jposition); + JPH_RotatedTranslatedShape_GetRotation((JPH_RotatedTranslatedShape*) shape, &jrotation); + vec3_init(position, &jposition.x); + quat_init(orientation, &jrotation.x); + } else { + vec3_set(position, 0.f, 0.f, 0.f); + quat_identity(orientation); + } +} + +void lovrColliderSetShapeOffset(Collider* collider, float* position, float* orientation) { + const JPH_Shape* shape = JPH_BodyInterface_GetShape(collider->world->bodies, collider->id); + + if (JPH_Shape_GetSubType(shape) == JPH_ShapeSubType_RotatedTranslated) { + JPH_Shape_Destroy((JPH_Shape*) shape); + } + + JPH_Vec3 jposition = { position[0], position[1], position[2] }; + JPH_Quat jrotation = { orientation[0], orientation[1], orientation[2], orientation[3] }; + shape = (JPH_Shape*) JPH_RotatedTranslatedShape_Create(&jposition, &jrotation, collider->shape->shape); + bool updateMass = collider->shape && (collider->shape->type == SHAPE_MESH || collider->shape->type == SHAPE_TERRAIN); + JPH_BodyInterface_SetShape(collider->world->bodies, collider->id, shape, updateMass, JPH_Activation_Activate); } Joint** lovrColliderGetJoints(Collider* collider, size_t* count) { @@ -500,6 +556,14 @@ void lovrColliderSetKinematic(Collider* collider, bool kinematic) { } } +bool lovrColliderIsSensor(Collider* collider) { + return JPH_Body_IsSensor(collider->body); +} + +void lovrColliderSetSensor(Collider* collider, bool sensor) { + JPH_Body_SetIsSensor(collider->body, sensor); +} + bool lovrColliderIsContinuous(Collider* collider) { return JPH_BodyInterface_GetMotionQuality(collider->world->bodies, collider->id) == JPH_MotionQuality_LinearCast; } @@ -538,22 +602,17 @@ void lovrColliderSetAwake(Collider* collider, bool awake) { } float lovrColliderGetMass(Collider* collider) { - if (collider->shapes.length > 0) { - JPH_MotionProperties* motionProperties = JPH_Body_GetMotionProperties(collider->body); - return 1.f / JPH_MotionProperties_GetInverseMassUnchecked(motionProperties); - } - return 0.f; + JPH_MotionProperties* motionProperties = JPH_Body_GetMotionProperties(collider->body); + return 1.f / JPH_MotionProperties_GetInverseMassUnchecked(motionProperties); } void lovrColliderSetMass(Collider* collider, float mass) { - if (collider->shapes.length > 0) { - JPH_MotionProperties* motionProperties = JPH_Body_GetMotionProperties(collider->body); - Shape* shape = collider->shapes.data[0]; - JPH_MassProperties* massProperties; - JPH_Shape_GetMassProperties(shape->shape, massProperties); - JPH_MassProperties_ScaleToMass(massProperties, mass); - JPH_MotionProperties_SetMassProperties(motionProperties, JPH_AllowedDOFs_All, massProperties); - } + JPH_MotionProperties* motionProperties = JPH_Body_GetMotionProperties(collider->body); + Shape* shape = collider->shape; + JPH_MassProperties* massProperties; + JPH_Shape_GetMassProperties(shape->shape, massProperties); + JPH_MassProperties_ScaleToMass(massProperties, mass); + JPH_MotionProperties_SetMassProperties(motionProperties, JPH_AllowedDOFs_All, massProperties); } void lovrColliderGetMassData(Collider* collider, float* cx, float* cy, float* cz, float* mass, float inertia[6]) { @@ -765,6 +824,8 @@ void lovrColliderGetAABB(Collider* collider, float aabb[6]) { aabb[5] = box.max.z; } +// Shapes + void lovrShapeDestroy(void* ref) { Shape* shape = ref; lovrShapeDestroyData(shape); @@ -773,6 +834,14 @@ void lovrShapeDestroy(void* ref) { void lovrShapeDestroyData(Shape* shape) { if (shape->shape) { + if (shape->type == SHAPE_COMPOUND) { + uint32_t count = lovrCompoundShapeGetChildCount(shape); + for (uint32_t i = 0; i < count; i++) { + Shape* child = lovrCompoundShapeGetChild(shape, i); + lovrRelease(child, lovrShapeDestroy); + } + } + JPH_Shape_Destroy(shape->shape); shape->shape = NULL; } @@ -782,58 +851,26 @@ ShapeType lovrShapeGetType(Shape* shape) { return shape->type; } -Collider* lovrShapeGetCollider(Shape* shape) { - return shape->collider; +void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* cz, float* mass, float inertia[6]) { + // } -bool lovrShapeIsEnabled(Shape* shape) { - return true; -} - -void lovrShapeSetEnabled(Shape* shape, bool enabled) { - if (!enabled) { - lovrLog(LOG_WARN, "PHY", "Jolt doesn't support disabling shapes"); +void lovrShapeGetAABB(Shape* shape, float position[3], float orientation[4], float aabb[6]) { + JPH_AABox box; + if (!position && !orientation) { + JPH_Shape_GetLocalBounds(shape->shape, &box); + } else { + JPH_RMatrix4x4 transform; + JPH_Vec3 scale = { 1.f, 1.f, 1.f }; + mat4_fromPose(&transform.m11, position, orientation); + JPH_Shape_GetWorldSpaceBounds(shape->shape, &transform, &scale, &box); } -} - -bool lovrShapeIsSensor(Shape* shape) { - lovrLog(LOG_WARN, "PHY", "Jolt sensor property fetched from collider, not shape"); - return JPH_Body_IsSensor(shape->collider->body); -} - -void lovrShapeSetSensor(Shape* shape, bool sensor) { - lovrLog(LOG_WARN, "PHY", "Jolt sensor property is applied to collider, not shape"); - JPH_Body_SetIsSensor(shape->collider->body, sensor); -} - -void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z) { - // todo: compound shapes - *x = 0.f; - *y = 0.f; - *z = 0.f; -} - -void lovrShapeSetPosition(Shape* shape, float x, float y, float z) { - // todo: compound shapes -} - -void lovrShapeGetOrientation(Shape* shape, float* orientation) { - // todo: compound shapes - orientation[0] = 0.f; - orientation[1] = 0.f; - orientation[2] = 0.f; - orientation[3] = 1.f; -} - -void lovrShapeSetOrientation(Shape* shape, float* orientation) { - // todo: compound shapes -} - -void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* cz, float* mass, float inertia[6]) {} - -void lovrShapeGetAABB(Shape* shape, float aabb[6]) { - // todo: with compound shapes this is no longer correct - lovrColliderGetAABB(shape->collider, aabb); + aabb[0] = box.min.x; + aabb[1] = box.max.x; + aabb[2] = box.min.y; + aabb[3] = box.max.y; + aabb[4] = box.min.z; + aabb[5] = box.max.z; } SphereShape* lovrSphereShapeCreate(float radius) { @@ -842,6 +879,7 @@ SphereShape* lovrSphereShapeCreate(float radius) { sphere->ref = 1; sphere->type = SHAPE_SPHERE; sphere->shape = (JPH_Shape*) JPH_SphereShape_Create(radius); + JPH_Shape_SetUserData(sphere->shape, (uint64_t) (uintptr_t) sphere); return sphere; } @@ -849,17 +887,13 @@ float lovrSphereShapeGetRadius(SphereShape* sphere) { return JPH_SphereShape_GetRadius((JPH_SphereShape*) sphere->shape); } -void lovrSphereShapeSetRadius(SphereShape* sphere, float radius) { - lovrLog(LOG_WARN, "PHY", "Jolt SphereShape radius is read-only"); - // todo: no setter available, but the shape could be removed and re-added -} - BoxShape* lovrBoxShapeCreate(float w, float h, float d) { BoxShape* box = lovrCalloc(sizeof(BoxShape)); box->ref = 1; box->type = SHAPE_BOX; const JPH_Vec3 halfExtent = { w / 2.f, h / 2.f, d / 2.f }; box->shape = (JPH_Shape*) JPH_BoxShape_Create(&halfExtent, 0.f); + JPH_Shape_SetUserData(box->shape, (uint64_t) (uintptr_t) box); return box; } @@ -871,17 +905,13 @@ void lovrBoxShapeGetDimensions(BoxShape* box, float* w, float* h, float* d) { *d = halfExtent.z * 2.f; } -void lovrBoxShapeSetDimensions(BoxShape* box, float w, float h, float d) { - lovrLog(LOG_WARN, "PHY", "Jolt BoxShape dimensions are read-only"); - // todo: no setter available, but the shape could be removed and re-added -} - CapsuleShape* lovrCapsuleShapeCreate(float radius, float length) { lovrCheck(radius > 0.f && length > 0.f, "CapsuleShape dimensions must be positive"); CapsuleShape* capsule = lovrCalloc(sizeof(CapsuleShape)); capsule->ref = 1; capsule->type = SHAPE_CAPSULE; capsule->shape = (JPH_Shape*) JPH_CapsuleShape_Create(length / 2, radius); + JPH_Shape_SetUserData(capsule->shape, (uint64_t) (uintptr_t) capsule); return capsule; } @@ -889,45 +919,26 @@ float lovrCapsuleShapeGetRadius(CapsuleShape* capsule) { return JPH_CapsuleShape_GetRadius((JPH_CapsuleShape*) capsule->shape); } -void lovrCapsuleShapeSetRadius(CapsuleShape* capsule, float radius) { - lovrLog(LOG_WARN, "PHY", "Jolt CapsuleShape radius is read-only"); - // todo: no setter available, but the shape could be removed and re-added -} - float lovrCapsuleShapeGetLength(CapsuleShape* capsule) { return 2.f * JPH_CapsuleShape_GetHalfHeightOfCylinder((JPH_CapsuleShape*) capsule->shape); } -void lovrCapsuleShapeSetLength(CapsuleShape* capsule, float length) { - lovrLog(LOG_WARN, "PHY", "Jolt CapsuleShape length is read-only"); - // todo: no setter available, but the shape could be removed and re-added -} - CylinderShape* lovrCylinderShapeCreate(float radius, float length) { lovrCheck(radius > 0.f && length > 0.f, "CylinderShape dimensions must be positive"); - CylinderShape* Cylinder = lovrCalloc(sizeof(CylinderShape)); - Cylinder->ref = 1; - Cylinder->type = SHAPE_CYLINDER; - Cylinder->shape = (JPH_Shape*) JPH_CylinderShape_Create(length / 2.f, radius); - return Cylinder; + CylinderShape* cylinder = lovrCalloc(sizeof(CylinderShape)); + cylinder->ref = 1; + cylinder->type = SHAPE_CYLINDER; + cylinder->shape = (JPH_Shape*) JPH_CylinderShape_Create(length / 2.f, radius); + JPH_Shape_SetUserData(cylinder->shape, (uint64_t) (uintptr_t) cylinder); + return cylinder; } -float lovrCylinderShapeGetRadius(CylinderShape* Cylinder) { - return JPH_CylinderShape_GetRadius((JPH_CylinderShape*) Cylinder->shape); +float lovrCylinderShapeGetRadius(CylinderShape* cylinder) { + return JPH_CylinderShape_GetRadius((JPH_CylinderShape*) cylinder->shape); } -void lovrCylinderShapeSetRadius(CylinderShape* Cylinder, float radius) { - lovrLog(LOG_WARN, "PHY", "Jolt CylinderShape radius is read-only"); - // todo: no setter available, but the shape could be removed and re-added -} - -float lovrCylinderShapeGetLength(CylinderShape* Cylinder) { - return JPH_CylinderShape_GetHalfHeight((JPH_CylinderShape*) Cylinder->shape) * 2.f; -} - -void lovrCylinderShapeSetLength(CylinderShape* cylinder, float length) { - lovrLog(LOG_WARN, "PHY", "Jolt CylinderShape length is read-only"); - // todo: no setter available, but the shape could be removed and re-added +float lovrCylinderShapeGetLength(CylinderShape* cylinder) { + return JPH_CylinderShape_GetHalfHeight((JPH_CylinderShape*) cylinder->shape) * 2.f; } MeshShape* lovrMeshShapeCreate(int vertexCount, float vertices[], int indexCount, uint32_t indices[]) { @@ -980,6 +991,102 @@ TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t n, float scaleXZ, return terrain; } +CompoundShape* lovrCompoundShapeCreate(Shape** shapes, vec3 positions, quat orientations, uint32_t count, bool freeze) { + lovrCheck(!freeze || count >= 2, "A frozen CompoundShape must contain at least two shapes"); + + CompoundShape* shape = lovrCalloc(sizeof(CompoundShape)); + shape->ref = 1; + shape->type = SHAPE_COMPOUND; + + JPH_CompoundShapeSettings* settings = freeze ? + (JPH_CompoundShapeSettings*) JPH_StaticCompoundShapeSettings_Create() : + (JPH_CompoundShapeSettings*) JPH_MutableCompoundShapeSettings_Create(); + + for (uint32_t i = 0; i < count; i++) { + lovrCheck(shapes[i]->type != SHAPE_COMPOUND, "Currently, nesting compound shapes is not supported"); + JPH_Vec3 position = { positions[3 * i + 0], positions[3 * i + 1], positions[3 * i + 2] }; + JPH_Quat rotation = { orientations[4 * i + 0], orientations[4 * i + 1], orientations[4 * i + 2], orientations[4 * i + 3] }; + JPH_CompoundShapeSettings_AddShape2(settings, &position, &rotation, shapes[i]->shape, 0); + lovrRetain(shapes[i]); + } + + if (freeze) { + shape->shape = (JPH_Shape*) JPH_StaticCompoundShape_Create((JPH_StaticCompoundShapeSettings*) settings); + } else { + shape->shape = (JPH_Shape*) JPH_MutableCompoundShape_Create((JPH_MutableCompoundShapeSettings*) settings); + } + + JPH_ShapeSettings_Destroy((JPH_ShapeSettings*) settings); + return shape; +} + +bool lovrCompoundShapeIsFrozen(CompoundShape* shape) { + return JPH_Shape_GetSubType(shape->shape) == JPH_ShapeSubType_StaticCompound; +} + +void lovrCompoundShapeAddChild(CompoundShape* shape, Shape* child, float* position, float* orientation) { + lovrCheck(!lovrCompoundShapeIsFrozen(shape), "CompoundShape is frozen and can not be changed"); + lovrCheck(child->type != SHAPE_COMPOUND, "Currently, nesting compound shapes is not supported"); + JPH_Vec3 pos = { position[0], position[1], position[2] }; + JPH_Quat rot = { orientation[0], orientation[1], orientation[2], orientation[3] }; + JPH_MutableCompoundShape_AddShape((JPH_MutableCompoundShape*) shape->shape, &pos, &rot, child->shape, 0); + lovrRetain(child); +} + +void lovrCompoundShapeReplaceChild(CompoundShape* shape, uint32_t index, Shape* child, float* position, float* orientation) { + lovrCheck(!lovrCompoundShapeIsFrozen(shape), "CompoundShape is frozen and can not be changed"); + lovrCheck(child->type != SHAPE_COMPOUND, "Currently, nesting compound shapes is not supported"); + lovrCheck(index < lovrCompoundShapeGetChildCount(shape), "CompoundShape has no child at index %d", index + 1); + JPH_Vec3 pos = { position[0], position[1], position[2] }; + JPH_Quat rot = { orientation[0], orientation[1], orientation[2], orientation[3] }; + lovrRelease(lovrCompoundShapeGetChild(shape, index), lovrShapeDestroy); + JPH_MutableCompoundShape_ModifyShape2((JPH_MutableCompoundShape*) shape->shape, index, &pos, &rot, child->shape); + lovrRetain(child); +} + +void lovrCompoundShapeRemoveChild(CompoundShape* shape, uint32_t index) { + lovrCheck(!lovrCompoundShapeIsFrozen(shape), "CompoundShape is frozen and can not be changed"); + lovrCheck(index < lovrCompoundShapeGetChildCount(shape), "CompoundShape has no child at index %d", index + 1); + Shape* child = lovrCompoundShapeGetChild(shape, index); + JPH_MutableCompoundShape_RemoveShape((JPH_MutableCompoundShape*) shape->shape, index); + lovrRelease(child, lovrShapeDestroy); +} + +Shape* lovrCompoundShapeGetChild(CompoundShape* shape, uint32_t index) { + if (index < lovrCompoundShapeGetChildCount(shape)) { + const JPH_Shape* child; + JPH_CompoundShape_GetSubShape((JPH_CompoundShape*) shape->shape, index, &child, NULL, NULL, NULL); + return (Shape*) (uintptr_t) JPH_Shape_GetUserData(child); + } else { + return NULL; + } +} + +uint32_t lovrCompoundShapeGetChildCount(CompoundShape* shape) { + return JPH_CompoundShape_GetNumSubShapes((JPH_CompoundShape*) shape->shape); +} + +void lovrCompoundShapeGetChildOffset(CompoundShape* shape, uint32_t index, float* position, float* orientation) { + lovrCheck(index < lovrCompoundShapeGetChildCount(shape), "CompoundShape has no child at index %d", index + 1); + const JPH_Shape* child; + JPH_Vec3 pos; + JPH_Quat rot; + uint32_t userData; + JPH_CompoundShape_GetSubShape((JPH_CompoundShape*) shape->shape, index, &child, &pos, &rot, &userData); + vec3_init(position, &pos.x); + quat_init(orientation, &rot.x); +} + +void lovrCompoundShapeSetChildOffset(CompoundShape* shape, uint32_t index, float* position, float* orientation) { + lovrCheck(!lovrCompoundShapeIsFrozen(shape), "CompoundShape is frozen and can not be changed"); + lovrCheck(index < lovrCompoundShapeGetChildCount(shape), "CompoundShape has no child at index %d", index + 1); + JPH_Vec3 pos = { position[0], position[1], position[2] }; + JPH_Quat rot = { orientation[0], orientation[1], orientation[2], orientation[3] }; + JPH_MutableCompoundShape_ModifyShape((JPH_MutableCompoundShape*) shape->shape, index, &pos, &rot); +} + +// Joints + void lovrJointGetAnchors(Joint* joint, float anchor1[3], float anchor2[3]) { JPH_Body* body1 = JPH_TwoBodyConstraint_GetBody1((JPH_TwoBodyConstraint*) joint->constraint); JPH_Body* body2 = JPH_TwoBodyConstraint_GetBody2((JPH_TwoBodyConstraint*) joint->constraint); diff --git a/src/modules/physics/physics_ode.c b/src/modules/physics/physics_ode.c index f3124ba2..0d413c83 100644 --- a/src/modules/physics/physics_ode.c +++ b/src/modules/physics/physics_ode.c @@ -22,10 +22,11 @@ struct Collider { Collider* prev; Collider* next; uint32_t tag; - arr_t(Shape*) shapes; + Shape* shape; arr_t(Joint*) joints; float friction; float restitution; + bool sensor; }; struct Shape { @@ -35,7 +36,6 @@ struct Shape { Collider* collider; void* vertices; void* indices; - bool sensor; }; struct Joint { @@ -66,8 +66,9 @@ static void raycastCallback(void* d, dGeomID a, dGeomID b) { RaycastCallback callback = data->callback; void* userdata = data->userdata; Shape* shape = dGeomGetData(b); + Collider* collider = dBodyGetData(dGeomGetBody(b)); - if (!shape) { + if (!shape || !collider) { return; } @@ -75,9 +76,7 @@ static void raycastCallback(void* d, dGeomID a, dGeomID b) { int count = dCollide(a, b, MAX_CONTACTS, &contact->geom, sizeof(dContact)); for (int i = 0; i < count; i++) { dContactGeom g = contact[i].geom; - data->shouldStop = callback( - shape, g.pos[0], g.pos[1], g.pos[2], g.normal[0], g.normal[1], g.normal[2], userdata - ); + data->shouldStop = callback(collider, g.pos, g.normal, 0, userdata); } } @@ -93,14 +92,15 @@ static void queryCallback(void* d, dGeomID a, dGeomID b) { if (data->shouldStop) return; Shape* shape = dGeomGetData(b); - if (!shape) { + Collider* collider = dBodyGetData(dGeomGetBody(b)); + if (!shape || !collider) { return; } dContactGeom contact; if (dCollide(a, b, 1 | CONTACTS_UNIMPORTANT, &contact, sizeof(contact))) { if (data->callback) { - data->shouldStop = data->callback(shape, data->userdata); + data->shouldStop = data->callback(collider, 0, data->userdata); } else { data->shouldStop = true; } @@ -279,7 +279,7 @@ int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float res int contactCount = dCollide(a->id, b->id, MAX_CONTACTS, &contacts[0].geom, sizeof(dContact)); - if (!a->sensor && !b->sensor) { + if (!colliderA->sensor && !colliderB->sensor) { for (int c = 0; c < contactCount; c++) { dJointID joint = dJointCreateContact(world->id, world->contactGroup, &contacts[c]); dJointAttach(joint, colliderA->body, colliderB->body); @@ -436,7 +436,7 @@ bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const ch return (world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i)); } -Collider* lovrColliderCreate(World* world, float x, float y, float z) { +Collider* lovrColliderCreate(World* world, Shape* shape, float x, float y, float z) { Collider* collider = lovrCalloc(sizeof(Collider)); collider->ref = 1; collider->body = dBodyCreate(world->id); @@ -445,9 +445,9 @@ Collider* lovrColliderCreate(World* world, float x, float y, float z) { collider->restitution = 0; collider->tag = NO_TAG; dBodySetData(collider->body, collider); - arr_init(&collider->shapes); arr_init(&collider->joints); + lovrColliderSetShape(collider, shape); lovrColliderSetPosition(collider, x, y, z); // Adjust the world's collider list @@ -467,7 +467,6 @@ Collider* lovrColliderCreate(World* world, float x, float y, float z) { void lovrColliderDestroy(void* ref) { Collider* collider = ref; lovrColliderDestroyData(collider); - arr_free(&collider->shapes); arr_free(&collider->joints); lovrFree(collider); } @@ -477,13 +476,9 @@ void lovrColliderDestroyData(Collider* collider) { return; } + lovrColliderSetShape(collider, NULL); + size_t count; - - Shape** shapes = lovrColliderGetShapes(collider, &count); - for (size_t i = 0; i < count; i++) { - lovrColliderRemoveShape(collider, shapes[i]); - } - Joint** joints = lovrColliderGetJoints(collider, &count); for (size_t i = 0; i < count; i++) { lovrRelease(joints[i], lovrJointDestroy); @@ -505,6 +500,14 @@ bool lovrColliderIsDestroyed(Collider* collider) { return !collider->body; } +bool lovrColliderIsEnabled(Collider* collider) { + return true; +} + +void lovrColliderSetEnabled(Collider* collider, bool enable) { + // +} + void lovrColliderInitInertia(Collider* collider, Shape* shape) { // compute inertia matrix for default density const float density = 1.0f; @@ -521,38 +524,50 @@ Collider* lovrColliderGetNext(Collider* collider) { return collider->next; } -void lovrColliderAddShape(Collider* collider, Shape* shape) { - lovrRetain(shape); - - if (shape->collider) { - lovrColliderRemoveShape(shape->collider, shape); - } - - shape->collider = collider; - dGeomSetBody(shape->id, collider->body); - dSpaceID newSpace = collider->world->space; - dSpaceAdd(newSpace, shape->id); +Shape* lovrColliderGetShape(Collider* collider, uint32_t child) { + return collider->shape; } -void lovrColliderRemoveShape(Collider* collider, Shape* shape) { - if (shape->collider == collider) { - dSpaceRemove(collider->world->space, shape->id); - dGeomSetBody(shape->id, 0); - shape->collider = NULL; - lovrRelease(shape, lovrShapeDestroy); +void lovrColliderSetShape(Collider* collider, Shape* shape) { + if (collider->shape) { + dSpaceRemove(collider->world->space, collider->shape->id); + dGeomSetBody(collider->shape->id, 0); + collider->shape->collider = NULL; + lovrRelease(collider->shape, lovrShapeDestroy); } -} -Shape** lovrColliderGetShapes(Collider* collider, size_t* count) { - arr_clear(&collider->shapes); - for (dGeomID geom = dBodyGetFirstGeom(collider->body); geom; geom = dBodyGetNextGeom(geom)) { - Shape* shape = dGeomGetData(geom); - if (shape) { - arr_push(&collider->shapes, shape); + collider->shape = shape; + + if (shape) { + if (shape->collider) { + lovrColliderSetShape(shape->collider, NULL); } + + shape->collider = collider; + dGeomSetBody(shape->id, collider->body); + dSpaceID newSpace = collider->world->space; + dSpaceAdd(newSpace, shape->id); + lovrRetain(shape); } - *count = collider->shapes.length; - return collider->shapes.data; +} + +void lovrColliderGetShapeOffset(Collider* collider, float* position, float* orientation) { + const dReal* p = dGeomGetOffsetPosition(collider->shape->id); + position[0] = p[0]; + position[1] = p[1]; + position[2] = p[2]; + dReal q[4]; + dGeomGetOffsetQuaternion(collider->shape->id, q); + orientation[0] = q[1]; + orientation[1] = q[2]; + orientation[2] = q[3]; + orientation[3] = q[0]; +} + +void lovrColliderSetShapeOffset(Collider* collider, float* position, float* orientation) { + dGeomSetOffsetPosition(collider->shape->id, position[0], position[1], position[2]); + dReal q[4] = { orientation[3], orientation[0], orientation[1], orientation[2] }; + dGeomSetOffsetQuaternion(collider->shape->id, q); } Joint** lovrColliderGetJoints(Collider* collider, size_t* count) { @@ -610,6 +625,14 @@ void lovrColliderSetKinematic(Collider* collider, bool kinematic) { } } +bool lovrColliderIsSensor(Collider* collider) { + return collider->sensor; +} + +void lovrColliderSetSensor(Collider* collider, bool sensor) { + collider->sensor = sensor; +} + bool lovrColliderIsContinuous(Collider* collider) { return false; } @@ -888,51 +911,6 @@ Collider* lovrShapeGetCollider(Shape* shape) { return shape->collider; } -bool lovrShapeIsEnabled(Shape* shape) { - return dGeomIsEnabled(shape->id); -} - -void lovrShapeSetEnabled(Shape* shape, bool enabled) { - if (enabled) { - dGeomEnable(shape->id); - } else { - dGeomDisable(shape->id); - } -} - -bool lovrShapeIsSensor(Shape* shape) { - return shape->sensor; -} - -void lovrShapeSetSensor(Shape* shape, bool sensor) { - shape->sensor = sensor; -} - -void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z) { - const dReal* position = dGeomGetOffsetPosition(shape->id); - *x = position[0]; - *y = position[1]; - *z = position[2]; -} - -void lovrShapeSetPosition(Shape* shape, float x, float y, float z) { - dGeomSetOffsetPosition(shape->id, x, y, z); -} - -void lovrShapeGetOrientation(Shape* shape, float* orientation) { - dReal q[4]; - dGeomGetOffsetQuaternion(shape->id, q); - orientation[0] = q[1]; - orientation[1] = q[2]; - orientation[2] = q[3]; - orientation[3] = q[0]; -} - -void lovrShapeSetOrientation(Shape* shape, float* orientation) { - dReal q[4] = { orientation[3], orientation[0], orientation[1], orientation[2] }; - dGeomSetOffsetQuaternion(shape->id, q); -} - void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* cz, float* mass, float inertia[6]) { dMass m; dMassSetZero(&m); @@ -973,6 +951,8 @@ void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* case SHAPE_TERRAIN: { break; } + + default: break; } const dReal* position = dGeomGetOffsetPosition(shape->id); @@ -996,7 +976,7 @@ void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* inertia[5] = m.I[9]; } -void lovrShapeGetAABB(Shape* shape, float aabb[6]) { +void lovrShapeGetAABB(Shape* shape, float position[3], float orientation[4], float aabb[6]) { dGeomGetAABB(shape->id, aabb); } @@ -1131,6 +1111,42 @@ TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t n, float scaleXZ, return terrain; } +CompoundShape* lovrCompoundShapeCreate(Shape** shapes, float* positions, float* orientations, uint32_t count, bool freeze) { + lovrThrow("ODE does not support compound shape"); +} + +bool lovrCompoundShapeIsFrozen(CompoundShape* shape) { + return false; +} + +void lovrCompoundShapeAddChild(CompoundShape* shape, Shape* child, float* position, float* orientation) { + // +} + +void lovrCompoundShapeReplaceChild(CompoundShape* shape, uint32_t index, Shape* child, float* position, float* orientation) { + // +} + +void lovrCompoundShapeRemoveChild(CompoundShape* shape, uint32_t index) { + // +} + +Shape* lovrCompoundShapeGetChild(CompoundShape* shape, uint32_t index) { + return NULL; +} + +uint32_t lovrCompoundShapeGetChildCount(CompoundShape* shape) { + return 0; +} + +void lovrCompoundShapeGetChildOffset(CompoundShape* shape, uint32_t index, float* position, float* orientation) { + // +} + +void lovrCompoundShapeSetChildOffset(CompoundShape* shape, uint32_t index, float* position, float* orientation) { + // +} + void lovrJointDestroy(void* ref) { Joint* joint = ref; lovrJointDestroyData(joint);