refactor(M03): extract M2 animated scene finalizer
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
| M2 placement grouper | Implemented extraction | [`m2-placement-grouper.md`](m2-placement-grouper.md) |
|
||||
| M2 build batch planner | Implemented extraction | [`m2-build-batch-planner.md`](m2-build-batch-planner.md) |
|
||||
| M2 runtime mesh rebuild classifier | Implemented extraction | [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) |
|
||||
| M2 animated scene finalizer | Implemented extraction | [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md) |
|
||||
| M2 animation load pipeline state | Implemented extraction | [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md) |
|
||||
| M2 mesh load pipeline state | Implemented extraction | [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md) |
|
||||
| M2 mesh resource cache state | Implemented extraction | [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md) |
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
# M2 Animated Scene Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-ANIMATED-SCENE-FINALIZER-001` |
|
||||
| Owners | Detached animated candidate, material repair and player validation |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animated-scene-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Finalize one loaded animated M2 scene on the main thread: instantiate a valid
|
||||
detached Node3D candidate, copy the historical static-prototype material mapping
|
||||
and accept only candidates containing AnimationPlayer descendants.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Call ResourceLoader or interpret threaded-load statuses.
|
||||
- Choose animation eligibility, cache paths, permits or material prototypes.
|
||||
- Adopt prototypes, mark static fallback or select/play animations.
|
||||
- Process native M2 animators or static Mesh rebuilds.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] -->|loaded Resource and material source| Finalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer -->|accepted Node3D and player count| Loader
|
||||
Loader --> Cache[M2PrototypeCacheState]
|
||||
```
|
||||
|
||||
Allowed dependencies are Godot scene/resource/material types. ResourceLoader,
|
||||
filesystem, workers, scheduler, builders, raw repositories, prototype cache and
|
||||
other application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `instantiate_candidate(resource)` | Ownership query | Instantiate PackedScene as detached Node3D | Main thread; caller receives valid candidate | Unsupported/invalid null; created wrong-type root freed |
|
||||
| `repair_materials(animated_root, material_source_root)` | Command | Apply historical material overrides | Main thread; borrowed roots | Null/empty roots no-op |
|
||||
| `finalize_candidate(candidate_root)` | Ownership command/query | Require AnimationPlayer and transfer root/count | Main thread; detached candidate | Null empty; rejected candidate freed |
|
||||
| `mesh_instances_in_subtree(root)` | Query | Depth-first MeshInstance3D inventory | Main thread; borrowed subtree | Null returns empty |
|
||||
| `animation_players_in_subtree(root)` | Query | Depth-first AnimationPlayer inventory | Main thread; borrowed subtree | Null returns empty |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Loaded Resource | Loader I/O adapter | Candidate instantiator | Borrowed Resource | One finalize permit |
|
||||
| Input | Static material prototype Node3D | Loader cache/build adapter | Material repair | Borrowed Node | One repair call |
|
||||
| Input | Detached animated candidate | Instantiator | Repair/final validation | Finalizer then caller/release | One attempt |
|
||||
| Output | Accepted prototype and player count | Finalizer | Loader adoption/log adapter | Exact Node transferred | Shutdown cache lifetime |
|
||||
| Output | Depth-first engine-node arrays | Traversal | Loader preparation/playback | Borrowed references | One call |
|
||||
|
||||
Side effects are PackedScene instantiation, surface override assignment and
|
||||
synchronous destruction of rejected detached roots.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Resource[Loaded Resource] --> Packed{PackedScene?}
|
||||
Packed -->|no| Reject[Return null]
|
||||
Packed -->|yes| Instantiate[Instantiate]
|
||||
Instantiate --> Root{Node3D?}
|
||||
Root -->|no| FreeWrong[Free root and reject]
|
||||
Root -->|yes| Repair[Repair materials]
|
||||
Repair --> Players{AnimationPlayer exists?}
|
||||
Players -->|no| FreeCandidate[Free and reject]
|
||||
Players -->|yes| Transfer[Transfer exact root and count]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Absent
|
||||
Absent --> Candidate: instantiate valid scene
|
||||
Candidate --> Candidate: repair materials
|
||||
Candidate --> Accepted: player found
|
||||
Candidate --> Released: no player
|
||||
Accepted --> [*]: ownership transferred
|
||||
Released --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant F as M2AnimatedSceneFinalizer
|
||||
participant C as M2PrototypeCacheState
|
||||
L->>F: instantiate_candidate(Resource)
|
||||
F-->>L: detached Node3D or null
|
||||
L->>L: get static material prototype
|
||||
L->>F: repair_materials(candidate, source)
|
||||
L->>F: finalize_candidate(candidate)
|
||||
alt accepted
|
||||
F-->>L: exact Node3D and player count
|
||||
L->>C: adopt animated prototype
|
||||
else rejected
|
||||
F-->>L: empty; candidate freed
|
||||
L->>C: mark animation static
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer --> Engine[PackedScene / Node3D / Mesh / Material / AnimationPlayer]
|
||||
Loader --> Resource[ResourceLoader]
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Loader --> Prototype[M2PrototypeCacheState]
|
||||
Finalizer -. no dependency .-> Resource
|
||||
Finalizer -. no dependency .-> Budget
|
||||
Finalizer -. no dependency .-> Prototype
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Every method runs synchronously on the renderer main thread.
|
||||
- A valid candidate is detached and finalizer-owned until acceptance.
|
||||
- Acceptance transfers the exact Node3D to the loader/prototype cache path.
|
||||
- Rejection frees the candidate synchronously, including wrong-type roots.
|
||||
- Traversal results borrow Nodes; source materials remain Resource-owned.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null/unsupported Resource | Type guard | Return null | Dedicated verifier | Loader marks static |
|
||||
| Wrong root type | Instantiated type guard | Free and return null | Node-count regression | Rebuild cache |
|
||||
| No material source/meshes | Null/empty traversal | Leave materials unchanged | Material fixture | Imported materials remain |
|
||||
| Missing source surface | Material lookup | First source material fallback | Mapping fixture | Repair static source |
|
||||
| No AnimationPlayer | Descendant inventory | Free and return empty | Lifetime regression | Loader marks static |
|
||||
| Shutdown/cancellation | Not owned | No retained state | N/A | Loader drains first |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Finalize permit | `m2_animation_finalize_ops_per_tick = 1` | Existing profiles | Yes | Caller bounds calls |
|
||||
| Material mapping | Depth-first/index-clamped | All | No | Preserves current appearance |
|
||||
| Required players | At least one | All | No | Rejects non-animated scenes |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The service retains no state and serializes nothing. GLB/cache formats, material
|
||||
versions and prototype lifetimes are unchanged; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Accepted result reports player count for the existing `M2_ANIM_CACHE` log.
|
||||
- Traversal order is deterministic depth-first preorder.
|
||||
- The service owns no logging or metrics.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_animated_scene_finalizer.gd` covers invalid Resources, wrong-root
|
||||
release, exact transfer, player traversal/count, override priority, fallback,
|
||||
source-index mapping, source boundaries and timing.
|
||||
- Adjacent animation pipeline/prototype/shutdown/material/build tests protect callers.
|
||||
- Fidelity evidence is exact mapping/lifecycle extraction; no asset-backed or
|
||||
original-client visual parity claim is made.
|
||||
- Performance budget: 10,000 depth-eight player inventories under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
Animation playback policy and native animator cloning remain separate services.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| PackedScene candidate | Implemented extraction | Type/lifetime fixture | Proprietary corrupt corpus pending |
|
||||
| Material repair | Implemented extraction | Exact-reference mapping | Asset-backed comparison pending |
|
||||
| Player validation | Implemented extraction | Nested traversal/transfer | Multi-player GLB corpus pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Material matching remains positional rather than semantic.
|
||||
- Accepted prototypes remain unbounded until final loader shutdown.
|
||||
- No private traversal, descriptor-pressure, p95/p99 or paired-client run exists.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | Candidate ownership, traversal, material repair and validation |
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending/terminal records before finalization |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Accepted prototype/static-only outcomes |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | I/O, permits, material source, adoption and logs |
|
||||
| `src/tools/verify_m2_animated_scene_finalizer.gd` | Scene/material/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
|
||||
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.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)
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
Own cross-frame bookkeeping between a successful animated M2 ResourceLoader
|
||||
request, terminal polling and budgeted main-thread scene finalization. This is
|
||||
an exact state extraction; animation eligibility, loading and Node lifecycle
|
||||
remain in `StreamingWorldLoader` and `M2PrototypeCacheState`.
|
||||
an exact state extraction; animation eligibility and loading remain in
|
||||
`StreamingWorldLoader`, while scene finalization and retained Node lifecycle
|
||||
belong to `M2AnimatedSceneFinalizer` and `M2PrototypeCacheState`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
@@ -178,8 +179,9 @@ no rebake or migration is required.
|
||||
|
||||
## Extension points
|
||||
|
||||
ResourceLoader polling or animated-scene finalization may later move behind
|
||||
separate adapters without changing this value-only state contract.
|
||||
ResourceLoader polling may later move behind a separate adapter without
|
||||
changing this value-only state contract. Animated-scene finalization is now a
|
||||
sibling service.
|
||||
|
||||
## Capability status
|
||||
|
||||
@@ -200,12 +202,14 @@ separate adapters without changing this value-only state contract.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending records, completion FIFO and metrics |
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | Candidate instantiation, material repair and player validation |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Animated prototype/static-only outcomes |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Eligibility, I/O, permits, instantiation and adoption |
|
||||
| `src/tools/verify_m2_animation_load_pipeline_state.gd` | Lifecycle/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
|
||||
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
|
||||
@@ -240,6 +240,7 @@ this service without moving ResourceLoader or builder ownership into it.
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-raw-model-repository.md`](m2-raw-model-repository.md)
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
|
||||
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
| 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 packages; M03 WMO placement package |
|
||||
| Owners | Renderer workstream / milestone integrator |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animation-load-pipeline`, 2026-07-17 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animated-scene-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||
|
||||
## Purpose
|
||||
@@ -134,6 +134,7 @@ from externally reading/writing loader-private queue, task, cache and tile-state
|
||||
| `M2BuildBatchPlanner.plan_batch` | Internal pure M2 service | Selects static/animated batch count and next group cursor | Main/any thread; stateless | Non-positive selected limit clamps to one; empty range completes |
|
||||
| `M2RuntimeMeshRebuildClassifier` | Internal memoized M2 service | Detects billboard/UV-rotation metadata requiring stale cached-mesh rebuild | Renderer main thread; cached until reset | Invalid variants/indices skipped; first path decision wins |
|
||||
| `M2AnimationLoadPipelineState` | Internal M2 async-state service | Owns animated scene pending request records and terminal finalize FIFO | Renderer main thread; map/session | Empty/duplicate/unknown transitions rejected |
|
||||
| `M2AnimatedSceneFinalizer` | Internal M2 scene-finalization service | Instantiates candidates, repairs materials and requires AnimationPlayer descendants | Renderer main thread; stateless after each call | Invalid/rejected detached roots are freed |
|
||||
| `M2MeshLoadPipelineState` | Internal M2 async-state service | Owns static Mesh pending request records and terminal finalize FIFO | Renderer main thread; map/session | Empty/duplicate/unknown transitions rejected |
|
||||
| `M2MeshResourceCacheState` | Internal M2 Resource cache | Owns prepared static Mesh references by normalized path | Renderer main thread; final shutdown | Empty path/null Mesh rejected; same path replaces |
|
||||
| `M2MeshResourceExtractor` | Internal M2 scene/resource service | Selects first Mesh from direct Resource, PackedScene or Node subtree | Renderer main thread; stateless except temporary instance | Invalid/no Mesh returns null; temporary PackedScene root freed |
|
||||
@@ -185,6 +186,7 @@ loader configuration remains transitional composition data, not a caller API.
|
||||
| Internal ADT water load | Tile key, ADT path, task ID and parsed Dictionary | Loader/worker / `AdtWaterLoadPipelineState` | Loader task start, budgeted drain and finalization | State-owned records; mutex result mailbox | Request through result completion/reset |
|
||||
| Internal raw M2 read | Extracted directory and normalized relative path | Loader / `M2RawModelRepository` | Finalizer, static or animated builder adapters | Fresh Dictionary; repository retains nothing | One synchronous native call |
|
||||
| Internal animated M2 load | Normalized path, cached GLB path and opaque terminal status | Loader / `M2AnimationLoadPipelineState` | Loader poll/finalize adapters | State-owned String/status records; detached pending snapshots | Request through terminal finalize/reset |
|
||||
| Internal animated M2 candidate | Loaded PackedScene, static material root and detached candidate | Loader / `M2AnimatedSceneFinalizer` | Loader prototype adoption/static fallback | Finalizer owns until rejection or exact-root transfer | One main-thread finalize permit |
|
||||
| Internal M2 prototype state | Normalized path, adopted static/animated Node or negative outcome | Loader / `M2PrototypeCacheState` | Loader reuse/fallback adapters | State-owned strong Node refs and Strings; detached diagnostics | Until final shutdown |
|
||||
| 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 |
|
||||
@@ -541,6 +543,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| 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 |
|
||||
| M2 build batch planner | Implemented extraction | Scene-free limit/count/cursor/source/timing contract | Queue/resource state, spatial cells and asset-backed p95/p99 remain pending |
|
||||
| M2 animation load pipeline state | Implemented extraction | Synthetic lifecycle/FIFO/source/timing contract | Asset-backed traversal/leak/animation-fidelity/p95/p99 pending |
|
||||
| M2 animated scene finalizer | Implemented extraction | Synthetic type/lifetime/material/player/source/timing contract | Asset-backed GLB traversal/material/animation comparison pending |
|
||||
| M2 prototype cache state | Implemented extraction | Synthetic identity/negative/lifecycle/source/timing plus shutdown contract | Asset-backed traversal/leak/p95/p99 pending |
|
||||
| WMO placement resolver | Implemented extraction | Scene-free path/identity/transform/source/timing contract | Asset-backed comparison pending |
|
||||
| WMO placement registry | Implemented extraction | Scene-free ownership/lifecycle/source/timing contract | Build/resource state and asset-backed cross-tile corpus pending |
|
||||
@@ -603,6 +606,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/render/liquid/adt_water_scene_finalizer.gd` | Main-thread ADT water build, tile attachment and optional editor ownership |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Billboard/UV-rotation stale cached-mesh rebuild decision and memoization |
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Animated M2 pending Resource paths, terminal statuses and finalize FIFO |
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | Animated scene candidate ownership, material repair and player validation |
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Static M2 pending Resource paths, terminal statuses and finalize FIFO |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared static M2 Mesh references and final-shutdown release |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene instance lifetime |
|
||||
@@ -625,6 +629,7 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/tools/verify_adt_water_scene_finalizer.gd` | Synthetic ADT water scene/ownership/boundary/timing regression |
|
||||
| `src/tools/verify_m2_runtime_mesh_rebuild_classifier.gd` | M2 rebuild predicate/cache/boundary/timing regression |
|
||||
| `src/tools/verify_m2_animation_load_pipeline_state.gd` | Animated M2 request/terminal/FIFO/boundary/timing regression |
|
||||
| `src/tools/verify_m2_animated_scene_finalizer.gd` | Animated M2 candidate/material/player/lifetime/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Static M2 request/terminal/FIFO/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Static M2 Mesh cache ownership/lifetime/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_resource_extractor.gd` | M2 direct/PackedScene/subtree order/lifetime/boundary/timing regression |
|
||||
|
||||
Reference in New Issue
Block a user