1use crate::components::*;
13use crate::hierarchy;
14use crate::mesh::{MeshData, Vertex};
15use crate::procedural::{signature, Rng};
16use crate::reflect::Field;
17use glam::{Quat, Vec3};
18use kvas_ecs::{Entity, World};
19use serde::{Deserialize, Serialize};
20use std::sync::Arc;
21
22crate::choice_enum! {
23 TreeStyle { Broadleaf, Conifer, Palm, Bush } default Broadleaf
25}
26
27#[derive(Clone, Serialize, Deserialize)]
29#[serde(default)]
30pub struct Tree {
31 pub style: TreeStyle,
33 pub height: f32,
35 pub trunk_radius: f32,
37 pub crookedness: f32,
39 pub levels: i32,
41 pub branches: i32,
43 pub branch_length: f32,
45 pub branch_angle: f32,
47 pub leaf_density: i32,
49 pub leaf_size: f32,
51 pub bark_color: [f32; 4],
53 pub leaf_color: [f32; 4],
55 pub leaf_color_2: [f32; 4],
57 pub wind: f32,
59 pub seed: i32,
61 #[serde(skip)]
62 #[doc(hidden)]
63 pub sig: u64,
64}
65
66impl Default for Tree {
67 fn default() -> Self {
68 Self {
69 style: TreeStyle::Broadleaf,
70 height: 5.5,
71 trunk_radius: 0.22,
72 crookedness: 0.35,
73 levels: 3,
74 branches: 4,
75 branch_length: 0.62,
76 branch_angle: 42.0,
77 leaf_density: 14,
78 leaf_size: 0.75,
79 bark_color: [0.29, 0.22, 0.16, 1.0],
80 leaf_color: [0.05, 0.14, 0.03, 1.0],
81 leaf_color_2: [0.16, 0.24, 0.04, 1.0],
82 wind: 1.0,
83 seed: 1,
84 sig: 0,
85 }
86 }
87}
88
89impl Component for Tree {
90 fn fields(&mut self) -> Vec<Field<'_>> {
91 vec![
92 Field::choice("style", &mut self.style),
93 Field::f32("height", &mut self.height),
94 Field::f32("trunk_radius", &mut self.trunk_radius),
95 Field::f32("crookedness", &mut self.crookedness),
96 Field::i32("levels", &mut self.levels),
97 Field::i32("branches", &mut self.branches),
98 Field::f32("branch_length", &mut self.branch_length),
99 Field::f32("branch_angle", &mut self.branch_angle),
100 Field::i32("leaf_density", &mut self.leaf_density),
101 Field::f32("leaf_size", &mut self.leaf_size),
102 Field::color("bark_color", &mut self.bark_color),
103 Field::color("leaf_color", &mut self.leaf_color),
104 Field::color("leaf_color_2", &mut self.leaf_color_2),
105 Field::f32("wind", &mut self.wind),
106 Field::i32("seed", &mut self.seed),
107 ]
108 }
109}
110
111#[derive(Clone, Copy, Default)]
113pub struct TreePart;
114
115const SIDES: usize = 9;
116
117struct Grow<'a> {
118 rng: Rng,
119 t: &'a Tree,
120 bark: MeshData,
121 tips: Vec<(Vec3, Vec3, f32)>,
123}
124
125fn any_perp(d: Vec3) -> Vec3 {
126 d.any_orthonormal_vector()
127}
128
129impl Grow<'_> {
130 fn r(&mut self) -> f32 {
131 self.rng.f32()
132 }
133
134 fn tube(&mut self, points: &[Vec3], radii: &[f32], v0: f32) {
136 let base = self.bark.vertices.len() as u32;
137 let mut v = v0;
138 let mut frame = any_perp((points[1] - points[0]).normalize_or_zero());
139 for (i, p) in points.iter().enumerate() {
140 let dir = if i + 1 < points.len() { points[i + 1] - *p } else { *p - points[i - 1] }.normalize_or_zero();
141 frame = (frame - dir * frame.dot(dir)).normalize_or(any_perp(dir));
143 let side = dir.cross(frame);
144 if i > 0 {
145 v += (points[i] - points[i - 1]).length() / (radii[i].max(0.01) * 6.0);
146 }
147 for k in 0..=SIDES {
148 let a = k as f32 / SIDES as f32 * std::f32::consts::TAU;
149 let n = frame * a.cos() + side * a.sin();
150 self.bark.vertices.push(Vertex { pos: (*p + n * radii[i]).into(), normal: n.into(), uv: [k as f32 / SIDES as f32, v] });
151 }
152 }
153 let ring = (SIDES + 1) as u32;
154 for i in 0..points.len() as u32 - 1 {
155 for k in 0..SIDES as u32 {
156 let (a, b, c, d) = (base + i * ring + k, base + i * ring + k + 1, base + (i + 1) * ring + k, base + (i + 1) * ring + k + 1);
157 self.bark.indices.extend([a, c, b, b, c, d]);
158 }
159 }
160 }
161
162 fn branch(&mut self, start: Vec3, dir: Vec3, length: f32, radius: f32, level: i32, v0: f32) {
164 let t = self.t;
165 let segments = if level == t.levels.max(1) { 7 } else { 4 };
166 let mut points = vec![start];
167 let mut radii = vec![radius];
168 let mut d = dir;
169 let mut p = start;
170 let seg = length / segments as f32;
171 let bend = t.crookedness * 0.45;
172 for i in 1..=segments {
173 let wander = Vec3::new(self.r() - 0.5, (self.r() - 0.5) * 0.4, self.r() - 0.5) * bend;
174 let tropism = match t.style {
176 TreeStyle::Conifer if level < t.levels => Vec3::NEG_Y * 0.08,
177 TreeStyle::Palm => Vec3::Y * 0.04 + Vec3::new(0.06, 0.0, 0.0) * (i as f32 / segments as f32),
178 _ => Vec3::Y * 0.05,
179 };
180 d = (d + wander + tropism).normalize();
181 p += d * seg;
182 points.push(p);
183 let k = i as f32 / segments as f32;
184 radii.push((radius * (1.0 - 0.72 * k)).max(0.008));
185 }
186 self.tube(&points, &radii, v0);
187 if level <= 0 {
188 self.tips.push((p, d, length));
189 let mid = points[points.len() / 2];
191 self.tips.push((mid, d, length * 0.8));
192 return;
193 }
194 let children = t.branches.clamp(1, 8);
195 let angle = t.branch_angle.to_radians();
196 let mut twist = self.r() * std::f32::consts::TAU;
197 for c in 0..children {
198 let along = match t.style {
199 TreeStyle::Conifer if level == t.levels => 0.25 + 0.7 * (c as f32 + self.r() * 0.5) / children as f32,
200 _ => 0.35 + 0.6 * (c as f32 + self.r()) / children as f32,
201 };
202 let idx = ((along * segments as f32) as usize).min(segments);
203 let at = points[idx];
204 let r_at = radii[idx];
205 twist += 2.399_963; let axis = (Quat::from_axis_angle(d, twist) * any_perp(d)).normalize();
207 let child_dir = (Quat::from_axis_angle(axis, angle * (0.8 + 0.4 * self.r())) * d).normalize();
208 let mut child_len = length * t.branch_length * (0.85 + 0.3 * self.r());
209 if t.style == TreeStyle::Conifer && level == t.levels {
210 child_len = t.height * 0.45 * (1.0 - along) * (0.85 + 0.3 * self.r());
212 }
213 self.branch(at, child_dir, child_len, r_at * 0.62, level - 1, v0);
214 }
215 }
216}
217
218pub fn build(t: &Tree) -> (MeshData, MeshData) {
220 let mut g = Grow { rng: Rng::new(t.seed as u64 ^ 0x7ee5), t, bark: MeshData::default(), tips: Vec::new() };
221 let levels = t.levels.clamp(1, 4);
222 let height = t.height.max(0.3);
223 match t.style {
224 TreeStyle::Bush => {
225 for i in 0..5 {
226 let a = i as f32 / 5.0 * std::f32::consts::TAU + g.r();
227 let dir = Vec3::new(a.cos() * 0.6, 1.0, a.sin() * 0.6).normalize();
228 g.branch(Vec3::ZERO, dir, height * 0.6, t.trunk_radius * 0.5, (levels - 1).max(1), 0.0);
229 }
230 }
231 TreeStyle::Palm => {
232 let tree = Tree { crookedness: t.crookedness * 0.5, ..t.clone() };
234 let mut p = Grow { rng: Rng::new(t.seed as u64 ^ 0x9a1), t: &tree, bark: MeshData::default(), tips: Vec::new() };
235 p.branch(Vec3::ZERO, Vec3::Y, height, t.trunk_radius, 0, 0.0);
236 let (top, _, _) = p.tips[0];
237 g.bark = p.bark;
238 for i in 0..9 {
239 let a = i as f32 / 9.0 * std::f32::consts::TAU + g.r() * 0.3;
240 let out = Vec3::new(a.cos(), 0.0, a.sin());
241 for s in 1..=6 {
242 let k = s as f32 / 6.0;
243 let pos = top + out * (k * height * 0.38) + Vec3::Y * (0.35 * k - 0.9 * k * k) * height * 0.3;
244 g.tips.push((pos, out, height * 0.1));
245 }
246 }
247 }
248 _ => {
249 let trunk_levels = levels;
250 g.branch(Vec3::ZERO, Vec3::Y, height, t.trunk_radius, trunk_levels, 0.0);
251 }
252 }
253
254 let mut leaves = MeshData::default();
257 let (mut lo, mut hi) = (Vec3::splat(f32::MAX), Vec3::splat(f32::MIN));
258 for (p, _, _) in &g.tips {
259 lo = lo.min(*p);
260 hi = hi.max(*p);
261 }
262 let centre = (lo + hi) * 0.5;
263 let conifer = t.style == TreeStyle::Conifer;
264 let per = t.leaf_density.clamp(1, 64) as usize;
265 let tips = g.tips.clone();
266 for (p, dir, len) in tips {
267 let spread = (len * 0.35).clamp(0.15, 1.2) * if conifer { 0.6 } else { 1.0 };
268 for _ in 0..per {
269 let offset = Vec3::new(g.r() - 0.5, g.r() - 0.5, g.r() - 0.5) * 2.0 * spread + dir * g.r() * spread * 0.5;
270 let at = p + offset;
271 let size = t.leaf_size * (0.7 + 0.6 * g.r()) * if conifer { 0.8 } else { 1.0 };
272 let facing = Vec3::new(g.r() - 0.5, g.r() - 0.5, g.r() - 0.5).normalize_or(Vec3::Y);
273 let u = any_perp(facing);
274 let v = facing.cross(u);
275 let (u, v) = if conifer { (dir * size * 0.9, v * size * 0.55) } else { (u * size * 0.5, v * size * 0.5) };
276 let mut n = (at - centre).normalize_or(Vec3::Y);
277 n = (n + Vec3::Y * 0.35).normalize();
278 let tint = (g.r() * 8.0).floor();
279 let base = leaves.vertices.len() as u32;
280 for (cx, cy) in [(-1.0f32, -1.0f32), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)] {
281 let pos = at + u * cx + v * cy;
282 leaves.vertices.push(Vertex { pos: pos.into(), normal: n.into(), uv: [(cx * 0.5 + 0.5) + tint, cy * 0.5 + 0.5] });
283 }
284 leaves.indices.extend([base, base + 1, base + 2, base, base + 2, base + 3]);
285 leaves.indices.extend([base + 2, base + 1, base, base + 3, base + 2, base]);
286 }
287 }
288 (g.bark, leaves)
289}
290
291pub fn update(world: &mut World) {
293 let orphans: Vec<Entity> = world
294 .entities_with::<TreePart>()
295 .into_iter()
296 .filter(|&c| hierarchy::parent_of(world, c).is_none_or(|p| !world.is_alive(p) || !world.has::<Tree>(p)))
297 .collect();
298 for c in orphans {
299 world.despawn(c);
300 }
301 for e in world.entities_with::<Tree>() {
302 let t = world.get::<Tree>(e).unwrap().clone();
303 let sig = signature(&(t.style, t.height.to_bits(), t.trunk_radius.to_bits(), t.crookedness.to_bits(), t.levels, t.branches, t.branch_length.to_bits(), t.branch_angle.to_bits(), t.leaf_density, t.leaf_size.to_bits(), t.seed));
304 let parts: Vec<Entity> = hierarchy::children_all(world, e).into_iter().filter(|&c| world.has::<TreePart>(c)).collect();
305 if t.sig != sig || parts.len() != 2 {
306 for c in &parts {
307 world.despawn(*c);
308 }
309 let (bark, leaves) = build(&t);
310 for (name, mesh) in [("Bark", bark), ("Leaves", leaves)] {
311 let c = world.spawn();
312 world.insert(c, Name(name.into()));
313 world.insert(c, Transform::default());
314 world.insert(c, Parent(e));
315 world.insert(c, Transient);
316 world.insert(c, TreePart);
317 world.insert(c, DynamicMesh { data: Arc::new(mesh), version: sig });
318 world.insert(c, MeshRenderer::default());
319 }
320 world.get_mut::<Tree>(e).unwrap().sig = sig;
321 }
322 for c in hierarchy::children_all(world, e) {
324 let is_leaves = world.get::<Name>(c).is_some_and(|n| n.0 == "Leaves");
325 if let Some(m) = world.get_mut::<MeshRenderer>(c) {
326 if is_leaves {
327 m.shader = "builtin:foliage".into();
328 m.color = t.leaf_color;
329 m.user = [t.wind, if t.style == TreeStyle::Conifer { 1.0 } else if t.style == TreeStyle::Palm { 2.0 } else { 0.0 }, 0.0, 0.0, t.leaf_color_2[0], t.leaf_color_2[1], t.leaf_color_2[2], 0.0];
330 m.cast_shadows = true;
331 m.roughness = 0.8;
332 } else {
333 m.shader = "builtin:bark".into();
334 m.color = t.bark_color;
335 m.user = [t.wind, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
336 m.cast_shadows = true;
337 m.roughness = 0.95;
338 }
339 }
340 }
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use crate::scene::Scene;
348
349 #[test]
350 fn every_style_grows_bark_and_leaves_deterministically() {
351 for style in [TreeStyle::Broadleaf, TreeStyle::Conifer, TreeStyle::Palm, TreeStyle::Bush] {
352 let t = Tree { style, ..Default::default() };
353 let (bark, leaves) = build(&t);
354 assert!(bark.indices.len() > 100 && leaves.indices.len() > 100, "{style:?}");
355 assert!(bark.validate().is_ok() && leaves.validate().is_ok());
356 assert_eq!(build(&t).1.vertices[5].pos, leaves.vertices[5].pos);
357 let (_, max) = bark.bounds();
358 assert!(max.y > t.height * 0.5, "{style:?} reaches up");
359 }
360 }
361
362 #[test]
363 fn parts_are_transient_and_rebuilt_on_change() {
364 let mut scene = Scene::default();
365 let e = hierarchy::spawn_named(&mut scene.world, "Oak");
366 scene.world.insert(e, Tree::default());
367 update(&mut scene.world);
368 let parts = hierarchy::children_all(&scene.world, e);
369 assert_eq!(parts.len(), 2);
370 assert!(hierarchy::children(&scene.world, e).is_empty(), "hidden from the hierarchy");
371 scene.world.get_mut::<Tree>(e).unwrap().wind = 3.0;
372 update(&mut scene.world);
373 assert_eq!(hierarchy::children_all(&scene.world, e), parts, "appearance changes keep the meshes");
374 scene.world.get_mut::<Tree>(e).unwrap().seed = 9;
375 update(&mut scene.world);
376 assert_ne!(hierarchy::children_all(&scene.world, e), parts);
377 }
378}