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

529 lines
43 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 facade/planner/scheduler/internal-access/ground/environment/entity packages; M03 terrain packages; M03 M2 unique/transform/grouping packages |
| Owners | Renderer workstream / milestone integrator |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-placement-grouper`, 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
Loader --> TerrainCache[TerrainQualityMeshCache]
TerrainCache --> Loader
Loader --> ChunkLod[TerrainChunkLodPlanner]
ChunkLod --> Loader
Loader --> ChunkQueue[TerrainChunkGeometryQueuePlanner]
ChunkQueue --> Loader
Loader --> M2Registry[M2UniquePlacementRegistry]
M2Registry --> Loader
Loader --> M2Grouper[M2PlacementGrouper]
M2Grouper --> M2Transform[M2PlacementTransformResolver]
M2Transform --> M2Grouper
M2Transform --> Loader
Native --> Parsed[Parsed tile/model data]
Parsed --> Loader
Loader --> Scene[SceneTree nodes]
Loader --> RS[RenderingServer RIDs/MultiMesh]
EnvironmentState[World time producer] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
EnvironmentSnapshot --> Facade
Facade --> SkyController[WowSkyController]
Sky[DBC sky/light data] --> SkyController
SkyController --> Scene
EntityState[Entity visual read model] --> EntitySnapshot[EntityPresentationSnapshot]
EntitySnapshot --> Facade
Facade --> EntityPresenter[WorldEntityPresenter]
EntityPresenter --> 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, immutable
environment/entity presentation input, metrics and rendered-ground diagnostics.
`StreamingWorldLoader`, `WowSkyController` and `WorldEntityPresenter` remain
internal scene implementations.
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.sample_ground_height` | Public read-only query | Samples already loaded render terrain at a typed Godot world position | Main thread; immutable caller-held result | Stable unavailable code for missing mesh/intersection/facade; not gameplay authority |
| `WorldRenderFacade.renderer_ground_query_snapshot` | Public diagnostic query | Returns ground height plus detached tile/readiness context without exposing mutable state | Main thread; caller-owned deep copy | Missing/invalid renderer returns empty snapshot with diagnostic |
| `WorldEnvironmentSnapshot.at_time_of_day` | Immutable value factory | Normalizes a finite authoritative time into `[0, 24)` hours | Any thread; caller-held value | Non-finite input creates invalid value with `environment_time_not_finite` |
| `WorldRenderFacade.apply_environment_snapshot` | Public command | Delegates exact world time to the scene-owned DBC sky controller | Main thread; snapshot remains caller-owned | Returns `false` for invalid input or missing/incompatible controller |
| `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 |
| `WorldRenderFacade.world_environment_controller_path` | Internal adapter property | Resolves the existing `WowSkyController` without exposing it to consumers | Main thread, scene lifetime | Missing method/path produces one diagnostic until recovery |
| `EntityPresentationSnapshot.create` | Immutable value factory | Validates contract-v1 identity, typed position, visual path, yaw, scale and visibility | Any thread; caller-owned | Stable invalid code; never mutates scene |
| `WorldRenderFacade.present_entity` | Public command | Idempotently creates/updates/replaces one session entity visual | Main thread/session | Returns false for invalid input, missing service or visual load/type failure |
| `WorldRenderFacade.remove_entity` | Public command | Removes one known session entity visual | Main thread/session | Invalid/unknown identity returns false |
| `WorldRenderFacade.entity_presentation_snapshot` | Public diagnostic query | Returns detached scalar entity inventory | Main thread/caller-owned copy | Missing/invalid service returns empty dictionary with diagnostic |
| `WorldRenderFacade.world_entity_presenter_path` | Internal adapter property | Resolves renderer-owned entity visual service | Main thread/scene lifetime | Missing methods/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 |
| `TerrainQualityMeshCache.store/restore/clear` | Internal terrain service | Owns admitted full-quality revisit Mesh references and LRU | Renderer thread/map session | Rejected/missed entry leaves loader fallback unchanged |
| `TerrainChunkLodPlanner.plan` | Internal pure terrain query | Maps populated parsed chunk indices to desired LOD 0/1/2 | Synchronous per target refresh | Invalid/disabled input returns empty for existing tile-LOD fallback |
| `TerrainChunkGeometryQueuePlanner.plan` | Internal pure terrain query | Produces chunk create/remove requests from current and desired state | Synchronous per queue rebuild | Invalid focus returns empty arrays |
| `M2UniquePlacementRegistry.reserve/release/clear` | Internal M2 service | Owns positive cross-tile ADT placement-ID reservations | Renderer thread/map session | Invalid/unkeyed/non-owner inputs preserve documented fallback |
| `M2PlacementTransformResolver.resolve_basis/resolve_origin_offset` | Internal pure M2 service | Resolves regular and calibrated model-specific ADT placement transforms | Worker/main thread; stateless | Unknown paths use regular basis and zero offset |
| `M2PlacementGrouper.group_placements` | Internal pure M2 service | Validates and groups ordered tile-local placement transforms by normalized path | Worker/main thread; stateless | Invalid variants/name IDs/empty paths are skipped |
Публичным 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 | Ground-query `GodotWorldPosition` | Renderer probe/future adapter | `WorldRenderFacade` | Immutable caller-held value | One main-thread query |
| Input | `WorldEnvironmentSnapshot` | World-state/capture adapter | `WorldRenderFacade` / `WowSkyController` | Immutable caller-held value; not retained | One main-thread update |
| Input | `EntityPresentationSnapshot` | Future world-state/preview adapter | `WorldRenderFacade` / entity presenter | Immutable caller-held full visual snapshot | One main-thread present/update |
| Input | `EntityId` removal command | Future entity registry/adapter | `WorldRenderFacade` / entity presenter | Immutable session-local value | One main-thread removal |
| 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 |
| Internal cache | Full-quality terrain Mesh/source | Loader terrain upgrade / `TerrainQualityMeshCache` | Revisited tile state | Cache-retained Mesh reference | Map session until eviction/reset |
| Internal plan | Parsed chunks, typed focus and immutable chunk-LOD policy | Loader / `TerrainChunkLodPlanner` | Loader desired state | Fresh dictionary; no retained resources | One target refresh |
| Internal plan | Current/desired chunk state and typed focus | Loader / `TerrainChunkGeometryQueuePlanner` | Loader chunk queues | Fresh request arrays; no retained resources | One queue rebuild |
| Internal registry | Tile key and M2 placement unique IDs | Loader / `M2UniquePlacementRegistry` | Filtered grouping input and tile retry state | Registry-owned strings; fresh result arrays | Map session |
| Internal transform | Rotation/path/scale | Loader or grouper / `M2PlacementTransformResolver` | Group/placeholder/instance transforms | Value-only Basis/Vector3 | One placement |
| Internal grouping | Tile origin, M2 names and placements | Loader / `M2PlacementGrouper` | Loader worker result/build job | Fresh Dictionary/Transform3D arrays | One grouping task |
| 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 | `RenderedGroundSample` | `WorldRenderFacade`/loaded terrain | Renderer diagnostics | Immutable caller-held value | One query |
| Output | Detached rendered-ground snapshot | `WorldRenderFacade` | Terrain-height probe | Caller-owned deep copy | One query |
| Output | Entity visual Node3D subtree | `WorldEntityPresenter` | SceneTree/viewport | Presenter-owned child | Until replace/remove/teardown |
| Output | Detached entity inventory | `WorldRenderFacade` | Diagnostics/tests | Caller-owned deep copy | One query |
| 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 --> TerrainCache[TerrainQualityMeshCache store]
TerrainCache --> Revisit[Revisited tile state restore]
R --> ChunkLod[TerrainChunkLodPlanner]
ChunkLod --> DesiredChunkLod[Desired chunk LOD state]
DesiredChunkLod --> ChunkQueue[TerrainChunkGeometryQueuePlanner]
ChunkQueue --> B
R --> M2Registry[M2UniquePlacementRegistry reserve]
M2Registry --> M2Grouper[M2PlacementGrouper]
M2Transform[M2PlacementTransformResolver] --> M2Grouper
M2Grouper --> M2
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]
Loaded[Loaded terrain Mesh and Transform] --> Ground[Facade ground query]
Ground --> GroundSample[RenderedGroundSample / detached diagnostics]
WorldTime[Authoritative/test time] --> EnvironmentSnapshot[WorldEnvironmentSnapshot]
EnvironmentSnapshot --> RF
RF --> SkyController[WowSkyController]
DBC[Light/Area/Skybox DBC] --> SkyController
SkyController --> World
EntityReadModel[Entity visual read model] --> EntitySnapshot[EntityPresentationSnapshot]
EntitySnapshot --> RF
RF --> EntityPresenter[WorldEntityPresenter]
EntityAsset[PackedScene path] --> EntityPresenter
EntityPresenter --> World
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
Source->>Facade: sample_ground_height(typed position)
Facade->>Stream: sample loaded render terrain
Stream-->>Facade: RenderedGroundSample / diagnostic snapshot
Source->>Facade: apply_environment_snapshot(exact time)
Facade->>Render: delegate to WowSkyController
Render->>Render: freeze local clock; retain DBC sampling/apply rules
Source->>Facade: present_entity(full visual snapshot)
Facade->>Render: delegate to WorldEntityPresenter
Render->>Render: create/update/replace owned entity root
Source->>Facade: remove_entity(EntityId)
Facade->>Render: detach and queue_free owned root
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, environment controller, focus source, returned metrics, queues,
caches, nodes or RIDs.
- `WorldEnvironmentSnapshot` is an immutable caller-owned scalar value. Neither
facade nor sky controller retains it; the controller copies normalized hours
into its existing fixed-clock fields on the main thread.
- `WowSkyController` continues to own DBC sampling state and mutations of its
duplicated Godot `Environment`, sun, skybox node and world shader globals.
- `WorldEntityPresenter` owns one optional visual Node3D subtree per presented
`EntityId`; it owns no authoritative entity state and excludes the local player.
- Entity presentation snapshots remain caller-owned inputs. Detached diagnostics
expose only identity debug keys, paths and visibility, never Node/Resource/RID.
- `TerrainQualityMeshCache` owns only full-quality revisit Mesh references and
LRU keys. The loader retains terrain tasks/results, tile state, source choice,
cache versions and every material/Node/RID finalization side effect.
- `TerrainChunkLodPlanner` is stateless and owns only call-local horizontal
distance calculations. The loader retains parsed chunks, desired state,
queues, budgets and all Mesh/Node/RID application side effects.
- `TerrainChunkGeometryQueuePlanner` is stateless and owns only call-local
create/remove request calculation and sorting. The loader adopts the fresh
arrays and retains removal-first drains, budgets and render side effects.
- `M2UniquePlacementRegistry` owns only positive unique-key to tile-key strings.
The loader retains owned/skipped tile arrays, candidate retry, grouping/build
tasks, caches and all MultiMesh/Node/Mesh/material/RID side effects.
- `M2PlacementTransformResolver` is stateless and retains no resources. The
grouper owns worker-path final transforms; direct placeholder/instance
transforms and every build/render side effect remain loader-owned.
- `M2PlacementGrouper` is stateless and owns only call-local grouped transforms.
The loader retains tasks, mutex/result queues, stale checks and build state.
- Rendered-ground query results and diagnostic snapshots are caller-owned values;
the facade never returns Mesh, Node, tile-state or queue references.
- Loaded-mesh ground sampling is renderer diagnostics, not authoritative gameplay
collision. M02 `TerrainQuery` composition and ADT cache ownership remain unchanged.
- `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 |
| Invalid environment time | Snapshot finite-value validation | Reject update; retain prior sky clock | `environment_time_not_finite` on invalid value | Supply a finite time snapshot |
| Missing environment controller | Facade path/method validation | Reject environment update; other facade APIs remain available | One facade diagnostic until recovery | Fix scene composition path/implementation |
| Invalid entity visual snapshot | Contract validation | Reject without scene mutation | Stable `entity_presentation_*` failure code | Repair adapter read model and resend |
| Missing/invalid entity visual asset | Resource existence/load/root check | New entity absent or prior replacement retained | Named presenter warning | Restore asset mapping and resend snapshot |
| Missing entity presenter | Facade path/method validation | Reject entity commands; other facade APIs remain available | One facade diagnostic until recovery | Fix runtime scene composition |
| 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 |
| Render terrain unavailable | Facade ground query | Immutable unavailable sample | Stable `render_terrain_*` failure code and optional snapshot | Wait for streaming or use gameplay-owned query backend |
| 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.
- Terrain chunk LOD contract: horizontal chunk-center distance, inclusive
thresholds, sparse indices, disabled/raw-custom policy, loader delegation and
bounded 250-by-256 planning without loading a world scene.
- Terrain chunk geometry queue contract: current/desired comparison, tile-LOD
takeover, sparse origin fallback, stable removals, nearest creates, loader
drain boundary and bounded multi-tile timing without a world scene.
- M2 unique placement contract: unkeyed pass-through, within-call/cross-tile
dedupe, owner release/retry, clear, detached diagnostics, loader boundary and
bounded reserve/release timing without a world scene.
- M2 placement transform contract: regular/cliffrock/waterfall formulas, path
normalization, scale compensation, all three consumers and bounded timing.
- M2 placement grouper contract: invalid/default records, historical path rules,
group order, local transforms, fresh output, source boundary and bounded timing.
- 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.
- Ground-query facade contract: available/unavailable typed result delegation,
detached nested diagnostics and terrain-probe source migration without private access.
- Rendered-ground value contract: available/non-finite/unavailable invariants pass
from a cold Godot class cache before editor import.
- 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 |
| M2 unique placement registry | Implemented extraction | Scene-free ownership/lifecycle/timing contract and historical `uid:11785` smoke | Group/build/tasks/finalization and asset-backed p95/p99 remain pending |
| M2 placement transform resolver | Implemented extraction | Scene-free formula/source/timing contract across three consumers | Asset-backed visual recheck and general placement parity pending |
| M2 placement grouper | Implemented extraction | Scene-free validation/order/transform/source/timing contract | Worker/build state, spatial cells and asset-backed p95/p99 remain pending |
| 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 | Implemented boundary / Partial fidelity | Facade contracts cover focus, environment, entity visuals, metrics and loaded-mesh ground query | Production consumers and fidelity remain |
| Environment snapshot input | Implemented boundary / Partial fidelity | Immutable time contract, facade/controller delegation and checkpoint migration | Weather/indoor state and paired zone-transition fidelity remain |
| Entity presentation commands | Implemented boundary / Prototype visuals | Versioned typed snapshot plus present/remove lifecycle and runtime service composition | Display/equipment/animation/effects and async scale path remain |
| Terrain quality Mesh cache service | Implemented extraction | Scene-free admission/LRU/clear contract and loader delegation | Remaining terrain build/state/finalization is monolithic |
| Terrain chunk LOD planner | Implemented extraction | Scene-free formula/dependency/timing contract and loader delegation | Queue/state/finalization and asset-backed p95/p99 remain pending |
| Terrain chunk geometry queue planner | Implemented extraction | Scene-free request/order/dependency/timing contract and loader delegation | Queue drains/state/finalization and asset-backed p95/p99 remain pending |
| 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/tool internal-access boundary | Implemented | Access gate scans gameplay, EditorPlugin packages and terrain probe against actual private streamer declarations | Add future renderer diagnostic tools explicitly to the protected list |
| Rendered terrain ground query | Implemented | `M03-RND-FACADE-GROUND-QUERY-001` typed result/snapshot isolation and probe migration | Diagnostic Mesh ray only; not movement/collision authority |
## 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.
- Rendered-ground sampling generates a triangle mesh on demand and retains the
historical nearby seam fallback. It is appropriate for diagnostics, not a
per-physics-frame gameplay query or a claim of exact terrain collision parity.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/world_render_facade.gd` | Stable caller boundary for typed focus/environment, detached metrics and rendered-ground queries |
| `src/render/environment/world_environment_snapshot.gd` | Immutable normalized world time input with explicit invalid state |
| `src/render/entities/entity_presentation_snapshot.gd` | Immutable contract-v1 entity visual input and validation |
| `src/render/entities/world_entity_presenter.gd` | Main-thread entity visual subtree ownership and lifecycle |
| `src/render/terrain/rendered_ground_sample.gd` | Immutable renderer-owned available/unavailable ground query result |
| `src/render/terrain/terrain_quality_mesh_cache.gd` | Full-quality terrain revisit Mesh retention and LRU ownership |
| `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` | Focus/environment/metrics/ground delegation, snapshot isolation and consumer wiring regression |
| `src/tools/verify_world_environment_snapshot.gd` | Cold-start environment value normalization and invalid-input regression |
| `src/tools/verify_world_entity_presentation.gd` | Entity snapshot, create/update/replace/remove and ownership regression |
| `src/tools/verify_rendered_ground_sample.gd` | Cold-start renderer ground result invariant regression |
| `src/tools/verify_terrain_quality_mesh_cache.gd` | Terrain cache admission, LRU, ownership and loader-boundary 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/registered renderer-tool 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 facade-backed 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)