bevy_elements 0.7.4

Living environments for Bevy: sun, moon and stars, climate-driven weather, GPU wind, volumetric clouds, rain, snow and fog, lightning, terrain with hydraulic and thermal erosion, flooding, snow cover, wetness and dust — simulated in compute shaders.
Documentation
// source_map_build: rasterise the water sources and sinks and the precipitation blockers into the
// per-cell source map. Runs when sources, sinks, blockers or the grid change, not every substep.
//
//   source_map[cell].x   sum of the depth rates of the sources covering the cell, m/s (sinks < 0)
//   source_map[cell].y   rain mask: 0 when a blocker box lies above the cell's surface, else 1
//
// A source covers the cells whose centres lie within its radius and at least the cell that holds
// its centre (the CPU counts the cells with the same expression, `model::covers`). A cell is
// shaded when the vertical line up from the surface point at its centre meets a blocker box (the
// same inverse-matrix box test as the precipitation kernel).

import bevy_elements::grid::GridParams;

struct HydroParams {
    grid: GridParams,
    sim: vec4<f32>,
    weather: vec4<f32>,
    misc: vec4<f32>,
    // z: sources uploaded. w: blockers uploaded.
    flags: vec4<u32>,
    erosion: vec4<f32>,
    thermal: vec4<f32>,
    // x: terrain y0 (m).
    extra: vec4<f32>,
}

struct Blocker {
    // Rows of the inverse of the box's affine matrix: local = rot · (world − centre).
    rot: array<vec4<f32>, 3>,
    centre: vec3<f32>,
    extent: vec3<f32>,
}

@group(0) @binding(0) var<uniform> params: HydroParams;
@group(0) @binding(1) var<storage, read> bedrock: array<f32>;
@group(0) @binding(2) var<storage, read> sediment: array<f32>;
// xy: world XZ of the centre, z: radius (m), w: depth rate per covered cell (m/s).
@group(0) @binding(3) var<storage, read> sources: array<vec4<f32>>;
@group(0) @binding(4) var<storage, read> blockers: array<Blocker>;
@group(0) @binding(5) var<storage, read_write> source_map: array<vec2<f32>>;

const MISS: f32 = 2.0;
// How far above the surface a blocker still shelters it, m.
const SHELTER_HEIGHT_M: f32 = 10000.0;

// Whether the segment p0 → p1 meets the blocker box.
fn segment_hits(b: Blocker, p0: vec3<f32>, p1: vec3<f32>) -> bool {
    let d0 = p0 - b.centre;
    let d1 = p1 - b.centre;
    let q0 = vec3<f32>(dot(b.rot[0].xyz, d0), dot(b.rot[1].xyz, d0), dot(b.rot[2].xyz, d0));
    let q1 = vec3<f32>(dot(b.rot[0].xyz, d1), dot(b.rot[1].xyz, d1), dot(b.rot[2].xyz, d1));
    let d = q1 - q0;
    var t_in = 0.0;
    var t_out = 1.0;
    for (var a = 0; a < 3; a += 1) {
        if abs(d[a]) < 1e-7 {
            if abs(q0[a]) > b.extent[a] {
                return false;
            }
        } else {
            let inv = 1.0 / d[a];
            let t_a = (-b.extent[a] - q0[a]) * inv;
            let t_b = (b.extent[a] - q0[a]) * inv;
            t_in = max(t_in, min(t_a, t_b));
            t_out = min(t_out, max(t_a, t_b));
        }
    }
    return t_in <= t_out;
}

@compute @workgroup_size(8, 8, 1)
fn source_map_build(@builtin(global_invocation_id) id: vec3<u32>) {
    let size = params.grid.size;
    if id.x >= size.x || id.y >= size.y {
        return;
    }
    let c = vec2<i32>(id.xy);
    let k = id.y * size.x + id.x;
    let centre = params.grid.origin + (vec2<f32>(c) + 0.5) * params.grid.cell;
    var rate = 0.0;
    for (var i = 0u; i < params.flags.z; i += 1u) {
        let s = sources[i];
        let d = centre - s.xy;
        let home = vec2<i32>(floor((s.xy - params.grid.origin) / params.grid.cell));
        if dot(d, d) <= s.z * s.z || (home.x == c.x && home.y == c.y) {
            rate += s.w;
        }
    }
    var mask = 1.0;
    if params.flags.w > 0u {
        let p0 = vec3<f32>(centre.x, params.extra.x + bedrock[k] + sediment[k], centre.y);
        let p1 = p0 + vec3<f32>(0.0, SHELTER_HEIGHT_M, 0.0);
        for (var i = 0u; i < params.flags.w; i += 1u) {
            if segment_hits(blockers[i], p0, p1) {
                mask = 0.0;
                break;
            }
        }
    }
    source_map[k] = vec2<f32>(rate, mask);
}