1
0
Fork 0
mirror of https://github.com/bjornbytes/lovr.git synced 2024-07-08 15:13:35 +00:00

Add contact callback and Contact object;

This commit is contained in:
bjorn 2024-04-21 21:57:51 -07:00
parent bee947a1d0
commit 979dd044d8
8 changed files with 542 additions and 1 deletions

View file

@ -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

@ -1 +1 @@
Subproject commit 5b9a9d94fb178bf5b45875f30321da624b19ffdc
Subproject commit 54a8f950dd55809979dc670097fa25de14723f5d

View file

@ -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);

128
src/api/l_physics_contact.c Normal file
View file

@ -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 }
};

View file

@ -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 },

View file

@ -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 {

View file

@ -5,17 +5,27 @@
#include <threads.h>
#include <joltc.h>
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) {

View file

@ -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) {