summaryrefslogtreecommitdiff
path: root/src/proc_gen/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/proc_gen/mod.rs')
-rw-r--r--src/proc_gen/mod.rs131
1 files changed, 104 insertions, 27 deletions
diff --git a/src/proc_gen/mod.rs b/src/proc_gen/mod.rs
index 4a1003a..abdd0b8 100644
--- a/src/proc_gen/mod.rs
+++ b/src/proc_gen/mod.rs
@@ -1,19 +1,29 @@
pub mod herringbone;
pub mod tileset_loader;
+use std::{
+ cmp::Ordering,
+ collections::HashMap,
+ ops::{Add, Mul, Sub},
+};
+
+use fxhash::FxHashMap;
use glam::IVec2;
use crate::{
config::{CHUNK_SIZE, TILESET_SCALING},
proc_gen::{
herringbone::{split_position, variant_index},
- tileset_loader::{Tile, TileOrientation, Tileset, TilesetPixelType, load_tileset},
+ tileset_loader::{Tile, TileOrientation, TilePixelType, Tileset, load_tileset},
},
sim::{cell::Cell, cell_manager::chunk::Chunk, entity::EntityDef},
};
#[inline]
-fn lerp(a: f32, b: f32, t: f32) -> f32 {
+fn lerp<T>(a: T, b: T, t: f32) -> T
+where
+ T: Copy + Add<Output = T> + Sub<Output = T> + Mul<f32, Output = T>,
+{
a + (b - a) * t
}
@@ -22,11 +32,16 @@ const PIXELS_NEIGHBOURHOOD_SIZE: i32 = CHUNK_PIXELS + 2;
// 2d slice of solidity; chunk + 1 margin
struct BiomeChunkContext {
- instantaneous_solidity: [f32; (PIXELS_NEIGHBOURHOOD_SIZE * PIXELS_NEIGHBOURHOOD_SIZE) as usize],
+ pixels: [TilePixelType; (PIXELS_NEIGHBOURHOOD_SIZE * PIXELS_NEIGHBOURHOOD_SIZE) as usize],
+}
+
+pub struct InterpolatedPixel {
+ pub interpolated_pixel_index: u8,
+ pub interpolated_solidity: f32,
}
impl BiomeChunkContext {
- fn lerped_solidity_at(&self, local: IVec2) -> f32 {
+ fn interpolated_at(&self, local: IVec2, tileset: &Tileset) -> InterpolatedPixel {
// pixel coord centre of cell coord local
let pixel_position = (local.as_vec2() + 0.5) / TILESET_SCALING as f32 + 0.5;
let pixel = pixel_position.floor();
@@ -34,22 +49,86 @@ impl BiomeChunkContext {
let frac = pixel_position - pixel;
let pixel = pixel.as_ivec2();
- let at = |dx, dy| {
- self.instantaneous_solidity
- [((pixel.x + dx) + (pixel.y + dy) * PIXELS_NEIGHBOURHOOD_SIZE) as usize]
+ let instantaneous_pixel_at = |dx, dy| {
+ self.pixels[((pixel.x + dx) + (pixel.y + dy) * PIXELS_NEIGHBOURHOOD_SIZE) as usize]
+ };
+
+ let instantaneous_idx_at = |dx, dy| match instantaneous_pixel_at(dx, dy) {
+ TilePixelType::Void => 255,
+ TilePixelType::Terrain(_) => 0,
+ TilePixelType::Custom(i) => i,
+ };
+
+ let instantaneous_solidity_at = |dx, dy| match instantaneous_pixel_at(dx, dy) {
+ TilePixelType::Void => 0.0,
+ TilePixelType::Terrain(o) => o,
+ TilePixelType::Custom(i) => {
+ tileset.manifest.palette.get(&i).map_or(0.0, |p| p.solidity)
+ }
};
// linearly interpolate between the solidity of the horizontal cells above and below according to the fractional x component
- let top = lerp(at(0, 0), at(1, 0), frac.x);
- let bottom = lerp(at(0, 1), at(1, 1), frac.x);
+ let s_top = lerp(
+ instantaneous_solidity_at(0, 0),
+ instantaneous_solidity_at(1, 0),
+ frac.x,
+ );
+ let s_bottom = lerp(
+ instantaneous_solidity_at(0, 1),
+ instantaneous_solidity_at(1, 1),
+ frac.x,
+ );
// and then interpolate between those according to the y component
- lerp(top, bottom, frac.y) - 0.5
+ let interpolated_solidity = lerp(s_top, s_bottom, frac.y) - 0.5;
+
+ // same for the pixel
+ // TODO don't allocate per-cell
+ let mut idxes: Vec<u8> = Vec::new();
+ let tl = instantaneous_idx_at(0, 0);
+ if tl != 255 && !idxes.contains(&tl) {
+ idxes.push(tl);
+ }
+ let tr = instantaneous_idx_at(1, 0);
+ if tr != 255 && !idxes.contains(&tr) {
+ idxes.push(tr);
+ }
+ let bl = instantaneous_idx_at(0, 1);
+ if bl != 255 && !idxes.contains(&bl) {
+ idxes.push(bl);
+ }
+ let br = instantaneous_idx_at(1, 1);
+ if br != 255 && !idxes.contains(&br) {
+ idxes.push(br);
+ }
+
+ let idx_strengths = idxes.iter().map(|&i| {
+ let i_top = lerp(
+ if tl == i { 1.0 } else { 0.0 },
+ if tr == i { 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 },
+ frac.x,
+ );
+
+ let interpolated_strength = lerp(i_top, i_bottom, frac.y);
+ (i, interpolated_strength)
+ });
+
+ InterpolatedPixel {
+ interpolated_pixel_index: idx_strengths
+ .max_by(|&(_, s1), &(_, s2)| s1.total_cmp(&s2))
+ .map_or(255, |(i, _)| i),
+ interpolated_solidity,
+ }
}
}
pub trait Biome {
- fn cell(&self, solidity: f32, world: IVec2) -> Cell;
+ fn fragment(&self, pixel: InterpolatedPixel, world: IVec2) -> Cell;
fn derive_entities_from_tile(&self, _tile: &Tile) -> Vec<EntityDef> {
Vec::new()
@@ -69,9 +148,9 @@ impl TilesetWorldGenerator {
}
}
- fn pixel_at(&self, pixel: IVec2) -> TilesetPixelType {
+ fn pixel_at(&self, pixel: IVec2) -> TilePixelType {
let (grid, local, orientation) =
- split_position(pixel, self.tileset.dimensions.short as i32);
+ split_position(pixel, self.tileset.manifest.dimensions.short as i32);
let tiles = match orientation {
TileOrientation::Horizontal => &self.tileset.horizontal_tiles,
@@ -81,39 +160,37 @@ impl TilesetWorldGenerator {
tiles[variant_index(grid, tiles.len())].pixel_at(local)
}
- fn structure_around(&self, chunk_position: IVec2) -> BiomeChunkContext {
+ fn get_biome_chunk_context(&self, chunk_position: IVec2) -> BiomeChunkContext {
let origin = chunk_position * CHUNK_PIXELS - 1;
- let mut occupancy = [0.0; (PIXELS_NEIGHBOURHOOD_SIZE * PIXELS_NEIGHBOURHOOD_SIZE) as usize];
+ let mut pixels =
+ [TilePixelType::Void; (PIXELS_NEIGHBOURHOOD_SIZE * PIXELS_NEIGHBOURHOOD_SIZE) as usize];
for y in 0..PIXELS_NEIGHBOURHOOD_SIZE {
for x in 0..PIXELS_NEIGHBOURHOOD_SIZE {
- occupancy[(x + y * PIXELS_NEIGHBOURHOOD_SIZE) as usize] =
- match self.pixel_at(origin + IVec2::new(x, y)) {
- TilesetPixelType::Void => 0.0,
- TilesetPixelType::Terrain => 1.0,
- };
+ pixels[(x + y * PIXELS_NEIGHBOURHOOD_SIZE) as usize] =
+ self.pixel_at(origin + IVec2::new(x, y));
}
}
- BiomeChunkContext {
- instantaneous_solidity: occupancy,
- }
+ BiomeChunkContext { pixels }
}
pub fn generate_chunk(&self, chunk_position: IVec2) -> Chunk {
let mut chunk = Chunk::void();
- let structure = self.structure_around(chunk_position);
+ let biome_chunk_context = self.get_biome_chunk_context(chunk_position);
let chunk_min = chunk_position * CHUNK_SIZE;
for y in 0..CHUNK_SIZE {
for x in 0..CHUNK_SIZE {
let local = IVec2::new(x, y);
- chunk.cells[(x + y * CHUNK_SIZE) as usize] = self
- .biome
- .cell(structure.lerped_solidity_at(local), chunk_min + local);
+ chunk.cells[(x + y * CHUNK_SIZE) as usize] = self.biome.fragment(
+ biome_chunk_context.interpolated_at(local, &self.tileset),
+ chunk_min + local,
+ );
}
}
+ chunk.sleeping = false;
chunk.mark_collider_dirty(0);
chunk