kiss3d 0.45.0

Keep it simple, stupid, 2D and 3D graphics engine for Rust.
Documentation
import package::common::unpack_mat3;
// 2D GPU skinning: each vertex is transformed by a weighted blend of up to four
// joint matrices, then by the object model transform and the camera. Joint matrices
// (bone world × inverse-bind, as 2D affine 3x3) are supplied per frame.

const MAX_JOINTS: u32 = 32u;

struct FrameUniforms {
    view_0: vec4<f32>,
    view_1: vec4<f32>,
    view_2: vec4<f32>,
    proj_0: vec4<f32>,
    proj_1: vec4<f32>,
    proj_2: vec4<f32>,
}

@group(0) @binding(0)
var<uniform> frame: FrameUniforms;

struct ObjectUniforms {
    model_0: vec4<f32>,
    model_1: vec4<f32>,
    model_2: vec4<f32>,
    color: vec4<f32>,
    // Each joint is three vec4 (padded 3x3 columns): joints[3*j + {0,1,2}].
    joints: array<vec4<f32>, 96>,
}

@group(1) @binding(0)
var<uniform> obj: ObjectUniforms;

@group(2) @binding(0)
var t_albedo: texture_2d<f32>;
@group(2) @binding(1)
var s_albedo: sampler;

struct VertexInput {
    @location(0) position: vec2<f32>,
    @location(1) tex_coord: vec2<f32>,
    @location(2) joints: vec4<u32>,
    @location(3) weights: vec4<f32>,
}

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) tex_coord: vec2<f32>,
}

fn joint_mat(j: u32) -> mat3x3<f32> {
    let b = 3u * j;
    return unpack_mat3(obj.joints[b], obj.joints[b + 1u], obj.joints[b + 2u]);
}

@vertex
fn vs_main(vertex: VertexInput) -> VertexOutput {
    var out: VertexOutput;

    let p = vec3<f32>(vertex.position, 1.0);
    // Weighted blend of the four influencing joints.
    var skinned = vec3<f32>(0.0);
    skinned += vertex.weights.x * (joint_mat(vertex.joints.x) * p);
    skinned += vertex.weights.y * (joint_mat(vertex.joints.y) * p);
    skinned += vertex.weights.z * (joint_mat(vertex.joints.z) * p);
    skinned += vertex.weights.w * (joint_mat(vertex.joints.w) * p);

    let view = unpack_mat3(frame.view_0, frame.view_1, frame.view_2);
    let proj = unpack_mat3(frame.proj_0, frame.proj_1, frame.proj_2);
    let model = unpack_mat3(obj.model_0, obj.model_1, obj.model_2);

    let world = model * vec3<f32>(skinned.xy, 1.0);
    var projected = proj * view * world;
    projected.z = 0.0;

    out.clip_position = vec4<f32>(projected, 1.0);
    out.tex_coord = vertex.tex_coord;
    return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    return textureSample(t_albedo, s_albedo, in.tex_coord) * obj.color;
}