Began work integrating new sites into the world

This commit is contained in:
Joshua Barretto 2021-02-10 01:18:22 +00:00
parent 6af0e77efd
commit c7e82aea26
6 changed files with 138 additions and 47 deletions

View File

@ -8,6 +8,7 @@ use crate::{
sim::WorldSim, sim::WorldSim,
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite}, site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite},
util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS}, util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS},
site2,
Index, Index,
}; };
use common::{ use common::{
@ -107,6 +108,7 @@ impl Civs {
attempt(5, || { attempt(5, || {
let (kind, size) = match ctx.rng.gen_range(0..8) { let (kind, size) = match ctx.rng.gen_range(0..8) {
0 => (SiteKind::Castle, 3), 0 => (SiteKind::Castle, 3),
1 => (SiteKind::Refactor, 5),
_ => (SiteKind::Dungeon, 0), _ => (SiteKind::Dungeon, 0),
}; };
let loc = find_site_loc(&mut ctx, None, size)?; let loc = find_site_loc(&mut ctx, None, size)?;
@ -142,14 +144,13 @@ impl Civs {
// Flatten ground around sites // Flatten ground around sites
for site in this.sites.values() { for site in this.sites.values() {
let radius = 48i32;
let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32); let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32);
let flatten_radius = match &site.kind { let (radius, flatten_radius) = match &site.kind {
SiteKind::Settlement => 10.0, SiteKind::Settlement => (32i32, 10.0),
SiteKind::Dungeon => 2.0, SiteKind::Dungeon => (8i32, 2.0),
SiteKind::Castle => 5.0, SiteKind::Castle => (16i32, 5.0),
SiteKind::Refactor => (0i32, 0.0),
}; };
let (raise, raise_dist): (f32, i32) = match &site.kind { let (raise, raise_dist): (f32, i32) = match &site.kind {
@ -215,6 +216,13 @@ impl Civs {
SiteKind::Castle => { SiteKind::Castle => {
WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng)) WorldSite::castle(Castle::generate(wpos, Some(ctx.sim), &mut rng))
}, },
SiteKind::Refactor => {
WorldSite::refactor({
let mut site = site2::Site::generate(&mut rng);
site.origin = wpos;
site
})
},
}); });
sim_site.site_tmp = Some(site); sim_site.site_tmp = Some(site);
let site_ref = &index.sites[site]; let site_ref = &index.sites[site];
@ -893,6 +901,7 @@ pub enum SiteKind {
Settlement, Settlement,
Dungeon, Dungeon,
Castle, Castle,
Refactor,
} }
impl Site { impl Site {

View File

@ -185,7 +185,7 @@ pub fn apply_trees_to(canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
TreeModel::Procedural(t, leaf_block) => Some( TreeModel::Procedural(t, leaf_block) => Some(
match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) { match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
(_, _, true, _) => StructureBlock::Block(BlockKind::Wood, Rgb::new(110, 68, 22)), (_, _, true, _) => StructureBlock::Block(BlockKind::Wood, Rgb::new(110, 68, 22)),
(_, _, _, true) => StructureBlock::Hollow, (_, _, _, true) => StructureBlock::None,
(true, _, _, _) => StructureBlock::Log, (true, _, _, _) => StructureBlock::Log,
(_, true, _, _) => *leaf_block, (_, true, _, _) => *leaf_block,
_ => StructureBlock::None, _ => StructureBlock::None,
@ -299,17 +299,17 @@ impl TreeConfig {
let log_scale = 1.0 + scale.log2().max(0.0); let log_scale = 1.0 + scale.log2().max(0.0);
Self { Self {
trunk_len: 32.0 * scale, trunk_len: 24.0 * scale,
trunk_radius: 1.25 * scale, trunk_radius: 1.25 * scale,
branch_child_len: 0.3 / scale, branch_child_len: 0.4 / scale,
branch_child_radius: 0.0, branch_child_radius: 0.0,
leaf_radius: 1.5 * log_scale..2.0 * log_scale, leaf_radius: 1.5 * log_scale..2.0 * log_scale,
straightness: 0.0, straightness: 0.0,
max_depth: 1, max_depth: 1,
splits: 50.0..70.0, splits: 50.0..70.0,
split_range: 0.1..1.2, split_range: 0.15..1.2,
branch_len_bias: 0.75, branch_len_bias: 0.75,
leaf_vertical_scale: 0.3, leaf_vertical_scale: 0.5,
proportionality: 1.0, proportionality: 1.0,
inhabited: false, inhabited: false,
} }
@ -362,6 +362,7 @@ impl ProceduralTree {
0, 0,
None, None,
rng, rng,
true,
); );
this.trunk_idx = trunk_idx; this.trunk_idx = trunk_idx;
@ -382,6 +383,7 @@ impl ProceduralTree {
depth: usize, depth: usize,
sibling_idx: Option<usize>, sibling_idx: Option<usize>,
rng: &mut impl Rng, rng: &mut impl Rng,
has_stairs: bool,
) -> (usize, Aabb<f32>) { ) -> (usize, Aabb<f32>) {
let end = start + dir * branch_len; let end = start + dir * branch_len;
let line = LineSegment3 { start, end }; let line = LineSegment3 { start, end };
@ -392,7 +394,7 @@ impl ProceduralTree {
0.0 0.0
}; };
let has_stairs = config.inhabited && depth < config.max_depth && branch_radius > 6.5 && start.xy().distance(end.xy()) < (start.z - end.z).abs() * 1.5; let has_stairs = /*has_stairs &&*/ config.inhabited && depth < config.max_depth && branch_radius > 6.5 && start.xy().distance(end.xy()) < (start.z - end.z).abs() * 1.5;
let bark_radius = if has_stairs { 5.0 } else { 0.0 } + wood_radius * 0.25; let bark_radius = if has_stairs { 5.0 } else { 0.0 } + wood_radius * 0.25;
// The AABB that covers this branch, along with wood and leaves that eminate // The AABB that covers this branch, along with wood and leaves that eminate
@ -458,6 +460,7 @@ impl ProceduralTree {
depth + 1, depth + 1,
child_idx, child_idx,
rng, rng,
has_stairs,
); );
child_idx = Some(branch_idx); child_idx = Some(branch_idx);
// Parent branches AABBs include the AABBs of child branches to allow for // Parent branches AABBs include the AABBs of child branches to allow for
@ -485,30 +488,30 @@ impl ProceduralTree {
pub fn get_bounds(&self) -> Aabb<f32> { self.branches[self.trunk_idx].aabb } pub fn get_bounds(&self) -> Aabb<f32> { self.branches[self.trunk_idx].aabb }
// Recursively search for branches or leaves by walking the tree's branch graph. // Recursively search for branches or leaves by walking the tree's branch graph.
fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, parent_d2: f32, branch_idx: usize) -> (bool, bool, bool, bool) { fn is_branch_or_leaves_at_inner(&self, pos: Vec3<f32>, parent: &Branch, branch_idx: usize) -> (bool, bool, bool, bool) {
let branch = &self.branches[branch_idx]; let branch = &self.branches[branch_idx];
// Always probe the sibling branch, since our AABB doesn't include its bounds // Always probe the sibling branch, since our AABB doesn't include its bounds
// (it's not one of our children) // (it's not one of our children)
let branch_or_leaves = branch let branch_or_leaves = branch
.sibling_idx .sibling_idx
.map(|idx| Vec4::<bool>::from(self.is_branch_or_leaves_at_inner(pos, parent_d2, idx))) .map(|idx| Vec4::<bool>::from(self.is_branch_or_leaves_at_inner(pos, parent, idx)))
.unwrap_or_default(); .unwrap_or_default();
// Only continue probing this sub-graph of the tree if the sample position falls // Only continue probing this sub-graph of the tree if the sample position falls
// within its AABB // within its AABB
if branch.aabb.contains_point(pos) { if branch.aabb.contains_point(pos) {
// Probe this branch // Probe this branch
let (this, d2) = branch.is_branch_or_leaves_at(pos, parent_d2); let (this, d2) = branch.is_branch_or_leaves_at(pos, parent);
let siblings = branch_or_leaves | Vec4::from(this); let siblings = branch_or_leaves | Vec4::from(this);
// Probe the children of this branch // Probe the children of this branch
let children = branch.child_idx let children = branch.child_idx
.map(|idx| Vec4::from(self.is_branch_or_leaves_at_inner(pos, d2, idx))) .map(|idx| Vec4::<bool>::from(self.is_branch_or_leaves_at_inner(pos, branch, idx)))
.unwrap_or_default(); .unwrap_or_default();
// Only allow empties for children if there is no solid at the current depth // Only allow empties for children if there is no solid at the current depth
(siblings | (children & Vec4::new(true, true, true, !(siblings.x | siblings.y)))).into_tuple() (siblings | children).into_tuple()
} else { } else {
branch_or_leaves.into_tuple() branch_or_leaves.into_tuple()
} }
@ -518,7 +521,8 @@ impl ProceduralTree {
/// position in the tree. /// position in the tree.
#[inline(always)] #[inline(always)]
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool, bool, bool) { pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>) -> (bool, bool, bool, bool) {
self.is_branch_or_leaves_at_inner(pos, 10000.0, self.trunk_idx) let (log, leaf, platform, air) = self.is_branch_or_leaves_at_inner(pos, &self.branches[self.trunk_idx], self.trunk_idx);
(log /*& !air*/, leaf & !air, platform & !air, air)
} }
} }
@ -543,26 +547,36 @@ struct Branch {
impl Branch { impl Branch {
/// Determine whether there are either branches or leaves at the given /// Determine whether there are either branches or leaves at the given
/// position in the branch. /// position in the branch.
pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>, parent_d2: f32) -> ((bool, bool, bool, bool), f32) { pub fn is_branch_or_leaves_at(&self, pos: Vec3<f32>, parent: &Branch) -> ((bool, bool, bool, bool), f32) {
// fn finvsqrt(x: f32) -> f32 { // fn finvsqrt(x: f32) -> f32 {
// let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1)); // let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
// y * (1.5 - ( x * 0.5 * y * y )) // y * (1.5 - ( x * 0.5 * y * y ))
// } // }
fn smooth(a: f32, b: f32, k: f32) -> f32 { fn length_factor(line: LineSegment3<f32>, p: Vec3<f32>) -> f32 {
// let h = (0.5 + 0.5 * (b - a) / k).clamped(0.0, 1.0); let len_sq = line.start.distance_squared(line.end);
// Lerp::lerp(b, a, h) - k * h * (1.0 - h) if len_sq < 0.001 {
0.0
let h = (k-(a-b).abs()).max(0.0); } else {
a.min(b) - h * h * 0.25 / k (p - line.start).dot(line.end - line.start) / len_sq
}
} }
// fn smooth(a: f32, b: f32, k: f32) -> f32 {
// // let h = (0.5 + 0.5 * (b - a) / k).clamped(0.0, 1.0);
// // Lerp::lerp(b, a, h) - k * h * (1.0 - h)
// let h = (k-(a-b).abs()).max(0.0);
// a.min(b) - h * h * 0.25 / k
// }
let p = self.line.projected_point(pos); let p = self.line.projected_point(pos);
let d2 = p.distance_squared(pos); let d2 = p.distance_squared(pos);
let d2_smooth = d2;//smooth(d2.sqrt(), parent_d2.sqrt(), 30.0).powi(2); let length_factor = length_factor(self.line, pos);
let wood_radius = Lerp::lerp(parent.wood_radius, self.wood_radius, length_factor);
let mask = if d2_smooth < self.wood_radius.powi(2) { let mask = if d2 < wood_radius.powi(2) {
(true, false, false, false) // Wood (true, false, false, false) // Wood
} else if { } else if {
let diff = (p - pos) / Vec3::new(1.0, 1.0, self.leaf_vertical_scale); let diff = (p - pos) / Vec3::new(1.0, 1.0, self.leaf_vertical_scale);
@ -571,13 +585,25 @@ impl Branch {
(false, true, false, false) // Leaves (false, true, false, false) // Leaves
} else { } else {
let stair_width = 5.0; let stair_width = 5.0;
let stair_thickness = 1.5; let stair_thickness = 2.0;
let stair_space = 6.0; let stair_space = 5.0;
if self.has_stairs && d2_smooth < (self.wood_radius + stair_width).powi(2) { if self.has_stairs {
let rpos = pos.xy() - p.xy(); let (platform, air) = if pos.z >= self.line.start.z.min(self.line.end.z) - 1.0 && pos.z <= self.line.start.z.max(self.line.end.z) + stair_thickness + stair_space && d2 < (wood_radius + stair_width).powi(2) {
let stretch = 32.0; let rpos = pos.xy() - p;
let stair_section = ((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z).rem_euclid(stretch); let stretch = 32.0;
(false, false, stair_section < stair_thickness, stair_section >= stair_thickness && stair_section < stair_thickness + stair_space) // Stairs let stair_section = ((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z).rem_euclid(stretch);
(stair_section < stair_thickness, stair_section >= stair_thickness && stair_section < stair_thickness + stair_space) // Stairs
} else {
(false, false)
};
let platform = platform || (self.has_stairs
&& self.wood_radius > 4.0
&& !air
&& d2 < (wood_radius + 10.0).powi(2)
&& pos.z % 48.0 < stair_thickness);
(false, false, platform, air)
} else { } else {
(false, false, false, false) (false, false, false, false)
} }

View File

@ -118,6 +118,7 @@ impl World {
}, },
}, },
civ::SiteKind::Castle => world_msg::SiteKind::Castle, civ::SiteKind::Castle => world_msg::SiteKind::Castle,
civ::SiteKind::Refactor => world_msg::SiteKind::Town,
}, },
wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), wpos: site.center * TerrainChunkSize::RECT_SIZE.map(|e| e as i32),
} }
@ -294,7 +295,7 @@ impl World {
// Apply site generation // Apply site generation
sim_chunk.sites.iter().for_each(|site| { sim_chunk.sites.iter().for_each(|site| {
index.sites[*site].apply_to(index, chunk_wpos2d, sample_get, &mut chunk) index.sites[*site].apply_to(&mut canvas, &mut dynamic_rng)
}); });
let gen_entity_pos = |dynamic_rng: &mut rand::rngs::ThreadRng| { let gen_entity_pos = |dynamic_rng: &mut rand::rngs::ThreadRng| {

View File

@ -11,7 +11,12 @@ pub use self::{
settlement::Settlement, settlement::Settlement,
}; };
use crate::{column::ColumnSample, IndexRef}; use crate::{
column::ColumnSample,
IndexRef,
site2,
Canvas,
};
use common::{ use common::{
generation::ChunkSupplement, generation::ChunkSupplement,
terrain::Block, terrain::Block,
@ -45,6 +50,7 @@ pub enum SiteKind {
Settlement(Settlement), Settlement(Settlement),
Dungeon(Dungeon), Dungeon(Dungeon),
Castle(Castle), Castle(Castle),
Refactor(site2::Site),
} }
impl Site { impl Site {
@ -69,11 +75,19 @@ impl Site {
} }
} }
pub fn refactor(s: site2::Site) -> Self {
Self {
kind: SiteKind::Refactor(s),
economy: Economy::default(),
}
}
pub fn radius(&self) -> f32 { pub fn radius(&self) -> f32 {
match &self.kind { match &self.kind {
SiteKind::Settlement(s) => s.radius(), SiteKind::Settlement(s) => s.radius(),
SiteKind::Dungeon(d) => d.radius(), SiteKind::Dungeon(d) => d.radius(),
SiteKind::Castle(c) => c.radius(), SiteKind::Castle(c) => c.radius(),
SiteKind::Refactor(s) => s.radius(),
} }
} }
@ -82,6 +96,7 @@ impl Site {
SiteKind::Settlement(s) => s.get_origin(), SiteKind::Settlement(s) => s.get_origin(),
SiteKind::Dungeon(d) => d.get_origin(), SiteKind::Dungeon(d) => d.get_origin(),
SiteKind::Castle(c) => c.get_origin(), SiteKind::Castle(c) => c.get_origin(),
SiteKind::Refactor(s) => s.origin,
} }
} }
@ -90,6 +105,7 @@ impl Site {
SiteKind::Settlement(s) => s.spawn_rules(wpos), SiteKind::Settlement(s) => s.spawn_rules(wpos),
SiteKind::Dungeon(d) => d.spawn_rules(wpos), SiteKind::Dungeon(d) => d.spawn_rules(wpos),
SiteKind::Castle(c) => c.spawn_rules(wpos), SiteKind::Castle(c) => c.spawn_rules(wpos),
SiteKind::Refactor(s) => s.spawn_rules(wpos),
} }
} }
@ -98,20 +114,24 @@ impl Site {
SiteKind::Settlement(s) => s.name(), SiteKind::Settlement(s) => s.name(),
SiteKind::Dungeon(d) => d.name(), SiteKind::Dungeon(d) => d.name(),
SiteKind::Castle(c) => c.name(), SiteKind::Castle(c) => c.name(),
SiteKind::Refactor(s) => "Experimental",
} }
} }
pub fn apply_to<'a>( pub fn apply_to<'a>(
&'a self, &'a self,
index: IndexRef, canvas: &mut Canvas,
wpos2d: Vec2<i32>, dynamic_rng: &mut impl Rng,
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
) { ) {
let info = canvas.info();
let get_col = |wpos| info.col(wpos + info.wpos);
match &self.kind { match &self.kind {
SiteKind::Settlement(s) => s.apply_to(index, wpos2d, get_column, vol), SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
SiteKind::Dungeon(d) => d.apply_to(index, wpos2d, get_column, vol), SiteKind::Dungeon(d) => d.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
SiteKind::Castle(c) => c.apply_to(index, wpos2d, get_column, vol), SiteKind::Castle(c) => c.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk),
SiteKind::Refactor(s) => {
s.render(canvas, dynamic_rng);
},
} }
} }
@ -129,6 +149,7 @@ impl Site {
}, },
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement), SiteKind::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
SiteKind::Refactor(_) => {},
} }
} }
} }

