nightshade 0.8.0

A cross-platform data-oriented game engine.
Documentation
struct Uniforms {
    view_proj: mat4x4<f32>,
    wall_freq_and_threshold: vec4<f32>,
    disp_and_alpha: vec4<f32>,
    uv_and_pad: vec4<f32>,
    sun_direction_and_intensity: vec4<f32>,
    sun_color: vec4<f32>,
    ambient_color: vec4<f32>,
}

struct InstanceData {
    model: mat4x4<f32>,
    camera_pos_object: vec4<f32>,
}

@group(0) @binding(0)
var<uniform> uniforms: Uniforms;

@group(1) @binding(0)
var ceiling_texture: texture_2d<f32>;
@group(1) @binding(1)
var ceiling_sampler: sampler;
@group(1) @binding(2)
var floor_texture: texture_2d<f32>;
@group(1) @binding(3)
var floor_sampler: sampler;
@group(1) @binding(4)
var wall_xy_texture: texture_2d<f32>;
@group(1) @binding(5)
var wall_xy_sampler: sampler;
@group(1) @binding(6)
var wall_zy_texture: texture_2d<f32>;
@group(1) @binding(7)
var wall_zy_sampler: sampler;
@group(1) @binding(8)
var noise_texture: texture_2d<f32>;
@group(1) @binding(9)
var noise_sampler: sampler;
@group(1) @binding(10)
var furniture_texture: texture_2d<f32>;
@group(1) @binding(11)
var furniture_sampler: sampler;
@group(1) @binding(12)
var exterior_texture: texture_2d<f32>;
@group(1) @binding(13)
var exterior_sampler: sampler;
@group(1) @binding(14)
var cubemap_texture: texture_cube<f32>;
@group(1) @binding(15)
var cubemap_sampler: sampler;

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

struct VertexInput {
    @location(0) position: vec3<f32>,
    @location(1) normal: vec3<f32>,
    @location(2) texcoord: vec2<f32>,
}

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) object_position: vec3<f32>,
    @location(1) texcoord: vec2<f32>,
    @location(2) lighting: vec4<f32>,
    @location(3) normal_object: vec3<f32>,
    @location(4) reflection: vec3<f32>,
    @location(5) camera_pos_object: vec3<f32>,
    @location(6) @interpolate(flat) model_scale: vec3<f32>,
}

@vertex
fn vertex_main(input: VertexInput, @builtin(instance_index) instance_index: u32) -> VertexOutput {
    let instance = instances[instance_index];
    let model = instance.model;
    let camera_pos_obj = instance.camera_pos_object.xyz;

    let world_position = model * vec4<f32>(input.position, 1.0);
    let clip_position = uniforms.view_proj * world_position;

    let world_normal = normalize((model * vec4<f32>(input.normal, 0.0)).xyz);
    let world_camera_dir = normalize(world_position.xyz - (model * vec4<f32>(camera_pos_obj, 1.0)).xyz);
    let reflection = reflect(world_camera_dir, world_normal);

    let light_dir = normalize(uniforms.sun_direction_and_intensity.xyz);
    let sun_intensity = uniforms.sun_direction_and_intensity.w;
    let light_strength = dot(world_normal, light_dir);
    let lighting = clamp(light_strength, 0.0, 1.0) * sun_intensity * vec4<f32>(uniforms.sun_color.xyz, 1.0) + uniforms.ambient_color;

    let scale = vec3<f32>(length(model[0].xyz), length(model[1].xyz), length(model[2].xyz));

    let abs_normal = abs(input.normal);
    var tex_scale: vec2<f32>;
    if (abs_normal.x > 0.5) {
        tex_scale = vec2<f32>(scale.z, scale.y);
    } else if (abs_normal.y > 0.5) {
        tex_scale = vec2<f32>(scale.x, scale.z);
    } else {
        tex_scale = vec2<f32>(scale.x, scale.y);
    }

    var output: VertexOutput;
    output.clip_position = clip_position;
    output.object_position = input.position;
    output.texcoord = input.texcoord * tex_scale * uniforms.uv_and_pad.x;
    output.lighting = lighting;
    output.normal_object = input.normal;
    output.reflection = reflection;
    output.camera_pos_object = camera_pos_obj;
    output.model_scale = scale;
    return output;
}

