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,
site::{namegen::NameGen, Castle, Dungeon, Settlement, Site as WorldSite},
util::{attempt, seed_expan, MapVec, CARDINALS, NEIGHBORS},
site2,
Index,
};
use common::{
@ -107,6 +108,7 @@ impl Civs {
attempt(5, || {
let (kind, size) = match ctx.rng.gen_range(0..8) {
0 => (SiteKind::Castle, 3),
1 => (SiteKind::Refactor, 5),
_ => (SiteKind::Dungeon, 0),
};
let loc = find_site_loc(&mut ctx, None, size)?;
@ -142,14 +144,13 @@ impl Civs {
// Flatten ground around sites
for site in this.sites.values() {
let radius = 48i32;
let wpos = site.center * TerrainChunkSize::RECT_SIZE.map(|e: u32| e as i32);
let flatten_radius = match &site.kind {
SiteKind::Settlement => 10.0,
SiteKind::Dungeon => 2.0,
SiteKind::Castle => 5.0,
let (radius, flatten_radius) = match &site.kind {
SiteKind::Settlement => (32i32, 10.0),
SiteKind::Dungeon => (8i32, 2.0),
SiteKind::Castle => (16i32, 5.0),
SiteKind::Refactor => (0i32, 0.0),
};
let (raise, raise_dist): (f32, i32) = match &site.kind {
@ -215,6 +216,13 @@ impl Civs {
SiteKind::Castle => {
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);
let site_ref = &index.sites[site];
@ -893,6 +901,7 @@ pub enum SiteKind {
Settlement,
Dungeon,
Castle,
Refactor,
}
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(
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::Hollow,
(_, _, _, true) => StructureBlock::None,
(true, _, _, _) => StructureBlock::Log,
(_, true, _, _) => *leaf_block,
_ => StructureBlock::None,
@ -299,17 +299,17 @@ impl TreeConfig {
let log_scale = 1.0 + scale.log2().max(0.0);
Self {
trunk_len: 32.0 * scale,
trunk_len: 24.0 * scale,
trunk_radius: 1.25 * scale,
branch_child_len: 0.3 / scale,
branch_child_len: 0.4 / scale,
branch_child_radius: 0.0,
leaf_radius: 1.5 * log_scale..2.0 * log_scale,
straightness: 0.0,
max_depth: 1,
splits: 50.0..70.0,
split_range: 0.1..1.2,
split_range: 0.15..1.2,
branch_len_bias: 0.75,
leaf_vertical_scale: 0.3,
leaf_vertical_scale: 0.5,
proportionality: 1.0,
inhabited: false,
}
@ -362,6 +362,7 @@ impl ProceduralTree {
0,
None,
rng,
true,
);
this.trunk_idx = trunk_idx;
@ -382,6 +383,7 @@ impl ProceduralTree {
depth: usize,
sibling_idx: Option<usize>,
rng: &mut impl Rng,
has_stairs: bool,
) -> (usize, Aabb<f32>) {
let end = start + dir * branch_len;
let line = LineSegment3 { start, end };
@ -392,7 +394,7 @@ impl ProceduralTree {
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;
// The AABB that covers this branch, along with wood and leaves that eminate
@ -458,6 +460,7 @@ impl ProceduralTree {
depth + 1,
child_idx,
rng,
has_stairs,
);
child_idx = Some(branch_idx);
// 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 }
// 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];
// Always probe the sibling branch, since our AABB doesn't include its bounds
// (it's not one of our children)
let branch_or_leaves = branch
.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();
// Only continue probing this sub-graph of the tree if the sample position falls
// within its AABB
if branch.aabb.contains_point(pos) {
// 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);
// Probe the children of this branch
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();
// 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 {
branch_or_leaves.into_tuple()
}
@ -518,7 +521,8 @@ impl ProceduralTree {
/// position in the tree.
#[inline(always)]
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 {
/// Determine whether there are either branches or leaves at the given
/// 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 {
// let y = f32::from_bits(0x5f375a86 - (x.to_bits() >> 1));
// y * (1.5 - ( x * 0.5 * y * y ))
// }
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
fn length_factor(line: LineSegment3<f32>, p: Vec3<f32>) -> f32 {
let len_sq = line.start.distance_squared(line.end);
if len_sq < 0.001 {
0.0
} else {
(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 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
} else if {
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
} else {
let stair_width = 5.0;
let stair_thickness = 1.5;
let stair_space = 6.0;
if self.has_stairs && d2_smooth < (self.wood_radius + stair_width).powi(2) {
let rpos = pos.xy() - p.xy();
let stretch = 32.0;
let stair_section = ((rpos.x as f32).atan2(rpos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z).rem_euclid(stretch);
(false, false, stair_section < stair_thickness, stair_section >= stair_thickness && stair_section < stair_thickness + stair_space) // Stairs
let stair_thickness = 2.0;
let stair_space = 5.0;
if self.has_stairs {
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 rpos = pos.xy() - p;
let stretch = 32.0;
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 {
(false, false, false, false)
}

View File

@ -118,6 +118,7 @@ impl World {
},
},
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),
}
@ -294,7 +295,7 @@ impl World {
// Apply site generation
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| {

View File

@ -11,7 +11,12 @@ pub use self::{
settlement::Settlement,
};
use crate::{column::ColumnSample, IndexRef};
use crate::{
column::ColumnSample,
IndexRef,
site2,
Canvas,
};
use common::{
generation::ChunkSupplement,
terrain::Block,
@ -45,6 +50,7 @@ pub enum SiteKind {
Settlement(Settlement),
Dungeon(Dungeon),
Castle(Castle),
Refactor(site2::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 {
match &self.kind {
SiteKind::Settlement(s) => s.radius(),
SiteKind::Dungeon(d) => d.radius(),
SiteKind::Castle(c) => c.radius(),
SiteKind::Refactor(s) => s.radius(),
}
}
@ -82,6 +96,7 @@ impl Site {
SiteKind::Settlement(s) => s.get_origin(),
SiteKind::Dungeon(d) => d.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::Dungeon(d) => d.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::Dungeon(d) => d.name(),
SiteKind::Castle(c) => c.name(),
SiteKind::Refactor(s) => "Experimental",
}
}
pub fn apply_to<'a>(
&'a self,
index: IndexRef,
wpos2d: Vec2<i32>,
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
canvas: &mut Canvas,
dynamic_rng: &mut impl Rng,
) {
let info = canvas.info();
let get_col = |wpos| info.col(wpos + info.wpos);
match &self.kind {
SiteKind::Settlement(s) => s.apply_to(index, wpos2d, get_column, vol),
SiteKind::Dungeon(d) => d.apply_to(index, wpos2d, get_column, vol),
SiteKind::Castle(c) => c.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(canvas.index, canvas.wpos, get_col, canvas.chunk),
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::Castle(c) => c.apply_supplement(dynamic_rng, wpos2d, get_column, supplement),
SiteKind::Refactor(_) => {},
}
}
}

View File

@ -5,18 +5,38 @@ use self::{
plot::{Plot, PlotKind},
tile::TileGrid,
};
use crate::util::Grid;
use crate::{
site::SpawnRules,
util::Grid,
Canvas,
};
use common::store::{Id, Store};
use rand::prelude::*;
use vek::*;
#[derive(Default)]
pub struct Site {
pub(crate) origin: Vec2<i32>,
tiles: TileGrid,
plots: Store<Plot>,
}
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> {
let radius = tile::MAX_BLOCK_RADIUS;
Aabr {
@ -47,6 +67,10 @@ impl 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()) }

View File

@ -1,4 +1,5 @@
use super::*;
use common::spiral::Spiral2d;
pub const TILE_SIZE: u32 = 7;
pub const ZONE_SIZE: u32 = 16;
@ -19,7 +20,9 @@ impl Default for 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;
self.zones
.get(tpos)
@ -28,6 +31,7 @@ impl TileGrid {
.get(tpos.map(|e| e.rem_euclid(ZONE_SIZE as i32)))
})
.and_then(|tile| tile.as_ref())
.unwrap_or(&EMPTY)
}
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>> {
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 {
pub fn empty() -> Self {
pub const fn empty() -> Self {
Self {
kind: TileKind::Empty,
plot: None,