ascending_graphics 0.38.4

A graphical rendering library for 2D, using wgpu and winit.
Documentation
struct wrapped_f32 {
  @size(16) elem: f32
}

struct Global {
    views: array<mat4x4<f32>, 8>, //16
    scales: array<wrapped_f32, 8>, //4
    proj: mat4x4<f32>, //16
    inverse_proj: mat4x4<f32>, //16
    eye: vec3<f32>, //16
    @size(16)  size: vec2<f32>, //8
    @size(16)  seconds: f32, //4
};

struct AreaLights {
    pos: vec2<f32>,
    color: u32,
    max_distance: f32,
    anim_speed: f32,
    dither: f32,
    animate: u32,
    camera_view: u32,
};

struct DirLights {
    pos: vec2<f32>,
    color: u32,
    max_distance: f32,
    max_width: f32,
    anim_speed: f32,
    angle: f32,
    dither: f32,
    fade_distance: f32,
    edge_fade_distance: f32,
    animate: u32,
    camera_view: u32,
};

@group(0)
@binding(0)
var<uniform> global: Global;

struct VertexInput {
    @builtin(vertex_index) vertex_idx: u32,
    @location(0) v_pos: vec2<f32>,
    @location(1) world_color: vec4<f32>,
    @location(2) enable_lights: u32,
    @location(3) dir_count: u32,
    @location(4) area_count: u32,
    @location(5) pos: vec3<f32>,
    @location(6) size: vec2<f32>,
};

struct VertexOutput {
    @invariant @builtin(position) clip_position: vec4<f32>,
    @location(0) tex_coords: vec4<f32>,
    @location(1) col: vec4<f32>,
    @location(2) enable_lights: u32,
    @location(3) dir_count: u32,
    @location(4) area_count: u32,
};

const c_area_lights: u32 = 2000u;
const c_dir_lights: u32 = 1333u;

@group(1)
@binding(0)
var<uniform> u_areas: array<AreaLights, c_area_lights>;
@group(2)
@binding(0)
var<uniform> u_dirs: array<DirLights, c_dir_lights>;

fn srgb_to_linear(c: f32) -> f32 {
    if c <= 0.04045 {
        return c / 12.92;
    } else {
        return pow((c + 0.055) / 1.055, 2.4);
    }
}

fn unpack_color(color: u32) -> vec4<f32> {
    return vec4<f32>(
        srgb_to_linear(f32((color & 0xff0000u) >> 16u) / 255.0),
        srgb_to_linear(f32((color & 0xff00u) >> 8u) / 255.0),
        srgb_to_linear(f32((color & 0xffu)) / 255.0),
        f32((color & 0xff000000u) >> 24u) / 255.0,
    );
}

@vertex
fn vertex(
    vertex: VertexInput,
) -> VertexOutput {
    var result: VertexOutput;
    let v = vertex.vertex_idx % 4u;

    switch v {
        case 1u: {
            result.clip_position = global.proj * vec4<f32>(vertex.pos.x + vertex.size.x, vertex.pos.y, vertex.pos.z, 1.0);
        }
        case 2u: {
            result.clip_position = global.proj * vec4<f32>(vertex.pos.x + vertex.size.x, vertex.pos.y + vertex.size.y, vertex.pos.z, 1.0);
        }
        case 3u: {
            result.clip_position = global.proj * vec4<f32>(vertex.pos.x, vertex.pos.y + vertex.size.y, vertex.pos.z, 1.0);
        }
        default: {
            result.clip_position = global.proj * vec4<f32>(vertex.pos.x, vertex.pos.y, vertex.pos.z, 1.0);
        }
    }

    result.tex_coords = global.inverse_proj * result.clip_position;
    result.tex_coords = result.tex_coords / result.tex_coords.w;
    result.col = vec4<f32>(srgb_to_linear(vertex.world_color.r), srgb_to_linear(vertex.world_color.g), srgb_to_linear(vertex.world_color.b), vertex.world_color.a);
    result.enable_lights = vertex.enable_lights;
    result.dir_count = vertex.dir_count;
    result.area_count = vertex.area_count;
    return result;
}

const pi: f32 = 3.14159265;
const two_pi: f32 = 6.2831853;

