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);
}