Review round (Hoshe + Tyre, both REQUEST_CHANGES) fully addressed: palette.md's glass carve-out re-keyed from retired never-shipped tokens (precision_glass/smart_facade) to the ratified glass_curtain_wall (wall) + industrial_glazing (facade); the toon/PBR treatment promoted from a docs-only ruling to D-257 (architecture, cross-refs D-235/D-244/D-043/ D-044/D-033) with palette.md §2 now citing it as authority; D-149 + D-257 added to decision_refs; manifest.json _comment gains the explicit underscore-prefix loader skip contract; master pipeline table Visual row Stub -> Active; conventions.md mask pointer Section 3 -> 5; pre-existing broken D-066 links in the mood-board transcript repaired (leave-cleaner). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
26 KiB
title, description, type, status, decision_refs, author, updated
| title | description | type | status | decision_refs | author | updated | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Visual Palette | Phase-4 visual style bible — camera, entity colours, environmental neutrality, D-235 material register, toon/PBR rendering treatment | design | active |
|
Araminta | 2026-07-25 |
Visual Palette
Status: Active — Phase-4 style bible (T-1052). This document is the current source of truth for rendered-look art direction. It supersedes the framing (not every individual data point) of the two documents listed in Superseded documents below.
1. Camera
Two camera records exist and apply to different surfaces — this was previously conflated in this document; corrected here.
| Camera | Angle | Where it applies | Status |
|---|---|---|---|
| D-148 | 30° low-angle Camera3D tilt (−30° from horizontal in the confirmed preset convention — see D-148's 2026-07-06 editorial note), 45° static map rotation |
Gameplay — the default in-world camera, Phase 5+ | Active, confirmed default |
| D-019 | −72.5° from horizontal (midpoint of the old "15–20° from vertical" sprite-tilt range) | Offline renderer only — the retired 2D-sprite production pipeline (renderer/, now repurposed per D-244 as the flat-2D-artwork generator; see renderer/README.md) |
Superseded for gameplay by D-148; survives only as an offline-render setting |
Why both survive: D-148 replaced D-019 as the gameplay camera when the project pivoted to live 3D rendering (D-149) and then to fully 3D objects (D-244) — there are no more pre-rendered sprites to view "at the angle" during play. D-019's −72.5° Camera3D setting doesn't disappear, though: it is still the correct setting for the offline render rig that produces flat 2D artwork (paintings, flags, billboards, signage — the D-244(b) content class), because that pipeline still renders a 3D scene down to a flat texture and needs a camera angle to do it. Two different cameras, two different jobs: one drives what the player sees, the other bakes a flat image asset.
Do not describe D-019's angle as "the gameplay camera" anywhere — that sentence is what this document is correcting.
2. Rendering Treatment — Toon vs. PBR
Authority: D-257 (environment props share the character toon shading treatment; minimal-PBR carve-out for glazing). This section is the practical guidance derived from that record — the binding decision and its full rationale live in D-257, not here.
Environment props and furniture share the character toon treatment. One shader family, one look, across the whole in-world scene — with a single, narrow, explicitly-named exception for glass and polished/mirror-finish metal, which get a distinct minimal-PBR material rather than being forced through the flat toon response.
2.1 What "sharing the toon treatment" means concretely
- Environment
.glbmodels use the same shader family as characters:toon.gdshader(flat lit/shadow-band ALBEDO, no PBR lighting response) ortoon_masked.gdshader(recolor-mask variant, for props that need runtime tint — e.g. a favorite-color object, a faction-liveried crate) where a tintable variant is needed.outline.gdshader(inverted-hull) applies to entities and to any prop the visual hierarchy calls out for emphasis (see §4 tie-in below) — not universally to every static prop, matching the existing entity > object > structure outline-weight hierarchy. - glb-gen's postprocess convention of forcing
roughness=1.0/specular=0.0"for toon compatibility" (postprocess_glb.py::setup_materials()) is correct and should stay the default for furniture, crates, signage, street furniture, wall/roof/facade instances — i.e. the D-235 ObjectTag vocabulary's bulk. These are flat-lit like characters; PBR specular highlights on a stucco wall or a wooden table would read as wet/glossy and contradict the toon language players already learn from characters. - Exception — glass and polished/mirror metal: the ratified D-235 glazing
tokens —
glass_curtain_wall(wall axis) andindustrial_glazing(facade axis) — and any deliberately reflective/polished metal prop (chrome fixture, mirror, glossy display case) get a separate minimal-PBR material:roughnessin the 0.05–0.2 range and non-zerospecular/metallicas appropriate, layered so the toon shadow-band lighting model still governs the base surface response but a specular highlight and partial reflection are allowed through. This is not a full PBR material (no environment-map reflections, no fresnel-driven rim lighting stack) — it is "toon base + a controlled specular term," the smallest change that makes glass/chrome read as glass/chrome instead of matte plastic.
2.2 Rationale
- Visual grammar principle (mine, standing): consistency compounds. A station built from one shader family reads as one coherent object, the way a real Rimworld or Hades scene does. Mixing a toon character against a PBR-lit room breaks the "one world" read the moment the player looks at their own hands next to a table.
- The postprocess default already assumes this.
setup_materials()'s comment ("for toon compatibility") was written for the character pipeline but Trellis is the same generator for environment.glbs — there was never a second postprocess path. Diverging environment props onto full PBR would mean either forking the postprocess script or hand-overriding every environment asset's material after generation. Sharing the treatment means the existing pipeline is already correct and needs no new work. - The exception is functionally forced, not a taste call. A flat-lit
roughness=1.0glass pane doesn't read as glass — it reads as a frosted grey panel, which actively misleads the player about sightlines (can I see through this?) in a game whose core mechanic is occlusion-based perception. Where "can the player see through/into this" is gameplay-load-bearing (glass_curtain_wall,industrial_glazing, shop windows, interior partitions), the material needs to look transparent/reflective enough to answer that question at a glance. This is a narrow, named carve-out — it does not reopen "should props be PBR," it closes the one case where toon-only actively lies to the player. - Scale check: this holds at boxes-with-labels (a labelled box has no material response either way, so the ruling is inert today) and at full art fidelity (the shader assignment is a material-slot choice per asset, not a structural decision that needs revisiting later).
2.3 What this does NOT change
- D-235's material/token vocabulary (§3) is unaffected — this ruling is about shader/material response, not which WallMaterial/RoofForm tokens exist.
- The character shader files (
toon.gdshader,toon_masked.gdshader,toon_garment.gdshader,outline.gdshader) are not modified by this ruling — environment props reuse them as-is or via a near-identical environment-scoped shader variant if slot/uniform needs differ (implementation detail for whoever writes the first environment shader, not an art-direction fork).
3. Color / Material Register — D-235 ObjectTag Vocabulary
D-235 (amended 2026-07-07, T-995) ratified the logical token vocabulary —
wiki/economics/object_tag_vocabulary.toml, 4 axes (wall/roof/facade/street),
32 specific tags + 4 generic fallback parents. D-235's own Implementation note
explicitly parks the rendered look of those tokens for "Phase 5+ and
post-launch" art production. This section is that pre-specification: not new
tokens, but the register each shipped tag should render into once art exists,
so asset generation has a target from day one instead of drifting per-artist.
How to read this table: "Hue range" is a rendered-look target for the
BaseColor map's dominant tone, expressed relative to D-235's existing rule
that color is template-bounded (a cultural palette cue, seed-selected within
range, "always within the template's register") — this section does not
override that; it gives the physically-plausible hue family each material
naturally sits in, which the template's seed then picks within. "Weathering
baseline" describes how the D-217 condition bands (Intact/Worn/Cracked/Broken,
driven by prosperity_score) should visually read for that material — i.e.
what "Worn" looks like on brick vs. on corrugated metal.
3.1 Wall materials
| Tag | Hue range | Grain / texture character | Weathering baseline (D-217 bands) |
|---|---|---|---|
concrete_wall |
Neutral grey, warm-leaning (#8a8680–#6e6a64) — never blue-grey/cool-grey |
Fine aggregate speckle, subtle formwork seams | Intact: even matte. Worn: rain-streak staining, hairline cracks. Cracked: spalling at edges, exposed aggregate. Broken: structural cracking, rebar-stain bleed. |
steel_frame |
Dark warm charcoal (#3a3632) frame, infill panel per adjacent tag |
Visible bolted/riveted joints, slight surface oxidation tint even when "new" | Intact: factory-primer flat. Worn: surface rust bloom at joints/edges only. Cracked: running rust streaks below joints. Broken: structural rust-through, buckling. |
brick_wall |
Warm terracotta-to-ochre (#9c5a3c–#c48a52), never cool/pink brick |
Visible coursing + mortar joint shadow, slight color variance per brick (batch variation) | Intact: crisp mortar lines. Worn: mortar erosion, efflorescence (white mineral bloom). Cracked: spalled brick faces, missing mortar. Broken: collapsed coursing, exposed cavity. |
rendered_wall |
Whatever the template's color_range picks — this tag is a finish, not a hue driver; render surface is smooth and matte regardless of picked hue | Smooth, minimal grain, occasional hairline render-crack pattern at Worn+ | Intact: clean flat. Worn: surface staining/streaking under sills and joints. Cracked: render cracking, patches of exposed substrate. Broken: large render loss, substrate fully exposed. |
stone_wall |
Warm grey-tan (#a89e8c–#8c8172) — explicitly not cool grey; this is the D-235 fallback-graph's flagship "gets it wrong if literal" case |
Visible coursing, natural stone color variance tile-to-tile, tool-marked or rough-cut surface depending on template | Intact: clean-cut edges. Worn: lichen/mineral staining in shadowed recesses. Cracked: mortar loss between courses, chipped arrises. Broken: displaced/missing stone units. |
timber_wall |
Warm mid-brown (#7a5c3e–#9c7a52), never grey-driftwood unless template explicitly calls for weathered/coastal register |
Visible grain direction, board/plank joint lines | Intact: sealed/painted finish. Worn: graying at exposed edges, finish wear. Cracked: splitting along grain, missing sealant. Broken: rot, structural failure at joints. |
stucco_wall |
Warm off-white to sand (#d8cfc0–#c4b89e) — whitewashed per the tag description, not pure white |
Smooth, slight hand-applied texture variance | Intact: clean whitewash. Worn: staining streaks below any protrusion. Cracked: hairline cracking, patch-color mismatch. Broken: large loss exposing substrate. |
glass_curtain_wall |
Neutral-to-cool tint on the glass itself (#c8d4dc @ low opacity) — see §2.1 PBR exception, this tag needs the specular/transparency carve-out to read correctly |
Minimal — reflection/transparency IS the surface character | Intact: clean, full reflectivity. Worn: dust film, reduced reflectivity. Cracked: visible cracked panes, some opacity/frosting. Broken: missing panes, boarded sections. |
composite_panel |
Whatever the template picks — this is a prefab finish tag like rendered_wall, hue is template-driven, not material-driven |
Flat, uniform, visible panel-seam grid (regular rhythm, unlike masonry coursing) | Intact: crisp seams, clean finish. Worn: seam staining, minor panel discoloration. Cracked: panel warping, seam gaps. Broken: missing/detached panels. |
rammed_earth_wall |
Warm ochre-to-umber banding (#8a6a48–#6e5236) |
Strong horizontal layer banding (the construction method's signature texture) | Intact: crisp banding. Worn: surface erosion softening band edges. Cracked: vertical cracking across bands. Broken: section collapse/erosion loss. |
3.2 Roof forms
| Tag | Hue range | Grain / texture character | Weathering baseline |
|---|---|---|---|
flat_roof |
Neutral dark grey (#4a4844) membrane |
Flat, minimal detail, visible seam lines on membrane roofing | Intact→Broken: ponding stains, membrane cracking, vegetation growth at Broken. |
pitched_roof |
Warm dark grey-brown (#4e463e) generic shingle/slate register unless a specific tag (below) narrows it |
Directional shingle/slate coursing | Moss/lichen accumulation in shadowed pitches as condition drops; missing units at Cracked+. |
corrugated_roof |
Weathers to rust-orange (#a65c34 at Worn+), starts galvanized silver-grey (#a8aaa8) when Intact — explicitly does not stay silver, this is D-235's own worked example |
Strong linear corrugation shadow pattern | Intact: clean galvanized sheen. Worn: rust bleeding from fastener points. Cracked: broad rust staining, sheet lifting at edges. Broken: rust-through holes, missing sheets. |
clay_tile_roof |
Warm terracotta (#b56a3e–#c8825a) |
Individual tile coursing, slight per-tile hue variance | Moss/lichen in valleys, cracked/slipped/missing tiles as condition drops. |
terraced_roof |
Match the deck surface (paved/planted per template), not a distinct roof hue | Flat deck texture, visible rail/parapet line | Surface wear on deck material, planter/rail deterioration at lower bands. |
vaulted_roof |
Match wall material register (masonry vaults read as an extension of the wall) | Strong directional shadow from the vault curvature | Standard masonry weathering (see stone/brick wall rows). |
green_roof |
Vegetated green, seasonally variable — the one roof tag where hue is intentionally NOT template-locked | Organic, irregular, canopy-like from above | Condition reads as vegetation health/coverage, not surface damage — sparse/patchy at low prosperity, not "broken" in the structural sense. |
3.3 Facade rhythms
Facade tags are about pattern, not hue — they inherit the wall tag's color register. Grain/weathering guidance:
| Tag | Grain / texture character | Weathering note |
|---|---|---|
regular_facade |
Even punched-window grid, no added ornament | Weathers uniformly with the wall tag beneath it. |
ornamental_facade |
Raised/carved detail catches shadow distinctly from the flat wall plane | Ornament erodes/chips before the flat wall does — detail loss is the earliest visible sign of Worn. |
industrial_glazing |
Regular steel-mullion grid, factory-sash proportions (wide, short panes) | Mullion rust (if steel_frame) precedes glazing damage. |
arcade_facade |
Strong repeating vertical shadow rhythm from the colonnade | Ground-level wear concentrates at column bases (foot traffic), not evenly. |
shuttered_facade |
Shutters as a distinct, often higher-saturation accent color against the wall's muted register — this is the one facade tag allowed a saturation bump (bounded by §5's object-tier ceiling below) | Shutter paint fades/peels faster than the wall behind it — a secondary, faster weathering clock on the same building. |
screen_facade |
Fine repeating perforation/louvre pattern, strong dappled shadow | Screen material (usually metal or timber lattice) weathers per its own material, independent of the wall plane behind. |
colonnade |
Freestanding column rhythm, deep shadow gaps between wall plane and columns | Column bases weather fastest (ground contact, foot traffic) — same principle as arcade_facade. |
lattice_screen |
Fine timber/metal lattice, mashrabiya-register — highest facade information-density texture in the set | Individual lattice elements can go missing at Cracked+ without the whole screen reading as "broken." |
3.4 Street surfaces
| Tag | Hue range | Grain / texture character | Weathering baseline |
|---|---|---|---|
paved |
Neutral grey (#66645e) |
Fine asphalt/poured texture, minimal joint pattern | Cracking, pothole pattern, patch-color mismatch (repairs never quite match original pour age). |
cobble |
Warm grey-tan, matches stone_wall's register |
Strong individual-unit texture, visible mortar/sand joints | Displaced/missing setts at low condition, not surface cracking (it's a unit system, not a monolithic pour). |
packed_earth |
Warm ochre-brown (#7a6248) |
Irregular, rutted, no joint pattern | Erosion channels, puddling, vegetation encroachment at edges as condition drops (or rises — an unmaintained packed-earth street can look "worse" toward Intact-adjacent if it's simply less used, this tag is condition-light). |
canal_way |
Water surface — hue driven by reflection/sediment, not a fixed material hue | N/A — water plane, not a solid surface | Water clarity/debris accumulation stands in for the condition ladder here. |
elevated_walkway |
Match structural material used (steel_frame/timber/composite per template) | Grated or planked, visible support structure below | Structural weathering per the material tag, plus grate/plank gap wear from foot traffic. |
heavy_haul |
Neutral dark grey (#4e4c48), reinforced-surface register |
Deep rut/track wear pattern, wider joint spacing than paved |
Track-line wear concentrates on wheel paths, not the full surface — an asymmetric wear pattern unlike paved's even cracking. |
boardwalk |
Warm grey-brown weathered timber (#8a7a64), coastal/wetland register — assume some grey weathering even at Intact, unlike timber_wall |
Plank coursing, visible gap joints | Individual plank replacement creates natural color patchwork even at high condition — this is the one street tag where slight inconsistency reads as authentic, not neglected. |
3.5 Generic fallback placeholders
generic_wall / generic_roof / generic_facade / generic_street render
as a flat, neutral, low-saturation grey (#6e6c68, roughly the mean of
the specific tags' hue ranges above) with no grain and no weathering
variation — condition bands still apply structurally (a generic-fallback
wall can still be "Broken" in silhouette/damage terms) but carry no material
texture, so the player reads "this is a wall, its specific material hasn't
loaded yet" rather than mistaking it for a deliberate concrete/stone choice.
This is the visual expression of D-235's fallback chain — a placeholder must
look unfinished, not like a legitimate flat concrete building, or its
eventual upgrade-in-place would read as an inconsistent hue reskin instead of
"detail arriving."
4. What Survives from Prior Documents
Per D-244 (2026-06-16) and this ticket's acceptance criteria, the following principles from the pre-cascade documents are inherited, restated here in current (3D, D-235-token) terms rather than the old (2D sprite, era-ladder) terms they were originally written in:
| Inherited principle | Original source | Current expression |
|---|---|---|
| D-033 entity color = relationship to player | visual-grammar-v01.md §3 | Unchanged — still the live decision, still the single most important color system in the game. Applies to entities regardless of render fidelity (rectangle, sprite, or 3D mesh). |
| "Functional warmth" — industrial infrastructure that people made livable, not military/not luxury | workshop-outcomes.md visual identity statements + Gore's naming | Standing mood target for the whole D-235 register above: hue ranges lean warm (terracotta, ochre, warm grey) rather than cool/clinical by default; the cool end of the palette is reserved for deliberately institutional template registers, not the default. |
| Environmental neutrality — strict zero shift (D-045) | workshop-outcomes.md §1.10 | Unchanged, still active. The D-235 material register above is diegetic/static — condition bands (D-217) shift with simulated prosperity_score, never with narrative/investigation state. This document adds no new exception to D-045. |
| Entity always wins visual ties (visual hierarchy: entity > object > structure) | visual-grammar-v01.md §1.4 / workshop-outcomes.md §1.4 | Survives as a rendering-order and saturation-hierarchy principle. In 3D terms: entity materials/outlines render at the highest effective saturation and are never occluded in a way that reads ambiguous against a similarly-toned prop. The saturation ceiling table (below) replaces the old sprite-outline-weight table. |
| Object favorite-color saturation ceiling | visual-grammar-v01.md §3.6 | Restated: entity colors (D-033) remain the highest-saturation elements in any scene; object/prop colors (including the shuttered_facade accent exception in §3.3) stay moderate; structure/material register (§3) stays lowest. Same three-tier ceiling, now anchored to D-235 tags instead of "Era 1/2/3" object palettes. |
| Readability over beauty | workshop-outcomes.md Principle 1 | Standing design principle, unchanged, applies to 3D rendering exactly as it applied to sprites. |
| Lighting-driven atmosphere, not baked mood | workshop-outcomes.md Principle 2 / visual-grammar-v01.md's "sprites are shape templates" | Re-expressed in §2 above: toon shading gives shape + local shadow-band response; scene lighting (not baked texture darkening) still carries mood. The specific mechanism (Light2D CanvasModulate) is retired with the sprite pipeline, but the principle — materials stay neutral, the lighting rig does the emotional work — carries forward to whatever 3D lighting setup Phase 5 builds. |
5. Superseded Documents
Two earlier documents overlap this one. Both are explicitly marked superseded here (and should carry a pointer back to this section) so no two contradictory style documents stay live at the same time:
docs/design/visual-grammar-v01.md— already carries its own superseded banner (2026-06-12, pointing at D-235/D-228 + character-visuals-spec.md). That banner is correct and unchanged by this ticket. What's specifically superseded: the entire sprite-era production model (§2 entity sprite dimensions, §4 z-level stack as sprite layers, §5 typography-for-sprite-canvas sizing, §6 two-tier animation frame-count tables, §9 resolution chain / Nano Banana workflow, and the three-construction-era material ladder (§1.2–§1.4's Era 1/2/3 zone palettes) — the era ladder is the exact thing D-235 retired by name ("Era is NOT a material filter here"). What's not superseded, just relocated: D-033 entity color values and D-045 environmental neutrality, both restated current in §4 above.docs/workshops/art-direction-mood-board/workshop-outcomes.md— this is a workshop transcript (type: workshop, status: archived) and correctly stays archived as a historical record; it is not live style guidance and should not be read as such. Its candidate decisions (D-042 through D-051 as originally proposed in §7) were formally recorded as the perception.md decisions cited throughout this document (D-033, D-043, D-044, D-045, D-046 etc.) — those decision records are the living artifact, not the workshop prose. Its three-construction-era material system (§1.8) is superseded by D-235 for the same reason as above. Its camera angle content (§1.2, §9.3) is superseded by D-148 per §1 above. Everything else in it (lighting three-reference model, fog cone shape, two-tier animation, insert overlay bloom spec, z-stack layer concept — not its sprite-specific implementation detail) remains valid design intent, inherited into current decisions rather than into this document directly.
Going forward: this document (palette.md) is the single live style
bible for Phase 4+ rendered look. New style guidance should land here, not in
a new parallel document, unless it's genuinely a different concern (e.g. the
model-naming/manifest conventions living in docs/assets/visual/conventions.md,
authored separately under T-1050).
Appendix — Decision Cross-References
| Decision | Relevance |
|---|---|
| D-019 | Original top-down camera + offline-renderer −72.5° angle. Superseded for gameplay by D-148; survives for the offline 2D-artwork render rig. |
| D-033 | Entity color = relationship to player. Unchanged, still the primary information-bearing color system. |
| D-045 | Environmental neutrality — strict zero shift. Governs how the D-235 material register (§3) may and may not respond to game state. |
| D-148 | 30° low-angle gameplay Camera3D, supersedes D-019 for gameplay. |
| D-149 | 3D characters rendered live in scene, not pre-rendered sprites. Backing precedent for §1's camera reasoning (no more sprites to render "at the angle" during play) and for §2's shared-shader-family argument (characters and environment props come off the same live-3D pipeline). |
| D-217 | Tile condition thresholds (prosperity_score bands) — the weathering-baseline mechanism referenced throughout §3. |
| D-232 | Trait-template catalog — era reframed as maintenance/wear + past-vogue holdover, not a material-tech ladder. Backing rationale for retiring the Era 1/2/3 ladder in §5. |
| D-235 | Building exterior visual grammar + ratified ObjectTag vocabulary (object_tag_vocabulary.toml). §3 is this document's rendered-look layer on top of D-235's logical tokens; §2's glazing carve-out names its ratified glass_curtain_wall/industrial_glazing tokens. |
| D-244 | 3D objects in-world; 2D limited to textures + flat artwork. Establishes the toon-shader-family context for §2 and retires the sprite catalog framing this document used to have. |
| D-257 | Environment props share the character toon shading treatment; minimal-PBR carve-out for glazing. The binding authority for §2 — this document states the practical guidance, D-257 is the decision record. |