Files
open-wc/docs/modules/m2-animated-scene-finalizer.md
T

9.3 KiB

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

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

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

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

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

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 field copying now belong to M2AnimationPlaybackController; instance/batch materialization remains loader-owned.

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_playback_controller.gd Accepted-instance playback and native animator startup
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