diff --git a/src/api/l_physics_world.c b/src/api/l_physics_world.c index 2270520e..71869069 100644 --- a/src/api/l_physics_world.c +++ b/src/api/l_physics_world.c @@ -27,7 +27,6 @@ static int nextOverlap(lua_State* L) { static void raycastCallback(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata) { lua_State* L = userdata; - luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, -1); luax_pushshape(L, shape); lua_pushnumber(L, x); @@ -39,6 +38,13 @@ static void raycastCallback(Shape* shape, float x, float y, float z, float nx, f lua_call(L, 7, 0); } +static void queryCallback(Shape* shape, void* userdata) { + lua_State* L = userdata; + lua_pushvalue(L, -1); + luax_pushshape(L, shape); + lua_call(L, 1, 0); +} + static int l_lovrWorldNewCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[3]; @@ -242,6 +248,29 @@ static int l_lovrWorldRaycast(lua_State* L) { return 0; } +static int l_lovrWorldQueryBox(lua_State* L) { + World* world = luax_checktype(L, 1, World); + float position[3], size[3]; + int index = 2; + index = luax_readvec3(L, index, position, NULL); + index = luax_readvec3(L, index, size, NULL); + luaL_checktype(L, index, LUA_TFUNCTION); + lua_settop(L, index); + lovrWorldQueryBox(world, position, size, queryCallback, L); + return 0; +} + +static int l_lovrWorldQuerySphere(lua_State* L) { + World* world = luax_checktype(L, 1, World); + float position[3]; + int index = luax_readvec3(L, 2, position, NULL); + float radius = luax_checkfloat(L, index++); + luaL_checktype(L, index, LUA_TFUNCTION); + lua_settop(L, index); + lovrWorldQuerySphere(world, position, radius, queryCallback, L); + return 0; +} + static int l_lovrWorldGetGravity(lua_State* L) { World* world = luax_checktype(L, 1, World); float x, y, z; @@ -392,6 +421,8 @@ const luaL_Reg lovrWorld[] = { { "collide", l_lovrWorldCollide }, { "getContacts", l_lovrWorldGetContacts }, { "raycast", l_lovrWorldRaycast }, + { "queryBox", l_lovrWorldQueryBox }, + { "querySphere", l_lovrWorldQuerySphere }, { "getGravity", l_lovrWorldGetGravity }, { "setGravity", l_lovrWorldSetGravity }, { "getTightness", l_lovrWorldGetTightness }, diff --git a/src/modules/physics/physics.c b/src/modules/physics/physics.c index 20d6554d..5e87ce6f 100644 --- a/src/modules/physics/physics.c +++ b/src/modules/physics/physics.c @@ -79,6 +79,26 @@ static void raycastCallback(void* data, dGeomID a, dGeomID b) { } } +typedef struct { + QueryCallback callback; + void* userdata; +} QueryData; + +static void queryCallback(void* data, dGeomID a, dGeomID b) { + QueryCallback callback = ((QueryData*) data)->callback; + void* userdata = ((QueryData*) data)->userdata; + Shape* shape = dGeomGetData(b); + + if (!shape) { + return; + } + + dContactGeom contact; + if (dCollide(a, b, 1 | CONTACTS_UNIMPORTANT, &contact, sizeof(contact))) { + callback(shape, userdata); + } +} + // XXX slow, but probably fine (tag names are not on any critical path), could switch to hashing if needed static uint32_t findTag(World* world, const char* name) { for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) { @@ -288,6 +308,22 @@ void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, floa dGeomDestroy(ray); } +void lovrWorldQueryBox(World* world, float position[3], float size[3], QueryCallback callback, void* userdata) { + QueryData data = { .callback = callback, .userdata = userdata }; + dGeomID box = dCreateBox(world->space, fabsf(size[0]), fabsf(size[1]), fabsf(size[2])); + dGeomSetPosition(box, position[0], position[1], position[2]); + dSpaceCollide2(box, (dGeomID) world->space, &data, queryCallback); + dGeomDestroy(box); +} + +void lovrWorldQuerySphere(World* world, float position[3], float radius, QueryCallback callback, void* userdata) { + QueryData data = { .callback = callback, .userdata = userdata }; + dGeomID sphere = dCreateSphere(world->space, fabsf(radius)); + dGeomSetPosition(sphere, position[0], position[1], position[2]); + dSpaceCollide2(sphere, (dGeomID) world->space, &data, queryCallback); + dGeomDestroy(sphere); +} + Collider* lovrWorldGetFirstCollider(World* world) { return world->head; } diff --git a/src/modules/physics/physics.h b/src/modules/physics/physics.h index d724170a..39ea296b 100644 --- a/src/modules/physics/physics.h +++ b/src/modules/physics/physics.h @@ -27,6 +27,7 @@ typedef Joint SliderJoint; typedef void (*CollisionResolver)(World* world, void* userdata); typedef void (*RaycastCallback)(Shape* shape, float x, float y, float z, float nx, float ny, float nz, void* userdata); +typedef void (*QueryCallback)(Shape* shape, void* userdata); bool lovrPhysicsInit(void); void lovrPhysicsDestroy(void); @@ -50,6 +51,8 @@ int lovrWorldGetNextOverlap(World* world, Shape** a, Shape** b); int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float restitution); void lovrWorldGetContacts(World* world, Shape* a, Shape* b, Contact contacts[MAX_CONTACTS], uint32_t* count); void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, float y2, float z2, RaycastCallback callback, void* userdata); +void lovrWorldQueryBox(World* world, float position[3], float size[3], QueryCallback callback, void* userdata); +void lovrWorldQuerySphere(World* world, float position[3], float radius, QueryCallback callback, void* userdata); Collider* lovrWorldGetFirstCollider(World* world); void lovrWorldGetGravity(World* world, float* x, float* y, float* z); void lovrWorldSetGravity(World* world, float x, float y, float z);