Files
open-wc/docs/modules/world-renderer.md
T

374 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# World Renderer
## Metadata
| Field | Value |
|---|---|
| Status | Partial |
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; `M03-RND-FACADE-001`; `M03-RND-STREAMING-PLANNER-001`; `M03-RND-SCHEDULER-001` |
| Owners | Renderer workstream / milestone integrator |
| Last verified | Worktree `work/sindo-main-codex/m03-render-budget-scheduler`, 2026-07-16 |
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
## Purpose
Загрузить и визуализировать мир WoW 3.3.5a вокруг streaming focus: terrain, ADT placements, M2, WMO, liquids, sky/light и character/world presentation experiments. Система должна скрывать I/O/parsing/finalization latency за prewarm и per-frame budgets.
## Non-goals
- Не владеет gameplay/world authoritative state.
- Не читает network packets или server DB.
- Не определяет player input/movement rules.
- Не является Content Project или editor source of truth.
- Текущий module не заявляет визуальное соответствие `1:1`; gaps ведутся в [`../../RENDER.md`](../../RENDER.md).
## Context and boundaries
```mermaid
flowchart LR
Player[Player or spectator Node3D] --> Adapter[Explicit source adapter]
Editor[Editor viewport adapter] --> Focus[StreamingFocus]
Capture[Capture tool] --> Adapter
SpawnFixture[Pinned server spawn fixture] --> SpawnManifest[Spawn render manifest]
SpawnManifest --> Capture
Adapter --> Focus
Focus --> Facade[WorldRenderFacade]
Facade --> Loader[StreamingWorldLoader internal]
Loader --> Planner[StreamingTargetPlanner]
Planner --> TargetPlan[StreamingTargetPlan]
TargetPlan --> Loader
Loader --> Budget[RenderBudgetScheduler]
Assets[Extracted WDT/ADT/M2/WMO/BLP] --> Native[Native loaders]
Cache[Baked terrain/M2/WMO caches] --> Loader
Native --> Parsed[Parsed tile/model data]
Parsed --> Loader
Loader --> Scene[SceneTree nodes]
Loader --> RS[RenderingServer RIDs/MultiMesh]
Sky[DBC sky/light data] --> SkyController[WowSkyController]
SkyController --> Scene
```
Allowed dependencies:
- native format loaders;
- render builders/material helpers;
- immutable/cached render resources;
- renderer configuration and focus;
- Godot rendering/scene APIs.
Forbidden dependencies:
- packet codecs/world session;
- gameplay reducers/quest/combat state;
- runtime UI Controls;
- direct TrinityCore/AzerothCore database access.
## Public API
Runtime, capture and probe callers use `WorldRenderFacade` for focus and metrics.
`StreamingWorldLoader` remains the internal scene implementation while later M03
packages add environment, entity-visual and ground-query contracts.
Gameplay modules and Godot `EditorPlugin` package sources are repository-gated
from externally reading/writing loader-private queue, task, cache and tile-state fields.
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `map_name` | Exported property | WDT/map directory name | Set before world load | Missing WDT produces diagnostic/empty world |
| `extracted_dir` | Exported property | Root of legally extracted client data | Session lifetime | Missing files reported per loader path |
| `StreamingFocus` | Immutable value | Typed Godot-basis position without Node/Camera dependency | Any thread; caller-owned reference | Null position is a caller contract violation |
| `WorldRenderFacade.streaming_focus_source_path` | Exported adapter property | Samples any explicit `Node3D` source into `StreamingFocus` | Main thread, scene lifetime | Missing/wrong node warns once and retains prior focus |
| `WorldRenderFacade.set_streaming_focus` | Public method | Accepts focus from app/editor/tool composition | Main thread/session | Missing internal renderer is diagnosed; no state is duplicated |
| `WorldRenderFacade.refresh_streaming_focus` | Public method | Samples configured source and delegates existing refresh thresholds | Main thread/session | Returns `false` when dependency/focus is unavailable |
| `WorldRenderFacade.renderer_metrics_snapshot` | Public method | Returns a deep-detached queue/cache/activity snapshot | Main thread/caller-owned result | Missing/invalid implementation result returns empty snapshot with diagnostic |
| `WorldRenderFacade.streaming_world_loader_path` | Internal adapter property | Resolves the existing implementation without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
| `camera_path` | Exported property | Camera used only by optional automatic overview positioning | Main thread, scene lifetime | Missing camera skips automatic positioning |
| `quality_preset` | Exported property | Applies renderer budgets/radii | Before/at runtime depending property | Invalid combination should be diagnosed |
| `runtime_stats_enabled` | Exported property | Enables periodic performance snapshot | Runtime mutable | Logging overhead only |
| `hitch_profiler_enabled` | Exported property | Enables named hitch sections | Runtime mutable | Logging overhead only |
Публичным contract не считаются `StreamingWorldLoader` methods/properties,
внутренние dictionaries, queues, job records и generated node names. Scene-owned
loader configuration remains transitional composition data, not a caller API.
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | `StreamingFocus` | Player/spectator/editor/capture adapter | `WorldRenderFacade` | Immutable caller value | Main thread/session |
| Input | Focus-source `NodePath` | Runtime/test scene composition | `WorldRenderFacade` source adapter | Scene-owned node, sampled position | Main thread/scene lifetime |
| Input | Map/configuration | Scene/app/editor | `StreamingWorldLoader` | Caller config, copied/read | Main thread/session |
| Input | WDT/ADT/M2/WMO/BLP bytes | Extracted asset repository | Native loaders/builders | Loader result owns parsed data | Worker or controlled load |
| Input | Baked resources | Cache build tools | Streamer/builders | Shared immutable resource/cache entry | Session/cache lifetime |
| Test input | Server-spawn render manifest | Coordinate fixture/verifier | Checkpoint capture tool | Repository-owned diagnostic data | Test-process lifetime |
| Output | `StreamingTargetPlan` | `StreamingTargetPlanner` | Streamer queue/state apply | Immutable ephemeral value | One refresh |
| Internal control | Per-frame lane limits and boolean permits | Streamer configuration / `RenderBudgetScheduler` | Ordered streamer drains | Scheduler-owned counters | Main thread/frame |
| Output | Desired tile/detail operations | Streamer plan application | Finalize queues | Loader-owned | Cross-frame |
| Output | Terrain/M2/WMO/liquid instances | Loader/builders | Godot world/renderer | Loader/world owner | Main-thread attach |
| Output | Detached renderer metrics | `WorldRenderFacade` | Capture/baseline tools | Caller-owned deep copy | On demand |
| Output | Logs/diagnostics | Hitch/stat instrumentation | Logs/baseline tools | Immutable records | Periodic/frame |
| Test output | Diagnostic spawn marker and cold/warm PNGs | Checkpoint capture tool | Human/integrator review | SceneTree marker; `user://` files | One capture process |
Side effects:
- reads extracted files and generated caches;
- creates/frees scene nodes, meshes, materials, MultiMeshes and RIDs;
- submits worker tasks;
- mutates shader globals/environment through render/sky paths;
- writes logs and baseline captures through tools.
## Data flow
```mermaid
flowchart TD
PlayerSource[Player/spectator Node3D] --> A[Explicit scene adapter]
C[Capture camera] --> A
SF[Pinned server spawn] --> SM[Validated spawn manifest]
SM --> C
SM --> Marker[Renderer-native origin marker]
E[Editor viewport] --> EA[Editor adapter]
A --> F[StreamingFocus]
EA --> F
F --> RF[WorldRenderFacade]
RF --> Planner[StreamingTargetPlanner]
Planner --> T[StreamingTargetPlan apply]
WDT[WDT tile catalog] --> Planner
T --> Q[Tile load queue]
Q --> Parse[Worker parse/cache load]
Parse --> R[Result queues]
R --> B[RenderBudgetScheduler permits]
B --> Terrain[Terrain attach/upgrade]
B --> M2[M2 group/MultiMesh attach]
B --> WMO[WMO instance/group attach]
B --> Liquid[Liquid attach]
Terrain --> World[Godot world]
M2 --> World
WMO --> World
Liquid --> World
Marker --> World
F --> Eviction[Eviction/retention decisions]
Eviction --> World
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Initializing
Initializing --> WaitingForFocus: WDT/catalog/config ready
Initializing --> Degraded: missing loaders/data/cache
WaitingForFocus --> Streaming: valid StreamingFocus
Streaming --> Streaming: load/finalize/evict ticks
Streaming --> WaitingForFocus: world reset without replacement focus
Streaming --> SwitchingMap: map/focus reset
SwitchingMap --> Initializing
WaitingForFocus --> ShuttingDown
Degraded --> ShuttingDown
Streaming --> ShuttingDown
ShuttingDown --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant Source as Player/Editor/Capture source
participant Focus as StreamingFocus adapter
participant Facade as WorldRenderFacade
participant Stream as StreamingWorldLoader internal
participant Planner as StreamingTargetPlanner
participant Worker as Worker task
participant Budget as RenderBudgetScheduler
participant Render as SceneTree/RenderingServer
Source->>Focus: sample explicit Godot position
Focus->>Facade: set/refresh typed focus
Facade->>Stream: delegate focus and refresh
Stream->>Planner: typed focus, catalog and target policy
Planner-->>Stream: immutable wanted/retained plan
Stream->>Stream: queue priority and loaded-tile LOD/detail state
Stream->>Worker: parse/load tile and detail data
Worker-->>Stream: immutable result
Stream->>Budget: begin_frame(operation limits)
Stream->>Budget: try_consume_permit(lane)
Budget-->>Stream: true while lane remains bounded
Stream->>Render: attach permitted terrain/M2/WMO/liquid work
Stream->>Render: evict outside retention range
Stream->>Budget: shutdown: cancel permit issuance
Stream->>Worker: shutdown: wait for WorkerThreadPool tasks
Stream->>Stream: shutdown: finish registered ResourceLoader requests
Stream->>Render: clear queues and owned tree nodes/RIDs
Stream->>Stream: free detached prototypes and release resource caches
```
## Ownership, threading and resources
- `StreamingWorldLoader` владеет tile states, queues, task registries, active world instances и render caches session scope.
- `WorldRenderFacade` owns only scene-relative adapter paths. It does not own the
streamer, focus source, returned metrics, queues, caches, nodes or RIDs.
- `StreamingTargetPlanner` is stateless and owns only call-local calculations;
its immutable plan is consumed synchronously by the streamer.
- `RenderBudgetScheduler` owns only per-frame lane counters and a terminal
cancellation flag. The streamer retains queue ordering and all operation side effects.
- Focus producers own the immutable `StreamingFocus` reference; the loader retains
the latest reference and converts it to `Vector3` only inside the render boundary.
- Worker tasks не должны менять SceneTree и shared Resource concurrently.
- Parsed/grouped results передаются обратно через guarded result queues.
- Mesh/material/node/RID finalization выполняется main thread и ограничивается exported budgets.
- Shutdown/map switch обязан дождаться WorkerThreadPool jobs и зарегистрированных threaded ResourceLoader requests до очистки очередей, nodes, caches и RIDs.
- Parented streamed nodes остаются owned by SceneTree during shutdown; detached M2/WMO prototype caches are explicitly `free()`d after asynchronous work drains.
- A temporary liquid root with no generated child geometry is explicitly freed by `ADTBuilder` instead of being returned or abandoned.
- Cache resources считаются immutable после публикации.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Missing WDT/ADT | File/catalog check | Tile/map unavailable | Loader warning/error | Fix extraction/profile, reload |
| Missing focus source | Explicit NodePath resolution | Retain current streamed content and prior valid focus | One warning until source recovers | Fix composition path or call `set_streaming_focus` |
| Missing renderer implementation | Facade path/method validation | Focus/metrics call fails without mutation | One facade diagnostic until recovery | Fix scene composition path/implementation |
| Invalid metrics result | Facade return-type validation | Return empty caller snapshot | Facade error | Restore `Dictionary` implementation contract |
| No focus supplied | Refresh contract | No target recomputation; queues continue draining | `refresh_streaming_focus()` returns `false` | Supply player/editor/capture focus |
| Cache version mismatch | Required format constant | Reject stale cache/fallback | Version diagnostic | Rebuild matching cache |
| Worker failure | Task result/empty data | Skip/degrade affected asset | Queue/task warning | Retry/rebuild source |
| Main-thread hitch | Named section timing | Frame spike, work remains queued | `HITCH` log | Lower budget/fix finalize path |
| D3D12 descriptor exhaustion | Rendering backend error | Render failure/fallback backend | Godot error + baseline report | Dedup resources/fix settings |
| Teleport/map change | Focus/session transition | Old jobs become stale | Target/session generation | Cancel/drop stale results |
| Shutdown leak | Godot leak/RID diagnostics | Resource retained | Verbose shutdown report + cache shutdown verifier | Drain requests, preserve tree ownership, then free detached prototypes and clear resource caches |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Quality preset | Scene-specific | All | Limited | Applies radii and finalize budgets |
| Terrain control splat | Enabled in current High paths | Render | Limited | Near terrain texture quality |
| M2/WMO/liquids | Scene/profile-specific | Render | Debug/config | Enables asset categories |
| Runtime stats/hitch profiler | Usually debug | Dev/test | Yes | Observability cost |
| Shadow flags | Mostly disabled/limited | Blizzlike/Enhanced | Profile | CPU/GPU cost and visuals |
Exact exported settings and cache versions remain documented in [`../../RENDER.md`](../../RENDER.md) until extracted services receive individual specs.
## Persistence, cache and migration
- `StreamingFocus` is runtime-only and is not serialized. Scene migration adds
`streaming_focus_source_path`; existing `camera_path` remains valid only for
optional automatic overview positioning.
- Renderer reads caches under `data/cache`; caches are not committed.
- Format changes require `FORMAT_VERSION`/required-version bump and rebuild instructions.
- Atomic cache publication and unified manifest are target-state work from FND/M03.
- Material-only changes may refresh without geometry rebake only when documented safe.
## Diagnostics and observability
- Logs: `HITCH`, `PERF`, `TERRAIN_QUALITY`, `SKY_LIGHT`, `SKYBOX_MODEL`.
- API snapshot: `WorldRenderFacade.renderer_metrics_snapshot()` returns detached
queue/cache/activity counts without exposing mutable renderer state.
- Metrics: queue depths, active tiles/assets, named finalize times, frame percentiles and max hitch.
- Debug views: render checkpoint scenes/captures; future streaming/LOD/portal overlays.
- Correlation IDs: currently mostly tile/path keys; target architecture adds session/job IDs.
## Verification
- Unit/contract tests: streaming-focus contract/wiring, material mapping, unique-ID dedupe, placement probes, baseline manifest, five-point coordinate calibration, synthetic perceptual checkpoint diff, camera-pose grid plan.
- Pure planner contract: center/corner/catalog/clamp/editor cases plus bounded
High-like iteration timing without loading a world scene.
- Budget scheduler contract: exact lane exhaustion, shared chunk removal/create
priority, independent lanes, frame reset, invalid limits, terminal cancellation,
dependency boundaries and bounded permit timing without loading a world scene.
- Internal-access contract: derives the current streamer's private queue/task/
cache/state field inventory and rejects external member/reflection access from
gameplay or EditorPlugin package code.
- Integration/E2E: Eastern Kingdoms/Kalimdor streaming scenes, seven M00
cold/warm checkpoints and one dedicated mapped server-spawn checkpoint.
- Fidelity evidence: семь локальных build 12340 reference JPG покрывают terrain/ADT/M2/WMO/liquid/sky и реальный `GryphonRoost01`; automated cold/warm metrics имеют `compared=14`, `missing=0`. Sky использует приблизительно paired camera, а animated-M2 остаётся asset-paired/placement-unpaired из-за synthetic probe.
- Performance budgets: M00 report records cold/warm p95 and max hitch; no final acceptance threshold yet.
- Manual diagnostics: [`../RENDER_BASELINE.md`](../RENDER_BASELINE.md) and [`../../RENDER.md`](../../RENDER.md).
## Extension points
- Target `WorldRenderFacade` for runtime/editor/capture callers.
- Extend `StreamingFocus` with measured direction/velocity needs when the pure planner is extracted.
- Extracted planner, budget scheduler, terrain, M2, WMO, liquid, sky and character services.
- Separate Blizzlike/Enhanced material and quality profiles.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| ADT streaming/terrain | Partial | M00 checkpoints and current scenes | Fidelity/performance gaps remain |
| Static M2 placement | Partial | MultiMesh/dedupe probes | Full materials/animation/effects |
| WMO rendering | Partial | Cached group rendering | Portals/rooms/material parity |
| Liquids | Partial | MH2O/MLIQ paths | LiquidType/depth/shore fidelity |
| Sky/light | Partial | DBC controller/logs | Zone transition paired validation |
| Character presentation | Prototype | Character creator/player experiment | Full customization/equipment states |
| Camera-independent streaming focus | Implemented | Typed contract, two player-focused runtime scenes and three explicitly camera-focused diagnostic tools | Multi-focus/direction/velocity belong to planner extraction |
| Golden server-spawn visualization | Implemented | Pinned AzerothCore fixture, validated renderer manifest and diagnostic origin-marker capture at ADT `(32,48)`/MCNK `(3,12)` | No TrinityCore row or original-client visual-parity claim |
| Stable renderer facade | Partial | `M03-RND-FACADE-001` contract/runtime/tool regressions | Focus and metrics migrated; environment/entity visuals/ground query remain |
| Pure streaming target planner | Implemented | `M03-RND-STREAMING-PLANNER-001` asset-free contract/performance regression | Queue scheduling and loaded-tile LOD application remain in streamer |
| Bounded render budget scheduler | Implemented | `M03-RND-SCHEDULER-001` asset-free permit/cancellation/performance regression | Queue storage, worker concurrency and operation execution remain in streamer/services |
| Gameplay/editor internal-access boundary | Implemented | `M03-RND-INTERNAL-ACCESS-GATE-001` scans actual private streamer declarations against gameplay and EditorPlugin sources | Renderer diagnostic probes remain a scoped exception pending ground-query/readiness facade contracts |
## Known gaps and risks
- Monolithic streamer mixes planning, jobs, caches and presentation.
- `camera_path` remains only for optional automatic overview positioning; it no
longer drives runtime streaming or discovers the active viewport camera.
- Original-client paired fidelity evidence incomplete.
- Первый paired run выявил coordinate/placement mismatch: несколько совпадающих server-derived camera positions оказываются под terrain или внутри WMO/rocks OpenWC.
- Terrain-height probe исключил under-terrain состояние для всех пяти точек; waterfall exact-XZ miss классифицирован как TriangleMesh seam/edge и подтверждён nearby sample в 2 units.
- Camera-occluder probe не нашёл camera containment в пяти точках; paired mismatch локализован прежде всего в manual look direction/target/FOV calibration, с явным ограничением по RID-only geometry.
- Empirical FOV sweep выявил, что checkpoint camera должна явно вызывать `make_current()`; после исправления projection ranking остаётся inconclusive из-за неизвестного manual yaw/pitch/framing reference.
- Camera-pose sweep can now rank bounded yaw/pitch grids without changing manifest defaults; perceptual ranking remains diagnostic and requires human framing approval.
- The first Goldshire pose grid was invalidated by WMO readiness: the inn is absent after 2 seconds but visible after the manifest-standard 8-second wait. Pose comparisons must stabilize asynchronous checkpoint content first.
- A ready-scene Goldshire 3x3 grid improved mean error from `0.101402` at zero offsets to `0.087952` at yaw `10`/pitch `-10`; pitch/FOV refinement remains required before manifest calibration.
- Joint Goldshire pitch/FOV refinement had no human-approved optimum: full-frame error preferred grass-heavy FOV 62/pitch -25, while FOV 38/pitch -10 better matched building scale. Landmark/region scoring is required before calibration.
- Common-region scoring `[300,100,1400,650]` moved Goldshire's numeric optimum to FOV 38/yaw 10/pitch -15, but human inspection still found an oversized/cropped facade; semantic landmarks or masks remain required.
- Final paired audit compared fourteen cold/warm images for seven private build 12340 references with zero missing pairs; mean error ranges `0.066893..0.176955`. Animated-M2 native motion is observed across two original-client phases, but its real Lakeshire placement differs from the synthetic Goldshire probe. Sky-transition evidence uses the checkpoint map position/time with manually approved approximate framing.
- D3D12 descriptor issues remain. Explicit shutdown ownership removes the GUI capture's Node/resource/RID leaks in focused and seven-checkpoint runs; a timing-dependent ObjectDB warning for anonymous zero-reference `RefCounted` objects remains as a separate engine-teardown diagnostic.
- M2/WMO/material/particle/ribbon/portal parity incomplete.
- Public API is mostly exported configuration rather than stable contracts.
- `probe_render_terrain_height.gd` remains a renderer-only diagnostic exception
that reads tile readiness/terrain internals; it is excluded from the gameplay/
EditorPlugin gate until equivalent ground-query and readiness facade contracts exist.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/world_render_facade.gd` | Stable caller boundary for typed focus and detached renderer metrics |
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
| `src/render/streaming/streaming_target_plan.gd` | Immutable planner result with read-only tile-key sets |
| `src/render/streaming/render_budget_scheduler.gd` | Scene-free per-frame operation permits and terminal cancellation |
| `src/scenes/streaming/streaming_world_loader.gd` | Current runtime orchestration, queues, caches and attachment |
| `src/domain/streaming/streaming_focus.gd` | Immutable camera-independent streaming position contract |
| `src/tools/verify_streaming_focus.gd` | Headless contract, dependency and runtime/tool wiring regression |
| `src/tools/verify_world_render_facade.gd` | Facade delegation, snapshot isolation and consumer wiring regression |
| `src/tools/verify_streaming_target_planner.gd` | Planner behavior, dependency and bounded timing regression |
| `src/tools/verify_render_budget_scheduler.gd` | Scheduler bounds, shared-lane priority, cancellation and timing regression |
| `src/tools/verify_renderer_internal_access.gd` | Gameplay/EditorPlugin boundary gate derived from private streamer fields |
| `addons/mpq_extractor/loaders/adt_builder.gd` | Terrain/material/liquid construction |
| `addons/mpq_extractor/loaders/m2_builder.gd` | Static M2 mesh/material construction |
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Native animated M2 experiment |
| `addons/mpq_extractor/loaders/wmo_builder.gd` | WMO groups/doodads/liquids construction |
| `src/scenes/sky/wow_sky_controller.gd` | DBC-driven sky/light/environment |
| `src/native/src/*_loader.cpp` | Native binary parsing |
| `src/tools/build_*cache.gd`, `src/tools/bake_*cache.gd` | Offline cache generation |
| `tools/run_render_baseline.ps1` | Unified M00 baseline runner |
| `src/tools/compare_render_checkpoints.gd` | Offline JPG/PNG paired-image perceptual metrics and JSON pass/fail report |
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Headless ownership regression for detached runtime prototypes, resource caches and empty liquid roots |
| `src/tools/capture_render_checkpoints.gd` | Deterministic no-roll checkpoint camera, performance and visual capture |
| `src/tools/server_spawn_render_manifest.json` | Dedicated mapped AzerothCore spawn checkpoint and diagnostic marker configuration |
| `src/tools/verify_server_spawn_renderer.gd` | Fixture-to-renderer checkpoint contract verification |
| `tools/sweep_render_checkpoint_camera_pose.ps1` | Offline yaw/pitch capture grid and paired-error ranking |
| `src/tools/verify_render_coordinate_calibration.gd` | Build 12340 camera-coordinate golden point round-trip diagnostic |
| `src/tools/probe_render_terrain_height.gd` | Offline active-mesh terrain height and camera-clearance report |
| `src/tools/probe_render_camera_occluders.gd` | Scene-tree placement containment and camera-to-target AABB intersection report |
## Related decisions and references
- [`../../RENDER.md`](../../RENDER.md)
- [`../RENDER_BASELINE.md`](../RENDER_BASELINE.md)
- [`../CAMERA_POSE_SWEEP.md`](../CAMERA_POSE_SWEEP.md)
- [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
- [`../GODOT_BEST_PRACTICES.md`](../GODOT_BEST_PRACTICES.md)