From 34611f20697b94a6930db37239a178f5cd12ee61 Mon Sep 17 00:00:00 2001 From: Josip Miskovic Date: Fri, 12 Aug 2022 07:04:59 +0200 Subject: [PATCH] Add physics heightfield shape --- src/api/l_physics.c | 1 + src/api/l_physics_shapes.c | 1 + src/api/l_physics_world.c | 55 +++++++++++++++++++++++++++++++++++ src/modules/physics/physics.c | 17 +++++++++++ src/modules/physics/physics.h | 4 +++ 5 files changed, 78 insertions(+) diff --git a/src/api/l_physics.c b/src/api/l_physics.c index df8c45b7..a2ad275e 100644 --- a/src/api/l_physics.c +++ b/src/api/l_physics.c @@ -10,6 +10,7 @@ StringEntry lovrShapeType[] = { [SHAPE_CAPSULE] = ENTRY("capsule"), [SHAPE_CYLINDER] = ENTRY("cylinder"), [SHAPE_MESH] = ENTRY("mesh"), + [SHAPE_TERRAIN] = ENTRY("terrain"), { 0 } }; diff --git a/src/api/l_physics_shapes.c b/src/api/l_physics_shapes.c index 7c755636..ef39cdef 100644 --- a/src/api/l_physics_shapes.c +++ b/src/api/l_physics_shapes.c @@ -13,6 +13,7 @@ void luax_pushshape(lua_State* L, Shape* shape) { case SHAPE_CAPSULE: luax_pushtype(L, CapsuleShape, shape); break; case SHAPE_CYLINDER: luax_pushtype(L, CylinderShape, shape); break; case SHAPE_MESH: luax_pushtype(L, MeshShape, shape); break; + case SHAPE_TERRAIN: luax_pushtype(L, TerrainShape, shape); break; default: lovrUnreachable(); } } diff --git a/src/api/l_physics_world.c b/src/api/l_physics_world.c index 41fd829e..9e248694 100644 --- a/src/api/l_physics_world.c +++ b/src/api/l_physics_world.c @@ -1,5 +1,6 @@ #include "api.h" #include "physics/physics.h" +#include "data/image.h" #include "util.h" #include #include @@ -42,6 +43,16 @@ static void raycastCallback(Shape* shape, float x, float y, float z, float nx, f lua_call(L, 7, 0); } +static float terrainCallback(lua_State * L, int fn_index, float x, float z) { + lua_pushvalue(L, fn_index); + lua_pushnumber(L, x); + lua_pushnumber(L, z); + lua_call(L, 2, 1); + float height = luax_checkfloat(L, -1); + lua_remove(L, -1); + return height; +} + static int l_lovrWorldNewCollider(lua_State* L) { World* world = luax_checktype(L, 1, World); float position[4]; @@ -148,6 +159,49 @@ static int l_lovrWorldNewMeshCollider(lua_State* L) { return 1; } +static int l_lovrWorldNewTerrainCollider(lua_State* L) { + World* world = luax_checktype(L, 1, World); + TerrainShape* shape; + float horizontalScale = luax_checkfloat(L, 2); + int type = lua_type(L, 3); + if (type == LUA_TNIL || type == LUA_TNONE) { + float vertices[4] = {0.f}; + shape = lovrTerrainShapeCreate(vertices, 2, 2, horizontalScale, 1.f); + } else if (type == LUA_TFUNCTION) { + unsigned samples = luax_optu32(L, 4, 100); + float* vertices = malloc(sizeof(float) * samples * samples); + for (unsigned i = 0; i < samples * samples; i++) { + float x = horizontalScale * (-0.5f + ((float) (i % samples)) / samples); + float z = horizontalScale * (-0.5f + ((float) (i / samples)) / samples); + vertices[i] = terrainCallback(L, 3, x, z); + } + shape = lovrTerrainShapeCreate(vertices, samples, samples, horizontalScale, 1.f); + free(vertices); + } else if (type == LUA_TUSERDATA) { + Image* image = luax_checktype(L, 3, Image); + uint32_t imageWidth = lovrImageGetWidth(image, 0); + uint32_t imageHeight = lovrImageGetHeight(image, 0); + float verticalScale = luax_optfloat(L, 4, 1.f); + float* vertices = malloc(sizeof(float) * imageWidth * imageHeight); + for (int y = 0; y < imageHeight; y++) { + for (int x = 0; x < imageWidth; x++) { + float pixel[4]; + lovrImageGetPixel(image, x, y, pixel); + vertices[x + y * imageWidth] = pixel[0]; + } + } + shape = lovrTerrainShapeCreate(vertices, imageWidth, imageHeight, horizontalScale, verticalScale); + free(vertices); + } + Collider* collider = lovrColliderCreate(world, 0, 0, 0); + lovrColliderAddShape(collider, shape); + lovrColliderSetKinematic(collider, true); + luax_pushtype(L, Collider, collider); + lovrRelease(collider, lovrColliderDestroy); + lovrRelease(shape, lovrShapeDestroy); + return 1; +} + static int l_lovrWorldGetColliders(lua_State* L) { World* world = luax_checktype(L, 1, World); @@ -388,6 +442,7 @@ const luaL_Reg lovrWorld[] = { { "newCylinderCollider", l_lovrWorldNewCylinderCollider }, { "newSphereCollider", l_lovrWorldNewSphereCollider }, { "newMeshCollider", l_lovrWorldNewMeshCollider }, + { "newTerrainCollider", l_lovrWorldNewTerrainCollider }, { "getColliders", l_lovrWorldGetColliders }, { "destroy", l_lovrWorldDestroy }, { "update", l_lovrWorldUpdate }, diff --git a/src/modules/physics/physics.c b/src/modules/physics/physics.c index 8ca0f09b..7071165a 100644 --- a/src/modules/physics/physics.c +++ b/src/modules/physics/physics.c @@ -798,6 +798,9 @@ void lovrShapeDestroyData(Shape* shape) { dGeomTriMeshDataDestroy(dataID); free(shape->vertices); free(shape->indices); + } else if (shape->type == SHAPE_TERRAIN) { + dHeightfieldDataID dataID = dGeomHeightfieldGetHeightfieldData(shape->id); + dGeomHeightfieldDataDestroy(dataID); } dGeomDestroy(shape->id); shape->id = NULL; @@ -1049,6 +1052,20 @@ MeshShape* lovrMeshShapeCreate(int vertexCount, float* vertices, int indexCount, return mesh; } +TerrainShape* lovrTerrainShapeCreate(float* vertices, int widthSamples, int depthSamples, float horizontalScale, float verticalScale) { + const float thickness = 10.f; + TerrainShape* terrain = calloc(1, sizeof(TerrainShape)); + lovrAssert(terrain, "Out of memory"); + terrain->ref = 1; + dHeightfieldDataID dataID = dGeomHeightfieldDataCreate(); + dGeomHeightfieldDataBuildSingle(dataID, vertices, 1, horizontalScale, horizontalScale, + widthSamples, depthSamples, verticalScale, 0.f, thickness, 0); + terrain->id = dCreateHeightfield(0, dataID, 1); + terrain->type = SHAPE_TERRAIN; + dGeomSetData(terrain->id, terrain); + return terrain; +} + void lovrJointDestroy(void* ref) { Joint* joint = ref; lovrJointDestroyData(joint); diff --git a/src/modules/physics/physics.h b/src/modules/physics/physics.h index 4e9d8531..dcc0a5d8 100644 --- a/src/modules/physics/physics.h +++ b/src/modules/physics/physics.h @@ -18,6 +18,7 @@ typedef Shape BoxShape; typedef Shape CapsuleShape; typedef Shape CylinderShape; typedef Shape MeshShape; +typedef Shape TerrainShape; typedef Joint BallJoint; typedef Joint DistanceJoint; @@ -131,6 +132,7 @@ typedef enum { SHAPE_CAPSULE, SHAPE_CYLINDER, SHAPE_MESH, + SHAPE_TERRAIN, } ShapeType; void lovrShapeDestroy(void* ref); @@ -171,6 +173,7 @@ float lovrCylinderShapeGetLength(CylinderShape* cylinder); void lovrCylinderShapeSetLength(CylinderShape* cylinder, float length); MeshShape* lovrMeshShapeCreate(int vertexCount, float vertices[], int indexCount, uint32_t indices[]); +TerrainShape* lovrTerrainShapeCreate(float* vertices, int widthSamples, int depthSamples, float horizontalScale, float verticalScale); // These tokens need to exist for Lua bindings #define lovrSphereShapeDestroy lovrShapeDestroy @@ -178,6 +181,7 @@ MeshShape* lovrMeshShapeCreate(int vertexCount, float vertices[], int indexCount #define lovrCapsuleShapeDestroy lovrShapeDestroy #define lovrCylinderShapeDestroy lovrShapeDestroy #define lovrMeshShapeDestroy lovrShapeDestroy +#define lovrTerrainShapeDestroy lovrShapeDestroy // Joints