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"
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]]
name = "proc-macro-crate"
version = "0.1.5"
@ -4731,6 +4741,12 @@ dependencies = [
"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]]
name = "range-alloc"
version = "0.1.2"
@ -5678,6 +5694,15 @@ dependencies = [
"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]]
name = "specs"
version = "0.16.2"
@ -5941,6 +5966,18 @@ dependencies = [
"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]]
name = "tap"
version = "1.0.1"
@ -6516,6 +6553,7 @@ version = "0.10.0"
dependencies = [
"approx 0.4.0",
"bitflags",
"bitvec",
"chrono",
"chrono-tz",
"clap 2.34.0",
@ -6555,6 +6593,7 @@ dependencies = [
"vek 0.15.8",
"veloren-common-assets",
"veloren-common-base",
"zerocopy",
]
[[package]]
@ -6981,6 +7020,7 @@ dependencies = [
"num-traits",
"ordered-float 2.10.0",
"packed_simd_2",
"probability",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rayon",
@ -7989,3 +8029,24 @@ checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c"
dependencies = [
"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
caves: true,
rocks: true,
shrubs: true,
rocks: false,
shrubs: false,
trees: true,
scatter: true,
scatter: false,
paths: true,
spots: true,
site2_towns: true,
site2_giant_trees: true,
wildlife_density: 1.0,
peak_naming: true,
biome_naming: true,

View File

@ -1778,7 +1778,7 @@ impl Client {
if self.state.terrain().get_key(*key).is_none() {
if !skip_mode && !self.pending_chunks.contains_key(key) {
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
&& current_tick_send_chunk_requests
< CURRENT_TICK_PENDING_CHUNKS_LIMIT

View File

@ -38,6 +38,8 @@ strum = { version = "0.24", features = ["derive"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
approx = "0.4.0"
bitvec = "0.22"
# bumpalo = { version = "3.9.1", features = ["allocator_api"] }
clap = "2.33"
crossbeam-utils = "0.8.1"
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"] }
rand = "0.8"
fxhash = "0.2.1"
zerocopy = "0.6.1"
# 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
where
T: Clone,

View File

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

View File

@ -1,3 +1,4 @@
use zerocopy::AsBytes;
use super::SpriteKind;
use crate::{
comp::{fluid_dynamics::LiquidKind, tool::ToolKind},
@ -14,6 +15,7 @@ use vek::*;
make_case_elim!(
block_kind,
#[derive(
AsBytes,
Copy,
Clone,
Debug,
@ -28,6 +30,7 @@ make_case_elim!(
Display,
)]
#[repr(u8)]
/// NOTE: repr(u8) preserves the niche optimization for fieldless enums!
pub enum BlockKind {
Air = 0x00, // Air counts as a fluid
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 {
kind: BlockKind,
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 {
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.
pub fn defragment(&mut self)
where
V: Clone + Eq + Hash,
V: zerocopy::AsBytes + Clone + Eq + Hash,
[(); { core::mem::size_of::<V>() }]:,
{
// First, defragment all subchunks.
self.sub_chunks.iter_mut().for_each(SubChunk::defragment);

View File

@ -1,9 +1,11 @@
use crate::vol::{
BaseVol, IntoPosIterator, IntoVolIterator, RasterableVol, ReadVol, VolSize, WriteVol,
};
use bitvec::prelude::*;
use core::{hash::Hash, iter::Iterator, marker::PhantomData, mem};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use zerocopy::AsBytes;
use vek::*;
#[derive(Debug)]
@ -119,13 +121,21 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
/// Compress this subchunk by frequency.
pub fn defragment(&mut self)
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
// 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 default = &self.default;
let empty_bits = /*BitArray::new([0u32; /*Self::GROUP_COUNT_TOTAL as usize >> 5*/8])*/bitarr![0; 256];
self.indices
.iter()
.enumerate()
@ -134,23 +144,29 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
let end = start + Self::GROUP_VOLUME as usize;
if let Some(group) = vox.get(start..end) {
// Check to see if all blocks in this group are the same.
let mut group = group.iter();
let first = group.next().expect("GROUP_VOLUME ≥ 1");
if group.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(vec![]).push(grp_idx);
// NOTE: First element must exist because GROUP_VOLUME ≥ 1
let first = &group[0];
let first_ = first.as_bytes();
// View group as bytes to benefit from specialization on [u8].
let group = group.as_bytes();
/* 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 {
// This slot is empty (i.e. has the default value).
map.entry(default).or_insert(vec![]).push(grp_idx);
// this slot is empty (i.e. has the default value).
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
// default.
let (new_default, default_groups) = if let Some((new_default, default_groups)) = map
.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)
} else {
@ -163,11 +179,12 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
let mut new_vox =
Vec::with_capacity(Self::GROUP_COUNT_TOTAL as usize - default_groups.len());
let num_groups = self.num_groups();
self.indices
let mut indices = &mut self.indices[..Self::GROUP_COUNT_TOTAL as usize];
indices
.iter_mut()
.enumerate()
.for_each(|(grp_idx, base)| {
if default_groups.contains(&grp_idx) {
if default_groups/*.contains(&grp_idx)*/[grp_idx] {
// Default groups become 255
*base = 255;
} else {

View File

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

View File

@ -2823,8 +2823,7 @@ fn handle_debug_column(
_action: &ChatCommand,
) -> CmdResult<()> {
let sim = server.world.sim();
let calendar = (*server.state.ecs().read_resource::<Calendar>()).clone();
let sampler = server.world.sample_columns();
/* let calendar = (*server.state.ecs().read_resource::<Calendar>()).clone(); */
let wpos = if let (Some(x), Some(y)) = parse_args!(args, i32, i32) {
Vec2::new(x, y)
} else {
@ -2832,7 +2831,9 @@ fn handle_debug_column(
// FIXME: Deal with overflow, if needed.
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 basement = sim.get_interpolated(wpos, |chunk| chunk.basement)?;
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 tree_density = sim.get_interpolated(wpos, |chunk| chunk.tree_density)?;
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 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 downhill = chunk.downhill;
let river = &chunk.river;
@ -2883,7 +2883,7 @@ 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));
Ok(())
} else {

View File

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

View File

@ -9,7 +9,7 @@ use crate::{
wiring, BattleModeBuffer, SpawnPoint,
};
use common::{
calendar::Calendar,
/* calendar::Calendar, */
character::CharacterId,
combat,
combat::DamageContributor,
@ -465,7 +465,7 @@ impl StateExt for State {
.for_each(|chunk_key| {
#[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);
}
});

View File

@ -15,7 +15,7 @@ use crate::{
ChunkRequest, SpawnPoint, Tick,
};
use common::{
calendar::Calendar,
/* calendar::Calendar, */
comp::{
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, Settings>,
Read<'a, TimeOfDay>,
Read<'a, Calendar>,
/* Read<'a, Calendar>, */
ReadExpect<'a, SlowJobPool>,
ReadExpect<'a, IndexOwned>,
ReadExpect<'a, Arc<World>>,
@ -92,7 +92,7 @@ impl<'a> System<'a> for Sys {
spawn_point,
server_settings,
time_of_day,
calendar,
/* calendar, */
slow_jobs,
index,
world,
@ -127,7 +127,7 @@ impl<'a> System<'a> for Sys {
&slow_jobs,
Arc::clone(&world),
index.clone(),
(*time_of_day, calendar.clone()),
(*time_of_day/*, calendar.clone()*/),
)
});

View File

@ -112,8 +112,8 @@ impl<'a> GreedyMesh<'a> {
max_size
);
// 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 small_size_threshold = 33.min(large_size_threshold / 2 + 1);
let large_size_threshold = /*256*/8.min(min_max_dim / 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 atlas =
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>,
lit_blocks: impl Iterator<Item = (Vec3<i32>, u8)>,
) -> impl Fn(Vec3<i32>) -> f32 + 'static + Send + Sync {
span!(_guard, "calc_light");
/* span!(_guard, "calc_light");
const UNKNOWN: u8 = 255;
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| {
let pos = wpos - min_bounds.min;
1.0
/* let pos = wpos - min_bounds.min;
let l = light_map2
.get(lm_idx2(pos.x, pos.y, pos.z))
.copied()
@ -220,7 +221,7 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
l as f32 / SUNLIGHT as f32
} else {
0.0
}
} */
}
}
@ -255,7 +256,7 @@ pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + '
let mut glow_blocks = Vec::new();
// TODO: This expensive, use BlocksOfInterest instead
/* // TODO: This expensive, use BlocksOfInterest instead
let mut volume = vol.cached();
for x in -MAX_LIGHT_DIST..range.size().w + 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)));
}
}
}
} */
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
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");
let mesh_focus_pos = focus_pos.map(|e| e.trunc()).xy().as_::<i64>();
for (todo, chunk) in self
let mut todo = self
.mesh_todo
.values_mut()
.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
.and_then(|todo| {
./*and_then*/filter_map(|todo| {
let pos = todo.pos;
Some((todo, scene_data.state
.terrain()

View File

@ -27,10 +27,12 @@ fxhash = "0.2.1"
image = { version = "0.23.12", default-features = false, features = ["png"] }
itertools = "0.10"
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 }
noisy_float = { version = "0.2", default-features = false }
num = "0.4"
ordered-float = "2.0.1"
probability = "0.18.0"
hashbrown = { version = "0.11", features = ["rayon", "serde", "nightly"] }
lazy_static = "1.4.0"
tracing = { version = "0.1", default-features = false }

View File

@ -225,7 +225,7 @@ impl Filler for NullCanvas {
fn dungeon(c: &mut Criterion) {
let pool = ThreadPoolBuilder::new().build().unwrap();
let (world, index) = World::generate(
230,
/*230*/59686,
WorldOpts {
seed_elements: true,
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);
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 = (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(|| {
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> {
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>,
cache: &'b mut SmallCache<Option<ColumnSample<'a>>>,
cache: &'b mut SmallCache<ColumnSample<'a>>,
wpos: Vec2<i32>,
index: IndexRef<'a>,
calendar: Option<&'a Calendar>,
) -> Option<&'b ColumnSample<'a>> {
/* index: IndexRef<'a>,
calendar: Option<&'a Calendar>, */
) -> &'b ColumnSample<'a> {
cache
.get(wpos, |wpos| column_gen.get((wpos, index, calendar)))
.as_ref()
}
.get(wpos, |wpos| column_gen.get(/*(wpos, index, calendar)*/wpos))
} */
pub fn get_z_cache(
/* pub fn get_z_cache(
&mut self,
wpos: Vec2<i32>,
index: IndexRef<'a>,
calendar: Option<&'a Calendar>,
) -> Option<ZCache<'a>> {
/* index: IndexRef<'a>,
calendar: Option<&'a Calendar>, */
) -> ZCache<'a> {
let BlockGen { column_gen } = self;
// 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> {
let z_cache = z_cache?;
let sample = &z_cache.sample;
pub fn get_with_z_cache(&mut self, wpos: Vec3<i32>, z_cache: ZCache) -> Option<Block> {
let sample = z_cache.sample;
let &ColumnSample {
alt,
basement,
@ -101,10 +99,14 @@ impl<'a> BlockGen<'a> {
{
Some(Block::empty())
} else {
let sinlike = |x: f32| {
// x.sin()
(x.fract() - 0.5).abs() * 2.0 - 1.0
};
let col = Lerp::lerp(
col.map(|e| e as f32),
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);
Some(Block::new(BlockKind::Rock, col))
@ -154,13 +156,15 @@ impl<'a> BlockGen<'a> {
}
}
#[derive(Clone,Copy)]
pub struct ZCache<'a> {
pub sample: ColumnSample<'a>,
pub calendar: Option<&'a Calendar>,
pub sample: &'a ColumnSample/*<'a>*/,
/* pub calendar: Option<&'a Calendar>, */
}
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
- (self.sample.chaos.min(1.0) * 16.0)
- self.sample.cliff_offset.max(0.0);

View File

@ -7,6 +7,7 @@ use crate::{
layer::spot::Spot,
sim::{SimChunk, WorldSim},
util::{Grid, Sampler},
TerrainGrid,
};
use common::{
calendar::Calendar,
@ -21,16 +22,16 @@ use vek::*;
pub struct CanvasInfo<'a> {
pub(crate) chunk_pos: 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) chunks: &'a WorldSim,
pub(crate) index: IndexRef<'a>,
pub(crate) chunk: &'a SimChunk,
pub(crate) calendar: Option<&'a Calendar>,
/* pub(crate) calendar: Option<&'a Calendar>, */
}
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 }
@ -42,24 +43,41 @@ impl<'a> CanvasInfo<'a> {
.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
.get(self.column_grid_border + wpos - self.wpos())
.and_then(Option::as_ref)
.map(|zc| &zc.sample)
/* .and_then(Option::as_ref)
.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
/// have it.
///
/// This function does not (currently) cache generated columns.
#[inline]
pub fn col_or_gen(&self, wpos: Vec2<i32>) -> Option<Cow<'a, ColumnSample>> {
self.col(wpos).map(Cow::Borrowed).or_else(|| {
Some(Cow::Owned(ColumnGen::new(self.chunks()).get((
wpos,
self.col_inner(wpos).map(Cow::Borrowed).or_else(|| {
let chunk_pos = TerrainGrid::chunk_key(wpos);
let column_gen = ColumnGen::new(self.chunks(), chunk_pos, self.index())?;
Some(Cow::Owned(column_gen.get((
wpos/* ,
self.index(),
self.calendar,
))?))
self.calendar, */
))))
})
}
@ -95,7 +113,7 @@ impl<'a> CanvasInfo<'a> {
sim: &'a WorldSim,
f: F,
) -> 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 {
chaos: 0.0,
alt: 0.0,
@ -128,7 +146,7 @@ impl<'a> CanvasInfo<'a> {
chunks: sim,
index,
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) {
let wpos2d = Vec2::new(x, y);
let info = self.info;
let col = if let Some(col) = info.col(wpos2d) {
let col = if let Some(col) = info.col_inner(wpos2d) {
col
} else {
return;
@ -229,7 +247,7 @@ impl<'a> Canvas<'a> {
seed,
col,
|sprite| block.with_sprite(sprite),
info.calendar,
info.calendar(),
) {
if !new_block.is_air() {
if with_snow && col.snow_cover && above {

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -87,8 +87,6 @@ pub struct Features {
pub scatter: bool,
pub paths: bool,
pub spots: bool,
pub site2_towns: bool,
pub site2_giant_trees: bool,
// 1.0 is the default wildlife density
pub wildlife_density: f32,
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!
dynamic_rng: &mut impl Rng,
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),
index: IndexRef,
supplement: &mut ChunkSupplement,

View File

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

View File

@ -16,7 +16,10 @@ use common::{
vol::ReadVol,
};
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 vek::*;
@ -51,10 +54,54 @@ pub fn tree_valid_at(col: &ColumnSample, seed: u32) -> bool {
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(
canvas: &mut Canvas,
dynamic_rng: &mut impl Rng,
calendar: Option<&Calendar>,
/* calendar: Option<&Calendar>, */
) {
// TODO: Get rid of this
#[allow(clippy::large_enum_variant)]
@ -72,6 +119,7 @@ pub fn apply_trees_to(
}
let info = canvas.info();
let calendar = info.calendar();
/* let mut tree_cache = StructureGenCache::new(info.chunks().gen_ctx.structure_gen.clone()); */
// Get all the trees in range.
@ -237,6 +285,7 @@ pub fn apply_trees_to(
render_area: Aabr<i32>,
filler: /*impl FnOnce(&'b mut Canvas<'a>) -> &'b mut F*/&'b mut F,
render: Render, */
let mut wpos = tree.pos;
let (bounds, hanging_sprites) = match &tree.model {
TreeModel::Structure(s) => {
site2::render_collect(
@ -251,7 +300,6 @@ pub fn apply_trees_to(
.fill(filler.prefab(s, tree.pos, tree.seed), filler);
},
);
arena.reset();
(s.get_bounds(), [(0.0004, SpriteKind::Beehive)].as_ref())
},
&TreeModel::Procedural(ref t, leaf_block) => {
@ -259,7 +307,6 @@ pub fn apply_trees_to(
let trunk_block = t.config.trunk_block;
let leaf_vertical_scale = t.config.leaf_vertical_scale.recip();
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
// 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)
},
};
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 {
TreeModel::Structure(s) => s.get_bounds(),
@ -808,7 +998,7 @@ impl TreeConfig {
pub struct ProceduralTree {
branches: Vec<Branch>,
trunk_idx: usize,
config: TreeConfig,
pub(crate) config: TreeConfig,
roots: Vec<Root>,
root_aabb: Aabb<f32>,
}

View File

@ -282,10 +282,10 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
}
}),
// 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
* 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
} else {
@ -293,9 +293,9 @@ pub fn spawn_manifest() -> Vec<(&'static str, DensityFn)> {
}
}),
// 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
* if matches!(col.chunk.get_biome(), BiomeKind::Ocean) {
* if matches!(c.get_biome(), BiomeKind::Ocean) {
0.001
} else {
0.0
@ -347,12 +347,12 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
// NOTE: Used only for dynamic elements like chests and entities!
dynamic_rng: &mut R,
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),
index: IndexRef,
chunk: &SimChunk,
supplement: &mut ChunkSupplement,
time: Option<&(TimeOfDay, Calendar)>,
time: Option<(&TimeOfDay, &Calendar)>,
) {
let scatter = &index.wildlife_spawns;
// Configurable density multiplier
@ -391,7 +391,7 @@ pub fn apply_wildlife_supplement<'a, R: Rng>(
.request(current_day_period, calendar, is_underwater, is_ice)
.and_then(|pack| {
(dynamic_rng.gen::<f32>() < density * col_sample.spawn_rate
&& col_sample.gradient < Some(1.3))
&& col_sample.gradient < /*Some(*/1.3/*)*/)
.then(|| pack)
})
})

View File

@ -47,6 +47,7 @@ pub use column::ColumnSample;
pub use index::{IndexOwned, IndexRef};
use crate::{
block::ZCache,
column::ColumnGen,
index::Index,
layer::spot::Spot,
@ -154,7 +155,7 @@ impl World {
name: site.site_tmp.map(|id| index.sites[id].name().to_string()),
// TODO: Probably unify these, at some point
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 {
difficulty: match site.site_tmp.map(|id| &index.sites[id].kind) {
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::Tree | civ::SiteKind::GiantTree => world_msg::SiteKind::Tree,
/* civ::SiteKind::Tree | */civ::SiteKind::GiantTree => world_msg::SiteKind::Tree,
// TODO: Maybe change?
civ::SiteKind::Gnarling | civ::SiteKind::Citadel => world_msg::SiteKind::Gnarling,
},
@ -187,21 +188,26 @@ impl World {
}),
)
.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(
&self,
) -> impl Sampler<
Index = (Vec2<i32>, IndexRef, Option<&'_ Calendar>),
Sample = Option<ColumnSample>,
> + '_ {
ColumnGen::new(&self.sim)
pub fn sample_columns<'a>(
&'a self,
chunk_pos: Vec2<i32>,
index: IndexRef<'a>,
/* calendar: Option<&'_ Calendar>, */
) -> Option<impl for<'b> Sampler<'a, 'b,
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(
&self,
@ -227,18 +233,31 @@ impl World {
chunk_pos: Vec2<i32>,
// TODO: misleading name
mut should_continue: impl FnMut() -> bool,
time: Option<(TimeOfDay, Calendar)>,
time: Option<(TimeOfDay/*, Calendar*/)>,
) -> 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_center_wpos2d = chunk_wpos2d + TerrainChunkSize::RECT_SIZE.map(|e| e as i32 / 2);
let grid_border = 4;
let zcache_grid = Grid::populate_from(
TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2,
|offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs, index, calendar),
let grid_border = /*4*/0;
let zcache_grid: Grid<ColumnSample> =
Grid::populate_by_row::<_, _, {TerrainChunkSize::RECT_SIZE.x}, {TerrainChunkSize::RECT_SIZE.y}>(
/* 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);
@ -246,8 +265,8 @@ impl World {
BlockKind::Rock,
zcache_grid
.get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2)
.and_then(|zcache| zcache.as_ref())
.map(|zcache| zcache.sample.stone_col)
/* .and_then(|zcache| zcache.as_ref()) */
.map(|zcache| zcache/*.sample*/.stone_col)
.unwrap_or_else(|| index.colors.deep_stone_color.into()),
);
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 calendar = self.sim.calendar.as_ref();
for y in 0..TerrainChunkSize::RECT_SIZE.y 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 z_cache = match zcache_grid.get(grid_border + offs) {
Some(Some(z_cache)) => z_cache,
let z_cache = zcache_grid.get(grid_border + offs)/*sampler.get_z_cache(chunk_wpos2d + offs, index, calendar)*/;
let z_cache = match z_cache/*.as_ref()*/ {
/*Some(*/Some(sample)/*)*/ =>
ZCache { sample/*, calendar*/ },
_ => continue,
};
// dbg!(chunk_pos, x, y, 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| {
let _ = chunk.set(Vec3::new(x, y, z), stone);
});
let mut block_ = None;
(min_z as i32..max_z as i32).for_each(|z| {
let lpos = Vec3::new(x, y, z);
let wpos = Vec3::from(chunk_wpos2d) + lpos;
if let Some(block) = sampler.get_with_z_cache(wpos, Some(z_cache)) {
let _ = chunk.set(lpos, block);
if let Some(block) = sampler.get_with_z_cache(wpos, /*Some(&*/z_cache/*)*/) {
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| {
zcache_grid
.get(grid_border + offs)
.and_then(Option::as_ref)
.map(|zc| &zc.sample)
// .and_then(Option::as_ref)
/* .map(|zc| &zc.sample) */
};
// Only use for rng affecting dynamic elements like chests and entities!
@ -363,7 +393,7 @@ impl World {
chunks: &self.sim,
index,
chunk: sim_chunk,
calendar,
/* calendar, */
},
chunk: &mut chunk,
// arena: &mut arena,
@ -383,7 +413,7 @@ impl World {
layer::apply_shrubs_to(&mut canvas, &mut dynamic_rng);
}
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 {
layer::apply_scatter_to(&mut canvas, &mut dynamic_rng);
@ -445,7 +475,7 @@ impl World {
&mut supplement,
);
// Apply layer supplement
/* // Apply layer supplement
layer::wildlife::apply_wildlife_supplement(
&mut dynamic_rng,
chunk_wpos2d,
@ -454,8 +484,8 @@ impl World {
index,
sim_chunk,
&mut supplement,
time.as_ref(),
);
time.as_ref().zip(calendar),
); */
// Apply site supplementary information
sim_chunk.sites.iter().for_each(|site| {
@ -487,10 +517,10 @@ impl World {
.sim()
.get_area_trees_par(min_wpos, max_wpos)
.filter_map(|attr| {
ColumnGen::new(self.sim())
.get((attr.pos, index, self.sim().calendar.as_ref()))
.filter(|col| layer::tree::tree_valid_at(col, attr.seed))
.zip(Some(attr))
let chunk_pos = TerrainGrid::chunk_key(attr.pos);
let col = ColumnGen::new(self.sim(), chunk_pos, index/*, self.sim().calendar.as_ref()*/)?
.get((attr.pos/*, index, self.sim().calendar.as_ref()*/));
layer::tree::tree_valid_at(&col, attr.seed).then_some((col, attr))
})
.filter_map(|(col, tree)| {
Some(lod::Object {
@ -562,11 +592,12 @@ impl World {
.filter(|(_, site)| matches!(&site.kind, SiteKind::GiantTree(_)))
.filter_map(|(_, site)| {
let wpos2d = site.get_origin();
let col = ColumnGen::new(self.sim()).get((
wpos2d,
let chunk_pos = TerrainGrid::chunk_key(wpos2d);
let col = ColumnGen::new(self.sim(), chunk_pos, index/*, self.sim().calendar.as_ref()*/)?.get((
wpos2d/* ,
index,
self.sim().calendar.as_ref(),
))?;
self.sim().calendar.as_ref(), */
));
Some(lod::Object {
kind: lod::ObjectKind::GiantTree,
pos: {

View File

@ -55,7 +55,7 @@ use common_net::msg::WorldMapMsg;
use enum_iterator::IntoEnumIterator;
use noise::{
BasicMulti, Billow, Fbm, HybridMulti, MultiFractal, NoiseFn, RangeFunction, RidgedMulti,
Seedable, SuperSimplex, Worley,
Seedable, SuperSimplex, Value, Worley,
};
use num::{traits::FloatConst, Float, Signed};
use rand::{Rng, SeedableRng};
@ -125,13 +125,15 @@ pub(crate) struct GenCtx {
pub _big_structure_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 _town_gen: StructureGen2d,
pub river_seed: RandomField,
pub rock_strength_nz: Fbm,
pub uplift_nz: Worley,
pub fast_hill_nz: Value,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
@ -531,6 +533,7 @@ impl WorldSim {
let rock_lacunarity = 2.0;
let uplift_scale = 128.0;
let uplift_turb_scale = uplift_scale / 4.0;
let hill_nz_seed;
// NOTE: Changing order will significantly change WorldGen, so try not to!
let gen_ctx = GenCtx {
@ -540,7 +543,7 @@ impl WorldSim {
.set_octaves(7)
.set_frequency(RidgedMulti::DEFAULT_FREQUENCY * (5_000.0 / continent_scale))
.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()
.set_octaves(8)
.set_frequency((10_000.0 / continent_scale) as f64)
@ -574,7 +577,9 @@ impl WorldSim {
.set_frequency(0.2)
.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()),
_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
/// 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(
self.map_size_lg(),
core::ops::RangeInclusive::new(CONFIG.sea_level, CONFIG.sea_level + self.max_height),
@ -1488,19 +1493,24 @@ impl WorldSim {
};
let samples_data = {
let column_sample = ColumnGen::new(self);
(0..self.map_size_lg().chunks_len())
.into_par_iter()
.map_init(
|| Box::new(BlockGen::new(ColumnGen::new(self))),
|_block_gen, posi| {
let sample = column_sample.get(
|| Box::new(BlockGen::new(
ColumnGen::new(self, Vec2::new(1, 1), index)
.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,
calendar,
calendar, */
)
)?;
);
// sample.water_level = CONFIG.sea_level.max(sample.water_level);
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() {
let site = &index.sites[site];
match site.kind {
SiteKind::Settlement(_) | SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
/*SiteKind::Settlement(_) | */SiteKind::Refactor(_) | SiteKind::CliffTown(_) => {
towns += site.economy.pop
},
SiteKind::Dungeon(_) => dungeons += site.economy.pop,
SiteKind::Castle(_) => castles += site.economy.pop,
SiteKind::Tree(_) => (),
/* SiteKind::Tree(_) => (), */
SiteKind::GiantTree(_) => (),
SiteKind::Gnarling(_) => {},
}
@ -538,7 +538,7 @@ mod tests {
resources,
neighbors,
kind: match i.kind {
crate::site::SiteKind::Settlement(_)
/* crate::site::SiteKind::Settlement(_) */
| crate::site::SiteKind::Refactor(_)
| crate::site::SiteKind::CliffTown(_) => {
common::terrain::site::SitesKind::Settlement
@ -584,9 +584,9 @@ mod tests {
))
},
// 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,
)),
)*/site2::Site::generate_city(&crate::Land::empty(), &mut env.rng, wpos)),
};
for g in i.resources.iter() {
//let c = sim::SimChunk::new();
@ -648,12 +648,12 @@ mod tests {
resources: &[(Good, f32)],
) -> Id<crate::site::Site> {
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,
None,
&mut env.rng,
//Some(name),
));
)*/site2::Site::generate_city(&crate::Land::empty(), &mut env.rng, wpos));
for (good, amount) in resources.iter() {
settlement.economy.natural_resources.chunks_per_resource
[(*good).try_into().unwrap_or_default()] = *amount;

View File

@ -189,7 +189,7 @@ impl Castle {
&'a self,
index: IndexRef,
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),
) {
for y in 0..vol.size_xy().y as i32 {
@ -284,11 +284,11 @@ impl Castle {
.map(|(dist, _, path, _)| path.head_space(dist))
.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
} else {
col_sample
};
}*/col_sample;
// 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);
@ -433,7 +433,7 @@ impl Castle {
// NOTE: Used only for dynamic elements like chests and entities!
_dynamic_rng: &mut impl Rng,
_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,
) {
// TODO

View File

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

View File

@ -557,7 +557,7 @@ impl Settlement {
&'a self,
index: IndexRef,
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),
) {
let colors = &index.colors.site.settlement;
@ -812,15 +812,20 @@ impl Settlement {
for structure in &self.structures {
let bounds = structure.bounds_2d();
// Skip this structure if it's not near this chunk
if !bounds.collides_with_aabr(Aabr {
let this_aabr = Aabr {
min: wpos2d - self.origin,
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;
}
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 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!
dynamic_rng: &mut impl Rng,
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,
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
// 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 {
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
} else {
// 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.
return
}*/;
}*/*/;
let index = self.canvas_info.index;
let p_bounds = p.get_bounds().center().xy();
let calendar = self.canvas_info.calendar();
@ -1681,7 +1681,7 @@ impl Painter<'_> {
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,
cache: &mut BoundsMap,
tree: &'b Store<Primitive<'b>>,

View File

@ -87,7 +87,7 @@ impl Site {
}
})
.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);
let base_spawn_rules = SpawnRules {
trees: max_warp == 1.0,
@ -862,10 +862,10 @@ impl Site {
.min_by_key(|d| (*d * 100.0) as i32);
if dist.map_or(false, |d| d <= 1.5) {
let alt = canvas.col(wpos2d).map_or(0, |col| col.alt as i32);
let sub_surface_color = canvas
.col(wpos2d)
.map_or(Rgb::zero(), |col| col.sub_surface_color * 0.5);
let col = /*canvas.col(wpos2d)*/None::<&crate::ColumnSample>;
let alt = col.map_or(0, |col| col.alt as i32);
let sub_surface_color =
col.map_or(Rgb::zero(), |col| col.sub_surface_color * 0.5);
let mut underground = true;
for z in -8..6 {
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);
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 sub_surface_color = canvas
.col(wpos2d)
.map_or(Rgb::zero(), |col| col.sub_surface_color * 0.5);
let sub_surface_color =
/*col.map_or(Rgb::zero(), |col| */col.sub_surface_color * 0.5/*)*/;
for z in -6..4 {
canvas.map(
Vec3::new(wpos2d.x, wpos2d.y, alt + z),

View File

@ -80,6 +80,26 @@ impl GiantTree {
impl<F: Filler> Structure<F> for GiantTree {
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 dark = Rgb::new(10, 70, 50).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,
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*/;
self.tree.walk(|branch, parent| {
let aabr = Aabr {
@ -125,6 +218,6 @@ impl<F: Filler> Structure<F> for GiantTree {
} else {
false
}
});
}); */
}
}

View File

@ -19,7 +19,7 @@ impl FastNoise {
}
}
impl Sampler<'static> for FastNoise {
impl Sampler<'static, '_> for FastNoise {
type Index = Vec3<f64>;
type Sample = f32;
@ -93,7 +93,7 @@ impl FastNoise2d {
}
}
impl Sampler<'static> for FastNoise2d {
impl Sampler<'static, '_> for FastNoise2d {
type Index = Vec2<f64>;
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 Sample = u32;
@ -54,7 +54,7 @@ impl RandomPerm {
}
}
impl Sampler<'static> for RandomPerm {
impl Sampler<'static, '_> for RandomPerm {
type Index = 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 Sample: 'a;
fn get(&self, index: Self::Index) -> Self::Sample;
fn get(&'b self, index: Self::Index) -> Self::Sample;
}
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 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 Sample = (Vec2<i32>, Vec2<i32>);