matma 0.1.0 is out on crates.io. It’s SIMD-first math for games and Vulkan with Slang/HLSL naming - a Rust port of vkm, my C++23 math library. Pure Rust, no third-party dependencies, and #![deny(unsafe_code)] everywhere except the small SIMD seam.
Think “GLM, but Vulkan conventions by default and Slang-style names so Rust and shader code mirror each other”.
use matma::*;
let q = quat::from_axis_angle(float3::Z, rad(30.0));
let model = compose_trs(float3::new(0.0, 1.0, 0.0), q, float3::ONE);
let view = look_at(float3::new(0.0, 0.0, 5.0), float3::ZERO, float3::UP);
let proj = perspective(rad(60.0), 16.0 / 9.0, 0.1, 100.0); // Vulkan clip: depth [0,1], Y down
let mvp = proj * view * model;
let clip = mvp * float4::new(0.0, 0.0, 0.0, 1.0);
let normal_packed: short2 = oct_encode16(float3::UP); // -> R16G16_SNORM vertex attribute
Why another math crate?
The shaders are written in Slang, so I wanted the CPU side to read the same: float3, float4x4, uint4, quat rather than Vec3 / Mat4, and * on vectors being component-wise like in a shader. (GLSL aliases such as vec3, mat4 and ivec2 are there too.) The other half is GPU interop - Vulkan’s clip space, std140 / std430 layouts and vertex packing - which usually ends up as hand-written glue in every project.
What’s in 0.1.0
- Vectors, matrices and quaternions:
Vector2/3/4<T>,Matrix<V, C>andQuaternion<T>are generic overf32,f64and the integer types;f32is the one with SIMD hooks. Swizzles,inverse,determinant,normal_matrix,slerp,look_rotation,decomposeand friends. - Vulkan conventions baked in:
[0, 1]depth and flipped Y inperspective/ortho. World space is right-handed, Y-up, forward is-Z; Vulkan’s Y-down only exists in clip space. - Column-major, column-vector math:
v' = M * v, composed right to left.float4x4is 64 contiguous bytes with the translation in the last column, so it memcpys straight into a UBOmat4. - Matrix shape in the type:
float3x4is 3 rows x 4 columns like Slang’sfloatRxC, and products check shapes at compile time (float2x3 * float3x2 = float2x2). - Shader-style common functions:
saturate,lerp,smoothstep,reflect,step, … generic over scalars and vectors, exactly like overload sets in a shader.min/max/clamptreat NaN the HLSL way, not the IEEE way. - GPU packing: snorm / unorm quantization (8/10/16-bit), octahedral normals, tangent + handedness packing and half floats -
const fnwherever nosqrtis involved. - SIMD: SSE2 on x86 / x86-64, NEON on AArch64 and a scalar fallback, behind one tiny
simdmodule. Theforce-scalarfeature switches the backend off to diff results.
std140 / std430 derives
My favourite part. Declare the uniform block with the same fields as the shader and derive its layout - the macro generates a padded struct at the GLSL offsets and checks them at compile time:
use matma::*;
// layout(std140) uniform CameraData { mat4 view; mat4 proj; vec3 eye; float near; };
#[derive(Clone, Copy, Std140)]
struct CameraData {
view: float4x4, // offset 0
proj: float4x4, // offset 64
eye: float3, // offset 128
near: f32, // offset 140 - tucked into the vec3's 4th lane, as in GLSL
} // size 144
The derive lives in the in-repo matma-derive crate (default feature derive); --no-default-features drops it entirely.
Source on GitHub.