diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a6017d271..9d122cf26e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow moving and resizing the chat with left and right mouse button respectively. - Missing plugins are requested from the server and cached locally. - Support for adding spots in plugins. +- Added real colours to LOD trees and rooftops, unique models for most tree kinds, and models for several buildings ### Changed diff --git a/Cargo.lock b/Cargo.lock index ecc5992933..2e2404e181 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7316,6 +7316,7 @@ version = "0.15.0" dependencies = [ "assets_manager", "backtrace", + "bitflags 2.5.0", "bytemuck", "chrono", "chumsky", diff --git a/assets/voxygen/lod/acacia.obj b/assets/voxygen/lod/acacia.obj new file mode 100644 index 0000000000..92499bbdd7 --- /dev/null +++ b/assets/voxygen/lod/acacia.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a5936bfdb8c4b520624a114ca483a2f98b8dc06e73adeb249c10102cc855e5d3 +size 6105 diff --git a/assets/voxygen/lod/arena.obj b/assets/voxygen/lod/arena.obj new file mode 100644 index 0000000000..9df00fa2f4 --- /dev/null +++ b/assets/voxygen/lod/arena.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9faa484801eb344e91107fabaf53759833991deadc29c006ffe04853b2144aad +size 144682 diff --git a/assets/voxygen/lod/baobab.obj b/assets/voxygen/lod/baobab.obj new file mode 100644 index 0000000000..80e47b5482 --- /dev/null +++ b/assets/voxygen/lod/baobab.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3becd9ff1cdf7c4194d39189c2145b8d0780720b6fad6087c1c2367be3cba71 +size 14974 diff --git a/assets/voxygen/lod/birch.obj b/assets/voxygen/lod/birch.obj new file mode 100644 index 0000000000..180f7b0025 --- /dev/null +++ b/assets/voxygen/lod/birch.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af6e9710ce261466e6c119567dce579a8c87e35ad86a7533d5a40551ebb3aa8c +size 8034 diff --git a/assets/voxygen/lod/dead.obj b/assets/voxygen/lod/dead.obj index ba176025a6..eb21d72a98 100644 --- a/assets/voxygen/lod/dead.obj +++ b/assets/voxygen/lod/dead.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8ce23c4423ab1bb1b62cc108896ffe94078d02abc0147483cf8e9bddebe81ba -size 16668 +oid sha256:f09c12972a9b1c54f9721806b562ea5897cde77a35d4ce0fe528300255aa6846 +size 6156 diff --git a/assets/voxygen/lod/desert_houses.obj b/assets/voxygen/lod/desert_houses.obj new file mode 100644 index 0000000000..cf8ba54a67 --- /dev/null +++ b/assets/voxygen/lod/desert_houses.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:febef70cb56c24e97ba4f0f2b4bf33a7597bb7363385afc39db7b346060acedf +size 16963 diff --git a/assets/voxygen/lod/frostpine.obj b/assets/voxygen/lod/frostpine.obj new file mode 100644 index 0000000000..a6a675a429 --- /dev/null +++ b/assets/voxygen/lod/frostpine.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33cc8309dbaf6f7bf8658f4321d1300477204e2f89158caf0ee5db12be423a10 +size 14068 diff --git a/assets/voxygen/lod/giant_tree.obj b/assets/voxygen/lod/giant_tree.obj index 537137b28e..5423cc4f2b 100644 --- a/assets/voxygen/lod/giant_tree.obj +++ b/assets/voxygen/lod/giant_tree.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e62225772849d8b21b4e3d071aad6baa20a5005c250b0c0c3f20d83f620c78d -size 165629 +oid sha256:de77ea0a9ab177af5a5c60ccf9e2b9414a40496a01d5f24b6d46e80ac05934f3 +size 77362 diff --git a/assets/voxygen/lod/haniwa.obj b/assets/voxygen/lod/haniwa.obj new file mode 100644 index 0000000000..4b20090d4a --- /dev/null +++ b/assets/voxygen/lod/haniwa.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dfb7b45d428e8b0f67f38b18f31bd08e9510af5b52b8268dd7dae886b75aadba +size 41276 diff --git a/assets/voxygen/lod/house.obj b/assets/voxygen/lod/house.obj index f2acb016d5..e790d47b3e 100644 --- a/assets/voxygen/lod/house.obj +++ b/assets/voxygen/lod/house.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8fc436550d5a359c260f6a46f3327795383ab5ffa48305f6d6de87305290a60 -size 1411 +oid sha256:c68b1dd9723536ea6840eb4a5a16676a74b04e373146e47249cd1b55353ac621 +size 3181 diff --git a/assets/voxygen/lod/mangrove.obj b/assets/voxygen/lod/mangrove.obj new file mode 100644 index 0000000000..b0557065e8 --- /dev/null +++ b/assets/voxygen/lod/mangrove.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9193266ea57f68ff020cdda45fbeced3bd17ed1e7edb6da6c5638eca798b01b2 +size 5505 diff --git a/assets/voxygen/lod/oak.obj b/assets/voxygen/lod/oak.obj index a0a89bbd6e..59ffdcd493 100644 --- a/assets/voxygen/lod/oak.obj +++ b/assets/voxygen/lod/oak.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:210ac6acdb4caf3a4c5e46eb197202944f6f5477b67e3ccb9eea75e76bd55743 -size 6952 +oid sha256:17e7d885e7dc4245f0c919f6093958be020e39f71317929a1074b7c4a1fe394e +size 7168 diff --git a/assets/voxygen/lod/palm.obj b/assets/voxygen/lod/palm.obj new file mode 100644 index 0000000000..cdfac45e31 --- /dev/null +++ b/assets/voxygen/lod/palm.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e847f15047894e01a834797a0a7a8064426215f84e41ee1a1bca174631bac0c0 +size 4646 diff --git a/assets/voxygen/lod/pine.obj b/assets/voxygen/lod/pine.obj index 7822519be5..f534b631e2 100644 --- a/assets/voxygen/lod/pine.obj +++ b/assets/voxygen/lod/pine.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f789d28ddd4e18c3d79f95377d9c141447deb9ef76d2a4350b32b890f81c659 -size 5109 +oid sha256:69f2d42e0ce95b659320d549ad573d591e66e6f10f566b4fced87f787b7a05dc +size 6311 diff --git a/assets/voxygen/lod/redwood.obj b/assets/voxygen/lod/redwood.obj new file mode 100644 index 0000000000..a1aef8de8d --- /dev/null +++ b/assets/voxygen/lod/redwood.obj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aefe57aacf6706d382a718b4bb3fee506db069ee06776606b12bb8bc0d9b0c6a +size 37168 diff --git a/assets/voxygen/shaders/lod-object-frag.glsl b/assets/voxygen/shaders/lod-object-frag.glsl index f9c6511642..3e0317385d 100644 --- a/assets/voxygen/shaders/lod-object-frag.glsl +++ b/assets/voxygen/shaders/lod-object-frag.glsl @@ -25,8 +25,7 @@ layout(location = 3) in vec3 model_pos; layout(location = 4) flat in uint f_flags; const uint FLAG_SNOW_COVERED = 1; -const uint FLAG_IS_BUILDING = 2; -const uint FLAG_IS_GIANT_TREE = 4; +const uint FLAG_GLOW = 2; layout(location = 0) out vec4 tgt_color; layout(location = 1) out uvec4 tgt_mat; @@ -84,13 +83,6 @@ void main() { vec3 k_d = vec3(1.0); vec3 k_s = vec3(R_s); - // Tree trunks - if ((f_flags & FLAG_IS_GIANT_TREE) > 0u) { - if (dot(abs(model_pos.xyz) * vec3(1.0, 1.0, 2.0), vec3(1)) < 430.0) { surf_color = vec3(0.05, 0.02, 0.0); } - } else { - if (model_pos.z < 25.0 && dot(abs(model_pos.xy), vec2(1)) < 6.0) { surf_color = vec3(0.05, 0.02, 0.0); } - } - vec3 voxel_norm = f_norm; float my_alt = f_pos.z + focus_off.z; float f_ao = 1.0; @@ -138,13 +130,8 @@ void main() { reflected_light *= f_ao; vec3 glow = vec3(0); - if ((f_flags & FLAG_IS_BUILDING) > 0u && abs(f_norm.z) < 0.1) { - ivec3 wpos = ivec3((f_pos.xyz + focus_off.xyz) * 0.2); - if (((wpos.x & wpos.y & wpos.z) & 1) == 1) { - glow += vec3(1, 0.7, 0.3) * 2; - } else { - reflected_light += vec3(1, 0.7, 0.3) * 0.9; - } + if ((f_flags & FLAG_GLOW) > 0u) { + glow += vec3(1, 0.7, 0.3) * 2; } vec3 side_color = surf_color; diff --git a/assets/voxygen/shaders/lod-object-vert.glsl b/assets/voxygen/shaders/lod-object-vert.glsl index aebbef7b0f..586cc9ccb3 100644 --- a/assets/voxygen/shaders/lod-object-vert.glsl +++ b/assets/voxygen/shaders/lod-object-vert.glsl @@ -20,9 +20,13 @@ layout(location = 0) in vec3 v_pos; layout(location = 1) in vec3 v_norm; layout(location = 2) in vec3 v_col; -layout(location = 3) in vec3 inst_pos; -layout(location = 4) in uvec3 inst_col; -layout(location = 5) in uint inst_flags; +layout(location = 3) in uint v_flags; +layout(location = 4) in vec3 inst_pos; +layout(location = 5) in vec3 inst_col; +layout(location = 6) in uint inst_flags; + +const uint FLAG_INST_COLOR = 1; +const uint FLAG_INST_GLOW = 2; layout(location = 0) out vec3 f_pos; layout(location = 1) out vec3 f_norm; @@ -47,8 +51,13 @@ void main() { #endif f_norm = v_norm; - f_col = vec4(vec3(inst_col) * (1.0 / 255.0) * v_col * (hash(inst_pos.xyxy) * 0.4 + 0.6), 1.0); - f_flags = inst_flags; + + if ((v_flags & FLAG_INST_COLOR) > 0u) { + f_col = vec4(inst_col, 1.0); + } else { + f_col = vec4(v_col, 1.0); + } + f_flags = inst_flags | (v_flags & FLAG_INST_GLOW); gl_Position = all_mat * diff --git a/common/src/lod.rs b/common/src/lod.rs index ff5b145062..6c420de168 100644 --- a/common/src/lod.rs +++ b/common/src/lod.rs @@ -8,31 +8,38 @@ pub const ZONE_SIZE: u32 = 32; bitflags::bitflags! { #[derive(Debug, Clone, Copy, Serialize, Deserialize)] - pub struct Flags: u8 { + pub struct InstFlags: u8 { const SNOW_COVERED = 0b00000001; - const IS_BUILDING = 0b00000010; - const IS_GIANT_TREE = 0b00000100; + const GLOW = 0b00000010; } } #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize, EnumIter)] #[repr(u16)] pub enum ObjectKind { - Oak, + GenericTree, Pine, Dead, House, GiantTree, - MapleTree, - Cherry, - AutumnTree, + Mangrove, + Acacia, + Birch, + Redwood, + Baobab, + Frostpine, + Haniwa, + Desert, + Palm, + Arena, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Object { pub kind: ObjectKind, pub pos: Vec3, - pub flags: Flags, + pub flags: InstFlags, + pub color: Rgb, } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index bc609b402f..71e10de702 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -128,6 +128,7 @@ num_cpus = "1.0" inline_tweak = { workspace = true } itertools = { workspace = true } sha2 = { workspace = true } +bitflags = { workspace = true, features = ["serde"] } # Discord RPC discord-sdk = { version = "0.3.0", optional = true } diff --git a/voxygen/src/render/pipelines/lod_object.rs b/voxygen/src/render/pipelines/lod_object.rs index 8a357335cf..074d797477 100644 --- a/voxygen/src/render/pipelines/lod_object.rs +++ b/voxygen/src/render/pipelines/lod_object.rs @@ -1,5 +1,6 @@ use super::super::{AaMode, GlobalsLayouts, Vertex as VertexTrait}; use bytemuck::{Pod, Zeroable}; +use common::util::srgb_to_linear; use std::mem; use vek::*; @@ -9,20 +10,27 @@ pub struct Vertex { pos: [f32; 3], norm: [f32; 3], col: [f32; 3], + flags: u32, } impl Vertex { - pub fn new(pos: Vec3, norm: Vec3, col: Rgb) -> Self { + pub fn new( + pos: Vec3, + norm: Vec3, + col: Rgb, + flags: crate::scene::lod::VertexFlags, + ) -> Self { Self { pos: pos.into_array(), norm: norm.into_array(), col: col.into_array(), + flags: flags.bits() as u32, } } fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - const ATTRIBUTES: [wgpu::VertexAttribute; 3] = - wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32x3]; + const ATTRIBUTES: [wgpu::VertexAttribute; 4] = + wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32x3, 3 => Uint32]; wgpu::VertexBufferLayout { array_stride: Self::STRIDE, step_mode: wgpu::VertexStepMode::Vertex, @@ -40,24 +48,24 @@ impl VertexTrait for Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)] pub struct Instance { inst_pos: [f32; 3], - inst_col: [u8; 4], + inst_col: [f32; 3], flags: u32, } impl Instance { - pub fn new(inst_pos: Vec3, col: Rgb, flags: common::lod::Flags) -> Self { + pub fn new(inst_pos: Vec3, col: Rgb, flags: common::lod::InstFlags) -> Self { Self { inst_pos: inst_pos.into_array(), - inst_col: Rgba::new(col.r, col.g, col.b, 255).into_array(), + inst_col: srgb_to_linear(col.map(|c| c as f32 / 255.0)).into_array(), flags: flags.bits() as u32, } } fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { const ATTRIBUTES: [wgpu::VertexAttribute; 3] = wgpu::vertex_attr_array![ - 3 => Float32x3, - 4 => Uint8x4, - 5 => Uint32, + 4 => Float32x3, + 5 => Float32x3, + 6 => Uint32, ]; wgpu::VertexBufferLayout { array_stride: mem::size_of::() as wgpu::BufferAddress, diff --git a/voxygen/src/scene/lod.rs b/voxygen/src/scene/lod.rs index 80842cb53b..c895006829 100644 --- a/voxygen/src/scene/lod.rs +++ b/voxygen/src/scene/lod.rs @@ -12,7 +12,7 @@ use common::{ assets::{AssetExt, ObjAsset}, lod, spiral::Spiral2d, - util::srgba_to_linear, + util::{srgb_to_linear, srgba_to_linear}, weather, }; use hashbrown::HashMap; @@ -23,6 +23,16 @@ use vek::*; // For culling const MAX_OBJECT_RADIUS: i32 = 64; +bitflags::bitflags! { + #[derive(Debug, Clone, Copy)] + pub struct VertexFlags: u8 { + // Use instance not vertex colour + const INST_COLOR = 0b00000001; + // Glow! + const GLOW = 0b00000010; + } +} + struct ObjectGroup { instances: Instances, // None implies no instances @@ -63,7 +73,10 @@ impl Lod { data, zone_objects: HashMap::new(), object_data: [ - (lod::ObjectKind::Oak, make_lod_object("oak", renderer)), + ( + lod::ObjectKind::GenericTree, + make_lod_object("oak", renderer), + ), (lod::ObjectKind::Pine, make_lod_object("pine", renderer)), (lod::ObjectKind::Dead, make_lod_object("dead", renderer)), (lod::ObjectKind::House, make_lod_object("house", renderer)), @@ -71,15 +84,30 @@ impl Lod { lod::ObjectKind::GiantTree, make_lod_object("giant_tree", renderer), ), - (lod::ObjectKind::MapleTree, make_lod_object("oak", renderer)), - (lod::ObjectKind::Cherry, make_lod_object("oak", renderer)), ( - lod::ObjectKind::AutumnTree, - make_lod_object("oak", renderer), + lod::ObjectKind::Mangrove, + make_lod_object("mangrove", renderer), ), + (lod::ObjectKind::Acacia, make_lod_object("acacia", renderer)), + (lod::ObjectKind::Birch, make_lod_object("birch", renderer)), + ( + lod::ObjectKind::Redwood, + make_lod_object("redwood", renderer), + ), + (lod::ObjectKind::Baobab, make_lod_object("baobab", renderer)), + ( + lod::ObjectKind::Frostpine, + make_lod_object("frostpine", renderer), + ), + (lod::ObjectKind::Haniwa, make_lod_object("haniwa", renderer)), + ( + lod::ObjectKind::Desert, + make_lod_object("desert_houses", renderer), + ), + (lod::ObjectKind::Palm, make_lod_object("palm", renderer)), + (lod::ObjectKind::Arena, make_lod_object("arena", renderer)), ] - .into_iter() - .collect(), + .into(), } } @@ -127,21 +155,10 @@ impl Lod { z_range.start.min(pos.z as i32)..z_range.end.max(pos.z as i32) }, )); - // TODO: Put this somewhere more easily configurable, like a manifest - let color = match object.kind { - lod::ObjectKind::Pine => Rgb::new(0, 25, 12), - lod::ObjectKind::Oak => Rgb::new(10, 50, 5), - lod::ObjectKind::Dead => Rgb::new(20, 10, 2), - lod::ObjectKind::House => Rgb::new(20, 15, 0), - lod::ObjectKind::GiantTree => Rgb::new(8, 35, 5), - lod::ObjectKind::MapleTree => Rgb::new(20, 0, 5), - lod::ObjectKind::Cherry => Rgb::new(70, 40, 70), - lod::ObjectKind::AutumnTree => Rgb::new(60, 25, 0), - }; objects .entry(object.kind) .or_default() - .push(LodObjectInstance::new(pos, color, object.flags)); + .push(LodObjectInstance::new(pos, object.color, object.flags)); } objects .into_iter() @@ -257,17 +274,30 @@ fn make_lod_object(name: &str, renderer: &mut Renderer) -> Model().ok()); + let color = color + .next() + .and_then(|r| Some(Rgb::new(r, color.next()?, color.next()?))) + .unwrap_or(Rgb::broadcast(127)); + let color = srgb_to_linear(color.map(|c| (c as f32 / 255.0))); + let flags = match objname { + "InstCol" => VertexFlags::INST_COLOR, + "Glow" => VertexFlags::GLOW, + _ => VertexFlags::empty(), + }; + obj.triangles().map(move |vs| { + let [a, b, c] = vs.map(|v| { + LodObjectVertex::new( + v.position().into(), + v.normal().unwrap_or([0.0, 0.0, 1.0]).into(), + color, + flags, + ) + }); + Tri::new(a, b, c) + }) }) .collect(); renderer.create_model(&mesh).expect("Mesh was empty!") diff --git a/world/src/all.rs b/world/src/all.rs index e4fc6867f7..a1a6ed9918 100644 --- a/world/src/all.rs +++ b/world/src/all.rs @@ -1,7 +1,11 @@ -use crate::util::math::close; +use crate::{ + util::{math::close, sampler::Sampler}, + IndexRef, +}; +use common::terrain::structure::StructureBlock; use std::ops::Range; use strum::EnumIter; -use vek::Vec2; +use vek::*; #[derive(Copy, Clone, Debug, EnumIter)] pub enum ForestKind { @@ -126,6 +130,28 @@ impl ForestKind { } } + pub fn leaf_block(&self) -> StructureBlock { + match self { + ForestKind::Palm => StructureBlock::PalmLeavesOuter, + ForestKind::Acacia => StructureBlock::Acacia, + ForestKind::Baobab => StructureBlock::Baobab, + ForestKind::Oak => StructureBlock::TemperateLeaves, + ForestKind::Chestnut => StructureBlock::Chestnut, + ForestKind::Cedar => StructureBlock::PineLeaves, + ForestKind::Pine => StructureBlock::PineLeaves, + ForestKind::Redwood => StructureBlock::PineLeaves, + ForestKind::Birch => StructureBlock::TemperateLeaves, + ForestKind::Mangrove => StructureBlock::Mangrove, + ForestKind::Giant => StructureBlock::TemperateLeaves, + ForestKind::Swamp => StructureBlock::TemperateLeaves, + ForestKind::Frostpine => StructureBlock::FrostpineLeaves, + ForestKind::Dead => StructureBlock::TemperateLeaves, + ForestKind::Mapletree => StructureBlock::MapleLeaves, + ForestKind::Cherry => StructureBlock::CherryLeaves, + ForestKind::AutumnTree => StructureBlock::AutumnLeaves, + } + } + pub fn proclivity(&self, env: &Environment) -> f32 { self.ideal_proclivity() * close(env.humid, self.humid_range()) @@ -136,6 +162,30 @@ impl ForestKind { } } +pub fn leaf_color( + index: IndexRef, + seed: u32, + lerp: f32, + sblock: &StructureBlock, +) -> Option> { + let ranges = sblock + .elim_case_pure(&index.colors.block.structure_blocks) + .as_ref() + .map(Vec::as_slice) + .unwrap_or(&[]); + + ranges + .get(crate::util::RandomPerm::new(seed).get(seed) as usize % ranges.len()) + .map(|range| { + Rgb::::lerp( + Rgb::::from(range.start).map(f32::from), + Rgb::::from(range.end).map(f32::from), + lerp, + ) + .map(|e| e as u8) + }) +} + /// Not currently used with trees generated by the tree layer, needs to be /// reworked pub struct TreeAttr { diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index 233f83d8a6..9c0c031f0f 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -1,6 +1,6 @@ use crate::{ column::{ColumnGen, ColumnSample}, - util::{FastNoise, RandomField, RandomPerm, Sampler, SmallCache}, + util::{FastNoise, RandomField, Sampler, SmallCache}, IndexRef, CONFIG, }; use common::{ @@ -212,8 +212,7 @@ pub fn block_from_structure( ) -> Option<(Block, Option)> { let field = RandomField::new(structure_seed); - let lerp = ((field.get(Vec3::from(structure_pos)).rem_euclid(256)) as f32 / 255.0) * 0.8 - + ((field.get(pos + i32::MAX / 2).rem_euclid(256)) as f32 / 255.0) * 0.2; + let lerp = field.get_f32(Vec3::from(structure_pos)) * 0.8 + field.get_f32(pos) * 0.2; let block = match sblock { StructureBlock::None => None, @@ -284,49 +283,21 @@ pub fn block_from_structure( | StructureBlock::MapleLeaves | StructureBlock::CherryLeaves | StructureBlock::AutumnLeaves => { - let ranges = sblock - .elim_case_pure(&index.colors.block.structure_blocks) - .as_ref() - .map(Vec::as_slice) - .unwrap_or(&[]); - let range = if ranges.is_empty() { - None + if calendar.map_or(false, |c| c.is_event(CalendarEvent::Christmas)) + && field.chance(pos + structure_pos, 0.025) + { + Some(Block::new(BlockKind::GlowingWeakRock, Rgb::new(255, 0, 0))) + } else if calendar.map_or(false, |c| c.is_event(CalendarEvent::Halloween)) + && (*sblock == StructureBlock::TemperateLeaves + || *sblock == StructureBlock::Chestnut + || *sblock == StructureBlock::CherryLeaves) + { + crate::all::leaf_color(index, structure_seed, lerp, &StructureBlock::AutumnLeaves) + .map(|col| Block::new(BlockKind::Leaves, col)) } else { - ranges.get( - RandomPerm::new(structure_seed).get(structure_seed) as usize % ranges.len(), - ) - }; - - range.map(|range| { - if calendar.map_or(false, |c| c.is_event(CalendarEvent::Christmas)) - && field.chance(pos + structure_pos, 0.025) - { - Block::new(BlockKind::GlowingWeakRock, Rgb::new(255, 0, 0)) - } else if calendar.map_or(false, |c| c.is_event(CalendarEvent::Halloween)) - && *sblock != StructureBlock::PineLeaves - { - let (c0, c1) = match structure_seed % 6 { - 0 => (Rgb::new(165.0, 150.0, 11.0), Rgb::new(170.0, 165.0, 16.0)), - 1 | 2 => (Rgb::new(218.0, 53.0, 3.0), Rgb::new(226.0, 62.0, 5.0)), - _ => (Rgb::new(230.0, 120.0, 20.0), Rgb::new(242.0, 130.0, 25.0)), - }; - - Block::new( - BlockKind::Leaves, - Rgb::::lerp(c0, c1, lerp).map(|e| e as u8), - ) - } else { - Block::new( - BlockKind::Leaves, - Rgb::::lerp( - Rgb::::from(range.start).map(f32::from), - Rgb::::from(range.end).map(f32::from), - lerp, - ) - .map(|e| e as u8), - ) - } - }) + crate::all::leaf_color(index, structure_seed, lerp, sblock) + .map(|col| Block::new(BlockKind::Leaves, col)) + } }, StructureBlock::BirchWood => { let wpos = pos + structure_pos; diff --git a/world/src/layer/tree.rs b/world/src/layer/tree.rs index 55d5ae4d17..b068945853 100644 --- a/world/src/layer/tree.rs +++ b/world/src/layer/tree.rs @@ -69,12 +69,13 @@ pub fn apply_trees_to( #[allow(clippy::large_enum_variant)] enum TreeModel { Structure(Structure), - Procedural(ProceduralTree, StructureBlock), + Procedural(ProceduralTree), } struct Tree { pos: Vec3, model: TreeModel, + leaf_block: StructureBlock, seed: u32, units: (Vec2, Vec2), lights: bool, @@ -105,151 +106,103 @@ pub fn apply_trees_to( let models: AssetHandle<_> = match forest_kind { ForestKind::Oak if QUIRKY_RAND.chance(seed + 1, 1.0 / 16.0) => *OAK_STUMPS, ForestKind::Oak if QUIRKY_RAND.chance(seed + 2, 1.0 / 20.0) => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::apple(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::TemperateLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::apple(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Palm => *PALMS, ForestKind::Acacia => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::acacia(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::Acacia, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::acacia(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Baobab => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::baobab(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::Baobab, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::baobab(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Oak => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::oak(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::TemperateLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::oak(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Dead => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::dead(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::TemperateLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::dead(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Chestnut => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::chestnut(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::Chestnut, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::chestnut(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Pine => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::pine(&mut RandomPerm::new(seed), scale, calendar), - &mut RandomPerm::new(seed), - ), - StructureBlock::PineLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::pine(&mut RandomPerm::new(seed), scale, calendar), + &mut RandomPerm::new(seed), + )); }, ForestKind::Cedar => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::cedar(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::PineLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::cedar(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Redwood => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::redwood(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::PineLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::redwood(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Birch => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::birch(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::TemperateLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::birch(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Frostpine => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::frostpine(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::FrostpineLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::frostpine(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Mangrove => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::jungle(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::Mangrove, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::jungle(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Swamp => *SWAMP_TREES, ForestKind::Giant => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::giant(&mut RandomPerm::new(seed), scale, inhabited), - &mut RandomPerm::new(seed), - ), - StructureBlock::TemperateLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::giant(&mut RandomPerm::new(seed), scale, inhabited), + &mut RandomPerm::new(seed), + )); }, ForestKind::Mapletree => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::oak(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::MapleLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::oak(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::Cherry => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::cherry(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::CherryLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::cherry(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, ForestKind::AutumnTree => { - break 'model TreeModel::Procedural( - ProceduralTree::generate( - TreeConfig::autumn_tree(&mut RandomPerm::new(seed), scale), - &mut RandomPerm::new(seed), - ), - StructureBlock::AutumnLeaves, - ); + break 'model TreeModel::Procedural(ProceduralTree::generate( + TreeConfig::autumn_tree(&mut RandomPerm::new(seed), scale), + &mut RandomPerm::new(seed), + )); }, }; @@ -260,6 +213,7 @@ pub fn apply_trees_to( .clone(), ) }, + leaf_block: forest_kind.leaf_block(), seed, units: UNIT_CHOOSER.get(seed), lights: inhabited, @@ -269,7 +223,7 @@ pub fn apply_trees_to( for tree in trees { let bounds = match &tree.model { TreeModel::Structure(s) => s.get_bounds(), - TreeModel::Procedural(t, _) => t.get_bounds().map(|e| e as i32), + TreeModel::Procedural(t) => t.get_bounds().map(|e| e as i32), }; let rpos2d = (wpos2d - tree.pos.xy()) @@ -282,7 +236,7 @@ pub fn apply_trees_to( let hanging_sprites = match &tree.model { TreeModel::Structure(_) => [(0.0004, SpriteKind::Beehive)].as_ref(), - TreeModel::Procedural(t, _) => t.config.hanging_sprites, + TreeModel::Procedural(t) => t.config.hanging_sprites, }; let mut is_top = true; @@ -303,7 +257,7 @@ pub fn apply_trees_to( info.index(), if let Some(block) = match &tree.model { TreeModel::Structure(s) => s.get(model_pos).ok(), - TreeModel::Procedural(t, leaf_block) => Some( + TreeModel::Procedural(t) => Some( match t.is_branch_or_leaves_at(model_pos.map(|e| e as f32 + 0.5)) { (_, _, true, _) => { sblock = StructureBlock::Filled( @@ -314,7 +268,7 @@ pub fn apply_trees_to( }, (_, _, _, true) => &StructureBlock::None, (true, _, _, _) => &t.config.trunk_block, - (_, true, _, _) => leaf_block, + (_, true, _, _) => &tree.leaf_block, _ => &StructureBlock::None, }, ), @@ -578,20 +532,20 @@ impl TreeConfig { let log_scale = 1.0 + scale.log2().max(0.0); Self { - trunk_len: 9.0 * scale, - trunk_radius: 2.0 * scale, - branch_child_len: 0.9, - branch_child_radius: 0.75, + trunk_len: 45.0 * scale, + trunk_radius: 1.8 * scale, + branch_child_len: 0.4, + branch_child_radius: 0.6, branch_child_radius_lerp: true, - leaf_radius: 4.0 * log_scale..5.0 * log_scale, + leaf_radius: 2.0 * log_scale..2.5 * log_scale, leaf_radius_scaled: 0.0, - straightness: 0.4, - max_depth: 4, - splits: 1.75..2.0, - split_range: 0.75..1.5, - branch_len_bias: 0.0, - leaf_vertical_scale: 0.4, - proportionality: 0.0, + straightness: 0.3, + max_depth: 2, + splits: 16.0..18.0, + split_range: 0.2..1.2, + branch_len_bias: 0.7, + leaf_vertical_scale: 0.3, + proportionality: 0.7, inhabited: false, hanging_sprites: &[(0.00007, SpriteKind::Beehive)], trunk_block: StructureBlock::Filled(BlockKind::Wood, Rgb::new(110, 68, 65)), diff --git a/world/src/lib.rs b/world/src/lib.rs index cebc3b1e2c..f6d1ca23c2 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -608,7 +608,7 @@ impl World { // Add trees prof_span!(guard, "add trees"); - objects.append( + objects.extend( &mut self .sim() .get_area_trees(min_wpos, max_wpos) @@ -621,15 +621,16 @@ impl World { .filter_map(|(col, tree)| { Some(lod::Object { kind: match tree.forest_kind { - all::ForestKind::Oak => lod::ObjectKind::Oak, all::ForestKind::Dead => lod::ObjectKind::Dead, - all::ForestKind::Pine - | all::ForestKind::Frostpine - | all::ForestKind::Redwood => lod::ObjectKind::Pine, - all::ForestKind::Mapletree => lod::ObjectKind::MapleTree, - all::ForestKind::Cherry => lod::ObjectKind::Cherry, - all::ForestKind::AutumnTree => lod::ObjectKind::AutumnTree, - _ => lod::ObjectKind::Oak, + all::ForestKind::Pine => lod::ObjectKind::Pine, + all::ForestKind::Mangrove => lod::ObjectKind::Mangrove, + all::ForestKind::Acacia => lod::ObjectKind::Acacia, + all::ForestKind::Birch => lod::ObjectKind::Birch, + all::ForestKind::Redwood => lod::ObjectKind::Redwood, + all::ForestKind::Baobab => lod::ObjectKind::Baobab, + all::ForestKind::Frostpine => lod::ObjectKind::Frostpine, + all::ForestKind::Palm => lod::ObjectKind::Palm, + _ => lod::ObjectKind::GenericTree, }, pos: { let rpos = tree.pos - min_wpos; @@ -639,19 +640,26 @@ impl World { rpos.map(|e| e as i16).with_z(col.alt as i16) } }, - flags: lod::Flags::empty() + flags: lod::InstFlags::empty() | if col.snow_cover { - lod::Flags::SNOW_COVERED + lod::InstFlags::SNOW_COVERED } else { - lod::Flags::empty() + lod::InstFlags::empty() }, + color: { + let field = crate::util::RandomField::new(tree.seed); + let lerp = field.get_f32(Vec3::from(tree.pos)) * 0.8 + 0.1; + let sblock = tree.forest_kind.leaf_block(); + + crate::all::leaf_color(index, tree.seed, lerp, &sblock) + .unwrap_or(Rgb::black()) + }, }) - }) - .collect(), + }), ); drop(guard); - // Add buildings + // Add structures objects.extend( index .sites @@ -661,68 +669,55 @@ impl World { .map2(min_wpos.zip(max_wpos), |e, (min, max)| e >= min && e < max) .reduce_and() }) - .filter_map(|(_, site)| match &site.kind { - SiteKind::Refactor(site) => { - Some(site.plots().filter_map(|plot| match &plot.kind { - site2::plot::PlotKind::House(_) => Some(site.tile_wpos(plot.root_tile)), + .filter_map(|(_, site)| { + site.site2().map(|site| { + site.plots().filter_map(|plot| match &plot.kind { + site2::plot::PlotKind::House(h) => Some(( + site.tile_wpos(plot.root_tile), + h.roof_color(), + lod::ObjectKind::House, + )), + site2::plot::PlotKind::GiantTree(t) => Some(( + site.tile_wpos(plot.root_tile), + t.leaf_color(), + lod::ObjectKind::GiantTree, + )), + site2::plot::PlotKind::Haniwa(_) => Some(( + site.tile_wpos(plot.root_tile), + Rgb::black(), + lod::ObjectKind::Haniwa, + )), + site2::plot::PlotKind::DesertCityMultiPlot(_) => Some(( + site.tile_wpos(plot.root_tile), + Rgb::black(), + lod::ObjectKind::Desert, + )), + site2::plot::PlotKind::DesertCityArena(_) => Some(( + site.tile_wpos(plot.root_tile), + Rgb::black(), + lod::ObjectKind::Arena, + )), _ => None, - })) - }, - _ => None, + }) + }) }) .flatten() - .filter_map(|wpos2d| { + .filter_map(|(wpos2d, color, model)| { ColumnGen::new(self.sim()) .get((wpos2d, index, self.sim().calendar.as_ref())) - .zip(Some(wpos2d)) + .zip(Some((wpos2d, color, model))) }) - .map(|(col, wpos2d)| lod::Object { - kind: lod::ObjectKind::House, + .map(|(column, (wpos2d, color, model))| lod::Object { + kind: model, pos: (wpos2d - min_wpos) .map(|e| e as i16) .with_z(self.sim().get_alt_approx(wpos2d).unwrap_or(0.0) as i16), - flags: lod::Flags::IS_BUILDING - | if col.snow_cover { - lod::Flags::SNOW_COVERED - } else { - lod::Flags::empty() - }, - }), - ); - - // Add giant trees - objects.extend( - index - .sites - .iter() - .filter(|(_, site)| { - site.get_origin() - .map2(min_wpos.zip(max_wpos), |e, (min, max)| e >= min && e < max) - .reduce_and() - }) - .filter(|(_, site)| matches!(&site.kind, SiteKind::GiantTree(_))) - .filter_map(|(_, site)| { - let wpos2d = site.get_origin(); - let col = ColumnGen::new(self.sim()).get(( - wpos2d, - index, - self.sim().calendar.as_ref(), - ))?; - Some(lod::Object { - kind: lod::ObjectKind::GiantTree, - pos: { - (wpos2d - min_wpos) - .map(|e| e as i16) - .with_z(self.sim().get_alt_approx(wpos2d).unwrap_or(0.0) as i16) - }, - flags: lod::Flags::empty() - | lod::Flags::IS_GIANT_TREE - | if col.snow_cover { - lod::Flags::SNOW_COVERED - } else { - lod::Flags::empty() - }, - }) + flags: if column.snow_cover { + lod::InstFlags::SNOW_COVERED + } else { + lod::InstFlags::empty() + }, + color, }), ); diff --git a/world/src/site2/plot/giant_tree.rs b/world/src/site2/plot/giant_tree.rs index a600a9b88c..f2a049c7e6 100644 --- a/world/src/site2/plot/giant_tree.rs +++ b/world/src/site2/plot/giant_tree.rs @@ -80,6 +80,18 @@ impl GiantTree { None } } + + pub fn leaf_color(&self) -> Rgb { + 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); + Lerp::lerp( + dark, + light, + fast_noise.get((self.wpos.map(|e| e as f64) * 0.05) * 0.5 + 0.5), + ) + .map(|e| e as u8) + } } impl Structure for GiantTree { @@ -88,14 +100,7 @@ impl Structure for GiantTree { #[cfg_attr(feature = "be-dyn-lib", export_name = "render_gianttree")] fn render_inner(&self, _site: &Site, _land: &Land, painter: &Painter) { - 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_col = self.leaf_color(); let mut rng = rand::thread_rng(); self.tree.walk(|branch, parent| { let aabr = Aabr { @@ -122,10 +127,7 @@ impl Structure for GiantTree { parent.get_leaf_radius(), branch.get_leaf_radius(), ) - .fill(Fill::Block(Block::new( - BlockKind::Leaves, - leaf_col.map(|e| e as u8), - ))); + .fill(Fill::Block(Block::new(BlockKind::Leaves, leaf_col))); // Calculate direction of the branch let branch_start = branch.get_line().start; let branch_end = branch.get_line().end; diff --git a/world/src/site2/plot/house.rs b/world/src/site2/plot/house.rs index 4d126cad2f..3d0de1b59f 100644 --- a/world/src/site2/plot/house.rs +++ b/world/src/site2/plot/house.rs @@ -95,6 +95,8 @@ impl House { } pub fn z_range(&self) -> Range { self.alt..self.alt + self.levels as i32 * STOREY } + + pub fn roof_color(&self) -> Rgb { self.roof_color } } const STOREY: i32 = 5;