render: extract native M2 animation resource observer
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
# M2 Native Animation Resource Observer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001` |
|
||||
| Owners | Native animation candidate policy, raw read, build and cache outcome |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-native-animation-resource-observer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing native GryphonRoost animated M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Resolve the historical native animated M2 path as one synchronous observation:
|
||||
select a GryphonRoost candidate, reuse cached state, read animated raw data,
|
||||
build a detached prototype and publish either the prototype or static-only result.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Add native animation candidates or change the `gryphonroost` predicate.
|
||||
- Observe/request cached GLB animation or poll/finalize ResourceLoader work.
|
||||
- Duplicate, attach, animate or free accepted instances in the SceneTree.
|
||||
- Consume render permits or change build queue, batching and dispatch policy.
|
||||
- Change native parsing/building, cache formats, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Observer[M2NativeAnimationResourceObserver]
|
||||
Observer --> Repository[M2RawModelRepository]
|
||||
Observer --> Builder[M2NativeAnimatedBuilder]
|
||||
Observer --> Cache[M2PrototypeCacheState]
|
||||
Observer --> Prototype[Detached animated prototype]
|
||||
Prototype --> Snapshot[M2BuildResourceSnapshot]
|
||||
Observer -->|no prototype| Cached[M2CachedAnimationResourceObserver]
|
||||
```
|
||||
|
||||
The observer is the native-first resource boundary. The loader supplies an
|
||||
already-normalized path and remains responsible for fallback ordering and for
|
||||
constructing the snapshot consumed by dispatch.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `is_native_animation_candidate(path)` | Pure query | Apply the historical case-insensitive GryphonRoost substring policy | Empty/unmatched path returns false |
|
||||
| `observe(path, extracted_dir, repository, cache, debug)` | Command/query | Return an exact cached/new native prototype or record static-only fallback | Invalid/non-candidate input returns null without mutation |
|
||||
| `build_animated_prototype(data, extracted_dir)` | Adapter command | Call the retained native animated builder dependency | Missing builder or invalid result returns null |
|
||||
|
||||
The optional constructor dependency exists for deterministic tests; production
|
||||
uses `M2NativeAnimatedBuilder` without a new abstraction or runtime setting.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Normalized relative M2 path | Loader path adapter | Observer | Copied String; one call |
|
||||
| Input | Extracted directory | Loader configuration | Repository/builder | Copied String; one call |
|
||||
| Input | Raw animated Dictionary | Repository | Observer/builder | Call-local value |
|
||||
| Input | Prototype cache state | Loader composition | Observer | Borrowed service; loader session |
|
||||
| Output | Cached or adopted prototype | Cache/observer | Loader snapshot | Borrowed exact Node3D reference |
|
||||
| Output | Static-only transition | Observer | Prototype cache | Copied path; loader session |
|
||||
| Output | Debug line | Observer | Runtime log | Emitted only when enabled and build succeeds |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Observe normalized path] --> Candidate{Valid native candidate?}
|
||||
Candidate -->|no| None[Return null]
|
||||
Candidate -->|yes| Cached{Cached prototype?}
|
||||
Cached -->|yes| Return[Return exact prototype]
|
||||
Cached -->|no| Static{Marked static-only?}
|
||||
Static -->|yes| None
|
||||
Static -->|no| Read[Read animated raw data]
|
||||
Read --> Surfaces{Data and animated surfaces?}
|
||||
Surfaces -->|no| Mark[Mark static-only]
|
||||
Surfaces -->|yes| Build[Build detached prototype]
|
||||
Build --> Children{Prototype has children?}
|
||||
Children -->|no| Mark
|
||||
Children -->|yes| Adopt[Adopt first cache prototype]
|
||||
Adopt --> Log{Debug enabled?}
|
||||
Log -->|yes| Emit[Emit exact native cache fields]
|
||||
Log -->|no| Return
|
||||
Emit --> Return
|
||||
Mark --> None
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Observing
|
||||
Observing --> Rejected: invalid or non-candidate
|
||||
Observing --> Cached: positive cache hit
|
||||
Observing --> StaticOnly: negative cache hit
|
||||
Observing --> Reading: uncached candidate
|
||||
Reading --> StaticOnly: no animated surfaces
|
||||
Reading --> Building: usable raw data
|
||||
Building --> StaticOnly: null or childless result
|
||||
Building --> Adopted: valid result
|
||||
Adopted --> [*]
|
||||
Cached --> [*]
|
||||
StaticOnly --> [*]
|
||||
Rejected --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant O as NativeAnimationObserver
|
||||
participant C as M2PrototypeCacheState
|
||||
participant R as M2RawModelRepository
|
||||
participant B as M2NativeAnimatedBuilder
|
||||
L->>O: observe(path, directory, repository, cache, debug)
|
||||
O->>C: find animated / is static-only
|
||||
alt uncached native candidate
|
||||
O->>R: load_animated_model_data(directory, path)
|
||||
R-->>O: raw Dictionary
|
||||
O->>B: build(raw, directory)
|
||||
alt valid prototype
|
||||
O->>C: adopt_animated_prototype(path, prototype)
|
||||
O-->>L: canonical prototype
|
||||
else unavailable
|
||||
O->>C: mark_animation_static(path)
|
||||
O-->>L: null
|
||||
end
|
||||
else cached result
|
||||
O-->>L: prototype or null
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Observer[M2NativeAnimationResourceObserver] --> Repository[M2RawModelRepository]
|
||||
Observer --> Builder[M2NativeAnimatedBuilder]
|
||||
Observer --> Cache[M2PrototypeCacheState]
|
||||
Loader[StreamingWorldLoader] --> Observer
|
||||
Observer -. no dependency .-> ResourceLoader
|
||||
Observer -. no dependency .-> Pipeline[M2AnimationLoadPipelineState]
|
||||
Observer -. no dependency .-> Scheduler[RenderBudgetScheduler]
|
||||
Observer -. no dependency .-> SceneTree
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Observation and native parsing/building remain synchronous on the renderer
|
||||
main thread, exactly as before extraction; there is no mid-call cancellation.
|
||||
- The observer retains no path, raw Dictionary, Node or service reference.
|
||||
- Prototype cache owns an accepted detached Node until final shutdown; callers
|
||||
only borrow the canonical reference.
|
||||
- The historical childless-builder rejection does not free its detached Node.
|
||||
This deliberately preserved lifetime edge is documented as a remaining leak
|
||||
risk rather than silently changed during architectural extraction.
|
||||
- Loader owns snapshots, fallback sequencing, permits, materialization and every
|
||||
SceneTree mutation.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Empty/non-candidate path or missing dependency | Return null without state mutation | Correct composition/path |
|
||||
| Cached prototype | Return exact canonical Node immediately | None |
|
||||
| Existing static-only outcome | Return null without repeated I/O | New loader session after source repair |
|
||||
| Empty raw data or animated surfaces | Mark static-only and return null | Repair source/parser and start new session |
|
||||
| Null/childless build | Mark static-only and return null | Repair builder/data and start new session |
|
||||
| Duplicate successful build | Cache releases later candidate and returns first | None |
|
||||
| Cancellation during native call | Not supported; call completes | Loader controls whether the call starts |
|
||||
| Shutdown | Observer retains nothing | Cache releases adopted Nodes after drains |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The observer consumes the existing extracted directory and `debug_streaming`
|
||||
flag. Candidate policy remains the case-insensitive substring `gryphonroost`.
|
||||
No profile, cache, budget or animation setting is added.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No persistence or format changes are introduced. Positive and static-only state
|
||||
continues to use the shutdown-lifetime prototype cache; no asset rebake is needed.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
A successful debug-enabled build emits the unchanged `M2_NATIVE_ANIM_CACHE`
|
||||
record with path, surface count, bone count, animation id, sequence index,
|
||||
activity score and length. Rejections remain silent as before.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated synthetic verifier covers candidate policy, invalid dependencies,
|
||||
cache identity, static suppression, raw/surface/build failures, preserved
|
||||
childless lifetime, exact repository/builder arguments, adoption, source
|
||||
ownership and bounded candidate checks.
|
||||
- Cached observer, raw repository, prototype cache, snapshot, dispatch, shutdown,
|
||||
facade, internal-access and render-baseline checks protect adjacent behavior.
|
||||
- Fidelity evidence is exact control-flow and diagnostic extraction. It is not
|
||||
original-client animation, visual, memory or asset-backed performance evidence.
|
||||
|
||||
## Extension points
|
||||
|
||||
Terminal cached-GLB polling/finalization can move behind a separate adapter
|
||||
without changing this native-first contract. Candidate expansion requires its
|
||||
own fidelity evidence and must not silently change `Blizzlike335` behavior.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Native candidate selection | Implemented extraction | Policy/source/timing verifier | Additional candidates require fidelity work |
|
||||
| Raw read/build/cache outcome | Implemented extraction | Failure/identity/adoption verifier | Asset-backed traversal pending |
|
||||
| Terminal cached ResourceLoader polling | Loader-owned | Existing pipeline regressions | Dedicated finalize adapter |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Native parsing/building is synchronous and not bounded by a render permit.
|
||||
- Rejected childless builder Nodes retain the historical unowned lifetime edge.
|
||||
- Private asset traversal, descriptor/leak pressure, p95/p99 and paired visual/
|
||||
animation evidence remain unavailable in this package.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Candidate/cache/raw/build/outcome/logging observation |
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Optional native file/parser boundary |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Positive prototype and static-only ownership |
|
||||
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Existing animated prototype construction |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, native-first fallback order and execution |
|
||||
| `src/tools/verify_m2_native_animation_resource_observer.gd` | Lifecycle/identity/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.md)
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-raw-model-repository.md`](m2-raw-model-repository.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)
|
||||
Reference in New Issue
Block a user