makepad/libs/game/gen/examples/silhouette.rs
Admin 1c418480fe Arcade: procedural generation pipeline (libs/game/gen)
Seed-deterministic, baked at spawn, emitted straight into the 24-byte packed
vertex format. Two devices given the same seed produce byte-identical
geometry — which is what lets a forest replicate as (preset, seed, position)
tuples instead of mesh data.

- L-system plants: expansion + 3D turtle + skeleton, 8 species (oak, pine,
  palm, bush, fern, cactus, dead, grass)
- Surface nets (not marching cubes — fewer, better-shaped triangles at
  low-poly) for rocks, boulders, mushrooms, clouds, blobs
- Spline tracks with width, banking, curbs and rails, returning centreline
  frames that carry lap distance — so spawn points and checkpoints derive
  from the track instead of a second hand-written list
- Poisson-disk scatter with flatness/height rules
- Texture generator with CPU mip chains (backends never generate them)
- LRU cache keyed by FNV-1a over the full recipe with floats hashed by exact
  bits; -0.0 and 0.0 normalise together (identical geometry), 4.0 and
  4.000001 stay distinct. Meshes hand out as Rc so eviction cannot pull
  geometry out from under a frame mid-draw
- DrawGameFoliage: growth and wind as an OPT-IN shader variant, a sibling of
  DrawGameSkinned rather than a flag inside the shared shaders — wind costs
  ~20 vertex ALU and the cube shader draws most of the world. Growth and flex
  weights share one packed nibble pair, so both animations cost zero extra
  vertex bytes

A realistic forest — 150x150 m, 582 trees, 3 species, 6 seeds — generates in
1.70 ms with 470 KB resident: 6 generations and 576 cache hits.

Three bugs the unit tests had passed, found by writing an ASCII silhouette
probe because captures were out of scope: every species came out ~4x its
requested height (the test compared two sizes RELATIVELY, so a uniform
overshoot sailed through — now the finished skeleton is measured and rescaled,
and the test asserts absolute height for all 8 species at three sizes); palm
emitted no foliage at all because its L-system contained no leaf symbol; and
cactus sprawled sideways like a shrub. Pine also dropped from 4 iterations to
3 — 15032 -> 2504 triangles, 728 -> 68 us — because 15k triangles for one
background tree is indefensible.

Honest caveat: that is a silhouette judgement, not a rendered one. Shading,
leaf-card orientation and the wind/growth animation are visually unverified.
Script verbs are not wired yet — the crate is a library only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-03 09:22:03 +02:00

16 lines
765 B
Rust

use makepad_game_gen::*;
fn main(){
for (name,h) in [("oak",4.0f32),("pine",5.0),("palm",6.0),("cactus",3.0)]{
let m = tree(name, TreeParams{seed:2,height:h,..Default::default()});
let (w,hh)=(37usize,17usize);
let mut grid=vec![b' ';w*hh];
let sx=m.max.x-m.min.x; let sy=m.max.y-m.min.y;
for v in m.vertices.chunks_exact(6){
let x=((v[0]-m.min.x)/sx.max(1e-6)*(w-1) as f32) as usize;
let y=((v[1]-m.min.y)/sy.max(1e-6)*(hh-1) as f32) as usize;
grid[(hh-1-y.min(hh-1))*w+x.min(w-1)]=b'#';
}
println!("--- {name} h={h} ({} tris, {:.1}x{:.1}) ---",m.triangle_count(),sx,sy);
for r in 0..hh{ println!("{}", String::from_utf8_lossy(&grid[r*w..(r+1)*w])); }
}
}