Skip to content

feat(forest): green thin-instanced conifer field with ×1–×3 random sizes (FLO-482)#125

Merged
Fl0p merged 1 commit into
mainfrom
flo-482-forest-trees
Jun 21, 2026
Merged

feat(forest): green thin-instanced conifer field with ×1–×3 random sizes (FLO-482)#125
Fl0p merged 1 commit into
mainfrom
flo-482-forest-trees

Conversation

@Fl0p

@Fl0p Fl0p commented Jun 21, 2026

Copy link
Copy Markdown
Collaborator

What & why

Board ask (FLO-468 → FLO-482): «натыкать больше ёлок в лесу, рандом размер ×1–×3; покрасить хвою в зелёный без текстур, а то белые совсем странно».

The forest tree GLB is a single material-less mesh (renders white), and the streamed manifest's 12 tree placements all collapsed onto one shared cached root (the loader ref-counts one model per asset id) — so the forest only ever showed one white tree.

Changes

  • forestTreeField.ts — pure, seeded scatter (generateForestTreePlacements): 64 conifers in a non-grid sqrt-weighted ring (clearing radius 7 → outer 58), random yaw, uniform random scale ×1…×3. Deterministic (fixed mulberry32 seed, no Date.now()) so tests never flake.
  • forestTrees.ts — loads the GLB once, hard-facets it (v1.2 read) and paints it a flat matte green (no textures), then thin-instances it across the scatter — one draw call per submesh regardless of count, so the higher tree count is no perf regression. Fire-and-forget mount; forestTreeLoader: null skips it for headless tests.
  • Wired mountForestTreeField into createForestScene; removed the collapsing tree placements from FOREST_MANIFEST (asset id stays registered).
  • Docs: docs/guide/forest-zone.md "Conifer field" section.

Verification

  • npm run lint clean · npm test green (53 tests across the 4 touched suites) · npm run build ok.
  • In-engine screenshot (docs/verification/flo482-forest-conifer-field.png): a dense field of varied-size green conifers, not white, not a grid.

Co-Authored-By: Wayland wayland@agents.flopbut.local

🤖 Generated with Claude Code

…zes (FLO-482)

The forest tree GLB (forest-tree.glb) is a single material-less mesh, so it
rendered white, and the streamed manifest's 12 tree placements all collapsed
onto one shared cached root (the loader ref-counts one model per asset id) — so
the forest only ever showed a single white tree.

Replace that with a greened, thin-instanced conifer field:

- forestTreeField.ts — pure, seeded scatter (generateForestTreePlacements):
  64 trees in a non-grid sqrt-weighted ring (clearing radius 7 → outer 58),
  each with a random yaw and a uniform random scale in ×1…×3. Deterministic
  via a fixed seed (mulberry32 createRng) — no Date.now(), so tests never flake.
- forestTrees.ts — loads the GLB once, hard-facets it (v1.2) and paints it a
  flat matte green (no textures), then thin-instances the prototype across the
  scatter (one draw call per submesh regardless of count — no perf regression).
  Fire-and-forget mount mirrors survivorAvatar; forestTreeLoader:null skips it.
- Wire mountForestTreeField into createForestScene; remove the collapsing tree
  placements from FOREST_MANIFEST (asset id stays registered).
- Vitest: scatter count/clearing/scale-band/variance + foliage colour/material
  + mount load/dispose. Docs: forest-zone.md "Conifer field" section.

Verified: npm run lint clean, npm test green (53 tests across the 4 touched
suites), npm run build ok. In-engine screenshot in docs/verification confirms
a dense field of varied-size green conifers (not white, not a grid).

Co-Authored-By: Wayland <wayland@agents.flopbut.local>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Fl0p Fl0p merged commit b8a9970 into main Jun 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant