frenderer 0.10.0

The friendly WGPU renderer.
Documentation
// A square!
var<private> VERTICES:array<vec2<f32>,6> = array<vec2<f32>,6>(
    // Bottom left, bottom right, top left; then top left, bottom right, top right.
    vec2<f32>(-0.5, -0.5),
    vec2<f32>(0.5, -0.5),
    vec2<f32>(-0.5, 0.5),
    vec2<f32>(-0.5, 0.5),
    vec2<f32>(0.5, -0.5),
    vec2<f32>(0.5, 0.5)
);

struct Camera {
    screen_pos: vec2<f32>,
    screen_size: vec2<f32>,
}

struct UVData {
    sheet_depth:u32,
    xy:u32,
    wh:u32, // actually two i16s
    colormod:u32 // actually four u8s
}

@group(0) @binding(0)
var<uniform> camera: Camera;
@group(0) @binding(1)
var<storage, read> s_world: array<vec4<f32>>;
@group(0) @binding(2)
var<storage, read> s_sheet: array<UVData>;

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) tex_coords: vec2<f32>,
    @location(1) @interpolate(flat) tex_index: u32,
    @location(2) colormod: vec4<f32>
}

fn u32_to_vec4(in:u32) -> vec4<f32> {
  let r = (in >> 24u) & 0x000000FFu;
  let g = (in >> 16u) & 0x000000FFu;
  let b = (in >>  8u) & 0x000000FFu;
  let a = in & 0x000000FFu;
  return vec4(f32(r)/255.0, f32(g)/255.0, f32(b)/255.0, f32(a)/255.0);
}

fn sprite_to_vert(trf:vec4<f32>, uvs:UVData, norm_vert:vec2<f32>) -> VertexOutput {
  let center:vec2<f32> = trf.yz;
  let size_bits:u32 = bitcast<u32>(trf.x);
  let size:vec2<f32> = vec2(f32(size_bits & 0x0000FFFFu),
                            f32((size_bits & 0xFFFF0000u) >> 16u)
                            );
  let tex_layer = uvs.sheet_depth & 0x0000FFFFu;
  let tex_depth = (uvs.sheet_depth & 0xFFFF0000u) >> 16u;
  let tex_size:vec2<u32> = textureDimensions(t_diffuse);
  let rot:f32 = trf.w;
  let sinrot:f32 = sin(rot);
  let cosrot:f32 = cos(rot);
  // scale
  var scaled = norm_vert*size;
  var rotated = vec2(
                     scaled.x*cosrot-scaled.y*sinrot,
                     scaled.x*sinrot+scaled.y*cosrot
                     );
  // now translate by trf (center, size):
  let world_pos = (center) + rotated;
  let camera_pos = world_pos - camera.screen_pos;
  let box_pos = camera_pos / (camera.screen_size*0.5);
  let ndc_pos = vec4(box_pos.xy, 0.0, 1.0) - vec4(1.0, 1.0, 0.0, 0.0);
  let tex_uvxy:vec2<f32> = unpack2x16unorm(uvs.xy)*65535.0;
  let tex_uvwh:vec2<f32> = unpack2x16snorm(uvs.wh)*32767.0;
  let tex_corner = vec2(tex_uvxy.x / f32(tex_size.x), tex_uvxy.y / f32(tex_size.y));
  let tex_uv_size = vec2(tex_uvwh.x / f32(tex_size.x), tex_uvwh.y / f32(tex_size.y));
  let norm_uv = vec2(norm_vert.x+0.5, 1.0-(norm_vert.y+0.5));
  let colormod = u32_to_vec4(uvs.colormod);
  // Larger y = smaller depth = closer to screen
  return VertexOutput(ndc_pos+vec4(0.0, 0.0, f32(tex_depth)/65535.0, 0.0), tex_corner + norm_uv*tex_uv_size, tex_layer, colormod);
}

@vertex
fn vs_storage_main(@builtin(vertex_index) in_vertex_index: u32, @builtin(instance_index) sprite_index:u32) -> VertexOutput {
  // We'll just look up the vertex data in those constant arrays
  let trf = s_world[sprite_index];
  let uvs = s_sheet[sprite_index];
  return sprite_to_vert(trf, uvs, VERTICES[in_vertex_index]);
}

@vertex
fn vs_storage_noinstance_main(@builtin(vertex_index) in_vertex_index: u32) -> VertexOutput {
    let sprite_index:u32 = in_vertex_index / u32(6);
    let vertex_index:u32 = in_vertex_index % u32(6);
    let trf = s_world[sprite_index];
    let uvs = s_sheet[sprite_index];
    return sprite_to_vert(trf, uvs, VERTICES[vertex_index]);
}

@vertex
fn vs_vbuf_main(@builtin(vertex_index) in_vertex_index: u32, @location(0) trf:vec4<f32>, @location(1) sheet_region:vec4<u32>) -> VertexOutput {
  return sprite_to_vert(trf, UVData(sheet_region.x, sheet_region.y, sheet_region.z, sheet_region.w), VERTICES[in_vertex_index]);
}


// Now our fragment shader needs two "global" inputs to be bound:
// A texture...
@group(1) @binding(0)
var t_diffuse: texture_2d_array<f32>;
// And a sampler.
@group(1) @binding(1)
var s_diffuse: sampler;
// Both are in the same binding group here since they go together naturally.

// Our fragment shader takes an interpolated `VertexOutput` as input now
@fragment
fn fs_main(in:VertexOutput) -> @location(0) vec4<f32> {
    // And we use the tex coords from the vertex output to sample from the texture.
    let color:vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords, in.tex_index);
    if color.w < 0.05 { discard; }
    // mod color by in.colormod
    let out_color = mix(color.xyz, in.colormod.xyz, in.colormod.w);
    return vec4<f32>(out_color.xyz, 1.0);
}