Tyre: D-244 now references its precedent (D-148 3D camera / D-149 live 3D characters — D-244 completes that pivot for objects + assets) and reconciles the 5 sprite-era decisions it touched, all amended 2026-06-17: - D-043: 'not 3D' / Light2D scoped to overlay layers; in-world is 3D. - D-044: sprite footprint retired (sim occupancy + visual hierarchy survive). - D-049: z-stack logical model survives; sprite/CanvasLayer implementation language retired. - D-066: dual-scale coordinate model + 2x2 geometry minimum survive; sprite-footprint language legacy. Hoshe (doc QA): - D-244 broken cross-file anchors fixed ([D-159]->scope.md, [D-241]->process.md). - renderer/README: bare [D-244] linked; D-019->D-148 camera note; legacy banner over the object-sprite workflow sections; resolution-chain output path qualified. - docs/assets/_templates/sprite.md + README: marked the template legacy object-sprite schema (model/texture/artwork template TBD). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
127 lines
7.4 KiB
Markdown
127 lines
7.4 KiB
Markdown
# Settled Reach — 2D Artwork Render Pipeline
|
||
|
||
> **Repurposed (D-244).** This offline Godot renderer is **not** the in-world object
|
||
> format. Per [D-244](../governance/decisions/architecture.md#d-244-asset-rendering--3d-objects-in-world-2d-limited-to-textures--flat-artwork) the in-world view renders 3D objects directly (props via Trellis
|
||
> `.glb` / `/glb-gen`; characters via the Quaternius pipeline + `CharacterVisualDescriptor`);
|
||
> the only flattened 2D content is **textures** and **flat 2D artwork** (paintings, flags,
|
||
> billboards, signage). This pipeline began as the #541 3D-to-2D *sprite* spike and now
|
||
> serves that flat artwork. The "render every entity/object/wall to sprites" framing below
|
||
> is **legacy** (the retired object-sprite use); the camera / lighting / resolution mechanics
|
||
> remain usable for flat-artwork rendering.
|
||
|
||
Offline Godot 4 renderer. Produces flat 2D artwork (and, in its legacy mode, 3D-to-2D sprites) at three resolutions from a fixed camera angle, with outlines applied at working resolution.
|
||
|
||
## Camera Specification (D-019)
|
||
|
||
- **Angle**: -72.5° from horizontal (= 17.5° from vertical, the midpoint of the 15-20° from vertical range)
|
||
- **Projection**: Orthographic (`size = 1.4`)
|
||
- **Position**: `(0, 3, 1)` — above and slightly in front of the model origin
|
||
- **Godot Transform3D**: `Transform3D(1, 0, 0, 0, 0.30071, 0.95372, 0, -0.95372, 0.30071, 0, 3, 1)`
|
||
|
||
**Note (D-244):** D-019 is superseded by D-148 (30° low-angle Camera3D) for the **gameplay** camera. The −72.5° angle here is the **offline render** camera used for 2D-artwork generation (and the retired object-sprite mode) — not the in-world gameplay camera.
|
||
|
||
This is "the angle" per the D-019 amendment (2026-02-12); v0.1 object-sprite rendering used it, now retired. The offline renderer keeps this fixed camera for flat-artwork output.
|
||
|
||
## Lighting Rig
|
||
|
||
Three-point studio rig. All lights have `shadow_enabled = false` — sprites are shape templates, no baked shadows or directional lighting. The Godot runtime PointLight2D pipeline provides all scene lighting at runtime (D-043).
|
||
|
||
| Light | Energy | Direction | Purpose |
|
||
|-------|--------|-----------|---------|
|
||
| KeyLight | 1.0 | Matches camera angle (-72.5° from horizontal) | Main illumination; ensures front face is lit as the camera sees it |
|
||
| FillLight | 0.4 | 45° from right side (-45° pitch, +90° yaw) | Fills shadow opposite the key; no deep-black patches on right face |
|
||
| RimLight | 0.3 | 45° from behind (-45° pitch, +180° yaw) | Back-edge accent; defines silhouette boundary for outline processing |
|
||
|
||
**Rationale**: Even 3-light coverage ensures no black patches on a convex mesh, keeping surface colors flat and readable for the outline processing step.
|
||
|
||
## Resolution Chain (D-043, D-044)
|
||
|
||
```
|
||
1024×1024 — source PNG, full fidelity for outline processing
|
||
↓ bilinear resize
|
||
256×256 — working PNG, outline applied at 4-8px, color #333340
|
||
↓ bilinear resize
|
||
64×64 — runtime PNG (legacy object-sprite output → client/assets/sprites/; for flat artwork, promote to client/assets/textures or .../artwork)
|
||
```
|
||
|
||
Outline implementation: alpha-mask dilation + flat color fill. Outline width in `render_export.gd`: `outline_width_px = 4` (at 256px = 1px effective at 64px).
|
||
|
||
## ⚠️ Legacy: object-sprite workflow (retired per D-244)
|
||
|
||
Everything from here down describes the **retired** object-sprite pipeline (#541 spike) — entity/object/wall sprites are no longer the in-world format (objects are 3D `.glb`; characters are 3D meshes). It is kept for reference, and because the camera / lighting / resolution mechanics above are reused for **2D-artwork** rendering. When generating flat artwork, promote outputs to `client/assets/textures` or `client/assets/artwork` (**not** `client/assets/sprites/`) and ignore the entity-sprite footprint / material / deployment specifics below.
|
||
|
||
## Entity Footprint Spec (D-044, D-066)
|
||
|
||
- Entity sprites occupy a **24×32px footprint** within the 64×64 visual tile
|
||
- Entities render across a **2×2 sim tile sprite footprint** (D-066)
|
||
- Visual tile = 64×64px at runtime; sim tile = 32×32px (0.5m)
|
||
- **NPC model scale**: `CapsuleMesh(radius=0.25, height=0.62)` at camera size 1.4 produces ~24×32px apparent size in the 64px output tile
|
||
|
||
## Usage
|
||
|
||
### Command Line
|
||
|
||
```bash
|
||
# From the repository root
|
||
godot --path renderer/ --headless --quit-after 1200 res://render_scene.tscn -- <model_name>
|
||
```
|
||
|
||
The `/sprite-gen` skill wraps this command and handles output placement.
|
||
|
||
### Adding a New Model
|
||
|
||
1. Create `renderer/models/<model_name>.tscn` — root node is a `MeshInstance3D` (or `Node3D` with children)
|
||
2. Center the mesh at the origin
|
||
3. Use a flat `StandardMaterial3D` (no baked shadows — just albedo color + roughness)
|
||
4. Run: `godot --path renderer/ --headless --quit-after 1200 res://render_scene.tscn -- <model_name>`
|
||
5. Output: 12 PNGs in `renderer/output/` (4 directions × 3 resolutions)
|
||
6. Runtime sprites: copy 64px variants to `client/assets/sprites/`
|
||
|
||
### Material Guidelines
|
||
|
||
- **Entity sprites**: neutral flat material (albedo Color(0.5, 0.5, 0.5)). D-033 relationship color tinting applied at runtime by the client's entity renderer. Outline color (#333340 per D-043) is compatible with all D-033 tint colors — the dark blue-grey outline remains visible against teal, green, amber, and red entity tints.
|
||
- **Structural sprites**: use zone-appropriate texture (`textures/wall_institutional_era1.png`, etc.)
|
||
- No specularity: `metallic = 0.0`, `roughness = 0.9`
|
||
- No emission, no normal maps — shape is the signal
|
||
|
||
## Models
|
||
|
||
| Model | File | Type | Notes |
|
||
|-------|------|------|-------|
|
||
| `wall_structural` | `models/wall_structural.tscn` | Structure | 1.0×0.8×0.2 box, institutional era-1 texture |
|
||
| `wall_bar_green` | `models/wall_bar_green.tscn` | Structure | 1.0×0.8×0.2 box, green panel texture (model only — no sprites rendered yet) |
|
||
| `npc_generic` | `models/npc_generic.tscn` | Entity | Capsule silhouette, neutral grey, 24×32px footprint. Rotationally symmetric — north/south and east/west pairs are near-identical by design (asymmetric silhouettes come from named NPC models with identifying features per D-044). |
|
||
|
||
## Output Naming Convention
|
||
|
||
```
|
||
<model_name>_<direction>_<resolution>.png
|
||
|
||
wall_structural_north_64.png
|
||
npc_generic_south_256.png
|
||
```
|
||
|
||
Directions: `north`, `east`, `south`, `west` (model rotated 0°, 90°, 180°, 270° around Y axis).
|
||
|
||
## Runtime Sprite Path
|
||
|
||
64px sprites are deployed to: `client/assets/sprites/`
|
||
|
||
Naming convention at the client side matches the pipeline output: `<model_name>_<direction>_64.png`. The 1024 and 256 variants stay in `renderer/output/` as pipeline intermediates (gitignored).
|
||
|
||
## Design Constraints
|
||
|
||
- **No baked lighting**: sprites are shape templates. Lighting is applied at runtime by Godot's PointLight2D per D-046.
|
||
- **No baked shadows**: shadow direction would conflict with runtime point lights at arbitrary positions.
|
||
- **No baked mood**: flat materials, three-point neutral rig. Zone atmosphere comes from runtime CanvasModulate.
|
||
- **Outline applied at 256px, not 64px**: ensures outline is crisp after bilinear downscale.
|
||
- **Transparent background**: `SubViewport.transparent_bg = true` — sprites are PNG with alpha, composited at runtime.
|
||
|
||
## Cross-References
|
||
|
||
- D-019: Camera angle spec and amendment
|
||
- D-043: Art direction — "functional warmth," resolution chain, outline color
|
||
- D-044: Visual hierarchy, entity footprint spec
|
||
- D-049: Z-level rendering stack (sprites land on layers 2-3)
|
||
- D-066: Dual-scale grid, 2×2 sim tile entity footprint
|