You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1345 lines
32 KiB
C

#define LUA_LIB
#include <lua.h>
#include <lauxlib.h>
#include <math.h>
#include <float.h>
#ifndef _MSC_VER
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif
#endif // !_MSC_VER
#ifndef lua_newuserdata
// lua_newuserdata is a macro in Lua 5.4
#define lua_newuserdatauv(L, sz, n) lua_newuserdata(L,sz)
#endif
#include "string.h"
#include "linalg.h"
#include "math3d.h"
#include "math3dfunc.h"
#define MAT_PERSPECTIVE 0
#define MAT_ORTHO 1
static int g_default_homogeneous_depth = 0;
int
math3d_homogeneous_depth() {
return g_default_homogeneous_depth;
}
static inline void *
STACKID(int64_t id) {
return (void *)id;
}
static inline void *
REFID(struct refobject *R) {
return STACKID(R->id);
}
static inline int64_t
LUAID(lua_State *L, int index) {
luaL_checktype(L, index, LUA_TLIGHTUSERDATA);
void * ud = lua_touserdata(L, index);
return (int64_t)ud;
}
static inline struct lastack *
GETLS(lua_State *L) {
return (struct lastack *)lua_touserdata(L, lua_upvalueindex(1));
}
static void
finalize(lua_State *L, lua_CFunction gc) {
lua_createtable(L, 0, 1);
lua_pushcfunction(L, gc);
lua_setfield(L, -2, "__gc");
lua_setmetatable(L, -2);
}
static int
boxstack_gc(lua_State *L) {
struct boxstack *bs = lua_touserdata(L, 1);
if (bs->LS) {
lastack_delete(bs->LS);
bs->LS = NULL;
}
return 0;
}
static int64_t
get_id(lua_State *L, int index, int ltype) {
if (ltype == LUA_TLIGHTUSERDATA) {
return (int64_t)lua_touserdata(L, index);
} else if (ltype == LUA_TUSERDATA) {
if (lua_rawlen(L, index) != sizeof(struct refobject)) {
luaL_error(L, "Invalid ref userdata");
}
struct refobject * ref = lua_touserdata(L, index);
return ref->id;
}
return luaL_argerror(L, index, "Need userdata");
}
static int
lref(lua_State *L) {
lua_settop(L, 1);
struct refobject * R = lua_newuserdatauv(L, sizeof(struct refobject), 0);
if (lua_isnil(L, 1)) {
R->id = 0;
} else {
int64_t id = get_id(L, 1, lua_type(L, 1));
R->id = lastack_mark(GETLS(L), id);
}
lua_pushvalue(L, lua_upvalueindex(2));
lua_setmetatable(L, -2);
return 1;
}
static int64_t
assign_id(lua_State *L, struct lastack *LS, int index, int mtype, int ltype) {
switch (ltype) {
case LUA_TNIL:
case LUA_TNONE:
// identity matrix
return lastack_constant(mtype);
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA: {
int64_t id = get_id(L, index, ltype);
int type;
const float * v = lastack_value(LS, id, &type);
if (type != mtype && v) {
if (mtype == LINEAR_TYPE_MAT && type == LINEAR_TYPE_QUAT) {
math3d_quat_to_matrix(LS, v);
id = lastack_pop(LS);
} else if (mtype == LINEAR_TYPE_QUAT && type == LINEAR_TYPE_MAT) {
math3d_matrix_to_quat(LS, v);
id = lastack_pop(LS);
} else {
return luaL_error(L, "%s type mismatch %s", lastack_typename(mtype), lastack_typename(type));
}
}
return lastack_mark(LS, id); }
default:
return luaL_error(L, "Invalid type %s for %s ref", lua_typename(L, ltype), lastack_typename(mtype));
}
}
static void
unpack_numbers(lua_State *L, int index, float *v, int n) {
int i;
for (i=0;i<n;i++) {
if (lua_geti(L, index, i+1) != LUA_TNUMBER) {
luaL_error(L, "Need a number from index %d", i+1);
}
v[i] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
}
typedef int64_t (*from_table_func)(lua_State *L, struct lastack *LS, int index);
static int64_t
vector_from_table(lua_State *L, struct lastack *LS, int index) {
int n = lua_rawlen(L, index);
if (n != 3 && n != 4)
return luaL_error(L, "Vector need a array of 3/4 (%d)", n);
float v[4];
v[3] = 1.0f;
unpack_numbers(L, index, v, n);
lastack_pushvec4(LS, v);
return lastack_pop(LS);
}
static const float *
object_from_index(lua_State *L, struct lastack *LS, int index, int mtype, from_table_func from_table) {
int ltype = lua_type(L, index);
const float * result = NULL;
switch(ltype) {
case LUA_TNIL:
case LUA_TNONE:
break;
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA: {
int64_t id = get_id(L, index, ltype);
int type;
result = lastack_value(LS, id, &type);
if (result == NULL || type != mtype) {
luaL_error(L, "Need a %s , it's a %s.", lastack_typename(mtype), result == NULL ? "invalid" : lastack_typename(type));
}
break; }
case LUA_TTABLE:
result = lastack_value(LS, from_table(L, LS, index), NULL);
break;
default:
luaL_error(L, "Invalid lua type %s", lua_typename(L, ltype));
}
return result;
}
static const float *
object_from_field(lua_State *L, struct lastack *LS, int index, const char *key, int mtype, from_table_func from_table) {
lua_getfield(L, index, key);
const float * result = object_from_index(L, LS, -1, mtype, from_table);
lua_pop(L, 1);
return result;
}
static int
quat_from_axis(lua_State *L, struct lastack *LS, int index, const char *key) {
if (lua_getfield(L, index, key) == LUA_TNIL) {
lua_pop(L, 1);
return 1;
}
const float * axis = object_from_index(L, LS, -1, LINEAR_TYPE_VEC4, vector_from_table);
lua_pop(L, 1);
if (lua_getfield(L, index, "r") != LUA_TNUMBER) {
return luaL_error(L, "Need .r for quat");
}
float r = lua_tonumber(L, -1);
lua_pop(L, 1);
math3d_make_quat_from_axis(LS, axis, r);
return 0;
}
static int64_t
quat_from_table(lua_State *L, struct lastack *LS, int index) {
int n = lua_rawlen(L, index);
if (n == 0) {
if (quat_from_axis(L, LS, index, "axis"))
return luaL_error(L, "Quat invalid arguments");
} else if (n == 3) {
float e[3];
unpack_numbers(L, index, e, 3);
math3d_make_quat_from_euler(LS, e[0], e[1], e[2]);
} else if (n == 4) {
float v[4];
unpack_numbers(L, index, v, 4);
lastack_pushquat(LS, v);
} else {
return luaL_error(L, "Quat need a array of 4 (quat) or 3 (eular), it's (%d)", n);
}
return lastack_pop(LS);
}
static int64_t
matrix_from_table(lua_State *L, struct lastack *LS, int index) {
int n = lua_rawlen(L, index);
if (n == 0) {
const float *s;
float tmp[4];
if (lua_getfield(L, index, "s") == LUA_TNUMBER) {
tmp[0] = lua_tonumber(L, -1);
tmp[1] = tmp[0];
tmp[2] = tmp[0];
tmp[3] = 0;
s = tmp;
} else {
s = object_from_index(L, LS, -1, LINEAR_TYPE_VEC4, vector_from_table);
}
lua_pop(L, 1);
const float *q = object_from_field(L, LS, index, "r", LINEAR_TYPE_QUAT, quat_from_table);
const float *t = object_from_field(L, LS, index, "t", LINEAR_TYPE_VEC4, vector_from_table);
math3d_make_srt(LS,s,q,t);
} else if (n != 16) {
return luaL_error(L, "Matrix need a array of 16 (%d)", n);
} else {
float v[16];
unpack_numbers(L, index, v, 16);
lastack_pushmatrix(LS, v);
}
return lastack_pop(LS);
}
static int64_t
assign_object(lua_State *L, struct lastack *LS, int index, int mtype, from_table_func from_table) {
int ltype = lua_type(L, index);
if (ltype == LUA_TTABLE) {
int64_t id = from_table(L, LS, index);
return lastack_mark(LS, id);
}
return assign_id(L, LS, index, mtype, ltype);
}
static int64_t
assign_matrix(lua_State *L, struct lastack *LS, int index) {
return assign_object(L, LS, index, LINEAR_TYPE_MAT, matrix_from_table);
}
static int64_t
assign_vector(lua_State *L, struct lastack *LS, int index) {
return assign_object(L, LS, index, LINEAR_TYPE_VEC4, vector_from_table);
}
static int64_t
assign_quat(lua_State *L, struct lastack *LS, int index) {
return assign_object(L, LS, index, LINEAR_TYPE_QUAT, quat_from_table);
}
static inline void
copy_matrix(lua_State *L, struct lastack *LS, int64_t id, float result[64]) {
int type;
const float *mat = lastack_value(LS, id, &type);
if (mat == NULL || type != LINEAR_TYPE_MAT)
luaL_error(L, "Need a matrix to decompose, it's a %s.", mat == NULL ? "None" : lastack_typename(type));
memcpy(result, mat, 16 * sizeof(float));
}
static int64_t
assign_scale(lua_State *L, struct lastack *LS, int index, int64_t oid) {
float mat[64];
float quat[4];
float tmp[4];
const float * scale = NULL;
if (lua_type(L, index) == LUA_TNUMBER) {
float us = lua_tonumber(L, index);
if (us != 1.0f) {
tmp[0] = tmp[1] = tmp[2] = us;
tmp[3] = 0;
scale = tmp;
}
} else {
scale = object_from_index(L, LS, index, LINEAR_TYPE_VEC4, vector_from_table);
}
copy_matrix(L, LS, oid, mat);
float *trans = &mat[3*4];
math3d_decompose_rot(mat, quat);
math3d_make_srt(LS, scale, quat, trans);
return lastack_mark(LS, lastack_pop(LS));
}
static int64_t
assign_rot(lua_State *L, struct lastack *LS, int index, int64_t oid) {
float mat[64];
float scale[4];
copy_matrix(L, LS, oid, mat);
math3d_decompose_scale(mat, scale);
float *trans = &mat[3*4];
const float * quat = object_from_index(L, LS, index, LINEAR_TYPE_QUAT, quat_from_table);
math3d_make_srt(LS, scale, quat, trans);
return lastack_mark(LS, lastack_pop(LS));
}
static int64_t
assign_trans(lua_State *L, struct lastack *LS, int index, int64_t oid) {
float mat[64];
copy_matrix(L, LS, oid, mat);
const float * t = object_from_index(L, LS, index, LINEAR_TYPE_VEC4, vector_from_table);
if (t == NULL) {
mat[3*4+0] = 0;
mat[3*4+1] = 0;
mat[3*4+2] = 0;
mat[3*4+3] = 1;
} else {
mat[3*4+0] = t[0];
mat[3*4+1] = t[1];
mat[3*4+2] = t[2];
mat[3*4+3] = 1;
}
lastack_pushmatrix(LS, mat);
return lastack_mark(LS, lastack_pop(LS));
}
static int
lref_setter(lua_State *L) {
struct refobject *R = lua_touserdata(L, 1);
const char *key = luaL_checkstring(L, 2);
struct lastack *LS = GETLS(L);
int64_t oid = R->id;
switch(key[0]) {
case 'i': // value id
R->id = lastack_mark(LS, get_id(L, 3, lua_type(L, 3)));
break;
case 'v': // should be vector
R->id = assign_vector(L, LS, 3);
break;
case 'q': // should be quat
R->id = assign_quat(L, LS, 3);
break;
case 'm': // should be matrix
R->id = assign_matrix(L, LS, 3);
break;
case 's':
R->id = assign_scale(L, LS, 3, oid);
break;
case 'r':
R->id = assign_rot(L, LS, 3, oid);
break;
case 't':
R->id = assign_trans(L, LS, 3, oid);
break;
default:
return luaL_error(L, "Invalid set key %s with ref object", key);
}
// we must unmark old id after assign, because 'v.i = v'
lastack_unmark(LS, oid);
return 0;
}
static void
to_table(lua_State *L, struct lastack *LS, int64_t id) {
int type;
const float * v = lastack_value(LS, id, &type);
if (v == NULL) {
lua_pushnil(L);
return;
}
int n = lastack_typesize(type);
int i;
lua_createtable(L, n, 1);
for (i=0;i<n;i++) {
lua_pushnumber(L, v[i]);
lua_rawseti(L, -2, i+1);
}
lua_pushstring(L, lastack_typename(type));
lua_setfield(L, -2, "type");
}
static int64_t
extract_srt(struct lastack *LS, const float *mat, int what) {
float v[4];
switch(what) {
case 's':
math3d_decompose_scale(mat, v);
lastack_pushvec4(LS, v);
break;
case 'r':
math3d_decompose_rot(mat, v);
lastack_pushquat(LS, v);
break;
case 't':
v[0] = mat[3*4+0];
v[1] = mat[3*4+1];
v[2] = mat[3*4+2];
v[3] = 1;
lastack_pushvec4(LS, v);
break;
default:
return 0;
}
return lastack_pop(LS);
}
static int
ref_get_key(lua_State *L) {
struct refobject *R = lua_touserdata(L, 1);
struct lastack * LS = GETLS(L);
const char *key = lua_tostring(L, 2);
switch(key[0]) {
case 'i':
lua_pushlightuserdata(L, REFID(R));
break;
case 'p':
lua_pushlightuserdata(L, (void *)(lastack_value(LS, R->id, NULL)));
break;
case 'v':
to_table(L, LS, R->id);
break;
case 's':
case 'r':
case 't': {
int type;
const float *m = lastack_value(LS, R->id, &type);
if (m == NULL || type != LINEAR_TYPE_MAT)
return luaL_error(L, "Not a matrix");
lua_pushlightuserdata(L, STACKID(extract_srt(LS, m ,key[0])));
break; }
default:
return luaL_error(L, "Invalid get key %s with ref object", key);
}
return 1;
}
static int
index_object(lua_State *L, struct lastack *LS, int64_t id, int idx) {
int type;
const float * v = lastack_value(LS, id, &type);
if (v == NULL) {
return luaL_error(L, "Invalid ref object");
}
if (idx < 1 || idx > 4) {
return luaL_error(L, "Invalid index %d", idx);
}
--idx;
switch (type) {
case LINEAR_TYPE_MAT:
lastack_pushvec4(LS, &v[idx*4]);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
break;
case LINEAR_TYPE_VEC4:
lua_pushnumber(L, v[idx]);
break;
case LINEAR_TYPE_QUAT:
lua_pushnumber(L, v[idx]);
break;
default:
return 0;
}
return 1;
}
static int
ref_get_number(lua_State *L) {
struct refobject *R = lua_touserdata(L, 1);
struct lastack * LS = GETLS(L);
int idx = lua_tointeger(L, 2);
int type;
const float * v = lastack_value(LS, R->id, &type);
if (v == NULL) {
return luaL_error(L, "Invalid ref object");
}
return index_object(L, LS, R->id, idx);
}
static int
lindex(lua_State *L) {
int64_t id = get_id(L, 1, lua_type(L, 1));
int idx = luaL_checkinteger(L, 2);
return index_object(L, GETLS(L), id, idx);
}
static int
lref_getter(lua_State *L) {
int type = lua_type(L, 2);
switch (type) {
case LUA_TNUMBER:
return ref_get_number(L);
case LUA_TSTRING:
return ref_get_key(L);
default:
return luaL_error(L, "Invalid key type %s", lua_typename(L, type));
}
}
static int
id_tostring(lua_State *L, int64_t id) {
int type;
const float * v = lastack_value(GETLS(L), id, &type);
if (v == NULL) {
lua_pushstring(L, "Invalid");
return 1;
}
switch (type) {
case LINEAR_TYPE_MAT:
lua_pushfstring(L, "MAT (%f,%f,%f,%f : %f,%f,%f,%f : %f,%f,%f,%f : %f,%f,%f,%f)",
v[0],v[1],v[2],v[3],
v[4],v[5],v[6],v[7],
v[8],v[9],v[10],v[11],
v[12],v[13],v[14],v[15]);
break;
case LINEAR_TYPE_VEC4:
lua_pushfstring(L, "VEC4 (%f,%f,%f,%f)",
v[0], v[1], v[2], v[3]);
break;
case LINEAR_TYPE_QUAT:
lua_pushfstring(L, "QUAT (%f,%f,%f,%f)",
v[0], v[1], v[2], v[3]);
break;
default:
lua_pushstring(L, "Unknown");
break;
}
return 1;
}
static int
lref_tostring(lua_State *L) {
struct refobject *R = lua_touserdata(L, 1);
if (R->id == 0) {
lua_pushstring(L, "Null");
return 1;
}
return id_tostring(L, R->id);
}
static int
ltostring(lua_State *L) {
int64_t id = get_id(L, 1, lua_type(L, 1));
return id_tostring(L, id);
}
static int
lref_gc(lua_State *L) {
struct refobject *R = lua_touserdata(L, 1);
if (R->id) {
lastack_unmark(GETLS(L), R->id);
R->id = 0;
}
return 0;
}
static int
new_object(lua_State *L, int type, from_table_func from_table, int narray) {
int argn = lua_gettop(L);
int64_t id;
if (argn == narray) {
int i;
float tmp[16];
struct lastack *LS = GETLS(L);
for (i=0;i<argn;i++) {
tmp[i] = luaL_checknumber(L, i+1);
}
lastack_pushobject(LS, tmp, type);
id = lastack_pop(LS);
} else {
switch(argn) {
case 0:
id = lastack_constant(type);
break;
case 1: {
int ltype = lua_type(L, 1);
struct lastack *LS = GETLS(L);
if (ltype == LUA_TTABLE) {
id = from_table(L, LS, 1);
} else {
id = get_id(L,1,ltype);
if (lastack_type(LS, id) != type) {
return luaL_error(L, "type mismatch %s %s", lastack_typename(type), lastack_type(LS, id));
}
}
break; }
default:
return luaL_error(L, "Invalid %s argument number %d", lastack_typename(type), argn);
}
}
lua_pushlightuserdata(L, STACKID(id));
return 1;
}
static int
lreset(lua_State *L) {
lastack_reset(GETLS(L));
return 0;
}
static const float *
get_object(lua_State *L, struct lastack *LS, int index, int *type) {
int ltype = lua_type(L, index);
if (ltype == LUA_TNUMBER) {
float n[4] = { lua_tonumber(L, index),0,0,0 };
lastack_pushvec4(LS, n);
*type = LINEAR_TYPE_NUM;
return lastack_value(LS, lastack_pop(LS), NULL);
}
int64_t id = get_id(L, index, ltype);
const float * v = lastack_value(LS, id, type);
if (v == NULL)
luaL_error(L, "Invalid id at stack %d", index);
return v;
}
static int
lmul(lua_State *L) {
int top = lua_gettop(L);
struct lastack *LS = GETLS(L);
int i;
float tmp[16];
int lt,rt;
if (top < 2) {
return luaL_error(L, "Need 2 or more objects");
}
const float *lv = get_object(L, LS, 1, &lt);
for (i=2;i<=top;i++) {
const float *rv = get_object(L, LS, i, &rt);
int result_type = math3d_mul_object(LS, lv, rv, lt, rt, tmp);
if (result_type == LINEAR_TYPE_NONE) {
return luaL_error(L, "Invalid mul arguments at %d, ltype = %d rtype = %d\nmatrix or quaternion mul vector should use 'transform' function", i, lt, rt);
}
lt = result_type;
lv = tmp;
}
lastack_pushobject(LS, tmp, lt);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static const float *
vector_from_index(lua_State *L, struct lastack *LS, int index) {
const float * v = object_from_index(L, LS, index, LINEAR_TYPE_VEC4, vector_from_table);
if (v == NULL)
luaL_error(L, "Need a vector");
return v;
}
static int
ladd(lua_State *L) {
struct lastack *LS = GETLS(L);
int i;
float tmp[4];
int top = lua_gettop(L);
if (top < 2) {
return luaL_error(L, "Need 2 or more vectors");
}
const float *lv = vector_from_index(L, LS, 1);
for (i=2;i<=top;i++) {
const float *rv = vector_from_index(L, LS, 2);
math3d_add_vec(LS, lv, rv, tmp);
lv = tmp;
}
lastack_pushvec4(LS, tmp);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lsub(lua_State *L) {
struct lastack *LS = GETLS(L);
float tmp[4];
const float *v0 = vector_from_index(L, LS, 1);
const float *v1 = vector_from_index(L, LS, 2);
math3d_sub_vec(LS, v0, v1, tmp);
lastack_pushvec4(LS, tmp);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lmuladd(lua_State *L){
struct lastack *LS = GETLS(L);
int ltype, rtype;
const float *v0 = get_object(L, LS, 1, &ltype);
if (ltype != LINEAR_TYPE_NUM && ltype != LINEAR_TYPE_VEC4){
return luaL_error(L, "argument 1 must be number/vec4:%s", lastack_typename(ltype));
}
const float *v1 = get_object(L, LS, 2, &rtype);
if (rtype != LINEAR_TYPE_NUM && rtype != LINEAR_TYPE_VEC4){
return luaL_error(L, "argument 1 must be number/vec4:%s", lastack_typename(rtype));
}
if ((ltype == LINEAR_TYPE_NUM && rtype == LINEAR_TYPE_VEC4) ||
(ltype == LINEAR_TYPE_VEC4 && rtype == LINEAR_TYPE_NUM) ||
(ltype == LINEAR_TYPE_VEC4 && rtype == LINEAR_TYPE_VEC4)){
float result[16];
math3d_mul_object(LS, v0, v1, ltype, rtype, result);
const float * v2 = vector_from_index(L, LS, 3);
math3d_add_vec(LS, result, v2, result);
lastack_pushvec4(LS, result);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
return luaL_error(L, "argumen 1/2 must be one of them as vec4, argument 1:%s, argument 2:%s", lastack_typename(ltype), lastack_typename(rtype));
}
static const float *
matrix_from_index(lua_State *L, struct lastack *LS, int index) {
const float * m = object_from_index(L, LS, index, LINEAR_TYPE_MAT, matrix_from_table);
if (m == NULL)
luaL_error(L, "Need a vector");
return m;
}
static const float *
quat_from_index(lua_State *L, struct lastack *LS, int index) {
const float * q = object_from_index(L, LS, index, LINEAR_TYPE_QUAT, quat_from_table);
if (q == NULL)
luaL_error(L, "Need a quat");
return q;
}
static int64_t
object_to_quat(lua_State *L, struct lastack *LS, int index) {
int64_t id = get_id(L, index, lua_type(L, index));
int type;
const float * v = lastack_value(LS, id, &type);
switch(type) {
case LINEAR_TYPE_MAT:
math3d_matrix_to_quat(LS, v);
break;
case LINEAR_TYPE_QUAT:
return id;
case LINEAR_TYPE_VEC4:
math3d_make_quat_from_euler(LS, v[0], v[1], v[2]);
break;
default:
return luaL_error(L, "Invalid type %s for quat", lastack_typename(type));
}
return lastack_pop(LS);
}
static int
lmatrix(lua_State *L) {
if (lua_isuserdata(L, 1)) {
struct lastack *LS = GETLS(L);
int64_t id = get_id(L, 1, lua_type(L, 1));
int type;
const float * quat = lastack_value(LS, id, &type);
if (quat && type == LINEAR_TYPE_QUAT) {
math3d_quat_to_matrix(LS, quat);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
}
return new_object(L, LINEAR_TYPE_MAT, matrix_from_table, 16);
}
static int
lvector(lua_State *L) {
int top = lua_gettop(L);
if (top == 3) {
lua_pushnumber(L, 0.0f);
} else if (top == 2) {
struct lastack *LS = GETLS(L);
if (!lua_isuserdata(L, 1)) {
return luaL_error(L, "Should be (vector id , number)");
}
int64_t id = get_id(L, 1, lua_type(L, 1));
int type;
const float *vec3 = lastack_value(LS, id, &type);
if (vec3 == NULL || type != LINEAR_TYPE_VEC4) {
return luaL_error(L, "Need a vector, it's %s", vec3 == NULL? "Invalid" : lastack_typename(id));
}
float n4 = luaL_checknumber(L, 2);
if (n4 == vec3[3]) {
lua_pushlightuserdata(L, STACKID(id));
} else {
float vec4 [4] = { vec3[0], vec3[1], vec3[2], n4 };
lastack_pushvec4(LS, vec4);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
}
return 1;
}
return new_object(L, LINEAR_TYPE_VEC4, vector_from_table, 4);
}
static int
lquaternion(lua_State *L) {
if (lua_isuserdata(L, 1)) {
int64_t id = object_to_quat(L, GETLS(L), 1);
lua_pushlightuserdata(L, STACKID(id));
return 1;
}
return new_object(L, LINEAR_TYPE_QUAT, quat_from_table, 4);
}
static int
lsrt(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * mat = matrix_from_index(L, LS, 1);
math3d_decompose_matrix(LS, mat);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 3;
}
static int
llength(lua_State *L) {
const float * v3 = vector_from_index(L, GETLS(L), 1);
lua_pushnumber(L, math3d_length(v3));
return 1;
}
static int
lfloor(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * v = vector_from_index(L, LS, 1);
math3d_floor(LS, v);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lceil(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * v = vector_from_index(L, LS, 1);
math3d_ceil(LS, v);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
ldot(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * v1 = vector_from_index(L, LS, 1);
const float * v2 = vector_from_index(L, LS, 2);
lua_pushnumber(L, math3d_dot(v1,v2));
return 1;
}
static int
lcross(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * v1 = vector_from_index(L, LS, 1);
const float * v2 = vector_from_index(L, LS, 2);
math3d_cross(LS, v1, v2);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lnormalize(lua_State *L) {
int type;
struct lastack *LS = GETLS(L);
const float *v = get_object(L, LS, 1, &type);
switch (type) {
case LINEAR_TYPE_VEC4:
math3d_normalize_vector(LS, v);
break;
case LINEAR_TYPE_QUAT:
math3d_normalize_quat(LS, v);
break;
default:
return luaL_error(L, "normalize don't support %s", lastack_typename(type));
}
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
ltranspose(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * mat = matrix_from_index(L, LS, 1);
math3d_transpose_matrix(LS, mat);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
linverse(lua_State *L) {
int type;
struct lastack *LS = GETLS(L);
const float *v = get_object(L, LS, 1, &type);
switch (type) {
case LINEAR_TYPE_VEC4: {
float iv[4] = { -v[0], -v[1], -v[2], v[3] };
lastack_pushvec4(LS, iv);
break; }
case LINEAR_TYPE_QUAT:
math3d_inverse_quat(LS, v);
break;
case LINEAR_TYPE_MAT:
math3d_inverse_matrix(LS, v);
break;
default:
return luaL_error(L, "inverse don't support %s", lastack_typename(type));
}
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
llookat(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * eye = vector_from_index(L, LS, 1);
const float * at = vector_from_index(L, LS, 2);
const float * up = object_from_index(L, LS, 3, LINEAR_TYPE_VEC4, vector_from_table);
math3d_lookat_matrix(LS, 0, eye, at, up);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
llookto(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * eye = vector_from_index(L, LS, 1);
const float * at = vector_from_index(L, LS, 2);
const float * up = object_from_index(L, LS, 3, LINEAR_TYPE_VEC4, vector_from_table);
math3d_lookat_matrix(LS, 1, eye, at, up);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lreciprocal(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * v = vector_from_index(L, LS, 1);
math3d_reciprocal(LS, v);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
ltodirection(lua_State *L) {
struct lastack *LS = GETLS(L);
int type;
const float *v = get_object(L, LS, 1, &type);
switch (type) {
case LINEAR_TYPE_QUAT:
math3d_quat_to_viewdir(LS, v);
break;
case LINEAR_TYPE_MAT:
math3d_rotmat_to_viewdir(LS, v);
break;
default:
return luaL_error(L, "todirection don't support %s", lastack_typename(type));
}
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
ltorotation(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * v = vector_from_index(L, LS, 1);
math3d_viewdir_to_quat(LS, v);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
ltotable(lua_State *L){
struct lastack *LS = GETLS(L);
int64_t id = get_id(L, 1, lua_type(L, 1));
to_table(L, LS, id);
return 1;
}
static int
lbase_axes(lua_State *L) {
struct lastack *LS = GETLS(L);
const float *forward = vector_from_index(L, LS, 1);
math3d_base_axes(LS, forward);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS))); // right
lua_pushlightuserdata(L, STACKID(lastack_pop(LS))); // up
return 2;
}
static int
ltransform(lua_State *L){
struct lastack *LS = GETLS(L);
const int64_t rotatorid = get_id(L, 1, lua_type(L, 1));
if (lua_isnone(L, 3)){
return luaL_error(L, "need argument 3, 0 for vector, 1 for point, 'nil' will use vector[4] value");
}
const float* v = vector_from_index(L, LS, 2);
float tmp[4];
if (!lua_isnil(L, 3)){
const float p = luaL_checknumber(L, 3);
if (p != v[3]) {
tmp[0] = v[0];
tmp[1] = v[1];
tmp[2] = v[2];
tmp[3] = p;
v = tmp;
}
}
int type;
const float *rotator = (const float *)lastack_value(LS, rotatorid, &type);
if (rotator == NULL) {
return luaL_error(L, "Invalid rotator id");
}
switch (type){
case LINEAR_TYPE_QUAT:
math3d_quat_transform(LS, rotator, v);
break;
case LINEAR_TYPE_MAT:
math3d_rotmat_transform(LS, rotator, v);
break;
default:
return luaL_error(L, "only support quat/mat for rotate vector:%s", lastack_typename(type));
}
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
ltransform_homogeneous_point(lua_State *L) {
struct lastack *LS = GETLS(L);
const float * mat = matrix_from_index(L, LS, 1);
const float * vec = vector_from_index(L, LS, 2);
math3d_mulH(LS, mat, vec);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static void
create_proj_mat(lua_State *L, struct lastack *LS, int index) {
float left, right, top, bottom;
lua_getfield(L, index, "n");
float near = luaL_optnumber(L, -1, 0.1f);
lua_pop(L, 1);
lua_getfield(L, index, "f");
float far = luaL_optnumber(L, -1, 100.0f);
lua_pop(L, 1);
int mattype = MAT_PERSPECTIVE;
if (lua_getfield(L, index, "fov") == LUA_TNUMBER) {
float fov = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "aspect");
float aspect = luaL_checknumber(L, -1);
lua_pop(L, 1);
float ymax = near * tanf(fov * (M_PI / 360));
float xmax = ymax * aspect;
left = -xmax;
right = xmax;
bottom = -ymax;
top = ymax;
} else {
lua_pop(L, 1); //pop "fov"
lua_getfield(L, index, "ortho");
if (lua_toboolean(L, -1)) {
mattype = MAT_ORTHO;
}
lua_pop(L, 1); //pop "ortho"
lua_getfield(L, index, "l");
left = luaL_checknumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "r");
right = luaL_checknumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "b");
bottom = luaL_checknumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "t");
top = luaL_checknumber(L, -1);
lua_pop(L, 1);
}
if (mattype == MAT_PERSPECTIVE) {
math3d_frustumLH(LS, left, right, bottom, top, near, far, g_default_homogeneous_depth);
} else {
math3d_orthoLH(LS, left, right, bottom, top, near, far, g_default_homogeneous_depth);
}
}
static int
lprojmat(lua_State *L) {
struct lastack *LS = GETLS(L);
luaL_checktype(L, 1, LUA_TTABLE);
create_proj_mat(L, LS, 1);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lminmax(lua_State *L){
struct lastack *LS = GETLS(L);
luaL_checktype(L, 1, LUA_TTABLE);
const int numpoints = (int)lua_rawlen(L, 1);
const float* transform = lua_isnoneornil(L, 2) ? NULL : matrix_from_index(L, LS, 2);
float minv[4] = {FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX};
float maxv[4] = {-FLT_MAX, -FLT_MAX, -FLT_MAX, -FLT_MAX};
for (int ii = 0; ii < numpoints; ++ii){
float v[4];
lua_geti(L, 1, ii+1);
unpack_numbers(L, -1, v, 4);
lua_pop(L, 1);
math3d_minmax(LS, transform, v, minv, maxv);
}
lastack_pushvec4(LS, minv);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
lastack_pushvec4(LS, maxv);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 2;
}
static int
llerp(lua_State *L){
struct lastack *LS = GETLS(L);
const float *v0 = vector_from_index(L, LS, 1);
const float *v1 = vector_from_index(L, LS, 1);
const float ratio = luaL_checknumber(L, 3);
float r[4];
math3d_lerp(LS, v0, v1, ratio, r);
lastack_pushvec4(LS, r);
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
static int
lstacksize(lua_State *L) {
struct lastack *LS = GETLS(L);
lua_pushinteger(L, lastack_size(LS));
return 1;
}
static int
lhomogeneous_depth(lua_State *L){
int num = lua_gettop(L);
if (num > 0){
g_default_homogeneous_depth = lua_toboolean(L, 1) != 0;
return 0;
}
lua_pushboolean(L, g_default_homogeneous_depth ? 1 : 0);
return 1;
}
static int
lpack(lua_State *L) {
size_t sz;
const char * format = luaL_checklstring(L, 1, &sz);
int n = lua_gettop(L);
int i;
if (n != 5 && n != 17) {
return luaL_error(L, "need 5 or 17 arguments , it's %d", n);
}
--n;
if (n != sz) {
return luaL_error(L, "Invalid format %s", format);
}
union {
float f[16];
uint32_t n[16];
} u;
for (i=0;i<n;i++) {
switch(format[i]) {
case 'f':
u.f[i] = luaL_checknumber(L, i+2);
break;
case 'd':
u.n[i] = luaL_checkinteger(L, i+2);
break;
default:
return luaL_error(L, "Invalid format %s", format);
}
}
struct lastack *LS = GETLS(L);
if (n == 4) {
lastack_pushvec4(LS, u.f);
} else {
lastack_pushmatrix(LS, u.f);
}
lua_pushlightuserdata(L, STACKID(lastack_pop(LS)));
return 1;
}
// input: view direction vector
// output:
// output radianX and radianY which can used to create quaternion that around x-axis and y-axis,
// multipy those quaternions can recreate view direction vector
static int
ldir2radian(lua_State *L){
struct lastack *LS = GETLS(L);
const float* v = vector_from_index(L, LS, 1);
float radians[2];
math3d_dir2radian(LS, v, radians);
lua_pushnumber(L, radians[0]);
lua_pushnumber(L, radians[1]);
return 2;
}
LUAMOD_API int
luaopen_math3d(lua_State *L) {
luaL_checkversion(L);
struct boxstack * bs = lua_newuserdatauv(L, sizeof(struct boxstack), 0);
bs->LS = lastack_new();
finalize(L, boxstack_gc);
lua_setfield(L, LUA_REGISTRYINDEX, MATH3D_STACK);
luaL_Reg l[] = {
{ "ref", NULL },
{ "tostring", ltostring },
{ "matrix", lmatrix },
{ "vector", lvector },
{ "quaternion", lquaternion },
{ "index", lindex },
{ "reset", lreset },
{ "mul", lmul },
{ "add", ladd },
{ "sub", lsub },
{ "muladd", lmuladd},
{ "srt", lsrt },
{ "length", llength },
{ "floor", lfloor },
{ "ceil", lceil },
{ "dot", ldot },
{ "cross", lcross },
{ "normalize", lnormalize },
{ "transpose", ltranspose },
{ "inverse", linverse },
{ "lookat", llookat },
{ "lookto", llookto },
{ "reciprocal", lreciprocal },
{ "todirection", ltodirection },
{ "torotation", ltorotation },
{ "totable", ltotable},
{ "base_axes", lbase_axes},
{ "transform", ltransform},
{ "transformH", ltransform_homogeneous_point },
{ "projmat", lprojmat },
{ "minmax", lminmax},
{ "lerp", llerp},
{ "dir2radian", ldir2radian},
{ "stacksize", lstacksize},
{ "homogeneous_depth", lhomogeneous_depth },
{ "pack", lpack },
{ NULL, NULL },
};
luaL_newlibtable(L,l);
lua_pushlightuserdata(L, bs->LS);
luaL_setfuncs(L,l,1);
luaL_Reg ref_mt[] = {
{ "__newindex", lref_setter },
{ "__index", lref_getter },
{ "__tostring", lref_tostring },
{ "__gc", lref_gc },
{ NULL, NULL },
};
lua_pushlightuserdata(L, bs->LS);
luaL_newlibtable(L,ref_mt);
lua_pushlightuserdata(L, bs->LS);
luaL_setfuncs(L,ref_mt,1);
lua_pushcclosure(L, lref, 2);
lua_setfield(L, -2, "ref");
return 1;
}
// util function
const float *
math3d_from_lua(lua_State *L, struct lastack *LS, int index, int type) {
switch(type) {
case LINEAR_TYPE_MAT:
return matrix_from_index(L, LS, index);
case LINEAR_TYPE_VEC4:
return vector_from_index(L, LS, index);
case LINEAR_TYPE_QUAT:
return quat_from_index(L, LS, index);
default:
luaL_error(L, "Invalid math3d object type %d", type);
}
return NULL;
}
const float *
math3d_from_lua_id(lua_State *L, struct lastack *LS, int index, int *type) {
int64_t id = get_id(L, index, lua_type(L, index));
*type = LINEAR_TYPE_NONE;
return lastack_value(LS, id, type);
}