From 6444ac199c85afc7e09250f8b8144d824e52e57c Mon Sep 17 00:00:00 2001 From: Manan Karnik Date: Sun, 24 Dec 2023 23:26:18 +0530 Subject: [PATCH] feat: add planet generation --- src/bin/terrain.rs | 276 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 101 ++++++++--------- src/planet.rs | 164 +++++++++++++++++++++++++++ src/terrain.rs | 6 +- 5 files changed, 493 insertions(+), 55 deletions(-) create mode 100644 src/bin/terrain.rs create mode 100644 src/planet.rs diff --git a/src/bin/terrain.rs b/src/bin/terrain.rs new file mode 100644 index 0000000..e68e9f5 --- /dev/null +++ b/src/bin/terrain.rs @@ -0,0 +1,276 @@ +use bevy::{pbr::wireframe::WireframePlugin, prelude::*}; +use bevy_egui::{ + egui::{self, RichText}, + EguiContexts, EguiPlugin, +}; +use bevy_generative::{ + noise::{FunctionName, Method, Noise, Region}, + terrain::{Terrain, TerrainBundle, TerrainPlugin}, +}; +use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin}; +use egui::{ComboBox, DragValue, Slider}; + +#[cfg(target_arch = "wasm32")] +fn main() { + App::new() + .add_plugins(DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + canvas: Some("#bevy-canvas".into()), + ..default() + }), + ..default() + })) + .add_plugins(EguiPlugin) + .add_plugins(WireframePlugin) + .add_plugins(PanOrbitCameraPlugin) + .add_plugins(TerrainPlugin) + .add_systems(Startup, setup) + .add_systems(Update, gui) + .run(); +} +#[cfg(not(target_arch = "wasm32"))] +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_plugins(EguiPlugin) + .add_plugins(WireframePlugin) + .add_plugins(PanOrbitCameraPlugin) + .add_plugins(TerrainPlugin) + .add_systems(Startup, setup) + .add_systems(Update, gui) + .run(); +} + +fn setup(mut commands: Commands) { + commands.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + color: Color::WHITE, + illuminance: 10000.0, + ..default() + }, + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + ..default() + }); + commands.spawn(( + Camera3dBundle { + transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), + camera_3d: Camera3d { + clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Custom( + Color::BLACK, + ), + ..default() + }, + ..default() + }, + PanOrbitCamera::default(), + )); + commands.spawn(TerrainBundle { + terrain: Terrain { + noise: Noise { + size: [100; 2], + ..default() + }, + ..default() + }, + ..default() + }); +} + +fn gui(mut contexts: EguiContexts, mut query: Query<&mut Terrain>) { + let mut terrain = query.single_mut(); + + let texture_id = contexts.add_image(terrain.noise.gradient.image.clone_weak()); + let mut min_pos = 0.0; + + egui::SidePanel::left("Config").show(contexts.ctx_mut(), |ui| { + ui.set_style(egui::style::Style { + spacing: egui::style::Spacing { + text_edit_width: 150.0, + ..default() + }, + ..default() + }); + ui.heading("Config"); + ui.separator(); + + if ui.button("Export").clicked() { + terrain.export = true + } + + ComboBox::from_label("Method") + .selected_text(terrain.noise.method.to_string()) + .show_ui(ui, |ui| { + ui.selectable_value( + &mut terrain.noise.method, + Method::OpenSimplex, + Method::OpenSimplex.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.method, + Method::Perlin, + Method::Perlin.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.method, + Method::PerlinSurflet, + Method::PerlinSurflet.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.method, + Method::Simplex, + Method::Simplex.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.method, + Method::SuperSimplex, + Method::SuperSimplex.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.method, + Method::Value, + Method::Value.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.method, + Method::Worley, + Method::Worley.to_string(), + ); + }); + ui.horizontal(|ui| { + ui.label("Seed"); + ui.add(DragValue::new(&mut terrain.noise.seed)); + }); + ui.horizontal(|ui| { + ui.label("X"); + ui.add(DragValue::new(&mut terrain.noise.offset[0])); + }); + ui.horizontal(|ui| { + ui.label("Y"); + ui.add(DragValue::new(&mut terrain.noise.offset[1])); + }); + ui.horizontal(|ui| { + ui.label("Width"); + ui.add(DragValue::new(&mut terrain.noise.size[0]).clamp_range(1..=10000)); + }); + ui.horizontal(|ui| { + ui.label("Height"); + ui.add(DragValue::new(&mut terrain.noise.size[1]).clamp_range(1..=10000)); + }); + ui.checkbox(&mut terrain.wireframe, "Wireframe"); + ui.add(Slider::new(&mut terrain.noise.scale, 1.0..=100.0).text("Scale")); + + ComboBox::from_label("Function") + .selected_text(if let Some(function_name) = &terrain.noise.function.name { + function_name.to_string() + } else { + "None".to_string() + }) + .show_ui(ui, |ui| { + ui.selectable_value(&mut terrain.noise.function.name, None, "None"); + ui.selectable_value( + &mut terrain.noise.function.name, + Some(FunctionName::BasicMulti), + FunctionName::BasicMulti.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.function.name, + Some(FunctionName::Billow), + FunctionName::Billow.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.function.name, + Some(FunctionName::Fbm), + FunctionName::Fbm.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.function.name, + Some(FunctionName::HybridMulti), + FunctionName::HybridMulti.to_string(), + ); + ui.selectable_value( + &mut terrain.noise.function.name, + Some(FunctionName::RidgedMulti), + FunctionName::RidgedMulti.to_string(), + ); + }); + if let Some(_function_name) = &terrain.noise.function.name { + ui.add(Slider::new(&mut terrain.noise.function.octaves, 0..=10).text("Octaves")); + ui.add( + Slider::new(&mut terrain.noise.function.frequency, 0.0..=10.0).text("Frequency"), + ); + ui.add( + Slider::new(&mut terrain.noise.function.lacunarity, 0.0..=30.0).text("Lacunarity"), + ); + ui.add( + Slider::new(&mut terrain.noise.function.persistence, 0.01..=1.0) + .text("Persistence"), + ); + } + ui.group(|ui| { + ui.add(egui::widgets::Image::new(egui::load::SizedTexture::new( + texture_id, + [ + terrain.noise.gradient.size[0] as f32, + terrain.noise.gradient.size[1] as f32, + ], + ))); + ui.add( + Slider::new(&mut terrain.noise.gradient.smoothness, 0.0..=1.0).text("Smoothness"), + ); + ui.horizontal(|ui| { + ui.label("Segments"); + ui.add(DragValue::new(&mut terrain.noise.gradient.segments).clamp_range(0..=100)); + }); + ui.horizontal(|ui| { + ui.label("Base Color"); + ui.color_edit_button_srgba_unmultiplied(&mut terrain.noise.base_color); + }); + ui.add(Slider::new(&mut terrain.height_exponent, 0.1..=2.5).text("Height Exponent")); + ui.add(Slider::new(&mut terrain.sea_level, 0.0..=100.0).text("Sea Level")); + ui.separator(); + if ui.button("Add Region").clicked() { + let index = terrain.noise.regions.len() + 1; + terrain.noise.regions.push(Region { + label: format!("Region #{index}"), + position: 0.0, + color: [0, 0, 0, 255], + ..default() + }); + } + ui.separator(); + let regions_len = terrain.noise.regions.len(); + let mut regions_to_remove: Vec = Vec::with_capacity(regions_len); + egui::ScrollArea::vertical().show(ui, |ui| { + for (i, region) in terrain.noise.regions.iter_mut().enumerate() { + ui.horizontal(|ui| { + ui.label(RichText::new(&format!("Region #{}", i + 1)).size(16.0)); + if ui.button("Remove").clicked() { + regions_to_remove.push(i); + } + }); + ui.horizontal(|ui| { + ui.label("Label"); + ui.text_edit_singleline(&mut region.label); + }); + + ui.horizontal(|ui| { + ui.label("Position"); + ui.add(DragValue::new(&mut region.position).clamp_range(min_pos..=100.0)); + }); + min_pos = region.position; + + ui.horizontal(|ui| { + ui.label("Color"); + ui.color_edit_button_srgba_unmultiplied(&mut region.color); + }); + if i != regions_len - 1 { + ui.separator(); + } + } + }); + for i in regions_to_remove { + terrain.noise.regions.remove(i); + } + }); + }); +} diff --git a/src/lib.rs b/src/lib.rs index 15cf03d..b93532d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,4 +16,5 @@ mod util; pub mod noise; pub mod noise_map; +pub mod planet; pub mod terrain; diff --git a/src/main.rs b/src/main.rs index e68e9f5..8df4380 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use bevy_egui::{ }; use bevy_generative::{ noise::{FunctionName, Method, Noise, Region}, - terrain::{Terrain, TerrainBundle, TerrainPlugin}, + planet::{Planet, PlanetBundle, PlanetPlugin}, }; use bevy_panorbit_camera::{PanOrbitCamera, PanOrbitCameraPlugin}; use egui::{ComboBox, DragValue, Slider}; @@ -23,7 +23,7 @@ fn main() { .add_plugins(EguiPlugin) .add_plugins(WireframePlugin) .add_plugins(PanOrbitCameraPlugin) - .add_plugins(TerrainPlugin) + .add_plugins(PlanetPlugin) .add_systems(Startup, setup) .add_systems(Update, gui) .run(); @@ -35,7 +35,7 @@ fn main() { .add_plugins(EguiPlugin) .add_plugins(WireframePlugin) .add_plugins(PanOrbitCameraPlugin) - .add_plugins(TerrainPlugin) + .add_plugins(PlanetPlugin) .add_systems(Startup, setup) .add_systems(Update, gui) .run(); @@ -64,10 +64,10 @@ fn setup(mut commands: Commands) { }, PanOrbitCamera::default(), )); - commands.spawn(TerrainBundle { - terrain: Terrain { + commands.spawn(PlanetBundle { + planet: Planet { noise: Noise { - size: [100; 2], + size: [2; 2], ..default() }, ..default() @@ -76,10 +76,10 @@ fn setup(mut commands: Commands) { }); } -fn gui(mut contexts: EguiContexts, mut query: Query<&mut Terrain>) { - let mut terrain = query.single_mut(); +fn gui(mut contexts: EguiContexts, mut query: Query<&mut Planet>) { + let mut planet = query.single_mut(); - let texture_id = contexts.add_image(terrain.noise.gradient.image.clone_weak()); + let texture_id = contexts.add_image(planet.noise.gradient.image.clone_weak()); let mut min_pos = 0.0; egui::SidePanel::left("Config").show(contexts.ctx_mut(), |ui| { @@ -94,143 +94,140 @@ fn gui(mut contexts: EguiContexts, mut query: Query<&mut Terrain>) { ui.separator(); if ui.button("Export").clicked() { - terrain.export = true + planet.export = true } ComboBox::from_label("Method") - .selected_text(terrain.noise.method.to_string()) + .selected_text(planet.noise.method.to_string()) .show_ui(ui, |ui| { ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::OpenSimplex, Method::OpenSimplex.to_string(), ); ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::Perlin, Method::Perlin.to_string(), ); ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::PerlinSurflet, Method::PerlinSurflet.to_string(), ); ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::Simplex, Method::Simplex.to_string(), ); ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::SuperSimplex, Method::SuperSimplex.to_string(), ); ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::Value, Method::Value.to_string(), ); ui.selectable_value( - &mut terrain.noise.method, + &mut planet.noise.method, Method::Worley, Method::Worley.to_string(), ); }); ui.horizontal(|ui| { ui.label("Seed"); - ui.add(DragValue::new(&mut terrain.noise.seed)); + ui.add(DragValue::new(&mut planet.noise.seed)); }); ui.horizontal(|ui| { ui.label("X"); - ui.add(DragValue::new(&mut terrain.noise.offset[0])); + ui.add(DragValue::new(&mut planet.noise.offset[0])); }); ui.horizontal(|ui| { ui.label("Y"); - ui.add(DragValue::new(&mut terrain.noise.offset[1])); + ui.add(DragValue::new(&mut planet.noise.offset[1])); }); ui.horizontal(|ui| { ui.label("Width"); - ui.add(DragValue::new(&mut terrain.noise.size[0]).clamp_range(1..=10000)); + ui.add(DragValue::new(&mut planet.noise.size[0]).clamp_range(1..=10000)); }); ui.horizontal(|ui| { ui.label("Height"); - ui.add(DragValue::new(&mut terrain.noise.size[1]).clamp_range(1..=10000)); + ui.add(DragValue::new(&mut planet.noise.size[1]).clamp_range(1..=10000)); }); - ui.checkbox(&mut terrain.wireframe, "Wireframe"); - ui.add(Slider::new(&mut terrain.noise.scale, 1.0..=100.0).text("Scale")); + ui.checkbox(&mut planet.wireframe, "Wireframe"); + ui.add(Slider::new(&mut planet.noise.scale, 1.0..=100.0).text("Scale")); ComboBox::from_label("Function") - .selected_text(if let Some(function_name) = &terrain.noise.function.name { + .selected_text(if let Some(function_name) = &planet.noise.function.name { function_name.to_string() } else { "None".to_string() }) .show_ui(ui, |ui| { - ui.selectable_value(&mut terrain.noise.function.name, None, "None"); + ui.selectable_value(&mut planet.noise.function.name, None, "None"); ui.selectable_value( - &mut terrain.noise.function.name, + &mut planet.noise.function.name, Some(FunctionName::BasicMulti), FunctionName::BasicMulti.to_string(), ); ui.selectable_value( - &mut terrain.noise.function.name, + &mut planet.noise.function.name, Some(FunctionName::Billow), FunctionName::Billow.to_string(), ); ui.selectable_value( - &mut terrain.noise.function.name, + &mut planet.noise.function.name, Some(FunctionName::Fbm), FunctionName::Fbm.to_string(), ); ui.selectable_value( - &mut terrain.noise.function.name, + &mut planet.noise.function.name, Some(FunctionName::HybridMulti), FunctionName::HybridMulti.to_string(), ); ui.selectable_value( - &mut terrain.noise.function.name, + &mut planet.noise.function.name, Some(FunctionName::RidgedMulti), FunctionName::RidgedMulti.to_string(), ); }); - if let Some(_function_name) = &terrain.noise.function.name { - ui.add(Slider::new(&mut terrain.noise.function.octaves, 0..=10).text("Octaves")); + if let Some(_function_name) = &planet.noise.function.name { + ui.add(Slider::new(&mut planet.noise.function.octaves, 0..=10).text("Octaves")); + ui.add(Slider::new(&mut planet.noise.function.frequency, 0.0..=10.0).text("Frequency")); ui.add( - Slider::new(&mut terrain.noise.function.frequency, 0.0..=10.0).text("Frequency"), + Slider::new(&mut planet.noise.function.lacunarity, 0.0..=30.0).text("Lacunarity"), ); ui.add( - Slider::new(&mut terrain.noise.function.lacunarity, 0.0..=30.0).text("Lacunarity"), - ); - ui.add( - Slider::new(&mut terrain.noise.function.persistence, 0.01..=1.0) - .text("Persistence"), + Slider::new(&mut planet.noise.function.persistence, 0.01..=1.0).text("Persistence"), ); } ui.group(|ui| { ui.add(egui::widgets::Image::new(egui::load::SizedTexture::new( texture_id, [ - terrain.noise.gradient.size[0] as f32, - terrain.noise.gradient.size[1] as f32, + planet.noise.gradient.size[0] as f32, + planet.noise.gradient.size[1] as f32, ], ))); ui.add( - Slider::new(&mut terrain.noise.gradient.smoothness, 0.0..=1.0).text("Smoothness"), + Slider::new(&mut planet.noise.gradient.smoothness, 0.0..=1.0).text("Smoothness"), ); ui.horizontal(|ui| { ui.label("Segments"); - ui.add(DragValue::new(&mut terrain.noise.gradient.segments).clamp_range(0..=100)); + ui.add(DragValue::new(&mut planet.noise.gradient.segments).clamp_range(0..=100)); }); ui.horizontal(|ui| { ui.label("Base Color"); - ui.color_edit_button_srgba_unmultiplied(&mut terrain.noise.base_color); + ui.color_edit_button_srgba_unmultiplied(&mut planet.noise.base_color); }); - ui.add(Slider::new(&mut terrain.height_exponent, 0.1..=2.5).text("Height Exponent")); - ui.add(Slider::new(&mut terrain.sea_level, 0.0..=100.0).text("Sea Level")); + ui.add(Slider::new(&mut planet.height_exponent, 0.1..=2.5).text("Height Exponent")); + ui.add(Slider::new(&mut planet.sea_level, 0.0..=100.0).text("Sea Level")); ui.separator(); if ui.button("Add Region").clicked() { - let index = terrain.noise.regions.len() + 1; - terrain.noise.regions.push(Region { + let index = planet.noise.regions.len() + 1; + planet.noise.regions.push(Region { label: format!("Region #{index}"), position: 0.0, color: [0, 0, 0, 255], @@ -238,10 +235,10 @@ fn gui(mut contexts: EguiContexts, mut query: Query<&mut Terrain>) { }); } ui.separator(); - let regions_len = terrain.noise.regions.len(); + let regions_len = planet.noise.regions.len(); let mut regions_to_remove: Vec = Vec::with_capacity(regions_len); egui::ScrollArea::vertical().show(ui, |ui| { - for (i, region) in terrain.noise.regions.iter_mut().enumerate() { + for (i, region) in planet.noise.regions.iter_mut().enumerate() { ui.horizontal(|ui| { ui.label(RichText::new(&format!("Region #{}", i + 1)).size(16.0)); if ui.button("Remove").clicked() { @@ -269,7 +266,7 @@ fn gui(mut contexts: EguiContexts, mut query: Query<&mut Terrain>) { } }); for i in regions_to_remove { - terrain.noise.regions.remove(i); + planet.noise.regions.remove(i); } }); }); diff --git a/src/planet.rs b/src/planet.rs new file mode 100644 index 0000000..eb0eb97 --- /dev/null +++ b/src/planet.rs @@ -0,0 +1,164 @@ +use bevy::{ + prelude::{ + info, App, Assets, Bundle, Component, Handle, Mesh, PbrBundle, Plugin, Query, ResMut, + StandardMaterial, Transform, Update, Vec3, + }, + render::render_resource::PrimitiveTopology, +}; + +use crate::{ + noise::{generate_noise_map, Noise}, + util::export_terrain, +}; + +#[derive(Component)] +pub struct Planet { + pub noise: Noise, + pub wireframe: bool, + pub height_exponent: f32, + pub sea_level: f32, + pub export: bool, +} + +impl Default for Planet { + fn default() -> Self { + Self { + noise: Noise::default(), + wireframe: false, + height_exponent: 1.0, + sea_level: 10.0, + export: false, + } + } +} + +#[derive(Bundle, Default)] +pub struct PlanetBundle { + pub planet: Planet, + pub pbr_bundle: PbrBundle, +} + +pub struct PlanetPlugin; + +impl Plugin for PlanetPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Update, generate_planet); + } +} + +fn generate_planet( + mut materials: ResMut>, + mut meshes: ResMut>, + mut query: Query<(&mut Planet, &mut Handle, &Handle)>, +) { + for (mut planet, mut mesh_handle, material) in &mut query { + if let Some(material) = materials.get_mut(material) { + *material = StandardMaterial::default() + } + + let noise = &mut planet.noise; + let vertices_count: usize = ((noise.size[0] + 1) * (noise.size[1] + 1)) as usize; + let triangle_count: usize = (noise.size[0] * noise.size[1] * 2 * 3) as usize; + + let mut positions: Vec<[f32; 3]> = vec![]; + let mut indices: Vec = vec![]; + let mut normals: Vec<[f32; 3]> = vec![]; + let mut uvs: Vec<[f32; 2]> = vec![]; + let mut colors: Vec<[f32; 4]> = vec![]; + + let mut index_start = 0; + for direction in [ + Vec3::Y, + Vec3::NEG_Y, + Vec3::X, + Vec3::NEG_X, + Vec3::Z, + Vec3::NEG_Z, + ] { + let (p, mut i, n, u, c) = generate_face(noise.size[0].min(noise.size[1]), direction); + positions.extend(p); + i = i.iter().map(|index| index + index_start).collect(); + index_start = i.iter().max().unwrap_or(&0) + 1; + indices.extend(i); + normals.extend(n); + uvs.extend(u); + colors.extend(c); + } + + if planet.wireframe { + let triangle_number = indices.len() / 3; + let cloned_indices = indices.clone(); + indices = vec![]; + for i in 0..triangle_number { + for j in &[0, 1, 1, 2, 2, 0] { + indices.push(cloned_indices[i * 3 + j]); + } + } + } + + let mut mesh = if planet.wireframe { + Mesh::new(PrimitiveTopology::LineList) + } else { + Mesh::new(PrimitiveTopology::TriangleList) + }; + mesh.set_indices(Some(bevy::render::mesh::Indices::U32(indices.clone()))); + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions.clone()); + mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); + mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors.clone()); + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + *mesh_handle = meshes.add(mesh); + + if planet.export { + export_terrain(positions, indices, colors); + planet.export = false; + } + } +} + +fn generate_face( + size: u32, + local_up: Vec3, +) -> ( + Vec<[f32; 3]>, + Vec, + Vec<[f32; 3]>, + Vec<[f32; 2]>, + Vec<[f32; 4]>, +) { + let axis_a = Vec3::new(local_up.y, local_up.z, local_up.x); + let axis_b = local_up.cross(axis_a); + let vertices_count = (size * size) as usize; + let triangle_count = ((size - 1) * (size - 1) * 6) as usize; + let mut positions: Vec<[f32; 3]> = Vec::with_capacity(vertices_count); + let mut indices: Vec = Vec::with_capacity(triangle_count); + let mut normals: Vec<[f32; 3]> = Vec::with_capacity(vertices_count); + let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(vertices_count); + let mut colors: Vec<[f32; 4]> = Vec::with_capacity(vertices_count); + + let size = size + 1; + for y in 0..size { + for x in 0..size { + let x_percent = x as f32 / (size as f32 - 1.0); + let y_percent = y as f32 / (size as f32 - 1.0); + let vertex = + (local_up + (x_percent - 0.5) * 2.0 * axis_a + (y_percent - 0.5) * 2.0 * axis_b) + .normalize(); + let i = x + y * size; + positions.push([vertex.x, vertex.y, vertex.z]); + normals.push([vertex.x, vertex.y, vertex.z]); + colors.push([1.0, 1.0, 1.0, 1.0]); + uvs.push([x_percent, y_percent]); + if x != size - 1 && y != size - 1 { + // Triangle 1 + indices.push(i); + indices.push(i + size + 1); + indices.push(i + size); + // Triangle 2 + indices.push(i); + indices.push(i + 1); + indices.push(i + size + 1); + } + } + } + (positions, indices, normals, uvs, colors) +} diff --git a/src/terrain.rs b/src/terrain.rs index ee11f2a..3f2406d 100644 --- a/src/terrain.rs +++ b/src/terrain.rs @@ -51,6 +51,9 @@ fn generate_terrain( mut query: Query<(&mut Terrain, &mut Handle, &Handle)>, ) { for (mut terrain, mut mesh_handle, material) in &mut query { + if let Some(material) = materials.get_mut(material) { + *material = StandardMaterial::default() + } let noise = &mut terrain.noise; let noise_values = generate_noise_map(&noise); @@ -99,9 +102,6 @@ fn generate_terrain( .expect("Could not convert to Rgba8UnormSrgb"), ); - if let Some(material) = materials.get_mut(material) { - *material = StandardMaterial::default() - } let vertices_count: usize = ((noise.size[0] + 1) * (noise.size[1] + 1)) as usize; let triangle_count: usize = (noise.size[0] * noise.size[1] * 2 * 3) as usize;