plutonium_engine 0.7.0

Pure Rust Graphics Engine
Documentation
struct TransformUniform {
    transform: mat4x4<f32>,
};

@group(1) @binding(0)
var<uniform> transformUniform: TransformUniform;

struct RectInstanceData {
    model: mat4x4<f32>,
    color: vec4<f32>,
    corner_radius_px: f32,
    border_thickness_px: f32,
    _pad0: vec2<f32>,
    border_color: vec4<f32>,
    rect_size_px: vec2<f32>,
    _pad1: vec2<f32>,
    _pad2: vec4<f32>,
};

@group(3) @binding(0)
var<storage, read> instances: array<RectInstanceData>;

struct VSOut {
    @builtin(position) position: vec4<f32>,
    @location(0) local_pos_ndc: vec2<f32>,
    @location(1) color: vec4<f32>,
    @location(2) corner_radius_px: f32,
    @location(3) border_thickness_px: f32,
    @location(4) border_color: vec4<f32>,
    @location(5) rect_size_px: vec2<f32>,
};

@vertex
fn vs_main(
    @location(0) position: vec2<f32>,
    @builtin(instance_index) instance_index: u32,
) -> VSOut {
    var out: VSOut;
    let inst = instances[instance_index];
    let pos4 = vec4<f32>(position, 0.0, 1.0);
    let world = transformUniform.transform * inst.model * pos4;
    out.position = world;
    // Pass along local coordinates in the range [-1, 1]
    out.local_pos_ndc = position;
    out.color = inst.color;
    out.corner_radius_px = inst.corner_radius_px;
    out.border_thickness_px = inst.border_thickness_px;
    out.border_color = inst.border_color;
    out.rect_size_px = inst.rect_size_px;
    return out;
}

@fragment
fn fs_main(
    @location(0) local_pos_ndc: vec2<f32>,
    @location(1) color: vec4<f32>,
    @location(2) corner_radius_px: f32,
    @location(3) border_thickness_px: f32,
    @location(4) border_color: vec4<f32>,
    @location(5) rect_size_px: vec2<f32>,
) -> @location(0) vec4<f32> {
    // Convert local position in [-1,1] to pixel space centered at origin
    let half_size_px = 0.5 * rect_size_px;
    let p_px = local_pos_ndc * half_size_px; // now in pixels relative to center

    // Compute SDF for rounded rectangle (outer)
    let r = max(corner_radius_px, 0.0);
    let q = abs(p_px) - (half_size_px - vec2<f32>(r, r));
    let outside = max(q, vec2<f32>(0.0, 0.0));
    let dist_outer = length(outside) - r;

    // Anti-alias factor
    let aa = 1.0; // 1 px AA
    let alpha_fill = clamp(0.5 - dist_outer / aa, 0.0, 1.0);

    // Border handling
    var out_color: vec4<f32> = color;
    if (border_thickness_px > 0.0) {
        let inner_r = max(r - border_thickness_px, 0.0);
        let q_in = abs(p_px) - (half_size_px - vec2<f32>(inner_r, inner_r));
        let outside_in = max(q_in, vec2<f32>(0.0, 0.0));
        let dist_inner = length(outside_in) - inner_r;

        // border region where outer <= 0 and inner > 0 (approximately)
        let border_mask = clamp(0.5 - dist_outer / aa, 0.0, 1.0) * (1.0 - clamp(0.5 - dist_inner / aa, 0.0, 1.0));
        // mix border and fill based on which region we're in
        let fill_mask = clamp(0.5 - dist_inner / aa, 0.0, 1.0);
        let border_col = vec4<f32>(border_color.rgb, border_color.a * border_mask);
        let fill_col = vec4<f32>(color.rgb, color.a * fill_mask);
        // alpha composite border over fill
        out_color = border_col + fill_col * (1.0 - border_col.a);
        // Ensure we also apply outer edge AA
        out_color.a = max(out_color.a, alpha_fill);
    } else {
        out_color.a = alpha_fill;
    }

    return out_color;
}