fix(assets): purge stale 5-layer/peripheral references from fog spec
Round 3 review fixes — thorough spec cleanup: - Remove visibility_sectors from data flow (peripheral removed in #569) - Remove player_pos uniform (cone center implicit in visibility_tex) - Update FogState pseudocode: remove sector step, add zone tint step - Update lifecycle diagram to match single update_from_state() call - Fix "5-layer fog" → "3-state fog" in Files to Create and impl notes - Mark zone tint open question as resolved (Sprint 22, D-077) - Document filter_nearest rationale on zone_tint_tex (D-073 hard zones) - Note low-saturation tint is intentional per D-046 Hopper test Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,8 @@ const EXP_VISIBLE: int = 255 # Currently in LOS — clear (written each frame
|
||||
|
||||
# Zone temperature tints (D-059 + D-046, Sprint 22) — keyed by zone_id string from server.
|
||||
# Matches audio_manager.gd ZONE_ASSETS zone_id strings for consistent zone semantics.
|
||||
# Low saturation is intentional (D-046): tints are subtle — distinguishable as warm/cool/neutral
|
||||
# in side-by-side comparison, not garish. The "Hopper test" validates this.
|
||||
# Colors are dark tints used as the fog overlay in the deep fog zone:
|
||||
# hub/workplace: #1a1f2e (cool blue-dark — terminal, institutional)
|
||||
# bar: #2a1f15 (warm amber-dark — social, inhabited)
|
||||
|
||||
@@ -12,7 +12,7 @@ shader_type canvas_item;
|
||||
|
||||
uniform sampler2D visibility_tex : filter_linear, repeat_disable;
|
||||
uniform sampler2D exploration_tex : filter_linear, repeat_disable;
|
||||
uniform sampler2D zone_tint_tex : filter_nearest, repeat_disable;
|
||||
uniform sampler2D zone_tint_tex : filter_nearest, repeat_disable; // nearest: zones have hard boundaries (D-073)
|
||||
uniform sampler2D noise_tex : filter_linear, repeat_enable;
|
||||
uniform vec2 rect_pos; // World-space position of the ColorRect (pixels)
|
||||
uniform vec2 rect_sz; // World-space size of the ColorRect (pixels)
|
||||
|
||||
@@ -58,9 +58,8 @@ The initial instinct is to put a fragment shader on the CanvasGroup itself (as `
|
||||
```
|
||||
Server (each tick)
|
||||
└─ ObserverSnapshot
|
||||
├─ visible_positions: Dictionary<Vector2i, true> (LOS result)
|
||||
├─ visibility_sectors: Dictionary<Vector2i, "Forward"|"Peripheral">
|
||||
└─ visible_tiles: Array<{x, y, z, type}> (known map extent)
|
||||
├─ visible_positions: Dictionary<Vector2i, true> (LOS result, forward cone only)
|
||||
└─ visible_tiles: Array<{x, y, z, type, zone_id}> (known map extent + zone metadata)
|
||||
|
||||
GameState (autoload)
|
||||
└─ Stores all above
|
||||
@@ -78,7 +77,6 @@ FogOverlay (Node2D) [fog_shader.gd]
|
||||
│ uniform sampler2D exploration_tex; # Historical explored
|
||||
│ uniform sampler2D zone_tint_tex; # Zone temperature colors
|
||||
│ uniform float time; # For noise animation
|
||||
│ uniform vec2 player_pos; # Vision cone center
|
||||
│ uniform vec2 map_offset; # World-to-texture mapping
|
||||
│ uniform vec2 map_size; # Texture dimensions in tiles
|
||||
└─ FogEntities (Node2D)
|
||||
@@ -107,13 +105,13 @@ var zone_tint_texture: ImageTexture
|
||||
|
||||
func update_from_state() -> void:
|
||||
# Called every tick by fog_shader.gd
|
||||
# 1. Resize textures if map_bounds changed
|
||||
# 1. Resize textures if map_bounds changed (grow-only)
|
||||
# 2. Clear visibility_image to 0 (black)
|
||||
# 3. Write visible_positions from GameState → red channel = 255
|
||||
# 4. Write visibility_sectors: Forward = 255, Peripheral = 180
|
||||
# 5. Update exploration_image: any currently-visible pixel → 255,
|
||||
# previously-visible pixels decay toward 128 over time
|
||||
# 6. Upload images to textures
|
||||
# 3. Write visible_positions from GameState → red channel = 255 (forward cone only)
|
||||
# 4. Update exploration_image: visible pixels → 255,
|
||||
# tiles leaving LOS decay to 128 (EXP_EXPLORED)
|
||||
# 5. Update zone_tint_image: write zone_id → temperature color per tile
|
||||
# 6. Upload changed images to textures
|
||||
```
|
||||
|
||||
**Performance note:** `Image.set_pixel()` in a loop is ~0.05ms for 400 tiles. Acceptable. For larger maps, switch to `Image.set_data()` with a pre-built `PackedByteArray`.
|
||||
@@ -202,13 +200,14 @@ Fog entities are NOT shader effects — they're GDScript-spawned sprites under `
|
||||
|
||||
```
|
||||
Per tick (in _process or on snapshot signal):
|
||||
1. FogState.update_visibility(GameState.visible_positions, GameState.visibility_sectors)
|
||||
→ Write visibility_image, upload to visibility_texture
|
||||
2. FogState.update_exploration(GameState.visible_positions)
|
||||
→ Mark visible tiles as explored, apply decay to non-visible explored tiles
|
||||
→ Upload to exploration_texture
|
||||
3. FogOverlay._process():
|
||||
→ Update shader uniforms (visibility_tex, exploration_tex, time, player_pos)
|
||||
1. FogState.update_from_state()
|
||||
→ Grow bounds if new tiles visible
|
||||
→ Write visibility from GameState.visible_positions (forward cone)
|
||||
→ Decay exploration: tiles leaving LOS → EXP_EXPLORED (128)
|
||||
→ Write zone tint from visible_tiles[].zone_id
|
||||
→ Upload changed textures
|
||||
2. FogOverlay._process():
|
||||
→ Update shader uniforms (visibility_tex, exploration_tex, zone_tint_tex, time)
|
||||
→ Update fog entity positions/states from ObserverSnapshot fog entity data
|
||||
```
|
||||
|
||||
@@ -228,7 +227,7 @@ Per tick (in _process or on snapshot signal):
|
||||
|------|------|---------|
|
||||
| `client/scripts/autoloads/fog_state.gd` | Autoload | Fog texture management, exploration persistence |
|
||||
| `client/scripts/rendering/fog_shader.gd` | Script | FogOverlay node controller, shader uniform updates |
|
||||
| `client/shaders/fog.gdshader` | Shader | Fragment shader for 5-layer fog |
|
||||
| `client/shaders/fog.gdshader` | Shader | Fragment shader for 3-state fog (clear / explored / unexplored) |
|
||||
| `client/scenes/fog_sound_ping.tscn` | Scene | Sound ping rings (deferred to Sprint 7+, #431) |
|
||||
| `client/scenes/fog_entity_ghost.tscn` | Scene | Recognized entity ghost (deferred to Sprint 7+, #431) |
|
||||
| `client/scenes/fog_entity_blob.tscn` | Scene | Unrecognized entity blob (deferred to Sprint 7+, #431) |
|
||||
@@ -253,10 +252,10 @@ Per tick (in _process or on snapshot signal):
|
||||
2. **Coordinate mapping is the hardest part.** Getting screen pixels → world tiles → texture UVs correct requires careful math. Test with a known map layout.
|
||||
3. **Use Godot's NoiseTexture2D** resource for the Perlin noise rather than computing it in the shader. Pass it as a uniform. Scroll the UV offset with TIME for animation.
|
||||
4. **The gradient edge** (Layer 1, 3-4 tile radius via 7x7 Gaussian) is the most visible quality differentiator. Use `smoothstep()` with the distance from the nearest non-visible tile. This may require encoding distance-to-edge in the visibility texture rather than binary 0/255.
|
||||
5. **Fog entities are Sprint 7+ (#431).** For this sprint, just get the 5-layer fog shader working. The FogEntities node can be empty.
|
||||
5. **Fog entities are Sprint 7+ (#431).** For this sprint, just get the 3-state fog shader working. The FogEntities node can be empty.
|
||||
6. **Test with the existing sim_bridge test mode** — it provides a visible_positions Dictionary with a 4-tile radius and Bresenham LOS. Good enough to validate the shader.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- **Q: How does the "maps app" data reach the client?** Layer 4 (unexplored + maps) needs to know which unexplored tiles the character's insert has map data for. This likely requires a new field in ObserverSnapshot (e.g., `mapped_tiles`). For Sprint 6, treat all explored tiles as "has maps" and all unexplored as "no maps" (layers 3 and 5 only, skip layer 4). Layer 4 is a v0.1.2+ feature.
|
||||
- **Q: Zone temperature tints — where do they come from?** Currently no per-tile zone data in the snapshot. For Sprint 6, use a hardcoded default (neutral dark). Zone tints require server-side zone metadata.
|
||||
- **Resolved (Sprint 22, #563):** Zone temperature tints come from `zone_id` field on `visible_tiles[]` in `ObserverSnapshot` (D-077). `fog_state.gd` maps zone_id strings to `ZONE_TINTS` color dictionary. Without zone metadata, defaults to neutral dark `#1a1a1a`.
|
||||
|
||||
Reference in New Issue
Block a user