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

215 lines
9.9 KiB
Markdown

# 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.
```mermaid
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
```mermaid
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
```mermaid
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
```mermaid
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 |
## Related decisions and references
- [`wmo-render-build-step-planner.md`](wmo-render-build-step-planner.md)
- [`wmo-render-build-queue.md`](wmo-render-build-queue.md)
- [`wmo-runtime-mesh-finalizer.md`](wmo-runtime-mesh-finalizer.md)
- [`world-renderer.md`](world-renderer.md)
- [`../../RENDER.md`](../../RENDER.md)
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)