#define LUA_LIB #include #include #include #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; }