Add physics heightfield shape

This commit is contained in:
Josip Miskovic 2022-08-12 07:04:59 +02:00 committed by Bjorn
parent 36e1471cf0
commit 34611f2069
5 changed files with 78 additions and 0 deletions

View File

@ -10,6 +10,7 @@ StringEntry lovrShapeType[] = {
[SHAPE_CAPSULE] = ENTRY("capsule"),
[SHAPE_CYLINDER] = ENTRY("cylinder"),
[SHAPE_MESH] = ENTRY("mesh"),
[SHAPE_TERRAIN] = ENTRY("terrain"),
{ 0 }
};

View File

@ -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();
}
}

View File

@ -1,5 +1,6 @@
#include "api.h"
#include "physics/physics.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
@ -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 },

View File

@ -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);

View File

@ -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