diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-30 20:10:07 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-30 20:10:07 -0700 |
| commit | 8e128d3ca24fa10dc7d871f7daf6c43eaddc385c (patch) | |
| tree | c4b32bfb559941041965418daefda1deffafd8e9 | |
| parent | 56bba5560be95c1e05bfec11e4fc085654f3589d (diff) | |
wip biome abstraction
| -rw-r--r-- | src/content/world/mines.rs | 41 | ||||
| -rw-r--r-- | src/main.rs | 6 | ||||
| -rw-r--r-- | src/proc_gen/herringbone.rs | 46 | ||||
| -rw-r--r-- | src/proc_gen/mod.rs | 60 | ||||
| -rw-r--r-- | src/proc_gen/tileset_loader.rs | 12 |
5 files changed, 138 insertions, 27 deletions
diff --git a/src/content/world/mines.rs b/src/content/world/mines.rs index e69de29..ae859bc 100644 --- a/src/content/world/mines.rs +++ b/src/content/world/mines.rs @@ -0,0 +1,41 @@ +use crate::{ + config::TILESET_SCALING, + content::materials::MaterialId, + proc_gen::{ + Biome, + tileset_loader::{Tile, TileOrientation, TilesetPixelType}, + }, + sim::cell::Cell, +}; + +pub struct MinesBiome; + +impl Biome for MinesBiome { + fn derive_world_from_tile(&self, tile: &Tile) -> Vec<Cell> { + let tp = TILESET_SCALING.pow(2) as u32; + let mut cells = + vec![Cell::void(); (tile.dimensions.short * tile.dimensions.long * tp) as usize]; + let ey = match tile.orientation { + TileOrientation::Horizontal => tile.dimensions.short, + TileOrientation::Vertical => tile.dimensions.long, + }; + let ex = match tile.orientation { + TileOrientation::Horizontal => tile.dimensions.long, + TileOrientation::Vertical => tile.dimensions.short, + }; + + for y in 0..ey { + for x in 0..ex { + let idx = (x + y * ex) * tp; + for i in 0..tp { + cells[(idx + i) as usize] = match tile.pixels[(x + y * ex) as usize] { + TilesetPixelType::Void => Cell::void(), + TilesetPixelType::Terrain => Cell::from_material(MaterialId::Dirt), + } + } + } + } + + cells + } +} diff --git a/src/main.rs b/src/main.rs index 0dd725d..118f2a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,7 @@ use crate::{ entity_grenade::entity_grenade_def, entity_tnt::entity_tnt_def, }, materials::MaterialId, + world::mines::MinesBiome, }, input::{ Input::{self}, @@ -147,7 +148,10 @@ impl Default for App { vfx_manager: Some(VfxManager::new()), sim_manager: Some(SimManager::new()), - world_generator: TilesetWorldGenerator::new("assets/tilesets/tileset1"), + world_generator: TilesetWorldGenerator::new( + "assets/tilesets/tileset1", + Box::new(MinesBiome), + ), last_render: Instant::now(), diff --git a/src/proc_gen/herringbone.rs b/src/proc_gen/herringbone.rs index 5e930ec..271b7a1 100644 --- a/src/proc_gen/herringbone.rs +++ b/src/proc_gen/herringbone.rs @@ -3,12 +3,10 @@ use glam::IVec2; use crate::{ config::TILESET_SCALING, - content::materials::MaterialId, proc_gen::tileset_loader::{Tile, TileOrientation, Tileset, TilesetPixelType}, - sim::cell::Cell, }; -fn split_position(world: IVec2, tileset: &Tileset) -> (IVec2, IVec2, TileOrientation) { +pub fn split_position(world: IVec2, tileset: &Tileset) -> (IVec2, IVec2, TileOrientation) { let s = tileset.dimensions.short as i32; let tx = world.x.div_euclid(s); let ty = world.y.div_euclid(s); @@ -74,10 +72,40 @@ fn get_pixel_at_position(world: IVec2, tileset: &Tileset) -> TilesetPixelType { tile.pixels[(local.x + local.y * width) as usize] } -pub fn get_cell_at_position(world: IVec2, tileset: &Tileset) -> Cell { - let pixel = get_pixel_at_position(world, tileset); - match pixel { - TilesetPixelType::Void => Cell::void(), - TilesetPixelType::Terrain => Cell::from_material(MaterialId::Dirt), - } +pub fn tiles_overlapping( + a: IVec2, + b: IVec2, + tileset: &Tileset, +) -> impl Iterator<Item = (IVec2, &Tile, TileOrientation)> { + let s = IVec2::new( + tileset.dimensions.short as i32, + tileset.dimensions.short as i32, + ); + + let ta = a.div_euclid(s) - 1; + let tb = (b - 1).div_euclid(s); + + (ta.y..=tb.y) + .flat_map(move |ty| (ta.x..=tb.x).map(move |tx| (tx, ty))) + .filter_map(move |(tx, ty)| match (tx - ty).rem_euclid(4) { + 0 => Some(( + IVec2::new(tx, ty), + pick_variant(IVec2::new(tx, ty), TileOrientation::Horizontal, tileset), + TileOrientation::Horizontal, + )), + 3 => Some(( + IVec2::new(tx, ty), + pick_variant(IVec2::new(tx, ty), TileOrientation::Vertical, tileset), + TileOrientation::Vertical, + )), + _ => None, + }) } + +// pub fn get_cell_at_position(world: IVec2, tileset: &Tileset) -> Cell { +// let pixel = get_pixel_at_position(world, tileset); +// match pixel { +// TilesetPixelType::Void => Cell::void(), +// TilesetPixelType::Terrain => Cell::from_material(MaterialId::Dirt), +// } +// } diff --git a/src/proc_gen/mod.rs b/src/proc_gen/mod.rs index 7913d22..049ad1a 100644 --- a/src/proc_gen/mod.rs +++ b/src/proc_gen/mod.rs @@ -4,34 +4,68 @@ pub mod tileset_loader; use glam::IVec2; use crate::{ - config::CHUNK_SIZE, + config::{CHUNK_SIZE, TILESET_SCALING}, proc_gen::{ - herringbone::get_cell_at_position, - tileset_loader::{Tileset, load_tileset}, + herringbone::tiles_overlapping, + tileset_loader::{Tile, TileOrientation, Tileset, load_tileset}, }, - sim::cell_manager::chunk::Chunk, + sim::{cell::Cell, cell_manager::chunk::Chunk, entity::EntityDef}, }; +pub trait Biome { + fn derive_world_from_tile(&self, _tile: &Tile) -> Vec<Cell> { + Vec::new() + } + fn derive_entities_from_tile(&self, _tile: &Tile) -> Vec<EntityDef> { + Vec::new() + } +} + pub struct TilesetWorldGenerator { tileset: Tileset, + biome: Box<dyn Biome>, } impl TilesetWorldGenerator { pub fn generate_chunk(&self, chunk_position: IVec2) -> Chunk { let mut chunk = Chunk::void(); - for x in 0..CHUNK_SIZE { - for y in 0..CHUNK_SIZE { - let cell = get_cell_at_position( - chunk_position * CHUNK_SIZE + IVec2::new(x, y), - &self.tileset, - ); - chunk.set_cell_at_local_position(x as u8, y as u8, cell); + + let a = chunk_position * CHUNK_SIZE; + let b = (chunk_position + 1) * CHUNK_SIZE; + let tiles = tiles_overlapping(a * TILESET_SCALING, b * TILESET_SCALING, &self.tileset); + let world_tiles = tiles.map(|(p, t, o)| (p, self.biome.derive_world_from_tile(t), o)); + + for (tp, t, o) in world_tiles { + let p = tp * self.tileset.dimensions.short as i32 * TILESET_SCALING; + let ta = p.max(a); + let tb = p.min(b); + if ta.x >= tb.x || ta.y >= tb.y { + continue; + } + + for y in ta.y..tb.y { + for x in ta.x..tb.x { + let sw = match o { + TileOrientation::Horizontal => { + self.tileset.dimensions.long as i32 * TILESET_SCALING + } + TileOrientation::Vertical => { + self.tileset.dimensions.short as i32 * TILESET_SCALING + } + }; + + let src = (y - p.y) * sw + (x - p.x); + let dst = (y - a.y) * CHUNK_SIZE + (x - a.x); + chunk.cells[dst as usize] = t[src as usize]; + } } } + chunk } - pub fn new(tileset_path: &str) -> Self { + + pub fn new(tileset_path: &str, biome: Box<dyn Biome>) -> Self { let tileset = load_tileset(tileset_path); - TilesetWorldGenerator { tileset } + TilesetWorldGenerator { tileset, biome } } } diff --git a/src/proc_gen/tileset_loader.rs b/src/proc_gen/tileset_loader.rs index 925b3ac..acb4d65 100644 --- a/src/proc_gen/tileset_loader.rs +++ b/src/proc_gen/tileset_loader.rs @@ -26,8 +26,8 @@ fn load_img(path: &Path) -> Result<(Vec<u8>, u32, u32), Box<dyn Error>> { Ok((buf, info.width, info.height)) } -#[derive(Deserialize, Debug)] -pub struct TilesetDimensions { +#[derive(Deserialize, Clone, Copy, Debug)] +pub struct TileDimensions { pub short: u32, pub long: u32, } @@ -40,7 +40,7 @@ pub enum TilesetPixelType { #[derive(Deserialize, Debug)] struct TilesetManifest { - dimensions: TilesetDimensions, + dimensions: TileDimensions, palette: HashMap<u8, TilesetPixelType>, } @@ -50,12 +50,14 @@ fn load_manifest(path: &Path) -> Result<TilesetManifest, Box<dyn Error>> { #[derive(Debug)] pub struct Tile { + pub orientation: TileOrientation, + pub dimensions: TileDimensions, pub pixels: Vec<TilesetPixelType>, } #[derive(Debug)] pub struct Tileset { - pub dimensions: TilesetDimensions, + pub dimensions: TileDimensions, pub vertical_tiles: Vec<Tile>, pub horizontal_tiles: Vec<Tile>, } @@ -115,6 +117,8 @@ fn parse_row( ); tiles.push(Tile { + orientation, + dimensions: manifest.dimensions, pixels: tile_pixels, }); |
