mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Evven zoomier WorldGen, lots of things broken!
This commit is contained in:
parent
a973155597
commit
a3b7127de9
61
Cargo.lock
generated
61
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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"}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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(), */
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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()*/),
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
@ -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()
|
||||
|
@ -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 }
|
||||
|
@ -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));
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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>,
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
109
world/src/lib.rs
109
world/src/lib.rs
@ -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: {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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(_) */
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
) {
|
||||
|
@ -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>>,
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
}
|
||||
});
|
||||
}); */
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -168,7 +168,7 @@ impl StructureGen2d {
|
||||
}
|
||||
}
|
||||
|
||||
impl Sampler<'static> for StructureGen2d {
|
||||
impl Sampler<'static, '_> for StructureGen2d {
|
||||
type Index = Vec2<i32>;
|
||||
type Sample = [StructureField; 9];
|
||||
|
||||
|
@ -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>);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user