fn calculate_noise(position: vec3<f32>) -> f32 {
    let noise_x = textureSample(noise_texture, noise_sampler, position.xy / 32.0).r;
    let noise_y = textureSample(noise_texture, noise_sampler, position.zx / 32.0).r;
    let noise_z = textureSample(noise_texture, noise_sampler, position.yz / 32.0).r;
    return (noise_x + noise_y + noise_z) / 3.0;
}

@fragment
fn fragment_main(input: VertexOutput) -> @location(0) vec4<f32> {
    let position = input.object_position;
    let camera_position = input.camera_pos_object;
    let normal = input.normal_object;
    let wall_freq = uniforms.wall_freq_and_threshold.xyz * input.model_scale;

    let direction = position - camera_position;

    let room_pos = position + vec3<f32>(0.5, 0.5, 0.5);
    let room_cam = camera_position + vec3<f32>(0.5, 0.5, 0.5);

    let walls_floor = floor(room_pos * wall_freq);

    let light_variation_raw = step(calculate_noise(walls_floor), uniforms.wall_freq_and_threshold.w);
    let light_variation = light_variation_raw * 0.7 + 0.3;

    let dir_step = step(vec3<f32>(0.0), direction);
    let walls = (walls_floor + dir_step) / wall_freq;

    let ray_fractions = (walls - room_cam) / direction;

    let interior_scale = 4.0 * input.model_scale;
    let intersection_xy = interior_scale.xy * (room_cam + ray_fractions.z * direction).xy;
    let intersection_xz = interior_scale.xz * (room_cam + ray_fractions.y * direction).xz;
    let intersection_zy = interior_scale.zy * (room_cam + ray_fractions.x * direction).zy;

    let ceiling_colour = textureSample(ceiling_texture, ceiling_sampler, intersection_xz);
    let floor_colour = textureSample(floor_texture, floor_sampler, intersection_xz);
    let vertical_colour = mix(floor_colour, ceiling_colour, step(0.0, direction.y));
    let wall_xy_colour = textureSample(wall_xy_texture, wall_xy_sampler, intersection_xy);
    let wall_zy_colour = textureSample(wall_zy_texture, wall_zy_sampler, intersection_zy);

    let x_vs_z = step(ray_fractions.x, ray_fractions.z);
    var walls_colour = mix(wall_xy_colour, wall_zy_colour, x_vs_z);
    let ray_fraction_x_vs_z = mix(ray_fractions.z, ray_fractions.x, x_vs_z);
    let xz_vs_y = step(ray_fraction_x_vs_z, ray_fractions.y);
    walls_colour = mix(vertical_colour, walls_colour, xz_vs_y);
    let ray_fraction_winner = mix(ray_fractions.y, ray_fraction_x_vs_z, xz_vs_y);

    let d = dot(position, normal) - uniforms.disp_and_alpha.w;
    let alpha_ray_fraction = (d - dot(camera_position, normal)) / dot(direction, normal);
    let alpha_plane_point = alpha_ray_fraction * direction + camera_position;

    let scaled_alpha = alpha_plane_point * input.model_scale;
    let alpha_plane_uv = vec2<f32>(
        scaled_alpha.x + scaled_alpha.z,
        -scaled_alpha.y
    ) * 4.0;
    let alpha_plane_colour = textureSample(furniture_texture, furniture_sampler, alpha_plane_uv);

    let alpha_plane_weight = step(alpha_ray_fraction, ray_fraction_winner) * (1.0 - alpha_plane_colour.a);

    var interior_colour = mix(walls_colour, alpha_plane_colour, alpha_plane_weight);
    interior_colour = interior_colour * light_variation;

    let diffuse_colour = textureSample(exterior_texture, exterior_sampler, input.texcoord);
    let cube_colour = textureSample(cubemap_texture, cubemap_sampler, input.reflection);

    let blended = mix(
        diffuse_colour * input.lighting,
        cube_colour + interior_colour,
        diffuse_colour.a
    );

    return vec4<f32>(blended.rgb, 1.0);
}