diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-09-01 20:23:56 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-09-01 20:23:56 -0700 |
| commit | 5ebc92d5a6bbbe14e7c4c25b25a4ff8578ddcd5a (patch) | |
| tree | c47ee1588da1e95d95d0eb064fdad39025d47a4a | |
| parent | 7eedb19a5f28150a28b8cf1ec5fb08139d6b5590 (diff) | |
smooth material mixing
| -rw-r--r-- | src/content/world/mines.rs | 77 | ||||
| -rw-r--r-- | src/proc_gen/mod.rs | 43 |
2 files changed, 88 insertions, 32 deletions
diff --git a/src/content/world/mines.rs b/src/content/world/mines.rs index d750619..961b068 100644 --- a/src/content/world/mines.rs +++ b/src/content/world/mines.rs @@ -1,5 +1,5 @@ use fastnoise_lite::{FastNoiseLite, FractalType}; -use glam::IVec2; +use glam::{IVec2, Vec2}; use crate::{ content::materials::MaterialId, @@ -7,28 +7,85 @@ use crate::{ sim::cell::Cell, }; -const ROUGHNESS: f32 = 0.375; +const DISPLACEMENT_ROUGHNESS: f32 = 0.375; +const BOUNDARY_ROUGHNESS: f32 = 0.375; pub struct MinesBiome { - pub edge_roughness: FastNoiseLite, + pub dirt_roughness: FastNoiseLite, + pub water_roughness: FastNoiseLite, + pub wood_roughness: FastNoiseLite, } impl MinesBiome { + fn pixel_idx_to_type(idx: u8) -> MinesBiomePixelType { + match idx { + 1 => MinesBiomePixelType::Dirt, + 12 => MinesBiomePixelType::Water, + 13 => MinesBiomePixelType::Wood, + _ => MinesBiomePixelType::Void, + } + } + + fn material_roughness(&self, pixel_type: MinesBiomePixelType, world: Vec2) -> f32 { + match pixel_type { + MinesBiomePixelType::Dirt => self.dirt_roughness.get_noise_2d(world.x, world.y), + MinesBiomePixelType::Water => self.water_roughness.get_noise_2d(world.x, world.y), + MinesBiomePixelType::Wood => self.wood_roughness.get_noise_2d(world.x, world.y), + _ => self.dirt_roughness.get_noise_2d(world.x, world.y), + } + } + pub fn new() -> Self { - let mut edge_roughness = FastNoiseLite::new(); - edge_roughness.set_fractal_type(Some(FractalType::Ridged)); - edge_roughness.set_fractal_octaves(Some(2)); - MinesBiome { edge_roughness } + let mut dirt_roughness = FastNoiseLite::with_seed(0); + dirt_roughness.set_fractal_type(Some(FractalType::Ridged)); + dirt_roughness.set_fractal_octaves(Some(2)); + + let mut water_roughness = FastNoiseLite::with_seed(100); + water_roughness.set_fractal_type(Some(FractalType::FBm)); + water_roughness.set_frequency(Some(0.005)); + water_roughness.set_fractal_octaves(Some(2)); + + let mut wood_roughness = FastNoiseLite::with_seed(200); + wood_roughness.set_fractal_type(Some(FractalType::FBm)); + wood_roughness.set_frequency(Some(0.02)); + wood_roughness.set_fractal_octaves(Some(2)); + MinesBiome { + dirt_roughness, + water_roughness, + wood_roughness, + } } } +enum MinesBiomePixelType { + Void, + Dirt, + Water, + Wood, +} + impl Biome for MinesBiome { fn fragment(&self, pixel: InterpolatedPixel, world: IVec2) -> Cell { let world = world.as_vec2(); - let displacement = ROUGHNESS * self.edge_roughness.get_noise_2d(world.x, world.y); - match pixel.interpolated_pixel_index { - 0 => { + let pixel_idx = pixel + .interpolated_pixel_indices + .iter() + .map(|&(i, s)| { + ( + i, + s + self.material_roughness(MinesBiome::pixel_idx_to_type(i), world) + * BOUNDARY_ROUGHNESS, + ) + }) + .max_by(|&(_, s1), &(_, s2)| s1.total_cmp(&s2)) + .map_or(0, |(i, _)| i); + + let displacement = + DISPLACEMENT_ROUGHNESS * self.dirt_roughness.get_noise_2d(world.x, world.y); + + match pixel_idx { + 1 => { if pixel.interpolated_solidity + displacement <= 0.0 { return Cell::void(); } diff --git a/src/proc_gen/mod.rs b/src/proc_gen/mod.rs index f97df66..4e333d1 100644 --- a/src/proc_gen/mod.rs +++ b/src/proc_gen/mod.rs @@ -31,7 +31,7 @@ struct BiomeChunkContext { } pub struct InterpolatedPixel { - pub interpolated_pixel_index: u8, + pub interpolated_pixel_indices: [(u8, f32); 4], pub interpolated_solidity: f32, } @@ -49,8 +49,8 @@ impl BiomeChunkContext { }; let instantaneous_idx_at = |dx, dy| match instantaneous_pixel_at(dx, dy) { - TilePixelType::Void => 255, - TilePixelType::Terrain(_) => 0, + TilePixelType::Void => 0, + TilePixelType::Terrain(_) => 1, TilePixelType::Custom(i) => i, }; @@ -79,44 +79,43 @@ impl BiomeChunkContext { // same for the pixel // TODO don't allocate per-cell - let mut idxes: Vec<u8> = Vec::new(); + let mut pixel_indices: Vec<u8> = Vec::new(); let tl = instantaneous_idx_at(0, 0); - if tl != 255 && !idxes.contains(&tl) { - idxes.push(tl); + if tl != 0 && !pixel_indices.contains(&tl) { + pixel_indices.push(tl); } let tr = instantaneous_idx_at(1, 0); - if tr != 255 && !idxes.contains(&tr) { - idxes.push(tr); + if tr != 0 && !pixel_indices.contains(&tr) { + pixel_indices.push(tr); } let bl = instantaneous_idx_at(0, 1); - if bl != 255 && !idxes.contains(&bl) { - idxes.push(bl); + if bl != 0 && !pixel_indices.contains(&bl) { + pixel_indices.push(bl); } let br = instantaneous_idx_at(1, 1); - if br != 255 && !idxes.contains(&br) { - idxes.push(br); + if br != 0 && !pixel_indices.contains(&br) { + pixel_indices.push(br); } - let idx_strengths = idxes.iter().map(|&i| { + let mut interpolated_pixel_indices: [(u8, f32); 4] = [(0, 0.0); 4]; + for (i, &p) in pixel_indices.iter().enumerate() { let i_top = lerp( - if tl == i { 1.0 } else { 0.0 }, - if tr == i { 1.0 } else { 0.0 }, + if tl == p { 1.0 } else { 0.0 }, + if tr == p { 1.0 } else { 0.0 }, frac.x, ); let i_bottom = lerp( - if bl == i { 1.0 } else { 0.0 }, - if br == i { 1.0 } else { 0.0 }, + if bl == p { 1.0 } else { 0.0 }, + if br == p { 1.0 } else { 0.0 }, frac.x, ); let interpolated_strength = lerp(i_top, i_bottom, frac.y); - (i, interpolated_strength) - }); + interpolated_pixel_indices[i] = (p, interpolated_strength); + } InterpolatedPixel { - interpolated_pixel_index: idx_strengths - .max_by(|&(_, s1), &(_, s2)| s1.total_cmp(&s2)) - .map_or(255, |(i, _)| i), + interpolated_pixel_indices, interpolated_solidity, } } |
