Builtin Library
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 tov.
Quaternions:
IDENTITY—{ 0, 0, 0, 1 }.qmul(a, b) -> { number }— Hamilton product; appliesb, thena.qnormalize(q),qinverse(q) -> { number }.qrotvec(q, v) -> { number }— rotate a vector.shortestArc(a, b) -> { number }— the rotation carrying unit vectoraontob.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 fromatobaboutaxis, in radians.swingTwist(q, axis) -> ({ number }, { number })— twist aboutaxis, 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
shortestArcon antiparallel vectors resolves to a half turn about a perpendicular axis. computeGlobalstolerates 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
parentindices are 0-based with -1 for a root, matching the rig format; the returned arrays are 1-based and parallel to the input.