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
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;
|
|
}
|