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

8.1 KiB

M2 Placement Transform Resolver

Metadata

Field Value
Status Implemented
Target/work package M03 / M03-RND-M2-PLACEMENT-TRANSFORM-001
Owners Pure ADT M2 placement basis and model-specific origin compensation
Last verified Worktree work/sindo-main-codex/m03-m2-placement-transform, 2026-07-16
Profiles/capabilities Existing static M2 paths; four calibrated model exceptions

Purpose

Resolve the unscaled Godot-space orientation and optional local origin offset used by ADT M2 worker grouping, placeholder rendering and instance creation.

Non-goals

  • Convert WoW/server/ADT coordinates or own placement positions.
  • Apply final scale clamps, build transforms, group placements or create Nodes.
  • Add, generalize or configure model-specific corrections.
  • Change M2 materials, animation, caches, visibility or quality profiles.
  • Claim general build-12340 placement parity.

Context and boundaries

flowchart LR
    ADT[ADT placement rotation/path/scale] --> Resolver[M2PlacementTransformResolver]
    Resolver --> Basis[Unscaled Basis]
    Resolver --> Offset[Local origin offset]
    Basis --> Group[Worker grouping]
    Basis --> Placeholder[Placeholder MultiMesh path]
    Basis --> Instance[Instance path]
    Offset --> Group
    Offset --> Placeholder
    Offset --> Instance
    Group --> Loader[StreamingWorldLoader-owned queues/build]

Allowed dependencies are Vector3, Basis, scalar math and path-string normalization. Node, SceneTree, RenderingServer, ResourceLoader, tasks, caches, files, gameplay, network and editor UI are forbidden.

Public API

Symbol Kind Purpose Thread/lifetime Errors
resolve_basis(rotation_radians, relative_m2_path) Pure query Return unscaled regular or calibrated placement orientation Worker/main thread; call-local result Unknown paths use Basis.from_euler
resolve_origin_offset(rotation_radians, relative_m2_path, scale_value) Pure query Compensate the measured tall-waterfall twist anchor Worker/main thread; call-local result Regular/unanchored paths return zero; scale clamps to 0.0001 for compensation

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Godot Euler rotation in radians ADT loader placement record Resolver Scalar copy One call
Input Relative M2 path ADT MMDX/name table Resolver allowlist Caller string One call
Input Raw placement scale ADT placement record Offset compensation Scalar copy One call
Output Unscaled Basis Resolver Group/placeholder/instance adapter Value copy One call
Output Local Vector3 offset Resolver Transform origin adapter Value copy One call

Callers preserve their historical basis scale minima: grouping/instance use 0.0001, while placeholders use 0.01.

Data flow

flowchart TD
    BasisCall[resolve_basis] --> ModelName[Normalize separators/case/basename]
    ModelName --> Waterfall{Waterfall sheet?}
    Waterfall -->|yes| WaterfallBasis[+90 degree world yaw * Euler * local-axis twist]
    Waterfall -->|no| Cliff{Elwynn cliff rock?}
    Cliff -->|yes| CliffBasis[Corrected Y/BACK/RIGHT composition]
    Cliff -->|no| Regular[Basis.from_euler]
    OffsetCall[resolve_origin_offset] --> Anchored{Tall waterfall anchor?}
    Anchored -->|no| Zero[Vector3.ZERO]
    Anchored -->|yes| Compensation[(base anchor - twisted anchor) * clamped scale]

Lifecycle/state

The resolver is stateless. It may be shared between the main thread and worker calls because each result is computed solely from value inputs. Construction, world reset and shutdown require no clear, cancellation or resource release.

Main sequence

sequenceDiagram
    participant Loader as StreamingWorldLoader
    participant Resolver as M2PlacementTransformResolver
    participant Consumer as Group/placeholder/instance path
    Loader->>Resolver: resolve_basis(rotation, path)
    Resolver-->>Loader: unscaled Basis
    Loader->>Resolver: resolve_origin_offset(rotation, path, scale)
    Resolver-->>Loader: local offset
    Loader->>Consumer: compose historical scaled Transform3D

Ownership, threading and resources

  • The resolver retains no mutable state or engine resources.
  • The loader owns placement records, final transforms, tasks, queues, caches, MultiMesh/Node/Mesh/material/RID creation and cleanup.
  • Pure methods are safe for current worker grouping and main-thread consumers.
  • Returned Basis/Vector3 values have no lifetime coupling to the resolver.

Errors, cancellation and recovery

Failure Detection Behavior Diagnostic Recovery
Empty/unknown path Allowlist miss Regular Euler basis and zero offset Contract fixture Expected fallback
Mixed case/backslashes/MDX Normalization Same calibrated model match Contract fixture None required
Zero/negative scale for anchored offset Numeric clamp Use 0.0001 compensation scale Contract fixture Caller may supply valid ADT scale later
World reset/shutdown No retained state No action Existing shutdown verifier New calls remain independent

There is no asynchronous operation or cancellation inside the resolver.

Configuration and capabilities

The resolver introduces no settings. The preserved constants and allowlists are:

  • world yaw PI for ElwynnCliffRock01/02;
  • world yaw PI/2 for NewWaterfall and ElwynnTallWaterfall01;
  • measured local axes/twist signs for both waterfall sheets;
  • measured tall-waterfall anchor (-2.667799, 89.62273, 0.00129).

Persistence, cache and migration

No persistence, schema or cache format participates. Existing ADT/M2 cache versions and rebuild instructions remain unchanged.

Diagnostics and observability

The resolver emits no logs. The contract verifier compares formulas/constants, checks all three loader consumers and records bounded synthetic timing. Visual and asset-backed evidence remains in the renderer checkpoint workflow.

Verification

  • verify_m2_placement_transform_resolver.gd: regular fallback, path normalization, both cliff rocks, both waterfalls, twist anchor, negative-scale compensation, three loader adapters, source boundary and 10,000-pair timing.
  • M2 unique/dedupe, terrain, facade, internal-access, manifest, shutdown, scheduler, streaming and coordinate regressions remain required.
  • Fidelity evidence is exact extraction of existing calibrated formulas. The private asset/camera comparison that motivated them is not reproduced here.

Extension points

  • The next package may compose this resolver into a pure placement grouper.
  • New model corrections require separate measured fidelity evidence; this module is not a generic override registry.

Capability status

Capability Status Evidence Gap/next step
Consistent transform rules across three M2 paths Implemented Formula/source/timing contract Asset-backed visual recheck pending
General ADT M2 placement parity Partial Existing checkpoint notes Broader fixture coverage required

Known gaps and risks

  • Four model-specific exceptions are empirical compatibility rules.
  • Most placements use raw Godot Euler behavior; independent client-wide transform validation is incomplete.
  • Callers intentionally retain different final scale clamps.
  • Grouping, build, cache and render ownership remain in the streamer.

Source map

Path Responsibility
src/render/m2/m2_placement_transform_resolver.gd Pure basis/offset rules and calibrated allowlists
src/scenes/streaming/streaming_world_loader.gd Three adapters and final Transform3D/resource ownership
src/tools/verify_m2_placement_transform_resolver.gd Formula, source and performance regression