mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Began work integrating new sites into the world
This commit is contained in:
parent
6af0e77efd
commit
c7e82aea26
@ -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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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| {
|
||||
|
@ -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(_) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()) }
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user