plutonium_engine 0.8.0

A pure-Rust, SVG-first 2D graphics engine built on wgpu, with text, widgets, animation, and a WebAssembly target
Documentation
struct TransformUniform {
    transform: mat4x4<f32>,
};

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

struct InstanceData {
    model: mat4x4<f32>,
    uv_offset: vec2<f32>,
    uv_scale: vec2<f32>,
    msdf_px_range: f32,
    _msdf_pad: array<f32, 3>,
    tint: vec4<f32>,
};

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

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) tex_coords: vec2<f32>,
    @location(1) uv_offset: vec2<f32>,
    @location(2) uv_scale: vec2<f32>,
    @location(3) tint: vec4<f32>,
    @location(4) msdf_px_range: f32,
};

@vertex
fn vs_main(
    @location(0) position: vec2<f32>,
    @location(1) tex_coords: vec2<f32>,
    @builtin(instance_index) instance_index: u32,
) -> VertexOutput {
    var output: VertexOutput;
    let inst = instances[instance_index];
    output.position = transformUniform.transform * inst.model * vec4<f32>(position, 0.0, 1.0);
    output.tex_coords = tex_coords;
    output.uv_offset = inst.uv_offset;
    output.uv_scale = inst.uv_scale;
    output.tint = inst.tint;
    output.msdf_px_range = inst.msdf_px_range;
    return output;
}

@group(0) @binding(0)
var my_texture: texture_2d<f32>;

@group(0) @binding(1)
var my_sampler: sampler;

struct UVTransform {
    uv_offset: vec2<f32>,
    uv_scale: vec2<f32>,
    tint: vec4<f32>,
};

@group(2) @binding(0)
var<uniform> uvTransform: UVTransform;

fn median3(v: vec3<f32>) -> f32 {
    return max(min(v.x, v.y), min(max(v.x, v.y), v.z));
}

@fragment
fn fs_main(
    @location(0) tex_coords: vec2<f32>,
    @location(1) uv_offset: vec2<f32>,
    @location(2) uv_scale: vec2<f32>,
    @location(3) tint: vec4<f32>,
    @location(4) msdf_px_range: f32,
) -> @location(0) vec4<f32> {
    let adjusted_tex_coords = tex_coords * uv_scale + uv_offset;
    // MSDF data must be sampled from base mip only; mips corrupt encoded distances.
    let sample_rgba = textureSampleLevel(my_texture, my_sampler, adjusted_tex_coords, 0.0);
    let msdf_sd = median3(sample_rgba.rgb) - 0.5;
    let sdf_sd = sample_rgba.a - 0.5;

    let px_range = max(msdf_px_range, 1.0);
    // Clamp MSDF to monochrome SDF neighborhood to suppress corner artifacts.
    let clamp_width = 1.0 / px_range;
    let msdf_clamped = clamp(msdf_sd, sdf_sd - clamp_width, sdf_sd + clamp_width);
    // Use clamped MSDF distance directly; alpha-SDF envelope min-clamping can
    // bias glyph sidebearings in this generator and create pair-specific
    // visual spacing drift (e.g. `j/u` vs `r/t`).
    let sd = msdf_clamped;
    let atlas_dims = vec2<f32>(textureDimensions(my_texture));
    let unit_range = vec2<f32>(px_range, px_range) / max(atlas_dims, vec2<f32>(1.0, 1.0));
    let screen_tex_size = vec2<f32>(1.0, 1.0) / max(fwidth(adjusted_tex_coords), vec2<f32>(1e-6, 1e-6));
    let screen_px_range = max(0.5 * dot(unit_range, screen_tex_size), 1.0);
    let alpha = clamp(screen_px_range * sd + 0.5, 0.0, 1.0);

    let out_tint = tint * uvTransform.tint;
    return vec4<f32>(out_tint.rgb, out_tint.a * alpha);
}