Files
open-wc/docs/modules/wmo-render-group-materializer.md
T
sindoring f4d5e29cc9 render: extract WMO render group materializer
Work-Package: M03-RND-WMO-RENDER-GROUP-MATERIALIZER-001
Agent: sindo-main-codex
Tests: 63/64 autonomous verifiers passed; proprietary ADT probe unavailable; baseline dry-run 7/7; documentation and coordination gates passed
Fidelity: exact behavior-preserving node materialization extraction; no new 3.3.5a parity claim
2026-08-01 10:22:21 +04:00

9.9 KiB

WMO Render Group Materializer

Metadata

Field Value
Status Implemented
Target M03 Renderer Facade and Safe Extraction
Work package M03-RND-WMO-RENDER-GROUP-MATERIALIZER-001
Owner Render
Last verified 2026-08-01

Purpose

WmoRenderGroupMaterializer creates and attaches one lightweight cached WMO render group on the renderer main thread. It owns the duplicated MeshInstance3D/MultiMeshInstance3D presentation rules that previously lived inside StreamingWorldLoader.

Non-goals

  • select a build step, advance/cancel a job or consume a render permit;
  • load, cache, validate or finalize WMO Resources;
  • resolve placement transforms or own the placement root;
  • choose Editor persistence policy or serialize generated nodes;
  • change WMO cache formats, materials, visibility or shadow policy.

Context and boundaries

The loader obtains a queue-owned WMO root and render Resource, asks WmoRenderBuildStepPlanner for one operation, and finalizes the selected Mesh. The materializer then performs only indexed node presentation and attachment. The loader retains scheduler, queue and Editor composition responsibilities.

flowchart LR
    Queue[WmoRenderBuildQueue] --> Loader[StreamingWorldLoader]
    Planner[WmoRenderBuildStepPlanner] --> Loader
    Loader --> Finalizer[WmoRuntimeMeshFinalizer]
    Loader --> Materializer[WmoRenderGroupMaterializer]
    Materializer --> MeshNode[MeshInstance3D]
    Materializer --> MultiMeshNode[MultiMeshInstance3D]
    MeshNode --> Root[Queue-owned WMO Node3D root]
    MultiMeshNode --> Root
    Loader --> EditorOwner[Optional Editor owner assignment]

Public API

Symbol Role Thread/lifetime Failure behavior
materialize_mesh_group(...) Create, configure and attach one indexed Mesh group Renderer main thread; stateless after return Null/invalid parent, null Mesh or negative index returns null
materialize_multimesh_group(...) Create, configure and attach one indexed MultiMesh doodad group Renderer main thread; stateless after return Null/invalid parent, null MultiMesh or negative index returns null

Both methods preserve exact Resource identity. group_index selects the optional name and transform; missing names use the historical indexed fallback and a missing transform leaves Transform3D.IDENTITY. Positive visibility range applies its caller-supplied margin. Shadow mode is always applied explicitly.

Inputs and outputs

Direction Contract/data Producer Consumer Ownership Thread/lifetime
Input Queue-owned WMO Node3D root WmoRenderBuildQueue via loader Materializer Borrowed; not retained One main-thread call
Input Selected Mesh or MultiMesh WMO render Resource via loader Materializer Borrowed exact Resource Parent-node lifetime after attach
Input Names, transforms and selected index WMO render Resource/planner via loader Materializer Borrowed value collections One call
Input Visibility end/margin and shadow flag Loader quality profile Materializer Scalar values One call
Output Attached MeshInstance3D or MultiMeshInstance3D Materializer Loader/SceneTree Parent root owns node and Resource reference Until placement release/world teardown

Side effects:

  • allocates exactly one Godot geometry node for valid input;
  • applies name, optional transform, shadow and optional visibility settings;
  • attaches the node exactly once to the supplied WMO root.

It performs no filesystem, ResourceLoader, worker, RenderingServer RID, cache, queue, permit, logging or direct Editor-owner mutation.

Data flow

flowchart LR
    Input[Root + Resource + indexed metadata + render settings] --> Validate{Valid root/resource/index?}
    Validate -->|no| Null[Return null; no attachment]
    Validate -->|yes| Kind{Mesh or MultiMesh method}
    Kind --> Mesh[Create MeshInstance3D]
    Kind --> Multi[Create MultiMeshInstance3D]
    Mesh --> Configure[Name + optional transform + render settings]
    Multi --> Configure
    Configure --> Attach[Attach once to WMO root]
    Attach --> Return[Return borrowed attached node]

Main sequence

