lovr/src/api/l_physics_shapes.c

456 lines
13 KiB
C

#include "api.h"
#include "physics/physics.h"
#include "data/image.h"
#include "core/maf.h"
#include "util.h"
#include <stdlib.h>
#include <string.h>
void luax_pushshape(lua_State* L, Shape* shape) {
switch (lovrShapeGetType(shape)) {
case SHAPE_SPHERE: luax_pushtype(L, SphereShape, shape); break;
case SHAPE_BOX: luax_pushtype(L, BoxShape, shape); break;
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();
}
}
Shape* luax_checkshape(lua_State* L, int index) {
Proxy* p = lua_touserdata(L, index);
if (p) {
const uint64_t hashes[] = {
hash64("SphereShape", strlen("SphereShape")),
hash64("BoxShape", strlen("BoxShape")),
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++) {
if (p->hash == hashes[i]) {
return p->object;
}
}
}
luax_typeerror(L, index, "Shape");
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[3];
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 = lovrMalloc(3 * vertexCount * sizeof(float));
indices = lovrMalloc(indexCount * sizeof(uint32_t));
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 scaleXZ = 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, scaleXZ, 1.f);
} else if (type == LUA_TFUNCTION) {
uint32_t n = luax_optu32(L, index + 1, 100);
float* vertices = lovrMalloc(sizeof(float) * n * n);
for (uint32_t i = 0; i < n * n; i++) {
float x = scaleXZ * (-.5f + ((float) (i % n)) / n);
float z = scaleXZ * (-.5f + ((float) (i / n)) / n);
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, n, scaleXZ, 1.f);
lovrFree(vertices);
return shape;
} else if (type == LUA_TUSERDATA) {
Image* image = luax_checktype(L, index, Image);
uint32_t n = lovrImageGetWidth(image, 0);
lovrCheck(lovrImageGetHeight(image, 0) == n, "TerrainShape images must be square");
float scaleY = luax_optfloat(L, index + 1, 1.f);
float* vertices = lovrMalloc(sizeof(float) * n * n);
for (uint32_t y = 0; y < n; y++) {
for (uint32_t x = 0; x < n; x++) {
float pixel[4];
lovrImageGetPixel(image, x, y, pixel);
vertices[x + y * n] = pixel[0];
}
}
TerrainShape* shape = lovrTerrainShapeCreate(vertices, n, scaleXZ, scaleY);
lovrFree(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);
return 0;
}
static int l_lovrShapeGetType(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
luax_pushenum(L, ShapeType, lovrShapeGetType(shape));
return 1;
}
static int l_lovrShapeGetCollider(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
luax_pushtype(L, Collider, lovrShapeGetCollider(shape));
return 1;
}
static int l_lovrShapeIsEnabled(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lua_pushboolean(L, lovrShapeIsEnabled(shape));
return 1;
}
static int l_lovrShapeSetEnabled(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
bool enabled = lua_toboolean(L, 2);
lovrShapeSetEnabled(shape, enabled);
return 0;
}
static int l_lovrShapeIsSensor(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lua_pushboolean(L, lovrShapeIsSensor(shape));
return 1;
}
static int l_lovrShapeSetSensor(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
bool sensor = lua_toboolean(L, 2);
lovrShapeSetSensor(shape, sensor);
return 0;
}
static void luax_pushshapestash(lua_State* L) {
lua_getfield(L, LUA_REGISTRYINDEX, "_lovrshapestash");
if (lua_isnil(L, -1)) {
lua_newtable(L);
lua_replace(L, -2);
// metatable
lua_newtable(L);
lua_pushliteral(L, "k");
lua_setfield(L, -2, "__mode");
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "_lovrshapestash");
}
}
static int l_lovrShapeGetUserData(lua_State* L) {
luax_checktype(L, 1, Shape);
luax_pushshapestash(L);
lua_pushvalue(L, 1);
lua_rawget(L, -2);
return 1;
}
static int l_lovrShapeSetUserData(lua_State* L) {
luax_checktype(L, 1, Shape);
luax_pushshapestash(L);
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_rawset(L, -3);
return 0;
}
static int l_lovrShapeGetPosition(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float x, y, z;
lovrShapeGetPosition(shape, &x, &y, &z);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 3;
}
static int l_lovrShapeSetPosition(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrCheck(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float position[3];
luax_readvec3(L, 2, position, NULL);
lovrShapeSetPosition(shape, position[0], position[1], position[2]);
return 0;
}
static int l_lovrShapeGetOrientation(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float angle, x, y, z, orientation[4];
lovrShapeGetOrientation(shape, orientation);
quat_getAngleAxis(orientation, &angle, &x, &y, &z);
lua_pushnumber(L, angle);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
return 4;
}
static int l_lovrShapeSetOrientation(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrCheck(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float orientation[4];
luax_readquat(L, 2, orientation, NULL);
lovrShapeSetOrientation(shape, orientation);
return 0;
}
static int l_lovrShapeGetPose(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float x, y, z;
lovrShapeGetPosition(shape, &x, &y, &z);
float angle, ax, ay, az, orientation[4];
lovrShapeGetOrientation(shape, orientation);
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_pushnumber(L, z);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 7;
}
static int l_lovrShapeSetPose(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
lovrCheck(lovrShapeGetCollider(shape) != NULL, "Shape must be attached to collider");
float position[3], orientation[4];
int index = luax_readvec3(L, 2, position, NULL);
luax_readquat(L, index, orientation, NULL);
lovrShapeSetPosition(shape, position[0], position[1], position[2]);
lovrShapeSetOrientation(shape, orientation);
return 0;
}
static int l_lovrShapeGetMass(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float density = luax_checkfloat(L, 2);
float cx, cy, cz, mass;
float inertia[6];
lovrShapeGetMass(shape, density, &cx, &cy, &cz, &mass, inertia);
lua_pushnumber(L, cx);
lua_pushnumber(L, cy);
lua_pushnumber(L, cz);
lua_pushnumber(L, mass);
lua_newtable(L);
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, inertia[i]);
lua_rawseti(L, -2, i + 1);
}
return 5;
}
static int l_lovrShapeGetAABB(lua_State* L) {
Shape* shape = luax_checkshape(L, 1);
float aabb[6];
lovrShapeGetAABB(shape, aabb);
for (int i = 0; i < 6; i++) {
lua_pushnumber(L, aabb[i]);
}
return 6;
}
#define lovrShape \
{ "destroy", l_lovrShapeDestroy }, \
{ "getType", l_lovrShapeGetType }, \
{ "getCollider", l_lovrShapeGetCollider }, \
{ "isEnabled", l_lovrShapeIsEnabled }, \
{ "setEnabled", l_lovrShapeSetEnabled }, \
{ "isSensor", l_lovrShapeIsSensor }, \
{ "setSensor", l_lovrShapeSetSensor }, \
{ "getUserData", l_lovrShapeGetUserData }, \
{ "setUserData", l_lovrShapeSetUserData }, \
{ "getPosition", l_lovrShapeGetPosition }, \
{ "setPosition", l_lovrShapeSetPosition }, \
{ "getOrientation", l_lovrShapeGetOrientation }, \
{ "setOrientation", l_lovrShapeSetOrientation }, \
{ "getPose", l_lovrShapeGetPose }, \
{ "setPose", l_lovrShapeSetPose }, \
{ "getMass", l_lovrShapeGetMass }, \
{ "getAABB", l_lovrShapeGetAABB }
static int l_lovrSphereShapeGetRadius(lua_State* L) {
SphereShape* sphere = luax_checktype(L, 1, SphereShape);
lua_pushnumber(L, lovrSphereShapeGetRadius(sphere));
return 1;
}
static int l_lovrSphereShapeSetRadius(lua_State* L) {
SphereShape* sphere = luax_checktype(L, 1, SphereShape);
float radius = luax_checkfloat(L, 2);
lovrSphereShapeSetRadius(sphere, radius);
return 0;
}
const luaL_Reg lovrSphereShape[] = {
lovrShape,
{ "getRadius", l_lovrSphereShapeGetRadius },
{ "setRadius", l_lovrSphereShapeSetRadius },
{ NULL, NULL }
};
static int l_lovrBoxShapeGetDimensions(lua_State* L) {
BoxShape* box = luax_checktype(L, 1, BoxShape);
float w, h, d;
lovrBoxShapeGetDimensions(box, &w, &h, &d);
lua_pushnumber(L, w);
lua_pushnumber(L, h);
lua_pushnumber(L, d);
return 3;
}
static int l_lovrBoxShapeSetDimensions(lua_State* L) {
BoxShape* box = luax_checktype(L, 1, BoxShape);
float size[3];
luax_readscale(L, 2, size, 3, NULL);
lovrBoxShapeSetDimensions(box, size[0], size[1], size[2]);
return 0;
}
const luaL_Reg lovrBoxShape[] = {
lovrShape,
{ "getDimensions", l_lovrBoxShapeGetDimensions },
{ "setDimensions", l_lovrBoxShapeSetDimensions },
{ NULL, NULL }
};
static int l_lovrCapsuleShapeGetRadius(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
lua_pushnumber(L, lovrCapsuleShapeGetRadius(capsule));
return 1;
}
static int l_lovrCapsuleShapeSetRadius(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
float radius = luax_checkfloat(L, 2);
lovrCapsuleShapeSetRadius(capsule, radius);
return 0;
}
static int l_lovrCapsuleShapeGetLength(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
lua_pushnumber(L, lovrCapsuleShapeGetLength(capsule));
return 1;
}
static int l_lovrCapsuleShapeSetLength(lua_State* L) {
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
float length = luax_checkfloat(L, 2);
lovrCapsuleShapeSetLength(capsule, length);
return 0;
}
const luaL_Reg lovrCapsuleShape[] = {
lovrShape,
{ "getRadius", l_lovrCapsuleShapeGetRadius },
{ "setRadius", l_lovrCapsuleShapeSetRadius },
{ "getLength", l_lovrCapsuleShapeGetLength },
{ "setLength", l_lovrCapsuleShapeSetLength },
{ NULL, NULL }
};
static int l_lovrCylinderShapeGetRadius(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
lua_pushnumber(L, lovrCylinderShapeGetRadius(cylinder));
return 1;
}
static int l_lovrCylinderShapeSetRadius(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
float radius = luax_checkfloat(L, 2);
lovrCylinderShapeSetRadius(cylinder, radius);
return 0;
}
static int l_lovrCylinderShapeGetLength(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
lua_pushnumber(L, lovrCylinderShapeGetLength(cylinder));
return 1;
}
static int l_lovrCylinderShapeSetLength(lua_State* L) {
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
float length = luax_checkfloat(L, 2);
lovrCylinderShapeSetLength(cylinder, length);
return 0;
}
const luaL_Reg lovrCylinderShape[] = {
lovrShape,
{ "getRadius", l_lovrCylinderShapeGetRadius },
{ "setRadius", l_lovrCylinderShapeSetRadius },
{ "getLength", l_lovrCylinderShapeGetLength },
{ "setLength", l_lovrCylinderShapeSetLength },
{ NULL, NULL }
};
const luaL_Reg lovrMeshShape[] = {
lovrShape,
{ NULL, NULL }
};
const luaL_Reg lovrTerrainShape[] = {
lovrShape,
{ NULL, NULL }
};