Implement vectors, matrices and quaternions

This commit is contained in:
LJ Sonic 2025-07-29 23:15:21 +02:00 committed by Anonimus
parent 8b2626165b
commit 18d54fc07b
14 changed files with 982 additions and 68 deletions

View file

@ -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

View file

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

View file

@ -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"

View file

@ -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");

205
src/lua_matrixlib.c Normal file
View file

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

131
src/lua_quaternionlib.c Normal file
View file

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

View file

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

217
src/lua_vectorlib.c Normal file
View file

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

70
src/matrix.c Normal file
View file

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

30
src/matrix.h Normal file
View file

@ -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

118
src/quaternion.c Normal file
View file

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

31
src/quaternion.h Normal file
View file

@ -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

124
src/vector3d.c Normal file
View file

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

34
src/vector3d.h Normal file
View file

@ -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