#define LUA_LIB #include #include #include #include #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;iid; 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;iid, 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 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;iLS = 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); }