The stylized tree's foliage was a stack of ellipsoid lobes (canopy) or loose
per-tip cards (cards): the first reads as a bunch of grapes, the second clumps
and leaves holes because every card is drawn independently.
Add a cluster foliage mode built the way stylized foliage is normally authored:
* leaf centres on a plane are blue noise (Poisson disk), so the distribution
has no clumps and no bare patches;
* only the leaves emit geometry, which is how a CPU mesh expresses
'leaves opaque, everything else transparent' without alpha sorting or
transparent overdraw;
* 8-10 such planes are placed around the cluster's Y axis with a small random
tilt and offset, plus a few near-horizontal planes that close the poles;
* vertex normals are transferred from the cluster sphere instead of the leaf's
own plane, so a bundle of flat cards still shades as a rounded mass;
* both windings are emitted, because the engine's mesh3d pipelines cull back
faces unless a Material opts into two-sided rasterization.
Cluster centres are picked from the existing foliage anchors in a shuffled order
under a Poisson separation test, so they cover the crown evenly, stay inside a
hard geometry budget, and the twigs visibly enter the foliage.
FoliageCluster is a standalone implementation unit with its own
FoliageClusterDesc so other recipes can reuse it, and its disk sampling goes
through the module's existing Bridson sampler (poissonDiskPoints) instead of a
second blue-noise implementation.
Also fix the mesh.tree schema, which advertised clusters and colonization as
leafMode/branchAlgorithm choices while the generator rejected both, and was
missing canopy.
Tests live in their own translation unit (the shared procgen.cpp is already
~4500 lines): property tests for the sampler, the sphere normal transfer and
two-sided emission, a case asserting every cluster knob is exposed in the
schema and actually changes the mesh, and an opt-in step-by-step render dump
(EVENGINE_TREE_STEPS_DIR) that draws each construction stage.
Verified with ctest -R 'mesh\.|poisson' (136/136).
Introduce examples/level-designer: a 3D level design tool assembled from the
existing composable editor, procgen, physics and animation components.
- Terrain is referenced rather than embedded. Levels either regenerate it from
sampler parameters, or point at a terrain asset on disk. Both encodings the
runtime persists are decodable: the chunked EVTR archive and the raw EVTRN
float32 heightfield written next to an eve.terrain/1 asset.
- Whitebox pieces come from the ProcgenKit recipe registry (75 prototype
pieces). A level stores only the recipe id plus parameter values, so the mesh,
renderable and play-mode collider are all derived and no geometry is
duplicated into the level file. Piece parameters are driven by the recipe's
own schema.
- Player spawn points are ordinary scene nodes carrying facing plus the
character and motion set play mode should start with.
- Play mode builds a runtime world from the same authored data (terrain height
field plus one static box per piece) and drives the player with the Motion
Matching controller, using capsule collision for movement and a third-person
follow camera. Authored data is never mutated by a play session.
Editor view control is an orbit camera around a pannable focus point: right
drag orbits, middle drag pans along the view's ground basis, Q/E zoom, and F
focuses the selection or frames the whole terrain. Play-mode view is the
third-person follow camera; camera yaw is also the locomotion basis, so mouse
look steers where WASD runs.
Add procgen terrain file decoding:
- TerrainFile.h/.cpp decode EVTR and EVTRN into a Heightmap plus spacing, height
range and format metadata. Failures return structured diagnostics
(not_found, parse_error, unsupported) and never a partially filled grid.
- procgen.loadTerrainFile(path, format) and procgen.loadTerrainBytes(bytes,
format) expose it to script through the existing owning-handle proxy path.
EVTR stores no metres-per-cell, so hasSpacing reports whether the decoded
spacing is authoritative.
Verification: examples/level-designer/smoke.nut drives the live engine over MCP
(56 checks: terrain sampling and snapping, whitebox placement and parameter
rebuild, spawn resolution, panel construction in every tool state, play-mode
assembly and landing on the height field, camera-relative locomotion, character
and motion set switching, level document round trip and rejection, asset
terrain loading and rebuild atomicity). The new decoder has a unit test with 58
assertions covering both encodings, magic mismatches, truncated payloads and
dimension/size disagreement.
Re-integrate the latest dev after it was fixed: dev's Run.cpp scriptError
breakage is resolved on the new dev head. Resolved merge conflicts by keeping
both the L-system / poissonDisk additions and dev's PCG/runtime-generation /
spatial-data additions in Procgen.h/.cpp, test/CMakeLists.txt, and
procgen.md.