diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eeb37c8..d99568a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -559,6 +559,7 @@ if(LOVR_ENABLE_PHYSICS) target_sources(lovr PRIVATE src/api/l_physics.c src/api/l_physics_collider.c + src/api/l_physics_contact.c src/api/l_physics_joints.c src/api/l_physics_shapes.c src/api/l_physics_world.c diff --git a/deps/jolt-physics-sharp b/deps/jolt-physics-sharp index 5b9a9d94..54a8f950 160000 --- a/deps/jolt-physics-sharp +++ b/deps/jolt-physics-sharp @@ -1 +1 @@ -Subproject commit 5b9a9d94fb178bf5b45875f30321da624b19ffdc +Subproject commit 54a8f950dd55809979dc670097fa25de14723f5d diff --git a/src/api/l_physics.c b/src/api/l_physics.c index e807c50a..a8d65597 100644 --- a/src/api/l_physics.c +++ b/src/api/l_physics.c @@ -307,6 +307,7 @@ static const luaL_Reg lovrPhysics[] = { extern const luaL_Reg lovrWorld[]; extern const luaL_Reg lovrCollider[]; +extern const luaL_Reg lovrContact[]; extern const luaL_Reg lovrBoxShape[]; extern const luaL_Reg lovrSphereShape[]; extern const luaL_Reg lovrCapsuleShape[]; @@ -327,6 +328,7 @@ int luaopen_lovr_physics(lua_State* L) { luax_register(L, lovrPhysics); luax_registertype(L, World); luax_registertype(L, Collider); + luax_registertype(L, Contact); luax_registertype(L, BoxShape); luax_registertype(L, SphereShape); luax_registertype(L, CapsuleShape); diff --git a/src/api/l_physics_contact.c b/src/api/l_physics_contact.c new file mode 100644 index 00000000..3151909f --- /dev/null +++ b/src/api/l_physics_contact.c @@ -0,0 +1,128 @@ +#include "api.h" +#include "physics/physics.h" +#include "util.h" + +static int l_lovrContactGetColliders(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + Collider* a = lovrContactGetColliderA(contact); + Collider* b = lovrContactGetColliderB(contact); + luax_pushtype(L, Collider, a); + luax_pushtype(L, Collider, b); + return 2; +} + +static int l_lovrContactGetShapes(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + Shape* a = lovrContactGetShapeA(contact); + Shape* b = lovrContactGetShapeB(contact); + luax_pushshape(L, a); + luax_pushshape(L, b); + return 2; +} + +static int l_lovrContactGetNormal(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float normal[3]; + lovrContactGetNormal(contact, normal); + lua_pushnumber(L, normal[0]); + lua_pushnumber(L, normal[1]); + lua_pushnumber(L, normal[2]); + return 3; +} + +static int l_lovrContactGetPenetration(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float penetration = lovrContactGetPenetration(contact); + lua_pushnumber(L, penetration); + return 1; +} + +static int l_lovrContactGetPoints(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + uint32_t count = lovrContactGetPointCount(contact); + lua_checkstack(L, count * 3); + float point[3]; + for (uint32_t i = 0; i < count; i++) { + lovrContactGetPoint(contact, i, point); + lua_pushnumber(L, point[0]); + lua_pushnumber(L, point[1]); + lua_pushnumber(L, point[2]); + } + return count * 3; +} + +static int l_lovrContactGetFriction(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float friction = lovrContactGetFriction(contact); + lua_pushnumber(L, friction); + return 1; +} + +static int l_lovrContactSetFriction(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float friction = luax_checkfloat(L, 2); + lovrContactSetFriction(contact, friction); + return 0; +} + +static int l_lovrContactGetRestitution(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float restitution = lovrContactGetRestitution(contact); + lua_pushnumber(L, restitution); + return 1; +} + +static int l_lovrContactSetRestitution(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float restitution = luax_checkfloat(L, 2); + lovrContactSetRestitution(contact, restitution); + return 0; +} + +static int l_lovrContactIsEnabled(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + bool enabled = lovrContactIsEnabled(contact); + lua_pushboolean(L, enabled); + return 1; +} + +static int l_lovrContactSetEnabled(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + lovrContactSetEnabled(contact, lua_toboolean(L, 2)); + return 0; +} + +static int l_lovrContactGetSurfaceVelocity(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float velocity[3]; + lovrContactGetSurfaceVelocity(contact, velocity); + lua_pushnumber(L, velocity[0]); + lua_pushnumber(L, velocity[1]); + lua_pushnumber(L, velocity[2]); + return 3; +} + +static int l_lovrContactSetSurfaceVelocity(lua_State* L) { + Contact* contact = luax_checktype(L, 1, Contact); + float velocity[3]; + luax_readvec3(L, 2, velocity, NULL); + lovrContactSetSurfaceVelocity(contact, velocity); + return 0; +} + +const luaL_Reg lovrContact[] = { + { "getColliders", l_lovrContactGetColliders }, + { "getShapes", l_lovrContactGetShapes }, + { "getNormal", l_lovrContactGetNormal }, + { "getPenetration", l_lovrContactGetPenetration }, + { "getPoints", l_lovrContactGetPoints }, + { "getFriction", l_lovrContactGetFriction }, + { "setFriction", l_lovrContactSetFriction }, + { "getRestitution", l_lovrContactGetRestitution }, + { "setRestitution", l_lovrContactSetRestitution }, + { "isEnabled", l_lovrContactIsEnabled }, + { "setEnabled", l_lovrContactSetEnabled }, + { "getSurfaceVelocity", l_lovrContactGetSurfaceVelocity }, + { "setSurfaceVelocity", l_lovrContactSetSurfaceVelocity }, + { NULL, NULL } +}; diff --git a/src/api/l_physics_world.c b/src/api/l_physics_world.c index de3aefe9..c1126a46 100644 --- a/src/api/l_physics_world.c +++ b/src/api/l_physics_world.c @@ -72,6 +72,64 @@ static void queryNoCallback(void* userdata, Collider* collider) { *((Collider**) userdata) = collider; } +static bool filterCallback(void* userdata, World* world, Collider* a, Collider* b) { + lua_State* L = userdata; + luax_pushstash(L, "lovr.world.filter"); + luax_pushtype(L, World, world); + lua_rawget(L, -2); + lua_remove(L, -2); + luax_pushtype(L, Collider, a); + luax_pushtype(L, Collider, b); + if (lua_pcall(L, 2, 1, 0)) { + lua_settop(L, 3); // Only keep first error + } else { + bool accept = lua_type(L, -1) != LUA_TBOOLEAN || lua_toboolean(L, -1); + lua_pop(L, 1); + return accept; + } + return true; +} + +static void enterCallback(void* userdata, World* world, Collider* a, Collider* b) { + lua_State* L = userdata; + luax_pushstash(L, "lovr.world.enter"); + luax_pushtype(L, World, world); + lua_rawget(L, -2); + lua_remove(L, -2); + luax_pushtype(L, Collider, a); + luax_pushtype(L, Collider, b); + if (lua_pcall(L, 2, 0, 0)) { + lua_settop(L, 3); // Only keep first error + } +} + +static void exitCallback(void* userdata, World* world, Collider* a, Collider* b) { + lua_State* L = userdata; + luax_pushstash(L, "lovr.world.exit"); + luax_pushtype(L, World, world); + lua_rawget(L, -2); + lua_remove(L, -2); + luax_pushtype(L, Collider, a); + luax_pushtype(L, Collider, b); + if (lua_pcall(L, 2, 0, 0)) { + lua_settop(L, 3); // Only keep first error + } +} + +static void contactCallback(void* userdata, World* world, Collider* a, Collider* b, Contact* contact) { + lua_State* L = userdata; + luax_pushstash(L, "lovr.world.contact"); + luax_pushtype(L, World, world); + lua_rawget(L, -2); + lua_remove(L, -2); + luax_pushtype(L, Collider, a); + luax_pushtype(L, Collider, b); + luax_pushtype(L, Contact, contact); + if (lua_pcall(L, 3, 0, 0)) { + lua_settop(L, 3); // Only keep first error + } +} + static int l_lovrWorldNewCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[3]; @@ -242,7 +300,11 @@ static int l_lovrWorldSetGravity(lua_State* L) { static int l_lovrWorldUpdate(lua_State* L) { World* world = luax_checktype(L, 1, World); float dt = luax_checkfloat(L, 2); + lua_settop(L, 2); lovrWorldUpdate(world, dt); + if (lua_type(L, 3) == LUA_TSTRING) { + lua_error(L); + } return 0; } @@ -473,6 +535,86 @@ static int l_lovrWorldSetSleepingAllowed(lua_State* L) { return 0; } +static int l_lovrWorldGetCallbacks(lua_State* L) { + luax_checktype(L, 1, World); + lua_settop(L, 1); + lua_createtable(L, 0, 3); + + luax_pushstash(L, "lovr.world.filter"); + lua_pushvalue(L, 1); + lua_rawget(L, -2); + lua_setfield(L, 2, "filter"); + lua_pop(L, 1); + + luax_pushstash(L, "lovr.world.enter"); + lua_pushvalue(L, 1); + lua_rawget(L, -2); + lua_setfield(L, 2, "enter"); + lua_pop(L, 1); + + luax_pushstash(L, "lovr.world.exit"); + lua_pushvalue(L, 1); + lua_rawget(L, -2); + lua_setfield(L, 2, "exit"); + lua_pop(L, 1); + + luax_pushstash(L, "lovr.world.contact"); + lua_pushvalue(L, 1); + lua_rawget(L, -2); + lua_setfield(L, 2, "exit"); + lua_pop(L, 1); + + return 1; +} + +static int l_lovrWorldSetCallbacks(lua_State* L) { + World* world = luax_checktype(L, 1, World); + if (lua_isnoneornil(L, 2)) { + lovrWorldSetCallbacks(world, &(WorldCallbacks) { 0 }); + return 0; + } + + luaL_checktype(L, 2, LUA_TTABLE); + + luax_pushstash(L, "lovr.world.filter"); + lua_pushvalue(L, 1); + lua_getfield(L, 2, "filter"); + bool filter = lua_type(L, -1) == LUA_TFUNCTION; + lua_rawset(L, -3); + lua_pop(L, 1); + + luax_pushstash(L, "lovr.world.enter"); + lua_pushvalue(L, 1); + lua_getfield(L, 2, "enter"); + bool enter = lua_type(L, -1) == LUA_TFUNCTION; + lua_rawset(L, -3); + lua_pop(L, 1); + + luax_pushstash(L, "lovr.world.exit"); + lua_pushvalue(L, 1); + lua_getfield(L, 2, "exit"); + bool exit = lua_type(L, -1) == LUA_TFUNCTION; + lua_rawset(L, -3); + lua_pop(L, 1); + + luax_pushstash(L, "lovr.world.contact"); + lua_pushvalue(L, 1); + lua_getfield(L, 2, "contact"); + bool contact = lua_type(L, -1) == LUA_TFUNCTION; + lua_rawset(L, -3); + lua_pop(L, 1); + + lovrWorldSetCallbacks(world, &(WorldCallbacks) { + .filter = filter ? filterCallback : NULL, + .enter = enter ? enterCallback : NULL, + .exit = exit ? exitCallback : NULL, + .contact = contact ? contactCallback : NULL, + .userdata = L + }); + + return 0; +} + static int l_lovrWorldGetStepCount(lua_State* L) { World* world = luax_checktype(L, 1, World); int iterations = lovrWorldGetStepCount(world); @@ -513,6 +655,8 @@ const luaL_Reg lovrWorld[] = { { "disableCollisionBetween", l_lovrWorldDisableCollisionBetween }, { "enableCollisionBetween", l_lovrWorldEnableCollisionBetween }, { "isCollisionEnabledBetween", l_lovrWorldIsCollisionEnabledBetween }, + { "getCallbacks", l_lovrWorldGetCallbacks }, + { "setCallbacks", l_lovrWorldSetCallbacks }, // Deprecated { "getTightness", l_lovrWorldGetTightness }, diff --git a/src/modules/physics/physics.h b/src/modules/physics/physics.h index 4de0e11a..bb19b313 100644 --- a/src/modules/physics/physics.h +++ b/src/modules/physics/physics.h @@ -10,6 +10,7 @@ typedef struct World World; typedef struct Collider Collider; +typedef struct Contact Contact; typedef struct Shape Shape; typedef struct Joint Joint; @@ -34,6 +35,14 @@ void lovrPhysicsDestroy(void); // World +typedef struct { + bool (*filter)(void* userdata, World* world, Collider* a, Collider* b); + void (*enter)(void* userdata, World* world, Collider* a, Collider* b); + void (*exit)(void* userdata, World* world, Collider* a, Collider* b); + void (*contact)(void* userdata, World* world, Collider* a, Collider* b, Contact* contact); + void* userdata; +} WorldCallbacks; + typedef struct { uint32_t tickRate; uint32_t tickLimit; @@ -85,6 +94,7 @@ bool lovrWorldQuerySphere(World* world, float position[3], float radius, uint32_ void lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2); void lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2); bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag2); +void lovrWorldSetCallbacks(World* world, WorldCallbacks* callbacks); // Deprecated int lovrWorldGetStepCount(World* world); @@ -167,6 +177,26 @@ void lovrColliderGetLinearVelocityFromLocalPoint(Collider* collider, float point void lovrColliderGetLinearVelocityFromWorldPoint(Collider* collider, float point[3], float velocity[3]); void lovrColliderGetAABB(Collider* collider, float aabb[6]); +// Contact + +Collider* lovrContactGetColliderA(Contact* contact); +Collider* lovrContactGetColliderB(Contact* contact); +Shape* lovrContactGetShapeA(Contact* contact); +Shape* lovrContactGetShapeB(Contact* contact); +void lovrContactGetNormal(Contact* contact, float normal[3]); +float lovrContactGetPenetration(Contact* contact); +uint32_t lovrContactGetPointCount(Contact* contact); +void lovrContactGetPoint(Contact* contact, uint32_t index, float point[3]); +float lovrContactGetFriction(Contact* contact); +void lovrContactSetFriction(Contact* contact, float friction); +float lovrContactGetRestitution(Contact* contact); +void lovrContactSetRestitution(Contact* contact, float restitution); +bool lovrContactIsEnabled(Contact* contact); +void lovrContactSetEnabled(Contact* contact, bool enable); +void lovrContactGetSurfaceVelocity(Contact* contact, float velocity[3]); +void lovrContactSetSurfaceVelocity(Contact* contact, float velocity[3]); +void lovrContactDestroy(void* ref); + // Shapes typedef enum { diff --git a/src/modules/physics/physics_jolt.c b/src/modules/physics/physics_jolt.c index a1de9dc6..a6155d82 100644 --- a/src/modules/physics/physics_jolt.c +++ b/src/modules/physics/physics_jolt.c @@ -5,17 +5,27 @@ #include #include +struct Contact { + Collider* colliderA; + Collider* colliderB; + const JPH_ContactManifold* manifold; + JPH_ContactSettings* settings; +}; + struct World { uint32_t ref; JPH_PhysicsSystem* system; JPH_BodyInterface* bodies; JPH_BodyActivationListener* activationListener; JPH_ObjectLayerPairFilter* objectLayerPairFilter; + JPH_ContactListener* listener; + Contact contact; Collider* colliders; Collider** activeColliders; uint32_t activeColliderCount; Joint* joints; uint32_t jointCount; + WorldCallbacks callbacks; float defaultLinearDamping; float defaultAngularDamping; bool defaultIsSleepingAllowed; @@ -156,6 +166,63 @@ static void onSleep(void* arg, JPH_BodyID id, uint64_t userData) { mtx_unlock(&world->lock); } +static JPH_ValidateResult onContactValidate(void* userdata, const JPH_Body* body1, const JPH_Body* body2, const JPH_RVec3* offset, const JPH_CollideShapeResult* result) { + World* world = userdata; + Collider* a = (Collider*) (uintptr_t) JPH_Body_GetUserData((JPH_Body*) body1); + Collider* b = (Collider*) (uintptr_t) JPH_Body_GetUserData((JPH_Body*) body2); + bool accept = world->callbacks.filter(world->callbacks.userdata, world, a, b); + return accept ? + JPH_ValidateResult_AcceptAllContactsForThisBodyPair : + JPH_ValidateResult_RejectAllContactsForThisBodyPair; +} + +static void onContactPersisted(void* userdata, const JPH_Body* body1, const JPH_Body* body2, const JPH_ContactManifold* manifold, JPH_ContactSettings* settings) { + World* world = userdata; + JPH_BodyID id1 = JPH_Body_GetID(body1); + JPH_BodyID id2 = JPH_Body_GetID(body2); + if (world->callbacks.contact) { + Collider* a = (Collider*) (uintptr_t) JPH_Body_GetUserData((JPH_Body*) body1); + Collider* b = (Collider*) (uintptr_t) JPH_Body_GetUserData((JPH_Body*) body2); + mtx_lock(&world->lock); + world->contact.colliderA = a; + world->contact.colliderB = b; + world->contact.manifold = manifold; + world->contact.settings = settings; + world->callbacks.contact(world->callbacks.userdata, world, a, b, &world->contact); + mtx_unlock(&world->lock); + } +} + +static void onContactAdded(void* userdata, const JPH_Body* body1, const JPH_Body* body2, const JPH_ContactManifold* manifold, JPH_ContactSettings* settings) { + World* world = userdata; + JPH_BodyID id1 = JPH_Body_GetID(body1); + JPH_BodyID id2 = JPH_Body_GetID(body2); + + if (world->callbacks.enter && !JPH_PhysicsSystem_WereBodiesInContact(world->system, id1, id2)) { + Collider* a = (Collider*) (uintptr_t) JPH_Body_GetUserData((JPH_Body*) body1); + Collider* b = (Collider*) (uintptr_t) JPH_Body_GetUserData((JPH_Body*) body2); + mtx_lock(&world->lock); + world->callbacks.enter(world->callbacks.userdata, world, a, b); + mtx_unlock(&world->lock); + } + + onContactPersisted(userdata, body1, body2, manifold, settings); +} + +static void onContactRemoved(void* userdata, const JPH_SubShapeIDPair* pair) { + World* world = userdata; + if (!JPH_PhysicsSystem_WereBodiesInContact(world->system, pair->Body1ID, pair->Body2ID)) { + JPH_BodyInterface* bodies = JPH_PhysicsSystem_GetBodyInterfaceNoLock(world->system); + Collider* a = (Collider*) (uintptr_t) JPH_BodyInterface_GetUserData(bodies, pair->Body1ID); + Collider* b = (Collider*) (uintptr_t) JPH_BodyInterface_GetUserData(bodies, pair->Body2ID); + if (a && b) { + mtx_lock(&world->lock); + world->callbacks.exit(world->callbacks.userdata, world, a, b); + mtx_unlock(&world->lock); + } + } +} + bool lovrPhysicsInit(void) { if (state.initialized) return false; JPH_Init(32 * 1024 * 1024); @@ -273,6 +340,7 @@ void lovrWorldDestroyData(World* world) { lovrColliderDestroyData(collider); world->colliders = next; } + if (world->listener) JPH_ContactListener_Destroy(world->listener); JPH_PhysicsSystem_Destroy(world->system); JPH_BodyActivationListener_Destroy(world->activationListener); } @@ -574,6 +642,26 @@ bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const ch return JPH_ObjectLayerPairFilterTable_ShouldCollide(world->objectLayerPairFilter, i, j); } +void lovrWorldSetCallbacks(World* world, WorldCallbacks* callbacks) { + if (!callbacks || (!callbacks->filter && !callbacks->enter && !callbacks->exit && !callbacks->contact)) { + JPH_PhysicsSystem_SetContactListener(world->system, NULL); + } else { + if (!world->listener) { + world->listener = JPH_ContactListener_Create(); + } + + JPH_ContactListener_SetProcs(world->listener, (JPH_ContactListener_Procs) { + .OnContactValidate = callbacks->filter ? onContactValidate : NULL, + .OnContactAdded = (callbacks->enter || callbacks->contact) ? onContactAdded : NULL, + .OnContactPersisted = callbacks->contact ? onContactPersisted : NULL, + .OnContactRemoved = callbacks->exit ? onContactRemoved : NULL + }, world); + + world->callbacks = *callbacks; + JPH_PhysicsSystem_SetContactListener(world->system, world->listener); + } +} + // Deprecated int lovrWorldGetStepCount(World* world) { return world->collisionSteps; } void lovrWorldSetStepCount(World* world, int iterations) { world->collisionSteps = iterations;} @@ -1151,6 +1239,82 @@ void lovrColliderGetAABB(Collider* collider, float aabb[6]) { aabb[5] = box.max.z; } +// Contact + +Collider* lovrContactGetColliderA(Contact* contact) { + return contact->colliderA; +} + +Collider* lovrContactGetColliderB(Contact* contact) { + return contact->colliderB; +} + +Shape* lovrContactGetShapeA(Contact* contact) { + return subshapeToShape(contact->colliderA, JPH_ContactManifold_GetSubShapeID1(contact->manifold)); +} + +Shape* lovrContactGetShapeB(Contact* contact) { + return subshapeToShape(contact->colliderB, JPH_ContactManifold_GetSubShapeID2(contact->manifold)); +} + +void lovrContactGetNormal(Contact* contact, float normal[3]) { + JPH_Vec3 n; + JPH_ContactManifold_GetWorldSpaceNormal(contact->manifold, &n); + vec3_fromJolt(normal, &n); +} + +float lovrContactGetPenetration(Contact* contact) { + return JPH_ContactManifold_GetPenetrationDepth(contact->manifold); +} + +uint32_t lovrContactGetPointCount(Contact* contact) { + return JPH_ContactManifold_GetPointCount(contact->manifold); +} + +void lovrContactGetPoint(Contact* contact, uint32_t index, float point[3]) { + JPH_Vec3 p; + JPH_ContactManifold_GetWorldSpaceContactPointOn2(contact->manifold, index, &p); + vec3_fromJolt(point, &p); +} + +float lovrContactGetFriction(Contact* contact) { + return JPH_ContactSettings_GetFriction(contact->settings); +} + +void lovrContactSetFriction(Contact* contact, float friction) { + return JPH_ContactSettings_SetFriction(contact->settings, friction); +} + +float lovrContactGetRestitution(Contact* contact) { + return JPH_ContactSettings_GetRestitution(contact->settings); +} + +void lovrContactSetRestitution(Contact* contact, float restitution) { + return JPH_ContactSettings_SetRestitution(contact->settings, restitution); +} + +bool lovrContactIsEnabled(Contact* contact) { + return JPH_ContactSettings_GetIsSensor(contact->settings); +} + +void lovrContactSetEnabled(Contact* contact, bool enable) { + JPH_ContactSettings_SetIsSensor(contact->settings, !enable); +} + +void lovrContactGetSurfaceVelocity(Contact* contact, float velocity[3]) { + JPH_Vec3 v; + JPH_ContactSettings_GetRelativeLinearSurfaceVelocity(contact->settings, &v); + vec3_fromJolt(velocity, &v); +} + +void lovrContactSetSurfaceVelocity(Contact* contact, float velocity[3]) { + JPH_ContactSettings_SetRelativeLinearSurfaceVelocity(contact->settings, vec3_toJolt(velocity)); +} + +void lovrContactDestroy(void* ref) { + // Contact is a temporary object owned by a World +} + // Shapes void lovrShapeDestroy(void* ref) { diff --git a/src/modules/physics/physics_ode.c b/src/modules/physics/physics_ode.c index 1344300b..2d377afa 100644 --- a/src/modules/physics/physics_ode.c +++ b/src/modules/physics/physics_ode.c @@ -423,6 +423,10 @@ bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const ch return (world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i)); } +void lovrWorldSetCallbacks(World* world, WorldCallbacks* callbacks) { + // +} + Collider* lovrColliderCreate(World* world, float position[3], Shape* shape) { Collider* collider = lovrCalloc(sizeof(Collider)); collider->ref = 1; @@ -828,6 +832,74 @@ void lovrColliderGetAABB(Collider* collider, float aabb[6]) { } } +Collider* lovrContactGetColliderA(Contact* contact) { + return NULL; +} + +Collider* lovrContactGetColliderB(Contact* contact) { + return NULL; +} + +Shape* lovrContactGetShapeA(Contact* contact) { + return NULL; +} + +Shape* lovrContactGetShapeB(Contact* contact) { + return NULL; +} + +void lovrContactGetNormal(Contact* contact, float normal[3]) { + // +} + +float lovrContactGetPenetration(Contact* contact) { + return 0.f; +} + +uint32_t lovrContactGetPointCount(Contact* contact) { + return 0; +} + +void lovrContactGetPoint(Contact* contact, uint32_t index, float point[3]) { + // +} + +float lovrContactGetFriction(Contact* contact) { + return 0.f; +} + +void lovrContactSetFriction(Contact* contact, float friction) { + // +} + +float lovrContactGetRestitution(Contact* contact) { + return 0.f; +} + +void lovrContactSetRestitution(Contact* contact, float restitution) { + // +} + +bool lovrContactIsEnabled(Contact* contact) { + return true; +} + +void lovrContactSetEnabled(Contact* contact, bool enable) { + // +} + +void lovrContactGetSurfaceVelocity(Contact* contact, float velocity[3]) { + // +} + +void lovrContactSetSurfaceVelocity(Contact* contact, float velocity[3]) { + // +} + +void lovrContactDestroy(void* ref) { + // +} + void lovrShapeDestroy(void* ref) { Shape* shape = ref; if (shape->type == SHAPE_MESH) {