Node transforms and scene-graph traversal.
A node positions itself either with a 16-float matrix or with separate
translation, rotation and scale — never both, and the specification is
explicit that the two forms compose as T * R * S, in that order. Getting
the order wrong produces a model that looks right until something is both
rotated and non-uniformly scaled, at which point it shears.
Matrices are column-major flat lists of 16 numbers, the same order glTF stores them in, so a matrix read from a file needs no rearranging.
Cycles
The node graph is a forest by specification, but nothing in a file enforces
it, and a node that lists an ancestor as its child makes naive traversal
loop until the machine dies. world_transforms/2 tracks the path it is on
and reports the cycle instead.
Summary
Functions
The identity transform.
A node's local transform.
Multiplies two column-major 4×4 matrices, a * b.
Transforms a point by a column-major matrix, with the implicit w = 1.
World transforms for every node reachable from a scene, keyed by node index.
Types
@type matrix() :: [number()]
A 4×4 transform as 16 numbers in column-major order.
Functions
@spec identity() :: matrix()
The identity transform.
iex> Gltf.Node.identity() |> Enum.take(4)
[1.0, 0.0, 0.0, 0.0]
A node's local transform.
Returns the node's matrix when it has one, otherwise composes T * R * S
from whichever of the three are present, defaulting each to its identity.
iex> Gltf.Node.local_transform(%{"translation" => [1, 2, 3]}) |> Enum.drop(12)
[1, 2, 3, 1.0]
Multiplies two column-major 4×4 matrices, a * b.
Applied to a column vector this means b acts first — which is why a parent
transform is the left operand.
Transforms a point by a column-major matrix, with the implicit w = 1.
@spec world_transforms( map(), keyword() ) :: {:ok, %{required(non_neg_integer()) => matrix()}} | {:error, Gltf.Error.t()}
World transforms for every node reachable from a scene, keyed by node index.
Nodes not reachable from the scene are absent rather than given an identity transform — an unreferenced node is not at the origin, it is nowhere.
Options
:scene— which scene to walk. Defaults to the document's ownscene, then to scene 0.