sequenceDiagram
    participant Loader as StreamingWorldLoader
    participant Queue as WmoRenderBuildQueue
    participant Finalizer as WmoRuntimeMeshFinalizer
    participant Materializer as WmoRenderGroupMaterializer
    participant Root as WMO Node3D root
    Loader->>Queue: read front job and cursors
    Loader->>Finalizer: finalize selected Mesh
    Loader->>Materializer: materialize selected group
    Materializer->>Root: add_child(geometry instance)
    Materializer-->>Loader: attached node or null
    Loader->>Loader: optional Editor ownership
    Loader->>Queue: adopt planned cursors
    Loader->>Loader: consume one group permit

Dependency diagram

flowchart TB
    Loader[StreamingWorldLoader] --> Materializer[WmoRenderGroupMaterializer]
    Materializer --> Engine[Node3D / GeometryInstance3D / Mesh / MultiMesh]
    Materializer -. no dependency .-> IO[ResourceLoader / FileAccess]
    Materializer -. no dependency .-> Queue[WmoRenderBuildQueue]
    Materializer -. no dependency .-> Scheduler[RenderBudgetScheduler]
    Materializer -. no dependency .-> Finalizer[WmoRuntimeMeshFinalizer]

Ownership, threading and resources

  • Both public methods are main-thread only because they mutate the SceneTree.
  • The caller owns the parent root; after attachment the root owns the new node.
  • The node retains the exact input Mesh or MultiMesh Resource reference.
  • The service retains no Node, Resource, RID, collection or per-group state.
  • Loader-owned optional recursive Editor ownership runs after successful return.

Errors, cancellation and recovery

Failure/state Detection Behavior Recovery
Null/freed parent Guard Return null; allocate/attach nothing Caller validates current queue job
Null Resource Guard Return null Loader advances the historically selected cursor
Negative index Guard Return null Planner supplies non-negative selected indices
Missing name Bounds check Use Group_N or DoodadGroup_N Rebuild cache metadata if desired
Missing transform Bounds check Retain identity transform Rebuild cache metadata if desired
Placement cancellation Loader/queue Parent and children released by existing lifecycle Re-request placement later
Shutdown Loader lifecycle Service has no retained state to drain New loader composes a new service

Configuration, capabilities and profiles

The service introduces no configuration or capability. It accepts the existing wmo_visibility_range, CHUNK_SIZE margin and wmo_cast_shadows values chosen by the loader quality profile. Blizzlike and Enhanced selection remains outside.

Persistence, cache and migrations

No persisted data or cache format changes. The service neither reads nor writes WMO cache files and requires no migration or rebake.

Diagnostics and observability

The service emits no logs or metrics. Existing wmo_groups queue depth, build permits and loader lifecycle diagnostics remain authoritative.

Verification and fidelity evidence

  • verify_wmo_render_group_materializer.gd covers exact Mesh/MultiMesh identity, indexed and fallback names, optional transforms, shadows, positive/disabled visibility, attachment, invalid input, source boundaries and 1,000 groups.
  • Adjacent WMO queue/planner/finalizer and checkpoint regressions protect the unchanged orchestration and visible output.
  • This is an exact code-motion extraction of existing Godot presentation rules; it adds no original-client 3.3.5a visual parity claim.

Performance budgets

The synthetic contract requires 1,000 simple Mesh group materializations in under one second. Production work remains limited to one group per scheduler permit. Asset-backed CPU/GPU p95/p99 and long traversal remain required evidence.

Extension points

  • Asset-backed WMO traversal can measure group attachment and lifetime without changing this API.
  • Additional render settings belong here only when they apply equally to both lightweight group-node kinds and have fidelity evidence.

Capability status

Capability Status Evidence Gap/next step
Mesh group materialization Implemented extraction Identity/name/transform/render/attachment contract Asset-backed visual/GPU p95/p99 pending
MultiMesh doodad group materialization Implemented extraction Identity/name/transform/render/attachment contract Asset-backed traversal/leak evidence pending
Build planning/queue progress Loader-owned Existing planner/queue regressions Further orchestration extraction pending
Editor persistence ownership Loader-owned Source-boundary contract Editor scene-save integration evidence pending

Known gaps and risks

  • SceneTree node creation remains synchronous main-thread work by design.
  • The synthetic fixture does not measure private WMO assets, GPU upload, portal visibility, original-client visuals, long traversal or leak behavior.

Source map

Path Responsibility
src/render/wmo/wmo_render_group_materializer.gd Indexed geometry-node creation, settings and attachment
src/scenes/streaming/streaming_world_loader.gd Composition, finalization, queue, permits, Editor ownership and lifecycle
src/tools/verify_wmo_render_group_materializer.gd Synthetic contract, ownership boundary and timing regression