kiss3d 0.42.0

Keep it simple, stupid, 2D and 3D graphics engine for Rust.
Documentation
// Planar (2D) polyline shader for thick line rendering
// Based on bevy_polyline but simplified for 2D (no near-plane clipping, no depth)
//
// Uses instanced rendering where each instance is a line segment.
// Material data (color, width) is passed per-instance via vertex attributes.

// View uniforms
// Note: mat3x3 is stored as array<vec4<f32>, 3> for proper alignment
struct ViewUniforms {
    view_0: vec4<f32>,
    view_1: vec4<f32>,
    view_2: vec4<f32>,
    proj_0: vec4<f32>,
    proj_1: vec4<f32>,
    proj_2: vec4<f32>,
    viewport: vec4<f32>, // x, y, width, height
}

@group(0) @binding(0)
var<uniform> view: ViewUniforms;

// Vertex input - line segment endpoints with material data (per-instance)
struct VertexInput {
    @location(0) point_a: vec2<f32>,
    @location(1) width: f32,
    @location(2) point_b: vec2<f32>,
    @location(3) color: vec4<f32>,
    @builtin(vertex_index) index: u32,
}

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

// Reconstruct mat3x3 from padded vec4 columns
fn unpack_mat3(col0: vec4<f32>, col1: vec4<f32>, col2: vec4<f32>) -> mat3x3<f32> {
    return mat3x3<f32>(
        col0.xyz,
        col1.xyz,
        col2.xyz
    );
}

@vertex
fn vs_main(vertex: VertexInput) -> VertexOutput {
    // 6 vertices per line segment forming 2 triangles
    // Positions encode: y = perpendicular offset (-0.5 or 0.5), z = along line (0 or 1)
    var positions = array<vec3<f32>, 6u>(
        vec3(0.0, -0.5, 0.0),
        vec3(0.0, -0.5, 1.0),
        vec3(0.0, 0.5, 1.0),
        vec3(0.0, -0.5, 0.0),
        vec3(0.0, 0.5, 1.0),
        vec3(0.0, 0.5, 0.0)
    );
    let position = positions[vertex.index % 6u];

    // Reconstruct view and projection matrices
    let view_mat = unpack_mat3(view.view_0, view.view_1, view.view_2);
    let proj_mat = unpack_mat3(view.proj_0, view.proj_1, view.proj_2);
    let view_proj = proj_mat * view_mat;

    // Transform endpoints to clip space
    let clip0 = view_proj * vec3(vertex.point_a, 1.0);
    let clip1 = view_proj * vec3(vertex.point_b, 1.0);

    // For 2D, w is typically 1.0, so screen space is just xy
    let resolution = vec2(view.viewport.z, view.viewport.w);
    let screen0 = resolution * (0.5 * clip0.xy + 0.5);
    let screen1 = resolution * (0.5 * clip1.xy + 0.5);

    // Calculate basis vectors for the line in screen space
    let line_dir = screen1 - screen0;
    let line_length = length(line_dir);

    // Handle degenerate case where points are at the same location
    var x_basis: vec2<f32>;
    var y_basis: vec2<f32>;
    if line_length > 0.001 {
        x_basis = line_dir / line_length;
        y_basis = vec2(-x_basis.y, x_basis.x);
    } else {
        x_basis = vec2(1.0, 0.0);
        y_basis = vec2(0.0, 1.0);
    }

    let line_width = vertex.width;

    // Calculate offset from line center
    let pt_offset = line_width * (position.x * x_basis + position.y * y_basis);
    let pt0 = screen0 + pt_offset;
    let pt1 = screen1 + pt_offset;
    let pt = mix(pt0, pt1, position.z);

    var out: VertexOutput;
    // Convert back from screen space to clip space
    out.clip_position = vec4((2.0 * pt) / resolution - 1.0, 0.0, 1.0);
    out.color = vertex.color;
    return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    return vec4<f32>(in.color.rgb, in.color.a);
}