fn flash_light(light_pos: vec2<f32>, pixel_pos: vec2<f32>, angle: f32, w_angle: f32, range: f32) -> f32 {
    let d = distance(light_pos, pixel_pos);
    let degree_radian = radians(angle + (w_angle / 2.0));
    let w_radian = clamp(radians(w_angle), 0.0, 2.0 * pi);
    // Calculate the start angle from the direction angle and the angle width of the "cone".
    let s_angle = degree_radian - (w_radian / 2.0);
    // Calculate the start vector.
    let s = vec2<f32>(cos(s_angle), sin(s_angle));
    // Calculate the direction between the pixel position and the light position and normalize the vector.
    let direction = normalize(pixel_pos - light_pos);
    let p_pos = vec3<f32>(pixel_pos.x, pixel_pos.y, 0.0);
    let l_pos = vec3<f32>(light_pos.x, light_pos.y, 0.0);
    let d_dir = vec3<f32>(direction.x, direction.y, 0.0);

    // Only emit light if the direction projected onto the start vector is within the angle width of our cone. 1.0 - (d / range)
    return select(1.0 - (d / range), 0.0, atan2(length(cross(vec3(direction, 0.0), vec3(s, 0.0))), dot(direction, s)) >= w_radian);
}

// Fragment shader
@fragment
fn fragment(vertex: VertexOutput,) -> @location(0) vec4<f32> {
    var col = vertex.col;

    if (vertex.enable_lights > 0u) {
        for(var i = 0u; i < min(vertex.area_count, c_area_lights); i += 1u) {
            let light = u_areas[i];
            let light_pos = vec3<f32>(light.pos.x, light.pos.y, 1.0);
            let scale_mat = mat4x4<f32> (
                vec4<f32>(global.scales[light.camera_view].elem, 0.0, 0.0, 0.0),
                vec4<f32>(0.0, global.scales[light.camera_view].elem, 0.0, 0.0),
                vec4<f32>(0.0, 0.0, 1.0, 0.0),
                vec4<f32>(0.0, 0.0, 0.0, 1.0),
            );
            let pos = (global.views[light.camera_view] * scale_mat) * vec4<f32>(light_pos, 1.0);
            var max_distance = light.max_distance * global.scales[light.camera_view].elem;

            let light_color = unpack_color(light.color);
            max_distance = max_distance - (f32(light.animate) *(1.0 * sin(global.seconds * light.anim_speed)));
            let dist = distance(pos.xy, vertex.tex_coords.xy);
            let cutoff = max(0.1, max_distance);
            let value = 1.0 - (dist / cutoff);
            var color2 = col; 
            let alpha = select(color2.a, mix(color2.a, light_color.a, value), dist <= cutoff);
            color2.a = alpha;
            col = select(color2, mix(color2, light_color, vec4<f32>(value)), dist <= cutoff);
        }

        for(var i = 0u; i < min(vertex.dir_count, c_dir_lights); i += 1u) {
            let light = u_dirs[i];
            let light_pos = vec3<f32>(light.pos.x, light.pos.y, 1.0);
            let scale_mat = mat4x4<f32> (
                vec4<f32>(global.scales[light.camera_view].elem, 0.0, 0.0, 0.0),
                vec4<f32>(0.0, global.scales[light.camera_view].elem, 0.0, 0.0),
                vec4<f32>(0.0, 0.0, 1.0, 0.0),
                vec4<f32>(0.0, 0.0, 0.0, 1.0),
            );
            let pos = (global.views[light.camera_view] * scale_mat) * vec4<f32>(light_pos, 1.0);
            var max_distance = light.max_distance * global.scales[light.camera_view].elem;
            var max_width = light.max_width * global.scales[light.camera_view].elem;
            let fade_distance = light.fade_distance * global.scales[light.camera_view].elem;
            let edge_fade_distance = light.edge_fade_distance * global.scales[light.camera_view].elem;

            let light_color = unpack_color(light.color);
            max_distance = max_distance - (f32(light.animate) *(1.0 * sin(global.seconds * light.anim_speed)));
            let dist_cutoff = max(0.1, max_distance);
            max_width = max_width - (f32(light.animate) *(1.0 * sin(global.seconds * light.anim_speed)));
            let width_cutoff = max(0.1, max_width);
            let value = flash_light(pos.xy, vertex.tex_coords.xy, light.angle, width_cutoff, dist_cutoff);
            var color2 = col; 
            let d = distance(pos.xy, vertex.tex_coords.xy);
            let alpha = select(color2.a, mix(color2.a, light_color.a, value),  d <= dist_cutoff);
            color2.a = alpha;
            col = select(color2, mix(color2, light_color, vec4<f32>(value)),  d <= dist_cutoff);
        }
    } 

    if (col.a <= 0.0) {
        discard;
    }

    return col;
}