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.

539 lines
13 KiB
C

#define LUA_LIB
#include <lua.h>
#include <lauxlib.h>
#include <stdint.h>
#include "math3d.h"
#include "linalg.h"
typedef enum {
SET_Mat = 0x01,
SET_Vec = 0x02,
SET_Array = 0x10,
SET_Unknown,
}StackElemType;
static inline void *
get_pointer(lua_State *L, struct lastack *LS, int index, int type) {
return (void *)math3d_from_lua(L, LS, index, type);
}
static void *
getopt_pointer(lua_State *L, struct lastack *LS, int index, int type) {
if (lua_isnoneornil(L, index)) {
return NULL;
} else {
return get_pointer(L, LS, index, type);
}
}
static void *
get_pointer_variant(lua_State *L, struct lastack *LS, int index, int elemtype) {
if (elemtype & SET_Mat) {
return get_pointer(L, LS, index, LINEAR_TYPE_MAT);
} else {
return get_pointer(L, LS, index, LINEAR_TYPE_VEC4);
}
}
// upvalue1 mathstack
// upvalue2 cfunction
// upvalue3 from
static int
lmatrix_adapter_1(lua_State *L) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
int from = lua_tointeger(L, lua_upvalueindex(3));
void * v = get_pointer(L, LS, from, LINEAR_TYPE_MAT);
lua_settop(L, from-1);
lua_pushlightuserdata(L, v);
return f(L);
}
static int
lmatrix_adapter_2(lua_State *L) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
int from = lua_tointeger(L, lua_upvalueindex(3));
void * v1 = getopt_pointer(L, LS, from, LINEAR_TYPE_MAT);
void * v2 = getopt_pointer(L, LS, from+1, LINEAR_TYPE_MAT);
lua_settop(L, from-1);
if (v1) {
lua_pushlightuserdata(L, v1);
} else {
lua_pushnil(L);
}
if (v2) {
lua_pushlightuserdata(L, v2);
} else {
lua_pushnil(L);
}
return f(L);
}
static int
lmatrix_adapter_var(lua_State *L) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
int from = lua_tointeger(L, lua_upvalueindex(3));
int i;
int top = lua_gettop(L);
for (i=from;i<=top;i++) {
void * v = getopt_pointer(L, LS, i, LINEAR_TYPE_MAT);
if (v) {
lua_pushlightuserdata(L, v);
lua_replace(L, i);
}
}
return f(L);
}
// upvalue1 : userdata mathstack
// cfunction original function
// integer from
// integer n
static int
lbind_matrix(lua_State *L) {
if (!lua_iscfunction(L, 1))
return luaL_error(L, "need a c function");
if (lua_getupvalue(L, 1, 1) != NULL)
luaL_error(L, "Only support light cfunction");
int from = luaL_checkinteger(L, 2);
int n = luaL_optinteger(L, 3, 0);
lua_CFunction f;
switch (n) {
case 0:
f = lmatrix_adapter_var;
break;
case 1:
f = lmatrix_adapter_1;
break;
case 2:
f = lmatrix_adapter_2;
break;
default:
return luaL_error(L, "Only support 1,2,0(vararg) now");
}
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushinteger(L, from);
lua_pushcclosure(L, f, 4);
return 1;
}
static int
lvector(lua_State *L) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
const int from = lua_tointeger(L, lua_upvalueindex(3));
const int top = lua_gettop(L);
int ii;
for (ii = from; ii <= top; ++ii) {
if (!lua_isnil(L, ii)) {
void* p = get_pointer(L, LS, ii, LINEAR_TYPE_VEC4);
lua_pushlightuserdata(L, p);
lua_replace(L, ii);
}
}
return f(L);
}
static int
lbind_vector(lua_State *L) {
if (!lua_iscfunction(L, 1))
return luaL_error(L, "need a c function");
if (lua_getupvalue(L, 1, 1) != NULL)
luaL_error(L, "Only support light cfunction");
luaL_checkinteger(L, 2);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, 1); // cfunction
lua_pushvalue(L, 2); // from
lua_pushcclosure(L, lvector, 3);
return 1;
}
static uint8_t
check_elem_type(lua_State *L, struct lastack *LS, int index) {
if (lua_type(L, index) == LUA_TTABLE) {
const int fieldtype = lua_getfield(L, index, "n");
lua_pop(L, 1);
if (fieldtype != LUA_TNIL){
const int elemtype = lua_geti(L, index, 1);
if (elemtype != LUA_TTABLE) {
int type;
math3d_from_lua_id(L, LS, -1, &type);
lua_pop(L, 1);
return SET_Array | (type == LINEAR_TYPE_MAT ? SET_Mat : SET_Vec);
}
lua_pop(L, 1);
return SET_Array | (lua_rawlen(L, index) >= 12 ? SET_Mat : SET_Vec);
}
return lua_rawlen(L, index) >= 12 ? SET_Mat : SET_Vec;
}
int type;
math3d_from_lua_id(L, LS, index, &type);
return type == LINEAR_TYPE_MAT ? SET_Mat : SET_Vec;
}
static void
unpack_table_on_stack(lua_State *L, struct lastack *LS, int from, int top, int elemtype) {
int stackidx;
for (stackidx = from; stackidx <= top; ++stackidx) {
if (lua_getfield(L, stackidx, "n") != LUA_TNIL) {
const int num = (int)lua_tointeger(L, -1);
lua_pop(L, 1); // pop 'n'
const int tablenum = (int)lua_rawlen(L, stackidx);
if (num != tablenum) {
luaL_error(L, "'n' field: %d not equal to table count: %d", num, tablenum);
}
int tblidx;
for (tblidx = 0; tblidx < num; ++tblidx) {
lua_geti(L, stackidx, tblidx + 1);
void * v = get_pointer_variant(L, LS, -1, elemtype);
if (v) {
lua_pop(L, 1); // pop lua_geti value
lua_pushlightuserdata(L, v);
} else {
luaL_checktype(L, -1, LUA_TTABLE);
}
// v == NULL will not pop, make it in the stack
}
}
}
int ii;
for (ii = 0; ii <= top - from; ++ii) {
lua_remove(L, from);
}
}
static void
convert_stack_value(lua_State *L, struct lastack *LS, int from, int top, int elemtype) {
int i;
for (i = from; i <= top; i++) {
void * v = get_pointer_variant(L, LS, i, elemtype);
if (v) {
lua_pushlightuserdata(L, v);
lua_replace(L, i);
}
}
}
// upvalue1 mathstack
// upvalue2 matrix cfunction
// upvalue3 vector cfunction
// upvalue4 integer from
static int
lvariant(lua_State *L) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
const int from = lua_tointeger(L, lua_upvalueindex(4));
const int top = lua_gettop(L);
const uint8_t elemtype = check_elem_type(L, LS, from);
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex((elemtype & SET_Mat) ? 2 : 3));
if (elemtype & SET_Array) {
unpack_table_on_stack(L, LS, from, top, elemtype);
} else {
convert_stack_value(L, LS, from, top, elemtype);
}
return f(L);
}
// upvalue1 : userdata mathstack
// cfunction original function for matrix
// cfunction original function for vector
// integer from
static int
lbind_variant(lua_State *L) {
if (!lua_iscfunction(L, 1))
return luaL_error(L, "need a c function");
if (lua_getupvalue(L, 1, 1) != NULL)
luaL_error(L, "Only support light cfunction");
if (!lua_iscfunction(L, 2))
return luaL_error(L, "need a c function");
if (lua_getupvalue(L, 2, 1) != NULL)
luaL_error(L, "Only support light cfunction");
luaL_checkinteger(L, 3);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushvalue(L, 3);
lua_pushcclosure(L, lvariant, 4);
return 1;
}
static int
lformat(lua_State *L, const char *format) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
int from = lua_tointeger(L, lua_upvalueindex(4));
int i;
int top = lua_gettop(L);
void *v = NULL;
for (i=0;format[i];i++) {
int index = from + i;
if (index > top)
luaL_error(L, "Invalid format string %s", format);
switch(format[i]) {
case 'm':
v = get_pointer(L, LS, index, LINEAR_TYPE_MAT);
break;
case 'v':
v = get_pointer(L, LS, index, LINEAR_TYPE_VEC4);
break;
case 'q':
v = get_pointer(L, LS, index, LINEAR_TYPE_QUAT);
break;
default:
luaL_error(L, "Invalid format string %s", format);
break;
}
lua_pushlightuserdata(L, v);
lua_replace(L, index);
}
return f(L);
}
static int
lformat_function(lua_State *L) {
lua_CFunction getformat = lua_tocfunction(L, lua_upvalueindex(3));
if (getformat(L) != 1 || lua_type(L, -1) != LUA_TLIGHTUSERDATA)
luaL_error(L, "Invalid format C function");
const char *format = (const char *)lua_touserdata(L, -1);
lua_pop(L, 1);
return lformat(L, format);
}
static int
lformat_string(lua_State *L) {
const char *format = lua_tostring(L, lua_upvalueindex(3));
return lformat(L, format);
}
// upvalue1: userdata mathstack
// cfunction original function
// cfunction function return (void *)format
// integer from
static int
lbind_format(lua_State *L) {
if (!lua_iscfunction(L, 1))
return luaL_error(L, "need a c function");
if (lua_getupvalue(L, 1, 1) != NULL)
luaL_error(L, "Only support light cfunction");
int string_version = 0;
if (lua_isstring(L, 2)) {
string_version = 1;
} else if (!lua_iscfunction(L, 2)) {
return luaL_error(L, "need a c format function or string");
}
if (lua_getupvalue(L, 2, 1) != NULL)
luaL_error(L, "Only support light cfunction");
luaL_checkinteger(L, 3);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushvalue(L, 3);
if (string_version) {
lua_pushcclosure(L, lformat_string, 4);
} else {
lua_pushcclosure(L, lformat_function, 4);
}
return 1;
}
struct stack_buf {
float mat[16];
struct stack_buf *prev;
};
static int
get_n(lua_State *L, int n, struct stack_buf *prev) {
if (n == 0) {
struct lastack *LS = lua_touserdata(L, lua_upvalueindex(1));
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
size_t sz = 0;
const char *format = lua_tolstring(L, lua_upvalueindex(3), &sz);
int ret = f(L);
if (ret == 0) {
lua_settop(L, 0);
} else {
int top = lua_gettop(L);
if (ret != top) {
if (ret < top && ret != 0) {
int remove = top-ret;
lua_rotate(L, 1, -remove);
}
lua_settop(L, ret);
}
}
luaL_checkstack(L, (int)sz, NULL);
int i = (int)sz - 1;
while (prev) {
switch(format[i]) {
case 'm':
lastack_pushmatrix(LS, prev->mat);
break;
case 'v':
lastack_pushvec4(LS, prev->mat);
break;
case 'q':
lastack_pushquat(LS, prev->mat);
break;
default:
luaL_error(L,"Invalid getter format %s", format);
break;
}
int64_t id = lastack_pop(LS);
lua_pushlightuserdata(L, (void *)id);
lua_insert(L, ret+1);
prev = prev->prev;
--i;
}
return ret + (int)sz;
}
struct stack_buf buf;
buf.prev = prev;
lua_pushlightuserdata(L, (void *)buf.mat);
return get_n(L, n-1, &buf);
}
static int
lgetter(lua_State *L) {
size_t n = 0;
lua_tolstring(L, lua_upvalueindex(3), &n);
luaL_checkstack(L, (int)(n + LUA_MINSTACK), NULL);
return get_n(L, (int)n, NULL);
}
// upvalue1 : userdata mathstack
// cfunction original getter
// string format "mvq" , m for matrix, v for vector4, q for quat
static int
lbind_getter(lua_State *L) {
if (!lua_iscfunction(L, 1))
return luaL_error(L, "need a c function");
luaL_checkstring(L, 2);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushcclosure(L, lgetter, 3);
return 1;
}
static int
loutput_object(lua_State *L, int ltype) {
lua_CFunction f = lua_tocfunction(L, lua_upvalueindex(2));
int retn = f(L);
int from = lua_tointeger(L, lua_upvalueindex(3));
int top = lua_gettop(L);
if (retn > from) {
lua_settop(L, retn);
top = retn;
}
from = top - retn + from;
int i;
struct lastack *LS = (struct lastack *)lua_touserdata(L, lua_upvalueindex(1));
for (i=from;i<=top;i++) {
if (lua_type(L, i) != LUA_TLIGHTUSERDATA) {
return luaL_error(L, "ret %d should be a lightuserdata", i);
}
const float *v = (const float *)lua_touserdata(L, i);
lastack_pushobject(LS, v, ltype);
lua_pushlightuserdata(L, (void *)(lastack_pop(LS)));
lua_replace(L, i);
}
return retn;
}
static int
loutput_matrix(lua_State *L) {
return loutput_object(L, LINEAR_TYPE_MAT);
}
static int
loutput_vector(lua_State *L) {
return loutput_object(L, LINEAR_TYPE_VEC4);
}
static int
loutput_quat(lua_State *L) {
return loutput_object(L, LINEAR_TYPE_QUAT);
}
// upvalue1 : userdata mathstack
// cfunction original output
// integer from
static int
lbind_output(lua_State *L, lua_CFunction output_func) {
if (!lua_iscfunction(L, 1))
return luaL_error(L, "need a c function");
luaL_checkinteger(L, 2);
lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_pushcclosure(L, output_func, 3);
return 1;
}
static int
lbind_output_matrix(lua_State *L) {
return lbind_output(L, loutput_matrix);
}
static int
lbind_output_vector(lua_State *L) {
return lbind_output(L, loutput_vector);
}
static int
lbind_output_quat(lua_State *L) {
return lbind_output(L, loutput_quat);
}
LUAMOD_API int
luaopen_math3d_adapter(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{ "matrix", lbind_matrix },
{ "vector", lbind_vector},
{ "variant", lbind_variant },
{ "format", lbind_format },
{ "getter", lbind_getter },
{ "output_matrix", lbind_output_matrix },
{ "output_vector", lbind_output_vector },
{ "output_quat", lbind_output_quat },
{ NULL, NULL },
};
luaL_newlibtable(L, l);
if (lua_getfield(L, LUA_REGISTRYINDEX, MATH3D_STACK) != LUA_TUSERDATA) {
return luaL_error(L, "request 'math3d' first");
}
struct boxstack * bs = lua_touserdata(L, -1);
lua_pop(L, 1);
lua_pushlightuserdata(L, bs->LS);
luaL_setfuncs(L,l,1);
return 1;
}