Files
open-wc/docs/modules/wmo-placement-resolver.md
T

9.0 KiB

WMO Placement Resolver

Metadata

Field Value
Status Implemented
Target/work package M03 / M03-RND-WMO-PLACEMENT-RESOLVER-001
Owners Pure WMO cache-key, placement-identity and world-transform rules
Last verified Worktree work/sindo-main-codex/m03-wmo-placement-resolver, 2026-07-17
Profiles/capabilities Existing ADT/WDT WMO placement paths

Purpose

Provide one scene-free boundary for normalized WMO cache keys, MODF placement identity and world-space Godot transforms across render-cache, cached-scene and live-prototype instance paths.

Non-goals

  • Parse ADT/WDT or convert canonical WoW coordinates.
  • Own WMO registry references, tile state, jobs, queues or retries; reference ownership belongs to WmoPlacementRegistry.
  • Load/validate cached scenes or lightweight render resources.
  • Create Nodes, Mesh/MultiMesh, materials or RIDs.
  • Implement portals, rooms, visibility, materials, doodads or WMO parity.

Context and boundaries

flowchart LR
    Parsed[ADT/WDT WMO placement] --> Loader[StreamingWorldLoader adapter]
    Loader --> Resolver[WmoPlacementResolver]
    Resolver --> CacheKey[Normalized cache key]
    Resolver --> Identity[Registry unique key]
    Resolver --> Transform[World Transform3D]
    CacheKey --> Cache[Loader WMO caches/requests]
    Identity --> Registry[WmoPlacementRegistry]
    Transform --> RenderRoot[Lightweight render root]
    Transform --> Scene[Cached scene instance]
    Transform --> Live[Live prototype instance]

Allowed dependencies are Dictionary/String values and Godot Vector3, Basis and Transform3D math. Node, SceneTree, RenderingServer, ResourceLoader, WorkerThreadPool, mutexes, files, gameplay, network and editor UI are forbidden.

Public API

Symbol Kind Purpose Thread/lifetime Errors
normalize_relative_path(relative_wmo_path) Pure query Return lowercase slash-normalized cache/lookup key Any thread; value result Empty input returns empty
resolve_unique_key(placement, tile_key, placement_index) Pure query Return positive UID identity or tile/index fallback Any thread; value result Missing/non-positive UID uses fallback
resolve_world_transform(placement) Pure query Compose world-space position, Godot Euler rotation and uniform scale Any thread; value result Missing fields use zero/zero/one defaults; scale is not clamped

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Relative WMO path WDT/ADT name table Path normalizer Caller String One call
Input Raw placement Dictionary WDT global or ADT MODF parser result Identity/transform queries Read-only shallow value One call
Input Tile key and placement index Loader build job Synthetic identity fallback Copied scalar/String Registry entry lifetime
Output Normalized relative path Resolver Render/scene cache and load-request maps New String value Request/cache lookup
Output uid:* or tile:*:* key Resolver WmoPlacementRegistry and loader ref arrays New String value Until unregister/reset
Output World Transform3D Resolver Three WMO instance adapters Value copy Instance lifetime after assignment

The resolver retains no source Dictionary, output or engine resource.

Data flow

flowchart TD
    Path[relative path] --> Slash[replace backslash with slash]
    Slash --> Lower[lowercase entire key]
    Placement[placement Dictionary] --> UID{unique_id > 0?}
    UID -->|yes| UidKey[uid decimal]
    UID -->|no| TileKey[tile key + placement index]
    Placement --> Defaults[position/rotation/scale defaults]
    Defaults --> Basis[Godot Basis.from_euler]
    Basis --> Scale[unclamped uniform scale]
    Scale --> World[world Transform3D]

Lifecycle/state

The resolver is stateless. Construction, world reset, map switch, cancellation and shutdown require no resolver operation.

Main sequence

