From 661e9188c7c93a5252744b7c7e3afc30b9e78421 Mon Sep 17 00:00:00 2001 From: bjorn Date: Fri, 19 May 2017 15:04:34 -0600 Subject: [PATCH] Custom collision handling; --- src/api/lovr.h | 2 + src/api/types/shapes.c | 9 ++++ src/api/types/world.c | 49 +++++++++++++++++++++- src/physics/physics.c | 93 ++++++++++++++++++++++++++++-------------- src/physics/physics.h | 15 ++++++- 5 files changed, 136 insertions(+), 32 deletions(-) diff --git a/src/api/lovr.h b/src/api/lovr.h index 726a611f..1d7c9d47 100644 --- a/src/api/lovr.h +++ b/src/api/lovr.h @@ -2,6 +2,7 @@ #include "filesystem/blob.h" #include "graphics/mesh.h" #include "math/math.h" +#include "physics/physics.h" #include "lib/map/map.h" int l_lovrAudioInit(lua_State* L); @@ -62,3 +63,4 @@ extern map_int_t WrapModes; void luax_checkmeshformat(lua_State* L, int index, MeshFormat* format); int luax_readtransform(lua_State* L, int index, mat4 transform); Blob* luax_readblob(lua_State* L, int index, const char* debug); +int luax_pushshape(lua_State* L, Shape* shape); diff --git a/src/api/types/shapes.c b/src/api/types/shapes.c index afa5de3c..754d5996 100644 --- a/src/api/types/shapes.c +++ b/src/api/types/shapes.c @@ -1,6 +1,15 @@ #include "api/lovr.h" #include "physics/physics.h" +int luax_pushshape(lua_State* L, Shape* shape) { + switch (lovrShapeGetType(shape)) { + case SHAPE_SPHERE: luax_pushtype(L, SphereShape, shape); return 1; + case SHAPE_BOX: luax_pushtype(L, BoxShape, shape); return 1; + case SHAPE_CAPSULE: luax_pushtype(L, CapsuleShape, shape); return 1; + case SHAPE_CYLINDER: luax_pushtype(L, CylinderShape, shape); return 1; + } +} + int l_lovrShapeGetType(lua_State* L) { Shape* shape = luax_checktypeof(L, 1, Shape); luax_pushenum(L, &ShapeTypes, lovrShapeGetType(shape)); diff --git a/src/api/types/world.c b/src/api/types/world.c index eee2ca5f..59b225ce 100644 --- a/src/api/types/world.c +++ b/src/api/types/world.c @@ -1,6 +1,27 @@ #include "api/lovr.h" #include "physics/physics.h" +static void collisionResolver(World* world, void* userdata) { + lua_State* L = userdata; + luaL_checktype(L, -1, LUA_TFUNCTION); + luax_pushtype(L, World, world); + lua_call(L, 1, 0); +} + +static int nextOverlap(lua_State* L) { + World* world = luax_checktype(L, lua_upvalueindex(1), World); + Shape* a; + Shape* b; + if (lovrWorldGetNextOverlap(world, &a, &b)) { + luax_pushshape(L, a); + luax_pushshape(L, b); + return 2; + } else { + lua_pushnil(L); + return 1; + } +} + int l_lovrWorldGetGravity(lua_State* L) { World* world = luax_checktype(L, 1, World); float x, y, z; @@ -68,12 +89,35 @@ int l_lovrWorldSetSleepingAllowed(lua_State* L) { } int l_lovrWorldUpdate(lua_State* L) { + lua_settop(L, 3); World* world = luax_checktype(L, 1, World); float dt = luaL_checknumber(L, 2); - lovrWorldUpdate(world, dt); + CollisionResolver resolver = lua_type(L, 3) == LUA_TFUNCTION ? collisionResolver : NULL; + lovrWorldUpdate(world, dt, resolver, L); return 0; } +int l_lovrWorldComputeOverlaps(lua_State* L) { + World* world = luax_checktype(L, 1, World); + lovrWorldComputeOverlaps(world); + return 0; +} + +int l_lovrWorldOverlaps(lua_State* L) { + luax_checktype(L, 1, World); + lua_settop(L, 1); + lua_pushcclosure(L, nextOverlap, 1); + return 1; +} + +int l_lovrWorldCollide(lua_State* L) { + World* world = luax_checktype(L, 1, World); + Shape* a = luax_checktypeof(L, 2, Shape); + Shape* b = luax_checktypeof(L, 3, Shape); + lua_pushboolean(L, lovrWorldCollide(world, a, b)); + return 1; +} + const luaL_Reg lovrWorld[] = { { "getGravity", l_lovrWorldGetGravity }, { "setGravity", l_lovrWorldSetGravity }, @@ -84,5 +128,8 @@ const luaL_Reg lovrWorld[] = { { "isSleepingAllowed", l_lovrWorldIsSleepingAllowed }, { "setSleepingAllowed", l_lovrWorldSetSleepingAllowed }, { "update", l_lovrWorldUpdate }, + { "computeOverlaps", l_lovrWorldComputeOverlaps }, + { "overlaps", l_lovrWorldOverlaps }, + { "collide", l_lovrWorldCollide }, { NULL, NULL } }; diff --git a/src/physics/physics.c b/src/physics/physics.c index 65415f7c..6a5b8942 100644 --- a/src/physics/physics.c +++ b/src/physics/physics.c @@ -2,31 +2,14 @@ #include "math/quat.h" #include -static void nearCallback(void* data, dGeomID shapeA, dGeomID shapeB) { +static void defaultNearCallback(void* data, dGeomID a, dGeomID b) { + lovrWorldCollide((World*) data, dGeomGetData(a), dGeomGetData(b)); +} + +static void customNearCallback(void* data, dGeomID shapeA, dGeomID shapeB) { World* world = data; - - dBodyID bodyA = dGeomGetBody(shapeA); - dBodyID bodyB = dGeomGetBody(shapeB); - if (bodyA && bodyB && dAreConnectedExcluding(bodyA, bodyB, dJointTypeContact)) { - return; - } - - dContact contacts[8]; - - for (int i = 0; i < 8; i++) { - contacts[i].surface.mode = 0; - contacts[i].surface.mu = dInfinity; - contacts[i].surface.mu2 = 0; - } - - int contactCount; - - contactCount = dCollide(shapeA, shapeB, 8, &contacts[0].geom, sizeof(dContact)); - - for (int i = 0; i < contactCount; i++) { - dJointID joint = dJointCreateContact(world->id, world->contactGroup, &contacts[i]); - dJointAttach(joint, bodyA, bodyB); - } + vec_push(&world->overlaps, dGeomGetData(shapeA)); + vec_push(&world->overlaps, dGeomGetData(shapeB)); } void lovrPhysicsInit() { @@ -51,6 +34,7 @@ World* lovrWorldCreate() { world->space = dHashSpaceCreate(0); dHashSpaceSetLevels(world->space, -4, 8); world->contactGroup = dJointGroupCreate(0); + vec_init(&world->overlaps); return world; } @@ -58,6 +42,7 @@ World* lovrWorldCreate() { void lovrWorldDestroy(const Ref* ref) { World* world = containerof(ref, World); dWorldDestroy(world->id); + vec_deinit(&world->overlaps); free(world); } @@ -101,12 +86,55 @@ void lovrWorldSetSleepingAllowed(World* world, int allowed) { dWorldSetAutoDisableFlag(world->id, allowed); } -void lovrWorldUpdate(World* world, float dt) { - dSpaceCollide(world->space, world, nearCallback); +void lovrWorldUpdate(World* world, float dt, CollisionResolver resolver, void* userdata) { + if (resolver) { + resolver(world, userdata); + } else { + dSpaceCollide(world->space, world, defaultNearCallback); + } + dWorldQuickStep(world->id, dt); dJointGroupEmpty(world->contactGroup); } +void lovrWorldComputeOverlaps(World* world) { + vec_clear(&world->overlaps); + dSpaceCollide(world->space, world, customNearCallback); +} + +int lovrWorldGetNextOverlap(World* world, Shape** a, Shape** b) { + if (world->overlaps.length == 0) { + *a = *b = NULL; + return 0; + } + + *a = vec_pop(&world->overlaps); + *b = vec_pop(&world->overlaps); + return 1; +} + +int lovrWorldCollide(World* world, Shape* a, Shape* b) { + if (!a || !b) { + return 0; + } + + dContact contacts[MAX_CONTACTS]; + + for (int i = 0; i < MAX_CONTACTS; i++) { + contacts[i].surface.mode = 0; + contacts[i].surface.mu = dInfinity; + } + + int contactCount = dCollide(a->id, b->id, MAX_CONTACTS, &contacts[0].geom, sizeof(dContact)); + + for (int i = 0; i < contactCount; i++) { + dJointID joint = dJointCreateContact(world->id, world->contactGroup, &contacts[i]); + dJointAttach(joint, a->body->id, b->body->id); + } + + return contactCount; +} + Body* lovrBodyCreate(World* world) { if (!world) { error("No world specified"); @@ -117,6 +145,7 @@ Body* lovrBodyCreate(World* world) { body->id = dBodyCreate(world->id); body->world = world; + dBodySetData(body->id, body); return body; } @@ -287,11 +316,11 @@ void lovrBodySetAwake(Body* body, int awake) { } void* lovrBodyGetUserData(Body* body) { - return dBodyGetData(body->id); + return body->userdata; } void lovrBodySetUserData(Body* body, void* data) { - dBodySetData(body->id, data); + body->userdata = data; } World* lovrBodyGetWorld(Body* body) { @@ -380,11 +409,11 @@ void lovrShapeSetEnabled(Shape* shape, int enabled) { } void* lovrShapeGetUserData(Shape* shape) { - return dGeomGetData(shape->id); + return shape->userdata; } void lovrShapeSetUserData(Shape* shape, void* data) { - dGeomSetData(shape->id, data); + shape->userdata = data; } void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z) { @@ -487,6 +516,7 @@ SphereShape* lovrSphereShapeCreate(float radius) { sphere->type = SHAPE_SPHERE; sphere->id = dCreateSphere(0, radius); + dGeomSetData(sphere->id, sphere); return sphere; } @@ -505,6 +535,7 @@ BoxShape* lovrBoxShapeCreate(float x, float y, float z) { box->type = SHAPE_BOX; box->id = dCreateBox(0, x, y, z); + dGeomSetData(box->id, box); return box; } @@ -527,6 +558,7 @@ CapsuleShape* lovrCapsuleShapeCreate(float radius, float length) { capsule->type = SHAPE_CAPSULE; capsule->id = dCreateCapsule(0, radius, length); + dGeomSetData(capsule->id, capsule); return capsule; } @@ -557,6 +589,7 @@ CylinderShape* lovrCylinderShapeCreate(float radius, float length) { cylinder->type = SHAPE_CYLINDER; cylinder->id = dCreateCylinder(0, radius, length); + dGeomSetData(cylinder->id, cylinder); return cylinder; } diff --git a/src/physics/physics.h b/src/physics/physics.h index 6767d150..e47ae85e 100644 --- a/src/physics/physics.h +++ b/src/physics/physics.h @@ -1,7 +1,12 @@ #include "util.h" +#include "lib/vec/vec.h" #include #include +#pragma once + +#define MAX_CONTACTS 4 + typedef enum { SHAPE_SPHERE, SHAPE_BOX, @@ -14,12 +19,14 @@ typedef struct { dWorldID id; dSpaceID space; dJointGroupID contactGroup; + vec_void_t overlaps; } World; typedef struct { Ref ref; dBodyID id; World* world; + void* userdata; } Body; typedef struct { @@ -27,6 +34,7 @@ typedef struct { ShapeType type; dGeomID id; Body* body; + void* userdata; } Shape; typedef Shape SphereShape; @@ -34,6 +42,8 @@ typedef Shape BoxShape; typedef Shape CapsuleShape; typedef Shape CylinderShape; +typedef void (*CollisionResolver)(World* world, void* userdata); + void lovrPhysicsInit(); void lovrPhysicsDestroy(); @@ -47,7 +57,10 @@ void lovrWorldGetAngularDamping(World* world, float* damping, float* threshold); void lovrWorldSetAngularDamping(World* world, float damping, float threshold); int lovrWorldIsSleepingAllowed(World* world); void lovrWorldSetSleepingAllowed(World* world, int allowed); -void lovrWorldUpdate(World* world, float dt); +void lovrWorldUpdate(World* world, float dt, CollisionResolver resolver, void* userdata); +void lovrWorldComputeOverlaps(World* world); +int lovrWorldGetNextOverlap(World* world, Shape** a, Shape** b); +int lovrWorldCollide(World* world, Shape* a, Shape* b); Body* lovrBodyCreate(); void lovrBodyDestroy(const Ref* ref);