Evven zoomier WorldGen, lots of things broken!

This commit is contained in:
Joshua Yanovski 2022-06-28 00:24:46 -07:00
parent a973155597
commit a3b7127de9
42 changed files with 1430 additions and 359 deletions

61
Cargo.lock generated
View File

@ -4411,6 +4411,16 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "probability"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34d1ba13c5cdf590c3d5ab5f53b36da2f596e43f8a27b236ad1801b5a1695d8"
dependencies = [
"random",
"special",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "0.1.5" version = "0.1.5"
@ -4731,6 +4741,12 @@ dependencies = [
"rand_core 0.5.1", "rand_core 0.5.1",
] ]
[[package]]
name = "random"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97d13a3485349981c90c79112a11222c3e6e75de1d52b87a7525b3bf5361420f"
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.2" version = "0.1.2"
@ -5678,6 +5694,15 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "special"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24a65e074159b75dcf173a4733ab2188baac24967b5c8ec9ed87ae15fcbc7636"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "specs" name = "specs"
version = "0.16.2" version = "0.16.2"
@ -5941,6 +5966,18 @@ dependencies = [
"unicode-xid 0.2.2", "unicode-xid 0.2.2",
] ]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.17",
"syn 1.0.90",
"unicode-xid 0.2.2",
]
[[package]] [[package]]
name = "tap" name = "tap"
version = "1.0.1" version = "1.0.1"
@ -6516,6 +6553,7 @@ version = "0.10.0"
dependencies = [ dependencies = [
"approx 0.4.0", "approx 0.4.0",
"bitflags", "bitflags",
"bitvec",
"chrono", "chrono",
"chrono-tz", "chrono-tz",
"clap 2.34.0", "clap 2.34.0",
@ -6555,6 +6593,7 @@ dependencies = [
"vek 0.15.8", "vek 0.15.8",
"veloren-common-assets", "veloren-common-assets",
"veloren-common-base", "veloren-common-base",
"zerocopy",
] ]
[[package]] [[package]]
@ -6981,6 +7020,7 @@ dependencies = [
"num-traits", "num-traits",
"ordered-float 2.10.0", "ordered-float 2.10.0",
"packed_simd_2", "packed_simd_2",
"probability",
"rand 0.8.5", "rand 0.8.5",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
"rayon", "rayon",
@ -7989,3 +8029,24 @@ checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c"
dependencies = [ dependencies = [
"time 0.3.9", "time 0.3.9",
] ]
[[package]]
name = "zerocopy"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236"
dependencies = [
"byteorder",
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f"
dependencies = [
"proc-macro2 1.0.36",
"syn 1.0.90",
"synstructure",
]

View File

@ -4,14 +4,12 @@
( (
caverns: false, // TODO: Disabled by default until cave overhaul caverns: false, // TODO: Disabled by default until cave overhaul
caves: true, caves: true,
rocks: true, rocks: false,
shrubs: true, shrubs: false,
trees: true, trees: true,
scatter: true, scatter: false,
paths: true, paths: true,
spots: true, spots: true,
site2_towns: true,
site2_giant_trees: true,
wildlife_density: 1.0, wildlife_density: 1.0,
peak_naming: true, peak_naming: true,
biome_naming: true, biome_naming: true,

View File

@ -1778,7 +1778,7 @@ impl Client {
if self.state.terrain().get_key(*key).is_none() { if self.state.terrain().get_key(*key).is_none() {
if !skip_mode && !self.pending_chunks.contains_key(key) { if !skip_mode && !self.pending_chunks.contains_key(key) {
const TOTAL_PENDING_CHUNKS_LIMIT: usize = 1024; const TOTAL_PENDING_CHUNKS_LIMIT: usize = 1024;
const CURRENT_TICK_PENDING_CHUNKS_LIMIT: usize = 2; const CURRENT_TICK_PENDING_CHUNKS_LIMIT: usize = 8;
if self.pending_chunks.len() < TOTAL_PENDING_CHUNKS_LIMIT if self.pending_chunks.len() < TOTAL_PENDING_CHUNKS_LIMIT
&& current_tick_send_chunk_requests && current_tick_send_chunk_requests
< CURRENT_TICK_PENDING_CHUNKS_LIMIT < CURRENT_TICK_PENDING_CHUNKS_LIMIT

View File

@ -38,6 +38,8 @@ strum = { version = "0.24", features = ["derive"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
approx = "0.4.0" approx = "0.4.0"
bitvec = "0.22"
# bumpalo = { version = "3.9.1", features = ["allocator_api"] }
clap = "2.33" clap = "2.33"
crossbeam-utils = "0.8.1" crossbeam-utils = "0.8.1"
bitflags = "1.2" bitflags = "1.2"
@ -53,6 +55,7 @@ tracing = { version = "0.1", default-features = false }
uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] } uuid = { version = "0.8.1", default-features = false, features = ["serde", "v4"] }
rand = "0.8" rand = "0.8"
fxhash = "0.2.1" fxhash = "0.2.1"
zerocopy = "0.6.1"
# Assets # Assets
common-assets = {package = "veloren-common-assets", path = "assets"} common-assets = {package = "veloren-common-assets", path = "assets"}

View File

@ -25,6 +25,34 @@ impl<T> Grid<T> {
} }
} }
#[inline]
pub fn populate_by_row<Row: FnMut(i32) -> Col, Col: FnMut(i32) -> T, const X: u32, const Y: u32>(
mut row: Row,
) -> Self
where
T: Clone + Default,
[(); {X as usize}]:
{
let mut cells = vec![T::default(); {X as usize * Y as usize}];
cells.array_chunks_mut::<{X as usize}>().enumerate().for_each(|(y, cells)| {
let mut col = row(y as i32);
cells.iter_mut().enumerate().for_each(|(x, cell)| {
*cell = col(x as i32);
});
});
Self {
cells,
/* cells: (0..size.y)
.flat_map(|y| {
let col = row(y);
(0..size.x).map(col)
})
/* .map(&mut f) */
.collect(), */
size: Vec2::new(X, Y).as_(),
}
}
pub fn new(size: Vec2<i32>, default_cell: T) -> Self pub fn new(size: Vec2<i32>, default_cell: T) -> Self
where where
T: Clone, T: Clone,

View File

@ -4,10 +4,13 @@
#![allow(clippy::option_map_unit_fn)] #![allow(clippy::option_map_unit_fn)]
#![deny(clippy::clone_on_ref_ptr)] #![deny(clippy::clone_on_ref_ptr)]
#![feature( #![feature(
allocator_api,
array_chunks,
associated_type_defaults, associated_type_defaults,
bool_to_option, bool_to_option,
fundamental, fundamental,
generic_const_exprs, generic_const_exprs,
generic_arg_infer,
label_break_value, label_break_value,
option_zip, option_zip,
trait_alias, trait_alias,

View File

@ -1,3 +1,4 @@
use zerocopy::AsBytes;
use super::SpriteKind; use super::SpriteKind;
use crate::{ use crate::{
comp::{fluid_dynamics::LiquidKind, tool::ToolKind}, comp::{fluid_dynamics::LiquidKind, tool::ToolKind},
@ -14,6 +15,7 @@ use vek::*;
make_case_elim!( make_case_elim!(
block_kind, block_kind,
#[derive( #[derive(
AsBytes,
Copy, Copy,
Clone, Clone,
Debug, Debug,
@ -28,6 +30,7 @@ make_case_elim!(
Display, Display,
)] )]
#[repr(u8)] #[repr(u8)]
/// NOTE: repr(u8) preserves the niche optimization for fieldless enums!
pub enum BlockKind { pub enum BlockKind {
Air = 0x00, // Air counts as a fluid Air = 0x00, // Air counts as a fluid
Water = 0x01, Water = 0x01,
@ -110,12 +113,30 @@ impl BlockKind {
} }
} }
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] #[derive(AsBytes, Copy, Clone, Debug, Eq, Serialize, Deserialize)]
/// NOTE: repr(C) appears to preservre niche optimizations!
#[repr(align(4), C)]
pub struct Block { pub struct Block {
kind: BlockKind, kind: BlockKind,
attr: [u8; 3], attr: [u8; 3],
} }
impl core::hash::Hash for Block {
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
u32::hash(&zerocopy::transmute!(*self), state)
}
}
impl PartialEq for Block {
#[inline]
fn eq(&self, other: &Self) -> bool {
let a: u32 = zerocopy::transmute!(*self);
let b: u32 = zerocopy::transmute!(*other);
a == b
}
}
impl Deref for Block { impl Deref for Block {
type Target = BlockKind; type Target = BlockKind;

View File

@ -106,7 +106,8 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
/// Compress chunk by using more intelligent defaults. /// Compress chunk by using more intelligent defaults.
pub fn defragment(&mut self) pub fn defragment(&mut self)
where where
V: Clone + Eq + Hash, V: zerocopy::AsBytes + Clone + Eq + Hash,
[(); { core::mem::size_of::<V>() }]:,
{ {
// First, defragment all subchunks. // First, defragment all subchunks.
self.sub_chunks.iter_mut().for_each(SubChunk::defragment); self.sub_chunks.iter_mut().for_each(SubChunk::defragment);

View File

@ -1,9 +1,11 @@
use crate::vol::{ use crate::vol::{
BaseVol, IntoPosIterator, IntoVolIterator, RasterableVol, ReadVol, VolSize, WriteVol, BaseVol, IntoPosIterator, IntoVolIterator, RasterableVol, ReadVol, VolSize, WriteVol,
}; };
use bitvec::prelude::*;
use core::{hash::Hash, iter::Iterator, marker::PhantomData, mem}; use core::{hash::Hash, iter::Iterator, marker::PhantomData, mem};
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use zerocopy::AsBytes;
use vek::*; use vek::*;
#[derive(Debug)] #[derive(Debug)]
@ -119,13 +121,21 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
/// Compress this subchunk by frequency. /// Compress this subchunk by frequency.
pub fn defragment(&mut self) pub fn defragment(&mut self)
where where
V: Clone + Eq + Hash, V: zerocopy::AsBytes + Clone + Eq + Hash,
[(); { core::mem::size_of::<V>() }]:,
{ {
// First, construct a HashMap with max capacity equal to GROUP_COUNT (since each // First, construct a HashMap with max capacity equal to GROUP_COUNT (since each
// filled group can have at most one slot). // filled group can have at most one slot).
let mut map = HashMap::with_capacity(Self::GROUP_COUNT_TOTAL as usize); //
// We use this hasher because:
//
// (1) we don't care about DDOS attacks (ruling out SipHash);
// (2) we don't care about determinism across computers (we could use AAHash);
// (3) we have 4-byte keys (for which FxHash is fastest).
let mut map = HashMap::/*with_capacity(Self::GROUP_COUNT_TOTAL as usize)*/with_hasher(core::hash::BuildHasherDefault::<fxhash::FxHasher64>::default());
let vox = &self.vox; let vox = &self.vox;
let default = &self.default; let default = &self.default;
let empty_bits = /*BitArray::new([0u32; /*Self::GROUP_COUNT_TOTAL as usize >> 5*/8])*/bitarr![0; 256];
self.indices self.indices
.iter() .iter()
.enumerate() .enumerate()
@ -134,23 +144,29 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
let end = start + Self::GROUP_VOLUME as usize; let end = start + Self::GROUP_VOLUME as usize;
if let Some(group) = vox.get(start..end) { if let Some(group) = vox.get(start..end) {
// Check to see if all blocks in this group are the same. // Check to see if all blocks in this group are the same.
let mut group = group.iter(); // NOTE: First element must exist because GROUP_VOLUME ≥ 1
let first = group.next().expect("GROUP_VOLUME ≥ 1"); let first = &group[0];
if group.all(|block| block == first) { let first_ = first.as_bytes();
// All blocks in the group were the same, so add our position to this entry // View group as bytes to benefit from specialization on [u8].
// in the HashMap. let group = group.as_bytes();
map.entry(first).or_insert(vec![]).push(grp_idx); /* let mut group = group.iter();
let first = group.next().expect("group_volume ≥ 1"); */
if group.array_chunks::<{ core::mem::size_of::<V>() }>().all(|block| block == first_) {
// all blocks in the group were the same, so add our position to this entry
// in the hashmap.
map.entry(first).or_insert_with(/*vec![]*//*bitvec::bitarr![0; chunk<v, s, m>::group_count_total]*/|| empty_bits)./*push*/set(grp_idx, true);
} }
} else { } else {
// This slot is empty (i.e. has the default value). // this slot is empty (i.e. has the default value).
map.entry(default).or_insert(vec![]).push(grp_idx); map.entry(default).or_insert_with(|| empty_bits)./*push*/set(grp_idx, true);
} }
}); });
// println!("{:?}", map);
// Now, find the block with max frequency in the HashMap and make that our new // Now, find the block with max frequency in the HashMap and make that our new
// default. // default.
let (new_default, default_groups) = if let Some((new_default, default_groups)) = map let (new_default, default_groups) = if let Some((new_default, default_groups)) = map
.into_iter() .into_iter()
.max_by_key(|(_, default_groups)| default_groups.len()) .max_by_key(|(_, default_groups)| default_groups./*len*/count_ones())
{ {
(new_default.clone(), default_groups) (new_default.clone(), default_groups)
} else { } else {
@ -163,11 +179,12 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
let mut new_vox = let mut new_vox =
Vec::with_capacity(Self::GROUP_COUNT_TOTAL as usize - default_groups.len()); Vec::with_capacity(Self::GROUP_COUNT_TOTAL as usize - default_groups.len());
let num_groups = self.num_groups(); let num_groups = self.num_groups();
self.indices let mut indices = &mut self.indices[..Self::GROUP_COUNT_TOTAL as usize];
indices
.iter_mut() .iter_mut()
.enumerate() .enumerate()
.for_each(|(grp_idx, base)| { .for_each(|(grp_idx, base)| {
if default_groups.contains(&grp_idx) { if default_groups/*.contains(&grp_idx)*/[grp_idx] {
// Default groups become 255 // Default groups become 255
*base = 255; *base = 255;
} else { } else {

View File

@ -44,7 +44,7 @@ impl ChunkGenerator {
slowjob_pool: &SlowJobPool, slowjob_pool: &SlowJobPool,
world: Arc<World>, world: Arc<World>,
index: IndexOwned, index: IndexOwned,
time: (TimeOfDay, Calendar), time: /*(TimeOfDay, Calendar)*/TimeOfDay,
) { ) {
let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) { let v = if let Entry::Vacant(v) = self.pending_chunks.entry(key) {
v v

View File

@ -2823,8 +2823,7 @@ fn handle_debug_column(
_action: &ChatCommand, _action: &ChatCommand,
) -> CmdResult<()> { ) -> CmdResult<()> {
let sim = server.world.sim(); let sim = server.world.sim();
let calendar = (*server.state.ecs().read_resource::<Calendar>()).clone(); /* let calendar = (*server.state.ecs().read_resource::<Calendar>()).clone(); */
let sampler = server.world.sample_columns();
let wpos = if let (Some(x), Some(y)) = parse_args!(args, i32, i32) { let wpos = if let (Some(x), Some(y)) = parse_args!(args, i32, i32) {
Vec2::new(x, y) Vec2::new(x, y)
} else { } else {
@ -2832,7 +2831,9 @@ fn handle_debug_column(
// FIXME: Deal with overflow, if needed. // FIXME: Deal with overflow, if needed.
pos.0.xy().map(|x| x as i32) pos.0.xy().map(|x| x as i32)
}; };
let msg_generator = |calendar| { let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
let msg_generator = |/*calendar*/| {
let sampler = server.world.sample_columns(chunk_pos, server.index.as_index_ref())?;
let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?; let alt = sim.get_interpolated(wpos, |chunk| chunk.alt)?;
let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?; let basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?;
let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?; let water_alt = sim.get_interpolated(wpos, |chunk| chunk.water_alt)?;
@ -2842,9 +2843,8 @@ fn handle_debug_column(
let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?; let rockiness = sim.get_interpolated(wpos, |chunk| chunk.rockiness)?;
let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?; let tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?; let spawn_rate = sim.get_interpolated(wpos, |chunk| chunk.spawn_rate)?;
let chunk_pos = wpos.map2(TerrainChunkSize::RECT_SIZE, |e, sz: u32| e / sz as i32);
let chunk = sim.get(chunk_pos)?; let chunk = sim.get(chunk_pos)?;
let col = sampler.get((wpos, server.index.as_index_ref(), Some(calendar)))?; let col = sampler.get((wpos/*, server.index.as_index_ref(), Some(calendar)*/));
let gradient = sim.get_gradient_approx(chunk_pos)?; let gradient = sim.get_gradient_approx(chunk_pos)?;
let downhill = chunk.downhill; let downhill = chunk.downhill;
let river = &chunk.river; let river = &chunk.river;
@ -2883,7 +2883,7 @@ spawn_rate {:?} "#,
spawn_rate spawn_rate
)) ))
}; };
if let Some(s) = msg_generator(&calendar) { if let Some(s) = msg_generator(/*&calendar*/) {
server.notify_client(client, ServerGeneral::server_msg(ChatType::CommandInfo, s)); server.notify_client(client, ServerGeneral::server_msg(ChatType::CommandInfo, s));
Ok(()) Ok(())
} else { } else {

View File

@ -420,7 +420,7 @@ impl Server {
.civs() .civs()
.sites() .sites()
.filter(|site| { .filter(|site| {
matches!(site.kind, SiteKind::Settlement | SiteKind::Refactor) matches!(site.kind, /* SiteKind::Settlement | */SiteKind::Refactor)
}) })
.map(|site| site.center) .map(|site| site.center)
.min_by_key(|site_pos| site_pos.distance_squared(center_chunk)) .min_by_key(|site_pos| site_pos.distance_squared(center_chunk))
@ -981,8 +981,8 @@ impl Server {
Arc::clone(world), Arc::clone(world),
index.clone(), index.clone(),
( (
*ecs.read_resource::<TimeOfDay>(), *ecs.read_resource::<TimeOfDay>()/* ,
(*ecs.read_resource::<Calendar>()).clone(), (*ecs.read_resource::<Calendar>()).clone(), */
), ),
); );
}); });
@ -1201,8 +1201,8 @@ impl Server {
Arc::clone(&self.world), Arc::clone(&self.world),
self.index.clone(), self.index.clone(),
( (
*ecs.read_resource::<TimeOfDay>(), *ecs.read_resource::<TimeOfDay>()/* ,
(*ecs.read_resource::<Calendar>()).clone(), (*ecs.read_resource::<Calendar>()).clone(), */
), ),
); );
} }

View File

@ -9,7 +9,7 @@ use crate::{
wiring, BattleModeBuffer, SpawnPoint, wiring, BattleModeBuffer, SpawnPoint,
}; };
use common::{ use common::{
calendar::Calendar, /* calendar::Calendar, */
character::CharacterId, character::CharacterId,
combat, combat,
combat::DamageContributor, combat::DamageContributor,
@ -465,7 +465,7 @@ impl StateExt for State {
.for_each(|chunk_key| { .for_each(|chunk_key| {
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
{ {
let time = (*ecs.read_resource::<TimeOfDay>(), (*ecs.read_resource::<Calendar>()).clone()); let time = (*ecs.read_resource::<TimeOfDay>()/* , (*ecs.read_resource::<Calendar>()).clone() */);
chunk_generator.generate_chunk(None, chunk_key, &slow_jobs, Arc::clone(world), index.clone(), time); chunk_generator.generate_chunk(None, chunk_key, &slow_jobs, Arc::clone(world), index.clone(), time);
} }
}); });

View File

@ -15,7 +15,7 @@ use crate::{
ChunkRequest, SpawnPoint, Tick, ChunkRequest, SpawnPoint, Tick,
}; };
use common::{ use common::{
calendar::Calendar, /* calendar::Calendar, */
comp::{ comp::{
self, agent, bird_medium, skillset::skills, BehaviorCapability, ForceUpdate, Pos, Waypoint, self, agent, bird_medium, skillset::skills, BehaviorCapability, ForceUpdate, Pos, Waypoint,
}, },
@ -59,7 +59,7 @@ impl<'a> System<'a> for Sys {
Read<'a, SpawnPoint>, Read<'a, SpawnPoint>,
Read<'a, Settings>, Read<'a, Settings>,
Read<'a, TimeOfDay>, Read<'a, TimeOfDay>,
Read<'a, Calendar>, /* Read<'a, Calendar>, */
ReadExpect<'a, SlowJobPool>, ReadExpect<'a, SlowJobPool>,
ReadExpect<'a, IndexOwned>, ReadExpect<'a, IndexOwned>,
ReadExpect<'a, Arc<World>>, ReadExpect<'a, Arc<World>>,
@ -92,7 +92,7 @@ impl<'a> System<'a> for Sys {
spawn_point, spawn_point,
server_settings, server_settings,
time_of_day, time_of_day,
calendar, /* calendar, */
slow_jobs, slow_jobs,
index, index,
world, world,
@ -127,7 +127,7 @@ impl<'a> System<'a> for Sys {
&slow_jobs, &slow_jobs,
Arc::clone(&world), Arc::clone(&world),
index.clone(), index.clone(),
(*time_of_day, calendar.clone()), (*time_of_day/*, calendar.clone()*/),
) )
}); });

View File

@ -112,8 +112,8 @@ impl<'a> GreedyMesh<'a> {
max_size max_size
); );
// TODO: Collect information to see if we can choose a good value here. // TODO: Collect information to see if we can choose a good value here.
let large_size_threshold = 256.min(min_max_dim / 2 + 1); let large_size_threshold = /*256*/8.min(min_max_dim / 2 + 1);
let small_size_threshold = 33.min(large_size_threshold / 2 + 1); let small_size_threshold = /*33*/3.min(large_size_threshold / 2 + 1);
let size = guillotiere::Size::new(32, 32).min(max_size); let size = guillotiere::Size::new(32, 32).min(max_size);
let atlas = let atlas =
guillotiere::SimpleAtlasAllocator::with_options(size, &guillotiere::AllocatorOptions { guillotiere::SimpleAtlasAllocator::with_options(size, &guillotiere::AllocatorOptions {

View File

@ -40,7 +40,7 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
vol: &VolGrid2d<V>, vol: &VolGrid2d<V>,
lit_blocks: impl Iterator<Item = (Vec3<i32>, u8)>, lit_blocks: impl Iterator<Item = (Vec3<i32>, u8)>,
) -> impl Fn(Vec3<i32>) -> f32 + 'static + Send + Sync { ) -> impl Fn(Vec3<i32>) -> f32 + 'static + Send + Sync {
span!(_guard, "calc_light"); /* span!(_guard, "calc_light");
const UNKNOWN: u8 = 255; const UNKNOWN: u8 = 255;
const OPAQUE: u8 = 254; const OPAQUE: u8 = 254;
@ -207,10 +207,11 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
} }
} }
drop(light_map); drop(light_map); */
move |wpos| { move |wpos| {
let pos = wpos - min_bounds.min; 1.0
/* let pos = wpos - min_bounds.min;
let l = light_map2 let l = light_map2
.get(lm_idx2(pos.x, pos.y, pos.z)) .get(lm_idx2(pos.x, pos.y, pos.z))
.copied() .copied()
@ -220,7 +221,7 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
l as f32 / SUNLIGHT as f32 l as f32 / SUNLIGHT as f32
} else { } else {
0.0 0.0
} } */
} }
} }
@ -255,7 +256,7 @@ pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + '
let mut glow_blocks = Vec::new(); let mut glow_blocks = Vec::new();
// TODO: This expensive, use BlocksOfInterest instead /* // TODO: This expensive, use BlocksOfInterest instead
let mut volume = vol.cached(); let mut volume = vol.cached();
for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST { for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST {
for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST { for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST {
@ -268,7 +269,7 @@ pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + '
.map(|glow| glow_blocks.push((wpos, glow))); .map(|glow| glow_blocks.push((wpos, glow)));
} }
} }
} } */
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0) // Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
let light = calc_light(true, SUNLIGHT, range, vol, core::iter::empty()); let light = calc_light(true, SUNLIGHT, range, vol, core::iter::empty());

View File

@ -945,13 +945,19 @@ impl<V: RectRasterableVol> Terrain<V> {
span!(guard, "Queue meshing from todo list"); span!(guard, "Queue meshing from todo list");
let mesh_focus_pos = focus_pos.map(|e| e.trunc()).xy().as_::<i64>(); let mesh_focus_pos = focus_pos.map(|e| e.trunc()).xy().as_::<i64>();
for (todo, chunk) in self let mut todo = self
.mesh_todo .mesh_todo
.values_mut() .values_mut()
.filter(|todo| !todo.is_worker_active) .filter(|todo| !todo.is_worker_active)
.min_by_key(|todo| ((todo.pos.as_::<i64>() * TerrainChunk::RECT_SIZE.as_::<i64>()).distance_squared(mesh_focus_pos), todo.started_tick)) // TODO: BinaryHeap
.collect::<Vec<_>>();
todo.sort_unstable_by_key(|todo| ((todo.pos.as_::<i64>() * TerrainChunk::RECT_SIZE.as_::<i64>()).distance_squared(mesh_focus_pos), todo.started_tick));
for (todo, chunk) in todo.into_iter()
.filter(|todo| !todo.is_worker_active)
/* .min_by_key(|todo| ((todo.pos.as_::<i64>() * TerrainChunk::RECT_SIZE.as_::<i64>()).distance_squared(mesh_focus_pos), todo.started_tick)) */
// Find a reference to the actual `TerrainChunk` we're meshing // Find a reference to the actual `TerrainChunk` we're meshing
.and_then(|todo| { ./*and_then*/filter_map(|todo| {
let pos = todo.pos; let pos = todo.pos;
Some((todo, scene_data.state Some((todo, scene_data.state
.terrain() .terrain()

View File

@ -27,10 +27,12 @@ fxhash = "0.2.1"
image = { version = "0.23.12", default-features = false, features = ["png"] } image = { version = "0.23.12", default-features = false, features = ["png"] }
itertools = "0.10" itertools = "0.10"
vek = { version = "0.15.8", features = ["serde"] } vek = { version = "0.15.8", features = ["serde"] }
# noise = { git = "https://github.com/Razaekel/noise-rs.git", rev = "ef8a859116c7f743bf00ee672a1b20ae653ec15c", version = "0.7", default-features = false }
noise = { version = "0.7", default-features = false } noise = { version = "0.7", default-features = false }
noisy_float = { version = "0.2", default-features = false } noisy_float = { version = "0.2", default-features = false }
num = "0.4" num = "0.4"
ordered-float = "2.0.1" ordered-float = "2.0.1"
probability = "0.18.0"
hashbrown = { version = "0.11", features = ["rayon", "serde", "nightly"] } hashbrown = { version = "0.11", features = ["rayon", "serde", "nightly"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
tracing = { version = "0.1", default-features = false } tracing = { version = "0.1", default-features = false }

View File

@ -225,7 +225,7 @@ impl Filler for NullCanvas {
fn dungeon(c: &mut Criterion) { fn dungeon(c: &mut Criterion) {
let pool = ThreadPoolBuilder::new().build().unwrap(); let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate( let (world, index) = World::generate(
230, /*230*/59686,
WorldOpts { WorldOpts {
seed_elements: true, seed_elements: true,
world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()), world_file: FileOpts::LoadAsset(DEFAULT_WORLD_MAP.into()),
@ -299,8 +299,12 @@ fn dungeon(c: &mut Criterion) {
bench_group("generate_citadel", "render_citadel", Site::generate_citadel); bench_group("generate_citadel", "render_citadel", Site::generate_citadel);
c.bench_function("generate_chunk", |b| { c.bench_function("generate_chunk", |b| {
// let chunk_pos = (world.sim().map_size_lg().chunks() >> 1).as_();
// let chunk_pos = Vec2::new(9500 / 32, 29042 / 32); // let chunk_pos = Vec2::new(9500 / 32, 29042 / 32);
let chunk_pos = (world.sim().map_size_lg().chunks() >> 1).as_(); // let chunk_pos = Vec2::new(26944 / 32, 26848 / 32);
let chunk_pos = Vec2::new(842, 839);
// let chunk_pos = Vec2::new(24507/32, 20682/32);
// let chunk_pos = Vec2::new(19638/32, 19621/32);
b.iter(|| { b.iter(|| {
black_box(world.generate_chunk(index.as_index_ref(), chunk_pos, || false, None)); black_box(world.generate_chunk(index.as_index_ref(), chunk_pos, || false, None));
}); });

View File

@ -31,35 +31,33 @@ pub struct BlockGen<'a> {
impl<'a> BlockGen<'a> { impl<'a> BlockGen<'a> {
pub fn new(column_gen: ColumnGen<'a>) -> Self { Self { column_gen } } pub fn new(column_gen: ColumnGen<'a>) -> Self { Self { column_gen } }
pub fn sample_column<'b>( /* pub fn sample_column<'b>(
column_gen: &ColumnGen<'a>, column_gen: &ColumnGen<'a>,
cache: &'b mut SmallCache<Option<ColumnSample<'a>>>, cache: &'b mut SmallCache<ColumnSample<'a>>,
wpos: Vec2<i32>, wpos: Vec2<i32>,
index: IndexRef<'a>, /* index: IndexRef<'a>,
calendar: Option<&'a Calendar>, calendar: Option<&'a Calendar>, */
) -> Option<&'b ColumnSample<'a>> { ) -> &'b ColumnSample<'a> {
cache cache
.get(wpos, |wpos| column_gen.get((wpos, index, calendar))) .get(wpos, |wpos| column_gen.get(/*(wpos, index, calendar)*/wpos))
.as_ref() } */
}
pub fn get_z_cache( /* pub fn get_z_cache(
&mut self, &mut self,
wpos: Vec2<i32>, wpos: Vec2<i32>,
index: IndexRef<'a>, /* index: IndexRef<'a>,
calendar: Option<&'a Calendar>, calendar: Option<&'a Calendar>, */
) -> Option<ZCache<'a>> { ) -> ZCache<'a> {
let BlockGen { column_gen } = self; let BlockGen { column_gen } = self;
// Main sample // Main sample
let sample = column_gen.get((wpos, index, calendar))?; let sample = column_gen.get((wpos/*, index, calendar*/));
Some(ZCache { sample, calendar }) /*Some(*/ZCache { sample, calendar: column_gen.sim.calendar.as_ref(), }/*)*/
} } */
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: Option<&ZCache>) -> Option<Block> { pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: ZCache) -> Option<Block> {
let z_cache = z_cache?; let sample = z_cache.sample;
let sample = &z_cache.sample;
let &ColumnSample { let &ColumnSample {
alt, alt,
basement, basement,
@ -101,10 +99,14 @@ impl<'a> BlockGen<'a> {
{ {
Some(Block::empty()) Some(Block::empty())
} else { } else {
let sinlike = |x: f32| {
// x.sin()
(x.fract() - 0.5).abs() * 2.0 - 1.0
};
let col = Lerp::lerp( let col = Lerp::lerp(
col.map(|e| e as f32), col.map(|e| e as f32),
col.map(|e| e as f32) * 0.7, col.map(|e| e as f32) * 0.7,
(wposf.z as f32 - basement * 0.3).div(2.0).sin() * 0.5 + 0.5, sinlike((wposf.z as f32 - basement * 0.3).div(2.0)) * 0.5 + 0.5,
) )
.map(|e| e as u8); .map(|e| e as u8);
Some(Block::new(BlockKind::Rock, col)) Some(Block::new(BlockKind::Rock, col))
@ -154,13 +156,15 @@ impl<'a> BlockGen<'a> {
} }
} }
#[derive(Clone,Copy)]
pub struct ZCache<'a> { pub struct ZCache<'a> {
pub sample: ColumnSample<'a>, pub sample: &'a ColumnSample/*<'a>*/,
pub calendar: Option<&'a Calendar>, /* pub calendar: Option<&'a Calendar>, */
} }
impl<'a> ZCache<'a> { impl<'a> ZCache<'a> {
pub fn get_z_limits(&self) -> (f32, f32) { pub fn get_z_limits(self) -> (f32, f32) {
// dbg!(self.sample.alt, self.sample.chaos, self.sample.cliff_offset);
let min = self.sample.alt let min = self.sample.alt
- (self.sample.chaos.min(1.0) * 16.0) - (self.sample.chaos.min(1.0) * 16.0)
- self.sample.cliff_offset.max(0.0); - self.sample.cliff_offset.max(0.0);

View File

@ -7,6 +7,7 @@ use crate::{
layer::spot::Spot, layer::spot::Spot,
sim::{SimChunk, WorldSim}, sim::{SimChunk, WorldSim},
util::{Grid, Sampler}, util::{Grid, Sampler},
TerrainGrid,
}; };
use common::{ use common::{
calendar::Calendar, calendar::Calendar,
@ -21,16 +22,16 @@ use vek::*;
pub struct CanvasInfo<'a> { pub struct CanvasInfo<'a> {
pub(crate) chunk_pos: Vec2<i32>, pub(crate) chunk_pos: Vec2<i32>,
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>*/ColumnSample/*<'a>*//*>*/>,
pub(crate) column_grid_border: i32, pub(crate) column_grid_border: i32,
pub(crate) chunks: &'a WorldSim, 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,
pub(crate) calendar: Option<&'a Calendar>, /* pub(crate) calendar: Option<&'a Calendar>, */
} }
impl<'a> CanvasInfo<'a> { impl<'a> CanvasInfo<'a> {
pub fn calendar(&self) -> Option<&'a Calendar> { self.calendar } pub fn calendar(&self) -> Option<&'a Calendar> { self.chunks.calendar.as_ref() }
pub fn wpos(&self) -> Vec2<i32> { self.wpos } pub fn wpos(&self) -> Vec2<i32> { self.wpos }
@ -42,24 +43,41 @@ impl<'a> CanvasInfo<'a> {
.into() .into()
} }
pub fn col(&self, wpos: Vec2<i32>) -> Option<&'a ColumnSample<'a>> { #[inline]
fn col_inner(&self, wpos: Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/> {
self.column_grid self.column_grid
.get(self.column_grid_border + wpos - self.wpos()) .get(self.column_grid_border + wpos - self.wpos())
.and_then(Option::as_ref) /* .and_then(Option::as_ref)
.map(|zc| &zc.sample) .map(|zc| &zc.sample) */
}
#[inline]
pub fn col(&self, wpos: Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/> {
/* match self.col_inner(wpos) {
Some(col) => Some(col),
None => {
println!("Hit: {:?} vs. {:?}", wpos, self.area());
None
}
} */
self.col_inner(wpos)
} }
/// Attempt to get the data for the given column, generating it if we don't /// Attempt to get the data for the given column, generating it if we don't
/// have it. /// have it.
/// ///
/// This function does not (currently) cache generated columns. /// This function does not (currently) cache generated columns.
#[inline]
pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> { pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
self.col(wpos).map(Cow::Borrowed).or_else(|| { self.col_inner(wpos).map(Cow::Borrowed).or_else(|| {
Some(Cow::Owned(ColumnGen::new(self.chunks()).get(( let chunk_pos = TerrainGrid::chunk_key(wpos);
wpos, let column_gen = ColumnGen::new(self.chunks(), chunk_pos, self.index())?;
Some(Cow::Owned(column_gen.get((
wpos/* ,
self.index(), self.index(),
self.calendar, self.calendar, */
))?)) ))))
}) })
} }
@ -95,7 +113,7 @@ impl<'a> CanvasInfo<'a> {
sim: &'a WorldSim, sim: &'a WorldSim,
f: F, f: F,
) -> A { ) -> A {
let zcache_grid = Grid::populate_from(Vec2::broadcast(1), |_| None); let zcache_grid = Grid::populate_from(Vec2::broadcast(0), |_| /*None*/unimplemented!("Zero size grid"));
let sim_chunk = SimChunk { let sim_chunk = SimChunk {
chaos: 0.0, chaos: 0.0,
alt: 0.0, alt: 0.0,
@ -128,7 +146,7 @@ impl<'a> CanvasInfo<'a> {
chunks: sim, chunks: sim,
index, index,
chunk: &sim_chunk, chunk: &sim_chunk,
calendar: None, /* calendar: None, */
}) })
} }
} }
@ -177,7 +195,7 @@ impl<'a> Canvas<'a> {
for x in chunk_aabr.min.x.max(aabr.min.x)..chunk_aabr.max.x.min(aabr.max.x) { for x in chunk_aabr.min.x.max(aabr.min.x)..chunk_aabr.max.x.min(aabr.max.x) {
let wpos2d = Vec2::new(x, y); let wpos2d = Vec2::new(x, y);
let info = self.info; let info = self.info;
let col = if let Some(col) = info.col(wpos2d) { let col = if let Some(col) = info.col_inner(wpos2d) {
col col
} else { } else {
return; return;
@ -229,7 +247,7 @@ impl<'a> Canvas<'a> {
seed, seed,
col, col,
|sprite| block.with_sprite(sprite), |sprite| block.with_sprite(sprite),
info.calendar, info.calendar(),
) { ) {
if !new_block.is_air() { if !new_block.is_air() {
if with_snow && col.snow_cover && above { if with_snow && col.snow_cover && above {

View File

@ -5,7 +5,7 @@ mod econ;
use crate::{ use crate::{
config::CONFIG, config::CONFIG,
sim::WorldSim, sim::WorldSim,
site::{namegen::NameGen, Castle, Settlement, Site as WorldSite, Tree}, site::{namegen::NameGen, Castle, /*Settlement, */Site as WorldSite/*, Tree */},
site2, site2,
util::{attempt, seed_expan, DHashMap, NEIGHBORS}, util::{attempt, seed_expan, DHashMap, NEIGHBORS},
Index, Land, Index, Land,
@ -119,11 +119,11 @@ impl Civs {
let (kind, size, avoid) = match ctx.rng.gen_range(0..64) { let (kind, size, avoid) = match ctx.rng.gen_range(0..64) {
0..=5 => (SiteKind::Castle, 3, (&castle_enemies, 40)), 0..=5 => (SiteKind::Castle, 3, (&castle_enemies, 40)),
28..=31 => { 28..=31 => {
if index.features().site2_giant_trees { /*if index.features().site2_giant_trees */{
(SiteKind::GiantTree, 4, (&tree_enemies, 40)) (SiteKind::GiantTree, 4, (&tree_enemies, 40))
} else { }/* else {
(SiteKind::Tree, 4, (&tree_enemies, 40)) (SiteKind::Tree, 4, (&tree_enemies, 40))
} }*/
}, },
32..=37 => (SiteKind::Gnarling, 5, (&gnarling_enemies, 40)), 32..=37 => (SiteKind::Gnarling, 5, (&gnarling_enemies, 40)),
// 32..=37 => (SiteKind::Citadel, 5, (&castle_enemies, 20)), // 32..=37 => (SiteKind::Citadel, 5, (&castle_enemies, 20)),
@ -166,19 +166,19 @@ impl Civs {
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 (radius, flatten_radius) = match &site.kind { let (radius, flatten_radius) = match &site.kind {
SiteKind::Settlement => (32i32, 10.0f32), /* SiteKind::Settlement => (32i32, 10.0f32), */
SiteKind::Dungeon => (8i32, 3.0), SiteKind::Dungeon => (8i32, 3.0f32),
SiteKind::Castle => (16i32, 5.0), SiteKind::Castle => (16i32, 5.0),
SiteKind::Refactor => (32i32, 10.0), SiteKind::Refactor => (32i32, 10.0),
SiteKind::CliffTown => (32i32, 10.0), SiteKind::CliffTown => (32i32, 10.0),
SiteKind::Tree => (12i32, 8.0), /* SiteKind::Tree => (12i32, 8.0), */
SiteKind::GiantTree => (12i32, 8.0), SiteKind::GiantTree => (12i32, 8.0),
SiteKind::Gnarling => (16i32, 10.0), SiteKind::Gnarling => (16i32, 10.0),
SiteKind::Citadel => (16i32, 0.0), SiteKind::Citadel => (16i32, 0.0),
}; };
let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind { let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
SiteKind::Settlement => (10.0, 6, true), /* SiteKind::Settlement => (10.0, 6, true), */
SiteKind::Castle => (0.0, 6, true), SiteKind::Castle => (0.0, 6, true),
_ => (0.0, 0, false), _ => (0.0, 0, false),
}; };
@ -235,9 +235,9 @@ impl Civs {
let mut rng = ctx.reseed().rng; let mut rng = ctx.reseed().rng;
let site = index.sites.insert(match &sim_site.kind { let site = index.sites.insert(match &sim_site.kind {
SiteKind::Settlement => { /* SiteKind::Settlement => {
WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng)) WorldSite::settlement(Settlement::generate(wpos, Some(ctx.sim), &mut rng))
}, }, */
SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon( SiteKind::Dungeon => WorldSite::dungeon(site2::Site::generate_dungeon(
&Land::from_sim(ctx.sim), &Land::from_sim(ctx.sim),
&mut rng, &mut rng,
@ -256,9 +256,9 @@ impl Civs {
&mut rng, &mut rng,
wpos, wpos,
)), )),
SiteKind::Tree => { /* SiteKind::Tree => {
WorldSite::tree(Tree::generate(wpos, &Land::from_sim(ctx.sim), &mut rng)) WorldSite::tree(Tree::generate(wpos, &Land::from_sim(ctx.sim), &mut rng))
}, }, */
SiteKind::GiantTree => WorldSite::giant_tree(site2::Site::generate_giant_tree( SiteKind::GiantTree => WorldSite::giant_tree(site2::Site::generate_giant_tree(
&Land::from_sim(ctx.sim), &Land::from_sim(ctx.sim),
&mut rng, &mut rng,
@ -974,7 +974,7 @@ impl Civs {
matches!( matches!(
p.kind, p.kind,
SiteKind::Refactor SiteKind::Refactor
| SiteKind::Settlement /* | SiteKind::Settlement */
| SiteKind::CliffTown | SiteKind::CliffTown
| SiteKind::Castle | SiteKind::Castle
) )
@ -984,7 +984,7 @@ impl Civs {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
nearby.sort_by_key(|(_, dist)| *dist as i32); nearby.sort_by_key(|(_, dist)| *dist as i32);
if let SiteKind::Refactor | SiteKind::Settlement | SiteKind::CliffTown | SiteKind::Castle = if let SiteKind::Refactor | /* SiteKind::Settlement | */SiteKind::CliffTown | SiteKind::Castle =
self.sites[site].kind self.sites[site].kind
{ {
for (nearby, _) in nearby.into_iter().take(5) { for (nearby, _) in nearby.into_iter().take(5) {
@ -1235,12 +1235,12 @@ impl fmt::Display for Site {
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum SiteKind { pub enum SiteKind {
Settlement, /* Settlement, */
Dungeon, Dungeon,
Castle, Castle,
Refactor, Refactor,
CliffTown, CliffTown,
Tree, /* Tree, */
GiantTree, GiantTree,
Gnarling, Gnarling,
Citadel, Citadel,
@ -1349,7 +1349,7 @@ impl SiteKind {
(-0.3..0.4).contains(&chunk.temp) && chunk.tree_density > 0.75 (-0.3..0.4).contains(&chunk.temp) && chunk.tree_density > 0.75
}, },
SiteKind::Citadel => (-0.3..0.7).contains(&chunk.temp) && chunk.tree_density < 0.4, SiteKind::Citadel => (-0.3..0.7).contains(&chunk.temp) && chunk.tree_density < 0.4,
SiteKind::GiantTree | SiteKind::Tree => chunk.tree_density > 0.4, SiteKind::GiantTree /* | SiteKind::Tree */=> chunk.tree_density > 0.4,
SiteKind::CliffTown => { SiteKind::CliffTown => {
(-0.6..0.4).contains(&chunk.temp) (-0.6..0.4).contains(&chunk.temp)
&& chunk.near_cliffs() && chunk.near_cliffs()
@ -1382,7 +1382,7 @@ impl SiteKind {
} }
true true
}, },
SiteKind::Refactor | SiteKind::Settlement => suitable_for_town(6.7), SiteKind::Refactor/* | SiteKind::Settlement*/ => suitable_for_town(6.7),
_ => true, _ => true,
} }
}) })
@ -1403,7 +1403,7 @@ impl Site {
pub fn is_dungeon(&self) -> bool { matches!(self.kind, SiteKind::Dungeon) } pub fn is_dungeon(&self) -> bool { matches!(self.kind, SiteKind::Dungeon) }
pub fn is_settlement(&self) -> bool { pub fn is_settlement(&self) -> bool {
matches!(self.kind, SiteKind::Settlement | SiteKind::Refactor) matches!(self.kind, /*SiteKind::Settlement | */SiteKind::Refactor)
} }
pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) } pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) }

File diff suppressed because it is too large Load Diff

View File

@ -87,8 +87,6 @@ pub struct Features {
pub scatter: bool, pub scatter: bool,
pub paths: bool, pub paths: bool,
pub spots: bool, pub spots: bool,
pub site2_towns: bool,
pub site2_giant_trees: bool,
// 1.0 is the default wildlife density // 1.0 is the default wildlife density
pub wildlife_density: f32, pub wildlife_density: f32,
pub peak_naming: bool, pub peak_naming: bool,

View File

@ -482,7 +482,7 @@ pub fn apply_caves_supplement<'a>(
// NOTE: Used only for dynamic elements like chests and entities! // NOTE: Used only for dynamic elements like chests and entities!
dynamic_rng: &mut impl Rng, dynamic_rng: &mut impl Rng,
wpos2d: Vec2<i32>, wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol), vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
index: IndexRef, index: IndexRef,
supplement: &mut ChunkSupplement, supplement: &mut ChunkSupplement,

View File

@ -675,7 +675,7 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
.enumerate() .enumerate()
.find_map(|(i, (kind, water_mode, f))| { .find_map(|(i, (kind, water_mode, f))| {
let (density, patch) = f(canvas.chunk(), col); let (density, patch) = f(canvas.chunk(), col);
let density = patch let density = /* patch
.map(|(base_density_prop, wavelen, threshold)| { .map(|(base_density_prop, wavelen, threshold)| {
if canvas if canvas
.index() .index()
@ -694,7 +694,7 @@ pub fn apply_scatter_to(canvas: &mut Canvas, rng: &mut impl Rng) {
density * base_density_prop density * base_density_prop
} }
}) })
.unwrap_or(density); .unwrap_or(*/density/*)*/;
if density > 0.0 if density > 0.0
&& rng.gen::<f32>() < density //RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density) && rng.gen::<f32>() < density //RandomField::new(i as u32).chance(Vec3::new(wpos2d.x, wpos2d.y, 0), density)
&& matches!(&water_mode, Underwater | Floating) == underwater && matches!(&water_mode, Underwater | Floating) == underwater

View File

@ -16,7 +16,10 @@ use common::{
vol::ReadVol, vol::ReadVol,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
use rand::prelude::*; use probability::{
prelude::{source, Gaussian, Inverse},
};
use rand::{distributions::Uniform, prelude::*};
use std::{f32, ops::Range}; use std::{f32, ops::Range};
use vek::*; use vek::*;
@ -51,10 +54,54 @@ pub fn tree_valid_at(col: &ColumnSample, seed: u32) -> bool {
true true
} }
// Inlined from
//
// https://docs.rs/probability/latest/src/probability/distribution/binomial.rs.html#393
//
// Approximate normal of binomial distribution.
fn approximate_by_normal(p: f64, np: f64, v: f64, u: f64) -> f64 {
let w = Gaussian::new(0.0, 1.0).inverse(u);
let w2 = w * w;
let w3 = w2 * w;
let w4 = w3 * w;
let w5 = w4 * w;
let w6 = w5 * w;
let sd = v.sqrt();
let sd_em1 = sd.recip();
let sd_em2 = v.recip();
let sd_em3 = sd_em1 * sd_em2;
let sd_em4 = sd_em2 * sd_em2;
let p2 = p * p;
let p3 = p2 * p;
let p4 = p2 * p2;
np +
sd * w +
(p + 1.0) / 3.0 -
(2.0 * p - 1.0) * w2 / 6.0 +
sd_em1 * w3 * (2.0 * p2 - 2.0 * p - 1.0) / 72.0 -
w * (7.0 * p2 - 7.0 * p + 1.0) / 36.0 +
sd_em2 * (2.0 * p - 1.0) * (p + 1.0) * (p - 2.0) * (3.0 * w4 + 7.0 * w2 - 16.0 / 1620.0) +
sd_em3 * (
w5 * (4.0 * p4 - 8.0 * p3 - 48.0 * p2 + 52.0 * p - 23.0) / 17280.0 +
w3 * (256.0 * p4 - 512.0 * p3 - 147.0 * p2 + 403.0 * p - 137.0) / 38880.0 -
w * (433.0 * p4 - 866.0 * p3 - 921.0 * p2 + 1354.0 * p - 671.0) / 38880.0
) +
sd_em4 * (
w6 * (2.0 * p - 1.0) * (p2 - p + 1.0) * (p2 - p + 19.0) / 34020.0 +
w4 * (2.0 * p - 1.0) * (9.0 * p4 - 18.0 * p3 - 35.0 * p2 + 44.0 * p - 25.0) / 15120.0 +
w2 * (2.0 * p - 1.0) * (
923.0 * p4 - 1846.0 * p3 + 5271.0 * p2 - 4348.0 * p + 5189.0
) / 408240.0 -
4.0 * (2.0 * p - 1.0) * (p + 1.0) * (p - 2.0) * (23.0 * p2 - 23.0 * p + 2.0) / 25515.0
)
// + O(v.powf(-2.5)), with probabilty of 1 - 2e-9
}
pub fn apply_trees_to( pub fn apply_trees_to(
canvas: &mut Canvas, canvas: &mut Canvas,
dynamic_rng: &mut impl Rng, dynamic_rng: &mut impl Rng,
calendar: Option<&Calendar>, /* calendar: Option<&Calendar>, */
) { ) {
// TODO: Get rid of this // TODO: Get rid of this
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
@ -72,6 +119,7 @@ pub fn apply_trees_to(
} }
let info = canvas.info(); let info = canvas.info();
let calendar = info.calendar();
/* let mut tree_cache = StructureGenCache::new(info.chunks().gen_ctx.structure_gen.clone()); */ /* let mut tree_cache = StructureGenCache::new(info.chunks().gen_ctx.structure_gen.clone()); */
// Get all the trees in range. // Get all the trees in range.
@ -237,6 +285,7 @@ pub fn apply_trees_to(
render_area: Aabr<i32>, render_area: Aabr<i32>,
filler: /*impl FnOnce(&'b mut Canvas<'a>) -> &'b mut F*/&'b mut F, filler: /*impl FnOnce(&'b mut Canvas<'a>) -> &'b mut F*/&'b mut F,
render: Render, */ render: Render, */
let mut wpos = tree.pos;
let (bounds, hanging_sprites) = match &tree.model { let (bounds, hanging_sprites) = match &tree.model {
TreeModel::Structure(s) => { TreeModel::Structure(s) => {
site2::render_collect( site2::render_collect(
@ -251,7 +300,6 @@ pub fn apply_trees_to(
.fill(filler.prefab(s, tree.pos, tree.seed), filler); .fill(filler.prefab(s, tree.pos, tree.seed), filler);
}, },
); );
arena.reset();
(s.get_bounds(), [(0.0004, SpriteKind::Beehive)].as_ref()) (s.get_bounds(), [(0.0004, SpriteKind::Beehive)].as_ref())
}, },
&TreeModel::Procedural(ref t, leaf_block) => { &TreeModel::Procedural(ref t, leaf_block) => {
@ -259,7 +307,6 @@ pub fn apply_trees_to(
let trunk_block = t.config.trunk_block; let trunk_block = t.config.trunk_block;
let leaf_vertical_scale = t.config.leaf_vertical_scale.recip(); let leaf_vertical_scale = t.config.leaf_vertical_scale.recip();
let branch_child_radius_lerp = t.config.branch_child_radius_lerp; let branch_child_radius_lerp = t.config.branch_child_radius_lerp;
let wpos = tree.pos;
// NOTE: Technically block_from_structure isn't correct here, because it could // NOTE: Technically block_from_structure isn't correct here, because it could
// lerp with position; in practice, it almost never does, and most of the other // lerp with position; in practice, it almost never does, and most of the other
@ -372,10 +419,153 @@ pub fn apply_trees_to(
}); });
}, },
); );
arena.reset();
(bounds, t.config.hanging_sprites) (bounds, t.config.hanging_sprites)
}, },
}; };
arena.reset();
// Subtract 1 from our max z, since we know there can be no hanging sprites at the top
// of the tree.
wpos.z -= 1;
let mut aabb = Aabb {
min: tree.pos + bounds.min.as_(),
max: wpos + bounds.max.as_(),
};
if !aabb.is_valid() {
// Zero-size tree somehow?
return;
}
let size = aabb.size();
// NOTE: The chunk volume should always fit into 24 bits, so if it doesn't we can
// exit early (as sprites not rendering will be the least of our worries).
let volume: u32 = if let Ok(volume) = size.product().abs().try_into() {
volume
} else {
tracing::warn!("Tree bounds were larger than our maximum chunk size...");
return;
};
if volume == 0 {
// One-layer tree, so we can't draw any sprites...
return;
}
// NOTE: volume.x * volume.y fits in 10 bits, so it fits in a u32, hence can't overflow.
let volume_x = size.w.abs() as u32;
// NOTE: volume.z fits into 14 bits, so it fits into a u32, and it times volume_x fits
// into 19 bits, so this can't overflow.
let volume_xy = volume_x * size.h.abs() as u32;
aabb.intersect(Aabb {
min: render_area.min.with_z(aabb.min.z),
max: render_area.max.with_z(aabb.max.z),
});
struct Source<T: ?Sized>(T);
impl<'a, T: rand::Rng + ?Sized> source::Source for Source<T> {
fn read_u64(&mut self) -> u64 {
self.0.next_u64()
}
}
let mut source = Source(&mut *dynamic_rng);
// Sprite rendering is performed by constructing random sequences of sample blocks,
// then testing whether they are valid for placement in the tree. This should
// hopefully be a lot more efficient than actually iterating through the tree and
// randomly sampling each time, since the sprite probability is tiny, assuming
// that we can keep sampling from the distribution cheap enough (to be determined!) and
// that the difference between overall volume and hangable surface area doesn't drop
// too low (very likely).
hanging_sprites.iter().for_each(|(chance, sprite)| {
// Let B(volume, chance) be the binomial distribution over independent
// tests of [volume] blocks, each of which has probability [chance]. We sample
// uniformly at random from this distribution in order to get an expected number of
// *successful* trials, without actually having to iterate over every block.
// Hopefully (assuming sampling the distribution isn't too slow), this should be a
// lot faster than *actually* iterating over that many blocks.
//
// NOTE: We assume u32 fits into usize, so this is legal; we should statically
// assert this somewhere (we might already do so).
/* println!("Generating samples: n={} p={}", volume, chance); */
let num_samples = {
use source::Source;
let n = volume;
let p = (*chance).into();
let q = 1.0 - p;
let np = volume as f64 * p;
let npq = np * q;
let u = source.read_f64();
approximate_by_normal(p, np, npq, u).floor() as usize
};
// let num_samples = Binomial::new(volume as usize, (*chance).into()).sample(&mut source);
// Now, we choose [num_samples] blocks uniformly at random from the AABB, and test
// them to see if they're candidates for our sprite; if so, we place it there.
/* println!("Generated samples: {}", num_samples); */
(&mut source.0)
// NOTE: Cannot panic, as we checked earlier that volume > 0.
.sample_iter(Uniform::new(0, volume))
.take(num_samples)
// NOTE: We gamble here that division and modulus for the tree volume will
// still be faster than sampling three times from the RNG.
.map(|ipos| {
let z = ipos.div_euclid(volume_xy);
let ipos = ipos - z * volume_xy;
let y = ipos.div_euclid(volume_x);
let x = ipos - y * volume_x;
tree.pos + Vec3::new(x, y, z).as_()
})
.for_each(|mut wpos| {
let get_block = |wpos| {
let mut model_pos = /*Vec3::from(*/
(wpos - tree.pos)/*
.xy()
.map2(Vec2::new(tree.units.0, tree.units.1), |rpos, unit| {
unit * rpos
})
.sum(),
) + Vec3::unit_z() * (wpos.z - tree.pos.z)*/;
block_from_structure(
info.index(),
match &tree.model {
TreeModel::Structure(s) => s.get(model_pos).ok().copied()?,
TreeModel::Procedural(t, leaf_block) =>
match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) {
(_, _, true, _) => {
StructureBlock::Filled(BlockKind::Wood, Rgb::new(150, 98, 41))
},
(_, _, _, true) => StructureBlock::None,
(true, _, _, _) => t.config.trunk_block,
(_, true, _, _) => *leaf_block,
_ => StructureBlock::None,
},
},
wpos,
tree.pos.xy(),
tree.seed,
&col,
Block::air,
calendar,
)
};
// Hanging sprites can be placed in locations that are currently empty, and
// which have a tree block above them. NOTE: The old code overwrote
// existing terrain as well, but we opt not to do this.
canvas.map(wpos, |block| {
if !block.is_filled() {
wpos.z += 1;
if get_block(wpos).map_or(false, |block| block.is_filled()) {
/* println!("Success: {:?} = {:?}", wpos, sprite); */
block.with_sprite(*sprite)
} else {
block
}
} else {
block
}
})
});
});
/* let bounds = match &tree.model { /* let bounds = match &tree.model {
TreeModel::Structure(s) => s.get_bounds(), TreeModel::Structure(s) => s.get_bounds(),
@ -808,7 +998,7 @@ impl TreeConfig {
pub struct ProceduralTree { pub struct ProceduralTree {
branches: Vec<Branch>, branches: Vec<Branch>,
trunk_idx: usize, trunk_idx: usize,
config: TreeConfig, pub(crate) config: TreeConfig,
roots: Vec<Root>, roots: Vec<Root>,
root_aabb: Aabb<f32>, root_aabb: Aabb<f32>,
} }

View File

@ -282,10 +282,10 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
} }
}), }),
// Ocean animals // Ocean animals
("world.wildlife.spawn.tropical.ocean", |_c, col| { ("world.wildlife.spawn.tropical.ocean", |c, col| {
close(col.temp, CONFIG.tropical_temp, 0.1) / 10.0 close(col.temp, CONFIG.tropical_temp, 0.1) / 10.0
* if col.water_dist.map(|d| d < 1.0).unwrap_or(false) * if col.water_dist.map(|d| d < 1.0).unwrap_or(false)
&& !matches!(col.chunk.get_biome(), BiomeKind::Ocean) && !matches!(c.get_biome(), BiomeKind::Ocean)
{ {
0.001 0.001
} else { } else {
@ -293,9 +293,9 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
} }
}), }),
// Arctic ocean animals // Arctic ocean animals
("world.wildlife.spawn.arctic.ocean", |_c, col| { ("world.wildlife.spawn.arctic.ocean", |c, col| {
close(col.temp, 0.0, 0.25) / 10.0 close(col.temp, 0.0, 0.25) / 10.0
* if matches!(col.chunk.get_biome(), BiomeKind::Ocean) { * if matches!(c.get_biome(), BiomeKind::Ocean) {
0.001 0.001
} else { } else {
0.0 0.0
@ -347,12 +347,12 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
// NOTE: Used only for dynamic elements like chests and entities! // NOTE: Used only for dynamic elements like chests and entities!
dynamic_rng: &mut R, dynamic_rng: &mut R,
wpos2d: Vec2<i32>, wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol), vol: &(impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
index: IndexRef, index: IndexRef,
chunk: &SimChunk, chunk: &SimChunk,
supplement: &mut ChunkSupplement, supplement: &mut ChunkSupplement,
time: Option<&(TimeOfDay, Calendar)>, time: Option<(&TimeOfDay, &Calendar)>,
) { ) {
let scatter = &index.wildlife_spawns; let scatter = &index.wildlife_spawns;
// Configurable density multiplier // Configurable density multiplier
@ -391,7 +391,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
.request(current_day_period, calendar, is_underwater, is_ice) .request(current_day_period, calendar, is_underwater, is_ice)
.and_then(|pack| { .and_then(|pack| {
(dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate (dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate
&& col_sample.gradient < Some(1.3)) && col_sample.gradient < /*Some(*/1.3/*)*/)
.then(|| pack) .then(|| pack)
}) })
}) })

View File

@ -47,6 +47,7 @@ pub use column::ColumnSample;
pub use index::{IndexOwned, IndexRef}; pub use index::{IndexOwned, IndexRef};
use crate::{ use crate::{
block::ZCache,
column::ColumnGen, column::ColumnGen,
index::Index, index::Index,
layer::spot::Spot, layer::spot::Spot,
@ -154,7 +155,7 @@ impl World {
name: site.site_tmp.map(|id| index.sites[id].name().to_string()), name: site.site_tmp.map(|id| index.sites[id].name().to_string()),
// TODO: Probably unify these, at some point // TODO: Probably unify these, at some point
kind: match &site.kind { kind: match &site.kind {
civ::SiteKind::Settlement | civ::SiteKind::Refactor | civ::SiteKind::CliffTown => world_msg::SiteKind::Town, /* civ::SiteKind::Settlement | */civ::SiteKind::Refactor | civ::SiteKind::CliffTown => world_msg::SiteKind::Town,
civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon { civ::SiteKind::Dungeon => world_msg::SiteKind::Dungeon {
difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) { difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) {
Some(site::SiteKind::Dungeon(d)) => d.dungeon_difficulty().unwrap_or(0), Some(site::SiteKind::Dungeon(d)) => d.dungeon_difficulty().unwrap_or(0),
@ -162,7 +163,7 @@ impl World {
}, },
}, },
civ::SiteKind::Castle => world_msg::SiteKind::Castle, civ::SiteKind::Castle => world_msg::SiteKind::Castle,
civ::SiteKind::Tree | civ::SiteKind::GiantTree => world_msg::SiteKind::Tree, /* civ::SiteKind::Tree | */civ::SiteKind::GiantTree => world_msg::SiteKind::Tree,
// TODO: Maybe change? // TODO: Maybe change?
civ::SiteKind::Gnarling | civ::SiteKind::Citadel => world_msg::SiteKind::Gnarling, civ::SiteKind::Gnarling | civ::SiteKind::Citadel => world_msg::SiteKind::Gnarling,
}, },
@ -187,21 +188,26 @@ impl World {
}), }),
) )
.collect(), .collect(),
..self.sim.get_map(index, self.sim().calendar.as_ref()) ..self.sim.get_map(index/*, self.sim().calendar.as_ref()*/)
} }
}) })
} }
pub fn sample_columns( pub fn sample_columns<'a>(
&self, &'a self,
) -> impl Sampler< chunk_pos: Vec2<i32>,
Index = (Vec2<i32>, IndexRef, Option<&'_ Calendar>), index: IndexRef<'a>,
Sample = Option<ColumnSample>, /* calendar: Option<&'_ Calendar>, */
> + '_ { ) -> Option<impl for<'b> Sampler<'a, 'b,
ColumnGen::new(&self.sim) Index = /*(Vec2<i32>, IndexRef, Option<&'_ Calendar>)*/Vec2<i32>,
Sample = ColumnSample/*<'a>*/,
> + 'a> {
ColumnGen::new(&self.sim, chunk_pos, index/*, calendar*/)
} }
pub fn sample_blocks(&self) -> BlockGen { BlockGen::new(ColumnGen::new(&self.sim)) } pub fn sample_blocks<'a>(&'a self, chunk_pos: Vec2<i32>, index: IndexRef<'a>/*, calendar: Option<&'_ Calendar>*/) -> Option<BlockGen<'a>> {
ColumnGen::new(&self.sim, chunk_pos, index/*, calendar*/).map(BlockGen::new)
}
pub fn find_accessible_pos( pub fn find_accessible_pos(
&self, &self,
@ -227,18 +233,31 @@ impl World {
chunk_pos: Vec2<i32>, chunk_pos: Vec2<i32>,
// TODO: misleading name // TODO: misleading name
mut should_continue: impl FnMut() -> bool, mut should_continue: impl FnMut() -> bool,
time: Option<(TimeOfDay, Calendar)>, time: Option<(TimeOfDay/*, Calendar*/)>,
) -> Result<(TerrainChunk, ChunkSupplement), ()> { ) -> Result<(TerrainChunk, ChunkSupplement), ()> {
let calendar = time.as_ref().map(|(_, cal)| cal); /* let calendar = time.as_ref().map(|(_, cal)| cal); */
let calendar = self.sim.calendar.as_ref();
let mut sampler = self.sample_blocks(); // FIXME: Deal with this properly if it's not okay to exit early.
let mut sampler = self.sample_blocks(chunk_pos, index/*, calendar*/).ok_or(())?;
// dbg!(&sampler.column_gen.chaos_spline);
let chunk_wpos2d = chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); let chunk_wpos2d = chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32);
let chunk_center_wpos2d = chunk_wpos2d + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2); let chunk_center_wpos2d = chunk_wpos2d + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2);
let grid_border = 4; let grid_border = /*4*/0;
let zcache_grid = Grid::populate_from( let zcache_grid: Grid<ColumnSample> =
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, Grid::populate_by_row::<_, _, {TerrainChunkSize::RECT_SIZE.x}, {TerrainChunkSize::RECT_SIZE.y}>(
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index, calendar), /* TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, */
|offs_y| {
let column_gen = sampler.column_gen.eval_at_row(chunk_wpos2d.y/* - grid_border*/ + offs_y);
move |offs_x| {
/* ZCache {
sample: */column_gen.get(chunk_wpos2d.x/* - grid_border*/ + offs_x)/*,
calendar: column_gen.parent.sim.calendar.as_ref(),
}*/
}
},
/* |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs/*, index, calendar*/)/*None*/ */
); );
let air = Block::air(SpriteKind::Empty); let air = Block::air(SpriteKind::Empty);
@ -246,8 +265,8 @@ impl World {
BlockKind::Rock, BlockKind::Rock,
zcache_grid zcache_grid
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2) .get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
.and_then(|zcache| zcache.as_ref()) /* .and_then(|zcache| zcache.as_ref()) */
.map(|zcache| zcache.sample.stone_col) .map(|zcache| zcache/*.sample*/.stone_col)
.unwrap_or_else(|| index.colors.deep_stone_color.into()), .unwrap_or_else(|| index.colors.deep_stone_color.into()),
); );
let water = Block::new(BlockKind::Water, Rgb::zero()); let water = Block::new(BlockKind::Water, Rgb::zero());
@ -311,6 +330,7 @@ impl World {
); );
let mut chunk = TerrainChunk::new(base_z, stone, air, meta); let mut chunk = TerrainChunk::new(base_z, stone, air, meta);
let calendar = self.sim.calendar.as_ref();
for y in 0..TerrainChunkSize::RECT_SIZE.y as i32 { for y in 0..TerrainChunkSize::RECT_SIZE.y as i32 {
for x in 0..TerrainChunkSize::RECT_SIZE.x as i32 { for x in 0..TerrainChunkSize::RECT_SIZE.x as i32 {
@ -320,33 +340,43 @@ impl World {
let offs = Vec2::new(x, y); let offs = Vec2::new(x, y);
let z_cache = match zcache_grid.get(grid_border + offs) { let z_cache = zcache_grid.get(grid_border + offs)/*sampler.get_z_cache(chunk_wpos2d + offs, index, calendar)*/;
Some(Some(z_cache)) => z_cache, let z_cache = match z_cache/*.as_ref()*/ {
/*Some(*/Some(sample)/*)*/ =>
ZCache { sample/*, calendar*/ },
_ => continue, _ => continue,
}; };
// dbg!(chunk_pos, x, y, z_cache.get_z_limits());
let (min_z, max_z) = z_cache.get_z_limits(); let (min_z, max_z) = z_cache.get_z_limits();
/* let max_z = min_z + 1.0;
let base_z = min_z as i32 - 1; */
(base_z..min_z as i32).for_each(|z| { (base_z..min_z as i32).for_each(|z| {
let _ = chunk.set(Vec3::new(x, y, z), stone); let _ = chunk.set(Vec3::new(x, y, z), stone);
}); });
let mut block_ = None;
(min_z as i32..max_z as i32).for_each(|z| { (min_z as i32..max_z as i32).for_each(|z| {
let lpos = Vec3::new(x, y, z); let lpos = Vec3::new(x, y, z);
let wpos = Vec3::from(chunk_wpos2d) + lpos; let wpos = Vec3::from(chunk_wpos2d) + lpos;
if let Some(block) = sampler.get_with_z_cache(wpos, Some(z_cache)) { if let Some(block) = sampler.get_with_z_cache(wpos, /*Some(&*/z_cache/*)*/) {
let _ = chunk.set(lpos, block); block_ = Some(block);
// let _ = chunk.set(lpos, block);
} }
}); });
if let Some(block_) = block_ {
let _ = chunk.set(Vec3::new(x, y, min_z as i32), block_);
}
} }
} }
let sample_get = |offs| { let sample_get = |offs| {
zcache_grid zcache_grid
.get(grid_border + offs) .get(grid_border + offs)
.and_then(Option::as_ref) // .and_then(Option::as_ref)
.map(|zc| &zc.sample) /* .map(|zc| &zc.sample) */
}; };
// Only use for rng affecting dynamic elements like chests and entities! // Only use for rng affecting dynamic elements like chests and entities!
@ -363,7 +393,7 @@ impl World {
chunks: &self.sim, chunks: &self.sim,
index, index,
chunk: sim_chunk, chunk: sim_chunk,
calendar, /* calendar, */
}, },
chunk: &mut chunk, chunk: &mut chunk,
// arena: &mut arena, // arena: &mut arena,
@ -383,7 +413,7 @@ impl World {
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng); layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
} }
if index.features.trees { if index.features.trees {
layer::apply_trees_to(&mut canvas, &mut dynamic_rng, calendar); layer::apply_trees_to(&mut canvas, &mut dynamic_rng/*, calendar*/);
} }
if index.features.scatter { if index.features.scatter {
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng); layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
@ -445,7 +475,7 @@ impl World {
&mut supplement, &mut supplement,
); );
// Apply layer supplement /* // Apply layer supplement
layer::wildlife::apply_wildlife_supplement( layer::wildlife::apply_wildlife_supplement(
&mut dynamic_rng, &mut dynamic_rng,
chunk_wpos2d, chunk_wpos2d,
@ -454,8 +484,8 @@ impl World {
index, index,
sim_chunk, sim_chunk,
&mut supplement, &mut supplement,
time.as_ref(), time.as_ref().zip(calendar),
); ); */
// Apply site supplementary information // Apply site supplementary information
sim_chunk.sites.iter().for_each(|site| { sim_chunk.sites.iter().for_each(|site| {
@ -487,10 +517,10 @@ impl World {
.sim() .sim()
.get_area_trees_par(min_wpos, max_wpos) .get_area_trees_par(min_wpos, max_wpos)
.filter_map(|attr| { .filter_map(|attr| {
ColumnGen::new(self.sim()) let chunk_pos = TerrainGrid::chunk_key(attr.pos);
.get((attr.pos, index, self.sim().calendar.as_ref())) let col = ColumnGen::new(self.sim(), chunk_pos, index/*, self.sim().calendar.as_ref()*/)?
.filter(|col| layer::tree::tree_valid_at(col, attr.seed)) .get((attr.pos/*, index, self.sim().calendar.as_ref()*/));
.zip(Some(attr)) layer::tree::tree_valid_at(&col, attr.seed).then_some((col, attr))
}) })
.filter_map(|(col, tree)| { .filter_map(|(col, tree)| {
Some(lod::Object { Some(lod::Object {
@ -562,11 +592,12 @@ impl World {
.filter(|(_, site)| matches!(&site.kind, SiteKind::GiantTree(_))) .filter(|(_, site)| matches!(&site.kind, SiteKind::GiantTree(_)))
.filter_map(|(_, site)| { .filter_map(|(_, site)| {
let wpos2d = site.get_origin(); let wpos2d = site.get_origin();
let col = ColumnGen::new(self.sim()).get(( let chunk_pos = TerrainGrid::chunk_key(wpos2d);
wpos2d, let col = ColumnGen::new(self.sim(), chunk_pos, index/*, self.sim().calendar.as_ref()*/)?.get((
wpos2d/* ,
index, index,
self.sim().calendar.as_ref(), self.sim().calendar.as_ref(), */
))?; ));
Some(lod::Object { Some(lod::Object {
kind: lod::ObjectKind::GiantTree, kind: lod::ObjectKind::GiantTree,
pos: { pos: {

View File

@ -55,7 +55,7 @@ use common_net::msg::WorldMapMsg;
use enum_iterator::IntoEnumIterator; use enum_iterator::IntoEnumIterator;
use noise::{ use noise::{
BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti, BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti,
Seedable, SuperSimplex, Worley, Seedable, SuperSimplex, Value, Worley,
}; };
use num::{traits::FloatConst, Float, Signed}; use num::{traits::FloatConst, Float, Signed};
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
@ -125,13 +125,15 @@ pub(crate) struct GenCtx {
pub _big_structure_gen: StructureGen2d, pub _big_structure_gen: StructureGen2d,
pub _region_gen: StructureGen2d, pub _region_gen: StructureGen2d,
pub _fast_turb_x_nz: FastNoise, // pub _fast_turb_x_nz: FastNoise,
pub _fast_turb_y_nz: FastNoise, pub _fast_turb_y_nz: FastNoise,
pub _town_gen: StructureGen2d, pub _town_gen: StructureGen2d,
pub river_seed: RandomField, pub river_seed: RandomField,
pub rock_strength_nz: Fbm, pub rock_strength_nz: Fbm,
pub uplift_nz: Worley, pub uplift_nz: Worley,
pub fast_hill_nz: Value,
} }
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize)]
@ -531,6 +533,7 @@ impl WorldSim {
let rock_lacunarity = 2.0; let rock_lacunarity = 2.0;
let uplift_scale = 128.0; let uplift_scale = 128.0;
let uplift_turb_scale = uplift_scale / 4.0; let uplift_turb_scale = uplift_scale / 4.0;
let hill_nz_seed;
// NOTE: Changing order will significantly change WorldGen, so try not to! // NOTE: Changing order will significantly change WorldGen, so try not to!
let gen_ctx = GenCtx { let gen_ctx = GenCtx {
@ -540,7 +543,7 @@ impl WorldSim {
.set_octaves(7) .set_octaves(7)
.set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale)) .set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale))
.set_seed(rng.gen()), .set_seed(rng.gen()),
hill_nz: SuperSimplex::new().set_seed(rng.gen()), hill_nz: SuperSimplex::new().set_seed({ hill_nz_seed = rng.gen(); hill_nz_seed }),
alt_nz: util::HybridMulti::new() alt_nz: util::HybridMulti::new()
.set_octaves(8) .set_octaves(8)
.set_frequency((10_000.0 / continent_scale) as f64) .set_frequency((10_000.0 / continent_scale) as f64)
@ -574,7 +577,9 @@ impl WorldSim {
.set_frequency(0.2) .set_frequency(0.2)
.set_seed(rng.gen()), .set_seed(rng.gen()),
_fast_turb_x_nz: FastNoise::new(rng.gen()), // _fast_turb_x_nz: FastNoise::new(rng.gen()),
fast_hill_nz: Value::new()
.set_seed({ let _ = rng.gen::<u32>(); hill_nz_seed }),
_fast_turb_y_nz: FastNoise::new(rng.gen()), _fast_turb_y_nz: FastNoise::new(rng.gen()),
_town_gen: StructureGen2d::new(rng.gen(), 2048, 1024), _town_gen: StructureGen2d::new(rng.gen(), 2048, 1024),
@ -1470,7 +1475,7 @@ impl WorldSim {
/// Draw a map of the world based on chunk information. Returns a buffer of /// Draw a map of the world based on chunk information. Returns a buffer of
/// u32s. /// u32s.
pub fn get_map(&self, index: IndexRef, calendar: Option<&Calendar>) -> WorldMapMsg { pub fn get_map(&self, index: IndexRef/*, calendar: Option<&Calendar>*/) -> WorldMapMsg {
let mut map_config = MapConfig::orthographic( let mut map_config = MapConfig::orthographic(
self.map_size_lg(), self.map_size_lg(),
core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height), core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height),
@ -1488,19 +1493,24 @@ impl WorldSim {
}; };
let samples_data = { let samples_data = {
let column_sample = ColumnGen::new(self);
(0..self.map_size_lg().chunks_len()) (0..self.map_size_lg().chunks_len())
.into_par_iter() .into_par_iter()
.map_init( .map_init(
|| Box::new(BlockGen::new(ColumnGen::new(self))), || Box::new(BlockGen::new(
|_block_gen, posi| { ColumnGen::new(self, Vec2::new(1, 1), index)
let sample = column_sample.get( .expect("BlockGen for 1 will never fail unless there are fewer than 4 chunks, \
which we should statically disallow (and if we don't, we should \
start"))),
|block_gen, posi| {
let chunk_pos = uniform_idx_as_vec2(self.map_size_lg(), posi);
block_gen.column_gen = ColumnGen::new(self, chunk_pos, index)?;
let sample = block_gen.column_gen.get(
( (
uniform_idx_as_vec2(self.map_size_lg(), posi) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32), chunk_pos * TerrainChunkSize::RECT_SIZE.map(|e| e as i32)/* ,
index, index,
calendar, calendar, */
) )
)?; );
// sample.water_level = CONFIG.sea_level.max(sample.water_level); // sample.water_level = CONFIG.sea_level.max(sample.water_level);
Some(sample) Some(sample)

View File

@ -213,12 +213,12 @@ fn simulate_return(index: &mut Index, world: &mut WorldSim) -> Result<(), std::i
for site in index.sites.ids() { for site in index.sites.ids() {
let site = &index.sites[site]; let site = &index.sites[site];
match site.kind { match site.kind {
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => { /*SiteKind::Settlement(_) | */SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
towns += site.economy.pop towns += site.economy.pop
}, },
SiteKind::Dungeon(_) => dungeons += site.economy.pop, SiteKind::Dungeon(_) => dungeons += site.economy.pop,
SiteKind::Castle(_) => castles += site.economy.pop, SiteKind::Castle(_) => castles += site.economy.pop,
SiteKind::Tree(_) => (), /* SiteKind::Tree(_) => (), */
SiteKind::GiantTree(_) => (), SiteKind::GiantTree(_) => (),
SiteKind::Gnarling(_) => {}, SiteKind::Gnarling(_) => {},
} }
@ -538,7 +538,7 @@ mod tests {
resources, resources,
neighbors, neighbors,
kind: match i.kind { kind: match i.kind {
crate::site::SiteKind::Settlement(_) /* crate::site::SiteKind::Settlement(_) */
| crate::site::SiteKind::Refactor(_) | crate::site::SiteKind::Refactor(_)
| crate::site::SiteKind::CliffTown(_) => { | crate::site::SiteKind::CliffTown(_) => {
common::terrain::site::SitesKind::Settlement common::terrain::site::SitesKind::Settlement
@ -584,9 +584,9 @@ mod tests {
)) ))
}, },
// common::terrain::site::SitesKind::Settlement | // common::terrain::site::SitesKind::Settlement |
_ => crate::site::Site::settlement(crate::site::Settlement::generate( _ => crate::site::Site::settlement(crate::/*site::Settlement::generate(
wpos, None, &mut rng, wpos, None, &mut rng,
)), )*/site2::Site::generate_city(&crate::Land::empty(), &mut env.rng, wpos)),
}; };
for g in i.resources.iter() { for g in i.resources.iter() {
//let c = sim::SimChunk::new(); //let c = sim::SimChunk::new();
@ -648,12 +648,12 @@ mod tests {
resources: &[(Good, f32)], resources: &[(Good, f32)],
) -> Id<crate::site::Site> { ) -> Id<crate::site::Site> {
let wpos = Vec2 { x: 42, y: 42 }; let wpos = Vec2 { x: 42, y: 42 };
let mut settlement = crate::site::Site::settlement(crate::site::Settlement::generate( let mut settlement = crate::site::Site::/*settlement*/refactor(crate::/*site::Settlement::generate(
wpos, wpos,
None, None,
&mut env.rng, &mut env.rng,
//Some(name), //Some(name),
)); )*/site2::Site::generate_city(&crate::Land::empty(), &mut env.rng, wpos));
for (good, amount) in resources.iter() { for (good, amount) in resources.iter() {
settlement.economy.natural_resources.chunks_per_resource settlement.economy.natural_resources.chunks_per_resource
[(*good).try_into().unwrap_or_default()] = *amount; [(*good).try_into().unwrap_or_default()] = *amount;

View File

@ -189,7 +189,7 @@ impl Castle {
&'a self, &'a self,
index: IndexRef, index: IndexRef,
wpos2d: Vec2<i32>, wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol), vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
) { ) {
for y in 0..vol.size_xy().y as i32 { for y in 0..vol.size_xy().y as i32 {
@ -284,11 +284,11 @@ impl Castle {
.map(|(dist, _, path, _)| path.head_space(dist)) .map(|(dist, _, path, _)| path.head_space(dist))
.unwrap_or(0); .unwrap_or(0);
let wall_sample = if let Some(col) = get_column(offs + wall_pos - rpos) { let wall_sample = /* if let Some(col) = get_column(offs + wall_pos - rpos) {
col col
} else { } else {
col_sample col_sample
}; }*/col_sample;
// Make sure particularly weird terrain doesn't give us underground walls // Make sure particularly weird terrain doesn't give us underground walls
let wall_alt = wall_alt + (wall_sample.alt as i32 - wall_alt - 10).max(0); let wall_alt = wall_alt + (wall_sample.alt as i32 - wall_alt - 10).max(0);
@ -433,7 +433,7 @@ impl Castle {
// NOTE: Used only for dynamic elements like chests and entities! // NOTE: Used only for dynamic elements like chests and entities!
_dynamic_rng: &mut impl Rng, _dynamic_rng: &mut impl Rng,
_wpos2d: Vec2<i32>, _wpos2d: Vec2<i32>,
_get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, _get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
_supplement: &mut ChunkSupplement, _supplement: &mut ChunkSupplement,
) { ) {
// TODO // TODO

View File

@ -3,11 +3,11 @@ mod castle;
pub mod economy; pub mod economy;
pub mod namegen; pub mod namegen;
pub mod settlement; pub mod settlement;
mod tree; // mod tree;
// Reexports // Reexports
pub use self::{ pub use self::{
block_mask::BlockMask, castle::Castle, economy::Economy, settlement::Settlement, tree::Tree, block_mask::BlockMask, castle::Castle, economy::Economy/* , settlement::Settlement, tree::Tree, */
}; };
use crate::{column::ColumnSample, site2, Canvas}; use crate::{column::ColumnSample, site2, Canvas};
@ -60,23 +60,23 @@ pub struct Site {
} }
pub enum SiteKind { pub enum SiteKind {
Settlement(Settlement), /* Settlement(Settlement), */
Dungeon(site2::Site), Dungeon(site2::Site),
Castle(Castle), Castle(Castle),
Refactor(site2::Site), Refactor(site2::Site),
CliffTown(site2::Site), CliffTown(site2::Site),
Tree(tree::Tree), /* Tree(tree::Tree), */
GiantTree(site2::Site), GiantTree(site2::Site),
Gnarling(site2::Site), Gnarling(site2::Site),
} }
impl Site { impl Site {
pub fn settlement(s: Settlement) -> Self { /* pub fn settlement(s: Settlement) -> Self {
Self { Self {
kind: SiteKind::Settlement(s), kind: SiteKind::Settlement(s),
economy: Economy::default(), economy: Economy::default(),
} }
} } */
pub fn dungeon(d: site2::Site) -> Self { pub fn dungeon(d: site2::Site) -> Self {
Self { Self {
@ -113,12 +113,12 @@ impl Site {
} }
} }
pub fn tree(t: tree::Tree) -> Self { /* pub fn tree(t: tree::Tree) -> Self {
Self { Self {
kind: SiteKind::Tree(t), kind: SiteKind::Tree(t),
economy: Economy::default(), economy: Economy::default(),
} }
} } */
pub fn giant_tree(gt: site2::Site) -> Self { pub fn giant_tree(gt: site2::Site) -> Self {
Self { Self {
@ -129,12 +129,12 @@ impl Site {
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(), SiteKind::Refactor(s) => s.radius(),
SiteKind::CliffTown(ct) => ct.radius(), SiteKind::CliffTown(ct) => ct.radius(),
SiteKind::Tree(t) => t.radius(), /* SiteKind::Tree(t) => t.radius(), */
SiteKind::GiantTree(gt) => gt.radius(), SiteKind::GiantTree(gt) => gt.radius(),
SiteKind::Gnarling(g) => g.radius(), SiteKind::Gnarling(g) => g.radius(),
} }
@ -142,12 +142,12 @@ impl Site {
pub fn get_origin(&self) -> Vec2<i32> { pub fn get_origin(&self) -> Vec2<i32> {
match &self.kind { match &self.kind {
SiteKind::Settlement(s) => s.get_origin(), /* SiteKind::Settlement(s) => s.get_origin(), */
SiteKind::Dungeon(d) => d.origin, SiteKind::Dungeon(d) => d.origin,
SiteKind::Castle(c) => c.get_origin(), SiteKind::Castle(c) => c.get_origin(),
SiteKind::Refactor(s) => s.origin, SiteKind::Refactor(s) => s.origin,
SiteKind::CliffTown(ct) => ct.origin, SiteKind::CliffTown(ct) => ct.origin,
SiteKind::Tree(t) => t.origin, /* SiteKind::Tree(t) => t.origin, */
SiteKind::GiantTree(gt) => gt.origin, SiteKind::GiantTree(gt) => gt.origin,
SiteKind::Gnarling(g) => g.origin, SiteKind::Gnarling(g) => g.origin,
} }
@ -155,12 +155,12 @@ impl Site {
pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules { pub fn spawn_rules(&self, wpos: Vec2<i32>) -> SpawnRules {
match &self.kind { match &self.kind {
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), SiteKind::Refactor(s) => s.spawn_rules(wpos),
SiteKind::CliffTown(ct) => ct.spawn_rules(wpos), SiteKind::CliffTown(ct) => ct.spawn_rules(wpos),
SiteKind::Tree(t) => t.spawn_rules(wpos), /* SiteKind::Tree(t) => t.spawn_rules(wpos), */
SiteKind::GiantTree(gt) => gt.spawn_rules(wpos), SiteKind::GiantTree(gt) => gt.spawn_rules(wpos),
SiteKind::Gnarling(g) => g.spawn_rules(wpos), SiteKind::Gnarling(g) => g.spawn_rules(wpos),
} }
@ -168,12 +168,12 @@ impl Site {
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
match &self.kind { match &self.kind {
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) => s.name(), SiteKind::Refactor(s) => s.name(),
SiteKind::CliffTown(ct) => ct.name(), SiteKind::CliffTown(ct) => ct.name(),
SiteKind::Tree(_) => "Giant Tree", /* SiteKind::Tree(_) => "Giant Tree", */
SiteKind::GiantTree(gt) => gt.name(), SiteKind::GiantTree(gt) => gt.name(),
SiteKind::Gnarling(g) => g.name(), SiteKind::Gnarling(g) => g.name(),
} }
@ -184,7 +184,7 @@ impl Site {
site_id: common::trade::SiteId, site_id: common::trade::SiteId,
) -> Option<common::trade::SiteInformation> { ) -> Option<common::trade::SiteInformation> {
match &self.kind { match &self.kind {
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => { /* SiteKind::Settlement(_) | */SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
Some(common::trade::SiteInformation { Some(common::trade::SiteInformation {
id: site_id, id: site_id,
unconsumed_stock: self unconsumed_stock: self
@ -203,12 +203,12 @@ impl Site {
let info = canvas.info(); let info = canvas.info();
let get_col = |wpos| info.col(wpos + info.wpos); let get_col = |wpos| info.col(wpos + info.wpos);
match &self.kind { match &self.kind {
SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), /* SiteKind::Settlement(s) => s.apply_to(canvas.index, canvas.wpos, get_col, canvas.chunk), */
SiteKind::Dungeon(d) => d.render(canvas, arena, dynamic_rng), SiteKind::Dungeon(d) => d.render(canvas, arena, dynamic_rng),
SiteKind::Castle(c) => c.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, arena, dynamic_rng), SiteKind::Refactor(s) => s.render(canvas, arena, dynamic_rng),
SiteKind::CliffTown(ct) => ct.render(canvas, arena, dynamic_rng), SiteKind::CliffTown(ct) => ct.render(canvas, arena, dynamic_rng),
SiteKind::Tree(t) => t.render(canvas, dynamic_rng), /* SiteKind::Tree(t) => t.render(canvas, dynamic_rng), */
SiteKind::GiantTree(gt) => gt.render(canvas, arena, dynamic_rng), SiteKind::GiantTree(gt) => gt.render(canvas, arena, dynamic_rng),
SiteKind::Gnarling(g) => g.render(canvas, arena, dynamic_rng), SiteKind::Gnarling(g) => g.render(canvas, arena, dynamic_rng),
} }
@ -219,22 +219,22 @@ impl Site {
// NOTE: Used only for dynamic elements like chests and entities! // NOTE: Used only for dynamic elements like chests and entities!
dynamic_rng: &mut impl Rng, dynamic_rng: &mut impl Rng,
wpos2d: Vec2<i32>, wpos2d: Vec2<i32>,
get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
supplement: &mut ChunkSupplement, supplement: &mut ChunkSupplement,
site_id: common::trade::SiteId, site_id: common::trade::SiteId,
) { ) {
match &self.kind { match &self.kind {
SiteKind::Settlement(s) => { /* SiteKind::Settlement(s) => {
let economy = self let economy = self
.trade_information(site_id) .trade_information(site_id)
.expect("Settlement has no economy"); .expect("Settlement has no economy");
s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy) s.apply_supplement(dynamic_rng, wpos2d, get_column, supplement, economy)
}, }, */
SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::Dungeon(d) => d.apply_supplement(dynamic_rng, wpos2d, 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(_) => {}, SiteKind::Refactor(_) => {},
SiteKind::CliffTown(_) => {}, SiteKind::CliffTown(_) => {},
SiteKind::Tree(_) => {}, /* SiteKind::Tree(_) => {}, */
SiteKind::GiantTree(gt) => gt.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::GiantTree(gt) => gt.apply_supplement(dynamic_rng, wpos2d, supplement),
SiteKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement), SiteKind::Gnarling(g) => g.apply_supplement(dynamic_rng, wpos2d, supplement),
} }
@ -243,7 +243,7 @@ impl Site {
pub fn do_economic_simulation(&self) -> bool { pub fn do_economic_simulation(&self) -> bool {
matches!( matches!(
self.kind, self.kind,
SiteKind::Refactor(_) | SiteKind::CliffTown(_) | SiteKind::Settlement(_) SiteKind::Refactor(_) | SiteKind::CliffTown(_)/* | SiteKind::Settlement(_) */
) )
} }
} }

View File

@ -557,7 +557,7 @@ impl Settlement {
&'a self, &'a self,
index: IndexRef, index: IndexRef,
wpos2d: Vec2<i32>, wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol), vol: &mut (impl BaseVol<Vox = Block> + RectSizedVol + ReadVol + WriteVol),
) { ) {
let colors = &index.colors.site.settlement; let colors = &index.colors.site.settlement;
@ -812,15 +812,20 @@ impl Settlement {
for structure in &self.structures { for structure in &self.structures {
let bounds = structure.bounds_2d(); let bounds = structure.bounds_2d();
// Skip this structure if it's not near this chunk let this_aabr = Aabr {
if !bounds.collides_with_aabr(Aabr {
min: wpos2d - self.origin, min: wpos2d - self.origin,
max: wpos2d - self.origin + vol.size_xy().map(|e| e as i32 + 1), max: wpos2d - self.origin + vol.size_xy().map(|e| e as i32 + 1),
}) { };
// Skip this structure if it's not near this chunk
if !bounds.collides_with_aabr(this_aabr) {
continue; continue;
} }
let bounds = structure.bounds(); let mut bounds = structure.bounds();
bounds.intersect(Aabb {
min: this_aabr.min.with_z(bounds.min.z),
max: this_aabr.max.with_z(bounds.max.z),
});
for x in bounds.min.x..bounds.max.x + 1 { for x in bounds.min.x..bounds.max.x + 1 {
for y in bounds.min.y..bounds.max.y + 1 { for y in bounds.min.y..bounds.max.y + 1 {
@ -850,7 +855,7 @@ impl Settlement {
// NOTE: Used only for dynamic elements like chests and entities! // NOTE: Used only for dynamic elements like chests and entities!
dynamic_rng: &mut impl Rng, dynamic_rng: &mut impl Rng,
wpos2d: Vec2<i32>, wpos2d: Vec2<i32>,
mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample<'a>>, mut get_column: impl FnMut(Vec2<i32>) -> Option<&'a ColumnSample/*<'a>*/>,
supplement: &mut ChunkSupplement, supplement: &mut ChunkSupplement,
economy: SiteInformation, economy: SiteInformation,
) { ) {

View File

@ -692,7 +692,7 @@ impl<'a, 'b, F: Filler> FillFn<'a, 'b, F> {
// we probably need an evaluator for the primitive tree that gets which point is queried at // we probably need an evaluator for the primitive tree that gets which point is queried at
// leaf nodes given an input point to make Translate/Rotate work generally // leaf nodes given an input point to make Translate/Rotate work generally
pub fn prefab(&self, p: &'static PrefabStructure, tr: Vec3<i32>, seed: u32) -> impl Fill + Copy + 'b { pub fn prefab(&self, p: &'static PrefabStructure, tr: Vec3<i32>, seed: u32) -> impl Fill + Copy + 'b {
let col_sample = /*if let Some(col_sample) = */self.canvas_info.col(self.canvas_info.wpos)/* { let col_sample = None;/*/*if let Some(col_sample) = */self.canvas_info.col(self.canvas_info.wpos)/* {
col_sample col_sample
} else { } else {
// Don't draw--technically we should probably not assume this much about // Don't draw--technically we should probably not assume this much about
@ -700,7 +700,7 @@ impl<'a, 'b, F: Filler> FillFn<'a, 'b, F> {
// //
// FIXME: Fix this for alternate fillers if it turns out to matter. // FIXME: Fix this for alternate fillers if it turns out to matter.
return return
}*/; }*/*/;
let index = self.canvas_info.index; let index = self.canvas_info.index;
let p_bounds = p.get_bounds().center().xy(); let p_bounds = p.get_bounds().center().xy();
let calendar = self.canvas_info.calendar(); let calendar = self.canvas_info.calendar();
@ -1681,7 +1681,7 @@ impl Painter<'_> {
Self::get_bounds_inner(cache, tree, prim) Self::get_bounds_inner(cache, tree, prim)
} */ } */
fn get_bounds_disjoint_inner<'a, 'b>( fn get_bounds_disjoint_inner<'a: 'b, 'b>(
arena: &'a bumpalo::Bump, arena: &'a bumpalo::Bump,
cache: &mut BoundsMap, cache: &mut BoundsMap,
tree: &'b Store<Primitive<'b>>, tree: &'b Store<Primitive<'b>>,

View File

@ -87,7 +87,7 @@ impl Site {
} }
}) })
.min_by_key(|d2| *d2 as i32) .min_by_key(|d2| *d2 as i32)
.map(|d2| d2.sqrt() as f32 / TILE_SIZE as f32) .map(|d2| d2/*.sqrt()*/ as f32 / (TILE_SIZE * TILE_SIZE) as f32)
.unwrap_or(1.0); .unwrap_or(1.0);
let base_spawn_rules = SpawnRules { let base_spawn_rules = SpawnRules {
trees: max_warp == 1.0, trees: max_warp == 1.0,
@ -862,10 +862,10 @@ impl Site {
.min_by_key(|d| (*d * 100.0) as i32); .min_by_key(|d| (*d * 100.0) as i32);
if dist.map_or(false, |d| d <= 1.5) { if dist.map_or(false, |d| d <= 1.5) {
let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32); let col = /*canvas.col(wpos2d)*/None::<&crate::ColumnSample>;
let sub_surface_color = canvas let alt = col.map_or(0, |col| col.alt as i32);
.col(wpos2d) let sub_surface_color =
.map_or(Rgb::zero(), |col| col.sub_surface_color * 0.5); col.map_or(Rgb::zero(), |col| col.sub_surface_color * 0.5);
let mut underground = true; let mut underground = true;
for z in -8..6 { for z in -8..6 {
canvas.map(Vec3::new(wpos2d.x, wpos2d.y, alt + z), |b| { canvas.map(Vec3::new(wpos2d.x, wpos2d.y, alt + z), |b| {
@ -938,11 +938,11 @@ impl Site {
// .min_by_key(|d| (*d * 100.0) as i32); // .min_by_key(|d| (*d * 100.0) as i32);
if min_dist.is_some() { if min_dist.is_some() {
let alt = /*avg_hard_alt.map(|(sum, weight)| sum / weight).unwrap_or_else(||*/ canvas.col(wpos2d).map_or(0.0, |col| col.alt)/*)*/ as i32; let col = /*canvas.col(wpos2d)*/col;
let alt = /*avg_hard_alt.map(|(sum, weight)| sum / weight).unwrap_or_else(||*/ /*col.map_or(0.0, |col| */col.alt/*))*/ as i32;
let mut underground = true; let mut underground = true;
let sub_surface_color = canvas let sub_surface_color =
.col(wpos2d) /*col.map_or(Rgb::zero(), |col| */col.sub_surface_color * 0.5/*)*/;
.map_or(Rgb::zero(), |col| col.sub_surface_color * 0.5);
for z in -6..4 { for z in -6..4 {
canvas.map( canvas.map(
Vec3::new(wpos2d.x, wpos2d.y, alt + z), Vec3::new(wpos2d.x, wpos2d.y, alt + z),

View File

@ -80,6 +80,26 @@ impl GiantTree {
impl<F: Filler> Structure<F> for GiantTree { impl<F: Filler> Structure<F> for GiantTree {
fn render<'a>(&self, _site: &Site, _land: Land, painter: &Painter<'a>, filler: &mut FillFn<'a, '_, F>) { fn render<'a>(&self, _site: &Site, _land: Land, painter: &Painter<'a>, filler: &mut FillFn<'a, '_, F>) {
let bounds = self.tree.get_bounds().map(|e| e as i32);
/* let trunk_block = self.tree.config.trunk_block; */
/* let leaf_vertical_scale = self.tree.config.leaf_vertical_scale.recip();
let branch_child_radius_lerp = self.tree.config.branch_child_radius_lerp; */
let leaf_vertical_scale = /*t.config.leaf_vertical_scale*/0.6f32.recip()/*1.0*/;
let branch_child_radius_lerp = true;
/* let trunk_block = filler.block_from_structure(
trunk_block,
self.wpos.xy(),
self.seed,
&col,
); */
let trunk_block = Block::new(BlockKind::Wood, Rgb::new(80, 32, 0));
/* let leaf_block = filler.block_from_structure(
leaf_block,
self.wpos.xy(),
self.seed,
&col,
); */
let fast_noise = FastNoise::new(self.seed); let fast_noise = FastNoise::new(self.seed);
let dark = Rgb::new(10, 70, 50).map(|e| e as f32); let dark = Rgb::new(10, 70, 50).map(|e| e as f32);
let light = Rgb::new(80, 140, 10).map(|e| e as f32); let light = Rgb::new(80, 140, 10).map(|e| e as f32);
@ -88,6 +108,79 @@ impl<F: Filler> Structure<F> for GiantTree {
light, light,
fast_noise.get((self.wpos.map(|e| e as f64) * 0.05) * 0.5 + 0.5), fast_noise.get((self.wpos.map(|e| e as f64) * 0.05) * 0.5 + 0.5),
); );
let leaf_block = Block::new(BlockKind::Leaves, leaf_col.map(|e| e as u8));
let trunk_block = filler.block(trunk_block);
let leaf_block = filler.block(leaf_block);
self.tree.walk(|branch, parent| {
let aabr = Aabr {
min: self.wpos.xy() + branch.get_aabb().min.xy().as_(),
max: self.wpos.xy() + branch.get_aabb().max.xy().as_(),
};
if aabr.collides_with_aabr(filler.render_aabr().as_()) {
let start =
self.wpos.as_::<f32>() + branch.get_line().start/*.as_()*//* - 0.5*/;
let end =
self.wpos.as_::<f32>() + branch.get_line().end/*.as_()*//* - 0.5*/;
let wood_radius = branch.get_wood_radius();
let leaf_radius = branch.get_leaf_radius();
let parent_wood_radius = if branch_child_radius_lerp {
parent.get_wood_radius()
} else {
wood_radius
};
let leaf_eats_wood = leaf_radius > wood_radius;
let leaf_eats_parent_wood = leaf_radius > parent_wood_radius;
if !leaf_eats_wood || !leaf_eats_parent_wood {
// Render the trunk, since it's not swallowed by its leaf.
painter
.line_two_radius(
start,
end,
parent_wood_radius,
wood_radius,
1.0,
)
.fill(/*filler.block(trunk_block)*/trunk_block, filler);
}
if leaf_eats_wood || leaf_eats_parent_wood {
// Render the leaf, since it's not *completely* swallowed
// by the trunk.
painter
.line_two_radius(
start,
end,
leaf_radius,
leaf_radius,
leaf_vertical_scale,
)
.fill(/*filler.block(leaf_block)*/leaf_block, filler);
}
true
} else {
false
}
});
/* // Draw the roots.
t.roots.iter().for_each(|root| {
painter
.line(
wpos/*.as_::<f32>()*/ + root.line.start.as_()/* - 0.5*/,
wpos/*.as_::<f32>()*/ + root.line.end.as_()/* - 0.5*/,
root.radius,
)
.fill(/*filler.block(leaf_block)*/trunk_block, filler);
}); */
/* let fast_noise = FastNoise::new(self.seed);
let dark = Rgb::new(10, 70, 50).map(|e| e as f32);
let light = Rgb::new(80, 140, 10).map(|e| e as f32);
let leaf_col = Lerp::lerp(
dark,
light,
fast_noise.get((self.wpos.map(|e| e as f64) * 0.05) * 0.5 + 0.5),
);
let leaf_vertical_scale = /*t.config.leaf_vertical_scale*/0.6f32.recip()/*1.0*/; let leaf_vertical_scale = /*t.config.leaf_vertical_scale*/0.6f32.recip()/*1.0*/;
self.tree.walk(|branch, parent| { self.tree.walk(|branch, parent| {
let aabr = Aabr { let aabr = Aabr {
@ -125,6 +218,6 @@ impl<F: Filler> Structure<F> for GiantTree {
} else { } else {
false false
} }
}); }); */
} }
} }

View File

@ -19,7 +19,7 @@ impl FastNoise {
} }
} }
impl Sampler<'static> for FastNoise { impl Sampler<'static, '_> for FastNoise {
type Index = Vec3<f64>; type Index = Vec3<f64>;
type Sample = f32; type Sample = f32;
@ -93,7 +93,7 @@ impl FastNoise2d {
} }
} }
impl Sampler<'static> for FastNoise2d { impl Sampler<'static, '_> for FastNoise2d {
type Index = Vec2<f64>; type Index = Vec2<f64>;
type Sample = f32; type Sample = f32;

View File

@ -17,7 +17,7 @@ impl RandomField {
} }
} }
impl Sampler<'static> for RandomField { impl Sampler<'static, '_> for RandomField {
type Index = Vec3<i32>; type Index = Vec3<i32>;
type Sample = u32; type Sample = u32;
@ -54,7 +54,7 @@ impl RandomPerm {
} }
} }
impl Sampler<'static> for RandomPerm { impl Sampler<'static, '_> for RandomPerm {
type Index = u32; type Index = u32;
type Sample = u32; type Sample = u32;

View File

@ -1,8 +1,8 @@
pub trait Sampler<'a>: Sized { pub trait Sampler<'a, 'b>: Sized {
type Index: 'a; type Index: 'a;
type Sample: 'a; type Sample: 'a;
fn get(&self, index: Self::Index) -> Self::Sample; fn get(&'b self, index: Self::Index) -> Self::Sample;
} }
pub trait SamplerMut<'a>: Sized { pub trait SamplerMut<'a>: Sized {

View File

@ -168,7 +168,7 @@ impl StructureGen2d {
} }
} }
impl Sampler<'static> for StructureGen2d { impl Sampler<'static, '_> for StructureGen2d {
type Index = Vec2<i32>; type Index = Vec2<i32>;
type Sample = [StructureField; 9]; type Sample = [StructureField; 9];

View File

@ -24,7 +24,7 @@ impl UnitChooser {
} }
} }
impl Sampler<'static> for UnitChooser { impl Sampler<'static, '_> for UnitChooser {
type Index = u32; type Index = u32;
type Sample = (Vec2<i32>, Vec2<i32>); type Sample = (Vec2<i32>, Vec2<i32>);