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
// The terrain's hand edit: one brush stroke over the bedrock layer (docs/research/terrain-sculpt.md).
//
//   terrain_sculpt   per cell inside the brush, bedrock += delta(kind, weight)
//
// `cells` is the *previous* pack (surface height in `.x`, m above the terrain's y0), read only, so
// the smooth and flatten kinds have no read-after-write race on the layer they write. The pack
// runs after this kernel in the same compute pass and carries the edit to everything that reads it.

import bevy_elements::grid::{GridParams, grid_index};
import bevy_elements::noise::perlin3;

struct SculptParams {
    grid: GridParams,
    // xy: brush centre, world XZ (m). z: radius (m). w: hardness, the fraction of the radius at full weight.
    area: vec4<f32>,
    // x: kind (0 raise, 1 lower, 2 smooth, 3 flatten, 4 noise). y: amount (raise, lower: m; smooth,
    // flatten: blend; noise: amplitude, m). z: flatten target above y0 (m) | noise feature size (m).
    // w: noise seed.
    shape: vec4<f32>,
}

@group(0) @binding(0) var<uniform> params: SculptParams;
@group(0) @binding(1) var<storage, read_write> bedrock: array<f32>;
@group(0) @binding(2) var<storage, read> cells: array<vec4<f32>>;

/// The brush weight at distance `d` from the centre: 1 inside the hardness plateau, a smoothstep
/// down to 0 at the radius, 0 beyond. Mirrored by `sculpt_weight` in `sculpt.rs`.
fn sculpt_weight(d: f32, r: f32, hardness: f32) -> f32 {
    let t = clamp((d / r - hardness) / (1.0 - hardness), 0.0, 1.0);
    let s = 1.0 - t;
    return s * s * (3.0 - 2.0 * s);
}

fn surface(c: vec2<i32>) -> f32 {
    return cells[grid_index(params.grid, c)].x;
}

@compute @workgroup_size(8, 8, 1)
fn terrain_sculpt(@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 p = params.grid.origin + (vec2<f32>(id.xy) + 0.5) * params.grid.cell;
    let d = length(p - params.area.xy);
    if d >= params.area.z {
        return;
    }
    let weight = sculpt_weight(d, params.area.z, params.area.w);
    let k = id.y * size.x + id.x;
    let kind = u32(params.shape.x + 0.5);
    let amount = params.shape.y;
    var delta = 0.0;
    if kind == 0u {
        delta = amount * weight;
    } else if kind == 1u {
        delta = -amount * weight;
    } else if kind == 2u || kind == 3u {
        let here = surface(c);
        if kind == 2u {
            var sum = 0.0;
            for (var j = -1; j <= 1; j += 1) {
                for (var i = -1; i <= 1; i += 1) {
                    if i != 0 || j != 0 {
                        sum += surface(c + vec2<i32>(i, j));
                    }
                }
            }
            delta = amount * weight * (sum / 8.0 - here);
        } else {
            delta = amount * weight * (params.shape.z - here);
        }
    } else {
        let q = vec3<f32>(p / params.shape.z, params.shape.w);
        let n = (perlin3(q) + 0.5 * perlin3(q * 2.0 + vec3<f32>(5.2, 1.3, 0.0)) + 0.25 * perlin3(q * 4.0 + vec3<f32>(9.7, 8.3, 0.0))) / 1.75;
        delta = amount * weight * n;
    }
    bedrock[k] += delta;
}