529 lines
43 KiB
Markdown
529 lines
43 KiB
Markdown
# 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)
|