Minor Shape improvements;

- Add helper functions for creating shapes to avoid duplication between
  newShape and newShapeCollider.
- Add lovr.physics.newMeshShape and lovr.physics.newTerrainShape
- Register TerrainShape so it has all the base Shape methods
- Smooth out a few TerrainShape warnings
This commit is contained in:
bjorn 2022-11-09 20:53:42 -08:00
parent 9783140725
commit f8f748320e
5 changed files with 144 additions and 93 deletions

View File

@ -182,4 +182,10 @@ void luax_pushjoint(struct lua_State* L, struct Joint* joint);
void luax_pushshape(struct lua_State* L, struct Shape* shape);
struct Joint* luax_checkjoint(struct lua_State* L, int index);
struct Shape* luax_checkshape(struct lua_State* L, int index);
struct Shape* luax_newsphereshape(struct lua_State* L, int index);
struct Shape* luax_newboxshape(struct lua_State* L, int index);
struct Shape* luax_newcapsuleshape(struct lua_State* L, int index);
struct Shape* luax_newcylindershape(struct lua_State* L, int index);
struct Shape* luax_newmeshshape(struct lua_State* L, int index);
struct Shape* luax_newterrainshape(struct lua_State* L, int index);
#endif

View File

@ -61,27 +61,21 @@ static int l_lovrPhysicsNewBallJoint(lua_State* L) {
}
static int l_lovrPhysicsNewBoxShape(lua_State* L) {
float size[4];
luax_readscale(L, 1, size, 3, NULL);
BoxShape* box = lovrBoxShapeCreate(size[0], size[1], size[2]);
BoxShape* box = luax_newboxshape(L, 1);
luax_pushtype(L, BoxShape, box);
lovrRelease(box, lovrShapeDestroy);
return 1;
}
static int l_lovrPhysicsNewCapsuleShape(lua_State* L) {
float radius = luax_optfloat(L, 1, 1.f);
float length = luax_optfloat(L, 2, 1.f);
CapsuleShape* capsule = lovrCapsuleShapeCreate(radius, length);
CapsuleShape* capsule = luax_newcapsuleshape(L, 1);
luax_pushtype(L, CapsuleShape, capsule);
lovrRelease(capsule, lovrShapeDestroy);
return 1;
}
static int l_lovrPhysicsNewCylinderShape(lua_State* L) {
float radius = luax_optfloat(L, 1, 1.f);
float length = luax_optfloat(L, 2, 1.f);
CylinderShape* cylinder = lovrCylinderShapeCreate(radius, length);
CylinderShape* cylinder = luax_newcylindershape(L, 1);
luax_pushtype(L, CylinderShape, cylinder);
lovrRelease(cylinder, lovrShapeDestroy);
return 1;
@ -112,6 +106,13 @@ static int l_lovrPhysicsNewHingeJoint(lua_State* L) {
return 1;
}
static int l_lovrPhysicsNewMeshShape(lua_State* L) {
MeshShape* mesh = luax_newmeshshape(L, 1);
luax_pushtype(L, MeshShape, mesh);
lovrRelease(mesh, lovrShapeDestroy);
return 1;
}
static int l_lovrPhysicsNewSliderJoint(lua_State* L) {
Collider* a = luax_checktype(L, 1, Collider);
Collider* b = luax_checktype(L, 2, Collider);
@ -124,13 +125,19 @@ static int l_lovrPhysicsNewSliderJoint(lua_State* L) {
}
static int l_lovrPhysicsNewSphereShape(lua_State* L) {
float radius = luax_optfloat(L, 1, 1.f);
SphereShape* sphere = lovrSphereShapeCreate(radius);
SphereShape* sphere = luax_newsphereshape(L, 1);
luax_pushtype(L, SphereShape, sphere);
lovrRelease(sphere, lovrShapeDestroy);
return 1;
}
static int l_lovrPhysicsNewTerrainShape(lua_State* L) {
TerrainShape* terrain = luax_newterrainshape(L, 1);
luax_pushtype(L, TerrainShape, terrain);
lovrRelease(terrain, lovrShapeDestroy);
return 1;
}
static const luaL_Reg lovrPhysics[] = {
{ "newWorld", l_lovrPhysicsNewWorld },
{ "newBallJoint", l_lovrPhysicsNewBallJoint },
@ -139,8 +146,10 @@ static const luaL_Reg lovrPhysics[] = {
{ "newCylinderShape", l_lovrPhysicsNewCylinderShape },
{ "newDistanceJoint", l_lovrPhysicsNewDistanceJoint },
{ "newHingeJoint", l_lovrPhysicsNewHingeJoint },
{ "newMeshShape", l_lovrPhysicsNewMeshShape },
{ "newSliderJoint", l_lovrPhysicsNewSliderJoint },
{ "newSphereShape", l_lovrPhysicsNewSphereShape },
{ "newTerrainShape", l_lovrPhysicsNewTerrainShape },
{ NULL, NULL }
};
@ -155,6 +164,7 @@ extern const luaL_Reg lovrBoxShape[];
extern const luaL_Reg lovrCapsuleShape[];
extern const luaL_Reg lovrCylinderShape[];
extern const luaL_Reg lovrMeshShape[];
extern const luaL_Reg lovrTerrainShape[];
int luaopen_lovr_physics(lua_State* L) {
lua_newtable(L);
@ -170,6 +180,7 @@ int luaopen_lovr_physics(lua_State* L) {
luax_registertype(L, CapsuleShape);
luax_registertype(L, CylinderShape);
luax_registertype(L, MeshShape);
luax_registertype(L, TerrainShape);
if (lovrPhysicsInit()) {
luax_atexit(L, lovrPhysicsDestroy);
}

View File

@ -1,9 +1,11 @@
#include "api.h"
#include "physics/physics.h"
#include "data/image.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <string.h>
void luax_pushshape(lua_State* L, Shape* shape) {
@ -28,6 +30,7 @@ Shape* luax_checkshape(lua_State* L, int index) {
hash64("CapsuleShape", strlen("CapsuleShape")),
hash64("CylinderShape", strlen("CylinderShape")),
hash64("MeshShape", strlen("MeshShape")),
hash64("TerrainShape", strlen("TerrainShape"))
};
for (size_t i = 0; i < COUNTOF(hashes); i++) {
@ -41,6 +44,102 @@ Shape* luax_checkshape(lua_State* L, int index) {
return NULL;
}
Shape* luax_newsphereshape(lua_State* L, int index) {
float radius = luax_optfloat(L, index, 1.f);
return lovrSphereShapeCreate(radius);
}
Shape* luax_newboxshape(lua_State* L, int index) {
float size[4];
luax_readscale(L, index, size, 3, NULL);
return lovrBoxShapeCreate(size[0], size[1], size[2]);
}
Shape* luax_newcapsuleshape(lua_State* L, int index) {
float radius = luax_optfloat(L, index + 0, 1.f);
float length = luax_optfloat(L, index + 1, 1.f);
return lovrCapsuleShapeCreate(radius, length);
}
Shape* luax_newcylindershape(lua_State* L, int index) {
float radius = luax_optfloat(L, index + 0, 1.f);
float length = luax_optfloat(L, index + 1, 1.f);
return lovrCylinderShapeCreate(radius, length);
}
Shape* luax_newmeshshape(lua_State* L, int index) {
float* vertices;
uint32_t* indices;
uint32_t vertexCount;
uint32_t indexCount;
bool shouldFree;
luax_readmesh(L, index, &vertices, &vertexCount, &indices, &indexCount, &shouldFree);
// If we do not own the mesh data, we must make a copy
// ode's trimesh collider needs to own the triangle info for the lifetime of the geom
// Note that if shouldFree is true, we don't free the data and let the physics module do it when
// the collider/shape is destroyed
if (!shouldFree) {
float* v = vertices;
uint32_t* i = indices;
vertices = malloc(3 * vertexCount * sizeof(float));
indices = malloc(indexCount * sizeof(uint32_t));
lovrAssert(vertices && indices, "Out of memory");
memcpy(vertices, v, 3 * vertexCount * sizeof(float));
memcpy(indices, i, indexCount * sizeof(uint32_t));
}
return lovrMeshShapeCreate(vertexCount, vertices, indexCount, indices);
}
Shape* luax_newterrainshape(lua_State* L, int index) {
float horizontalScale = luax_checkfloat(L, index++);
int type = lua_type(L, index);
if (type == LUA_TNIL || type == LUA_TNONE) {
float vertices[4] = { 0.f };
return lovrTerrainShapeCreate(vertices, 2, 2, horizontalScale, 1.f);
} else if (type == LUA_TFUNCTION) {
uint32_t samples = luax_optu32(L, index + 1, 100);
float* vertices = malloc(sizeof(float) * samples * samples);
lovrAssert(vertices, "Out of memory");
for (uint32_t i = 0; i < samples * samples; i++) {
float x = horizontalScale * (-.5f + ((float) (i % samples)) / samples);
float z = horizontalScale * (-.5f + ((float) (i / samples)) / samples);
lua_pushvalue(L, index);
lua_pushnumber(L, x);
lua_pushnumber(L, z);
lua_call(L, 2, 1);
lovrCheck(lua_type(L, -1) == LUA_TNUMBER, "Expected TerrainShape callback to return a number");
vertices[i] = luax_tofloat(L, -1);
lua_pop(L, 1);
}
TerrainShape* shape = lovrTerrainShapeCreate(vertices, samples, samples, horizontalScale, 1.f);
free(vertices);
return shape;
} else if (type == LUA_TUSERDATA) {
Image* image = luax_checktype(L, index, Image);
uint32_t imageWidth = lovrImageGetWidth(image, 0);
uint32_t imageHeight = lovrImageGetHeight(image, 0);
float verticalScale = luax_optfloat(L, index + 1, 1.f);
float* vertices = malloc(sizeof(float) * imageWidth * imageHeight);
lovrAssert(vertices, "Out of memory");
for (uint32_t y = 0; y < imageHeight; y++) {
for (uint32_t x = 0; x < imageWidth; x++) {
float pixel[4];
lovrImageGetPixel(image, x, y, pixel);
vertices[x + y * imageWidth] = pixel[0];
}
}
TerrainShape* shape = lovrTerrainShapeCreate(vertices, imageWidth, imageHeight, horizontalScale, verticalScale);
free(vertices);
return shape;
} else {
luax_typeerror(L, index, "Image, number, or function");
return NULL;
}
}
static int l_lovrShapeDestroy(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrShapeDestroyData(shape);
@ -313,3 +412,8 @@ const luaL_Reg lovrMeshShape[] = {
lovrShape,
{ NULL, NULL }
};
const luaL_Reg lovrTerrainShape[] = {
lovrShape,
{ NULL, NULL }
};

View File

@ -1,10 +1,8 @@
#include "api.h"
#include "physics/physics.h"
#include "data/image.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
@ -43,16 +41,6 @@ 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];
@ -65,11 +53,10 @@ static int l_lovrWorldNewCollider(lua_State* L) {
static int l_lovrWorldNewBoxCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4], size[4];
float position[4];
int index = luax_readvec3(L, 2, position, NULL);
luax_readscale(L, index, size, 3, NULL);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
BoxShape* shape = lovrBoxShapeCreate(size[0], size[1], size[2]);
BoxShape* shape = luax_newboxshape(L, index);
lovrColliderAddShape(collider, shape);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
@ -82,10 +69,8 @@ static int l_lovrWorldNewCapsuleCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
int index = luax_readvec3(L, 2, position, NULL);
float radius = luax_optfloat(L, index++, 1.f);
float length = luax_optfloat(L, index, 1.f);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
CapsuleShape* shape = lovrCapsuleShapeCreate(radius, length);
CapsuleShape* shape = luax_newcapsuleshape(L, index);
lovrColliderAddShape(collider, shape);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
@ -98,10 +83,8 @@ static int l_lovrWorldNewCylinderCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
int index = luax_readvec3(L, 2, position, NULL);
float radius = luax_optfloat(L, index++, 1.f);
float length = luax_optfloat(L, index, 1.f);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
CylinderShape* shape = lovrCylinderShapeCreate(radius, length);
CylinderShape* shape = luax_newcylindershape(L, index);
lovrColliderAddShape(collider, shape);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
@ -114,9 +97,8 @@ static int l_lovrWorldNewSphereCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float position[4];
int index = luax_readvec3(L, 2, position, NULL);
float radius = luax_optfloat(L, index, 1.f);
Collider* collider = lovrColliderCreate(world, position[0], position[1], position[2]);
SphereShape* shape = lovrSphereShapeCreate(radius);
SphereShape* shape = luax_newsphereshape(L, index);
lovrColliderAddShape(collider, shape);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
@ -127,30 +109,8 @@ static int l_lovrWorldNewSphereCollider(lua_State* L) {
static int l_lovrWorldNewMeshCollider(lua_State* L) {
World* world = luax_checktype(L, 1, World);
float* vertices;
uint32_t* indices;
uint32_t vertexCount;
uint32_t indexCount;
bool shouldFree;
luax_readmesh(L, 2, &vertices, &vertexCount, &indices, &indexCount, &shouldFree);
// If we do not own the mesh data, we must make a copy
// ode's trimesh collider needs to own the triangle info for the lifetime of the geom
// Note that if shouldFree is true, we don't free the data and let the physics module do it when
// the collider/shape is destroyed
if (!shouldFree) {
float* v = vertices;
uint32_t* i = indices;
vertices = malloc(3 * vertexCount * sizeof(float));
indices = malloc(indexCount * sizeof(uint32_t));
lovrAssert(vertices && indices, "Out of memory");
memcpy(vertices, v, 3 * vertexCount * sizeof(float));
memcpy(indices, i, indexCount * sizeof(uint32_t));
}
Collider* collider = lovrColliderCreate(world, 0, 0, 0);
MeshShape* shape = lovrMeshShapeCreate(vertexCount, vertices, indexCount, indices);
Collider* collider = lovrColliderCreate(world, 0.f, 0.f, 0.f);
MeshShape* shape = luax_newmeshshape(L, 2);
lovrColliderAddShape(collider, shape);
lovrColliderInitInertia(collider, shape);
luax_pushtype(L, Collider, collider);
@ -161,39 +121,8 @@ static int l_lovrWorldNewMeshCollider(lua_State* L) {
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);
Collider* collider = lovrColliderCreate(world, 0.f, 0.f, 0.f);
TerrainShape* shape = luax_newterrainshape(L, 2);
lovrColliderAddShape(collider, shape);
lovrColliderSetKinematic(collider, true);
luax_pushtype(L, Collider, collider);

View File

@ -132,7 +132,7 @@ typedef enum {
SHAPE_CAPSULE,
SHAPE_CYLINDER,
SHAPE_MESH,
SHAPE_TERRAIN,
SHAPE_TERRAIN
} ShapeType;
void lovrShapeDestroy(void* ref);
@ -173,7 +173,8 @@ 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);
TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t widthSamples, uint32_t depthSamples, float horizontalScale, float verticalScale);
// These tokens need to exist for Lua bindings
#define lovrSphereShapeDestroy lovrShapeDestroy