View File

@ -5,18 +5,38 @@ use self::{
plot::{Plot, PlotKind}, plot::{Plot, PlotKind},
tile::TileGrid, tile::TileGrid,
}; };
use crate::util::Grid; use crate::{
site::SpawnRules,
util::Grid,
Canvas,
};
use common::store::{Id, Store}; use common::store::{Id, Store};
use rand::prelude::*; use rand::prelude::*;
use vek::*; use vek::*;
#[derive(Default)] #[derive(Default)]
pub struct Site { pub struct Site {
pub(crate) origin: Vec2<i32>,
tiles: TileGrid, tiles: TileGrid,
plots: Store<Plot>, plots: Store<Plot>,
} }
impl Site { impl Site {
pub fn radius(&self) -> f32 {
(tile::MAX_BLOCK_RADIUS.pow(2) as f32 * 2.0).sqrt()
}
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
if wpos.distance_squared(self.origin) < 100i32.pow(2) {
SpawnRules {
trees: false,
..SpawnRules::default()
}
} else {
SpawnRules::default()
}
}
pub fn bounds(&self) -> Aabr<i32> { pub fn bounds(&self) -> Aabr<i32> {
let radius = tile::MAX_BLOCK_RADIUS; let radius = tile::MAX_BLOCK_RADIUS;
Aabr { Aabr {
@ -47,6 +67,10 @@ impl Site {
site site
} }
pub fn render(&self, canvas: &mut Canvas, dynamic_rng: &mut impl Rng) {
// TODO
}
} }
pub fn test_site() -> Site { Site::generate(&mut thread_rng()) } pub fn test_site() -> Site { Site::generate(&mut thread_rng()) }

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use common::spiral::Spiral2d;
pub const TILE_SIZE: u32 = 7; pub const TILE_SIZE: u32 = 7;
pub const ZONE_SIZE: u32 = 16; pub const ZONE_SIZE: u32 = 16;
@ -19,7 +20,9 @@ impl Default for TileGrid {
} }
impl TileGrid { impl TileGrid {
pub fn get(&self, tpos: Vec2<i32>) -> Option<&Tile> { pub fn get(&self, tpos: Vec2<i32>) -> &Tile {
static EMPTY: Tile = Tile::empty();
let tpos = tpos + TILE_RADIUS as i32; let tpos = tpos + TILE_RADIUS as i32;
self.zones self.zones
.get(tpos) .get(tpos)
@ -28,6 +31,7 @@ impl TileGrid {
.get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32))) .get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
}) })
.and_then(|tile| tile.as_ref()) .and_then(|tile| tile.as_ref())
.unwrap_or(&EMPTY)
} }
pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> { pub fn get_mut(&mut self, tpos: Vec2<i32>) -> Option<&mut Tile> {
@ -41,8 +45,14 @@ impl TileGrid {
}) })
} }
pub fn set(&mut self, tpos: Vec2<i32>, tile: Tile) -> Option<Tile> {
self.get_mut(tpos).map(|t| std::mem::replace(t, tile))
}
pub fn find_near(&self, tpos: Vec2<i32>, f: impl Fn(&Tile) -> bool) -> Option<Vec2<i32>> { pub fn find_near(&self, tpos: Vec2<i32>, f: impl Fn(&Tile) -> bool) -> Option<Vec2<i32>> {
None const MAX_SEARCH_RADIUS_BLOCKS: u32 = 256;
const MAX_SEARCH_CELLS: u32 = ((MAX_SEARCH_RADIUS_BLOCKS / TILE_SIZE) * 2).pow(2);
Spiral2d::new().take(MAX_SEARCH_CELLS as usize).map(|r| tpos + r).find(|tpos| (&f)(self.get(*tpos)))
} }
} }
@ -59,7 +69,7 @@ pub struct Tile {
} }
impl Tile { impl Tile {
pub fn empty() -> Self { pub const fn empty() -> Self {
Self { Self {
kind: TileKind::Empty, kind: TileKind::Empty,
plot: None, plot: None,