Skip to main content

Math

src/math/ and src/rand.hh — GTE-accelerated matrix/vector helpers, lerp, trig, and a lightweight RNG. All free functions or static-only classes; there's no state to manage beyond Rand, which you own an instance of yourself.

GTEMath​

src/math/gte-math.hh

class GTEMath final {
public:
static psyqo::Vec3 ProjectVectorOntoAxes(const psyqo::Matrix33 &axisMatrix, const psyqo::Vec3 &normalizedVec);
};

A GTE-register-backed equivalent of vector-onto-axes projection — used wherever the engine needs that specific operation in a hot path (camera, skeleton posing, collision). As of v0.0.1, GTEMath lost MultiplyMatrix33/MultiplyMatrixVec3 — hot-path matrix/vector multiplication elsewhere in the engine (camera, particle emitters, matrix helpers) now calls psyqo::GteMath directly instead of going through this wrapper.

Matrix​

src/math/matrix.hh

psyqo::Matrix33 TransposeMatrix33(const psyqo::Matrix33 &rotationMatrix);
psyqo::Matrix33 InverseMatrix33(const psyqo::Matrix33 &rotationMatrix);

For an orthonormal rotation matrix, the transpose is the inverse, and it's the cheaper way to get there: TransposeMatrix33 is exact and free, while InverseMatrix33 pays for a determinant and a fixed-point division to land on the same result. Camera::inverseRotationMatrix calls InverseMatrix33 on exactly such a matrix (built from three normalized, orthogonal basis vectors), and would be cheaper calling TransposeMatrix33 instead, the way SkeletonController::UpdateSkeletonBoneMatrices already does for bind-pose inversion. Treat InverseMatrix33 as the fallback for a genuinely non-orthonormal matrix, not the clearer-sounding default. If the matrix is near-degenerate (determinant close to zero), it returns the input matrix unchanged rather than dividing by a tiny number.

Vector​

src/math/vector.hh

psyqo::FixedPoint<> DotProduct(const psyqo::Vec3 &a, const psyqo::Vec3 &b);
psyqo::Vec2 Lerp(const psyqo::Vec2 &a, const psyqo::Vec2 &b, const psyqo::FixedPoint<> &t);
psyqo::Vec3 Lerp(const psyqo::Vec3 &a, const psyqo::Vec3 &b, const psyqo::FixedPoint<> &t);
bool IsVector3Zero(const psyqo::Vec3 &v);

Lerp​

src/math/lerp.hh

psyqo::FixedPoint<> Lerp(const psyqo::FixedPoint<>& a, const psyqo::FixedPoint<>& b, const psyqo::FixedPoint<>& t);
psyqo::FixedPoint<> inverseLerp(uint32_t a, uint32_t b, uint32_t value);

inverseLerp is the reverse operation — given a value between a and b, returns how far along that range it is as a 0..1 fixed-point fraction.

Trig​

src/math/trig.hh

psyqo::Angle atan2_fixed(int16_t y, int16_t x);
psyqo::Angle LerpAngle(const psyqo::Angle &a, const psyqo::Angle &b, const psyqo::FixedPoint<10> &t);
  • atan2_fixed uses a compass convention, not standard math atan2: 0.0_pi = up, 0.5_pi = right, rotating clockwise — matches how the engine's angles are used elsewhere (e.g. GameObjectRotation), but don't assume the usual "0 = right, counter-clockwise" convention here.
  • LerpAngle is a plain linear lerp of the two angle values — it does not take the shortest path around the wrap point, so interpolating e.g. from 0.9_pi to -0.9_pi will swing the long way round through 0 rather than the short way through ±π.

Rand​

src/rand.hh

A simple, not cryptographically secure PRNG (adapted from the pcsx-redux examples). You own your own instance — MadnightEngine exposes one at g_madnightEngine.m_rand for convenience.

class Rand {
public:
uint32_t rand(); // 32-bit random number, never 0
template <uint32_t RANGE> uint32_t rand(); // [0, RANGE)
uint32_t rand(uint32_t max); // [0, max)
uint32_t rand(uint32_t min, uint32_t max); // [min, max)

// seed == 0 breaks the generator — avoid it. Recommended: seed from
// GPU::now() so you don't get the same sequence every run.
void seed(uint32_t seed);
};
g_madnightEngine.m_rand.seed(Renderer::Instance().GPU().now());
auto roll = g_madnightEngine.m_rand.rand(1, 6); // 1..5 -- rand(min, max) is exclusive of max