lovr/src/api/l_math_vectors.c

2020 lines
56 KiB
C

#include "api.h"
#include "core/maf.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#define EQ_THRESHOLD 1e-10f
static const uint32_t* swizzles[5] = {
[2] = (uint32_t[]) {
['x'] = 1,
['y'] = 2,
['r'] = 1,
['g'] = 2,
['s'] = 1,
['t'] = 2
},
[3] = (uint32_t[]) {
['x'] = 1,
['y'] = 2,
['z'] = 3,
['r'] = 1,
['g'] = 2,
['b'] = 3,
['s'] = 1,
['t'] = 2,
['p'] = 3
},
[4] = (uint32_t[]) {
['x'] = 1,
['y'] = 2,
['z'] = 3,
['w'] = 4,
['r'] = 1,
['g'] = 2,
['b'] = 3,
['a'] = 4,
['s'] = 1,
['t'] = 2,
['p'] = 3,
['q'] = 4
}
};
// Helpers
int luax_readvec3(lua_State* L, int index, vec3 v, const char* expected) {
switch (lua_type(L, index)) {
case LUA_TNIL:
case LUA_TNONE:
v[0] = v[1] = v[2] = 0.f;
return ++index;
case LUA_TNUMBER:
v[0] = luax_optfloat(L, index++, 0.f);
v[1] = luax_optfloat(L, index++, 0.f);
v[2] = luax_optfloat(L, index++, 0.f);
return index;
default:
vec3_init(v, luax_checkvector(L, index++, V_VEC3, expected ? expected : "vec3 or number"));
return index;
}
}
int luax_readscale(lua_State* L, int index, vec3 v, int components, const char* expected) {
switch (lua_type(L, index)) {
case LUA_TNIL:
case LUA_TNONE:
v[0] = v[1] = v[2] = 1.f;
return index + components;
case LUA_TNUMBER:
if (components == 1) {
v[0] = v[1] = v[2] = luax_optfloat(L, index++, 0.f);
} else {
v[0] = 1.f;
for (int i = 0; i < components; i++) {
v[i] = luax_optfloat(L, index++, v[0]);
}
}
return index;
default: {
VectorType type;
float* u = luax_tovector(L, index++, &type);
if (type == V_VEC2) {
v[0] = u[0];
v[1] = u[1];
v[2] = 1.f;
} else if (type == V_VEC3) {
vec3_init(v, u);
} else {
return luax_typeerror(L, index, "vec2, vec3, or number");
}
return index;
}
}
}
int luax_readquat(lua_State* L, int index, quat q, const char* expected) {
float angle, ax, ay, az;
switch (lua_type(L, index)) {
case LUA_TNIL:
case LUA_TNONE:
quat_identity(q);
return ++index;
case LUA_TNUMBER:
angle = luax_optfloat(L, index++, 0.f);
ax = luax_optfloat(L, index++, 0.f);
ay = luax_optfloat(L, index++, 1.f);
az = luax_optfloat(L, index++, 0.f);
quat_fromAngleAxis(q, angle, ax, ay, az);
return index;
default:
quat_init(q, luax_checkvector(L, index++, V_QUAT, expected ? expected : "quat or number"));
return index;
}
}
int luax_readmat4(lua_State* L, int index, mat4 m, int scaleComponents) {
switch (lua_type(L, index)) {
case LUA_TNIL:
case LUA_TNONE:
mat4_identity(m);
return index + 1;
case LUA_TLIGHTUSERDATA:
case LUA_TUSERDATA:
default: {
VectorType type;
float* p = luax_tovector(L, index, &type);
if (type == V_MAT4) {
mat4_init(m, p);
return index + 1;
}
} // Fall through
case LUA_TNUMBER: {
float S[4];
float R[4];
mat4_identity(m);
index = luax_readvec3(L, index, m + 12, "mat4, vec3, or number");
index = luax_readscale(L, index, S, scaleComponents, NULL);
index = luax_readquat(L, index, R, NULL);
m[15] = 1.f;
mat4_rotateQuat(m, R);
mat4_scale(m, S[0], S[1], S[2]);
return index;
}
}
}
// vec2
static int l_lovrVec2Equals(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u = luax_checkvector(L, 2, V_VEC2, NULL);
float dx = v[0] - u[0];
float dy = v[1] - u[1];
float distance2 = dx * dx + dy * dy;
bool equal = distance2 < EQ_THRESHOLD;
lua_pushboolean(L, equal);
return 1;
}
static int l_lovrVec2Unpack(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
return 2;
}
int l_lovrVec2Set(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
if (lua_isnoneornil(L, 2) || lua_type(L, 2) == LUA_TNUMBER) {
v[0] = luax_optfloat(L, 2, 0.f);
v[1] = luax_optfloat(L, 3, v[0]);
} else {
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
v[0] = u[0];
v[1] = u[1];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Add(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] += x;
v[1] += luax_optfloat(L, 3, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
v[0] += u[0];
v[1] += u[1];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Sub(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] -= x;
v[1] -= luax_optfloat(L, 3, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
v[0] -= u[0];
v[1] -= u[1];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Mul(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] *= x;
v[1] *= luax_optfloat(L, 3, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
v[0] *= u[0];
v[1] *= u[1];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Div(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] /= x;
v[1] /= luax_optfloat(L, 3, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
v[0] /= u[0];
v[1] /= u[1];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Length(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1]));
return 1;
}
static int l_lovrVec2Normalize(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float length = v[0] * v[0] + v[1] * v[1];
if (length != 0.f) {
length = 1.f / sqrtf(length);
v[0] *= length;
v[1] *= length;
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Distance(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u;
float uvec[2];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
}
float dx = v[0] - u[0];
float dy = v[1] - u[1];
lua_pushnumber(L, sqrtf(dx * dx + dy * dy));
return 1;
}
static int l_lovrVec2Dot(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u;
float uvec[2];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
}
lua_pushnumber(L, v[0] * u[0] + v[1] * u[1]);
return 1;
}
static int l_lovrVec2Lerp(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u;
float uvec[2];
float t;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
t = luax_checkfloat(L, 4);
} else {
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
t = luax_checkfloat(L, 3);
}
v[0] = v[0] + (u[0] - v[0]) * t;
v[1] = v[1] + (u[1] - v[1]) * t;
lua_settop(L, 1);
return 1;
}
static int l_lovrVec2Angle(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u;
float uvec[2];
float dot, length_v, length_u;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
}
length_v = sqrtf(v[0] * v[0] + v[1] * v[1]);
length_u = sqrtf(u[0] * u[0] + u[1] * u[1]);
if ((length_v == 0.f) || (length_u == 0.f)) {
lua_pushnumber(L, (float) M_PI / 2.f);
} else {
dot = v[0] * u[0] + v[1] * u[1];
lua_pushnumber(L, acosf(dot / (length_v * length_u)));
}
return 1;
}
static int l_lovrVec2__add(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC2);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC2, NULL);
out[0] = u[0] + x;
out[1] = u[1] + x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] + x;
out[1] = v[1] + x;
} else {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
out[0] = v[0] + u[0];
out[1] = v[1] + u[1];
}
return 1;
}
static int l_lovrVec2__sub(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC2);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC2, NULL);
out[0] = u[0] - x;
out[1] = u[1] - x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] - x;
out[1] = v[1] - x;
} else {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
out[0] = v[0] - u[0];
out[1] = v[1] - u[1];
}
return 1;
}
static int l_lovrVec2__mul(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC2);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC2, NULL);
out[0] = u[0] * x;
out[1] = u[1] * x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] * x;
out[1] = v[1] * x;
} else {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
out[0] = v[0] * u[0];
out[1] = v[1] * u[1];
}
return 1;
}
static int l_lovrVec2__div(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC2);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC2, NULL);
out[0] = u[0] / x;
out[1] = u[1] / x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] / x;
out[1] = v[1] / x;
} else {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* u = luax_checkvector(L, 2, V_VEC2, "vec2 or number");
out[0] = v[0] / u[0];
out[1] = v[1] / u[1];
}
return 1;
}
static int l_lovrVec2__unm(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
float* out = luax_newtempvector(L, V_VEC2);
out[0] = -v[0];
out[1] = -v[1];
return 1;
}
static int l_lovrVec2__len(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1]));
return 1;
}
static int l_lovrVec2__tostring(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
lua_pushfstring(L, "(%f, %f)", v[0], v[1]);
return 1;
}
static int l_lovrVec2__newindex(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index == 1 || index == 2) {
float x = luax_checkfloat(L, 3);
v[index - 1] = x;
return 0;
}
} else if (lua_type(L, 2) == LUA_TSTRING) {
size_t length;
const char* str = lua_tolstring(L, 2, &length);
const unsigned char* key = (const unsigned char*) str;
if (length == 1 && swizzles[2][key[0]]) {
v[swizzles[2][key[0]] - 1] = luax_checkfloat(L, 3);
return 0;
} else if (length == 2 && swizzles[2][key[0]] && swizzles[2][key[1]]) {
float* u = luax_checkvector(L, 3, V_VEC2, NULL);
for (size_t i = 0; i < length; i++) {
v[swizzles[2][key[i]] - 1] = u[i];
}
return 0;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to assign property %s of vec2 (invalid property)", lua_tostring(L, -1));
}
static int l_lovrVec2__index(lua_State* L) {
if (lua_type(L, 1) == LUA_TUSERDATA) {
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1)) {
return 1;
} else {
lua_pop(L, 2);
}
}
float* v = luax_checkvector(L, 1, V_VEC2, NULL);
int type = lua_type(L, 2);
if (type == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index == 1 || index == 2) {
lua_pushnumber(L, v[index - 1]);
return 1;
}
} else if (type == LUA_TSTRING) {
size_t length;
const char* str = lua_tolstring(L, 2, &length);
const unsigned char* key = (const unsigned char*) str;
if (length == 1 && swizzles[2][key[0]]) {
lua_pushnumber(L, v[swizzles[2][key[0]] - 1]);
return 1;
} else if (length == 2 && swizzles[2][key[0]] && swizzles[2][key[1]]) {
float* out = luax_newtempvector(L, V_VEC2);
out[0] = v[swizzles[2][key[0]] - 1];
out[1] = v[swizzles[2][key[1]] - 1];
return 1;
} else if (length == 3 && swizzles[2][key[0]] && swizzles[2][key[1]] && swizzles[2][key[2]]) {
float* out = luax_newtempvector(L, V_VEC3);
out[0] = v[swizzles[2][key[0]] - 1];
out[1] = v[swizzles[2][key[1]] - 1];
out[2] = v[swizzles[2][key[2]] - 1];
return 1;
} else if (length == 4 && swizzles[2][key[0]] && swizzles[2][key[1]] && swizzles[2][key[2]] && swizzles[2][key[3]]) {
float* out = luax_newtempvector(L, V_VEC4);
out[0] = v[swizzles[2][key[0]] - 1];
out[1] = v[swizzles[2][key[1]] - 1];
out[2] = v[swizzles[2][key[2]] - 1];
out[3] = v[swizzles[2][key[3]] - 1];
return 1;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to index field %s of vec2 (invalid property)", lua_tostring(L, -1));
}
const luaL_Reg lovrVec2[] = {
{ "equals", l_lovrVec2Equals },
{ "unpack", l_lovrVec2Unpack },
{ "set", l_lovrVec2Set },
{ "add", l_lovrVec2Add },
{ "sub", l_lovrVec2Sub },
{ "mul", l_lovrVec2Mul },
{ "div", l_lovrVec2Div },
{ "length", l_lovrVec2Length },
{ "normalize", l_lovrVec2Normalize },
{ "distance", l_lovrVec2Distance },
{ "dot", l_lovrVec2Dot },
{ "lerp", l_lovrVec2Lerp },
{ "angle", l_lovrVec2Angle },
{ "__add", l_lovrVec2__add },
{ "__sub", l_lovrVec2__sub },
{ "__mul", l_lovrVec2__mul },
{ "__div", l_lovrVec2__div },
{ "__unm", l_lovrVec2__unm },
{ "__len", l_lovrVec2__len },
{ "__tostring", l_lovrVec2__tostring },
{ "__newindex", l_lovrVec2__newindex },
{ "__index", l_lovrVec2__index },
{ NULL, NULL }
};
// vec3
static int l_lovrVec3Equals(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC3, NULL);
float* u = luax_checkvector(L, 2, V_VEC3, NULL);
float dx = v[0] - u[0];
float dy = v[1] - u[1];
float dz = v[2] - u[2];
float distance2 = dx * dx + dy * dy + dz * dz;
bool equal = distance2 < EQ_THRESHOLD;
lua_pushboolean(L, equal);
return 1;
}
static int l_lovrVec3Unpack(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
}
int l_lovrVec3Set(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
if (lua_isnoneornil(L, 2) || lua_type(L, 2) == LUA_TNUMBER) {
float x = luax_optfloat(L, 2, 0.f);
vec3_set(v, x, luax_optfloat(L, 3, x), luax_optfloat(L, 4, x));
} else {
VectorType t;
float* p = luax_tovector(L, 2, &t);
if (p && t == V_VEC3) {
vec3_init(v, p);
} else if (p && t == V_MAT4) {
vec3_set(v, p[12], p[13], p[14]);
} else{
luax_typeerror(L, 2, "vec3, mat4, or number");
}
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Add(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] += x;
v[1] += luax_optfloat(L, 3, x);
v[2] += luax_optfloat(L, 4, x);
v[3] += 0.f;
} else {
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
vec3_add(v, u);
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Sub(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] -= x;
v[1] -= luax_optfloat(L, 3, x);
v[2] -= luax_optfloat(L, 4, x);
v[3] -= 0.f;
} else {
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
vec3_sub(v, u);
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Mul(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] *= x;
v[1] *= luax_optfloat(L, 3, x);
v[2] *= luax_optfloat(L, 4, x);
} else {
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
v[0] *= u[0];
v[1] *= u[1];
v[2] *= u[2];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Div(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] /= x;
v[1] /= luax_optfloat(L, 3, x);
v[2] /= luax_optfloat(L, 4, x);
} else {
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
v[0] /= u[0];
v[1] /= u[1];
v[2] /= u[2];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Length(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
lua_pushnumber(L, vec3_length(v));
return 1;
}
static int l_lovrVec3Normalize(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3_normalize(v);
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Distance(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
}
lua_pushnumber(L, vec3_distance(v, u));
return 1;
}
static int l_lovrVec3Dot(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
}
lua_pushnumber(L, vec3_dot(v, u));
return 1;
}
static int l_lovrVec3Cross(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
}
vec3_cross(v, u);
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Lerp(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u;
float uvec[4];
float t;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
u = uvec;
t = luax_checkfloat(L, 5);
} else {
u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
t = luax_checkfloat(L, 3);
}
vec3_lerp(v, u, t);
lua_settop(L, 1);
return 1;
}
static int l_lovrVec3Angle(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
}
lua_pushnumber(L, vec3_angle(v, u));
return 1;
}
static int l_lovrVec3__add(lua_State* L) {
vec3 out = luax_newtempvector(L, V_VEC3);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
vec3 v = luax_checkvector(L, 2, V_VEC3, NULL);
out[0] = v[0] + x;
out[1] = v[1] + x;
out[2] = v[2] + x;
out[3] = v[3] + x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] + x;
out[1] = v[1] + x;
out[2] = v[2] + x;
out[3] = v[3] + x;
} else {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
out[0] = v[0] + u[0], out[1] = v[1] + u[1], out[2] = v[2] + u[2], out[3] = v[3] + u[3];
}
return 1;
}
static int l_lovrVec3__sub(lua_State* L) {
vec3 out = luax_newtempvector(L, V_VEC3);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
vec3 v = luax_checkvector(L, 2, V_VEC3, NULL);
out[0] = v[0] - x;
out[1] = v[1] - x;
out[2] = v[2] - x;
out[3] = v[3] - x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] - x;
out[1] = v[1] - x;
out[2] = v[2] - x;
out[3] = v[3] - x;
} else {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
out[0] = v[0] - u[0], out[1] = v[1] - u[1], out[2] = v[2] - u[2], out[3] = v[3] - u[3];
}
return 1;
}
static int l_lovrVec3__mul(lua_State* L) {
vec3 out = luax_newtempvector(L, V_VEC3);
if (lua_type(L, 1) == LUA_TNUMBER) {
vec3 u = luax_checkvector(L, 2, V_VEC3, NULL);
vec3_scale(vec3_init(out, u), lua_tonumber(L, 1));
} else if (lua_type(L, 2) == LUA_TNUMBER) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3_scale(vec3_init(out, v), lua_tonumber(L, 2));
} else {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
out[0] = v[0] * u[0], out[1] = v[1] * u[1], out[2] = v[2] * u[2], out[3] = v[3] * u[3];
}
return 1;
}
static int l_lovrVec3__div(lua_State* L) {
vec3 out = luax_newtempvector(L, V_VEC3);
if (lua_type(L, 1) == LUA_TNUMBER) {
vec3 u = luax_checkvector(L, 2, V_VEC3, NULL);
vec3_init(out, u);
vec3_scale(out, 1. / lua_tonumber(L, 1));
} else if (lua_type(L, 2) == LUA_TNUMBER) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3_init(out, v);
vec3_scale(out, 1. / lua_tonumber(L, 2));
} else {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 u = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
out[0] = v[0] / u[0], out[1] = v[1] / u[1], out[2] = v[2] / u[2], out[3] = v[3] / u[3];
}
return 1;
}
static int l_lovrVec3__unm(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
vec3 out = luax_newtempvector(L, V_VEC3);
vec3_scale(vec3_init(out, v), -1.f);
return 1;
}
static int l_lovrVec3__len(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
lua_pushnumber(L, vec3_length(v));
return 1;
}
static int l_lovrVec3__tostring(lua_State* L) {
vec3 v = luax_checkvector(L, 1, V_VEC3, NULL);
lua_pushfstring(L, "(%f, %f, %f)", v[0], v[1], v[2]);
return 1;
}
static int l_lovrVec3__newindex(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC3, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index == 1 || index == 2 || index == 3) {
float x = luax_checkfloat(L, 3);
v[index - 1] = x;
return 0;
}
} else if (lua_type(L, 2) == LUA_TSTRING) {
size_t length;
const char* str = lua_tolstring(L, 2, &length);
const unsigned char* key = (const unsigned char*) str;
if (length == 1 && swizzles[3][key[0]]) {
v[swizzles[3][key[0]] - 1] = luax_checkfloat(L, 3);
return 0;
} else if (length == 2 && swizzles[3][key[0]] && swizzles[3][key[1]]) {
float* u = luax_checkvector(L, 3, V_VEC2, NULL);
for (size_t i = 0; i < length; i++) {
v[swizzles[3][key[i]] - 1] = u[i];
}
return 0;
} else if (length == 3 && swizzles[3][key[0]] && swizzles[3][key[1]] && swizzles[3][key[2]]) {
float* u = luax_checkvector(L, 3, V_VEC3, NULL);
for (size_t i = 0; i < length; i++) {
v[swizzles[3][key[i]] - 1] = u[i];
}
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to assign property %s of vec3 (invalid property)", lua_tostring(L, -1));
}
static int l_lovrVec3__index(lua_State* L) {
if (lua_type(L, 1) == LUA_TUSERDATA) {
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1)) {
return 1;
} else {
lua_pop(L, 2);
}
}
float* v = luax_checkvector(L, 1, V_VEC3, NULL);
int type = lua_type(L, 2);
if (type == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index == 1 || index == 2 || index == 3) {
lua_pushnumber(L, v[index - 1]);
return 1;
}
} else if (type == LUA_TSTRING) {
size_t length;
const char* str = lua_tolstring(L, 2, &length);
const unsigned char* key = (const unsigned char*) str;
if (length == 1 && swizzles[3][key[0]]) {
lua_pushnumber(L, v[swizzles[3][key[0]] - 1]);
return 1;
} else if (length == 2 && swizzles[3][key[0]] && swizzles[3][key[1]]) {
float* out = luax_newtempvector(L, V_VEC2);
out[0] = v[swizzles[3][key[0]] - 1];
out[1] = v[swizzles[3][key[1]] - 1];
return 1;
} else if (length == 3 && swizzles[3][key[0]] && swizzles[3][key[1]] && swizzles[3][key[2]]) {
float* out = luax_newtempvector(L, V_VEC3);
out[0] = v[swizzles[3][key[0]] - 1];
out[1] = v[swizzles[3][key[1]] - 1];
out[2] = v[swizzles[3][key[2]] - 1];
return 1;
} else if (length == 4 && swizzles[3][key[0]] && swizzles[3][key[1]] && swizzles[3][key[2]] && swizzles[3][key[3]]) {
float* out = luax_newtempvector(L, V_VEC4);
out[0] = v[swizzles[3][key[0]] - 1];
out[1] = v[swizzles[3][key[1]] - 1];
out[2] = v[swizzles[3][key[2]] - 1];
out[3] = v[swizzles[3][key[3]] - 1];
return 1;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to index field %s of vec3 (invalid property)", lua_tostring(L, -1));
}
const luaL_Reg lovrVec3[] = {
{ "equals", l_lovrVec3Equals },
{ "unpack", l_lovrVec3Unpack },
{ "set", l_lovrVec3Set },
{ "add", l_lovrVec3Add },
{ "sub", l_lovrVec3Sub },
{ "mul", l_lovrVec3Mul },
{ "div", l_lovrVec3Div },
{ "length", l_lovrVec3Length },
{ "normalize", l_lovrVec3Normalize },
{ "distance", l_lovrVec3Distance },
{ "dot", l_lovrVec3Dot },
{ "cross", l_lovrVec3Cross },
{ "lerp", l_lovrVec3Lerp },
{ "angle", l_lovrVec3Angle },
{ "__add", l_lovrVec3__add },
{ "__sub", l_lovrVec3__sub },
{ "__mul", l_lovrVec3__mul },
{ "__div", l_lovrVec3__div },
{ "__unm", l_lovrVec3__unm },
{ "__len", l_lovrVec3__len },
{ "__tostring", l_lovrVec3__tostring },
{ "__newindex", l_lovrVec3__newindex },
{ "__index", l_lovrVec3__index },
{ NULL, NULL }
};
// vec4
static int l_lovrVec4Equals(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u = luax_checkvector(L, 2, V_VEC4, NULL);
float dx = v[0] - u[0];
float dy = v[1] - u[1];
float dz = v[2] - u[2];
float dw = v[3] - u[3];
float distance2 = dx * dx + dy * dy + dz * dz + dw * dw;
bool equal = distance2 < EQ_THRESHOLD;
lua_pushboolean(L, equal);
return 1;
}
static int l_lovrVec4Unpack(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
lua_pushnumber(L, v[3]);
return 4;
}
int l_lovrVec4Set(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
if (lua_isnoneornil(L, 2) || lua_type(L, 2) == LUA_TNUMBER) {
v[0] = luax_optfloat(L, 2, 0.f);
v[1] = luax_optfloat(L, 3, v[0]);
v[2] = luax_optfloat(L, 4, v[0]);
v[3] = luax_optfloat(L, 5, v[0]);
} else {
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
v[0] = u[0];
v[1] = u[1];
v[2] = u[2];
v[3] = u[3];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Add(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] += x;
v[1] += luax_optfloat(L, 3, x);
v[2] += luax_optfloat(L, 4, x);
v[3] += luax_optfloat(L, 5, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
v[0] += u[0];
v[1] += u[1];
v[2] += u[2];
v[3] += u[3];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Sub(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] -= x;
v[1] -= luax_optfloat(L, 3, x);
v[2] -= luax_optfloat(L, 4, x);
v[3] -= luax_optfloat(L, 5, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
v[0] -= u[0];
v[1] -= u[1];
v[2] -= u[2];
v[3] -= u[3];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Mul(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] *= x;
v[1] *= luax_optfloat(L, 3, x);
v[2] *= luax_optfloat(L, 4, x);
v[3] *= luax_optfloat(L, 5, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
v[0] *= u[0];
v[1] *= u[1];
v[2] *= u[2];
v[3] *= u[3];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Div(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
v[0] /= x;
v[1] /= luax_optfloat(L, 3, x);
v[2] /= luax_optfloat(L, 4, x);
v[3] /= luax_optfloat(L, 5, x);
} else {
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
v[0] /= u[0];
v[1] /= u[1];
v[2] /= u[2];
v[3] /= u[3];
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Length(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]));
return 1;
}
static int l_lovrVec4Normalize(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float length = v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3];
if (length != 0.f) {
length = 1.f / sqrtf(length);
v[0] *= length;
v[1] *= length;
v[2] *= length;
v[3] *= length;
}
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Distance(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);;
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
}
float dx = v[0] - u[0];
float dy = v[1] - u[1];
float dz = v[2] - u[2];
float dw = v[3] - u[3];
lua_pushnumber(L, sqrtf(dx * dx + dy * dy + dz * dz + dw * dw));
return 1;
}
static int l_lovrVec4Dot(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u;
float uvec[4];
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
}
lua_pushnumber(L, v[0] * u[0] + v[1] * u[1] + v[2] * u[2] + v[3] * u[3]);
return 1;
}
static int l_lovrVec4Lerp(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u;
float uvec[4];
float t;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
t = luax_checkfloat(L, 6);
} else {
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
t = luax_checkfloat(L, 3);
}
v[0] = v[0] + (u[0] - v[0]) * t;
v[1] = v[1] + (u[1] - v[1]) * t;
v[2] = v[2] + (u[2] - v[2]) * t;
v[3] = v[3] + (u[3] - v[3]) * t;
lua_settop(L, 1);
return 1;
}
static int l_lovrVec4Angle(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u;
float uvec[4];
float dot, length_v, length_u;
if (lua_type(L, 2) == LUA_TNUMBER) {
uvec[0] = lua_tonumber(L, 2);
uvec[1] = luax_checkfloat(L, 3);
uvec[2] = luax_checkfloat(L, 4);
uvec[3] = luax_checkfloat(L, 5);
u = uvec;
} else {
u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
}
length_v = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]);
length_u = sqrtf(u[0] * u[0] + u[1] * u[1] + u[2] * u[2] + u[3] * u[3]);
if ((length_v == 0.f) || (length_u == 0.f)) {
lua_pushnumber(L, (float) M_PI / 2);
} else {
dot = v[0] * u[0] + v[1] * u[1] + v[2] * u[2] + v[3] * u[3];
lua_pushnumber(L, acosf(dot / (length_v * length_u)));
}
return 1;
}
static int l_lovrVec4__add(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC4);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC4, NULL);
out[0] = u[0] + x;
out[1] = u[1] + x;
out[2] = u[2] + x;
out[3] = u[3] + x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] + x;
out[1] = v[1] + x;
out[2] = v[2] + x;
out[3] = v[3] + x;
} else {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
out[0] = v[0] + u[0];
out[1] = v[1] + u[1];
out[2] = v[2] + u[2];
out[3] = v[3] + u[3];
}
return 1;
}
static int l_lovrVec4__sub(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC4);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC4, NULL);
out[0] = u[0] - x;
out[1] = u[1] - x;
out[2] = u[2] - x;
out[3] = u[3] - x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] - x;
out[1] = v[1] - x;
out[2] = v[2] - x;
out[3] = v[3] - x;
} else {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
out[0] = v[0] - u[0];
out[1] = v[1] - u[1];
out[2] = v[2] - u[2];
out[3] = v[3] - u[3];
}
return 1;
}
static int l_lovrVec4__mul(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC4);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC4, NULL);
out[0] = u[0] * x;
out[1] = u[1] * x;
out[2] = u[2] * x;
out[3] = u[3] * x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] * x;
out[1] = v[1] * x;
out[2] = v[2] * x;
out[3] = v[3] * x;
} else {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
out[0] = v[0] * u[0];
out[1] = v[1] * u[1];
out[2] = v[2] * u[2];
out[3] = v[3] * u[3];
}
return 1;
}
static int l_lovrVec4__div(lua_State* L) {
float* out = luax_newtempvector(L, V_VEC4);
if (lua_type(L, 1) == LUA_TNUMBER) {
float x = lua_tonumber(L, 1);
float* u = luax_checkvector(L, 2, V_VEC4, NULL);
out[0] = u[0] / x;
out[1] = u[1] / x;
out[2] = u[2] / x;
out[3] = u[3] / x;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float x = lua_tonumber(L, 2);
out[0] = v[0] / x;
out[1] = v[1] / x;
out[2] = v[2] / x;
out[3] = v[3] / x;
} else {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* u = luax_checkvector(L, 2, V_VEC4, "vec4 or number");
out[0] = v[0] / u[0];
out[1] = v[1] / u[1];
out[2] = v[2] / u[2];
out[3] = v[3] / u[3];
}
return 1;
}
static int l_lovrVec4__unm(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
float* out = luax_newtempvector(L, V_VEC4);
out[0] = -v[0];
out[1] = -v[1];
out[2] = -v[2];
out[3] = -v[3];
return 1;
}
static int l_lovrVec4__len(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
lua_pushnumber(L, sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]));
return 1;
}
static int l_lovrVec4__tostring(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
lua_pushfstring(L, "(%f, %f, %f, %f)", v[0], v[1], v[2], v[3]);
return 1;
}
static int l_lovrVec4__newindex(lua_State* L) {
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index >= 1 && index <= 4) {
float x = luax_checkfloat(L, 3);
v[index - 1] = x;
return 0;
}
} else if (lua_type(L, 2) == LUA_TSTRING) {
size_t length;
const char* str = lua_tolstring(L, 2, &length);
const unsigned char* key = (const unsigned char*) str;
if (length == 1 && swizzles[4][key[0]]) {
v[swizzles[4][key[0]] - 1] = luax_checkfloat(L, 3);
return 0;
} else if (length == 2 && swizzles[4][key[0]] && swizzles[4][key[1]]) {
float* u = luax_checkvector(L, 3, V_VEC2, NULL);
for (size_t i = 0; i < length; i++) {
v[swizzles[4][key[i]] - 1] = u[i];
}
return 0;
} else if (length == 3 && swizzles[4][key[0]] && swizzles[4][key[1]] && swizzles[4][key[2]]) {
float* u = luax_checkvector(L, 3, V_VEC3, NULL);
for (size_t i = 0; i < length; i++) {
v[swizzles[4][key[i]] - 1] = u[i];
}
return 0;
} else if (length == 4 && swizzles[4][key[0]] && swizzles[4][key[1]] && swizzles[4][key[2]] && swizzles[4][key[3]]) {
float* u = luax_checkvector(L, 3, V_VEC4, NULL);
for (size_t i = 0; i < length; i++) {
v[swizzles[4][key[i]] - 1] = u[i];
}
return 0;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to assign property %s of vec4 (invalid property)", lua_tostring(L, -1));
}
static int l_lovrVec4__index(lua_State* L) {
if (lua_type(L, 1) == LUA_TUSERDATA) {
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1)) {
return 1;
} else {
lua_pop(L, 2);
}
}
float* v = luax_checkvector(L, 1, V_VEC4, NULL);
int type = lua_type(L, 2);
if (type == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index >= 1 && index <= 4) {
lua_pushnumber(L, v[index - 1]);
return 1;
}
} else if (type == LUA_TSTRING) {
size_t length;
const char* str = lua_tolstring(L, 2, &length);
const unsigned char* key = (const unsigned char*) str;
if (length == 1 && swizzles[4][key[0]]) {
lua_pushnumber(L, v[swizzles[4][key[0]] - 1]);
return 1;
} else if (length == 2 && swizzles[4][key[0]] && swizzles[4][key[1]]) {
float* out = luax_newtempvector(L, V_VEC2);
out[0] = v[swizzles[4][key[0]] - 1];
out[1] = v[swizzles[4][key[1]] - 1];
return 1;
} else if (length == 3 && swizzles[4][key[0]] && swizzles[4][key[1]] && swizzles[4][key[2]]) {
float* out = luax_newtempvector(L, V_VEC3);
out[0] = v[swizzles[4][key[0]] - 1];
out[1] = v[swizzles[4][key[1]] - 1];
out[2] = v[swizzles[4][key[2]] - 1];
return 1;
} else if (length == 4 && swizzles[4][key[0]] && swizzles[4][key[1]] && swizzles[4][key[2]] && swizzles[4][key[3]]) {
float* out = luax_newtempvector(L, V_VEC4);
out[0] = v[swizzles[4][key[0]] - 1];
out[1] = v[swizzles[4][key[1]] - 1];
out[2] = v[swizzles[4][key[2]] - 1];
out[3] = v[swizzles[4][key[3]] - 1];
return 1;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to index field %s of vec4 (invalid property)", lua_tostring(L, -1));
}
const luaL_Reg lovrVec4[] = {
{ "equals", l_lovrVec4Equals },
{ "unpack", l_lovrVec4Unpack },
{ "set", l_lovrVec4Set },
{ "add", l_lovrVec4Add },
{ "sub", l_lovrVec4Sub },
{ "mul", l_lovrVec4Mul },
{ "div", l_lovrVec4Div },
{ "length", l_lovrVec4Length },
{ "normalize", l_lovrVec4Normalize },
{ "distance", l_lovrVec4Distance },
{ "dot", l_lovrVec4Dot },
{ "lerp", l_lovrVec4Lerp },
{ "angle", l_lovrVec4Angle },
{ "__add", l_lovrVec4__add },
{ "__sub", l_lovrVec4__sub },
{ "__mul", l_lovrVec4__mul },
{ "__div", l_lovrVec4__div },
{ "__unm", l_lovrVec4__unm },
{ "__len", l_lovrVec4__len },
{ "__tostring", l_lovrVec4__tostring },
{ "__newindex", l_lovrVec4__newindex },
{ "__index", l_lovrVec4__index },
{ NULL, NULL }
};
// quat
static int l_lovrQuatEquals(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
quat r = luax_checkvector(L, 2, V_QUAT, NULL);
float dot = q[0] * r[0] + q[1] * r[1] + q[2] * r[2] + q[3] * r[3];
bool equal = fabsf(dot) >= 1.f - 1e-5f;
lua_pushboolean(L, equal);
return 1;
}
static int l_lovrQuatUnpack(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
bool raw = lua_toboolean(L, 2);
if (raw) {
lua_pushnumber(L, q[0]);
lua_pushnumber(L, q[1]);
lua_pushnumber(L, q[2]);
lua_pushnumber(L, q[3]);
} else {
float angle, ax, ay, az;
quat_getAngleAxis(q, &angle, &ax, &ay, &az);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
}
return 4;
}
int l_lovrQuatSet(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
if (lua_isnoneornil(L, 2)) {
quat_identity(q);
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float x = lua_tonumber(L, 2);
float y = luax_checkfloat(L, 3);
float z = luax_checkfloat(L, 4);
float w = luax_checkfloat(L, 5);
bool raw = lua_toboolean(L, 6);
if (raw) {
quat_set(q, x, y, z, w);
} else {
quat_fromAngleAxis(q, x, y, z, w);
}
} else {
VectorType type;
float* p = luax_tovector(L, 2, &type);
if (!p) return luax_typeerror(L, 2, "vec3, quat, or number");
if (type == V_VEC3) {
if (lua_gettop(L) > 2) {
vec3 u = luax_checkvector(L, 3, V_VEC3, "vec3");
quat_between(q, p, u);
} else {
quat_between(q, (float[4]) { 0.f, 0.f, -1.f }, p);
}
} else if (type == V_QUAT) {
quat_init(q, p);
} else if (type == V_MAT4) {
quat_fromMat4(q, p);
} else {
return luax_typeerror(L, 2, "vec3, quat, mat4, or number");
}
}
lua_settop(L, 1);
return 1;
}
static int l_lovrQuatMul(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
VectorType type;
float* r = luax_tovector(L, 2, &type);
if (!r || (type != V_VEC3 && type != V_QUAT)) return luax_typeerror(L, 2, "quat or vec3");
if (type == V_VEC3) {
quat_rotate(q, r);
lua_settop(L, 2);
} else {
quat_mul(q, q, r);
lua_settop(L, 1);
}
return 1;
}
static int l_lovrQuatLength(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
lua_pushnumber(L, quat_length(q));
return 1;
}
static int l_lovrQuatNormalize(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
quat_normalize(q);
lua_settop(L, 1);
return 1;
}
static int l_lovrQuatDirection(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
vec3 v = luax_newtempvector(L, V_VEC3);
quat_getDirection(q, v);
return 1;
}
static int l_lovrQuatConjugate(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
quat_conjugate(q);
lua_settop(L, 1);
return 1;
}
static int l_lovrQuatSlerp(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
quat r = luax_checkvector(L, 2, V_QUAT, NULL);
float t = luax_checkfloat(L, 3);
quat_slerp(q, r, t);
lua_settop(L, 1);
return 1;
}
static int l_lovrQuat__mul(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
VectorType type;
float* r = luax_tovector(L, 2, &type);
if (!r) return luax_typeerror(L, 2, "quat or vec3");
if (type == V_VEC3) {
vec3 out = luax_newtempvector(L, V_VEC3);
quat_rotate(q, vec3_init(out, r));
} else {
quat out = luax_newtempvector(L, V_QUAT);
quat_mul(out, q, r);
}
return 1;
}
static int l_lovrQuat__len(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
lua_pushnumber(L, quat_length(q));
return 1;
}
static int l_lovrQuat__tostring(lua_State* L) {
quat q = luax_checkvector(L, 1, V_QUAT, NULL);
lua_pushfstring(L, "(%f, %f, %f, %f)", q[0], q[1], q[2], q[3]);
return 1;
}
static int l_lovrQuat__newindex(lua_State* L) {
float* q = luax_checkvector(L, 1, V_QUAT, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index == 1 || index == 2 || index == 3 || index == 4) {
float x = luax_checkfloat(L, 3);
q[index - 1] = x;
return 0;
}
} else if (lua_type(L, 2) == LUA_TSTRING) {
size_t length;
const char* key = lua_tolstring(L, 2, &length);
float x = luax_checkfloat(L, 3);
if (length == 1 && key[0] >= 'w' && key[0] <= 'z') {
int index = key[0] == 'w' ? 3 : key[0] - 'x';
q[index] = x;
return 0;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to assign property %s of quat (invalid property)", lua_tostring(L, -1));
}
static int l_lovrQuat__index(lua_State* L) {
if (lua_type(L, 1) == LUA_TUSERDATA) {
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1)) {
return 1;
} else {
lua_pop(L, 2);
}
}
float* q = luax_checkvector(L, 1, V_QUAT, NULL);
int type = lua_type(L, 2);
if (type == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index == 1 || index == 2 || index == 3 || index == 4) {
lua_pushnumber(L, q[index - 1]);
return 1;
}
} else if (type == LUA_TSTRING) {
size_t length;
const char* key = lua_tolstring(L, 2, &length);
if (length == 1 && key[0] >= 'w' && key[0] <= 'z') {
int index = key[0] == 'w' ? 3 : key[0] - 'x';
lua_pushnumber(L, q[index]);
return 1;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to index field %s of quat (invalid property)", lua_tostring(L, -1));
}
const luaL_Reg lovrQuat[] = {
{ "equals", l_lovrQuatEquals },
{ "unpack", l_lovrQuatUnpack },
{ "set", l_lovrQuatSet },
{ "mul", l_lovrQuatMul },
{ "length", l_lovrQuatLength },
{ "normalize", l_lovrQuatNormalize },
{ "direction", l_lovrQuatDirection },
{ "conjugate", l_lovrQuatConjugate },
{ "slerp", l_lovrQuatSlerp },
{ "__mul", l_lovrQuat__mul },
{ "__len", l_lovrQuat__len },
{ "__tostring", l_lovrQuat__tostring },
{ "__newindex", l_lovrQuat__newindex },
{ "__index", l_lovrQuat__index },
{ NULL, NULL }
};
// mat4
static int l_lovrMat4Equals(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
mat4 n = luax_checkvector(L, 2, V_MAT4, NULL);
for (int i = 0; i < 16; i += 4) {
float dx = m[i + 0] - n[i + 0];
float dy = m[i + 1] - n[i + 1];
float dz = m[i + 2] - n[i + 2];
float dw = m[i + 3] - n[i + 3];
float distance2 = dx * dx + dy * dy + dz * dz + dw * dw;
if (distance2 > EQ_THRESHOLD) {
lua_pushboolean(L, false);
return 1;
}
}
lua_pushboolean(L, true);
return 1;
}
static int l_lovrMat4Unpack(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
if (lua_toboolean(L, 2)) {
for (int i = 0; i < 16; i++) {
lua_pushnumber(L, m[i]);
}
return 16;
} else {
float position[4], scale[4], angle, ax, ay, az;
mat4_getPosition(m, position);
mat4_getScale(m, scale);
mat4_getAngleAxis(m, &angle, &ax, &ay, &az);
lua_pushnumber(L, position[0]);
lua_pushnumber(L, position[1]);
lua_pushnumber(L, position[2]);
lua_pushnumber(L, scale[0]);
lua_pushnumber(L, scale[1]);
lua_pushnumber(L, scale[2]);
lua_pushnumber(L, angle);
lua_pushnumber(L, ax);
lua_pushnumber(L, ay);
lua_pushnumber(L, az);
return 10;
}
}
int l_lovrMat4Set(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
int top = lua_gettop(L);
int type = lua_type(L, 2);
if (type == LUA_TNONE || type == LUA_TNIL || (top == 2 && type == LUA_TNUMBER)) {
float x = luax_optfloat(L, 2, 1.f);
memset(m, 0, 16 * sizeof(float));
m[0] = m[5] = m[10] = m[15] = x;
} else if (top == 17) {
for (int i = 2; i <= 17; i++) {
*m++ = luax_checkfloat(L, i);
}
} else {
VectorType vectorType;
float* n = luax_tovector(L, 2, &vectorType);
if (vectorType == V_MAT4) {
mat4_init(m, n);
} else {
int index = 2;
mat4_identity(m);
float position[4];
index = luax_readvec3(L, index, position, "nil, number, vec3, or mat4");
m[12] = position[0];
m[13] = position[1];
m[14] = position[2];
float* v = luax_tovector(L, index, &vectorType);
if (vectorType == V_QUAT) {
mat4_rotateQuat(m, v);
} else if ((top - index) == 3 && lua_type(L, top) == LUA_TNUMBER) {
float angle = luax_checkfloat(L, index++);
float ax = luax_checkfloat(L, index++);
float ay = luax_checkfloat(L, index++);
float az = luax_checkfloat(L, index++);
mat4_rotate(m, angle, ax, ay, az);
} else {
if (vectorType == V_VEC3) {
m[0] = v[0];
m[5] = v[1];
m[10] = v[2];
index++;
} else if (lua_type(L, index) == LUA_TNUMBER) {
m[0] = luax_checkfloat(L, index++);
m[5] = luax_checkfloat(L, index++);
m[10] = luax_checkfloat(L, index++);
}
float rotation[4];
luax_readquat(L, index, rotation, NULL);
mat4_rotateQuat(m, rotation);
}
}
}
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Mul(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
VectorType type;
float* n = luax_tovector(L, 2, &type);
if (n && type == V_MAT4) {
mat4_mul(m, n);
lua_settop(L, 1);
return 1;
} else if (n && type == V_VEC3) {
mat4_transform(m, n);
lua_settop(L, 2);
return 1;
} else if (n && type == V_VEC4) {
mat4_mulVec4(m, n);
lua_settop(L, 2);
return 1;
} else if (lua_type(L, 2) == LUA_TNUMBER) {
float x = luaL_checknumber(L, 2);
float y = luaL_optnumber(L, 3, 0.f);
float z = luaL_optnumber(L, 4, 0.f);
float v[4] = { x, y, z };
mat4_transform(m, v);
lua_pushnumber(L, v[0]);
lua_pushnumber(L, v[1]);
lua_pushnumber(L, v[2]);
return 3;
} else {
return luax_typeerror(L, 2, "mat4, vec3, or number");
}
}
static int l_lovrMat4Identity(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
mat4_identity(m);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Invert(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
mat4_invert(m);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Transpose(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
mat4_transpose(m);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Translate(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
mat4_translate(m, luax_checkfloat(L, 2), luax_checkfloat(L, 3), luax_checkfloat(L, 4));
} else {
float* v = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
mat4_translate(m, v[0], v[1], v[2]);
}
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Rotate(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
mat4_rotate(m, luax_checkfloat(L, 2), luax_optfloat(L, 3, 0.f), luax_optfloat(L, 4, 1.f), luax_optfloat(L, 5, 0.f));
} else {
float* q = luax_checkvector(L, 2, V_QUAT, "quat or number");
mat4_rotateQuat(m, q);
}
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Scale(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
float x = luax_checkfloat(L, 2);
mat4_scale(m, x, luax_optfloat(L, 3, x), luax_optfloat(L, 4, x));
} else {
float* s = luax_checkvector(L, 2, V_VEC3, "vec3 or number");
mat4_scale(m, s[0], s[1], s[2]);
}
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Orthographic(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
float left = luax_checkfloat(L, 2);
float right = luax_checkfloat(L, 3);
float top = luax_checkfloat(L, 4);
float bottom = luax_checkfloat(L, 5);
float clipNear = luax_checkfloat(L, 6);
float clipFar = luax_checkfloat(L, 7);
mat4_orthographic(m, left, right, top, bottom, clipNear, clipFar);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Perspective(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
float clipNear = luax_checkfloat(L, 2);
float clipFar = luax_checkfloat(L, 3);
float fov = luax_checkfloat(L, 4);
float aspect = luax_checkfloat(L, 5);
mat4_perspective(m, clipNear, clipFar, fov, aspect);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Fov(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
float left = luax_checkfloat(L, 2);
float right = luax_checkfloat(L, 3);
float up = luax_checkfloat(L, 4);
float down = luax_checkfloat(L, 5);
float clipNear = luax_checkfloat(L, 6);
float clipFar = luax_checkfloat(L, 7);
mat4_fov(m, left, right, up, down, clipNear, clipFar);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4LookAt(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
vec3 from = luax_checkvector(L, 2, V_VEC3, NULL);
vec3 to = luax_checkvector(L, 3, V_VEC3, NULL);
vec3 up = lua_isnoneornil(L, 4) ? (float[4]) { 0.f, 1.f, 0.f } : luax_checkvector(L, 4, V_VEC3, NULL);
mat4_lookAt(m, from, to, up);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4Target(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
vec3 from = luax_checkvector(L, 2, V_VEC3, NULL);
vec3 to = luax_checkvector(L, 3, V_VEC3, NULL);
vec3 up = lua_isnoneornil(L, 4) ? (float[4]) { 0.f, 1.f, 0.f } : luax_checkvector(L, 4, V_VEC3, NULL);
mat4_target(m, from, to, up);
lua_settop(L, 1);
return 1;
}
static int l_lovrMat4__mul(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
VectorType type;
float* n = luax_tovector(L, 2, &type);
if (!n || (type == V_VEC2 || type == V_QUAT)) return luax_typeerror(L, 2, "mat4, vec3, or vec4");
if (type == V_MAT4) {
mat4 out = luax_newtempvector(L, V_MAT4);
mat4_mul(mat4_init(out, m), n);
} else if (type == V_VEC3) {
vec3 out = luax_newtempvector(L, V_VEC3);
vec3_init(out, n);
mat4_transform(m, out);
} else if (type == V_VEC4) {
float* out = luax_newtempvector(L, V_VEC4);
memcpy(out, n, 4 * sizeof(float));
mat4_mulVec4(m, out);
} else {
lovrThrow("Unreachable");
}
return 1;
}
static int l_lovrMat4__tostring(lua_State* L) {
luax_checkvector(L, 1, V_MAT4, NULL);
lua_pushliteral(L, "mat4");
return 1;
}
static int l_lovrMat4__newindex(lua_State* L) {
mat4 m = luax_checkvector(L, 1, V_MAT4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index >= 1 && index <= 16) {
m[index - 1] = luax_checkfloat(L, 3);
return 0;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to assign property %s of mat4 (invalid property)", lua_tostring(L, -1));
}
static int l_lovrMat4__index(lua_State* L) {
if (lua_type(L, 1) == LUA_TUSERDATA) {
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
if (!lua_isnil(L, -1)) {
return 1;
} else {
lua_pop(L, 2);
}
}
float* m = luax_checkvector(L, 1, V_MAT4, NULL);
if (lua_type(L, 2) == LUA_TNUMBER) {
int index = lua_tointeger(L, 2);
if (index >= 1 && index <= 16) {
lua_pushnumber(L, m[index - 1]);
return 1;
}
}
lua_getglobal(L, "tostring");
lua_pushvalue(L, 2);
lua_call(L, 1, 1);
return luaL_error(L, "attempt to index field %s of mat4 (invalid property)", lua_tostring(L, -1));
}
const luaL_Reg lovrMat4[] = {
{ "equals", l_lovrMat4Equals },
{ "unpack", l_lovrMat4Unpack },
{ "set", l_lovrMat4Set },
{ "mul", l_lovrMat4Mul },
{ "identity", l_lovrMat4Identity },
{ "invert", l_lovrMat4Invert },
{ "transpose", l_lovrMat4Transpose },
{ "translate", l_lovrMat4Translate },
{ "rotate", l_lovrMat4Rotate },
{ "scale", l_lovrMat4Scale },
{ "orthographic", l_lovrMat4Orthographic },
{ "perspective", l_lovrMat4Perspective },
{ "fov", l_lovrMat4Fov },
{ "lookAt", l_lovrMat4LookAt },
{ "target", l_lovrMat4Target },
{ "__mul", l_lovrMat4__mul },
{ "__tostring", l_lovrMat4__tostring },
{ "__newindex", l_lovrMat4__newindex },
{ "__index", l_lovrMat4__index },
{ NULL, NULL }
};