mittens-engine 0.6.0

A Vulkan and OpenXR scene engine with ECS, reactive signals, and Meow Meow scripting
Documentation
use mittens_engine::engine;

#[derive(Debug, Clone, Copy)]
pub struct PerlinNoise {
    perm: [u8; 512],
}

impl PerlinNoise {
    pub fn new(seed: u32) -> Self {
        let mut base = [0u8; 256];
        for (i, slot) in base.iter_mut().enumerate() {
            *slot = i as u8;
        }

        let mut state = seed;
        for i in (1..256).rev() {
            state = state.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
            let j = (state as usize) % (i + 1);
            base.swap(i, j);
        }

        let mut perm = [0u8; 512];
        for i in 0..512 {
            perm[i] = base[i & 255];
        }

        Self { perm }
    }

    pub fn sample2(&self, x: f32, y: f32) -> f32 {
        let xi = x.floor() as i32 & 255;
        let yi = y.floor() as i32 & 255;

        let xf = x - x.floor();
        let yf = y - y.floor();

        let u = fade(xf);
        let v = fade(yf);

        let aa = self.perm[(self.perm[xi as usize] as usize + yi as usize) & 255];
        let ab = self.perm[(self.perm[xi as usize] as usize + ((yi + 1) as usize)) & 255];
        let ba = self.perm[(self.perm[((xi + 1) & 255) as usize] as usize + yi as usize) & 255];
        let bb =
            self.perm[(self.perm[((xi + 1) & 255) as usize] as usize + ((yi + 1) as usize)) & 255];

        let x1 = lerp(grad2(aa, xf, yf), grad2(ba, xf - 1.0, yf), u);
        let x2 = lerp(grad2(ab, xf, yf - 1.0), grad2(bb, xf - 1.0, yf - 1.0), u);
        lerp(x1, x2, v)
    }

    pub fn fractal2(&self, x: f32, y: f32, octaves: u32, lacunarity: f32, gain: f32) -> f32 {
        let mut freq = 1.0;
        let mut amp = 1.0;
        let mut total = 0.0;
        let mut norm = 0.0;

        for _ in 0..octaves.max(1) {
            total += self.sample2(x * freq, y * freq) * amp;
            norm += amp;
            freq *= lacunarity;
            amp *= gain;
        }

        if norm > 0.0 { total / norm } else { 0.0 }
    }
}

fn fade(t: f32) -> f32 {
    t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
}

fn lerp(a: f32, b: f32, t: f32) -> f32 {
    a + (b - a) * t
}

fn grad2(hash: u8, x: f32, y: f32) -> f32 {
    match hash & 0x7 {
        0 => x + y,
        1 => -x + y,
        2 => x - y,
        3 => -x - y,
        4 => x,
        5 => -x,
        6 => y,
        _ => -y,
    }
}

pub fn spawn_perlin_cube_patch(
    universe: &mut engine::Universe,
    anchor: [f32; 3],
    width: usize,
    depth: usize,
) {
    use engine::ecs::component::{ColorComponent, RenderableComponent, TransformComponent};

    if width == 0 || depth == 0 {
        return;
    }

    let noise = PerlinNoise::new(0xCA7F_2026);
    let spacing = 0.22_f32;
    let cube_size = 0.20_f32;
    let sample_scale = 0.052_f32;
    let height_scale = 2.6_f32;
    let half_w = (width as f32 - 1.0) * spacing * 0.5;
    let half_d = (depth as f32 - 1.0) * spacing * 0.5;

    let root = universe
        .world
        .add_component(TransformComponent::new().with_position(anchor[0], anchor[1], anchor[2]));
    universe.add(root);

    for z in 0..depth {
        for x in 0..width {
            let nx = x as f32 * sample_scale;
            let nz = z as f32 * sample_scale;
            let h = noise.fractal2(nx, nz, 5, 2.0, 0.5);
            let y = h * height_scale;

            let tx = universe.world.add_component(
                TransformComponent::new()
                    .with_position(x as f32 * spacing - half_w, y, z as f32 * spacing - half_d)
                    .with_scale(cube_size, cube_size, cube_size),
            );
            let renderable = universe.world.add_component(RenderableComponent::cube());

            let shade = ((h + 1.0) * 0.5).clamp(0.0, 1.0);
            let r = 0.10 + 0.18 * shade;
            let g = 0.28 + 0.55 * shade;
            let b = 0.40 + 0.22 * shade;
            let color = universe
                .world
                .add_component(ColorComponent::rgba(r, g, b, 1.0));

            let _ = universe.attach(root, tx);
            let _ = universe.attach(tx, renderable);
            let _ = universe.attach(renderable, color);
        }
    }
}