"""NumPy matrix helpers for TRS composition and GPU upload."""
import numpy as np
[docs]
def mat4_from_trs(
pos: tuple[float, float, float] | np.ndarray,
rot,
scl: tuple[float, float, float] | np.ndarray,
) -> np.ndarray:
"""Build model matrix from position, rotation quaternion, and scale.
Args:
pos: Position (x, y, z)
rot: Rotation quaternion — Quat or any object with .w/.x/.y/.z
scl: Scale (x, y, z)
Returns:
4x4 model matrix as numpy array (Translate * Rotate * Scale)
"""
from ..math import quat_to_mat4, scale, translate
return translate(pos) @ quat_to_mat4(rot) @ scale(scl)
[docs]
def batch_mat4_from_trs(
positions: np.ndarray,
rotations: np.ndarray,
scales: np.ndarray,
) -> np.ndarray:
"""Build N model matrices from arrays of positions, quaternions, and scales.
Args:
positions: (N, 3) float32 positions
rotations: (N, 4) float32 quaternions [w, x, y, z]
scales: (N, 3) float32 scale factors
Returns:
(N, 4, 4) float32 model matrices (Translate * Rotate * Scale)
"""
n = positions.shape[0]
w, x, y, z = rotations[:, 0], rotations[:, 1], rotations[:, 2], rotations[:, 3]
xx, yy, zz = x * x, y * y, z * z
xy, xz, yz = x * y, x * z, y * z
wx, wy, wz = w * x, w * y, w * z
sx, sy, sz = scales[:, 0], scales[:, 1], scales[:, 2]
out = np.zeros((n, 4, 4), dtype=np.float32)
out[:, 0, 0] = (1.0 - 2.0 * (yy + zz)) * sx
out[:, 0, 1] = (2.0 * (xy - wz)) * sy
out[:, 0, 2] = (2.0 * (xz + wy)) * sz
out[:, 1, 0] = (2.0 * (xy + wz)) * sx
out[:, 1, 1] = (1.0 - 2.0 * (xx + zz)) * sy
out[:, 1, 2] = (2.0 * (yz - wx)) * sz
out[:, 2, 0] = (2.0 * (xz - wy)) * sx
out[:, 2, 1] = (2.0 * (yz + wx)) * sy
out[:, 2, 2] = (1.0 - 2.0 * (xx + yy)) * sz
out[:, 0, 3] = positions[:, 0]
out[:, 1, 3] = positions[:, 1]
out[:, 2, 3] = positions[:, 2]
out[:, 3, 3] = 1.0
return out
[docs]
def mat4_to_bytes(m: np.ndarray) -> bytes:
"""Convert mat4 to bytes for GPU upload (64 bytes, row-major float32)."""
return np.ascontiguousarray(m, dtype=np.float32).tobytes()