diff --git a/assets/world/structures/human/blacksmith.vox b/assets/world/structure/human/blacksmith.vox similarity index 100% rename from assets/world/structures/human/blacksmith.vox rename to assets/world/structure/human/blacksmith.vox diff --git a/assets/world/structures/human/house_1.vox b/assets/world/structure/human/house_1.vox similarity index 100% rename from assets/world/structures/human/house_1.vox rename to assets/world/structure/human/house_1.vox diff --git a/assets/world/structures/human/house_2.vox b/assets/world/structure/human/house_2.vox similarity index 100% rename from assets/world/structures/human/house_2.vox rename to assets/world/structure/human/house_2.vox diff --git a/assets/world/structures/human/mage_tower.vox b/assets/world/structure/human/mage_tower.vox similarity index 100% rename from assets/world/structures/human/mage_tower.vox rename to assets/world/structure/human/mage_tower.vox diff --git a/assets/world/structures/human/stables_1.vox b/assets/world/structure/human/stables_1.vox similarity index 100% rename from assets/world/structures/human/stables_1.vox rename to assets/world/structure/human/stables_1.vox diff --git a/assets/world/structures/human/town_hall.vox b/assets/world/structure/human/town_hall.vox similarity index 100% rename from assets/world/structures/human/town_hall.vox rename to assets/world/structure/human/town_hall.vox diff --git a/assets/world/structures/human/town_hall_spire.vox b/assets/world/structure/human/town_hall_spire.vox similarity index 100% rename from assets/world/structures/human/town_hall_spire.vox rename to assets/world/structure/human/town_hall_spire.vox diff --git a/assets/world/structure/natural/ribcage-large.vox b/assets/world/structure/natural/ribcage-large.vox new file mode 100644 index 0000000000..6e819868a3 --- /dev/null +++ b/assets/world/structure/natural/ribcage-large.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fe98cfc8bc80b57e4ec18a7b26365495f7b4158a1340511bac29780adc5fe3c +size 9376 diff --git a/assets/world/structure/natural/ribcage-small.vox b/assets/world/structure/natural/ribcage-small.vox new file mode 100644 index 0000000000..bd8331d821 --- /dev/null +++ b/assets/world/structure/natural/ribcage-small.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51527d5c611a9a39dd8880b159b46cb754ef6bcba52621caf72aed35b3d5f72f +size 2728 diff --git a/assets/world/structure/natural/skull-large.vox b/assets/world/structure/natural/skull-large.vox new file mode 100644 index 0000000000..cb855f9576 --- /dev/null +++ b/assets/world/structure/natural/skull-large.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a500426f6e90c7653d1f732a57c79bbd3782a69f67e3b8451ade64e9cdc4911 +size 8464 diff --git a/assets/world/structure/natural/tower-ruin.vox b/assets/world/structure/natural/tower-ruin.vox new file mode 100644 index 0000000000..fa3b4880b4 --- /dev/null +++ b/assets/world/structure/natural/tower-ruin.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6135fec30e0b95d66e2105355f4c459912fd0665090a370ddf558871bdc74713 +size 87382 diff --git a/assets/world/structure/natural/witch-hut.vox b/assets/world/structure/natural/witch-hut.vox new file mode 100644 index 0000000000..37cb72b490 --- /dev/null +++ b/assets/world/structure/natural/witch-hut.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b141134c8afbc89cd2858d83e390b175a4f119d36482a053f18a6928ec21046a +size 9708 diff --git a/assets/world/tree/fruit/1.vox b/assets/world/tree/fruit/1.vox new file mode 100644 index 0000000000..4c6a550088 --- /dev/null +++ b/assets/world/tree/fruit/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29f4e1cb1a389e1e7094ca59115189354fb8c008661a17d2fbe45dbf15f8da45 +size 45145 diff --git a/assets/world/tree/fruit/2.vox b/assets/world/tree/fruit/2.vox new file mode 100644 index 0000000000..46c60c36e6 --- /dev/null +++ b/assets/world/tree/fruit/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:086d818f8d0f0b53564f68e29b5176293e4d5ddd4e044f60e2aec0997196d709 +size 45361 diff --git a/assets/world/tree/fruit/3.vox b/assets/world/tree/fruit/3.vox new file mode 100644 index 0000000000..5a74e9e95a --- /dev/null +++ b/assets/world/tree/fruit/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f22e491dd240f64655d72caf2f6cc70b607b2506dc10fb965aafe23b13ebde99 +size 45357 diff --git a/assets/world/tree/fruit/4.vox b/assets/world/tree/fruit/4.vox new file mode 100644 index 0000000000..3608cbabd0 --- /dev/null +++ b/assets/world/tree/fruit/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72e75bd106009dc57f31e4ca52dba70dfed2f8c6d38780473b41df4d70001d46 +size 45009 diff --git a/assets/world/tree/fruit/5.vox b/assets/world/tree/fruit/5.vox new file mode 100644 index 0000000000..3380acc31e --- /dev/null +++ b/assets/world/tree/fruit/5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98eeb7f435816700b8d4f9db6265732a6c760bd524ad32a4db471fe993c9f4bc +size 45001 diff --git a/assets/world/tree/fruit/6.vox b/assets/world/tree/fruit/6.vox new file mode 100644 index 0000000000..dff9b9d188 --- /dev/null +++ b/assets/world/tree/fruit/6.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51c4772b406bd878555be294e3699308b85062b9ce3624161b51f2e2c3ca5a62 +size 45177 diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 5e3d9ccee0..82579efaef 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -219,7 +219,7 @@ impl<'a> System<'a> for Sys { pos.0 += pos_delta / increments; // While the player is colliding with the terrain... - while collision_with(pos.0, near_iter.clone()) && attempts < 32 { + while collision_with(pos.0, near_iter.clone()) && attempts < 16 { // Calculate the player's AABB let player_aabb = Aabb { min: pos.0 + Vec3::new(-player_rad, -player_rad, 0.0), @@ -279,9 +279,11 @@ impl<'a> System<'a> for Sys { // When the resolution direction is non-vertical, we must be colliding with a wall // If the space above is free... - if !collision_with(pos.0 + Vec3::unit_z() * 1.1, near_iter.clone()) + if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), near_iter.clone()) // ...and we're being pushed out horizontally... && resolve_dir.z == 0.0 + // ...and the vertical resolution direction is sufficiently great... + && -dir.z > 0.1 // ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)... && (vel.0.z <= 0.0 || terrain .get((pos.0 - Vec3::unit_z()).map(|e| e.floor() as i32)) @@ -294,27 +296,30 @@ impl<'a> System<'a> for Sys { ) { // ...block-hop! - pos.0.z = (pos.0.z + 1.0).ceil(); + pos.0.z = (pos.0.z + 0.1).ceil(); on_ground = true; break; } else { - // Resolve the collision normally - pos.0 += resolve_dir; - vel.0 = vel - .0 - .map2(resolve_dir, |e, d| if d == 0.0 { e } else { 0.0 }); + // Correct the velocity + vel.0 = vel.0.map2( + resolve_dir, + |e, d| if d * e.signum() < 0.0 { 0.0 } else { e }, + ); } + // Resolve the collision normally + pos.0 += resolve_dir; + attempts += 1; } } if on_ground { let _ = on_grounds.insert(entity, OnGround); - // If we're not on the ground but the space below us is free, then "snap" to the ground + // If the space below us is free, then "snap" to the ground } else if collision_with(pos.0 - Vec3::unit_z() * 1.05, near_iter.clone()) && vel.0.z < 0.0 - && vel.0.z > -1.0 + && vel.0.z > -1.5 && was_on_ground { pos.0.z = (pos.0.z - 0.05).floor(); diff --git a/common/src/terrain/structure.rs b/common/src/terrain/structure.rs index b4b069f9d2..4d386c9743 100644 --- a/common/src/terrain/structure.rs +++ b/common/src/terrain/structure.rs @@ -1,7 +1,7 @@ use super::Block; use crate::{ assets::{self, Asset}, - vol::{BaseVol, ReadVol, Vox, WriteVol}, + vol::{BaseVol, ReadVol, SizedVol, Vox, WriteVol}, volumes::dyna::{Dyna, DynaErr}, }; use dot_vox::DotVoxData; @@ -12,7 +12,9 @@ use vek::*; pub enum StructureBlock { TemperateLeaves, PineLeaves, + Acacia, PalmLeaves, + Fruit, Block(Block), } @@ -44,6 +46,13 @@ impl Structure { self.center = center; self } + + pub fn get_bounds(&self) -> Aabb { + Aabb { + min: -self.center, + max: self.vol.get_size().map(|e| e as i32) - self.center, + } + } } impl BaseVol for Structure { @@ -83,6 +92,8 @@ impl Asset for Structure { 0 => StructureBlock::TemperateLeaves, 1 => StructureBlock::PineLeaves, 2 => StructureBlock::PalmLeaves, + 4 => StructureBlock::Acacia, + 7 => StructureBlock::Fruit, index => { let color = palette .get(index as usize) diff --git a/server/src/lib.rs b/server/src/lib.rs index 428dddc28b..702a13e286 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -84,7 +84,7 @@ impl Server { let mut state = State::default(); state .ecs_mut() - .add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 305.0))); + .add_resource(SpawnPoint(Vec3::new(16_384.0, 16_384.0, 380.0))); let this = Self { state, diff --git a/voxygen/shaders/include/sky.glsl b/voxygen/shaders/include/sky.glsl index 2cddb38394..599cb10669 100644 --- a/voxygen/shaders/include/sky.glsl +++ b/voxygen/shaders/include/sky.glsl @@ -1,17 +1,17 @@ const float PI = 3.141592; -const vec3 SKY_DAY_TOP = vec3(0.2, 0.3, 0.9); -const vec3 SKY_DAY_MID = vec3(0.15, 0.2, 0.8); +const vec3 SKY_DAY_TOP = vec3(0.35, 0.45, 0.9); +const vec3 SKY_DAY_MID = vec3(0.25, 0.35, 0.8); const vec3 SKY_DAY_BOT = vec3(0.02, 0.1, 0.3); const vec3 DAY_LIGHT = vec3(0.5, 0.5, 1.0); const vec3 SKY_DUSK_TOP = vec3(0.1, 0.15, 0.3); -const vec3 SKY_DUSK_MID = vec3(0.9, 0.3, 0.2); +const vec3 SKY_DUSK_MID = vec3(0.8, 0.25, 0.2); const vec3 SKY_DUSK_BOT = vec3(0.01, 0.05, 0.15); -const vec3 DUSK_LIGHT = vec3(0.9, 0.2, 0.1); +const vec3 DUSK_LIGHT = vec3(0.9, 0.4, 0.3); -const vec3 SKY_NIGHT_TOP = vec3(0.002, 0.002, 0.005); -const vec3 SKY_NIGHT_MID = vec3(0.002, 0.01, 0.03); +const vec3 SKY_NIGHT_TOP = vec3(0.001, 0.001, 0.0025); +const vec3 SKY_NIGHT_MID = vec3(0.001, 0.005, 0.02); const vec3 SKY_NIGHT_BOT = vec3(0.002, 0.002, 0.005); const vec3 NIGHT_LIGHT = vec3(0.002, 0.01, 0.03); @@ -31,7 +31,7 @@ float get_sun_brightness(vec3 sun_dir) { const float PERSISTENT_AMBIANCE = 0.008; vec3 get_sun_diffuse(vec3 norm, float time_of_day) { - const float SUN_AMBIANCE = 0.1; + const float SUN_AMBIANCE = 0.075; vec3 sun_dir = get_sun_dir(time_of_day); diff --git a/voxygen/shaders/postprocess.frag b/voxygen/shaders/postprocess.frag index 4e1adf4040..1e40bdbbb9 100644 --- a/voxygen/shaders/postprocess.frag +++ b/voxygen/shaders/postprocess.frag @@ -167,8 +167,7 @@ void main() { //vec4 fxaa_color = texture(src_color, uv); vec4 hsva_color = vec4(rgb2hsv(fxaa_color.rgb), fxaa_color.a); - hsva_color.y *= 1.3; - hsva_color.x -= 0.015; + hsva_color.y *= 1.45; hsva_color.z *= 0.85; //hsva_color.z = 1.0 - 1.0 / (1.0 * hsva_color.z + 1.0); vec4 final_color = vec4(hsv2rgb(hsva_color.rgb), hsva_color.a); diff --git a/voxygen/src/render/pipelines/terrain.rs b/voxygen/src/render/pipelines/terrain.rs index 83d7c9b554..e3cfaf1900 100644 --- a/voxygen/src/render/pipelines/terrain.rs +++ b/voxygen/src/render/pipelines/terrain.rs @@ -50,7 +50,7 @@ impl Vertex { pos_norm: 0 | ((pos.x as u32) & 0x00FF) << 0 | ((pos.y as u32) & 0x00FF) << 8 - | ((pos.z as u32) & 0x1FFF) << 16 + | ((pos.z.max(0.0).min((1 << 13) as f32) as u32) & 0x1FFF) << 16 | ((norm_bits as u32) & 0x7) << 29, col_light: 0 | ((col.r.mul(200.0) as u32) & 0xFF) << 8 diff --git a/world/src/all.rs b/world/src/all.rs index 41f8eee75a..f687672ccb 100644 --- a/world/src/all.rs +++ b/world/src/all.rs @@ -1,6 +1,7 @@ #[derive(Copy, Clone)] pub enum ForestKind { Palm, + Savannah, Oak, Pine, SnowPine, diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 3105554052..0c3c0d5871 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -1,12 +1,12 @@ -mod tree; +mod natural; use crate::{ - column::{ColumnGen, ColumnSample}, + column::{ColumnGen, ColumnSample, StructureData}, util::{HashCache, RandomField, Sampler, SamplerMut}, - World, + World, CONFIG, }; use common::{ - terrain::{structure::StructureBlock, Block}, + terrain::{structure::StructureBlock, Block, Structure}, vol::{ReadVol, Vox}, }; use noise::NoiseFn; @@ -52,7 +52,7 @@ impl<'a> BlockGen<'a> { cache, Vec2::from(*cliff_pos), ) { - Some(cliff_sample) if cliff_sample.cliffs && cliff_sample.spawn_rate > 0.5 => { + Some(cliff_sample) if cliff_sample.is_cliffs && cliff_sample.spawn_rate > 0.5 => { let cliff_pos3d = Vec3::from(*cliff_pos); let height = RandomField::new(seed + 1).get(cliff_pos3d) % 48; @@ -86,18 +86,45 @@ impl<'a> BlockGen<'a> { let sample = Self::sample_column(column_gen, column_cache, wpos)?; // Tree samples - let mut tree_samples = [None, None, None, None, None, None, None, None, None]; - for i in 0..tree_samples.len() { - tree_samples[i] = Self::sample_column( - column_gen, - column_cache, - Vec2::from(sample.close_trees[i].0), - ); + let mut structure_samples = [None, None, None, None, None, None, None, None, None]; + for i in 0..structure_samples.len() { + if let Some(st) = sample.close_structures[i] { + let st_sample = Self::sample_column(column_gen, column_cache, Vec2::from(st.pos)); + structure_samples[i] = st_sample; + } + } + + let mut structures = [None, None, None, None, None, None, None, None, None]; + for i in 0..structures.len() { + if let (Some(st), Some(st_sample)) = + (sample.close_structures[i], structure_samples[i].clone()) + { + let st_info = match st.meta { + None => natural::structure_gen( + column_gen, + column_cache, + i, + st.pos, + st.seed, + &structure_samples, + ), + Some(meta) => Some(StructureInfo { + pos: Vec3::from(st.pos) + Vec3::unit_z() * st_sample.alt as i32, + seed: st.seed, + meta, + }), + }; + + if let Some(st_info) = st_info { + structures[i] = Some((st_info, st_sample)); + } + } } Some(ZCache { + wpos, sample, - tree_samples, + structures, }) } @@ -117,7 +144,7 @@ impl<'a> BlockGen<'a> { sub_surface_color, //tree_density, //forest_kind, - close_trees, + //close_structures, cave_xy, cave_alt, rock, @@ -128,9 +155,8 @@ impl<'a> BlockGen<'a> { .. } = &z_cache?.sample; - let tree_samples = &z_cache?.tree_samples; + let structures = &z_cache?.structures; - let wpos2d = Vec2::from(wpos); let wposf = wpos.map(|e| e as f64); let (definitely_underground, height, water_height) = @@ -146,7 +172,7 @@ impl<'a> BlockGen<'a> { .get((wposf.div(Vec3::new(150.0, 150.0, 150.0))).into_array()) as f32) .mul((chaos - 0.1).max(0.0)) - .mul(115.0); + .mul(96.0); let height = if (wposf.z as f32) < alt + warp - 10.0 { // Shortcut cliffs @@ -177,12 +203,12 @@ impl<'a> BlockGen<'a> { (alt + warp).max(cliff_height) }; - (false, height, water_level + warp) + (false, height, (water_level + warp).max(CONFIG.sea_level)) }; // Sample blocks - let stone_col = Rgb::new(200, 220, 255); + let stone_col = Rgb::new(240, 230, 220); // let dirt_col = Rgb::new(79, 67, 60); let air = Block::empty(); @@ -193,7 +219,7 @@ impl<'a> BlockGen<'a> { // let warm_stone = Block::new(1, Rgb::new(165, 165, 130)); let water = Block::new(1, Rgb::new(100, 150, 255)); - let grass_depth = 2.0; + let grass_depth = 1.5 + 2.0 * chaos; let block = if (wposf.z as f32) < height - grass_depth { let col = Lerp::lerp( sub_surface_color.map(|e| (e * 255.0) as u8), @@ -263,99 +289,39 @@ impl<'a> BlockGen<'a> { } }); - fn block_from_structure( - sblock: StructureBlock, - pos: Vec3, - structure_pos: Vec2, - structure_seed: u32, - _sample: &ColumnSample, - ) -> Block { - let field = RandomField::new(structure_seed + 0); - - let lerp = 0.5 - + ((field.get(Vec3::from(structure_pos)) % 256) as f32 / 256.0 - 0.5) * 0.75 - + ((field.get(Vec3::from(pos)) % 256) as f32 / 256.0 - 0.5) * 0.2; - - match sblock { - StructureBlock::TemperateLeaves => Block::new( - 1, - Lerp::lerp( - Rgb::new(0.0, 80.0, 40.0), - Rgb::new(120.0, 255.0, 10.0), - lerp, - ) - .map(|e| e as u8), - ), - StructureBlock::PineLeaves => Block::new( - 1, - Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp) - .map(|e| e as u8), - ), - StructureBlock::PalmLeaves => Block::new( - 1, - Lerp::lerp( - Rgb::new(30.0, 100.0, 30.0), - Rgb::new(120.0, 255.0, 0.0), - lerp, - ) - .map(|e| e as u8), - ), - StructureBlock::Block(block) => block, - } - } - let block = if definitely_underground { block.unwrap_or(Block::empty()) } else { - match block { - Some(block) => block, - None => (&close_trees).iter().enumerate().fold( - air, - |block, (tree_idx, (tree_pos, tree_seed))| { - if !block.is_empty() { - block - } else { - match &tree_samples[tree_idx] { - Some(tree_sample) - if wpos2d.distance_squared(*tree_pos) < 28 * 28 - && tree_sample.tree_density - > 0.5 + (*tree_seed as f32 / 1000.0).fract() * 0.2 - && tree_sample.alt > tree_sample.water_level - && tree_sample.spawn_rate > 0.5 => - { - let cliff_height = Self::get_cliff_height( - column_gen, - column_cache, - tree_pos.map(|e| e as f32), - &tree_sample.close_cliffs, - cliff_hill, - ); - let height = tree_sample.alt.max(cliff_height); - let tree_pos3d = - Vec3::new(tree_pos.x, tree_pos.y, height as i32); - let rpos = wpos - tree_pos3d; + block + .or_else(|| { + structures.iter().find_map(|st| { + let (st, st_sample) = st.as_ref()?; - let trees = tree::kinds(tree_sample.forest_kind); // Choose tree kind + st.get(wpos, st_sample) - block.or(trees[*tree_seed as usize % trees.len()] - .get((rpos * 128) / 128) // Scaling - .map(|b| { - block_from_structure( - *b, - rpos, - *tree_pos, - *tree_seed, - &tree_sample, - ) - }) - .unwrap_or(Block::empty())) - } - _ => block, - } - } - }, - ), - } + /* + let rpos = wpos - st.pos; + let block_pos = Vec3::unit_z() * rpos.z + + Vec3::from(st.units.0) * rpos.x + + Vec3::from(st.units.1) * rpos.y; + + st.volume + .get((block_pos * 128) / 128) // Scaling + .map(|b| { + block_from_structure( + *b, + block_pos, + st.pos.into(), + st.seed, + st_sample, + ) + }) + .ok() + .filter(|block| !block.is_empty()) + */ + }) + }) + .unwrap_or(Block::empty()) }; Some(block) @@ -363,8 +329,46 @@ impl<'a> BlockGen<'a> { } pub struct ZCache<'a> { + wpos: Vec2, sample: ColumnSample<'a>, - tree_samples: [Option>; 9], + structures: [Option<(StructureInfo, ColumnSample<'a>)>; 9], +} + +impl<'a> ZCache<'a> { + pub fn get_z_limits(&self) -> (f32, f32) { + let cave_depth = if self.sample.cave_xy.abs() > 0.9 { + (self.sample.alt - self.sample.cave_alt + 8.0).max(0.0) + } else { + 0.0 + }; + + let min = self.sample.alt - (self.sample.chaos * 48.0 + cave_depth) - 4.0; + + let cliff = if self.sample.near_cliffs { 48.0 } else { 0.0 }; + let warp = self.sample.chaos * 48.0; + let structure = self.structures.iter().filter_map(|st| st.as_ref()).fold( + 0, + |a, (st_info, st_sample)| { + let bounds = st_info.get_bounds(); + let st_area = Aabr { + min: Vec2::from(bounds.min), + max: Vec2::from(bounds.max), + }; + + if st_area.contains_point(self.wpos - st_info.pos) { + a.max(bounds.max.z) + } else { + a + } + }, + ) as f32; + + let max = (self.sample.alt + cliff + structure + warp + 8.0) + .max(self.sample.water_level) + .max(CONFIG.sea_level + 2.0); + + (min, max) + } } impl<'a> SamplerMut for BlockGen<'a> { @@ -376,3 +380,127 @@ impl<'a> SamplerMut for BlockGen<'a> { self.get_with_z_cache(wpos, z_cache.as_ref()) } } + +#[derive(Copy, Clone)] +pub enum StructureMeta { + Pyramid { + height: i32, + }, + Volume { + units: (Vec2, Vec2), + volume: &'static Structure, + }, +} + +pub struct StructureInfo { + pos: Vec3, + seed: u32, + meta: StructureMeta, +} + +impl StructureInfo { + fn get_bounds(&self) -> Aabb { + match self.meta { + StructureMeta::Pyramid { height } => { + let base = 40; + Aabb { + min: Vec3::new(-base - height, -base - height, -base), + max: Vec3::new(base + height, base + height, height), + } + } + StructureMeta::Volume { units, volume } => { + let bounds = volume.get_bounds(); + + (Aabb { + min: Vec3::from(units.0 * bounds.min.x + units.1 * bounds.min.y) + + Vec3::unit_z() * bounds.min.z, + max: Vec3::from(units.0 * bounds.max.x + units.1 * bounds.max.y) + + Vec3::unit_z() * bounds.max.z, + }) + .made_valid() + } + } + } + + fn get(&self, wpos: Vec3, sample: &ColumnSample) -> Option { + match self.meta { + StructureMeta::Pyramid { height } => { + if wpos.z - self.pos.z + < height + - Vec2::from(wpos - self.pos) + .map(|e: i32| (e.abs() / 2) * 2) + .reduce_max() + { + Some(Block::new(2, Rgb::new(180, 140, 90))) + } else { + None + } + } + StructureMeta::Volume { units, volume } => { + let rpos = wpos - self.pos; + let block_pos = Vec3::unit_z() * rpos.z + + Vec3::from(units.0) * rpos.x + + Vec3::from(units.1) * rpos.y; + + volume + .get((block_pos * 128) / 128) // Scaling + .map(|b| { + block_from_structure(*b, block_pos, self.pos.into(), self.seed, sample) + }) + .ok() + .filter(|block| !block.is_empty()) + } + } + } +} + +fn block_from_structure( + sblock: StructureBlock, + pos: Vec3, + structure_pos: Vec2, + structure_seed: u32, + _sample: &ColumnSample, +) -> Block { + let field = RandomField::new(structure_seed + 0); + + let lerp = 0.5 + + ((field.get(Vec3::from(structure_pos)) % 256) as f32 / 256.0 - 0.5) * 0.65 + + ((field.get(Vec3::from(pos)) % 256) as f32 / 256.0 - 0.5) * 0.15; + + match sblock { + StructureBlock::TemperateLeaves => Block::new( + 1, + Lerp::lerp(Rgb::new(0.0, 70.0, 35.0), Rgb::new(100.0, 140.0, 0.0), lerp) + .map(|e| e as u8), + ), + StructureBlock::PineLeaves => Block::new( + 1, + Lerp::lerp(Rgb::new(0.0, 60.0, 50.0), Rgb::new(30.0, 100.0, 10.0), lerp) + .map(|e| e as u8), + ), + StructureBlock::PalmLeaves => Block::new( + 1, + Lerp::lerp( + Rgb::new(15.0, 100.0, 30.0), + Rgb::new(55.0, 220.0, 0.0), + lerp, + ) + .map(|e| e as u8), + ), + StructureBlock::Acacia => Block::new( + 1, + Lerp::lerp( + Rgb::new(35.0, 100.0, 10.0), + Rgb::new(70.0, 190.0, 25.0), + lerp, + ) + .map(|e| e as u8), + ), + StructureBlock::Fruit => Block::new( + 1, + Lerp::lerp(Rgb::new(255.0, 0.0, 0.0), Rgb::new(200.0, 255.0, 6.0), lerp) + .map(|e| e as u8), + ), + StructureBlock::Block(block) => block, + } +} diff --git a/world/src/block/tree.rs b/world/src/block/natural.rs similarity index 74% rename from world/src/block/tree.rs rename to world/src/block/natural.rs index 2248e28a07..f4958dac70 100644 --- a/world/src/block/tree.rs +++ b/world/src/block/natural.rs @@ -1,16 +1,90 @@ -use crate::all::ForestKind; +use super::{BlockGen, StructureInfo, StructureMeta, ZCache}; +use crate::{ + all::ForestKind, + column::{ColumnGen, ColumnSample}, + util::{HashCache, RandomPerm, Sampler}, + CONFIG, +}; use common::{assets, terrain::Structure}; use lazy_static::lazy_static; use std::sync::Arc; use vek::*; -pub fn kinds(forest_kind: ForestKind) -> &'static [Arc] { - match forest_kind { - ForestKind::Palm => &PALMS, - ForestKind::Oak => &OAKS, - ForestKind::Pine => &PINES, - ForestKind::SnowPine => &SNOW_PINES, +static VOLUME_RAND: RandomPerm = RandomPerm::new(0xDB21C052); +static UNIT_RAND: RandomPerm = RandomPerm::new(0x700F4EC7); +static QUIRKY_RAND: RandomPerm = RandomPerm::new(0xA634460F); + +pub fn structure_gen<'a>( + column_gen: &ColumnGen<'a>, + column_cache: &mut HashCache, Option>>, + idx: usize, + st_pos: Vec2, + st_seed: u32, + structure_samples: &[Option; 9], +) -> Option { + let st_sample = &structure_samples[idx].as_ref()?; + + // Assuming it's a tree... figure out when it SHOULDN'T spawn + if st_sample.tree_density < 0.5 + (st_seed as f32 / 1000.0).fract() * 0.2 + || st_sample.alt < st_sample.water_level + || st_sample.spawn_rate < 0.5 + { + return None; } + + let cliff_height = BlockGen::get_cliff_height( + column_gen, + column_cache, + st_pos.map(|e| e as f32), + &st_sample.close_cliffs, + st_sample.cliff_hill, + ); + + let wheight = st_sample.alt.max(cliff_height); + let st_pos3d = Vec3::new(st_pos.x, st_pos.y, wheight as i32); + + let volumes: &'static [_] = if QUIRKY_RAND.get(st_seed) % 64 == 17 { + if st_sample.temp > CONFIG.desert_temp { + &QUIRKY_DRY + } else { + &QUIRKY + } + } else { + match st_sample.forest_kind { + ForestKind::Palm => &PALMS, + ForestKind::Savannah => &ACACIAS, + ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 16 == 7 => &OAK_STUMPS, + ForestKind::Oak if QUIRKY_RAND.get(st_seed) % 8 == 7 => &FRUIT_TREES, + ForestKind::Oak => &OAKS, + ForestKind::Pine => &PINES, + ForestKind::SnowPine => &SNOW_PINES, + } + }; + + const UNIT_CHOICES: [(Vec2, Vec2); 8] = [ + (Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: 1 }), + (Vec2 { x: 1, y: 0 }, Vec2 { x: 0, y: -1 }), + (Vec2 { x: -1, y: 0 }, Vec2 { x: 0, y: 1 }), + (Vec2 { x: -1, y: 0 }, Vec2 { x: 0, y: -1 }), + (Vec2 { x: 0, y: 1 }, Vec2 { x: 1, y: 0 }), + (Vec2 { x: 0, y: 1 }, Vec2 { x: -1, y: 0 }), + (Vec2 { x: 0, y: -1 }, Vec2 { x: 1, y: 0 }), + (Vec2 { x: 0, y: -1 }, Vec2 { x: -1, y: 0 }), + ]; + + Some(StructureInfo { + pos: st_pos3d, + seed: st_seed, + meta: StructureMeta::Volume { + units: UNIT_CHOICES[UNIT_RAND.get(st_seed) as usize % UNIT_CHOICES.len()], + volume: &volumes[(VOLUME_RAND.get(st_seed) / 13) as usize % volumes.len()], + }, + }) +} + +fn st_asset(path: &str, offset: impl Into>) -> Arc { + assets::load_map(path, |s: Structure| s.with_center(offset.into())) + .expect("Failed to load structure asset") } lazy_static! { @@ -43,6 +117,9 @@ lazy_static! { assets::load_map("world/tree/oak_green/9.vox", |s: Structure| s .with_center(Vec3::new(26, 26, 14))) .unwrap(), + ]; + + pub static ref OAK_STUMPS: Vec> = vec![ // oak stumps assets::load_map("world/tree/oak_stump/1.vox", |s: Structure| s .with_center(Vec3::new(15, 18, 10))) @@ -278,31 +355,35 @@ lazy_static! { pub static ref SNOW_PINES: Vec> = vec![ // snow pines - assets::load_map("world/tree/snow_pine/1.vox", |s: Structure| s - .with_center(Vec3::new(15, 15, 14))) - .unwrap(), - assets::load_map("world/tree/snow_pine/2.vox", |s: Structure| s - .with_center(Vec3::new(15, 15, 14))) - .unwrap(), - assets::load_map("world/tree/snow_pine/3.vox", |s: Structure| s - .with_center(Vec3::new(17, 15, 12))) - .unwrap(), - assets::load_map("world/tree/snow_pine/4.vox", |s: Structure| s - .with_center(Vec3::new(10, 8, 12))) - .unwrap(), - assets::load_map("world/tree/snow_pine/5.vox", |s: Structure| s - .with_center(Vec3::new(12, 12, 12))) - .unwrap(), - assets::load_map("world/tree/snow_pine/6.vox", |s: Structure| s - .with_center(Vec3::new(11, 10, 12))) - .unwrap(), - assets::load_map("world/tree/snow_pine/7.vox", |s: Structure| s - .with_center(Vec3::new(16, 15, 12))) - .unwrap(), - assets::load_map("world/tree/snow_pine/8.vox", |s: Structure| s - .with_center(Vec3::new(12, 10, 12))) - .unwrap(), + st_asset("world/tree/snow_pine/1.vox", (15, 15, 14)), + st_asset("world/tree/snow_pine/2.vox", (15, 15, 14)), + st_asset("world/tree/snow_pine/3.vox", (17, 15, 12)), + st_asset("world/tree/snow_pine/4.vox", (10, 8, 12)), + st_asset("world/tree/snow_pine/5.vox", (12, 12, 12)), + st_asset("world/tree/snow_pine/6.vox", (11, 10, 12)), + st_asset("world/tree/snow_pine/7.vox", (16, 15, 12)), + st_asset("world/tree/snow_pine/8.vox", (12, 10, 12)), ]; + + pub static ref ACACIAS: Vec> = vec![ + // snow pines + st_asset("world/tree/acacia/1.vox", (16, 17, 1)), + st_asset("world/tree/acacia/2.vox", (5, 6, 1)), + st_asset("world/tree/acacia/3.vox", (5, 6, 1)), + st_asset("world/tree/acacia/4.vox", (15, 16, 1)), + st_asset("world/tree/acacia/5.vox", (19, 18, 1)), + ]; + + pub static ref FRUIT_TREES: Vec> = vec![ + // snow pines + st_asset("world/tree/fruit/1.vox", (5, 5, 7)), + st_asset("world/tree/fruit/2.vox", (5, 5, 7)), + st_asset("world/tree/fruit/3.vox", (5, 5, 7)), + st_asset("world/tree/fruit/4.vox", (5, 5, 7)), + st_asset("world/tree/fruit/5.vox", (5, 5, 7)), + st_asset("world/tree/fruit/6.vox", (5, 5, 7)), + ]; + /* // snow birches -> need roots! assets::load_map("world/tree/snow_birch/1.vox", |s: Structure| s @@ -350,4 +431,15 @@ lazy_static! { .unwrap(), ]; */ + + pub static ref QUIRKY: Vec> = vec![ + st_asset("world/structure/natural/tower-ruin.vox", (11, 14, 5)), + st_asset("world/structure/natural/witch-hut.vox", (10, 13, 9)), + ]; + + pub static ref QUIRKY_DRY: Vec> = vec![ + st_asset("world/structure/natural/ribcage-small.vox", (7, 13, 4)), + st_asset("world/structure/natural/ribcage-large.vox", (13, 19, 8)), + st_asset("world/structure/natural/skull-large.vox", (15, 20, 4)), + ]; } diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 44836c72cb..e9a45bb0a2 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,4 +1,10 @@ -use crate::{all::ForestKind, sim::LocationInfo, util::Sampler, World, CONFIG}; +use crate::{ + all::ForestKind, + block::StructureMeta, + sim::{LocationInfo, SimChunk}, + util::Sampler, + World, CONFIG, +}; use common::{terrain::TerrainChunkSize, vol::VolSize}; use noise::NoiseFn; use std::{ @@ -15,6 +21,51 @@ impl<'a> ColumnGen<'a> { pub fn new(world: &'a World) -> Self { Self { world } } + + fn get_local_structure(&self, wpos: Vec2) -> Option { + let (pos, seed) = self + .world + .sim() + .gen_ctx + .region_gen + .get(wpos) + .iter() + .copied() + .min_by_key(|(pos, _)| pos.distance_squared(wpos)) + .unwrap(); + + let chunk = self.world.sim().get(pos)?; + + if seed % 5 == 2 && chunk.temp > CONFIG.desert_temp && chunk.alt > CONFIG.sea_level + 5.0 { + Some(StructureData { + pos, + seed, + meta: Some(StructureMeta::Pyramid { height: 140 }), + }) + } else { + None + } + } + + fn gen_close_structures(&self, wpos: Vec2) -> [Option; 9] { + let mut metas = [None; 9]; + self.world + .sim() + .gen_ctx + .structure_gen + .get(wpos) + .into_iter() + .copied() + .enumerate() + .for_each(|(i, (pos, seed))| { + metas[i] = self.get_local_structure(pos).or(Some(StructureData { + pos, + seed, + meta: None, + })); + }); + metas + } } impl<'a> Sampler for ColumnGen<'a> { @@ -47,6 +98,7 @@ impl<'a> Sampler for ColumnGen<'a> { const RIVER_PROPORTION: f32 = 0.025; + /* let river = dryness .abs() .neg() @@ -54,6 +106,8 @@ impl<'a> Sampler for ColumnGen<'a> { .div(RIVER_PROPORTION) .max(0.0) .mul((1.0 - (chaos - 0.15) * 20.0).max(0.0).min(1.0)); + */ + let river = 0.0; let cliff_hill = (sim.gen_ctx.small_nz.get((wposf.div(128.0)).into_array()) as f32).mul(16.0); @@ -61,10 +115,11 @@ impl<'a> Sampler for ColumnGen<'a> { let riverless_alt = sim.get_interpolated(wpos, |chunk| chunk.alt)? + (sim.gen_ctx.small_nz.get((wposf.div(256.0)).into_array()) as f32) .abs() - .mul(chaos.max(0.2)) + .mul(chaos.max(0.15)) .mul(64.0); - let cliffs = sim_chunk.cliffs; + let is_cliffs = sim_chunk.is_cliffs; + let near_cliffs = sim_chunk.near_cliffs; let alt = riverless_alt - (1.0 - river) @@ -74,7 +129,7 @@ impl<'a> Sampler for ColumnGen<'a> { .mul(0.5) .mul(24.0); - let water_level = (riverless_alt - 4.0 - 5.0 * chaos).max(CONFIG.sea_level); + let water_level = riverless_alt - 4.0 - 5.0 * chaos; let rock = (sim.gen_ctx.small_nz.get( Vec3::new(wposf.x, wposf.y, alt as f64) @@ -102,28 +157,32 @@ impl<'a> Sampler for ColumnGen<'a> { let warm_grass = Rgb::new(0.18, 0.65, 0.0); let cold_stone = Rgb::new(0.55, 0.7, 0.75); let warm_stone = Rgb::new(0.65, 0.65, 0.35); - let beach_sand = Rgb::new(0.93, 0.84, 0.4); - let desert_sand = Rgb::new(0.98, 0.8, 0.15); + let beach_sand = Rgb::new(0.9, 0.85, 0.3); + let desert_sand = Rgb::new(1.0, 0.7, 0.15); let snow = Rgb::broadcast(1.0); + let dirt = Lerp::lerp(Rgb::new(0.2, 0.1, 0.05), Rgb::new(0.4, 0.25, 0.0), marble); + let cliff = Rgb::lerp(cold_stone, warm_stone, marble); + let grass = Rgb::lerp(cold_grass, warm_grass, marble); let sand = Rgb::lerp(beach_sand, desert_sand, marble); - let cliff = Rgb::lerp(cold_stone, warm_stone, marble); - let dirt = Lerp::lerp(Rgb::new(0.2, 0.1, 0.05), Rgb::new(0.4, 0.25, 0.0), marble); - - let turf = grass; + let tropical = Rgb::lerp( + grass, + Rgb::new(0.85, 0.4, 0.2), + marble_small.sub(0.5).mul(0.2).add(0.75), + ); let ground = Rgb::lerp( Rgb::lerp( snow, - turf, + grass, temp.sub(CONFIG.snow_temp) .sub((marble - 0.5) * 0.05) .mul(256.0), ), - sand, - temp.sub(CONFIG.desert_temp).mul(32.0), + Rgb::lerp(tropical, sand, temp.sub(CONFIG.desert_temp).mul(32.0)), + temp.sub(CONFIG.tropical_temp).mul(16.0), ); // Work out if we're on a path or near a town @@ -227,25 +286,27 @@ impl<'a> Sampler for ColumnGen<'a> { cliff, snow, (alt - CONFIG.sea_level - - 0.35 * CONFIG.mountain_scale + - 0.4 * CONFIG.mountain_scale - alt_base - temp * 96.0 - marble * 24.0) / 12.0, ), - (alt - CONFIG.sea_level - 0.3 * CONFIG.mountain_scale + marble * 128.0) / 100.0, + (alt - CONFIG.sea_level - 0.25 * CONFIG.mountain_scale + marble * 128.0) + / 100.0, ), // Beach - ((alt - CONFIG.sea_level - 2.0) / 5.0).min(1.0 - river * 2.0), + ((alt - CONFIG.sea_level - 1.0) / 2.0).min(1.0 - river * 2.0), ), sub_surface_color: dirt, tree_density, forest_kind: sim_chunk.forest_kind, - close_trees: sim.gen_ctx.tree_gen.get(wpos), + close_structures: self.gen_close_structures(wpos), cave_xy, cave_alt, rock, - cliffs, + is_cliffs, + near_cliffs, cliff_hill, close_cliffs: sim.gen_ctx.cliff_gen.get(wpos), temp, @@ -265,14 +326,22 @@ pub struct ColumnSample<'a> { pub sub_surface_color: Rgb, pub tree_density: f32, pub forest_kind: ForestKind, - pub close_trees: [(Vec2, u32); 9], + pub close_structures: [Option; 9], pub cave_xy: f32, pub cave_alt: f32, pub rock: f32, - pub cliffs: bool, + pub is_cliffs: bool, + pub near_cliffs: bool, pub cliff_hill: f32, pub close_cliffs: [(Vec2, u32); 9], pub temp: f32, pub spawn_rate: f32, pub location: Option<&'a LocationInfo>, } + +#[derive(Copy, Clone)] +pub struct StructureData { + pub pos: Vec2, + pub seed: u32, + pub meta: Option, +} diff --git a/world/src/config.rs b/world/src/config.rs index 035b4c60d4..fc3199d0de 100644 --- a/world/src/config.rs +++ b/world/src/config.rs @@ -2,12 +2,14 @@ pub struct Config { pub sea_level: f32, pub mountain_scale: f32, pub snow_temp: f32, + pub tropical_temp: f32, pub desert_temp: f32, } pub const CONFIG: Config = Config { sea_level: 140.0, - mountain_scale: 1200.0, + mountain_scale: 1000.0, snow_temp: -0.4, - desert_temp: 0.4, + tropical_temp: 0.25, + desert_temp: 0.45, }; diff --git a/world/src/lib.rs b/world/src/lib.rs index 15c691f0d9..32603d93dd 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -71,12 +71,19 @@ impl World { .and_then(|base_z| self.sim.get(chunk_pos).map(|sim_chunk| (base_z, sim_chunk))) { Some((base_z, sim_chunk)) => (base_z as i32, sim_chunk), - None => return TerrainChunk::new(0, water, air, TerrainChunkMeta::void()), + None => { + return TerrainChunk::new( + CONFIG.sea_level as i32, + water, + air, + TerrainChunkMeta::void(), + ) + } }; let meta = TerrainChunkMeta::new(sim_chunk.get_name(&self.sim), sim_chunk.get_biome()); - let mut chunk = TerrainChunk::new(base_z - 8, stone, air, meta); + let mut chunk = TerrainChunk::new(base_z, stone, air, meta); let mut sampler = self.sample_blocks(); @@ -84,26 +91,24 @@ impl World { for y in 0..TerrainChunkSize::SIZE.y as i32 { let wpos2d = Vec2::new(x, y) + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); - let _wposf2d = wpos2d.map(|e| e as f64); - let min_z = self - .sim - .get_interpolated(wpos2d, |chunk| chunk.get_min_z()) - .unwrap_or(0.0) as i32; + let z_cache = match sampler.get_z_cache(wpos2d) { + Some(z_cache) => z_cache, + None => continue, + }; - let max_z = self - .sim - .get_interpolated(wpos2d, |chunk| chunk.get_max_z()) - .unwrap_or(0.0) as i32; + let (min_z, max_z) = z_cache.get_z_limits(); - let z_cache = sampler.get_z_cache(wpos2d); + for z in base_z..min_z as i32 { + let _ = chunk.set(Vec3::new(x, y, z), stone); + } - for z in min_z..max_z { + for z in min_z as i32..max_z as i32 { let lpos = Vec3::new(x, y, z); let wpos = lpos + Vec3::from(chunk_pos) * TerrainChunkSize::SIZE.map(|e| e as i32); - if let Some(block) = sampler.get_with_z_cache(wpos, z_cache.as_ref()) { + if let Some(block) = sampler.get_with_z_cache(wpos, Some(&z_cache)) { let _ = chunk.set(lpos, block); } } diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 6014db6726..7981874852 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -16,7 +16,7 @@ use common::{ }; use noise::{BasicMulti, HybridMulti, MultiFractal, NoiseFn, RidgedMulti, Seedable, SuperSimplex}; use rand::{prng::XorShiftRng, Rng, SeedableRng}; -use std::ops::{Add, Div, Mul, Sub}; +use std::ops::{Add, Div, Mul, Neg, Sub}; use vek::*; pub const WORLD_SIZE: Vec2 = Vec2 { x: 1024, y: 1024 }; @@ -38,7 +38,8 @@ pub(crate) struct GenCtx { pub cave_0_nz: SuperSimplex, pub cave_1_nz: SuperSimplex, - pub tree_gen: StructureGen2d, + pub structure_gen: StructureGen2d, + pub region_gen: StructureGen2d, pub cliff_gen: StructureGen2d, } @@ -75,8 +76,9 @@ impl WorldSim { cave_0_nz: SuperSimplex::new().set_seed(seed + 13), cave_1_nz: SuperSimplex::new().set_seed(seed + 14), - tree_gen: StructureGen2d::new(seed, 32, 24), - cliff_gen: StructureGen2d::new(seed, 80, 56), + structure_gen: StructureGen2d::new(seed, 32, 24), + region_gen: StructureGen2d::new(seed + 1, 400, 96), + cliff_gen: StructureGen2d::new(seed + 2, 80, 56), }; let mut chunks = Vec::new(); @@ -306,8 +308,6 @@ impl WorldSim { } } -const Z_TOLERANCE: (f32, f32) = (100.0, 128.0); - pub struct SimChunk { pub chaos: f32, pub alt_base: f32, @@ -315,7 +315,7 @@ pub struct SimChunk { pub temp: f32, pub dryness: f32, pub rockiness: f32, - pub cliffs: bool, + pub is_cliffs: bool, pub near_cliffs: bool, pub tree_density: f32, pub forest_kind: ForestKind, @@ -353,6 +353,8 @@ impl SimChunk { .add(0.3) .max(0.0); + let temp = gen_ctx.temp_nz.get((wposf.div(12000.0)).into_array()) as f32; + let dryness = gen_ctx.dry_nz.get( (wposf .add(Vec2::new( @@ -366,44 +368,53 @@ impl SimChunk { .into_array(), ) as f32; - let chaos = (gen_ctx.chaos_nz.get((wposf.div(4_000.0)).into_array()) as f32) + let chaos = (gen_ctx.chaos_nz.get((wposf.div(3_000.0)).into_array()) as f32) .add(1.0) .mul(0.5) .mul( (gen_ctx.chaos_nz.get((wposf.div(6_000.0)).into_array()) as f32) - .powf(2.0) - .add(0.5) + .abs() + .max(0.25) .min(1.0), ) - .powf(1.5) - .add(0.1 * hill); + .add(0.15 * hill) + .mul( + temp.sub(CONFIG.desert_temp) + .neg() + .mul(12.0) + .max(0.35) + .min(1.0), + ) + .max(0.1); - let chaos = chaos + chaos.mul(16.0).sin().mul(0.02); - - let alt_base = gen_ctx.alt_nz.get((wposf.div(6_000.0)).into_array()) as f32; - let alt_base = alt_base - .mul(0.4) - .add(alt_base.mul(128.0).sin().mul(0.005)) - .mul(400.0); + let alt_base = (gen_ctx.alt_nz.get((wposf.div(12_000.0)).into_array()) as f32) + .mul(250.0) + .sub(25.0); let alt_main = (gen_ctx.alt_nz.get((wposf.div(2_000.0)).into_array()) as f32) .abs() - .powf(1.8); + .powf(1.35); - let alt = CONFIG.sea_level + let map_edge_factor = pos + .map2(WORLD_SIZE.map(|e| e as i32), |e, sz| { + (sz / 2 - (e - sz / 2).abs()) as f32 / 16.0 + }) + .reduce_partial_min() + .max(0.0) + .min(1.0); + + let alt = (CONFIG.sea_level + alt_base + (0.0 + alt_main - + gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32 - * alt_main.max(0.1) - * chaos - * 1.6) - .add(1.0) - .mul(0.5) - .mul(chaos) - .mul(CONFIG.mountain_scale); - - let temp = gen_ctx.temp_nz.get((wposf.div(8192.0)).into_array()) as f32; + + (gen_ctx.small_nz.get((wposf.div(300.0)).into_array()) as f32) + .mul(alt_main.max(0.25)) + .mul(1.6)) + .add(1.0) + .mul(0.5) + .mul(chaos) + .mul(CONFIG.mountain_scale)) + * map_edge_factor; let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2; @@ -417,24 +428,28 @@ impl SimChunk { .sub(0.1) .mul(1.3) .max(0.0), - cliffs: cliff > 0.5 + is_cliffs: cliff > 0.5 && dryness > 0.05 && alt > CONFIG.sea_level + 5.0 && dryness.abs() > 0.075, - near_cliffs: cliff > 0.4, + near_cliffs: cliff > 0.25, tree_density: (gen_ctx.tree_nz.get((wposf.div(1024.0)).into_array()) as f32) + .mul(1.5) .add(1.0) .mul(0.5) .mul(1.2 - chaos * 0.95) - .add(0.1) + .add(0.05) .mul(if alt > CONFIG.sea_level + 5.0 { 1.0 } else { 0.0 - }), + }) + .max(0.0), forest_kind: if temp > 0.0 { if temp > CONFIG.desert_temp { ForestKind::Palm + } else if temp > CONFIG.tropical_temp { + ForestKind::Savannah } else { ForestKind::Oak } @@ -451,16 +466,7 @@ impl SimChunk { } pub fn get_base_z(&self) -> f32 { - self.alt - Z_TOLERANCE.0 * self.chaos - } - - pub fn get_min_z(&self) -> f32 { - self.alt - Z_TOLERANCE.0 * (self.chaos * 1.2 + 0.3) - } - - pub fn get_max_z(&self) -> f32 { - (self.alt + Z_TOLERANCE.1 * if self.near_cliffs { 1.0 } else { 0.5 }) - .max(CONFIG.sea_level + 2.0) + self.alt - self.chaos * 50.0 - 16.0 } pub fn get_name(&self, world: &WorldSim) -> Option { diff --git a/world/src/util/mod.rs b/world/src/util/mod.rs index a9c3cb8ae9..fd538e21ce 100644 --- a/world/src/util/mod.rs +++ b/world/src/util/mod.rs @@ -6,7 +6,7 @@ pub mod structure; // Reexports pub use self::{ hash_cache::HashCache, - random::RandomField, + random::{RandomField, RandomPerm}, sampler::{Sampler, SamplerMut}, structure::StructureGen2d, }; diff --git a/world/src/util/random.rs b/world/src/util/random.rs index 6e30b635bb..ece87a30ce 100644 --- a/world/src/util/random.rs +++ b/world/src/util/random.rs @@ -6,7 +6,7 @@ pub struct RandomField { } impl RandomField { - pub fn new(seed: u32) -> Self { + pub const fn new(seed: u32) -> Self { Self { seed } } } @@ -35,3 +35,28 @@ impl Sampler for RandomField { a } } + +pub struct RandomPerm { + seed: u32, +} + +impl RandomPerm { + pub const fn new(seed: u32) -> Self { + Self { seed } + } +} + +impl Sampler for RandomPerm { + type Index = u32; + type Sample = u32; + + fn get(&self, perm: Self::Index) -> Self::Sample { + let mut a = perm; + a = (a ^ 61) ^ (a >> 16); + a = a + (a << 3); + a = a ^ (a >> 4); + a = a * 0x27d4eb2d; + a = a ^ (a >> 15); + a + } +}