diff --git a/src/api/physics.c b/src/api/physics.c index 6fb22dd5..416f7615 100644 --- a/src/api/physics.c +++ b/src/api/physics.c @@ -33,7 +33,27 @@ int l_lovrPhysicsInit(lua_State* L) { } int l_lovrPhysicsNewWorld(lua_State* L) { - World* world = lovrWorldCreate(); + float xg = luaL_optnumber(L, 1, 0.f); + float yg = luaL_optnumber(L, 2, -9.81); + float zg = luaL_optnumber(L, 3, 0.f); + int allowSleep = lua_gettop(L) < 4 || lua_toboolean(L, 4); + const char* tags[16]; + int tagCount; + if (lua_type(L, 5) == LUA_TTABLE) { + tagCount = lua_objlen(L, 5); + for (int i = 0; i < tagCount; i++) { + lua_rawgeti(L, -1, i + 1); + if (lua_isstring(L, -1)) { + tags[i] = lua_tostring(L, -1); + } else { + return luaL_error(L, "World tags must be a table of strings"); + } + lua_pop(L, 1); + } + } else { + tagCount = 0; + } + World* world = lovrWorldCreate(xg, yg, zg, allowSleep, tags, tagCount); luax_pushtype(L, World, world); return 1; } diff --git a/src/api/types/collider.c b/src/api/types/collider.c index cda56831..10b302c3 100644 --- a/src/api/types/collider.c +++ b/src/api/types/collider.c @@ -449,6 +449,27 @@ int l_lovrColliderSetRestitution(lua_State* L) { return 0; } +int l_lovrColliderGetTag(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + lua_pushstring(L, lovrColliderGetTag(collider)); + return 1; +} + +int l_lovrColliderSetTag(lua_State* L) { + Collider* collider = luax_checktype(L, 1, Collider); + if (lua_isnoneornil(L, 2)) { + lovrColliderSetTag(collider, NULL); + return 0; + } + + const char* tag = luaL_checkstring(L, 2); + if (lovrColliderSetTag(collider, tag)) { + return luaL_error(L, "Invalid tag %s", tag); + } + + return 0; +} + const luaL_Reg lovrCollider[] = { { "destroy", l_lovrColliderDestroy }, { "getWorld", l_lovrColliderGetWorld }, @@ -495,5 +516,7 @@ const luaL_Reg lovrCollider[] = { { "setFriction", l_lovrColliderSetFriction }, { "getRestitution", l_lovrColliderGetRestitution }, { "setRestitution", l_lovrColliderSetRestitution }, + { "getTag", l_lovrColliderGetTag }, + { "setTag", l_lovrColliderSetTag }, { NULL, NULL } }; diff --git a/src/api/types/world.c b/src/api/types/world.c index af7f6ddf..86f4ad44 100644 --- a/src/api/types/world.c +++ b/src/api/types/world.c @@ -200,6 +200,30 @@ int l_lovrWorldRaycast(lua_State* L) { return 0; } +int l_lovrWorldDisableCollisionBetween(lua_State* L) { + World* world = luax_checktype(L, 1, World); + const char* tag1 = luaL_checkstring(L, 2); + const char* tag2 = luaL_checkstring(L, 3); + lovrWorldDisableCollisionBetween(world, tag1, tag2); + return 0; +} + +int l_lovrWorldEnableCollisionBetween(lua_State* L) { + World* world = luax_checktype(L, 1, World); + const char* tag1 = luaL_checkstring(L, 2); + const char* tag2 = luaL_checkstring(L, 3); + lovrWorldEnableCollisionBetween(world, tag1, tag2); + return 0; +} + +int l_lovrWorldIsCollisionEnabledBetween(lua_State* L) { + World* world = luax_checktype(L, 1, World); + const char* tag1 = luaL_checkstring(L, 2); + const char* tag2 = luaL_checkstring(L, 3); + lua_pushboolean(L, lovrWorldIsCollisionEnabledBetween(world, tag1, tag2)); + return 1; +} + const luaL_Reg lovrWorld[] = { { "newCollider", l_lovrWorldNewCollider }, { "newBoxCollider", l_lovrWorldNewBoxCollider }, @@ -220,5 +244,8 @@ const luaL_Reg lovrWorld[] = { { "isSleepingAllowed", l_lovrWorldIsSleepingAllowed }, { "setSleepingAllowed", l_lovrWorldSetSleepingAllowed }, { "raycast", l_lovrWorldRaycast }, + { "disableCollisionBetween", l_lovrWorldDisableCollisionBetween }, + { "enableCollisionBetween", l_lovrWorldEnableCollisionBetween }, + { "isCollisionEnabledBetween", l_lovrWorldIsCollisionEnabledBetween }, { NULL, NULL } }; diff --git a/src/physics/physics.c b/src/physics/physics.c index d701a793..8c1930e6 100644 --- a/src/physics/physics.c +++ b/src/physics/physics.c @@ -42,7 +42,7 @@ void lovrPhysicsDestroy() { dCloseODE(); } -World* lovrWorldCreate() { +World* lovrWorldCreate(float xg, float yg, float zg, int allowSleep, const char** tags, int tagCount) { World* world = lovrAlloc(sizeof(World), lovrWorldDestroy); if (!world) return NULL; @@ -51,6 +51,16 @@ World* lovrWorldCreate() { dHashSpaceSetLevels(world->space, -4, 8); world->contactGroup = dJointGroupCreate(0); vec_init(&world->overlaps); + lovrWorldSetGravity(world, xg, yg, zg); + lovrWorldSetSleepingAllowed(world, allowSleep); + map_init(&world->tags); + for (int i = 0; i < tagCount; i++) { + map_set(&world->tags, tags[i], i); + } + + for (int i = 0; i < MAX_TAGS; i++) { + world->masks[i] = ~0; + } return world; } @@ -111,10 +121,14 @@ int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float res return 0; } - dContact contacts[MAX_CONTACTS]; - Collider* colliderA = a->collider; Collider* colliderB = b->collider; + int tag1 = colliderA->tag; + int tag2 = colliderB->tag; + + if (tag1 != NO_TAG && tag2 != NO_TAG && !((world->masks[tag1] & (1 << tag2)) && (world->masks[tag2] & (1 << tag1)))) { + return 0; + } if (friction < 0) { friction = sqrt(colliderA->friction * colliderB->friction); @@ -124,6 +138,7 @@ int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float res restitution = MAX(colliderA->restitution, colliderB->restitution); } + dContact contacts[MAX_CONTACTS]; for (int i = 0; i < MAX_CONTACTS; i++) { contacts[i].surface.mode = 0; contacts[i].surface.mu = friction; @@ -197,6 +212,56 @@ void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, floa dGeomDestroy(ray); } +const char* lovrWorldGetTagName(World* world, int tag) { + if (tag == NO_TAG) { + return NULL; + } + + const char* key; + map_iter_t iter = map_iter(&world->tags); + while ((key = map_next(&world->tags, &iter))) { + if (*map_get(&world->tags, key) == tag) { + return key; + } + } + + return NULL; +} + +int lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2) { + int* index1 = map_get(&world->tags, tag1); + int* index2 = map_get(&world->tags, tag2); + if (!index1 || !index2) { + return NO_TAG; + } + + world->masks[*index1] &= ~(1 << *index2); + world->masks[*index2] &= ~(1 << *index1); + return 0; +} + +int lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2) { + int* index1 = map_get(&world->tags, tag1); + int* index2 = map_get(&world->tags, tag2); + if (!index1 || !index2) { + return NO_TAG; + } + + world->masks[*index1] |= (1 << *index2); + world->masks[*index2] |= (1 << *index1); + return 0; +} + +int lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag2) { + int* index1 = map_get(&world->tags, tag1); + int* index2 = map_get(&world->tags, tag2); + if (!index1 || !index2) { + return NO_TAG; + } + + return (world->masks[*index1] & (1 << *index2)) && (world->masks[*index2] & (1 << *index1)); +} + Collider* lovrColliderCreate(World* world) { if (!world) { error("No world specified"); @@ -209,6 +274,7 @@ Collider* lovrColliderCreate(World* world) { collider->world = world; collider->friction = 0; collider->restitution = 0; + collider->tag = NO_TAG; dBodySetData(collider->body, collider); vec_init(&collider->shapes); vec_init(&collider->joints); @@ -539,6 +605,26 @@ void lovrColliderGetAABB(Collider* collider, float aabb[6]) { } } +const char* lovrColliderGetTag(Collider* collider) { + return lovrWorldGetTagName(collider->world, collider->tag); +} + +int lovrColliderSetTag(Collider* collider, const char* tag) { + if (tag == NULL) { + collider->tag = NO_TAG; + return 0; + } + + int* index = map_get(&collider->world->tags, tag); + + if (!index) { + return NO_TAG; + } + + collider->tag = *index; + return 0; +} + void lovrShapeDestroy(const Ref* ref) { Shape* shape = containerof(ref, Shape); lovrShapeDestroyData(shape); diff --git a/src/physics/physics.h b/src/physics/physics.h index a76a59f1..ef929d26 100644 --- a/src/physics/physics.h +++ b/src/physics/physics.h @@ -1,11 +1,14 @@ #include "util.h" #include "lib/vec/vec.h" +#include "lib/map/map.h" #include #include #pragma once #define MAX_CONTACTS 4 +#define MAX_TAGS 16 +#define NO_TAG ~0 typedef enum { SHAPE_SPHERE, @@ -26,6 +29,8 @@ typedef struct { dSpaceID space; dJointGroupID contactGroup; vec_void_t overlaps; + map_int_t tags; + uint16_t masks[MAX_TAGS]; } World; typedef struct { @@ -33,6 +38,7 @@ typedef struct { dBodyID body; World* world; void* userdata; + int tag; vec_void_t shapes; vec_void_t joints; float friction; @@ -74,7 +80,7 @@ typedef struct { void lovrPhysicsInit(); void lovrPhysicsDestroy(); -World* lovrWorldCreate(); +World* lovrWorldCreate(float xg, float yg, float zg, int allowSleep, const char** tags, int tagCount); void lovrWorldDestroy(const Ref* ref); void lovrWorldDestroyData(World* world); void lovrWorldUpdate(World* world, float dt, CollisionResolver resolver, void* userdata); @@ -90,6 +96,10 @@ void lovrWorldSetAngularDamping(World* world, float damping, float threshold); int lovrWorldIsSleepingAllowed(World* world); void lovrWorldSetSleepingAllowed(World* world, int allowed); void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, float y2, float z2, RaycastCallback callback, void* userdata); +const char* lovrWorldGetTagName(World* world, int tag); +int lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2); +int lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2); +int lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag); Collider* lovrColliderCreate(); void lovrColliderDestroy(const Ref* ref); @@ -140,6 +150,8 @@ float lovrColliderGetFriction(Collider* collider); void lovrColliderSetFriction(Collider* collider, float friction); float lovrColliderGetRestitution(Collider* collider); void lovrColliderSetRestitution(Collider* collider, float restitution); +const char* lovrColliderGetTag(Collider* collider); +int lovrColliderSetTag(Collider* collider, const char* tag); void lovrShapeDestroy(const Ref* ref); void lovrShapeDestroyData(Shape* shape);