Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit44fd9e59bd

README.md

rigmath

Quaternion algebra, vector helpers, and forward kinematics over a bone hierarchy. One definition of the math, shared by retargeting and IK.

Quaternions are { x, y, z, w } arrays and vectors are { x, y, z } arrays, matching the conventions the engine's rig data already uses, so values read straight out of ecs.Skeleton.bones or a parsed .rig need no conversion.

Exports

Vectors:

  • vdot(a, b) -> number — dot product.
  • vcross(a, b) -> { number } — cross product.
  • vlen(v) -> number — Euclidean length.
  • vsub(a, b), vadd(a, b), vscale(v, s) -> { number } — component-wise arithmetic.
  • vnormalize(v) -> { number } — unit vector; a zero-length input returns zero.
  • vperpendicular(v) -> { number } — a deterministic unit vector at right angles to v.

Quaternions:

  • IDENTITY — { 0, 0, 0, 1 }.
  • qmul(a, b) -> { number } — Hamilton product; applies b, then a.
  • qnormalize(q), qinverse(q) -> { number }.
  • qrotvec(q, v) -> { number } — rotate a vector.
  • shortestArc(a, b) -> { number } — the rotation carrying unit vector a onto b.
  • axisAngle(axis, angle) -> { number } — from an axis and radians.
  • qslerp(a, b, t) -> { number } — shortest-arc interpolation.
  • qangle(q) -> number — rotation magnitude in radians, [0, pi].
  • signedAngle(a, b, axis) -> number — roll from a to b about axis, in radians.
  • swingTwist(q, axis) -> ({ number }, { number }) — twist about axis, then the remaining swing.

Scalars:

  • isFinite(n) -> boolean, clamp(v, lo, hi) -> number.

Forward kinematics:

  • computeGlobals(bones) -> (gRot, gPos) — global rest transforms from local ones.
  • computeBoneLengths(bones, gPos) -> { number } — each bone's distance to its farthest child.

Usage

local rigmath = require("modules.rigmath")

-- Point a bone's forward axis at a target.
local dir = rigmath.vnormalize(rigmath.vsub(targetPos, bonePos))
local swing = rigmath.shortestArc(rigmath.qrotvec(boneRot, { 0, 0, 1 }), dir)
local aimed = rigmath.qmul(swing, boneRot)

-- Blend the result in at a weight.
local final = rigmath.qslerp(boneRot, aimed, 0.5)

Notes

  • Degenerate input never produces NaN. A zero-length vector normalizes to zero, a degenerate quaternion normalizes to identity, and shortestArc on antiparallel vectors resolves to a half turn about a perpendicular axis.
  • computeGlobals tolerates any bone ordering, including a parent listed after its child, and falls back to the local transform for a bone left unresolved by a cyclic parent.
  • Bone parent indices are 0-based with -1 for a root, matching the rig format; the returned arrays are 1-based and parallel to the input.