feat(tools): render FGFoliage + data-driven foliage/grass colour on the base map (#246) - #284
Merged
Merged
Conversation
Satisfactory stores the bulk of world foliage (~71k components, ~3.3M instances — most trees plus all small foliage) in a custom FGFoliageInstancedSMC component that the renderer's scene collector never reads, so those trees are invisible on the map. This is the "other tree species" follow-up called out in base-map-renderer.md §6. Capture the reverse-engineered decode as an agreed spec before coding: - Register FGFoliageInstancedSMC as a HISM (CUE4Parse.UE4.Assets. ObjectTypeRegistry) so its per-instance transform buffer deserialises. - Use the FGFoliage-specific world-position formula: the km-scale cell offset lives on the owning InstancedFoliageActor's RootComponent (via AttachParent), NOT in TranslatedInstanceSpaceOrigin (which is a red herring equal to instance[0] and double-counts if added). - Render trees + large foliage only (drop grass/ground-cover) via the existing --flora include-filter and flora rasterisation. Slice plan and risks (perf, double-render, "large" threshold) included. Cross-referenced from CLAUDE.md and base-map-renderer.md §6. Refs #246 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Satisfactory keeps ~3.3M foliage instances (most trees + all small foliage) in a custom FGFoliageInstancedSMC component the scene collector never read, so the map was near-treeless. Decode and render it: - Register FGFoliageInstancedSMC as a HierarchicalInstancedSMC (CUE4Parse.UE4.Assets.ObjectTypeRegistry) so CUE4Parse deserialises its per-instance transform buffer. - Read it in SceneCollector.TryAddMesh: widen the component-name guard to "…InstancedSMC", and use the FGFoliage-specific world formula — the km-scale cell offset lives on the owning InstancedFoliageActor's RootComponent (via AttachParent), NOT TranslatedInstanceSpaceOrigin (which equals instance[0] and double-counts). Stock foliage keeps the origin+translation formula. - Trees & large only: the existing --flora include-filter (/Foliage/Trees/ + /Coral/) already classifies these; grass/ferns/small foliage fall through to null and are dropped, so the 3.3M never all rasterise. - Log placed-mesh + flora-instance counts (no silent truncation). - Tidy the `treeat` probe to resolve FGFoliage with the same formula. A downsample-4 render now places 150,786 flora instances (up from the sparse stock-only set); full forest canopy + coral fields appear. Tests green (54/54); build clean (TWAE). Design & decode rationale: docs/base-map-foliage-decode.md. Refs #246 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nder (#246) Slice 3 of the FGFoliage work — bushes, textures, double-render, and the default flora scope: - Default flora is now the FULL set: DefaultFloraFolders = ["/Environment/Foliage/", "/Environment/Bush/"] — Coral, all Trees, Grass, Flowers, /SmallFoliage/ (ferns/bushes/roots) plus the large bushes. A full-map render is ~2.6M instances in ~114s at full res (downsample 2); the FGFoliage decode is what makes that affordable. Small foliage reads as ground cover, not clutter. Narrow via --flora (e.g. "/Foliage/Trees/,/Environment/Bush/") for a trees-only map. - Fix HLOD double-render: skip components whose type contains "HLOD" in TryAddMesh. `probe florasrc` (new audit) showed FGFoliage is disjoint from stock foliage (no new double-plant), but HLOD proxies were 1:1 duplicates of 13 tree/rock meshes (e.g. SM_TitanTree_01: individual=73 AND HLOD=73) — planted twice. The real instances remain. - Per-mesh textures: confirmed already wired (MeshGeometryCache samples each section's material albedo via MaterialColourSampler; rasteriser uses it, palette fallback only for coral + DesertRock). A full render albedo-samples 192 of 248 unique meshes. Log now reports the count. Tests 54/54; build clean (TWAE). Docs updated (base-map-foliage-decode.md outcomes + base-map-renderer.md §6 filter/HLOD). Refs #246 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…246) Two new pieces for the dense-foliage half of the base map, committed on their own so they are tracked; the call sites that wire them in are still in progress. `GroundCover` accumulates dense ground-cover foliage (grass, ferns, small plants) as a per-cell colour and blends it into the terrain, so vegetated ground reads as its canopy colour from above. Grass geometry is far too thin to fill a map cell if z-buffered as canopy, so each instance instead stamps a small disc — a radius of 2 cells, since the game's grass carpet spreads between plants and a plant every few metres reads as continuous cover. Overlap drives the blend: three overlapping instances tint fully, so dense cover (Jungle Spires) greens completely while a stray desert plant barely shifts the sand. Void and unpainted cells are left alone. `GrassDataProbe` reads the baked FLandscapeComponentGrassData for the component covering a coordinate, reporting per grass type (Grass, Forest, Sand, …) the average, maximum and at-point density, then correlating the dominant type against vertex height. That is the game's real runtime-grass spawn, so it shows where green grass actually grows even where the weightmap says Sand. Refs #246. See docs/base-map-foliage-decode.md.
…ss + real albedo (#246) Colour the terrain and its cover from game data instead of guessed palettes, fixing the Jungle Spires (sandy tops that are grass in-game), Crater Lakes and Blue Crater biomes: - Baked Landscape-Grass overlay: read FLandscapeComponentGrassData (per-vertex, per-grass-type density baked into each LandscapeComponent) and tint the terrain with the grass carpet's colour where the game grows it. This is the authoritative ground-vegetation signal — it owns the landscape colour (Grass Fields/Northern Forest green, Red Bamboo red, Blue Crater blue-grey), no weightmap guessing. - Grass colour derived, not hardcoded: each grass type's colour = its primary grass mesh albedo x the material "Color Tint" param. - Real per-species albedo: MaterialColourSampler now prefers the material's PM_Diffuse / "Grass Albedo" param over the shared first texture (TX_Grass_01_BC), so blue crater grass reads blue, pink grass pink, etc. — a map-wide colour-fidelity fix. - Grass grows on the topmost object: the ground-cover foliage tint now colours grass-carpeted rock-mesh tops (the stacked-mesh spires) too, so Jungle Spires tops read green; and it no longer over-paints the landscape ground (the baked overlay owns that), fixing Crater Lakes. - Diagnostics: probe grassdata (baked grass density + height correlation) and richer terrainmat/matcolour material dumps. Coral stays its stylized palette colour by choice (its albedo is a dull green/brown; its real per-species emissive is orange/cyan, not the recognisable purple). Build clean (TWAE); tests 54/54. Refs #246 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 AI-generated PR
Renders Satisfactory's full world foliage on the #246 base map and makes the terrain/cover colours data-driven. The whole "other trees" + foliage-colour arc for the base-map renderer (host-only tool
tools/sf-map-renderer).What's in it
FGFoliageInstancedSMC(~3.3M instances: trees, bushes, grass, coral) viaObjectTypeRegistry+ the cell-offset transform (offset lives on the owningInstancedFoliageActor, notTranslatedInstanceSpaceOrigin). Spec:docs/base-map-foliage-decode.md.FLandscapeComponentGrassData(per-vertex, per-grass-type density) drives the ground grass colour, so vegetated biomes read correctly (Grass Fields/forests green, Red Bamboo red, Blue Crater blue-grey).Color Tint;MaterialColourSamplernow reads each mesh's real albedo (PM_Diffuse/Grass Albedo) instead of a shared placeholder texture (map-wide fidelity fix).Coral keeps its stylized palette colour by choice (its real emissive is orange/cyan per species, not the recognisable purple).
Verify
Refs #246