Custom collision handling;

This commit is contained in:
bjorn 2017-05-19 15:04:34 -06:00
parent e6b8d0adea
commit 661e9188c7
5 changed files with 136 additions and 32 deletions

View File

@ -2,6 +2,7 @@
#include "filesystem/blob.h" #include "filesystem/blob.h"
#include "graphics/mesh.h" #include "graphics/mesh.h"
#include "math/math.h" #include "math/math.h"
#include "physics/physics.h"
#include "lib/map/map.h" #include "lib/map/map.h"
int l_lovrAudioInit(lua_State* L); 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); void luax_checkmeshformat(lua_State* L, int index, MeshFormat* format);
int luax_readtransform(lua_State* L, int index, mat4 transform); int luax_readtransform(lua_State* L, int index, mat4 transform);
Blob* luax_readblob(lua_State* L, int index, const char* debug); Blob* luax_readblob(lua_State* L, int index, const char* debug);
int luax_pushshape(lua_State* L, Shape* shape);

View File

@ -1,6 +1,15 @@
#include "api/lovr.h" #include "api/lovr.h"
#include "physics/physics.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) { int l_lovrShapeGetType(lua_State* L) {
Shape* shape = luax_checktypeof(L, 1, Shape); Shape* shape = luax_checktypeof(L, 1, Shape);
luax_pushenum(L, &ShapeTypes, lovrShapeGetType(shape)); luax_pushenum(L, &ShapeTypes, lovrShapeGetType(shape));

View File

@ -1,6 +1,27 @@
#include "api/lovr.h" #include "api/lovr.h"
#include "physics/physics.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) { int l_lovrWorldGetGravity(lua_State* L) {
World* world = luax_checktype(L, 1, World); World* world = luax_checktype(L, 1, World);
float x, y, z; float x, y, z;
@ -68,12 +89,35 @@ int l_lovrWorldSetSleepingAllowed(lua_State* L) {
} }
int l_lovrWorldUpdate(lua_State* L) { int l_lovrWorldUpdate(lua_State* L) {
lua_settop(L, 3);
World* world = luax_checktype(L, 1, World); World* world = luax_checktype(L, 1, World);
float dt = luaL_checknumber(L, 2); 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; 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[] = { const luaL_Reg lovrWorld[] = {
{ "getGravity", l_lovrWorldGetGravity }, { "getGravity", l_lovrWorldGetGravity },
{ "setGravity", l_lovrWorldSetGravity }, { "setGravity", l_lovrWorldSetGravity },
@ -84,5 +128,8 @@ const luaL_Reg lovrWorld[] = {
{ "isSleepingAllowed", l_lovrWorldIsSleepingAllowed }, { "isSleepingAllowed", l_lovrWorldIsSleepingAllowed },
{ "setSleepingAllowed", l_lovrWorldSetSleepingAllowed }, { "setSleepingAllowed", l_lovrWorldSetSleepingAllowed },
{ "update", l_lovrWorldUpdate }, { "update", l_lovrWorldUpdate },
{ "computeOverlaps", l_lovrWorldComputeOverlaps },
{ "overlaps", l_lovrWorldOverlaps },
{ "collide", l_lovrWorldCollide },
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -2,31 +2,14 @@
#include "math/quat.h" #include "math/quat.h"
#include <stdlib.h> #include <stdlib.h>
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; World* world = data;
vec_push(&world->overlaps, dGeomGetData(shapeA));
dBodyID bodyA = dGeomGetBody(shapeA); vec_push(&world->overlaps, dGeomGetData(shapeB));
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);
}
} }
void lovrPhysicsInit() { void lovrPhysicsInit() {
@ -51,6 +34,7 @@ World* lovrWorldCreate() {
world->space = dHashSpaceCreate(0); world->space = dHashSpaceCreate(0);
dHashSpaceSetLevels(world->space, -4, 8); dHashSpaceSetLevels(world->space, -4, 8);
world->contactGroup = dJointGroupCreate(0); world->contactGroup = dJointGroupCreate(0);
vec_init(&world->overlaps);
return world; return world;
} }
@ -58,6 +42,7 @@ World* lovrWorldCreate() {
void lovrWorldDestroy(const Ref* ref) { void lovrWorldDestroy(const Ref* ref) {
World* world = containerof(ref, World); World* world = containerof(ref, World);
dWorldDestroy(world->id); dWorldDestroy(world->id);
vec_deinit(&world->overlaps);
free(world); free(world);
} }
@ -101,12 +86,55 @@ void lovrWorldSetSleepingAllowed(World* world, int allowed) {
dWorldSetAutoDisableFlag(world->id, allowed); dWorldSetAutoDisableFlag(world->id, allowed);
} }
void lovrWorldUpdate(World* world, float dt) { void lovrWorldUpdate(World* world, float dt, CollisionResolver resolver, void* userdata) {
dSpaceCollide(world->space, world, nearCallback); if (resolver) {
resolver(world, userdata);
} else {
dSpaceCollide(world->space, world, defaultNearCallback);
}
dWorldQuickStep(world->id, dt); dWorldQuickStep(world->id, dt);
dJointGroupEmpty(world->contactGroup); 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) { Body* lovrBodyCreate(World* world) {
if (!world) { if (!world) {
error("No world specified"); error("No world specified");
@ -117,6 +145,7 @@ Body* lovrBodyCreate(World* world) {
body->id = dBodyCreate(world->id); body->id = dBodyCreate(world->id);
body->world = world; body->world = world;
dBodySetData(body->id, body);
return body; return body;
} }
@ -287,11 +316,11 @@ void lovrBodySetAwake(Body* body, int awake) {
} }
void* lovrBodyGetUserData(Body* body) { void* lovrBodyGetUserData(Body* body) {
return dBodyGetData(body->id); return body->userdata;
} }
void lovrBodySetUserData(Body* body, void* data) { void lovrBodySetUserData(Body* body, void* data) {
dBodySetData(body->id, data); body->userdata = data;
} }
World* lovrBodyGetWorld(Body* body) { World* lovrBodyGetWorld(Body* body) {
@ -380,11 +409,11 @@ void lovrShapeSetEnabled(Shape* shape, int enabled) {
} }
void* lovrShapeGetUserData(Shape* shape) { void* lovrShapeGetUserData(Shape* shape) {
return dGeomGetData(shape->id); return shape->userdata;
} }
void lovrShapeSetUserData(Shape* shape, void* data) { void lovrShapeSetUserData(Shape* shape, void* data) {
dGeomSetData(shape->id, data); shape->userdata = data;
} }
void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z) { void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z) {
@ -487,6 +516,7 @@ SphereShape* lovrSphereShapeCreate(float radius) {
sphere->type = SHAPE_SPHERE; sphere->type = SHAPE_SPHERE;
sphere->id = dCreateSphere(0, radius); sphere->id = dCreateSphere(0, radius);
dGeomSetData(sphere->id, sphere);
return sphere; return sphere;
} }
@ -505,6 +535,7 @@ BoxShape* lovrBoxShapeCreate(float x, float y, float z) {
box->type = SHAPE_BOX; box->type = SHAPE_BOX;
box->id = dCreateBox(0, x, y, z); box->id = dCreateBox(0, x, y, z);
dGeomSetData(box->id, box);
return box; return box;
} }
@ -527,6 +558,7 @@ CapsuleShape* lovrCapsuleShapeCreate(float radius, float length) {
capsule->type = SHAPE_CAPSULE; capsule->type = SHAPE_CAPSULE;
capsule->id = dCreateCapsule(0, radius, length); capsule->id = dCreateCapsule(0, radius, length);
dGeomSetData(capsule->id, capsule);
return capsule; return capsule;
} }
@ -557,6 +589,7 @@ CylinderShape* lovrCylinderShapeCreate(float radius, float length) {
cylinder->type = SHAPE_CYLINDER; cylinder->type = SHAPE_CYLINDER;
cylinder->id = dCreateCylinder(0, radius, length); cylinder->id = dCreateCylinder(0, radius, length);
dGeomSetData(cylinder->id, cylinder);
return cylinder; return cylinder;
} }

View File

@ -1,7 +1,12 @@
#include "util.h" #include "util.h"
#include "lib/vec/vec.h"
#include <stdint.h> #include <stdint.h>
#include <ode/ode.h> #include <ode/ode.h>
#pragma once
#define MAX_CONTACTS 4
typedef enum { typedef enum {
SHAPE_SPHERE, SHAPE_SPHERE,
SHAPE_BOX, SHAPE_BOX,
@ -14,12 +19,14 @@ typedef struct {
dWorldID id; dWorldID id;
dSpaceID space; dSpaceID space;
dJointGroupID contactGroup; dJointGroupID contactGroup;
vec_void_t overlaps;
} World; } World;
typedef struct { typedef struct {
Ref ref; Ref ref;
dBodyID id; dBodyID id;
World* world; World* world;
void* userdata;
} Body; } Body;
typedef struct { typedef struct {
@ -27,6 +34,7 @@ typedef struct {
ShapeType type; ShapeType type;
dGeomID id; dGeomID id;
Body* body; Body* body;
void* userdata;
} Shape; } Shape;
typedef Shape SphereShape; typedef Shape SphereShape;
@ -34,6 +42,8 @@ typedef Shape BoxShape;
typedef Shape CapsuleShape; typedef Shape CapsuleShape;
typedef Shape CylinderShape; typedef Shape CylinderShape;
typedef void (*CollisionResolver)(World* world, void* userdata);
void lovrPhysicsInit(); void lovrPhysicsInit();
void lovrPhysicsDestroy(); void lovrPhysicsDestroy();
@ -47,7 +57,10 @@ void lovrWorldGetAngularDamping(World* world, float* damping, float* threshold);
void lovrWorldSetAngularDamping(World* world, float damping, float threshold); void lovrWorldSetAngularDamping(World* world, float damping, float threshold);
int lovrWorldIsSleepingAllowed(World* world); int lovrWorldIsSleepingAllowed(World* world);
void lovrWorldSetSleepingAllowed(World* world, int allowed); 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(); Body* lovrBodyCreate();
void lovrBodyDestroy(const Ref* ref); void lovrBodyDestroy(const Ref* ref);