Render LUT Implementation Plan¶
Purpose¶
This is the progress tracker for reusable render LUT work.
Always update this file when changing LUT data contracts, LUT registry behavior, sprite palette references, LUT atlas upload, LUT shader sampling, RD LUT UI, map-local LUT behavior, or validation coverage.
Design context:
Current Decisions¶
- LUTs are reusable render data, not bird-specific data.
- The first supported LUT type is
grayscale-ramp. - One LUT is conceptually 1D: grayscale
0..255to RGB. - The runtime LUT atlas is
256xN, where each row is one 1D LUT. - Shared LUT definitions live in
assets/data/render_luts.json. - Sprite definitions reference LUTs by
palette.lutRefs; they do not embed color stops. - LUT refs support explicit IDs and fixed two-digit variant ranges.
- Variant IDs use
<family>.variant.<nn>, for exampleanimal.bird.dark.variant.00. - Two-digit variants cap one generated family at
00..99. - Generated variant families can derive from an explicit base LUT through
baseLutId; inline variant stops remain backward-tolerant fallback input. - Variant family defaults to the selected explicit LUT ID.
- LUT recoloring happens before existing sprite terrain lighting.
- LUT rows are presentation data only and do not affect gameplay truth.
Implemented¶
- Add
assets/data/render_luts.json. - Add
src/render/renderLutRegistry.js. - Normalize grayscale ramp color stops.
- Interpolate stops into 256-pixel RGBA rows.
- Generate fixed two-digit variant IDs.
- Support
variants[].baseLutIdso generated rows derive from explicit editable LUT stops. - Expand explicit ID refs and variant range refs.
- Load the shared LUT registry during startup.
- Add optional sprite
palette.mode = "grayscale-lut". - Add optional sprite
palette.lutRefs. - Add stable per-bird LUT row selection in
swarmAgentSpriteRuntime. - Attach the LUT atlas to the combined agent sprite snapshot.
- Extend
mapSpriteRenderervertex packing with optional palette row. - Upload the LUT atlas as a nearest-filtered texture.
- Sample LUT rows in the map-sprite fragment shader before lighting.
- Add bird metadata refs to
assets/data/agents/swarm_sprites.json. - Add focused tests for LUT interpolation, registry loading, range expansion, sprite metadata normalization, swarm palette row selection, and map-sprite vertex packing.
- Document the implemented architecture in
AI_CONTEXT.md. - Create the dedicated design doc.
- Link the design and plan docs from
docs/moc.md.
Phase 1: Contract Hardening¶
- Decide whether unknown LUT refs should fail startup or degrade silently.
- Decision: shipped startup-loaded sprite data fails startup on invalid LUT refs.
- Future optional map-local overrides may choose a softer policy if needed.
- Add a registry validation result listing missing refs per sprite.
- Add tests for missing explicit IDs and missing range members.
- Decide whether duplicate LUT IDs should warn or remain first-wins.
- Decision: first normalized ID wins for deterministic atlas rows, and the
registry exposes
duplicateIdsfor debug/validation tooling. - Add a lightweight debug snapshot with row IDs and row count.
Phase 2: Authoring And Preview¶
- Add pure preview helpers for rendering a LUT row to a canvas/image data.
- Add a compact RD readout under
RD > Sprites > LUT. - Show loaded LUT count and selected bird LUT ref count.
- Show a preview strip for one selected LUT row.
- Add a read-only row selector for resolved bird LUT rows.
- Add numeric stop editing only after preview/readout is proven useful.
- Superseded by the large editor; compact editing remains only for quick runtime-local checks.
- Add large editor overlay for explicit LUTs.
- Opened from
RD > Sprites > LUT. - Uses a dedicated explicit-LUT selector separate from agent/swarm consumers.
- Shows one selected 1D LUT row, not the full atlas.
- Add draggable stop handles after the data/edit lifecycle is stable.
- Handles are HTML controls over a large single-row preview canvas.
- Endpoints are locked to positions
0and255for the first editor slice. - Remove the large editor position slider once dragging is implemented.
- Add generated-variant preview rows for variant families based on the selected explicit LUT.
- Show variant-family settings next to the generated preview.
- Add runtime-local variant-family sliders for count, seed, position jitter, brightness jitter, and color jitter.
- Treat
count = 0as the no-variants state and keep variant controls available for every explicit LUT. - Default variant family IDs to the selected explicit LUT ID.
- Add selected-LUT usage debug readout for sprite metadata references.
- Split the large editor lower area into a ⅓ usage/debug box and a ⅔ variant preview/control box.
- Dock the gameplay large LUT editor directly against the RD panel and extend it to the right screen boundary.
- Reparent the large LUT editor overlay to
document.bodyso RD overflow containers cannot clip the docked editor.
Phase 3: Runtime Editing¶
- Add an explicit LUT runtime owner if editing becomes mutable.
src/render/renderLutRuntime.jsnow owns source-definition snapshots, resolved registry access, selected row state, ref resolution, rebuild, and preview image data.- Rebuild the
256xNatlas after RD edits. - Rebuild updates the runtime registry, RD preview/readout, and active sprite render LUT snapshot data.
- Rebuild base-linked generated variant rows from edited explicit LUT stops.
- Refresh affected render snapshots without broad renderer resets.
- Decide whether RD edits are dev-only or saveable.
- Decision: runtime LUT edits are saveable through a dedicated LUT editor action for global data or map-local Save All for map-local data.
- If saveable, define whether edits write to global data, map-local data, or a separate dev override file.
- Decision: both. The LUT editor has a global/map-local source scope.
Save Globalwritesassets/data/render_luts.json; map-local source scope is saved through map Save All asrender_luts.json. - Add save diagnostics and dirty-state handling.
- The editor shows save mode, target path, map folder, and write API availability.
- Draft edits and applied runtime-global edits are tracked separately.
- Closing or switching editable LUTs warns before discarding draft/runtime global-save state.
- Add explicit LUT create, rename, and delete controls.
- Add variant-family name editing and generated ID preview.
- Add
RD > Agents > Swarmbird LUT family/range assignment controls.
Phase 4: Map And Biome Overrides¶
- Decide whether maps can provide optional
render_luts.jsonsidecars. - Define merge order:
- global defaults
- map-local additions
- map-local overrides
- Keep startup/title errors visible if required map-local LUT references are invalid.
- Document Tauri packaging implications if map-local LUT files are added.
- Existing asset-copy packaging includes map-local sidecars under
assets/<mapName>/.
Phase 5: Selection Policies¶
- Stable random row selection for birds.
- Weighted row selection.
- Rare-row selection for unusual animal variants.
- Shipped proof:
animal.bird.rare.whiteis referenced withrare: true. - Biome/weather/scenario constrained selection.
- State-driven explicit row selection for future conditions such as wet, injured, diseased, winter, or faction variants.
- Add editable controls for variant-family count, seed, position jitter, brightness jitter, and color jitter.
- Avoid separate create/delete controls for variant families.
- Decision: every explicit LUT can edit a runtime-local family;
count = 0disables generated rows.
Phase 6: Additional Consumers¶
- Birds.
- Hawks.
- Ground animals.
- NPC clothing or faction markings.
- Player clothing/equipment experiments.
- Structure state visuals.
- UI/debug scalar ramps.
- Resource/knowledge overlay color ramps.
Phase 7: Deferred LUT Types¶
- Palette-index remap mode.
- Multi-channel mask recoloring mode.
- UV lookup animation/appearance mode.
- 3D color-grading LUTs.
- Material-output LUTs.
These are documented in docs/RENDER_LUT_DESIGN.md but are not part of the
current implementation slice.
Validation¶
Focused checks:
node --check src\render\renderLutRegistry.js
node --check src\gameplay\agentSpriteModel.js
node --check src\gameplay\swarmAgentSpriteRuntime.js
node --check src\render\mapSpriteRenderer.js
node --check src\main.js
node --test tests\renderLutRegistry.test.js tests\agentSpriteModel.test.js tests\agentSpriteDefinitionRegistry.test.js tests\swarmAgentSpriteRuntime.test.js tests\mapSpriteRenderer.test.js
Integration checks after render/main changes:
Current validation status:
- Focused syntax checks passed after initial implementation.
- Focused LUT/sprite tests passed after initial implementation.
- Full JS suite passed after initial implementation.
- Focused LUT/RD syntax and tests passed after base-linked variant preview slice.
- Full JS suite passed after base-linked variant preview slice.
- Focused LUT/RD/sprite tests passed after variant slider slice.
- Markdown lint is currently blocked by unrelated existing dirty docs:
docs/notes.md.
Manual Smoke Test¶
- Load default map.
- Enable swarm sprite mode.
- Confirm birds still animate and rotate.
- Confirm bird colors vary when LUT mode is active.
- Confirm hawks still render normally.
- Confirm player sprite still renders normally.
- Confirm structures still render below agents.
- Confirm sprite lighting still responds to sun/shadow/point lights.