mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Began work on CSG-based primitive tree site structure generation system
This commit is contained in:
parent
48cbb4bed1
commit
aedfd65721
@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cmp::{Eq, PartialEq},
|
cmp::{Eq, PartialEq, Ord, PartialOrd, Ordering},
|
||||||
fmt, hash,
|
fmt, hash,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Index, IndexMut},
|
ops::{Index, IndexMut},
|
||||||
@ -29,6 +29,16 @@ impl<T> Eq for Id<T> {}
|
|||||||
impl<T> PartialEq for Id<T> {
|
impl<T> PartialEq for Id<T> {
|
||||||
fn eq(&self, other: &Self) -> bool { self.idx == other.idx && self.gen == other.gen }
|
fn eq(&self, other: &Self) -> bool { self.idx == other.idx && self.gen == other.gen }
|
||||||
}
|
}
|
||||||
|
impl<T> Ord for Id<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
(self.idx, self.gen).cmp(&(other.idx, other.gen))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> PartialOrd for Id<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
impl<T> fmt::Debug for Id<T> {
|
impl<T> fmt::Debug for Id<T> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
|
@ -2,8 +2,9 @@ use crate::{
|
|||||||
block::ZCache,
|
block::ZCache,
|
||||||
column::ColumnSample,
|
column::ColumnSample,
|
||||||
index::IndexRef,
|
index::IndexRef,
|
||||||
sim::{SimChunk, WorldSim as Land},
|
sim::{SimChunk, WorldSim},
|
||||||
util::Grid,
|
util::Grid,
|
||||||
|
land::Land,
|
||||||
};
|
};
|
||||||
use common::{
|
use common::{
|
||||||
terrain::{Block, TerrainChunk, TerrainChunkSize},
|
terrain::{Block, TerrainChunk, TerrainChunkSize},
|
||||||
@ -17,7 +18,7 @@ pub struct CanvasInfo<'a> {
|
|||||||
pub(crate) wpos: Vec2<i32>,
|
pub(crate) wpos: Vec2<i32>,
|
||||||
pub(crate) column_grid: &'a Grid<Option<ZCache<'a>>>,
|
pub(crate) column_grid: &'a Grid<Option<ZCache<'a>>>,
|
||||||
pub(crate) column_grid_border: i32,
|
pub(crate) column_grid_border: i32,
|
||||||
pub(crate) land: &'a Land,
|
pub(crate) chunks: &'a WorldSim,
|
||||||
pub(crate) index: IndexRef<'a>,
|
pub(crate) index: IndexRef<'a>,
|
||||||
pub(crate) chunk: &'a SimChunk,
|
pub(crate) chunk: &'a SimChunk,
|
||||||
}
|
}
|
||||||
@ -45,7 +46,11 @@ impl<'a> CanvasInfo<'a> {
|
|||||||
|
|
||||||
pub fn chunk(&self) -> &'a SimChunk { self.chunk }
|
pub fn chunk(&self) -> &'a SimChunk { self.chunk }
|
||||||
|
|
||||||
pub fn land(&self) -> &'a Land { self.land }
|
pub fn chunks(&self) -> &'a WorldSim { self.chunks }
|
||||||
|
|
||||||
|
pub fn land(&self) -> Land<'_> {
|
||||||
|
Land::from_sim(self.chunks)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Canvas<'a> {
|
pub struct Canvas<'a> {
|
||||||
|
@ -58,11 +58,11 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
|
|||||||
|
|
||||||
let info = canvas.info();
|
let info = canvas.info();
|
||||||
canvas.foreach_col(|canvas, wpos2d, col| {
|
canvas.foreach_col(|canvas, wpos2d, col| {
|
||||||
let trees = info.land().get_near_trees(wpos2d);
|
let trees = info.chunks().get_near_trees(wpos2d);
|
||||||
|
|
||||||
for TreeAttr { pos, seed, scale, forest_kind, inhabited } in trees {
|
for TreeAttr { pos, seed, scale, forest_kind, inhabited } in trees {
|
||||||
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
|
let tree = if let Some(tree) = tree_cache.entry(pos).or_insert_with(|| {
|
||||||
let col = ColumnGen::new(info.land()).get((pos, info.index()))?;
|
let col = ColumnGen::new(info.chunks()).get((pos, info.index()))?;
|
||||||
|
|
||||||
let is_quirky = QUIRKY_RAND.chance(seed, 1.0 / 500.0);
|
let is_quirky = QUIRKY_RAND.chance(seed, 1.0 / 500.0);
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
const_panic,
|
const_panic,
|
||||||
label_break_value,
|
label_break_value,
|
||||||
or_patterns,
|
or_patterns,
|
||||||
array_value_iter
|
array_value_iter,
|
||||||
|
array_map,
|
||||||
)]
|
)]
|
||||||
|
|
||||||
mod all;
|
mod all;
|
||||||
@ -282,7 +283,7 @@ impl World {
|
|||||||
wpos: chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
wpos: chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
|
||||||
column_grid: &zcache_grid,
|
column_grid: &zcache_grid,
|
||||||
column_grid_border: grid_border,
|
column_grid_border: grid_border,
|
||||||
land: &self.sim,
|
chunks: &self.sim,
|
||||||
index,
|
index,
|
||||||
chunk: sim_chunk,
|
chunk: sim_chunk,
|
||||||
},
|
},
|
||||||
|
63
world/src/site2/gen.rs
Normal file
63
world/src/site2/gen.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use common::{
|
||||||
|
terrain::Block,
|
||||||
|
store::{Id, Store},
|
||||||
|
};
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
pub enum Primitive {
|
||||||
|
Empty, // Placeholder
|
||||||
|
Aabb(Aabb<i32>),
|
||||||
|
And(Id<Primitive>, Id<Primitive>),
|
||||||
|
Or(Id<Primitive>, Id<Primitive>),
|
||||||
|
Xor(Id<Primitive>, Id<Primitive>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Fill {
|
||||||
|
pub prim: Id<Primitive>,
|
||||||
|
pub block: Block,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fill {
|
||||||
|
fn contains_at(&self, tree: &Store<Primitive>, prim: Id<Primitive>, pos: Vec3<i32>) -> bool {
|
||||||
|
match &tree[prim] {
|
||||||
|
Primitive::Empty => false,
|
||||||
|
Primitive::Aabb(aabb) => (aabb.min.x..aabb.max.x).contains(&pos.x) && (aabb.min.y..aabb.max.y).contains(&pos.y),
|
||||||
|
Primitive::And(a, b) => self.contains_at(tree, *a, pos) & self.contains_at(tree, *b, pos),
|
||||||
|
Primitive::Or(a, b) => self.contains_at(tree, *a, pos) | self.contains_at(tree, *b, pos),
|
||||||
|
Primitive::Xor(a, b) => self.contains_at(tree, *a, pos) ^ self.contains_at(tree, *b, pos),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_at(&self, tree: &Store<Primitive>, pos: Vec3<i32>) -> Option<Block> {
|
||||||
|
Some(self.block).filter(|_| self.contains_at(tree, self.prim, pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_bounds_inner(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
|
||||||
|
match &tree[prim] {
|
||||||
|
Primitive::Empty => Aabb::new_empty(Vec3::zero()),
|
||||||
|
Primitive::Aabb(aabb) => *aabb,
|
||||||
|
Primitive::And(a, b) => self.get_bounds_inner(tree, *a).intersection(self.get_bounds_inner(tree, *b)),
|
||||||
|
Primitive::Or(a, b) | Primitive::Xor(a, b) => self.get_bounds_inner(tree, *a).union(self.get_bounds_inner(tree, *b)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bounds(&self, tree: &Store<Primitive>) -> Aabb<i32> {
|
||||||
|
self.get_bounds_inner(tree, self.prim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Structure {
|
||||||
|
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Fill)>(
|
||||||
|
&self,
|
||||||
|
emit_prim: F,
|
||||||
|
emit_fill: G,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// Generate a primitive tree and fills for this structure
|
||||||
|
fn render_collect(&self) -> (Store<Primitive>, Vec<Fill>) {
|
||||||
|
let mut tree = Store::default();
|
||||||
|
let mut fills = Vec::new();
|
||||||
|
let root = self.render(|p| tree.insert(p), |f| fills.push(f));
|
||||||
|
(tree, fills)
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,15 @@
|
|||||||
|
mod gen;
|
||||||
mod plot;
|
mod plot;
|
||||||
mod tile;
|
mod tile;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
plot::{Plot, PlotKind},
|
plot::{Plot, PlotKind},
|
||||||
tile::{TileGrid, Tile, TileKind, HazardKind, TILE_SIZE},
|
tile::{TileGrid, Tile, TileKind, HazardKind, TILE_SIZE},
|
||||||
|
gen::{Primitive, Fill, Structure},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
site::SpawnRules,
|
site::SpawnRules,
|
||||||
util::{Grid, attempt, CARDINALS, SQUARE_4, SQUARE_9},
|
util::{Grid, attempt, DHashSet, CARDINALS, SQUARE_4, SQUARE_9, LOCALITY},
|
||||||
Canvas,
|
Canvas,
|
||||||
Land,
|
Land,
|
||||||
};
|
};
|
||||||
@ -86,7 +88,11 @@ impl Site {
|
|||||||
MAX_ITERS,
|
MAX_ITERS,
|
||||||
&heuristic,
|
&heuristic,
|
||||||
|tile| { let tile = *tile; CARDINALS.iter().map(move |dir| tile + *dir) },
|
|tile| { let tile = *tile; CARDINALS.iter().map(move |dir| tile + *dir) },
|
||||||
|a, b| rng.gen_range(1.0..1.5),
|
|a, b| {
|
||||||
|
let alt_a = land.get_alt_approx(self.tile_center_wpos(*a));
|
||||||
|
let alt_b = land.get_alt_approx(self.tile_center_wpos(*b));
|
||||||
|
(alt_a - alt_b).abs() / TILE_SIZE as f32
|
||||||
|
},
|
||||||
|tile| *tile == b,
|
|tile| *tile == b,
|
||||||
).into_path()?;
|
).into_path()?;
|
||||||
|
|
||||||
@ -95,7 +101,6 @@ impl Site {
|
|||||||
root_tile: a,
|
root_tile: a,
|
||||||
tiles: path.clone().into_iter().collect(),
|
tiles: path.clone().into_iter().collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
base_alt: 0,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.roads.push(plot);
|
self.roads.push(plot);
|
||||||
@ -163,7 +168,6 @@ impl Site {
|
|||||||
root_tile: pos,
|
root_tile: pos,
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
tiles: aabr_tiles(aabr).collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
base_alt: land.get_alt_approx(self.tile_center_wpos(aabr.center())) as i32,
|
|
||||||
});
|
});
|
||||||
self.plazas.push(plaza);
|
self.plazas.push(plaza);
|
||||||
self.blit_aabr(aabr, Tile {
|
self.blit_aabr(aabr, Tile {
|
||||||
@ -239,11 +243,10 @@ impl Site {
|
|||||||
let size = (2.0 + rng.gen::<f32>().powf(8.0) * 3.0).round() as u32;
|
let size = (2.0 + rng.gen::<f32>().powf(8.0) * 3.0).round() as u32;
|
||||||
if let Some((aabr, _)) = attempt(10, || site.find_roadside_aabr(rng, 4..(size + 1).pow(2), Extent2::broadcast(size))) {
|
if let Some((aabr, _)) = attempt(10, || site.find_roadside_aabr(rng, 4..(size + 1).pow(2), Extent2::broadcast(size))) {
|
||||||
let plot = site.create_plot(Plot {
|
let plot = site.create_plot(Plot {
|
||||||
kind: PlotKind::House,
|
kind: PlotKind::House(plot::House::generate(land, rng, &site, aabr)),
|
||||||
root_tile: aabr.center(),
|
root_tile: aabr.center(),
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
tiles: aabr_tiles(aabr).collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
base_alt: land.get_alt_approx(site.tile_center_wpos(aabr.center())) as i32,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
site.blit_aabr(aabr, Tile {
|
site.blit_aabr(aabr, Tile {
|
||||||
@ -260,7 +263,6 @@ impl Site {
|
|||||||
root_tile: aabr.center(),
|
root_tile: aabr.center(),
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
tiles: aabr_tiles(aabr).collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
base_alt: land.get_alt_approx(site.tile_center_wpos(aabr.center())) as i32,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
site.blit_aabr(aabr, Tile {
|
site.blit_aabr(aabr, Tile {
|
||||||
@ -309,7 +311,6 @@ impl Site {
|
|||||||
root_tile: aabr.center(),
|
root_tile: aabr.center(),
|
||||||
tiles: aabr_tiles(aabr).collect(),
|
tiles: aabr_tiles(aabr).collect(),
|
||||||
seed: rng.gen(),
|
seed: rng.gen(),
|
||||||
base_alt: land.get_alt_approx(site.tile_center_wpos(aabr.center())) as i32,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Walls
|
// Walls
|
||||||
@ -367,57 +368,47 @@ impl Site {
|
|||||||
|
|
||||||
pub fn render_tile(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng, tpos: Vec2<i32>) {
|
pub fn render_tile(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng, tpos: Vec2<i32>) {
|
||||||
let tile = self.tiles.get(tpos);
|
let tile = self.tiles.get(tpos);
|
||||||
let twpos = self.tile_wpos(tpos);
|
let twpos = self.tile_center_wpos(tpos);
|
||||||
let cols = (-(TILE_SIZE as i32)..TILE_SIZE as i32 * 2).map(|y| (-(TILE_SIZE as i32)..TILE_SIZE as i32 * 2).map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))).flatten();
|
let cols = (-(TILE_SIZE as i32)..TILE_SIZE as i32 * 2).map(|y| (-(TILE_SIZE as i32)..TILE_SIZE as i32 * 2).map(move |x| (twpos + Vec2::new(x, y), Vec2::new(x, y)))).flatten();
|
||||||
|
|
||||||
match &tile.kind {
|
match &tile.kind {
|
||||||
TileKind::Empty | TileKind::Hazard(_) => {},
|
TileKind::Empty | TileKind::Hazard(_) => {},
|
||||||
TileKind::Road => cols.for_each(|(wpos2d, offs)| {
|
TileKind::Road => {
|
||||||
let tpos = self.tile_wpos(wpos2d);
|
let near_roads = CARDINALS
|
||||||
|
.map(|rpos| if self.tiles.get(tpos + rpos) == tile {
|
||||||
|
Some(LineSegment2 {
|
||||||
|
start: self.tile_center_wpos(tpos).map(|e| e as f32),
|
||||||
|
end: self.tile_center_wpos(tpos + rpos).map(|e| e as f32),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
});
|
||||||
|
|
||||||
let is_x = [
|
cols.for_each(|(wpos2d, offs)| {
|
||||||
self.tiles.get(tpos - Vec2::unit_x()) == tile,
|
let wpos2df = wpos2d.map(|e| e as f32);
|
||||||
self.tiles.get(tpos) == tile,
|
let nearest_road = near_roads
|
||||||
self.tiles.get(tpos + Vec2::unit_x()) == tile,
|
.iter()
|
||||||
];
|
.copied()
|
||||||
|
.filter_map(|line| Some(line?.projected_point(wpos2df)))
|
||||||
|
.min_by_key(|p| p.distance_squared(wpos2df) as i32);
|
||||||
|
|
||||||
let dist_x = [
|
let is_near_road = nearest_road.map_or(false, |r| r.distance_squared(wpos2df) < 3.0f32.powi(2));
|
||||||
if is_x[0] ^ is_x[1] { Some((offs.x % tile::TILE_SIZE as i32) * if is_x[1] { -1 } else { 1 }) } else { None },
|
|
||||||
if is_x[1] ^ is_x[2] { Some((tile::TILE_SIZE as i32 - offs.x % tile::TILE_SIZE as i32) * if is_x[1] { -1 } else { 1 }) } else { None },
|
|
||||||
].iter().filter_map(|x| *x).min();
|
|
||||||
|
|
||||||
let is_y = [
|
if let Some(nearest_road) = nearest_road
|
||||||
self.tiles.get(tpos - Vec2::unit_y()) == tile,
|
.filter(|r| r.distance_squared(wpos2df) < 4.0f32.powi(2))
|
||||||
self.tiles.get(tpos) == tile,
|
{
|
||||||
self.tiles.get(tpos + Vec2::unit_y()) == tile,
|
let road_alt = canvas.col(nearest_road.map(|e| e.floor() as i32)).map_or(0, |col| col.alt as i32);
|
||||||
];
|
(-4..5).for_each(|z| canvas.map(
|
||||||
|
Vec3::new(wpos2d.x, wpos2d.y, road_alt + z),
|
||||||
let dist_y = [
|
|b| if z > 0 {
|
||||||
if is_y[0] ^ is_y[1] { Some((offs.y % tile::TILE_SIZE as i32) * if is_y[1] { -1 } else { 1 }) } else { None },
|
Block::air(SpriteKind::Empty)
|
||||||
if is_y[1] ^ is_y[2] { Some((tile::TILE_SIZE as i32 - offs.y % tile::TILE_SIZE as i32) * if is_y[1] { -1 } else { 1 }) } else { None },
|
} else {
|
||||||
].iter().filter_map(|x| *x).min();
|
Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))
|
||||||
|
},
|
||||||
let dist = dist_x.unwrap_or(-(tile::TILE_SIZE as i32)).min(dist_y.unwrap_or(-(tile::TILE_SIZE as i32)));
|
));
|
||||||
|
}
|
||||||
if dist > 4 {
|
});
|
||||||
let alt = canvas.col(wpos2d).map_or(0, |c| c.alt as i32);
|
},
|
||||||
(-4..5).for_each(|z| canvas.map(
|
|
||||||
Vec3::new(wpos2d.x, wpos2d.y, alt + z),
|
|
||||||
|b| if [
|
|
||||||
BlockKind::Grass,
|
|
||||||
BlockKind::Earth,
|
|
||||||
BlockKind::Sand,
|
|
||||||
BlockKind::Snow,
|
|
||||||
BlockKind::Rock,
|
|
||||||
]
|
|
||||||
.contains(&b.kind()) {
|
|
||||||
Block::new(BlockKind::Rock, Rgb::new(55, 45, 65))
|
|
||||||
} else {
|
|
||||||
b.with_sprite(SpriteKind::Empty)
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,9 +419,42 @@ impl Site {
|
|||||||
max: self.wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2) + 3, // Round up, uninclusive, border
|
max: self.wpos_tile_pos(canvas.wpos() + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + 2) + 3, // Round up, uninclusive, border
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Don't double-generate the same plot per chunk!
|
||||||
|
let mut plots = DHashSet::default();
|
||||||
|
|
||||||
for y in tile_aabr.min.y..tile_aabr.max.y {
|
for y in tile_aabr.min.y..tile_aabr.max.y {
|
||||||
for x in tile_aabr.min.x..tile_aabr.max.x {
|
for x in tile_aabr.min.x..tile_aabr.max.x {
|
||||||
self.render_tile(canvas, dynamic_rng, Vec2::new(x, y));
|
self.render_tile(canvas, dynamic_rng, Vec2::new(x, y));
|
||||||
|
|
||||||
|
if let Some(plot) = self.tiles.get(Vec2::new(x, y)).plot {
|
||||||
|
plots.insert(plot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut plots_to_render = plots.into_iter().collect::<Vec<_>>();
|
||||||
|
plots_to_render.sort_unstable();
|
||||||
|
|
||||||
|
for plot in plots_to_render {
|
||||||
|
let (prim_tree, fills) = match &self.plots[plot].kind {
|
||||||
|
PlotKind::House(house) => house.render_collect(),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
for fill in fills {
|
||||||
|
let aabb = fill.get_bounds(&prim_tree);
|
||||||
|
|
||||||
|
for x in aabb.min.x..aabb.max.x + 1 {
|
||||||
|
for y in aabb.min.y..aabb.max.y + 1 {
|
||||||
|
for z in aabb.min.z..aabb.max.z + 1 {
|
||||||
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
|
if let Some(block) = fill.sample_at(&prim_tree, pos) {
|
||||||
|
canvas.set(pos, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,7 +462,7 @@ impl Site {
|
|||||||
// let tile = self.wpos_tile(wpos2d);
|
// let tile = self.wpos_tile(wpos2d);
|
||||||
// let seed = tile.plot.map_or(0, |p| self.plot(p).seed);
|
// let seed = tile.plot.map_or(0, |p| self.plot(p).seed);
|
||||||
// match tile.kind {
|
// match tile.kind {
|
||||||
// TileKind::Field | TileKind::Road => (-4..5).for_each(|z| canvas.map(
|
// TileKind::Field /*| TileKind::Road*/ => (-4..5).for_each(|z| canvas.map(
|
||||||
// Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32 + z),
|
// Vec3::new(wpos2d.x, wpos2d.y, col.alt as i32 + z),
|
||||||
// |b| if [
|
// |b| if [
|
||||||
// BlockKind::Grass,
|
// BlockKind::Grass,
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
mod house;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
house::House,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::util::DHashSet;
|
use crate::util::DHashSet;
|
||||||
use common::path::Path;
|
use common::path::Path;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
@ -7,7 +14,6 @@ pub struct Plot {
|
|||||||
pub(crate) root_tile: Vec2<i32>,
|
pub(crate) root_tile: Vec2<i32>,
|
||||||
pub(crate) tiles: DHashSet<Vec2<i32>>,
|
pub(crate) tiles: DHashSet<Vec2<i32>>,
|
||||||
pub(crate) seed: u32,
|
pub(crate) seed: u32,
|
||||||
pub(crate) base_alt: i32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plot {
|
impl Plot {
|
||||||
@ -22,7 +28,7 @@ impl Plot {
|
|||||||
|
|
||||||
pub enum PlotKind {
|
pub enum PlotKind {
|
||||||
Field,
|
Field,
|
||||||
House,
|
House(House),
|
||||||
Plaza,
|
Plaza,
|
||||||
Castle,
|
Castle,
|
||||||
Road(Path<Vec2<i32>>),
|
Road(Path<Vec2<i32>>),
|
||||||
|
43
world/src/site2/plot/house.rs
Normal file
43
world/src/site2/plot/house.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use super::*;
|
||||||
|
use crate::Land;
|
||||||
|
use common::terrain::{Block, BlockKind};
|
||||||
|
use vek::*;
|
||||||
|
use rand::prelude::*;
|
||||||
|
|
||||||
|
pub struct House {
|
||||||
|
bounds: Aabr<i32>,
|
||||||
|
alt: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl House {
|
||||||
|
pub fn generate(land: &Land, rng: &mut impl Rng, site: &Site, tile_aabr: Aabr<i32>) -> Self {
|
||||||
|
Self {
|
||||||
|
bounds: Aabr {
|
||||||
|
min: site.tile_wpos(tile_aabr.min),
|
||||||
|
max: site.tile_wpos(tile_aabr.max),
|
||||||
|
},
|
||||||
|
alt: land.get_alt_approx(site.tile_center_wpos(tile_aabr.center())) as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Structure for House {
|
||||||
|
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Fill)>(
|
||||||
|
&self,
|
||||||
|
mut emit_prim: F,
|
||||||
|
mut emit_fill: G,
|
||||||
|
) {
|
||||||
|
let wall = emit_prim(Primitive::Aabb(Aabb {
|
||||||
|
min: Vec3::new(self.bounds.min.x, self.bounds.min.y, self.alt - 8),
|
||||||
|
max: Vec3::new(self.bounds.max.x, self.bounds.max.y, self.alt + 16),
|
||||||
|
}));
|
||||||
|
let inner = emit_prim(Primitive::Aabb(Aabb {
|
||||||
|
min: Vec3::new(self.bounds.min.x + 1, self.bounds.min.y + 1, self.alt - 8),
|
||||||
|
max: Vec3::new(self.bounds.max.x - 1, self.bounds.max.y - 1, self.alt + 16),
|
||||||
|
}));
|
||||||
|
emit_fill(Fill {
|
||||||
|
prim: emit_prim(Primitive::Xor(wall, inner)),
|
||||||
|
block: Block::new(BlockKind::Rock, Rgb::new(150, 50, 10)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user