nightshade-api 0.56.0

Procedural high level API for the nightshade game engine
Documentation
//! Generated geometry: a sine heightfield you can walk plus a field of cubes
//! that ripple every frame through one instanced batch.

use nightshade_api::prelude::*;

type Vertex = ([f32; 3], [f32; 3], [f32; 2]);

struct Field {
    cubes: Entity,
    gem: Entity,
    columns: Vec<(f32, f32)>,
}

const GRID: i32 = 24;

fn main() {
    run(setup, update).unwrap();
}

fn setup(world: &mut World) -> Field {
    let (vertices, indices) = heightfield();
    let terrain =
        spawn_custom_mesh_collider(world, "terrain", &vertices, &indices, vec3(0.0, 0.0, 0.0));
    set_color(world, terrain, GREEN);

    register_texture(world, "checker", 8, 8, &checker_pixels());
    register_material(
        world,
        "tiles",
        Material {
            base_texture: Some("checker".to_string()),
            roughness: 0.6,
            ..Default::default()
        },
    );

    let mut columns = Vec::new();
    let mut transforms = Vec::new();
    for row in 0..8 {
        for column in 0..8 {
            let x = column as f32 * 1.5 - 6.0;
            let z = row as f32 * 1.5 - 6.0;
            columns.push((x, z));
            transforms.push(InstanceTransform::new(
                vec3(x, 4.0, z),
                nalgebra_glm::Quat::identity(),
                vec3(0.4, 0.4, 0.4),
            ));
        }
    }
    let cubes = spawn_instanced_with_material(world, Shape::Cube, transforms, "tiles");

    let (gem_vertices, gem_indices) = gem();
    let gem = spawn_custom_mesh(
        world,
        "gem",
        &gem_vertices,
        &gem_indices,
        vec3(0.0, 7.0, 0.0),
    );
    set_color(world, gem, PURPLE);
    set_emissive(world, gem, [0.4, 0.2, 0.8], 3.0);

    first_person(world, vec3(0.0, 4.0, 18.0));
    spawn_text(world, "WASD walk the heightfield", ScreenAnchor::BottomLeft);

    Field {
        cubes,
        gem,
        columns,
    }
}

fn gem() -> (Vec<Vertex>, Vec<u32>) {
    let points: [[f32; 3]; 6] = [
        [1.0, 0.0, 0.0],
        [-1.0, 0.0, 0.0],
        [0.0, 1.4, 0.0],
        [0.0, -1.4, 0.0],
        [0.0, 0.0, 1.0],
        [0.0, 0.0, -1.0],
    ];
    let vertices: Vec<Vertex> = points
        .iter()
        .map(|point| {
            let length = (point[0] * point[0] + point[1] * point[1] + point[2] * point[2]).sqrt();
            (
                *point,
                [point[0] / length, point[1] / length, point[2] / length],
                [0.5, 0.5],
            )
        })
        .collect();
    let indices = vec![
        2, 4, 0, 2, 0, 5, 2, 5, 1, 2, 1, 4, 3, 0, 4, 3, 5, 0, 3, 1, 5, 3, 4, 1,
    ];
    (vertices, indices)
}

fn heightfield() -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();
    for row in 0..=GRID {
        for column in 0..=GRID {
            let x = column as f32 - GRID as f32 * 0.5;
            let z = row as f32 - GRID as f32 * 0.5;
            let height = (x * 0.4).sin() * (z * 0.4).cos() * 1.5;
            let u = column as f32 / GRID as f32;
            let v = row as f32 / GRID as f32;
            vertices.push(([x, height, z], [0.0, 1.0, 0.0], [u, v]));
        }
    }

    let stride = (GRID + 1) as u32;
    let mut indices = Vec::new();
    for row in 0..GRID as u32 {
        for column in 0..GRID as u32 {
            let top_left = row * stride + column;
            let top_right = top_left + 1;
            let bottom_left = top_left + stride;
            let bottom_right = bottom_left + 1;
            indices.extend_from_slice(&[
                top_left,
                bottom_left,
                top_right,
                top_right,
                bottom_left,
                bottom_right,
            ]);
        }
    }

    (vertices, indices)
}

fn checker_pixels() -> Vec<u8> {
    let mut pixels = Vec::with_capacity(8 * 8 * 4);
    for row in 0..8 {
        for column in 0..8 {
            let value: u8 = if (row + column) % 2 == 0 { 230 } else { 40 };
            pixels.extend_from_slice(&[value, value, value, 255]);
        }
    }
    pixels
}

fn update(world: &mut World, field: &mut Field) {
    rotate(world, field.gem, Vec3::y(), delta_time(world));
    let time = elapsed_seconds(world);
    let transforms = field
        .columns
        .iter()
        .map(|&(x, z)| {
            let height = 4.0 + (time * 2.0 + x + z).sin();
            InstanceTransform::new(
                vec3(x, height, z),
                nalgebra_glm::Quat::identity(),
                vec3(0.4, 0.4, 0.4),
            )
        })
        .collect();
    set_instances(world, field.cubes, transforms);
}