summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2026-09-01 20:23:56 -0700
committerKai Stevenson <kai@kaistevenson.com>2026-09-01 20:23:56 -0700
commit5ebc92d5a6bbbe14e7c4c25b25a4ff8578ddcd5a (patch)
treec47ee1588da1e95d95d0eb064fdad39025d47a4a /src
parent7eedb19a5f28150a28b8cf1ec5fb08139d6b5590 (diff)
smooth material mixing
Diffstat (limited to 'src')
-rw-r--r--src/content/world/mines.rs77
-rw-r--r--src/proc_gen/mod.rs43
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,
}
}