From 18d54fc07bef2ef63f0c18b43f03c43f97e5692c Mon Sep 17 00:00:00 2001 From: LJ Sonic Date: Tue, 29 Jul 2025 23:15:21 +0200 Subject: [PATCH] Implement vectors, matrices and quaternions --- src/Sourcefile | 6 ++ src/lua_baselib.c | 7 +- src/lua_libs.h | 10 +- src/lua_maplib.c | 64 ------------ src/lua_matrixlib.c | 205 +++++++++++++++++++++++++++++++++++++ src/lua_quaternionlib.c | 131 ++++++++++++++++++++++++ src/lua_script.c | 3 + src/lua_vectorlib.c | 217 ++++++++++++++++++++++++++++++++++++++++ src/matrix.c | 70 +++++++++++++ src/matrix.h | 30 ++++++ src/quaternion.c | 118 ++++++++++++++++++++++ src/quaternion.h | 31 ++++++ src/vector3d.c | 124 +++++++++++++++++++++++ src/vector3d.h | 34 +++++++ 14 files changed, 982 insertions(+), 68 deletions(-) create mode 100644 src/lua_matrixlib.c create mode 100644 src/lua_quaternionlib.c create mode 100644 src/lua_vectorlib.c create mode 100644 src/matrix.c create mode 100644 src/matrix.h create mode 100644 src/quaternion.c create mode 100644 src/quaternion.h create mode 100644 src/vector3d.c create mode 100644 src/vector3d.h diff --git a/src/Sourcefile b/src/Sourcefile index 5340ee62f..42346fd49 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -26,6 +26,7 @@ hu_stuff.c i_time.c y_inter.c st_stuff.c +matrix.c m_aatree.c m_anigif.c m_argv.c @@ -65,6 +66,7 @@ p_slopes.c p_sweep.cpp p_test.cpp p_deepcopy.cpp +quaternion.c tables.c r_bsp.cpp r_data.c @@ -88,6 +90,7 @@ r_debug.cpp r_debug_parser.cpp screen.c taglist.c +vector3d.c v_video.c s_sound.c sounds.c @@ -117,6 +120,9 @@ lua_hudlib_drawlist.c lua_followerlib.c lua_terrainlib.c lua_waypointslib.c +lua_vectorlib.c +lua_matrixlib.c +lua_quaternionlib.c k_kart.c k_collide.c k_color.c diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 8d9b23125..da35ae43a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -191,8 +191,6 @@ static const struct { {META_NODE, "node_t"}, #endif {META_SLOPE, "slope_t"}, - {META_VECTOR2, "vector2_t"}, - {META_VECTOR3, "vector3_t"}, {META_MAPHEADER, "mapheader_t"}, {META_POLYOBJ, "polyobj_t"}, @@ -245,6 +243,11 @@ static const struct { {META_OVERLAY, "t_overlay_t"}, {META_TERRAIN, "terrain_t"}, + {META_VECTOR2, "vector2_t"}, + {META_VECTOR3, "vector3_t"}, + {META_MATRIX, "matrix_t"}, + {META_QUATERNION, "quaternion_t"}, + {NULL, NULL} }; diff --git a/src/lua_libs.h b/src/lua_libs.h index b261b6be8..7dbfbef38 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -65,8 +65,6 @@ extern lua_State *gL; #define META_NODE "NODE_T*" #endif #define META_SLOPE "PSLOPE_T*" -#define META_VECTOR2 "VECTOR2_T" -#define META_VECTOR3 "VECTOR3_T" #define META_MAPHEADER "MAPHEADER_T*" #define META_POLYOBJ "POLYOBJ_T*" @@ -119,6 +117,11 @@ extern lua_State *gL; #define META_OVERLAY "T_OVERLAY_T*" #define META_TERRAIN "TERRAIN_T*" +#define META_VECTOR2 "VECTOR2_T" +#define META_VECTOR3 "VECTOR3_T" +#define META_MATRIX "MATRIX_T" +#define META_QUATERNION "QUATERNION_T" + boolean luaL_checkboolean(lua_State *L, int narg); int LUA_EnumLib(lua_State *L); @@ -141,6 +144,9 @@ int LUA_FollowerLib(lua_State *L); int LUA_BotVarsLib(lua_State *L); int LUA_TerrainLib(lua_State *L); int LUA_WaypointLib(lua_State *L); +int LUA_MatrixLib(lua_State *L); +int LUA_QuaternionLib(lua_State *L); +int LUA_VectorLib(lua_State *L); #ifdef __cplusplus } // extern "C" diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 4d3f050dd..c687ec3dd 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -381,19 +381,6 @@ static const char *const slope_opt[] = { "flags", NULL}; -// shared by both vector2_t and vector3_t -enum vector_e { - vector_x = 0, - vector_y, - vector_z -}; - -static const char *const vector_opt[] = { - "x", - "y", - "z", - NULL}; - enum activator_e { activator_valid = 0, activator_mo, @@ -2486,47 +2473,6 @@ static int slope_set(lua_State *L) return 0; } -/////////////// -// vector*_t // -/////////////// - -static int vector2_get(lua_State *L) -{ - vector2_t *vec = *((vector2_t **)luaL_checkudata(L, 1, META_VECTOR2)); - enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt); - - if (!vec) - return luaL_error(L, "accessed vector2_t doesn't exist anymore."); - - switch(field) - { - case vector_x: lua_pushfixed(L, vec->x); return 1; - case vector_y: lua_pushfixed(L, vec->y); return 1; - default: break; - } - - return 0; -} - -static int vector3_get(lua_State *L) -{ - vector3_t *vec = *((vector3_t **)luaL_checkudata(L, 1, META_VECTOR3)); - enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt); - - if (!vec) - return luaL_error(L, "accessed vector3_t doesn't exist anymore."); - - switch(field) - { - case vector_x: lua_pushfixed(L, vec->x); return 1; - case vector_y: lua_pushfixed(L, vec->y); return 1; - case vector_z: lua_pushfixed(L, vec->z); return 1; - default: break; - } - - return 0; -} - ///////////////////// // mapheaderinfo[] // ///////////////////// @@ -2896,16 +2842,6 @@ int LUA_MapLib(lua_State *L) lua_setfield(L, -2, "__newindex"); lua_pop(L, 1); - luaL_newmetatable(L, META_VECTOR2); - lua_pushcfunction(L, vector2_get); - lua_setfield(L, -2, "__index"); - lua_pop(L, 1); - - luaL_newmetatable(L, META_VECTOR3); - lua_pushcfunction(L, vector3_get); - lua_setfield(L, -2, "__index"); - lua_pop(L, 1); - luaL_newmetatable(L, META_MAPHEADER); lua_pushcfunction(L, mapheaderinfo_get); lua_setfield(L, -2, "__index"); diff --git a/src/lua_matrixlib.c b/src/lua_matrixlib.c new file mode 100644 index 000000000..0035f81e6 --- /dev/null +++ b/src/lua_matrixlib.c @@ -0,0 +1,205 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file lua_matrixlib.c +/// \brief matrix library for Lua scripting + +#include "matrix.h" +#include "vector3d.h" +#include "lua_script.h" +#include "lua_libs.h" + +static vector3_t *GetVector(lua_State *L, int index) +{ + vector3_t *vec = lua_touserdata(L, index); + + if (!vec) + return NULL; + + if (!lua_getmetatable(L, index)) + return NULL; + + lua_getfield(L, LUA_REGISTRYINDEX, META_VECTOR3); + if (!lua_rawequal(L, -1, -2)) + return NULL; + + lua_pop(L, 2); + + return vec; +} + +static matrix_t *NewMatrix(lua_State *L) +{ + matrix_t *mat = lua_newuserdata(L, sizeof(*mat)); + luaL_getmetatable(L, META_MATRIX); + lua_setmetatable(L, -2); + return mat; +} + +//////////////////// +// STATIC MEMBERS // +//////////////////// + +static int matrix_new(lua_State *L) +{ + Matrix_SetIdentity(NewMatrix(L)); + return 1; +} + +static int matrix_fromTranslation(lua_State *L) +{ + fixed_t x = luaL_checkfixed(L, 1); + fixed_t y = luaL_checkfixed(L, 2); + fixed_t z = luaL_checkfixed(L, 3); + + Matrix_SetTranslation(NewMatrix(L), x, y, z); + return 1; +} + +static int matrix_fromScaling(lua_State *L) +{ + fixed_t x = luaL_checkfixed(L, 1); + fixed_t y = luaL_checkfixed(L, 2); + fixed_t z = luaL_checkfixed(L, 3); + + Matrix_SetScaling(NewMatrix(L), x, y, z); + return 1; +} + +static luaL_Reg matrix[] = { + {"new", matrix_new}, + {"fromTranslation", matrix_fromTranslation}, + {"fromScaling", matrix_fromScaling}, + {NULL, NULL} +}; + +///////////// +// MEMBERS // +///////////// + +enum matrixfield_e { + matrixfield_clone = 0, + matrixfield_getvalue, + matrixfield_setvalue, + matrixfield_mulXYZ, +}; + +static const char *const matrixfield_opt[] = { + "clone", + "mulXYZ", + NULL}; + +static int matrix_clone(lua_State *L) +{ + matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX); + Matrix_Copy(NewMatrix(L), mat); + return 1; +} + +static int matrix_getvalue(lua_State *L) +{ + matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX); + INT32 row = luaL_checkinteger(L, 2); + INT32 col = luaL_checkinteger(L, 3); + + if (row < 1 || row > 4) + return luaL_error(L, "matrix row %d out of range (1 - 4)", row); + if (col < 1 || col > 4) + return luaL_error(L, "matrix column %d out of range (1 - 4)", col); + + lua_pushfixed(L, mat->matrix[row - 1][col - 1]); + return 1; +} + +static int matrix_setvalue(lua_State *L) +{ + matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX); + INT32 row = luaL_checkinteger(L, 2); + INT32 col = luaL_checkinteger(L, 3); + fixed_t value = luaL_checkfixed(L, 4); + + if (row < 1 || row > 4) + return luaL_error(L, "matrix row %d out of range (1 - 4)", row); + if (col < 1 || col > 4) + return luaL_error(L, "matrix column %d out of range (1 - 4)", col); + + mat->matrix[row - 1][col - 1] = value; + return 0; +} + +static int matrix_mulXYZ(lua_State *L) +{ + matrix_t *mat = luaL_checkudata(L, 1, META_MATRIX); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + fixed_t z = luaL_checkfixed(L, 4); + + vector3_t vec; + vector3_t result; + Vector3D_Set(&vec, x, y, z); + Matrix_MulVector(&result, mat, &vec); + + lua_pushfixed(L, result.x); + lua_pushfixed(L, result.y); + lua_pushfixed(L, result.z); + return 3; +} + +static int matrix_get(lua_State *L) +{ + enum matrixfield_e field = luaL_checkoption(L, 2, matrixfield_opt[0], matrixfield_opt); + + switch(field) + { + case matrixfield_clone: lua_pushcfunction(L, matrix_clone); return 1; + case matrixfield_getvalue: lua_pushcfunction(L, matrix_getvalue); return 1; + case matrixfield_setvalue: lua_pushcfunction(L, matrix_setvalue); return 1; + case matrixfield_mulXYZ: lua_pushcfunction(L, matrix_mulXYZ); return 1; + + default: break; + } + + return 0; +} + +/////////////// +// OPERATORS // +/////////////// + +static int matrix_mul(lua_State *L) +{ + matrix_t *mat1 = luaL_checkudata(L, 1, META_MATRIX); + + vector3_t *vec2 = GetVector(L, 2); + if (vec2) + { + vector3_t *result = lua_newuserdata(L, sizeof(*result)); + luaL_getmetatable(L, META_VECTOR3); + lua_setmetatable(L, -2); + + Matrix_MulVector(result, mat1, vec2); + } + else + { + matrix_t *mat2 = luaL_checkudata(L, 2, META_MATRIX); + Matrix_Mul(NewMatrix(L), mat1, mat2); + } + + return 1; +} + +int LUA_MatrixLib(lua_State *L) +{ + luaL_newmetatable(L, META_MATRIX); + LUA_SetCFunctionField(L, "__index", matrix_get); + LUA_SetCFunctionField(L, "__mul", matrix_mul); + lua_pop(L, 1); + + luaL_register(L, "Matrix", matrix); + return 0; +} diff --git a/src/lua_quaternionlib.c b/src/lua_quaternionlib.c new file mode 100644 index 000000000..fb698c236 --- /dev/null +++ b/src/lua_quaternionlib.c @@ -0,0 +1,131 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file lua_quaternionlib.c +/// \brief quaternion library for Lua scripting + +#include "quaternion.h" +#include "lua_script.h" +#include "lua_libs.h" + +static quaternion_t *NewQuaternion(lua_State *L) +{ + quaternion_t *quat = lua_newuserdata(L, sizeof(*quat)); + luaL_getmetatable(L, META_QUATERNION); + lua_setmetatable(L, -2); + return quat; +} + +//////////////////// +// STATIC MEMBERS // +//////////////////// + +static int quaternion_new(lua_State *L) +{ + Quaternion_SetIdentity(NewQuaternion(L)); + return 1; +} + +static int quaternion_fromAxisRotation(lua_State *L) +{ + vector3_t *axis = luaL_checkudata(L, 1, META_VECTOR3); + fixed_t angle = luaL_checkfixed(L, 2); + Quaternion_SetAxisRotation(NewQuaternion(L), axis, angle); + return 1; +} + +static luaL_Reg quaternion[] = { + {"new", quaternion_new}, + {"fromAxisRotation", quaternion_fromAxisRotation}, + {NULL, NULL} +}; + +///////////// +// MEMBERS // +///////////// + +static int quaternion_clone(lua_State *L) +{ + quaternion_t *quat = luaL_checkudata(L, 1, META_QUATERNION); + Quaternion_Copy(NewQuaternion(L), quat); + return 1; +} + +static int quaternion_toMatrix(lua_State *L) +{ + quaternion_t *quat = luaL_checkudata(L, 1, META_QUATERNION); + + matrix_t *mat = lua_newuserdata(L, sizeof(*mat)); + luaL_getmetatable(L, META_MATRIX); + lua_setmetatable(L, -2); + + Quaternion_ToMatrix(mat, quat); + return 1; +} + +enum quaternionfield_e { + quaternionfield_clone = 0, + quaternionfield_toMatrix, + quaternionfield_x, + quaternionfield_y, + quaternionfield_z, + quaternionfield_w, +}; + +static const char *const quaternionfield_opt[] = { + "clone", + "toMatrix", + "x", + "y", + "z", + "w", + NULL}; + +static int quaternion_get(lua_State *L) +{ + quaternion_t *quat = luaL_checkudata(L, 1, META_QUATERNION); + enum quaternionfield_e field = luaL_checkoption(L, 2, quaternionfield_opt[0], quaternionfield_opt); + + switch(field) + { + case quaternionfield_clone: lua_pushcfunction(L, quaternion_clone); return 1; + case quaternionfield_toMatrix: lua_pushcfunction(L, quaternion_toMatrix); return 1; + + case quaternionfield_x: lua_pushfixed(L, quat->x); return 1; + case quaternionfield_y: lua_pushfixed(L, quat->y); return 1; + case quaternionfield_z: lua_pushfixed(L, quat->z); return 1; + case quaternionfield_w: lua_pushfixed(L, quat->w); return 1; + + default: break; + } + + return 0; +} + +/////////////// +// OPERATORS // +/////////////// + +static int quaternion_mul(lua_State *L) +{ + quaternion_t *quat1 = luaL_checkudata(L, 1, META_QUATERNION); + quaternion_t *quat2 = luaL_checkudata(L, 2, META_QUATERNION); + Quaternion_Mul(NewQuaternion(L), quat1, quat2); + return 1; +} + +int LUA_QuaternionLib(lua_State *L) +{ + luaL_newmetatable(L, META_QUATERNION); + LUA_SetCFunctionField(L, "__index", quaternion_get); + LUA_SetCFunctionField(L, "__mul", quaternion_mul); + lua_pop(L, 1); + + luaL_register(L, "Quaternion", quaternion); + return 0; +} diff --git a/src/lua_script.c b/src/lua_script.c index f646db2ac..cf8a80437 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -63,6 +63,9 @@ static lua_CFunction liblist[] = { LUA_BotVarsLib, // botvars_t LUA_TerrainLib, // t_splash_t, t_footstep_t, t_overlay_t, terrain_t LUA_WaypointLib, // waypoint_t + LUA_VectorLib, // vectors + LUA_MatrixLib, // matrices + LUA_QuaternionLib, // quaternions NULL }; diff --git a/src/lua_vectorlib.c b/src/lua_vectorlib.c new file mode 100644 index 000000000..43faad3ad --- /dev/null +++ b/src/lua_vectorlib.c @@ -0,0 +1,217 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file lua_vectorlib.c +/// \brief vector library for Lua scripting + +#include "vector3d.h" +#include "lua_script.h" +#include "lua_libs.h" + +// shared by both Vector2D and Vector3D +enum vectorfield_e { + vectorfield_clone = 0, + vectorfield_opposite, + vectorfield_x, + vectorfield_y, + vectorfield_z, + vectorfield_length, + vectorfield_normalized, +}; + +static const char *const vectorfield_opt[] = { + "vector_clone", + "vector_opposite", + "x", + "y", + "z", + "vector_length", + "vector_normalized", + NULL}; + +//////////////////////////// +// VECTOR2 STATIC MEMBERS // +//////////////////////////// + +///////////////////// +// VECTOR2 MEMBERS // +///////////////////// + +static int vector2d_get(lua_State *L) +{ + vector2_t *vec = *((vector2_t **)luaL_checkudata(L, 1, META_VECTOR2)); + enum vectorfield_e field = luaL_checkoption(L, 2, vectorfield_opt[0], vectorfield_opt); + + switch(field) + { + case vectorfield_x: lua_pushfixed(L, vec->x); return 1; + case vectorfield_y: lua_pushfixed(L, vec->y); return 1; + default: break; + } + + return 0; +} + +///////////// +// VECTOR3 // +///////////// + +static vector3_t *NewVector3(lua_State *L) +{ + vector3_t *vec = lua_newuserdata(L, sizeof(*vec)); + luaL_getmetatable(L, META_VECTOR3); + lua_setmetatable(L, -2); + return vec; +} + +//////////////////////////// +// VECTOR3 STATIC MEMBERS // +//////////////////////////// + +static int vector3d_new(lua_State *L) +{ + fixed_t x = luaL_checkfixed(L, 1); + fixed_t y = luaL_checkfixed(L, 2); + fixed_t z = luaL_optfixed(L, 3, 0); + + Vector3D_Set(NewVector3(L), x, y, z); + return 1; +} + +static luaL_Reg vector3d[] = { + {"new", vector3d_new}, + {NULL, NULL} +}; + +///////////////////// +// VECTOR3 MEMBERS // +///////////////////// + +static int vector3d_clone(lua_State *L) +{ + vector3_t *vec = luaL_checkudata(L, 1, META_VECTOR3); + Vector3D_Copy(NewVector3(L), vec); + return 1; +} + +static int vector3d_opposite(lua_State *L) +{ + vector3_t *vec = luaL_checkudata(L, 1, META_VECTOR3); + Vector3D_Opposite(NewVector3(L), vec); + return 1; +} + +static int vector3d_get(lua_State *L) +{ + vector3_t *vec = luaL_checkudata(L, 1, META_VECTOR3); + enum vectorfield_e field = luaL_checkoption(L, 2, vectorfield_opt[0], vectorfield_opt); + + if (!vec) + return luaL_error(L, "accessed vector3_t doesn't exist anymore."); + + switch(field) + { + case vectorfield_clone: lua_pushcfunction(L, vector3d_clone); return 1; + case vectorfield_opposite: lua_pushcfunction(L, vector3d_opposite); return 1; + + case vectorfield_x: lua_pushfixed(L, vec->x); return 1; + case vectorfield_y: lua_pushfixed(L, vec->y); return 1; + case vectorfield_z: lua_pushfixed(L, vec->z); return 1; + case vectorfield_length: lua_pushfixed(L, Vector3D_Length(vec)); return 1; + case vectorfield_normalized: Vector3D_Normalize(NewVector3(L), vec); return 1; + + default: break; + } + + return 0; +} + +/////////////////////// +// VECTOR3 OPERATORS // +/////////////////////// + +static int vector3d_eq(lua_State *L) +{ + vector3_t *vec1 = luaL_checkudata(L, 1, META_VECTOR3); + vector3_t *vec2 = luaL_checkudata(L, 2, META_VECTOR3); + lua_pushboolean(L, Vector3D_Equal(vec1, vec2)); + return 1; +} + +static int vector3d_op( + lua_State *L, + vector3_t *(*opvector)(vector3_t*, vector3_t*, vector3_t*), + vector3_t *(*opfixed)(vector3_t*, vector3_t*, fixed_t) +) +{ + if (lua_isnumber(L, 1) && (opfixed == Vector3D_AddFixed || opfixed == Vector3D_MulFixed)) + { + fixed_t n1 = lua_tofixed(L, 1); + vector3_t *vec2 = luaL_checkudata(L, 2, META_VECTOR3); + opfixed(NewVector3(L), vec2, n1); + } + else if (lua_isnumber(L, 2)) + { + vector3_t *vec1 = luaL_checkudata(L, 1, META_VECTOR3); + fixed_t n2 = lua_tofixed(L, 2); + opfixed(NewVector3(L), vec1, n2); + } + else + { + vector3_t *vec1 = luaL_checkudata(L, 1, META_VECTOR3); + vector3_t *vec2 = luaL_checkudata(L, 2, META_VECTOR3); + opvector(NewVector3(L), vec1, vec2); + } + + return 1; +} + +static int vector3d_add(lua_State *L) +{ + return vector3d_op(L, Vector3D_Add, Vector3D_AddFixed); +} + +static int vector3d_sub(lua_State *L) +{ + return vector3d_op(L, Vector3D_Sub, Vector3D_SubFixed); +} + +static int vector3d_mul(lua_State *L) +{ + return vector3d_op(L, Vector3D_Mul, Vector3D_MulFixed); +} + +static int vector3d_div(lua_State *L) +{ + return vector3d_op(L, Vector3D_Div, Vector3D_DivFixed); +} + +static int vector3d_unm(lua_State *L) +{ + vector3_t *vec = luaL_checkudata(L, 1, META_VECTOR3); + Vector3D_Opposite(NewVector3(L), vec); + return 1; +} + +int LUA_VectorLib(lua_State *L) +{ + LUA_RegisterUserdataMetatable(L, META_VECTOR2, vector2d_get, NULL, NULL); + + luaL_newmetatable(L, META_VECTOR3); + LUA_SetCFunctionField(L, "__index", vector3d_get); + LUA_SetCFunctionField(L, "__eq", vector3d_eq); + LUA_SetCFunctionField(L, "__add", vector3d_add); + LUA_SetCFunctionField(L, "__sub", vector3d_sub); + LUA_SetCFunctionField(L, "__mul", vector3d_mul); + LUA_SetCFunctionField(L, "__div", vector3d_div); + LUA_SetCFunctionField(L, "__unm", vector3d_unm); + lua_pop(L, 1); + + luaL_register(L, "Vector3D", vector3d); + return 0; +} diff --git a/src/matrix.c b/src/matrix.c new file mode 100644 index 000000000..f950df36e --- /dev/null +++ b/src/matrix.c @@ -0,0 +1,70 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file matrix.c +/// \brief Fixed-point 3D vector + +#include "matrix.h" + +static matrix_t identitymatrix = {{ + {FRACUNIT, 0, 0, 0}, + {0, FRACUNIT, 0, 0}, + {0, 0, FRACUNIT, 0}, + {0, 0, 0, FRACUNIT}, +}}; + +matrix_t *Matrix_SetIdentity(matrix_t *mat) +{ + return memcpy(mat, identitymatrix.matrix, sizeof(*mat)); +} + +matrix_t *Matrix_SetTranslation(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z) +{ + Matrix_SetIdentity(mat); + + mat->matrix[0][3] = x; + mat->matrix[1][3] = y; + mat->matrix[2][3] = z; + + return mat; +} + +matrix_t *Matrix_SetScaling(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z) +{ + Matrix_SetIdentity(mat); + + mat->matrix[0][0] = x; + mat->matrix[1][1] = y; + mat->matrix[2][2] = z; + + return mat; +} + +matrix_t *Matrix_Copy(matrix_t *out, matrix_t *in) +{ + return memcpy(out, in, sizeof(*out)); +} + +matrix_t *Matrix_Mul(matrix_t *out, matrix_t *a, matrix_t *b) +{ + for (size_t r = 0; r < 4; r++) + for (size_t c = 0; c < 4; c++) + for (size_t i = 0; i < 4; i++) + out->matrix[r][c] += FixedMul(a->matrix[r][i], b->matrix[i][c]); + + return out; +} + +vector3_t *Matrix_MulVector(vector3_t *out, matrix_t *a, vector3_t *b) +{ + out->x = FixedMul(a->matrix[0][0], b->x) + FixedMul(a->matrix[0][1], b->y) + FixedMul(a->matrix[0][2], b->z) + a->matrix[0][3]; + out->y = FixedMul(a->matrix[1][0], b->x) + FixedMul(a->matrix[1][1], b->y) + FixedMul(a->matrix[1][2], b->z) + a->matrix[1][3]; + out->z = FixedMul(a->matrix[2][0], b->x) + FixedMul(a->matrix[2][1], b->y) + FixedMul(a->matrix[2][2], b->z) + a->matrix[2][3]; + + return out; +} diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 000000000..bcfd003af --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,30 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file matrix.c +/// \brief Fixed-point 4x4 matrix + +#ifndef __MATRIX__ +#define __MATRIX__ + +#include "m_fixed.h" +#include "vector3d.h" + +typedef struct +{ + fixed_t matrix[4][4]; +} matrix_t; + +matrix_t *Matrix_SetIdentity(matrix_t *mat); +matrix_t *Matrix_SetTranslation(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z); +matrix_t *Matrix_SetScaling(matrix_t *mat, fixed_t x, fixed_t y, fixed_t z); +matrix_t *Matrix_Copy(matrix_t *out, matrix_t *in); +matrix_t *Matrix_Mul(matrix_t *out, matrix_t *a, matrix_t *b); +vector3_t *Matrix_MulVector(vector3_t *out, matrix_t *a, vector3_t *b); + +#endif diff --git a/src/quaternion.c b/src/quaternion.c new file mode 100644 index 000000000..e566546b6 --- /dev/null +++ b/src/quaternion.c @@ -0,0 +1,118 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file quaternion.c +/// \brief Fixed-point 3D vector + +#include "quaternion.h" +#include "vector3d.h" +#include "matrix.h" +#include "r_main.h" // R_PointToDist2 + +quaternion_t *Quaternion_Set(quaternion_t *quat, fixed_t x, fixed_t y, fixed_t z, fixed_t w) +{ + quat->x = x; + quat->y = y; + quat->z = z; + quat->w = w; + + return quat; +} + +quaternion_t *Quaternion_SetIdentity(quaternion_t *quat) +{ + return Quaternion_Set(quat, 0, 0, 0, FRACUNIT); +} + +quaternion_t *Quaternion_SetAxisRotation(quaternion_t *quat, vector3_t *axis, fixed_t angle) +{ + fixed_t cosangle = FINECOSINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK); + fixed_t sinangle = FINESINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK); + vector3_t normaxis; + + Vector3D_Normalize(&normaxis, axis); + + return Quaternion_Set(quat, + FixedMul(normaxis.x, sinangle), + FixedMul(normaxis.y, sinangle), + FixedMul(normaxis.z, sinangle), + cosangle + ); +} + +quaternion_t *Quaternion_Copy(quaternion_t *out, quaternion_t *in) +{ + return memcpy(out, in, sizeof(*out)); +} + +matrix_t *Quaternion_ToMatrix(matrix_t *mat, const quaternion_t *quat) +{ + fixed_t x = quat->x, y = quat->y, z = quat->z, w = quat->w; + + fixed_t xx2 = 2 * FixedMul(x, x); + fixed_t xy2 = 2 * FixedMul(x, y); + fixed_t xz2 = 2 * FixedMul(x, z); + fixed_t xw2 = 2 * FixedMul(x, w); + fixed_t yy2 = 2 * FixedMul(y, y); + fixed_t yz2 = 2 * FixedMul(y, z); + fixed_t yw2 = 2 * FixedMul(y, w); + fixed_t zz2 = 2 * FixedMul(z, z); + fixed_t zw2 = 2 * FixedMul(z, w); + + Matrix_SetIdentity(mat); + + mat->matrix[0][0] = FRACUNIT - yy2 - zz2; + mat->matrix[0][1] = xy2 - zw2; + mat->matrix[0][2] = xz2 + yw2; + + mat->matrix[1][0] = xy2 + zw2; + mat->matrix[1][1] = FRACUNIT - xx2 - zz2; + mat->matrix[1][2] = yz2 - xw2; + + mat->matrix[2][0] = xz2 - yw2; + mat->matrix[2][1] = yz2 + xw2; + mat->matrix[2][2] = FRACUNIT - xx2 - yy2; + + mat->matrix[3][3] = FRACUNIT; + + return mat; +} + +quaternion_t *Quaternion_Normalize(quaternion_t *out, quaternion_t *in) +{ + fixed_t sqlen = + FixedMul(in->x, in->x) + + FixedMul(in->y, in->y) + + FixedMul(in->z, in->z) + + FixedMul(in->w, in->w); + + if (sqlen < FRACUNIT / 1024) + return Quaternion_Set(out, in->x, in->y, in->z, in->w); + + fixed_t len = R_PointToDist2(0, 0, R_PointToDist2(0, 0, R_PointToDist2(0, 0, in->x, in->y), in->z), in->w); + + return Quaternion_Set(out, + FixedDiv(in->x, len), + FixedDiv(in->y, len), + FixedDiv(in->z, len), + FixedDiv(in->w, len) + ); +} + +quaternion_t *Quaternion_Mul(quaternion_t *out, quaternion_t *a, quaternion_t *b) +{ + fixed_t ax = a->x, ay = a->y, az = a->z, aw = a->w; + fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->w; + + return Quaternion_Normalize(out, Quaternion_Set(out, + FixedMul(aw, bx) + FixedMul(ax, bw) + FixedMul(ay, bz) - FixedMul(az, by), + FixedMul(aw, by) - FixedMul(ax, bz) + FixedMul(ay, bw) + FixedMul(az, bx), + FixedMul(aw, bz) + FixedMul(ax, by) - FixedMul(ay, bx) + FixedMul(az, bw), + FixedMul(aw, bw) - FixedMul(ax, bx) - FixedMul(ay, by) - FixedMul(az, bz) + )); +} diff --git a/src/quaternion.h b/src/quaternion.h new file mode 100644 index 000000000..7118eb7c6 --- /dev/null +++ b/src/quaternion.h @@ -0,0 +1,31 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file quaternion.c +/// \brief Fixed-point quaternion + +#ifndef __QUATERNION__ +#define __QUATERNION__ + +#include "m_fixed.h" +#include "matrix.h" + +typedef struct +{ + fixed_t x, y, z, w; +} quaternion_t; + +quaternion_t *Quaternion_Set(quaternion_t *quat, fixed_t x, fixed_t y, fixed_t z, fixed_t w); +quaternion_t *Quaternion_SetIdentity(quaternion_t *quat); +quaternion_t *Quaternion_SetAxisRotation(quaternion_t *quat, vector3_t *axis, fixed_t angle); +quaternion_t *Quaternion_Copy(quaternion_t *out, quaternion_t *in); +matrix_t *Quaternion_ToMatrix(matrix_t *mat, const quaternion_t *quat); +quaternion_t *Quaternion_Normalize(quaternion_t *out, quaternion_t *in); +quaternion_t *Quaternion_Mul(quaternion_t *out, quaternion_t *a, quaternion_t *b); + +#endif diff --git a/src/vector3d.c b/src/vector3d.c new file mode 100644 index 000000000..abf4e95b8 --- /dev/null +++ b/src/vector3d.c @@ -0,0 +1,124 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file vector3d.c +/// \brief Fixed-point 3D vector + +#include "vector3d.h" +#include "r_main.h" // R_PointToDist2 + +vector3_t *Vector3D_Set(vector3_t *vec, fixed_t x, fixed_t y, fixed_t z) +{ + vec->x = x; + vec->y = y; + vec->z = z; + + return vec; +} + +vector3_t *Vector3D_Copy(vector3_t *out, vector3_t *in) +{ + return memcpy(out, in, sizeof(*out)); +} + +fixed_t Vector3D_Length(vector3_t *vec) +{ + return R_PointToDist2(0, 0, R_PointToDist2(0, 0, vec->x, vec->y), vec->z); +} + +vector3_t *Vector3D_Opposite(vector3_t *out, vector3_t *in) +{ + return Vector3D_Set(out, -in->x, -in->y, -in->z); +} + +vector3_t *Vector3D_Normalize(vector3_t *out, vector3_t *in) +{ + fixed_t len = Vector3D_Length(in); + + if (len == 0) + return Vector3D_Set(out, in->x, in->y, in->z); + else + return Vector3D_Set(out, FixedDiv(in->x, len), FixedDiv(in->y, len), FixedDiv(in->z, len)); +} + +boolean Vector3D_Equal(vector3_t *a, vector3_t *b) +{ + return (a->x == b->x && a->y == b->y && a->z == b->z); +} + +vector3_t *Vector3D_Add(vector3_t *out, vector3_t *a, vector3_t *b) +{ + out->x = a->x + b->x; + out->y = a->y + b->y; + out->z = a->z + b->z; + + return out; +} + +vector3_t *Vector3D_Sub(vector3_t *out, vector3_t *a, vector3_t *b) +{ + out->x = a->x - b->x; + out->y = a->y - b->y; + out->z = a->z - b->z; + + return out; +} + +vector3_t *Vector3D_Mul(vector3_t *out, vector3_t *a, vector3_t *b) +{ + out->x = FixedMul(a->x, b->x); + out->y = FixedMul(a->y, b->y); + out->z = FixedMul(a->z, b->z); + + return out; +} + +vector3_t *Vector3D_Div(vector3_t *out, vector3_t *a, vector3_t *b) +{ + out->x = FixedDiv(a->x, b->x); + out->y = FixedDiv(a->y, b->y); + out->z = FixedDiv(a->z, b->z); + + return out; +} + +vector3_t *Vector3D_AddFixed(vector3_t *out, vector3_t *a, fixed_t b) +{ + out->x = a->x + b; + out->y = a->y + b; + out->z = a->z + b; + + return out; +} + +vector3_t *Vector3D_SubFixed(vector3_t *out, vector3_t *a, fixed_t b) +{ + out->x = a->x - b; + out->y = a->y - b; + out->z = a->z - b; + + return out; +} + +vector3_t *Vector3D_MulFixed(vector3_t *out, vector3_t *a, fixed_t b) +{ + out->x = FixedMul(a->x, b); + out->y = FixedMul(a->y, b); + out->z = FixedMul(a->z, b); + + return out; +} + +vector3_t *Vector3D_DivFixed(vector3_t *out, vector3_t *a, fixed_t b) +{ + out->x = FixedDiv(a->x, b); + out->y = FixedDiv(a->y, b); + out->z = FixedDiv(a->z, b); + + return out; +} diff --git a/src/vector3d.h b/src/vector3d.h new file mode 100644 index 000000000..1076716de --- /dev/null +++ b/src/vector3d.h @@ -0,0 +1,34 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by LJ Sonic +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file vector3d.c +/// \brief Fixed-point 3D vector + +#ifndef __VECTOR3D__ +#define __VECTOR3D__ + +#include "m_fixed.h" + +vector3_t *Vector3D_Set(vector3_t *vec, fixed_t x, fixed_t y, fixed_t z); +vector3_t *Vector3D_Copy(vector3_t *out, vector3_t *in); +fixed_t Vector3D_Length(vector3_t *vec); +vector3_t *Vector3D_Opposite(vector3_t *out, vector3_t *in); +vector3_t *Vector3D_Normalize(vector3_t *out, vector3_t *in); +boolean Vector3D_Equal(vector3_t *a, vector3_t *b); + +vector3_t *Vector3D_Add(vector3_t *out, vector3_t *a, vector3_t *b); +vector3_t *Vector3D_Sub(vector3_t *out, vector3_t *a, vector3_t *b); +vector3_t *Vector3D_Mul(vector3_t *out, vector3_t *a, vector3_t *b); +vector3_t *Vector3D_Div(vector3_t *out, vector3_t *a, vector3_t *b); + +vector3_t *Vector3D_AddFixed(vector3_t *out, vector3_t *a, fixed_t b); +vector3_t *Vector3D_SubFixed(vector3_t *out, vector3_t *a, fixed_t b); +vector3_t *Vector3D_MulFixed(vector3_t *out, vector3_t *a, fixed_t b); +vector3_t *Vector3D_DivFixed(vector3_t *out, vector3_t *a, fixed_t b); + +#endif