Merge branch 'treeco/lod-models' into 'master'

Overhaul of LOD models

See merge request veloren/veloren!4390
This commit is contained in:
Isse 2024-03-23 18:54:17 +00:00
commit e22cbab7ce
29 changed files with 383 additions and 335 deletions

View File

@ -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

1
Cargo.lock generated
View File

@ -7316,6 +7316,7 @@ version = "0.15.0"
dependencies = [
"assets_manager",
"backtrace",
"bitflags 2.5.0",
"bytemuck",
"chrono",
"chumsky",

BIN
assets/voxygen/lod/acacia.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/arena.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/baobab.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/birch.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/dead.obj (Stored with Git LFS)

Binary file not shown.

BIN
assets/voxygen/lod/desert_houses.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/frostpine.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/giant_tree.obj (Stored with Git LFS)

Binary file not shown.

BIN
assets/voxygen/lod/haniwa.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/house.obj (Stored with Git LFS)

Binary file not shown.

BIN
assets/voxygen/lod/mangrove.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/oak.obj (Stored with Git LFS)

Binary file not shown.

BIN
assets/voxygen/lod/palm.obj (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/lod/pine.obj (Stored with Git LFS)

Binary file not shown.

BIN
assets/voxygen/lod/redwood.obj (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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) {
if ((f_flags & FLAG_GLOW) > 0u) {
glow += vec3(1, 0.7, 0.3) * 2;
} else {
reflected_light += vec3(1, 0.7, 0.3) * 0.9;
}
}
vec3 side_color = surf_color;

View File

@ -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 *

View File

@ -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<i16>,
pub flags: Flags,
pub flags: InstFlags,
pub color: Rgb<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]

View File

@ -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 }

View File

@ -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<f32>, norm: Vec3<f32>, col: Rgb<f32>) -> Self {
pub fn new(
pos: Vec3<f32>,
norm: Vec3<f32>,
col: Rgb<f32>,
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<f32>, col: Rgb<u8>, flags: common::lod::Flags) -> Self {
pub fn new(inst_pos: Vec3<f32>, col: Rgb<u8>, 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::<Self>() as wgpu::BufferAddress,

View File

@ -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<LodObjectInstance>,
// 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,18 +274,31 @@ fn make_lod_object(name: &str, renderer: &mut Renderer) -> Model<LodObjectVertex
let mesh = model
.read()
.0
.triangles()
.map(|vs| {
.objects()
.flat_map(|(objname, obj)| {
let mut color = objname.split('_').filter_map(|x| x.parse::<u8>().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(),
Rgb::broadcast(1.0),
//v.color().unwrap_or([1.0; 3]).into(),
color,
flags,
)
});
Tri::new(a, b, c)
})
})
.collect();
renderer.create_model(&mesh).expect("Mesh was empty!")
}

View File

@ -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<Rgb<u8>> {
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::<f32>::lerp(
Rgb::<u8>::from(range.start).map(f32::from),
Rgb::<u8>::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 {

View File

@ -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<SpriteCfg>)> {
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
} 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))
Some(Block::new(BlockKind::GlowingWeakRock, Rgb::new(255, 0, 0)))
} else if calendar.map_or(false, |c| c.is_event(CalendarEvent::Halloween))
&& *sblock != StructureBlock::PineLeaves
&& (*sblock == StructureBlock::TemperateLeaves
|| *sblock == StructureBlock::Chestnut
|| *sblock == StructureBlock::CherryLeaves)
{
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::<f32>::lerp(c0, c1, lerp).map(|e| e as u8),
)
crate::all::leaf_color(index, structure_seed, lerp, &StructureBlock::AutumnLeaves)
.map(|col| Block::new(BlockKind::Leaves, col))
} else {
Block::new(
BlockKind::Leaves,
Rgb::<f32>::lerp(
Rgb::<u8>::from(range.start).map(f32::from),
Rgb::<u8>::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;

View File

@ -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<i32>,
model: TreeModel,
leaf_block: StructureBlock,
seed: u32,
units: (Vec2<i32>, Vec2<i32>),
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(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::apple(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::TemperateLeaves,
);
));
},
ForestKind::Palm => *PALMS,
ForestKind::Acacia => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::acacia(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::Acacia,
);
));
},
ForestKind::Baobab => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::baobab(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::Baobab,
);
));
},
ForestKind::Oak => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::oak(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::TemperateLeaves,
);
));
},
ForestKind::Dead => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::dead(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::TemperateLeaves,
);
));
},
ForestKind::Chestnut => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::chestnut(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::Chestnut,
);
));
},
ForestKind::Pine => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::pine(&mut RandomPerm::new(seed), scale, calendar),
&mut RandomPerm::new(seed),
),
StructureBlock::PineLeaves,
);
));
},
ForestKind::Cedar => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::cedar(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::PineLeaves,
);
));
},
ForestKind::Redwood => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::redwood(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::PineLeaves,
);
));
},
ForestKind::Birch => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::birch(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::TemperateLeaves,
);
));
},
ForestKind::Frostpine => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::frostpine(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::FrostpineLeaves,
);
));
},
ForestKind::Mangrove => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::jungle(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::Mangrove,
);
));
},
ForestKind::Swamp => *SWAMP_TREES,
ForestKind::Giant => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::giant(&mut RandomPerm::new(seed), scale, inhabited),
&mut RandomPerm::new(seed),
),
StructureBlock::TemperateLeaves,
);
));
},
ForestKind::Mapletree => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::oak(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::MapleLeaves,
);
));
},
ForestKind::Cherry => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::cherry(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::CherryLeaves,
);
));
},
ForestKind::AutumnTree => {
break 'model TreeModel::Procedural(
ProceduralTree::generate(
break 'model TreeModel::Procedural(ProceduralTree::generate(
TreeConfig::autumn_tree(&mut RandomPerm::new(seed), scale),
&mut RandomPerm::new(seed),
),
StructureBlock::AutumnLeaves,
);
));
},
};
@ -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)),

View File

@ -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)),
_ => None,
}))
},
.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,
})
})
})
.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
flags: if column.snow_cover {
lod::InstFlags::SNOW_COVERED
} else {
lod::Flags::empty()
lod::InstFlags::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()
},
})
color,
}),
);

View File

@ -80,6 +80,18 @@ impl GiantTree {
None
}
}
pub fn leaf_color(&self) -> Rgb<u8> {
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;

View File

@ -95,6 +95,8 @@ impl House {
}
pub fn z_range(&self) -> Range<i32> { self.alt..self.alt + self.levels as i32 * STOREY }
pub fn roof_color(&self) -> Rgb<u8> { self.roof_color }
}
const STOREY: i32 = 5;