blankart/src/lua_quaternionlib.c
2026-01-01 14:45:23 -05:00

192 lines
4.7 KiB
C

// 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 "m_fixed.h"
#include "lua_script.h"
#include "lua_libs.h"
static vector4_t *NewQuaternion(lua_State *L)
{
vector4_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)
{
vector4_t *q = NewQuaternion(L);
q->x = 0;
q->y = 0;
q->z = 0;
q->a = FRACUNIT;
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);
fixed_t cosangle = FINECOSINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK);
fixed_t sinangle = FINESINE(((angle / 2) >> ANGLETOFINESHIFT) & FINEMASK);
vector3_t normaxis;
FV3_NormalizeEx(axis, &normaxis);
FV4_Load(NewQuaternion(L),
FixedMul(normaxis.x, sinangle),
FixedMul(normaxis.y, sinangle),
FixedMul(normaxis.z, sinangle),
cosangle
);
return 1;
}
static luaL_Reg quaternion[] = {
{"new", quaternion_new},
{"fromAxisRotation", quaternion_fromAxisRotation},
{NULL, NULL}
};
/////////////
// MEMBERS //
/////////////
static int quaternion_clone(lua_State *L)
{
vector4_t *quat = luaL_checkudata(L, 1, META_QUATERNION);
FV4_Copy(NewQuaternion(L), quat);
return 1;
}
static int vector4_toMatrix(lua_State *L)
{
vector4_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);
fixed_t x = quat->x, y = quat->y, z = quat->z, w = quat->a;
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);
FM_LoadIdentity(mat);
mat->m[0 + 0*4] = FRACUNIT - yy2 - zz2;
mat->m[0 + 1*4] = xy2 - zw2;
mat->m[0 + 2*4] = xz2 + yw2;
mat->m[1 + 0*4] = xy2 + zw2;
mat->m[1 + 1*4] = FRACUNIT - xx2 - zz2;
mat->m[1 + 2*4] = yz2 - xw2;
mat->m[2 + 0*4] = xz2 - yw2;
mat->m[2 + 1*4] = yz2 + xw2;
mat->m[2 + 2*4] = FRACUNIT - xx2 - yy2;
mat->m[3 + 3*4] = FRACUNIT;
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)
{
vector4_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, vector4_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->a); return 1;
default: break;
}
return 0;
}
///////////////
// OPERATORS //
///////////////
// FV4_Mul isn't an accurate multiplication, so let's silently use LJ Sonic's multiplication
// function.
static vector4_t *QuaternionMul(vector4_t *out, vector4_t *a, vector4_t *b)
{
fixed_t ax = a->x, ay = a->y, az = a->z, aw = a->a;
fixed_t bx = b->x, by = b->y, bz = b->z, bw = b->a;
FV4_NormalizeEx(out, FV4_Load(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)
));
return out;
}
static int quaternion_mul(lua_State *L)
{
vector4_t *quat1 = luaL_checkudata(L, 1, META_QUATERNION);
vector4_t *quat2 = luaL_checkudata(L, 2, META_QUATERNION);
QuaternionMul(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;
}