i wanna finally finish Eightfold but god it's been almost two years(!!) and i gotta figure out what the hell i was doing when i last touched it
at least it's almost done lol
EDIT: oh my god, everything is commented as fuck, i'm such a big brain smartgirl for doing that, thank you past me
like, look at this, these are some ridiculously informative comments (this is from some sample code, though; the comments in the actual library are a bit less thorough):
/// Convert a `glTF` [Transform](gltf::scene::Transform) to a [nalgebra]
/// [Affine3].
///
/// An affine transformation is, in order, a non-uniform scaling, a rotation, and then a
/// translation.
///
/// A `glTF` transformation is stored either as an affine transformation matrix, or as separate
/// translation, rotation, and scale components. Therefore, the most general possible kind of
/// transformation is affine, which means that the *least* general kind of transformation we can
/// return is an [Affine3].
///
/// ## See Also
///
/// * [nalgebra's explanation of transformations](https://www.nalgebra.org/docs/user_guide/points_and_transformations/#transformations)
pub fn gltf_to_nalgebra(g: &gltf::scene::Transform) -> Affine3<f32> {
match g {
// the Matrix variant is stored as a column-major [[f32; 4]; 4], so we can just transmute
// that into an [f32; 16] and use that directly.
gltf::scene::Transform::Matrix { matrix: ref m } => {
// the glTF spec states that matrix transformations *must* be decomposable to their
// translation, rotation, and scale components. Therefore, a matrix from a compliant
// glTF file can be converted directly to an Affine3.
Affine3::<f32>::from_matrix_unchecked(Matrix4::from_column_slice(
// arrays are stored contiguously, so, in memory, an [[f32; 4]; 4] is identical to
// an [f32; 16], which means we can safely interpret one to the other.
//
// `std::mem::transmute` tells Rust's compiler that we want to interpret something of
// type `A` as, instead, something of type `B`. It doesn't actually do anything at
// runtime.
unsafe { std::mem::transmute::<&[[f32; 4]; 4], &[f32; 16]>(m) }.as_slice(),
))
}
// this is a bit more complicated, because we have to convert these three components into
// a single Transform3.
gltf::scene::Transform::Decomposed {
translation: ref trans, // [x, y, z]
rotation: ref rot, // unit quaternion, [x, y, z, w]
scale: ref scl, // [x, y, z]
} => {
// Store the resulting homogeneous Matrix4 as an Affine3.
// We don't have to check for correctness, because we already know
// that the matrix we're storing represents an affine transformation.
Affine3::from_matrix_unchecked(
// construct an Isometry (a rotation followed by a
// translation) from `trans` and `rot`
Isometry3::from_parts(
Translation3::from(*trans), // <- we can convert `trans` directly
Unit::new_unchecked(Quaternion::from(*rot)), // <- same with `rot`. The glTF spec
// requires rotations to be stored as
// unit quaternions, so we don't need
// to validate that here.
// Conveniently, nalgebra and glTF
// use the same format for
// quaternions.
)
// convert the Isometry3 to a homogenous Matrix4, so we can
// apply the scaling (remember, an isometry is a rotation
// followed by a translation; it, by definition, cannot have a
// rotation, and the Isometry3 struct reflects that.)
.to_homogeneous()
// apply the scaling, resulting in a matrix M = Translation * Rotation * Scale.
//
// Reminder: when transforming a point using a matrix, the transformations
// are applied to the point in the reverse of the order they were applied to the
// matrix. So, a point transformed by a `TRS` (`Translation * Rotation * Scale`)
// matrix is first scaled, then rotated, then translated. This is important because
// applying those transformations in another order would produce a different end
// result.
.prepend_nonuniform_scaling(&Vector3::from(*scl)),
)
}
}
}