diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-29 21:26:41 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-29 21:26:41 -0700 |
| commit | a57cb58602b323469861c0b06573c6e47db56f34 (patch) | |
| tree | ef00d97d1843f35472473a0ed8a3dedbfa9b43b9 | |
| parent | e0feb1dc1821f3e1e3d6b9697cd4560b377988b8 (diff) | |
generating wood caves
| -rw-r--r-- | Cargo.lock | 10 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/main.rs | 32 | ||||
| -rw-r--r-- | src/proc_gen/mod.rs | 48 | ||||
| -rw-r--r-- | src/renderer/mod.rs | 12 | ||||
| -rw-r--r-- | src/renderer/ui.rs | 23 | ||||
| -rw-r--r-- | src/sim/cell_manager/manager.rs | 16 |
7 files changed, 122 insertions, 20 deletions
@@ -833,6 +833,15 @@ dependencies = [ ] [[package]] +name = "fastnoise-lite" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5f3c3cc9081e5d0e18bcd50e80cd33cba47fc22f88a9da9c33ecd1c87ea5c0" +dependencies = [ + "num-traits", +] + +[[package]] name = "fax" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2445,6 +2454,7 @@ dependencies = [ "egui-wgpu", "egui-winit", "env_logger", + "fastnoise-lite", "futures", "fxhash", "glam 0.33.3", @@ -36,6 +36,7 @@ glam = "0.33.3" png = "0.18.1" toml = "1.1.4" serde = "1.0.229" +fastnoise-lite = "1.1.1" [features] profiler = [] diff --git a/src/main.rs b/src/main.rs index 9c702a8..0284aaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod camera; mod config; mod content; mod input; +mod proc_gen; mod renderer; mod sim; mod sprite_loader; @@ -30,6 +31,7 @@ use crate::{ Input::{self}, InputManager, }, + proc_gen::WorldGenerator, renderer::RendererState, sim::{entity::EntityDef, rb_manager::DebugRenderMode, sim_manager::SimManager}, vfx::VfxManager, @@ -46,6 +48,10 @@ struct Config { cells_render: bool, debug_render: bool, debug_render_mode: DebugRenderMode, + + // proc gen + proc_gen_seed: i32, + cave_noise_freq: f32, } struct Diagnostics { @@ -63,6 +69,7 @@ struct App { camera: Option<Camera>, vfx_manager: Option<VfxManager>, sim_manager: Option<SimManager>, + world_generator: Option<WorldGenerator>, // renderer last_render: Instant, @@ -123,6 +130,18 @@ impl App { impl Default for App { fn default() -> Self { + let config = Config { + use_threading: true, + brush_radius: 10.0, + brush_material: MaterialId::Sand, + dropper_material: MaterialId::Wood, + cells_render: true, + debug_render: false, + debug_render_mode: DebugRenderMode::default(), + + proc_gen_seed: 10, + cave_noise_freq: 0.005, + }; Self { window: None, renderer_state: None, @@ -133,18 +152,11 @@ impl Default for App { vfx_manager: Some(VfxManager::new()), sim_manager: Some(SimManager::new()), + world_generator: Some(WorldGenerator::new(&config)), last_render: Instant::now(), - config: Config { - use_threading: true, - brush_radius: 10.0, - brush_material: MaterialId::Sand, - dropper_material: MaterialId::Wood, - cells_render: true, - debug_render: false, - debug_render_mode: DebugRenderMode::default(), - }, + config, diagnostics: Diagnostics { fps: 0.0, frame_times: VecDeque::new(), @@ -227,6 +239,7 @@ impl ApplicationHandler for App { && let Some(sim) = &mut self.sim_manager && let Some(vfx) = &mut self.vfx_manager && let Some(camera) = &mut self.camera + && let Some(world_generator) = &mut self.world_generator { renderer_state.render( sim, @@ -235,6 +248,7 @@ impl ApplicationHandler for App { &mut self.config, &self.diagnostics, &self.input_manager, + world_generator, ); vfx.after_render(delta_time); diff --git a/src/proc_gen/mod.rs b/src/proc_gen/mod.rs new file mode 100644 index 0000000..8c965fb --- /dev/null +++ b/src/proc_gen/mod.rs @@ -0,0 +1,48 @@ +use fastnoise_lite::FastNoiseLite; +use glam::{IVec2, Vec2}; + +use crate::{ + Config, + config::CHUNK_SIZE, + content::materials::MaterialId, + sim::{cell::Cell, cell_manager::chunk::Chunk}, +}; + +pub struct WorldGenerator { + cave_noise: FastNoiseLite, +} + +impl WorldGenerator { + pub fn generate_chunk(&mut self, chunk_position: IVec2) -> Chunk { + let mut c = Chunk::void(); + let position = chunk_position * CHUNK_SIZE; + for x in 0..CHUNK_SIZE { + for y in 0..CHUNK_SIZE { + let cell_position = position.as_vec2() + Vec2::new(x as f32, y as f32); + let cnv = self + .cave_noise + .get_noise_2d(cell_position.x, cell_position.y); + if cnv < 0.5 { + c.set_cell_at_local_position( + x as u8, + y as u8, + Cell::from_material(MaterialId::Wood), + ); + } + } + } + c + } + + pub fn update_params(&mut self, config: &Config) { + self.cave_noise = FastNoiseLite::with_seed(config.proc_gen_seed + 50); + self.cave_noise.set_frequency(Some(config.cave_noise_freq)); + } + + pub fn new(config: &Config) -> Self { + let cave_noise = FastNoiseLite::with_seed(config.proc_gen_seed + 50); + let mut wg = WorldGenerator { cave_noise }; + wg.update_params(config); + wg + } +} diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 127a785..b5508e2 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -10,6 +10,7 @@ use crate::{ camera::Camera, config::{CELLS_IN_CHUNK, CHUNK_SIZE}, content::{materials::MaterialId, vfx::VfxMaterialId}, + proc_gen::WorldGenerator, renderer::ui::draw_egui, sim::{ cell_manager::manager::CellManager, entity::EntityId, @@ -772,6 +773,7 @@ impl RendererState { config: &mut Config, diagnostics: &Diagnostics, input_manager: &InputManager, + world_generator: &mut WorldGenerator, ) { puffin::profile_function!(); @@ -819,7 +821,15 @@ impl RendererState { .resizable(false) // TODO collapse button .show_collapsible(ui, &mut true, |panel_ui| { - draw_egui(panel_ui, config, camera, diagnostics, input_manager, sim) + draw_egui( + panel_ui, + config, + camera, + diagnostics, + input_manager, + sim, + world_generator, + ) }); }); diff --git a/src/renderer/ui.rs b/src/renderer/ui.rs index 02ebb48..9af0113 100644 --- a/src/renderer/ui.rs +++ b/src/renderer/ui.rs @@ -1,10 +1,12 @@ use egui::{Color32, Stroke, Ui, epaint::CircleShape}; +use glam::IVec2; use rapier2d::pipeline::QueryFilter; use crate::{ Camera, Config, Diagnostics, content::materials::MaterialId, input::InputManager, + proc_gen::WorldGenerator, sim::{rb_manager::DebugRenderMode, sim_manager::SimManager}, }; @@ -23,7 +25,8 @@ pub fn draw_egui<'a>( camera: &mut Camera, diagnostics: &Diagnostics, input_manager: &InputManager, - sim: &SimManager, + sim: &mut SimManager, + world_generator: &mut WorldGenerator, ) { puffin::profile_function!(); ui.heading("Config"); @@ -104,7 +107,19 @@ pub fn draw_egui<'a>( ui.label(format!("Real FPS: {}", diagnostics.fps)); ui.heading("Camera"); ui.add(egui::Slider::new(&mut camera.zoom, 0.0..=10.0).text("Zoom")); - ui.heading("Input"); + ui.heading("Proc gen"); + if ui.button("Generate").clicked() { + world_generator.update_params(config); + for cx in -5..5 { + for cy in -5..5 { + let chunk = world_generator.generate_chunk(IVec2::new(cx, cy)); + sim.cell_manager.upsert(cx, cy, chunk); + } + } + } + ui.add(egui::Slider::new(&mut config.proc_gen_seed, -100000..=100000).text("Seed")); + ui.label("Cave noise"); + ui.add(egui::Slider::new(&mut config.cave_noise_freq, 0.0..=1.0).text("Frequency")); ui.label(format!( "Mouse (world): x,y=({x}, {y})", @@ -118,13 +133,13 @@ pub fn draw_egui<'a>( y = input_manager.chunk_mouse_pos.y )); - if let Some(&chunk) = sim.cell_manager.chunk_position_to_chunk_idx.get(&( + if let Some(&idx) = sim.cell_manager.chunk_position_to_chunk_idx.get(&( input_manager.chunk_mouse_pos.x, input_manager.chunk_mouse_pos.y, )) { ui.label(format!( "Sleeping={}", - sim.cell_manager.chunks[chunk].sleeping + sim.cell_manager.chunks[idx].sleeping )); } diff --git a/src/sim/cell_manager/manager.rs b/src/sim/cell_manager/manager.rs index 64b97f1..c8015f7 100644 --- a/src/sim/cell_manager/manager.rs +++ b/src/sim/cell_manager/manager.rs @@ -48,10 +48,14 @@ impl CellManager { } } - pub fn insert(&mut self, x: i32, y: i32, chunk: Chunk) { - self.chunk_position_to_chunk_idx - .insert((x, y), self.chunks.len()); - self.chunks.push(chunk); + pub fn upsert(&mut self, x: i32, y: i32, chunk: Chunk) { + if let Some(&idx) = self.chunk_position_to_chunk_idx.get(&(x, y)) { + self.chunks[idx] = chunk; + } else { + self.chunk_position_to_chunk_idx + .insert((x, y), self.chunks.len()); + self.chunks.push(chunk); + } } pub fn tick(&mut self, use_threading: bool) { @@ -70,9 +74,9 @@ impl CellManager { for y in -10..1 { for x in -100..100 { if y == 0 { - world.insert(x, y, Chunk::floor()); + world.upsert(x, y, Chunk::floor()); } else { - world.insert(x, y, Chunk::void()); + world.upsert(x, y, Chunk::void()); } } } |