sequenceDiagram
    participant Loader as StreamingWorldLoader
    participant Resolver as WmoPlacementResolver
    participant Registry as WmoPlacementRegistry
    participant Instance as Render/cached/live instance
    Loader->>Resolver: normalize_relative_path(path)
    Resolver-->>Loader: cache key
    Loader->>Resolver: resolve_unique_key(placement, tile, index)
    Resolver-->>Registry: identity adopted by loader
    Loader->>Resolver: resolve_world_transform(placement)
    Resolver-->>Loader: value Transform3D
    Loader->>Instance: assign transform and attach/build

Ownership, threading and resources

  • The resolver owns only call-local scalar/value calculations.
  • WmoPlacementRegistry owns placement-key reference sets. The loader owns its key-to-Node map, cache/load-request state, jobs/queues, resource fallback and cancellation.
  • The loader and builders own every Node/Mesh/MultiMesh/material/RID lifecycle.
  • Pure calls are thread-safe; current consumers execute on the main thread.

Errors, cancellation and recovery

Failure Detection Behavior Diagnostic Recovery
Empty path Empty String Empty normalized key Contract fixture Loader cache path rejects/degrades
Missing/non-positive UID Integer rule Tile/index key prevents same-tile collisions Contract fixture Rebake old ADT cache to restore cross-tile UID dedupe
Missing transform fields Dictionary defaults Zero position/rotation; scale one Contract fixture Repair parser/cache input
Zero/negative scale No historical clamp Preserve zero/mirrored basis Contract fixture Preserve source behavior
Resource missing/pending Outside resolver Loader retry/fallback path unchanged Existing loader diagnostics Restore cache/raw asset
Tile unregister/reset Outside resolver Loader releases refs/nodes/jobs Shutdown/registry regressions New placements resolve afresh

Configuration and capabilities

The resolver introduces no setting or profile branch. WMO radii, visibility, shadow/occlusion flags, cache size limits and build budgets remain loader-owned.

Persistence, cache and migration

No format is written. Existing WMO .tscn/lightweight render cache versions and ADT/WDT formats are unchanged. Older placement caches without positive UID retain the existing per-tile fallback and require no migration for this extraction.

Diagnostics and observability

The resolver emits no logs. Its verifier records path/identity/transform/source contracts and bounded 20,000-triple timing. WMO queue/cache/instance metrics remain in the facade/loader diagnostic snapshot.

Verification

  • verify_wmo_placement_resolver.gd: slash/case/empty path, positive UID, missing/zero/negative UID fallback, default and rotated/scaled transform, unclamped scale, historical Node3D property equivalence, stateless results, all loader adapters, dependency boundary and bounded timing.
  • Existing WMO material/cache/shutdown and renderer baseline regressions remain required alongside M2/terrain/facade/internal-access/coordinate gates.
  • Fidelity evidence is exact extraction of current placement rules. No new original-client WMO visual/portal/material parity claim is made.

Extension points

  • WmoPlacementRegistry consumes these stable keys; later packages may extract WMO build/resource state without changing the resolver.
  • Render-group build cursor and resource-selection state require separate lifecycle/cancellation contracts.

Capability status

Capability Status Evidence Gap/next step
Consistent WMO placement identity/transform Implemented extraction Contract/source/timing verifier Asset-backed placement comparison pending
WMO placement registry Implemented extraction Ownership/source/timing verifier Asset-backed cross-tile corpus pending
WMO build services Remains in loader Existing runtime regressions Stateful extraction pending
Portals/rooms/material fidelity Partial M00 checkpoint/material evidence Broader WMO implementation pending

Known gaps and risks

  • Raw placement Dictionaries remain the parser/cache boundary.
  • Old caches without UID cannot dedupe one WMO across ADT tile boundaries.
  • WMO scale remains intentionally unclamped, including zero/negative input.
  • Asset-backed visual/p95/p99 evidence is unavailable in this package.

Source map

Path Responsibility
src/render/wmo/wmo_placement_resolver.gd Path, identity and transform rules
src/render/wmo/wmo_placement_registry.gd Placement reference-set ownership
src/scenes/streaming/streaming_world_loader.gd Node map/cache/build/resource ownership
src/tools/verify_wmo_placement_resolver.gd Contract, source and timing regression