mirror of https://github.com/bjornbytes/lovr.git
506 lines
15 KiB
C
506 lines
15 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_CONVEX: luax_pushtype(L, ConvexShape, shape); break;
|
|
case SHAPE_MESH: luax_pushtype(L, MeshShape, shape); break;
|
|
case SHAPE_TERRAIN: luax_pushtype(L, TerrainShape, shape); break;
|
|
case SHAPE_COMPOUND: luax_pushtype(L, CompoundShape, 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("ConvexShape", strlen("ConvexShape")),
|
|
hash64("MeshShape", strlen("MeshShape")),
|
|
hash64("TerrainShape", strlen("TerrainShape")),
|
|
hash64("CompoundShape", strlen("CompoundShape"))
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
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_newconvexshape(lua_State* L, int index) {
|
|
float* points;
|
|
uint32_t count;
|
|
bool shouldFree;
|
|
luax_readmesh(L, index, &points, &count, NULL, NULL, &shouldFree);
|
|
ConvexShape* shape = lovrConvexShapeCreate(points, count);
|
|
if (shouldFree) lovrFree(points);
|
|
return shape;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
Shape* luax_newcompoundshape(lua_State* L, int index) {
|
|
if (lua_isnoneornil(L, index)) {
|
|
return lovrCompoundShapeCreate(NULL, NULL, NULL, 0, false);
|
|
}
|
|
|
|
luaL_checktype(L, index, LUA_TTABLE);
|
|
int length = luax_len(L, index);
|
|
|
|
uint32_t defer = lovrDeferPush();
|
|
Shape** shapes = lovrMalloc(length * sizeof(Shape*));
|
|
float* positions = lovrMalloc(length * 3 * sizeof(float));
|
|
float* orientations = lovrMalloc(length * 4 * sizeof(float));
|
|
lovrDefer(lovrFree, shapes);
|
|
lovrDefer(lovrFree, positions);
|
|
lovrDefer(lovrFree, orientations);
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
lua_rawgeti(L, index, i + 1);
|
|
lovrCheck(lua_istable(L, -1), "Expected table of tables for compound shape");
|
|
|
|
lua_rawgeti(L, -1, 1);
|
|
shapes[i] = luax_checkshape(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
int index = 2;
|
|
lua_rawgeti(L, -1, index);
|
|
switch (lua_type(L, -1)) {
|
|
case LUA_TNIL:
|
|
vec3_set(&positions[3 * i], 0.f, 0.f, 0.f);
|
|
lua_pop(L, 1);
|
|
break;
|
|
case LUA_TNUMBER:
|
|
lua_rawgeti(L, -2, index + 1);
|
|
lua_rawgeti(L, -3, index + 2);
|
|
vec3_set(&positions[3 * i], luax_tofloat(L, -3), luax_tofloat(L, -2), luax_tofloat(L, -1));
|
|
lua_pop(L, 3);
|
|
index += 3;
|
|
break;
|
|
default: {
|
|
float* v = luax_checkvector(L, -1, V_VEC3, "nil, number, or vec3");
|
|
vec3_init(&positions[3 * i], v);
|
|
lua_pop(L, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lua_rawgeti(L, -1, index);
|
|
switch (lua_type(L, -1)) {
|
|
case LUA_TNIL:
|
|
quat_identity(&orientations[4 * i]);
|
|
lua_pop(L, 1);
|
|
break;
|
|
case LUA_TNUMBER:
|
|
lua_rawgeti(L, -2, index);
|
|
lua_rawgeti(L, -3, index);
|
|
lua_rawgeti(L, -4, index);
|
|
quat_set(&orientations[4 * i], luax_tofloat(L, -4), luax_tofloat(L, -3), luax_tofloat(L, -2), luax_tofloat(L, -1));
|
|
lua_pop(L, 4);
|
|
break;
|
|
default: {
|
|
float* q = luax_checkvector(L, -1, V_QUAT, "nil, number, or quat");
|
|
quat_init(&positions[4 * i], q);
|
|
lua_pop(L, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
lua_getfield(L, index, "freeze");
|
|
bool freeze = lua_toboolean(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
CompoundShape* shape = lovrCompoundShapeCreate(shapes, positions, orientations, length, freeze);
|
|
lovrDeferPop(defer);
|
|
return shape;
|
|
}
|
|
|
|
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 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_lovrShapeGetMass(lua_State* L) {
|
|
Shape* shape = luax_checkshape(L, 1);
|
|
float density = luax_checkfloat(L, 2);
|
|
float centerOfMass[3], mass, inertia[6];
|
|
lovrShapeGetMass(shape, density, centerOfMass, &mass, inertia);
|
|
lua_pushnumber(L, centerOfMass[0]);
|
|
lua_pushnumber(L, centerOfMass[1]);
|
|
lua_pushnumber(L, centerOfMass[2]);
|
|
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 position[3], orientation[4], aabb[6];
|
|
if (lua_gettop(L) >= 2) {
|
|
int index = 2;
|
|
index = luax_readvec3(L, index, position, NULL);
|
|
index = luax_readquat(L, index, orientation, NULL);
|
|
lovrShapeGetAABB(shape, position, orientation, aabb);
|
|
} else {
|
|
lovrShapeGetAABB(shape, NULL, NULL, aabb);
|
|
}
|
|
for (int i = 0; i < 6; i++) {
|
|
lua_pushnumber(L, aabb[i]);
|
|
}
|
|
return 6;
|
|
}
|
|
|
|
#define lovrShape \
|
|
{ "destroy", l_lovrShapeDestroy }, \
|
|
{ "getType", l_lovrShapeGetType }, \
|
|
{ "getUserData", l_lovrShapeGetUserData }, \
|
|
{ "setUserData", l_lovrShapeSetUserData }, \
|
|
{ "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;
|
|
}
|
|
|
|
const luaL_Reg lovrSphereShape[] = {
|
|
lovrShape,
|
|
{ "getRadius", l_lovrSphereShapeGetRadius },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static int l_lovrBoxShapeGetDimensions(lua_State* L) {
|
|
BoxShape* box = luax_checktype(L, 1, BoxShape);
|
|
float dimensions[3];
|
|
lovrBoxShapeGetDimensions(box, dimensions);
|
|
lua_pushnumber(L, dimensions[0]);
|
|
lua_pushnumber(L, dimensions[1]);
|
|
lua_pushnumber(L, dimensions[2]);
|
|
return 3;
|
|
}
|
|
|
|
const luaL_Reg lovrBoxShape[] = {
|
|
lovrShape,
|
|
{ "getDimensions", l_lovrBoxShapeGetDimensions },
|
|
{ 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_lovrCapsuleShapeGetLength(lua_State* L) {
|
|
CapsuleShape* capsule = luax_checktype(L, 1, CapsuleShape);
|
|
lua_pushnumber(L, lovrCapsuleShapeGetLength(capsule));
|
|
return 1;
|
|
}
|
|
|
|
const luaL_Reg lovrCapsuleShape[] = {
|
|
lovrShape,
|
|
{ "getRadius", l_lovrCapsuleShapeGetRadius },
|
|
{ "getLength", l_lovrCapsuleShapeGetLength },
|
|
{ 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_lovrCylinderShapeGetLength(lua_State* L) {
|
|
CylinderShape* cylinder = luax_checktype(L, 1, CylinderShape);
|
|
lua_pushnumber(L, lovrCylinderShapeGetLength(cylinder));
|
|
return 1;
|
|
}
|
|
|
|
const luaL_Reg lovrCylinderShape[] = {
|
|
lovrShape,
|
|
{ "getRadius", l_lovrCylinderShapeGetRadius },
|
|
{ "getLength", l_lovrCylinderShapeGetLength },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
const luaL_Reg lovrConvexShape[] = {
|
|
lovrShape,
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
const luaL_Reg lovrMeshShape[] = {
|
|
lovrShape,
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
const luaL_Reg lovrTerrainShape[] = {
|
|
lovrShape,
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
static int l_lovrCompoundShapeIsFrozen(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
bool frozen = lovrCompoundShapeIsFrozen(shape);
|
|
lua_pushboolean(L, frozen);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeAddChild(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
Shape* child = luax_checkshape(L, 2);
|
|
float position[3], orientation[4];
|
|
int index = 3;
|
|
index = luax_readvec3(L, index, position, NULL);
|
|
index = luax_readquat(L, index, orientation, NULL);
|
|
lovrCompoundShapeAddChild(shape, child, position, orientation);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeReplaceChild(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
uint32_t index = luax_checku32(L, 2) - 1;
|
|
Shape* child = luax_checkshape(L, 3);
|
|
float position[3], orientation[4];
|
|
int i = 4;
|
|
i = luax_readvec3(L, i, position, NULL);
|
|
i = luax_readquat(L, i, orientation, NULL);
|
|
lovrCompoundShapeReplaceChild(shape, index, child, position, orientation);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeRemoveChild(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
uint32_t index = luax_checku32(L, 2) - 1;
|
|
lovrCompoundShapeRemoveChild(shape, index);
|
|
return 0;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeGetChild(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
uint32_t index = luax_checku32(L, 2) - 1;
|
|
Shape* child = lovrCompoundShapeGetChild(shape, index);
|
|
luax_pushshape(L, child);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeGetChildren(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
int count = (int) lovrCompoundShapeGetChildCount(shape);
|
|
lua_createtable(L, count, 0);
|
|
for (int i = 0; i < count; i++) {
|
|
Shape* child = lovrCompoundShapeGetChild(shape, (uint32_t) i);
|
|
luax_pushshape(L, child);
|
|
lua_rawseti(L, -2, i + 1);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeGetChildCount(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
uint32_t count = lovrCompoundShapeGetChildCount(shape);
|
|
lua_pushinteger(L, count);
|
|
return 1;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeGetChildOffset(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
uint32_t index = luax_checku32(L, 2) - 1;
|
|
float position[3], orientation[4], angle, ax, ay, az;
|
|
lovrCompoundShapeGetChildOffset(shape, index, position, orientation);
|
|
quat_getAngleAxis(orientation, &angle, &ax, &ay, &az);
|
|
lua_pushnumber(L, position[0]);
|
|
lua_pushnumber(L, position[1]);
|
|
lua_pushnumber(L, position[2]);
|
|
lua_pushnumber(L, angle);
|
|
lua_pushnumber(L, ax);
|
|
lua_pushnumber(L, ay);
|
|
lua_pushnumber(L, az);
|
|
return 7;
|
|
}
|
|
|
|
static int l_lovrCompoundShapeSetChildOffset(lua_State* L) {
|
|
CompoundShape* shape = luax_checktype(L, 1, CompoundShape);
|
|
uint32_t index = luax_checku32(L, 2) - 1;
|
|
float position[3], orientation[4];
|
|
int i = 3;
|
|
i = luax_readvec3(L, i, position, NULL);
|
|
i = luax_readquat(L, i, orientation, NULL);
|
|
lovrCompoundShapeSetChildOffset(shape, index, position, orientation);
|
|
return 0;
|
|
}
|
|
|
|
const luaL_Reg lovrCompoundShape[] = {
|
|
lovrShape,
|
|
{ "isFrozen", l_lovrCompoundShapeIsFrozen },
|
|
{ "addChild", l_lovrCompoundShapeAddChild },
|
|
{ "replaceChild", l_lovrCompoundShapeReplaceChild },
|
|
{ "removeChild", l_lovrCompoundShapeRemoveChild },
|
|
{ "getChild", l_lovrCompoundShapeGetChild },
|
|
{ "getChildren", l_lovrCompoundShapeGetChildren },
|
|
{ "getChildCount", l_lovrCompoundShapeGetChildCount },
|
|
{ "getChildOffset", l_lovrCompoundShapeGetChildOffset },
|
|
{ "setChildOffset", l_lovrCompoundShapeSetChildOffset },
|
|
{ "__len", l_lovrCompoundShapeGetChildCount }, // :)
|
|
{ NULL, NULL }
|
|
};
|