Compare commits
108 Commits
20a64b5dfc
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 1fd87b3132 | |||
| bc4440562d | |||
| 5b73ddadc9 | |||
| a11eb50219 | |||
| adc441ffca | |||
| afbc4faab3 | |||
| f77f50c0f9 | |||
| 4369dd3c91 | |||
| fe6d0deab9 | |||
| 203d40bd0e | |||
| 3bf1c2c657 | |||
| d9d20cb53f | |||
| ef6d324b4f | |||
| f978a16806 | |||
| 541279ed45 | |||
| 8da41dc17c | |||
| 7e97b19095 | |||
| 07521ee6a4 | |||
| f8348cf0cb | |||
| 57d0a9f8bd | |||
| 73b30ca699 | |||
| ffed91c364 | |||
| cfa3dc1009 | |||
| 57d3330944 | |||
| 705354dc14 | |||
| 6deb80c0dd | |||
| f4d5e29cc9 | |||
| 543ee1572b | |||
| f35dcf3a09 | |||
| d65ebee57f | |||
| a59086fdf0 | |||
| ae40e3f1d4 | |||
| 49e70260ed | |||
| 406dc464c3 | |||
| 6a0f9bd3ba | |||
| 3525cc0ead | |||
| 7779e9e75f | |||
| 9ba7ca3263 | |||
| 8c54313f7d | |||
| 1acddab8a5 | |||
| 5a1cd9d7c8 | |||
| a86f8f2212 | |||
| fb7c9f174e | |||
| f1a7400ed1 | |||
| 34b700051f | |||
| 71a1012779 | |||
| ddeb708c08 | |||
| c328d86554 | |||
| 75ddf6a2ed | |||
| 06f6394043 | |||
| b20f0d7f6f | |||
| e7cd967dce | |||
| ff952da7d8 | |||
| 70729bb341 | |||
| d37c799850 | |||
| c24c3f159c | |||
| 1cb0101a73 | |||
| d22a9cd743 | |||
| 6f385ed261 | |||
| f79e064d25 | |||
| 6a23c1b996 | |||
| df87619220 | |||
| 3519f183bb | |||
| 5ebf4de2ff | |||
| a043c79654 | |||
| 194b64d030 | |||
| 7cb3e3412f | |||
| f062ea91cd | |||
| 032a256e70 | |||
| 4354834c50 | |||
| 41b3b63215 | |||
| ec1b90f1e4 | |||
| 0decd10e09 | |||
| b113db01cd | |||
| 99a90ddfb3 | |||
| ed71b36aec | |||
| 17f5cc0faa | |||
| 1fb566f9bc | |||
| c74b90a8ea | |||
| 1dc013e5c2 | |||
| 8938350a59 | |||
| 4a8338f2f0 | |||
| 3ee9e77422 | |||
| 81e33a9bc4 | |||
| 5e82daacb9 | |||
| 8339189907 | |||
| 252927d90c | |||
| 0fd052923d | |||
| 86814deca1 | |||
| c251985256 | |||
| 899a9ae012 | |||
| 456f3e2334 | |||
| 9ab4c0762d | |||
| 5b2d9f23fe | |||
| c0fc191bcd | |||
| 15c24692f0 | |||
| 9b73571dcb | |||
| 5be36376c2 | |||
| c0a7bfbd8d | |||
| e7a7c67f6d | |||
| bc2bd06abc | |||
| 1b450dc580 | |||
| 03ba129a52 | |||
| c20bc62ac0 | |||
| 3dccd3e3ee | |||
| 672e0ce2a5 | |||
| b8e0f11ed5 | |||
| 2695afcdba |
+28
@@ -5,3 +5,31 @@
|
||||
path = third_party/godot-cpp
|
||||
url = https://github.com/godotengine/godot-cpp
|
||||
branch = 4.5
|
||||
[submodule "reference/open-realm"]
|
||||
path = reference/open-realm
|
||||
url = https://github.com/corepunch/open-realm.git
|
||||
branch = main
|
||||
[submodule "reference/whoa"]
|
||||
path = reference/whoa
|
||||
url = https://github.com/whoahq/whoa
|
||||
branch = master
|
||||
[submodule "reference/WoWee"]
|
||||
path = reference/WoWee
|
||||
url = https://github.com/Kelsidavis/WoWee.git
|
||||
branch = master
|
||||
[submodule "reference/WowUnreal"]
|
||||
path = reference/WowUnreal
|
||||
url = https://github.com/Clancey/WowUnreal
|
||||
branch = main
|
||||
[submodule "reference/wow.export"]
|
||||
path = reference/wow.export
|
||||
url = https://github.com/Kruithne/wow.export.git
|
||||
branch = main
|
||||
[submodule "reference/blender-wow-studio/pywowlib"]
|
||||
path = reference/blender-wow-studio-3.4-1.1.0_Experimental/io_scene_wmo/pywowlib
|
||||
url = https://github.com/wowdev/pywowlib.git
|
||||
branch = master
|
||||
[submodule "reference/benilla"]
|
||||
path = reference/benilla
|
||||
url = https://github.com/samwhosung/benilla.git
|
||||
branch = main
|
||||
|
||||
@@ -32,10 +32,24 @@ Paired run 2026-07-11 подтвердил крупный coordinate/placement g
|
||||
- `src/render/wmo/wmo_render_build_step_planner.gd` - mesh-first lightweight WMO group operation and cursor planning without Nodes or Resources.
|
||||
- `src/render/wmo/wmo_render_build_queue.gd` / `wmo_render_build_job.gd` - typed pending group jobs, FIFO placement keys and strong root/resource references without engine destruction.
|
||||
- `src/render/wmo/wmo_render_resource_cache_state.gd` - validated lightweight WMO render Resources, negative cache and pending cache paths without ResourceLoader I/O.
|
||||
- `src/render/wmo/wmo_render_resource_finalizer.gd` - lightweight WMO terminal polling, script/format validation and Resource/missing publication.
|
||||
- `src/render/wmo/wmo_scene_resource_finalizer.gd` - cached WMO terminal polling, PackedScene probe validation/lifetime and scene/missing publication.
|
||||
- `src/render/wmo/wmo_scene_resource_cache_state.gd` - validated cached-WMO PackedScenes, negative cache and pending `.tscn` paths without file/I/O/Node ownership.
|
||||
- `src/render/liquid/adt_water_load_pipeline_state.gd` - ADT water pending FIFO/dedupe, active task IDs and worker-safe parsed-result mailbox without parser or Node ownership.
|
||||
- `src/render/liquid/adt_water_scene_finalizer.gd` - stateless main-thread ADT water build/attach and optional persisted Editor ownership through the existing ADTBuilder.
|
||||
- `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` - memoized billboard/UV-rotation decision for stale cached M2 runtime mesh refresh.
|
||||
- `src/render/m2/m2_animated_scene_finalizer.gd` - animated PackedScene candidate ownership, historical material repair and AnimationPlayer validation.
|
||||
- `src/render/m2/m2_animation_playback_controller.gd` - deterministic per-instance phase, default animation selection and native/imported playback mutation.
|
||||
- `src/render/m2/m2_animated_instance_materializer.gd` - main-thread animated instance duplication, render settings, playback startup and non-empty batch attachment.
|
||||
- `src/render/m2/m2_static_batch_materializer.gd` - main-thread static M2 MultiMesh construction, render settings and attachment.
|
||||
- `src/render/m2/m2_build_queue.gd` / `m2_build_job.gd` - typed pending M2 jobs, FIFO/stale tile keys, grouped-transform references and progress cursors without engine destruction.
|
||||
- `src/render/m2/m2_build_dispatch_planner.gd` - pure animation/static wait, materialization and missing-model advance decision.
|
||||
- `src/render/m2/m2_build_resource_snapshot.gd` - typed per-step animated/static resource observations without engine destruction.
|
||||
- `src/render/m2/m2_static_build_resource_observer.gd` - static Mesh cache lookup, threaded request selection and missing transition.
|
||||
- `src/render/m2/m2_cached_animation_resource_observer.gd` - cached animated GLB eligibility, threaded request admission and snapshot production.
|
||||
- `src/render/m2/m2_animation_load_pipeline_state.gd` - animated M2 threaded-load request records and completion-order finalize FIFO without I/O or Node ownership.
|
||||
- `src/render/m2/m2_animation_resource_finalizer.gd` - cached animated M2 terminal status polling, Resource load, scene finalization and prototype/static-only outcome.
|
||||
- `src/render/m2/m2_mesh_resource_finalizer.gd` - static M2 terminal status polling, Mesh extraction/preparation and cache/missing outcome.
|
||||
- `src/render/m2/m2_mesh_load_pipeline_state.gd` - static M2 threaded-load request records, terminal statuses and completion-order finalize FIFO without I/O or Mesh ownership.
|
||||
- `src/render/m2/m2_mesh_resource_cache_state.gd` - normalized-path prepared static M2 Mesh references with final-shutdown lifetime.
|
||||
- `src/render/m2/m2_mesh_resource_extractor.gd` - first-Mesh selection from direct/PackedScene/Node inputs with temporary instance cleanup.
|
||||
@@ -319,7 +333,7 @@ Native M2 animation first pass for composite doodads:
|
||||
- `M2NativeAnimator` evaluates the selected Stand sequence and applies WoW-style bone matrices:
|
||||
`T(pivot + translation) * R * S * T(-pivot) * parent`.
|
||||
- Vertex influences are resolved through `.skin` local bone indices and the M2 `boneCombos` palette, matching the original section/batch renderer model used by WoW/whoa.
|
||||
- `StreamingWorldLoader` routes only `gryphonroost` through this native path; simple critters still use the existing GLB allowlist and most M2 world props stay on static MultiMesh.
|
||||
- `M2NativeAnimationResourceObserver` routes only `gryphonroost` through this native path; simple critters still use the existing GLB allowlist and most M2 world props stay on static MultiMesh.
|
||||
- Current implementation rebuilds the animated `ArrayMesh` on CPU. This is correct enough for the problematic rare doodads and gives us the same data layout that can later move to shader/GPU skinning.
|
||||
|
||||
Причина: композитные doodad вроде `GryphonRoost` снова ломают визуал при GLB-анимации. До M2-native renderer все world placement M2 должны оставаться статическими. GLB-анимация оставлена только как вручную включаемый debug experiment через allowlist.
|
||||
@@ -672,6 +686,42 @@ SKYBOX_MODEL ...
|
||||
- полноценный liquid rendering там не реализован;
|
||||
- skybox/liquid не стоит напрямую переносить как готовый код.
|
||||
|
||||
По WoWee (reviewed update `626243e937fb93965fa583a6507ed5a1aa7dda4b` →
|
||||
`607ea3b8369851014721416293f8e95dfbe64eec`, 2026-09-05):
|
||||
|
||||
- сильнейший M04 signal — не новый renderer целиком, а узкие regression fixtures:
|
||||
M2 global-sequence/color-alpha sampling, `$FSD` event timing, 8x8 liquid masks,
|
||||
rotated WMO liquid projection и CPU vertex/shader interface checks;
|
||||
- общий M2+skin/external-`.anim` resolver и selective animation loading полезны как
|
||||
decomposition, но должны использовать существующие OpenWC repository/worker/
|
||||
main-thread finalization boundaries;
|
||||
- track sampler пока линейно обрабатывает Hermite/Bezier, а particle/ribbon path
|
||||
содержит flame/smoke и orientation heuristics; это не fidelity oracle. Для
|
||||
effect architecture Benilla остаётся более полным secondary reference;
|
||||
- placement rotation order для наклонённых doodads в WoWee явно не решён.
|
||||
OpenWC не меняет calibrated MDDF/MODF transforms без tilted build-12340 fixture;
|
||||
- real-asset tests, которые превращают отсутствие assets в success, не считаются
|
||||
evidence. OpenWC fixture обязан иметь provenance/hash и явно fail/skip-report;
|
||||
- modified MIT license WoWee запрещает использование как основы/компонента
|
||||
commercial video game без разрешения: используем только независимо проверенные
|
||||
идеи и tests, не копируем/не вендорим код без legal review.
|
||||
|
||||
По Benilla (pinned research commit
|
||||
`bc1a2428dd7e00ca8abbfb8f0bf53750dae7b123`):
|
||||
|
||||
- полезна граница `world renderer -> asset/formats` без зависимости от game/UI;
|
||||
- M2 animation разделяет selection policy и renderer machinery: pose sampling,
|
||||
parent-order composition, global sequences, billboard replacement, attachment
|
||||
anchors и palette upload;
|
||||
- particles и ribbons симулируются на CPU для temporal fidelity, но записывают
|
||||
геометрию в один shared effect vertex/index stream с сортировкой, batching и
|
||||
camera-relative upload вместо отдельных dynamic Mesh/Material на emitter;
|
||||
- WMO portal flood, material pass ordering, fog/blend policy, effect lifecycle и
|
||||
corpus/probe tooling являются полезными sources для M04 fixtures;
|
||||
- это Vanilla 1.12.1/Bevy reference, не build-12340 oracle: record layouts,
|
||||
shader flags, effect timing и performance должны быть повторно проверены в
|
||||
Godot против оригинального WoW 3.3.5a.
|
||||
|
||||
По WoW 3.3.5a:
|
||||
|
||||
- старый клиент не рендерил все как modern physically based renderer;
|
||||
@@ -1046,11 +1096,45 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
remaining-count capping and next-offset/group-completion calculation.
|
||||
- Existing animated/static limits still clamp to at least one; resource readiness,
|
||||
queue rotation, serial progression and `M2_BUILD` permit consumption are unchanged.
|
||||
- The loader retains build jobs/queues, tile cancellation, animation/mesh caches,
|
||||
retries and all Node/MultiMesh/Mesh/material/RID finalization.
|
||||
- `M2BuildQueue` now retains typed pending jobs, FIFO/stale keys and cursors.
|
||||
The loader retains tile cancellation, resource readiness/retries and all
|
||||
Node/MultiMesh/Mesh/material/RID finalization.
|
||||
- Cache formats, quality profiles, batching output and visible rules are unchanged.
|
||||
Asset-backed p95/p99 and spatial-cell batching evidence remain pending.
|
||||
|
||||
## 2026-07-18 M2 Build Dispatch Planner Extraction
|
||||
|
||||
- `M2BuildDispatchPlanner` now owns the pure action priority between pending
|
||||
animation, animated materialization, pending/ready static Mesh and terminally
|
||||
missing-model advancement.
|
||||
- Pending animation still wins before batch planning. Unresolved static Meshes
|
||||
still rotate and consume one `M2_BUILD` permit without advancing progress.
|
||||
- Empty batches advance without serial change; animated, static and terminally
|
||||
missing positive batches retain the historical serial increment.
|
||||
- Resource lookup/request order, queue/cursor adoption, materialization, permits,
|
||||
Node lifetime, cache formats, profiles and visible rules remain unchanged.
|
||||
|
||||
## 2026-07-18 M2 Build Resource Snapshot Extraction
|
||||
|
||||
- `M2BuildResourceSnapshot` now carries one build-step normalized path, optional
|
||||
animated prototype, pending-animation state, optional static Mesh and terminal
|
||||
missing-model state through typed accessors.
|
||||
- Animation observation is captured first. Static observation is adopted only
|
||||
for a positive non-animated batch after animation is no longer pending.
|
||||
- The dispatch planner consumes the snapshot; the loader borrows the selected
|
||||
prototype/Mesh for the existing materializer calls.
|
||||
- ResourceLoader/cache requests, permit/cursor transitions, engine lifetime,
|
||||
cache formats, profiles and visible rules remain unchanged.
|
||||
|
||||
## 2026-07-18 M2 Static Build Resource Observer Extraction
|
||||
|
||||
- `M2StaticBuildResourceObserver` now owns prepared-Mesh lookup, existing-request
|
||||
detection, `.tscn`-before-`.glb` candidate selection, pivot-prefix GLB rejection,
|
||||
threaded request admission and terminal missing transition for build jobs.
|
||||
- The observer fills `M2BuildResourceSnapshot`; the loader retains animation
|
||||
observation, finalize drains, materialization, permits and engine lifetime.
|
||||
- Candidate order, request errors, cache formats, profiles and visuals are unchanged.
|
||||
|
||||
## 2026-07-17 M2 Runtime Mesh Rebuild Classifier Extraction
|
||||
|
||||
- `M2RuntimeMeshRebuildClassifier` now owns the memoized decision used when a
|
||||
@@ -1083,6 +1167,108 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Cache formats, material refresh, profiles and visible output are unchanged.
|
||||
Synthetic state timing is not asset-backed I/O, leak or p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Animation Load Pipeline State Extraction
|
||||
|
||||
- `M2AnimationLoadPipelineState` now owns animated M2 successful threaded-load
|
||||
request records and the FIFO populated at loaded/failed terminal transitions.
|
||||
- Pending snapshots preserve insertion order, while finalization follows completion
|
||||
order. All three historical `m2_animation` metrics still count both stages.
|
||||
- Shutdown still drains pending ResourceLoader paths before clear; map reset and
|
||||
shutdown preserve the same two clear sites.
|
||||
- At this extraction stage, `StreamingWorldLoader` retained GLB eligibility/cache selection, every
|
||||
ResourceLoader call, `M2_ANIMATION_FINALIZE` permits, scene instantiation,
|
||||
material repair and prototype/static-fallback decisions.
|
||||
- Cache formats, animation behavior and visible output are unchanged. Synthetic
|
||||
timing is not asset-backed I/O, leak, animation-fidelity or p95/p99 evidence.
|
||||
|
||||
## 2026-07-18 M2 Cached Animation Resource Observer Extraction
|
||||
|
||||
- `M2CachedAnimationResourceObserver` now owns cached animated-prototype lookup,
|
||||
allow/deny matching, historical `.glb` candidate selection, GLB animation/
|
||||
primitive/schema safety validation, request admission and static-only fallback.
|
||||
- The observer produces `M2BuildResourceSnapshot` with the exact borrowed cached
|
||||
prototype or pending state and retains/frees no engine object.
|
||||
- Native GryphonRoost raw-data build/debug logging belongs to the sibling native
|
||||
observer; terminal polling, finalization, permits and SceneTree mutation remain
|
||||
loader-owned.
|
||||
- Defaults, path order, accepted empty/`pivot_prefix_v1` schemas, cache format,
|
||||
metrics and visible behavior are unchanged. Generated GLB metadata fixtures
|
||||
are not private asset, leak, p95/p99 or original-client animation evidence.
|
||||
|
||||
## 2026-07-18 M2 Animation Resource Finalizer Extraction
|
||||
|
||||
- `M2AnimationResourceFinalizer` now owns cached animated M2 terminal status
|
||||
polling, completion-FIFO pops, terminal Resource retrieval, candidate
|
||||
instantiation, repair/validation, prototype/static-only adoption and success log.
|
||||
- The two-phase preparation/completion API preserves the prior ordering: the
|
||||
loader resolves a material prototype only after a loaded PackedScene produces
|
||||
a detached Node3D candidate. One scheduler permit still pops one record.
|
||||
- `StreamingWorldLoader` retains the `M2_ANIMATION_FINALIZE` permit loop,
|
||||
material-prototype lookup, build dispatch, materialization and SceneTree lifetime.
|
||||
- No request order, status rule, cache format, profile or visible behavior changed.
|
||||
Synthetic fixtures are not asset-backed animation/leak/p95/p99 evidence.
|
||||
|
||||
## 2026-07-18 M2 Mesh Resource Finalizer Extraction
|
||||
|
||||
- `M2MeshResourceFinalizer` now owns static M2 terminal status polling,
|
||||
completion-FIFO pops, terminal Resource retrieval, first-Mesh extraction,
|
||||
stale/current runtime preparation and Mesh/missing cache adoption.
|
||||
- Pending paths are still polled in insertion order. One
|
||||
`M2_MESH_FINALIZE` permit still pops at most one terminal record; cached Mesh
|
||||
outcomes skip terminal retrieval exactly as before.
|
||||
- Current Meshes retain exact identity and skip raw reads. Stale Meshes still
|
||||
request raw data only when `M2RuntimeMeshFinalizer` requires it, preserving
|
||||
refresh-version, rebuild and original-Mesh fallback rules.
|
||||
- `StreamingWorldLoader` retains request admission, scheduler permits,
|
||||
composition, static materialization and shutdown drain ordering. Cache paths,
|
||||
profiles and visible behavior are unchanged; synthetic fixtures are not
|
||||
asset-backed visual/leak/p95/p99 evidence.
|
||||
|
||||
## 2026-07-18 M2 Native Animation Resource Observer Extraction
|
||||
|
||||
- `M2NativeAnimationResourceObserver` now owns the exact case-insensitive
|
||||
`gryphonroost` candidate rule, animated prototype/static-only cache checks,
|
||||
synchronous raw animated read, native builder call, adoption and success log.
|
||||
- The native attempt remains first. Empty raw data/surfaces and null/childless
|
||||
builds retain the same static-only fallback; accepted Nodes remain owned by
|
||||
`M2PrototypeCacheState` until final shutdown.
|
||||
- `StreamingWorldLoader` retains observer order, typed snapshot construction,
|
||||
cached-GLB fallback, terminal ResourceLoader polling/finalize, permits,
|
||||
materialization and SceneTree lifetime.
|
||||
- The historical unfreed childless builder result is deliberately preserved and
|
||||
documented as a leak risk. No parser, cache, profile or visible rule changed;
|
||||
synthetic fixtures are not asset-backed animation or performance evidence.
|
||||
|
||||
## 2026-07-18 M2 Animated Scene Finalizer Extraction
|
||||
|
||||
- `M2AnimatedSceneFinalizer` now owns terminal animated PackedScene candidate
|
||||
instantiation, depth-first Mesh/AnimationPlayer traversal, static-prototype
|
||||
material override mapping and rejected detached-root destruction.
|
||||
- Material repair retains source/target depth-first ordering, source-index clamp,
|
||||
per-surface override priority and first discovered material fallback.
|
||||
- A candidate still requires at least one AnimationPlayer. Accepted exact Node3D
|
||||
and player count return to the loader; invalid candidates are freed, including
|
||||
the previously unhandled non-Node3D imported-root case.
|
||||
- `StreamingWorldLoader` retains ResourceLoader I/O/statuses, finalize permits,
|
||||
material-prototype selection/loading, prototype adoption/static fallback,
|
||||
animation playback policy and diagnostic logging.
|
||||
- Cache formats, profiles and visible rules are unchanged. Synthetic scene and
|
||||
material fixtures are not asset-backed animation-fidelity or p95/p99 evidence.
|
||||
|
||||
## 2026-07-18 M2 Animation Playback Controller Extraction
|
||||
|
||||
- `M2AnimationPlaybackController` now owns the stable path/index phase formula,
|
||||
ordinary/fish/bird animation-name priority, substring/first-name fallbacks and
|
||||
AnimationPlayer linear-loop/play/seek mutation.
|
||||
- Exact-script native animators retain depth-first pairing, the same five copied
|
||||
fields, optional `prepare_runtime`, phase assignment and debug-state sampling.
|
||||
- Native debug records are detached and sampled only when `debug_streaming` is
|
||||
enabled; `StreamingWorldLoader` retains path normalization and exact log text.
|
||||
- Loader still duplicates/transforms/attaches instances, applies visibility,
|
||||
shadows and Editor ownership, owns build cursors/permits and obtains player inventory.
|
||||
- Cache formats, playback priorities, phase behavior and visible rules are unchanged.
|
||||
Synthetic timing is not asset-backed animation fidelity or p95/p99 evidence.
|
||||
|
||||
## 2026-07-17 M2 Mesh Resource Cache State Extraction
|
||||
|
||||
- `M2MeshResourceCacheState` now owns normalized-path references to prepared
|
||||
@@ -1113,12 +1299,14 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
|
||||
- `M2RuntimeMeshFinalizer` now owns material refresh version `2`, stale-Mesh
|
||||
rebuild classification, M2Builder rebuild and original-Mesh fallback.
|
||||
- Current Meshes still skip raw `.m2` loading. `M2RawModelRepository` now owns
|
||||
FileAccess/ClassDB M2Loader I/O and supplies raw data through the loader only
|
||||
when the finalizer reports a stale Mesh; both historical clear sites persist.
|
||||
- Current Meshes still skip raw `.m2` loading. `M2RawModelRepository` owns
|
||||
FileAccess/ClassDB M2Loader I/O and supplies raw data through
|
||||
`M2MeshResourceFinalizer` only when the runtime finalizer reports a stale Mesh;
|
||||
both historical clear sites persist.
|
||||
- Billboard/UV-rotation predicates, rebuild extraction, metadata key and failure
|
||||
fallback are unchanged. Cache adoption decisions, permits and MultiMesh
|
||||
materialization remain loader-owned; negative outcomes belong to prototype state.
|
||||
fallback are unchanged. Mesh resource finalization owns cache adoption;
|
||||
permits and MultiMesh materialization remain loader-owned, while negative
|
||||
outcomes belong to prototype state.
|
||||
- Synthetic triangle rebuild/fallback timing is not asset-backed material,
|
||||
descriptor-pressure/leak or p95/p99 evidence.
|
||||
|
||||
@@ -1127,10 +1315,10 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- `M2RawModelRepository` now owns the repeated extracted-file and optional
|
||||
native `M2Loader` boundary for static `load_m2` and animated
|
||||
`load_m2_animated` raw Dictionaries.
|
||||
- `StreamingWorldLoader` delegates the stale-Mesh refresh, static prototype and
|
||||
native animated prototype reads. It retains normalization, `.tscn/.glb`
|
||||
fallback order, builders, permits and Node/Mesh use; prototype/negative state
|
||||
is now isolated in `M2PrototypeCacheState`.
|
||||
- `StreamingWorldLoader` delegates stale-Mesh refresh and static reads directly;
|
||||
`M2NativeAnimationResourceObserver` delegates native animated reads. The loader
|
||||
retains normalization, `.tscn/.glb` fallback order, permits and Node/Mesh use;
|
||||
prototype/negative state is isolated in `M2PrototypeCacheState`.
|
||||
- The repository retains no path, native object or parsed data. Empty paths,
|
||||
absent files/classes/methods and invalid results produce the same empty-value
|
||||
fallback contract; path join/globalization and native method names are exact.
|
||||
@@ -1168,9 +1356,10 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
|
||||
- `WmoRenderResourceCacheState` now owns validated lightweight-WMO render
|
||||
Resources, negative entries and normalized-path to pending-cache-path records.
|
||||
- `StreamingWorldLoader` still constructs cache paths, calls `ResourceLoader`,
|
||||
polls requests and validates `WMOStreamingResource` script identity plus
|
||||
`FORMAT_VERSION` before completing cache state.
|
||||
- `StreamingWorldLoader` still constructs cache paths and starts requests.
|
||||
`WmoRenderResourceFinalizer` polls terminal requests and validates exact
|
||||
`WMOStreamingResource` script identity plus `FORMAT_VERSION` before completing
|
||||
cache state.
|
||||
- Map reset and orderly request draining clear pending/negative state while
|
||||
retaining accepted Resources; final runtime cache release clears all state.
|
||||
- Missing render-cache files still are not negatively cached, preserving retry
|
||||
@@ -1178,13 +1367,28 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Asset-backed corrupt-cache, traversal/leak p95/p99 and paired fidelity evidence
|
||||
remain pending.
|
||||
|
||||
## 2026-07-18 WMO Render Resource Finalizer Extraction
|
||||
|
||||
- `WmoRenderResourceFinalizer` now owns lightweight WMO render-cache terminal
|
||||
status polling, loaded Resource retrieval, exact script/current-format
|
||||
validation and Resource/missing publication.
|
||||
- Detached pending snapshots retain Dictionary insertion order. Non-terminal
|
||||
requests remain pending; failed, null, wrong-script and stale-format outcomes
|
||||
retain the historical negative-cache transition.
|
||||
- Accepted current-or-newer Resources keep exact identity. Loader retains cache
|
||||
path selection, request admission, fallback/build orchestration, Node lifetime
|
||||
and shutdown drain order.
|
||||
- Cache format, profiles and visible output are unchanged. Synthetic fixtures
|
||||
are not serialized private assets, leak, p95/p99 or visual-fidelity evidence.
|
||||
|
||||
## 2026-07-17 WMO Scene Resource Cache State Extraction
|
||||
|
||||
- `WmoSceneResourceCacheState` now owns validated cached-WMO PackedScenes,
|
||||
negative entries and normalized-path to pending-`.tscn` records.
|
||||
- `StreamingWorldLoader` still checks file existence and
|
||||
`wmo_max_runtime_scene_mb`, calls `ResourceLoader`, instantiates a validation
|
||||
probe, checks WMOBuilder cache metadata and frees the probe before adoption.
|
||||
`wmo_max_runtime_scene_mb` and starts requests. `WmoSceneResourceFinalizer`
|
||||
owns terminal ResourceLoader I/O, validation-probe instantiation, WMOBuilder
|
||||
metadata validation and probe release before adoption.
|
||||
- Missing files, oversize scenes, request errors, load failures and stale scenes
|
||||
retain their prior negative-cache and live-prototype fallback behavior.
|
||||
- Map reset clears pending/negative state while retaining accepted scenes; final
|
||||
@@ -1192,6 +1396,21 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- Asset-backed oversize/stale fixtures, traversal/leak p95/p99 and paired fidelity
|
||||
evidence remain pending.
|
||||
|
||||
## 2026-07-18 WMO Scene Resource Finalizer Extraction
|
||||
|
||||
- `WmoSceneResourceFinalizer` now owns cached WMO `.tscn` terminal status
|
||||
polling, loaded Resource/PackedScene validation, call-local probe lifetime and
|
||||
exact scene/missing publication.
|
||||
- Pending snapshots preserve Dictionary insertion order. Non-terminal requests
|
||||
remain pending; failed, null, wrong-type, wrong-root and stale outcomes keep
|
||||
the existing negative-cache transition.
|
||||
- Current scenes retain exact PackedScene identity. Accepted and rejected probes
|
||||
are freed before return; the rejected non-Node3D root now also releases its
|
||||
temporary Node, closing a leak without changing fallback or visible output.
|
||||
- Loader retains file/size admission, oversize log, request start, live fallback,
|
||||
placed Node materialization and shutdown order. Synthetic fixtures are not
|
||||
serialized private assets, long leak, p95/p99 or visual-fidelity evidence.
|
||||
|
||||
## 2026-07-17 ADT Water Load Pipeline State Extraction
|
||||
|
||||
- `AdtWaterLoadPipelineState` now owns ADT water pending FIFO/deduplication,
|
||||
@@ -1266,6 +1485,74 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- ADT parsing, quality tasks/results, tile state, cache format versions, material/
|
||||
Node/RID finalization, budgets and visible terrain behavior remain loader-owned.
|
||||
|
||||
## 2026-07-18 WMO Runtime Mesh Finalizer Extraction
|
||||
|
||||
- `WmoRuntimeMeshFinalizer` now owns cached WMO Mesh material refresh version
|
||||
`10`, in-place ArrayMesh surface iteration and WMOBuilder material-definition
|
||||
reconstruction.
|
||||
- `StreamingWorldLoader` retains cached-scene traversal, lightweight build-job
|
||||
traversal, Node/MultiMesh attachment, permits and lifetime, and delegates each
|
||||
borrowed Mesh to the service.
|
||||
- The historical metadata keys, compact texture0/texture1/texture2 ordering,
|
||||
flags/shader/blend values, cached shader colors and exact Mesh identity are
|
||||
unchanged. Null/unmarked surfaces and null builder results retain their prior
|
||||
Material.
|
||||
- Asset-free verification covers `27` identity/version/type/material/source
|
||||
cases and 1,000 current-Mesh calls. This is orchestration extraction, not new
|
||||
build-12340 material or visual parity evidence.
|
||||
|
||||
## 2026-08-01 WMO Render Group Materializer Extraction
|
||||
|
||||
- `WmoRenderGroupMaterializer` now owns creation and single attachment of the
|
||||
lightweight cached WMO `MeshInstance3D` and `MultiMeshInstance3D` groups.
|
||||
- Indexed names/transforms, `Group_N`/`DoodadGroup_N` fallbacks, exact
|
||||
Mesh/MultiMesh identity, shadow mode and positive visibility range/margin are
|
||||
unchanged.
|
||||
- `StreamingWorldLoader` retains build-step selection, runtime Mesh finalization,
|
||||
queue cursors, permits, optional Editor ownership and placement lifetime.
|
||||
- Asset-free verification covers `37` presentation/ownership/source cases and
|
||||
1,000 simple group attachments. This extraction adds no asset-backed GPU,
|
||||
leak, p95/p99 or original-client visual-fidelity evidence.
|
||||
|
||||
## 2026-08-01 WMO Runtime Scene Preparer Extraction
|
||||
|
||||
- `WmoRuntimeScenePreparer` now owns cached WMO parent-before-children Mesh/
|
||||
MultiMesh finalization and the shared cached/live render-policy preparation.
|
||||
- The historical path distinction is unchanged: live-built duplicates do not
|
||||
cross the cached runtime Mesh finalizer boundary.
|
||||
- Disabled occlusion still removes only the direct child named `Occluders`;
|
||||
enabled shadows still set descendant GeometryInstance3D nodes ON, while the
|
||||
disabled shadow branch preserves existing values.
|
||||
- Instantiation, placement, attachment, registry lifetime, Editor ownership,
|
||||
queues and permits remain loader-owned. Synthetic traversal timing is not
|
||||
private-asset visual, leak, GPU or p95/p99 evidence.
|
||||
|
||||
## 2026-08-01 WMO Scene Instance Factory Extraction
|
||||
|
||||
- `WmoSceneInstanceFactory` now owns cached PackedScene instantiation/currentness
|
||||
validation and live-prototype duplication with shared basename/placement rules.
|
||||
- Cached validation still precedes placement; live duplicates still skip the
|
||||
scene-cache validator. Accepted descendant Resources retain exact identity.
|
||||
- Invalid non-Node3D cached roots are now freed synchronously, closing an
|
||||
error-path lifetime leak that normal scene-cache admission already prevents.
|
||||
- Source lookup, ResourceLoader, runtime preparation, attachment, registry,
|
||||
queues and permits remain loader-owned. Synthetic factory timing is not
|
||||
private-asset visual, leak/GPU or p95/p99 evidence.
|
||||
|
||||
## 2026-08-02 M03 Renderer Closeout
|
||||
|
||||
- M03 preserves the M00 `High` topology and batching while enforcing four
|
||||
CPU-only worker boundaries, fifteen main-thread finalization lanes and seven
|
||||
explicit cache versions through the renderer closeout contract verifier.
|
||||
- Performance acceptance uses exact-cache paired M00/M03 captures plus a second
|
||||
ten-second window. A metric must exceed its unchanged 10% budget in both
|
||||
protocols to be a repeatable regression; the closeout result is `0/84`.
|
||||
- Native M2 startup no longer copies an ArrayMesh that is immediately discarded.
|
||||
It creates an instance-local mesh, reapplies shared Materials and performs one
|
||||
phased rebuild before attachment; `_ready()` is idempotent afterward.
|
||||
- Checkpoint evidence is asset-backed but is not an original-client pixel-parity
|
||||
claim. Long traversal and original-client approval remain release gates.
|
||||
|
||||
## Practical Rule For Future Work
|
||||
|
||||
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
# M03-QAR-INTEGRATOR-CLOSEOUT-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex-m03-integrator:2026-08-03 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-QAR-INTEGRATOR-CLOSEOUT-001:203d40b -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex-m03-integrator`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex-m03-integrator/m03-closeout`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-closeout`
|
||||
|
||||
## Outcome
|
||||
|
||||
Close M03 only after an overall renderer performance comparison, cache-version
|
||||
and main-thread-finalization contract gates, the complete autonomous verifier
|
||||
suite, documentation/coordination gates and integrator review all pass.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change renderer visuals, placements, materials, animations or quality policy.
|
||||
- Add new renderer features or advance to M04/M08.
|
||||
- Claim original-client visual parity beyond existing M00 evidence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: M03 closeout verifier/comparator, closeout specification, this claim
|
||||
- Shared: renderer verification runner, renderer/module registries, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`, `targets/README.md`
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Compare identical checkpoint/pass pairs against the accepted M00 report.
|
||||
- Require matching environment/profile/cache state and exact cache version keys.
|
||||
- Enforce the accepted 10% ceilings for p95, p99, max hitch, load time and memory.
|
||||
- Verify GPU/SceneTree finalization remains main-thread and budget-gated by source.
|
||||
- Keep all generated/private reports outside Git; commit only aggregate evidence.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: all accepted M03 facade/planner/scheduler and terrain/M2/WMO/liquid packages
|
||||
- Blocks: M03 DONE marker and activation of the next user-selected plan
|
||||
|
||||
## Verification
|
||||
|
||||
- Full cold/warm renderer capture; deterministic performance comparison; complete
|
||||
headless verifier suite; project/editor parse; baseline manifest/checkpoint dry
|
||||
run; dependency, documentation and coordination gates.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline comparator/gate docs; closeout module specification with API/I/O,
|
||||
ownership and sequence/dependency diagrams; renderer docs and final M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: integrated
|
||||
- Done: renderer closeout gates, exact-cache performance evidence, full verifier
|
||||
suite, documentation, M03 DONE transition and M04 administrative activation
|
||||
- Next: receive the user's plan correction before beginning M04 work
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-QAR-INTEGRATOR-CLOSEOUT-001:203d40b -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commits: `d9d20cb`, `3bf1c2c`, `203d40b`; fast-forwarded to `master`.
|
||||
- Verification: all `67/67` autonomous Godot verifiers; M2 playback `20`
|
||||
cases; renderer contracts `workers=4 frame_steps=15 cache_versions=7
|
||||
nested_glb=1`; performance stability `84` metrics with `0` repeatable
|
||||
regressions; documentation, coordination and diff gates passed.
|
||||
- Fidelity: exact cache inventory and asset-backed checkpoints cover terrain,
|
||||
ADT boundary, dense M2, large WMO, liquid, native animation and dusk sky.
|
||||
This is extraction/performance evidence, not original-client pixel parity.
|
||||
- Remaining risks: original-client visual approval, long-traversal descriptor
|
||||
pressure, scaling native animation beyond CPU deformation and interruption of
|
||||
already in-flight worker jobs remain later release concerns.
|
||||
- Documentation: added the renderer closeout module specification with data-flow
|
||||
and sequence diagrams; updated testing policy, renderer source maps, M2
|
||||
animation ownership/sequence documentation, `RENDER.md` and M03 Evidence.
|
||||
@@ -0,0 +1,104 @@
|
||||
# M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-animated-instance-materializer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animated-instance-materializer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract main-thread animated M2 instance duplication, render-property application,
|
||||
playback startup and batch attachment from `StreamingWorldLoader`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change build-job cursors, budgets, prototype selection or animation eligibility.
|
||||
- Change animation selection, phase, native animator behavior or diagnostic text.
|
||||
- Own Editor scene persistence, static MultiMesh materialization or tile cleanup.
|
||||
- Change visible transforms, visibility ranges, shadows or placement ordering.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_animated_instance_materializer.gd`,
|
||||
`src/tools/verify_m2_animated_instance_materializer.gd`,
|
||||
`docs/modules/m2-animated-instance-materializer.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 animation
|
||||
playback/finalizer/build/facade/internal-access/manifest verifiers and specs,
|
||||
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Duplicate flags, names, transforms and input order remain unchanged.
|
||||
- Visibility end/margin and shadow mode are applied recursively before attachment.
|
||||
- Playback data copy/start delegates to the accepted playback controller contract.
|
||||
- The batch is attached only when at least one duplicate succeeds.
|
||||
- Loader retains diagnostic formatting/path normalization and Editor ownership.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted playback package on master `5b2d9f2`
|
||||
- Blocks: further animated M2 build orchestration extraction
|
||||
- External state: Godot Node duplication and SceneTree attachment remain main-thread APIs
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated duplication/order/render/playback/empty/source/timing verifier;
|
||||
M2 animated pipeline/finalizer/playback/prototype/build/shutdown/material/facade/
|
||||
internal-access/manifest regressions; docs/coordination/dry-run gates
|
||||
- Fixtures: synthetic prototype trees with geometry, AnimationPlayer and native animator
|
||||
- Fidelity evidence: exact transforms, flags, render settings, playback delegation and order
|
||||
- Performance budget: 1,000 synthetic instances under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs
|
||||
- Module spec with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent playback/finalizer/build/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2AnimatedInstanceMaterializer`
|
||||
- Simplest solution: synchronous main-thread RefCounted returning batch diagnostics
|
||||
- Rejected complexity: signals, async queue, callbacks and generic scene factory
|
||||
- Unavoidable complexity: imported AnimationPlayer and native animator paths coexist
|
||||
- Measured optimization evidence: bounded synthetic batch materialization
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready for integration
|
||||
- Done: materializer, loader adapter, verifier and required documentation
|
||||
- Next: integrator merge and post-merge acceptance
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `456f3e2`
|
||||
- Results: materializer passes 12 duplication/order/render/playback/diagnostic/
|
||||
source cases and 1,000 instances in 9.267 ms; all 50 autonomous headless
|
||||
verifiers pass, while the proprietary ADT placement probe is unavailable
|
||||
without `data/extracted`; checkpoint dry-run passes 7/7; documentation and
|
||||
coordination gates pass; loader-private inventory remains 30.
|
||||
- Remaining risks: private asset traversal/visual comparison, descriptor
|
||||
pressure, leaks and p95/p99 remain unavailable; materialization is still
|
||||
synchronous main-thread work and no original-client comparison is claimed.
|
||||
- Documentation updated: inline API; `m2-animated-instance-materializer.md`
|
||||
with data-flow, lifecycle, sequence and dependency diagrams; playback,
|
||||
finalizer, build, world-renderer specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001:456f3e2 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `c251985`
|
||||
- Post-merge results: materializer `cases=12 instances=1000 elapsed_ms=13.719`;
|
||||
playback/finalizer, shutdown, facade, internal-access `30`, checkpoint dry-run
|
||||
`7/7`, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001:c251985 -->
|
||||
@@ -0,0 +1,110 @@
|
||||
# M03-RND-M2-ANIMATED-SCENE-FINALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATED-SCENE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-animated-scene-finalizer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animated-scene-finalizer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract animated M2 PackedScene candidate instantiation, material override repair
|
||||
and AnimationPlayer validation from `StreamingWorldLoader` into an explicit
|
||||
main-thread finalizer.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Call ResourceLoader or interpret threaded-load statuses.
|
||||
- Choose animation eligibility, cache paths, material prototypes or permits.
|
||||
- Adopt/cache prototype Nodes or change static/native animation paths.
|
||||
- Change visible materials, animation selection, cache formats or profiles.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_animated_scene_finalizer.gd`,
|
||||
`src/tools/verify_m2_animated_scene_finalizer.gd`,
|
||||
`docs/modules/m2-animated-scene-finalizer.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
|
||||
animation pipeline/prototype/material/build/shutdown/facade/internal-access/
|
||||
manifest verifiers and module specs, `docs/modules/world-renderer.md`,
|
||||
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Only a PackedScene whose instantiated root is Node3D becomes a candidate;
|
||||
unsupported or invalid roots return null and any created invalid root is freed.
|
||||
- Material repair preserves depth-first MeshInstance3D order, source-index
|
||||
clamping, per-surface override priority and first-material fallback.
|
||||
- Finalization accepts a candidate only when at least one descendant
|
||||
AnimationPlayer exists; rejected detached candidates are freed synchronously.
|
||||
- Accepted result transfers the exact Node3D and player count to the loader.
|
||||
- Loader retains ResourceLoader, material-prototype acquisition, permit,
|
||||
prototype adoption/static fallback and diagnostic-log decisions.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted animation load pipeline package on master `c20bc62`
|
||||
- Blocks: further animated M2 build/materialization decomposition
|
||||
- External state: imported GLB/PackedScene shape remains engine-owned input
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated invalid/ownership/traversal/material/finalization/source/
|
||||
timing verifier; animation pipeline/prototype/shutdown/material/M2 build/facade/
|
||||
internal-access/manifest and adjacent M2 regressions; docs/coordination/dry-run gates
|
||||
- Fixtures: synthetic PackedScenes, nested AnimationPlayers and ArrayMesh materials
|
||||
- Fidelity evidence: exact material mapping and accepted/rejected Node transitions
|
||||
- Performance budget: 10,000 depth-first candidate queries under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent animation/prototype/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2AnimatedSceneFinalizer`
|
||||
- Simplest solution: one stateless main-thread RefCounted over engine scene types
|
||||
- Rejected complexity: callbacks, generic scene pipeline, signals or cache ownership
|
||||
- Unavoidable complexity: material repair occurs between candidate creation and acceptance
|
||||
- Measured optimization evidence: bounded synthetic subtree traversal
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: finalizer, loader adapters, lifecycle/material verifier and required documentation
|
||||
- Next: continue animated M2 playback/instance materialization extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `1b450dc`
|
||||
- Results: animated scene finalizer passes 13 invalid/type/lifetime/traversal/
|
||||
material/transfer/source cases and 10,000 depth-eight queries in 40.126 ms;
|
||||
all 48 autonomous headless verifiers pass, while the proprietary ADT placement
|
||||
probe is unavailable without `data/extracted`; checkpoint dry-run passes 7/7;
|
||||
documentation and coordination gates pass; loader-private inventory remains 30.
|
||||
- Remaining risks: proprietary GLB traversal, material/animation comparison,
|
||||
descriptor pressure, leaks and p95/p99 remain unavailable; no paired original-
|
||||
client visual comparison is claimed. Positional material mapping is unchanged.
|
||||
- Documentation updated: inline API; `m2-animated-scene-finalizer.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; animation pipeline,
|
||||
prototype/world-renderer specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATED-SCENE-FINALIZER-001:1b450dc -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `e7a7c67`
|
||||
- Post-merge results: finalizer `cases=13 iterations=10000
|
||||
elapsed_ms=40.314`; animation pipeline, prototype state, shutdown, materials,
|
||||
facade, internal-access `30`, manifest `7/7`, documentation and coordination passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATED-SCENE-FINALIZER-001:e7a7c67 -->
|
||||
@@ -0,0 +1,112 @@
|
||||
# M03-RND-M2-ANIMATION-LOAD-PIPELINE-001 — M2 animation load pipeline state
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATION-LOAD-PIPELINE-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Ownership
|
||||
|
||||
- Target: M03
|
||||
- Program: RND
|
||||
- Owner/Agent ID: sindo-main-codex
|
||||
- Branch: `work/sindo-main-codex/m03-m2-animation-load-pipeline`
|
||||
- Lease expires UTC: 2026-07-20
|
||||
- Integrator: M03 milestone integrator
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract threaded animated-M2 pending request records and completion-order
|
||||
finalize FIFO from `StreamingWorldLoader` into an explicit state service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Call ResourceLoader, select/validate GLB paths or interpret status values.
|
||||
- Instantiate/repair/cache animated Nodes or own prototype outcomes.
|
||||
- Change animation allow/deny/candidate policy, permits or visible behavior.
|
||||
- Add cancellation, persistence, retries, dependencies or cache-format changes.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_animation_load_pipeline_state.gd`,
|
||||
`src/tools/verify_m2_animation_load_pipeline_state.gd`,
|
||||
`docs/modules/m2-animation-load-pipeline-state.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
|
||||
prototype/shutdown/material/build/facade/internal-access/manifest verifiers
|
||||
and module specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
|
||||
`RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
|
||||
proprietary renderer corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- One non-empty normalized path has at most one pending cache path.
|
||||
- Pending snapshots preserve Dictionary insertion order and detach records.
|
||||
- Completing a known request moves one copied record with opaque status into a
|
||||
completion-order FIFO; unknown/duplicate completion is rejected.
|
||||
- Defensive discard removes pending state without creating a finalize record.
|
||||
- Total work remains pending plus finalize count at all three historical metrics.
|
||||
- Map reset and shutdown retain the same two clear sites; shutdown still drains
|
||||
ResourceLoader paths before clear.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted prototype cache package on master `20a64b5`
|
||||
- Blocks: animated M2 finalization/materialization service extraction
|
||||
- External state: ResourceLoader request statuses remain loader-owned
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated validation/dedupe/order/transition/discard/snapshot/clear/
|
||||
source/timing verifier; prototype/shutdown/material/M2 build/facade/internal-
|
||||
access/manifest and adjacent M2 regressions; docs/coordination/dry-run gates
|
||||
- Fixtures: synthetic normalized/cache paths and opaque integer statuses
|
||||
- Fidelity evidence: request insertion, terminal FIFO, metrics and clear transitions
|
||||
- Performance budget: 100 cycles over 256 request completions under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline public API docs
|
||||
- Module specification with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent M2/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2AnimationLoadPipelineState`
|
||||
- Simplest solution: one main-thread RefCounted with pending map and finalize FIFO
|
||||
- Rejected complexity: generic Resource pipeline, signals, callbacks, typed
|
||||
ResourceLoader enum wrapper, Node ownership or async abstraction
|
||||
- Unavoidable complexity: pending insertion order and terminal completion order differ
|
||||
- Measured optimization evidence: bounded synthetic transition loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: state, loader adapters, lifecycle verifier and required documentation
|
||||
- Next: continue animated M2 finalization/materialization extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `b8e0f11`
|
||||
- Results: animation pipeline passes 11 validation/dedupe/order/transition/FIFO/
|
||||
discard/snapshot/source cases and 100-by-256 cycles in 69.798 ms; all 47
|
||||
autonomous headless verifiers pass, while the proprietary ADT placement probe
|
||||
is unavailable without `data/extracted`; checkpoint dry-run passes 7/7;
|
||||
documentation and coordination gates pass; loader-private inventory decreases
|
||||
from 31 to 30.
|
||||
- Remaining risks: successful animated-GLB traversal, descriptor pressure,
|
||||
leaks, animation fidelity and p95/p99 require proprietary data; no paired
|
||||
original-client visual comparison is claimed.
|
||||
- Documentation updated: inline API; `m2-animation-load-pipeline-state.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; prototype/world-renderer
|
||||
specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATION-LOAD-PIPELINE-001:b8e0f11 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `3dccd3e`
|
||||
- Post-merge results: animation pipeline `cases=11 iterations=100
|
||||
elapsed_ms=70.309`; prototype state, shutdown, materials, facade,
|
||||
internal-access `30`, manifest `7/7`, documentation and coordination gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATION-LOAD-PIPELINE-001:3dccd3e -->
|
||||
@@ -0,0 +1,110 @@
|
||||
# M03-RND-M2-ANIMATION-PLAYBACK-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATION-PLAYBACK-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-animation-playback`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animation-playback`
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract deterministic animated M2 playback selection/phase, AnimationPlayer
|
||||
mutation and native animator data/start behavior from `StreamingWorldLoader`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Duplicate or attach animated instances/batch roots.
|
||||
- Own visibility, shadow, Editor owner or build-job cursor rules.
|
||||
- Load/finalize/cache scenes or change animation eligibility.
|
||||
- Change candidate priority, phase formula, logging text or visible playback.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_animation_playback_controller.gd`,
|
||||
`src/tools/verify_m2_animation_playback_controller.gd`,
|
||||
`docs/modules/m2-animation-playback-controller.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 animated
|
||||
finalizer/build/prototype/material/facade/internal-access/manifest verifiers and
|
||||
specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Phase remains `abs(hash("path:index")) % 1000 / 1000.0`.
|
||||
- Exact animation-name priority remains ordinary Stand/Idle/Run/Walk, fish
|
||||
Run/Walk/Swim/Stand/Idle/Death, birds Run/Walk/Swim/Stand/Idle, then
|
||||
case-insensitive substring fallback and first-name fallback.
|
||||
- Every AnimationPlayer animation becomes linear-looping before selected play;
|
||||
positive-length selection seeks to deterministic phase.
|
||||
- Native animators match exact script identity in depth-first order, copy the
|
||||
same five fields, call prepare_runtime when available and set phase.
|
||||
- Runtime debug state is sampled only when existing debug mode requests it;
|
||||
loader retains final log formatting/path normalization.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted animated scene finalizer package on master `c0a7bfb`
|
||||
- Blocks: animated M2 instance materializer extraction
|
||||
- External state: AnimationPlayer and M2NativeAnimator APIs remain engine/scene contracts
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated phase/selection/loop/play/seek/native-copy/start/diagnostic/
|
||||
source/timing verifier; animated finalizer/pipeline/prototype/build/shutdown/
|
||||
material/facade/internal-access/manifest regressions; docs/coordination/dry-run gates
|
||||
- Fixtures: synthetic AnimationPlayers and real M2NativeAnimator nodes
|
||||
- Fidelity evidence: exact priority, phase and mutation/reference contracts
|
||||
- Performance budget: 20,000 selection/phase operations under 1 second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs
|
||||
- Module spec with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent finalizer/build/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2AnimationPlaybackController`
|
||||
- Simplest solution: stateless main-thread RefCounted over supplied Nodes/players
|
||||
- Rejected complexity: signals, playback state machine, generic animation framework
|
||||
- Unavoidable complexity: native and imported AnimationPlayer paths coexist
|
||||
- Measured optimization evidence: bounded selection/phase loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: playback controller, loader adapters, verifier and required documentation
|
||||
- Next: extract animated M2 instance/batch materialization
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `9b73571`
|
||||
- Results: playback controller passes 15 phase/selection/loop/play/seek/native-
|
||||
copy/start/diagnostic/source cases and 20,000 phase-selection pairs in
|
||||
23.415 ms; all 49 autonomous headless verifiers pass, while the proprietary
|
||||
ADT placement probe is unavailable without `data/extracted`; checkpoint
|
||||
dry-run passes 7/7; documentation and coordination gates pass; loader-private
|
||||
inventory remains 30.
|
||||
- Remaining risks: private animation-name/timing/native visual comparison,
|
||||
descriptor pressure, leaks and p95/p99 remain unavailable; default selection
|
||||
is still heuristic and no paired original-client comparison is claimed.
|
||||
- Documentation updated: inline API; `m2-animation-playback-controller.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; finalizer/build/world-
|
||||
renderer specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATION-PLAYBACK-001:9b73571 -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `c0fc191`
|
||||
- Post-merge results: playback `cases=15 iterations=20000 elapsed_ms=23.008`;
|
||||
animated finalizer/pipeline, prototype, build, shutdown, materials, facade,
|
||||
internal-access `30`, manifest `7/7`, documentation and coordination passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATION-PLAYBACK-001:c0fc191 -->
|
||||
@@ -0,0 +1,84 @@
|
||||
# M03-RND-M2-ANIMATION-RESOURCE-FINALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATION-RESOURCE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-animation-resource-finalizer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animation-resource-finalizer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move cached animated M2 ResourceLoader status polling, terminal Resource load,
|
||||
candidate finalization, prototype/static-only adoption and success diagnostics
|
||||
from the loader into a dedicated two-phase finalization service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change cached animation eligibility, request admission or native-first order.
|
||||
- Move material-prototype lookup, permit ownership, playback or materialization.
|
||||
- Change ResourceLoader status order, cache formats, profiles or visible output.
|
||||
- Generalize static and animated ResourceLoader paths behind callbacks/frameworks.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_animation_resource_finalizer.gd`,
|
||||
`src/tools/verify_m2_animation_resource_finalizer.gd`,
|
||||
`docs/modules/m2-animation-resource-finalizer.md`, this claim
|
||||
- Shared: loader, animation pipeline/scene finalizer/prototype cache/cached
|
||||
observer/world-renderer specs and verifiers, module registry, `RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Pending request snapshots are polled in existing Dictionary insertion order.
|
||||
- Empty Resource paths are discarded and marked static-only immediately.
|
||||
- Only LOADED/FAILED statuses move into completion-order finalize FIFO.
|
||||
- One permit processes one popped terminal record, including skipped/stale records.
|
||||
- Existing prototype/static-only state suppresses terminal Resource loading.
|
||||
- LOADED Resource is instantiated before material-prototype lookup is requested.
|
||||
- Candidate repair/finalize/adoption and exact `M2_ANIM_CACHE` debug fields remain.
|
||||
- Rejected candidates are released by the existing animated scene finalizer.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted animation pipeline, scene finalizer, cached/native observers
|
||||
- Blocks: remaining loader-owned M2 material-prototype lookup and action execution
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic polling/status/FIFO/skip/load/candidate/repair/adoption/log/source and
|
||||
timing contracts; adjacent M2/renderer gates; full headless suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: static M2 Resource terminal polling/finalize extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `e7cd967`
|
||||
- Results: finalizer `28` cases / `1,000` timing iterations; all `58/58`
|
||||
autonomous headless regressions; facade/internal-access/shutdown/manifest;
|
||||
documentation `45`; coordination; checkpoint dry-run `7/7`.
|
||||
- Remaining risks: private asset animation/visual/leak/p95/p99 evidence is absent;
|
||||
terminal Resource get/instantiation/repair stays synchronous; immediate prepared-
|
||||
candidate completion is a composition precondition; material lookup stays loader-owned.
|
||||
- Documentation updated: new finalizer API/I/O/data-flow/state/sequence/dependency/
|
||||
ownership spec; pipeline, scene finalizer, cached observer, prototype cache,
|
||||
world renderer, module registry, `RENDER.md` and M03 Evidence.
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge: `06f6394`
|
||||
- Post-merge: finalizer, pipeline, scene finalizer, cached observer, prototype
|
||||
cache, snapshot, dispatch, shutdown, facade, internal-access `30`, manifest
|
||||
`7/7`, documentation `45`, coordination and checkpoint dry-run `7/7` passed.
|
||||
@@ -0,0 +1,97 @@
|
||||
# M03-RND-M2-BUILD-DISPATCH-PLANNER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-DISPATCH-PLANNER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-build-dispatch-planner`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-build-dispatch-planner`
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract the pure M2 build-step dispatch decision from the loader while preserving
|
||||
animation-wait priority, static-resource retry, missing-model advancement and
|
||||
exact permit/cursor behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Move ResourceLoader I/O, path lookup, cache state or retry ownership.
|
||||
- Create/free Nodes, Meshes, MultiMeshes or animated instances in the planner.
|
||||
- Change batch sizing, queue ordering, transforms, visuals or cache versions.
|
||||
- Add spatial-cell batching or new diagnostics/profile settings.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_build_dispatch_planner.gd`,
|
||||
`src/tools/verify_m2_build_dispatch_planner.gd`,
|
||||
`docs/modules/m2-build-dispatch-planner.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 build/
|
||||
queue/materializer/cache verifiers and specs, `docs/modules/world-renderer.md`,
|
||||
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Pending animation request wins before empty/animated/static decisions.
|
||||
- Positive batch with animated prototype selects animated materialization.
|
||||
- Positive static batch selects prepared Mesh, waits when unresolved, and advances
|
||||
without materialization only when the model is terminally missing.
|
||||
- Zero/non-positive batch advances without materialization.
|
||||
- Planner returns a fresh action plan and owns no state or engine reference.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted typed M2 build queue on master `c74b90a`
|
||||
- Blocks: extracting stateful M2 resource readiness/dispatch orchestration
|
||||
- External state: cache lookups, requests and engine lifetime remain loader APIs
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated action-priority/matrix/fresh-result/source/timing verifier;
|
||||
M2 queue/planner/materializer/cache/prototype/shutdown/facade/internal-access/
|
||||
manifest regressions; documentation, coordination and checkpoint dry-run gates
|
||||
- Fixtures: scalar availability and terminal-state combinations only
|
||||
- Fidelity evidence: exact existing branch priority and retry/advance behavior
|
||||
- Performance budget: 20,000 pure dispatch plans under one second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs
|
||||
- Module spec with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent M2 build/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name: `M2BuildDispatchPlanner`
|
||||
- Simplest solution: one stateless RefCounted with explicit action constants
|
||||
- Rejected complexity: callbacks, signals, cache references and generic state machine
|
||||
- Unavoidable complexity: animation-pending priority precedes every other action
|
||||
- Measured optimization evidence: bounded synthetic decision loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: integration accepted
|
||||
- Done: implementation, verification, documentation and worktree handoff
|
||||
- Next: extract typed M2 resource observation/request orchestration
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `17f5cc0faa376c8ac84a7ac72d5b8c0366866980`
|
||||
- Results: dispatch planner `cases=10 iterations=20000 elapsed_ms=11.933`;
|
||||
all autonomous headless regressions `53/53`; internal access
|
||||
`private_symbols=30`; baseline manifest/dry-run `7/7`; documentation
|
||||
`module_specs=40`; coordination passed with 30 historical expired warnings.
|
||||
- Remaining risks: proprietary ADT visual/p95/p99 evidence is unavailable;
|
||||
resource observation/requests, action execution, permits and engine lifetime
|
||||
intentionally remain loader-owned.
|
||||
- Documentation updated: new `m2-build-dispatch-planner.md` with API/I/O,
|
||||
data-flow, lifecycle, sequence, dependency and ownership diagrams; batch/queue/
|
||||
world renderer specs, module registry, RENDER and M03 Evidence updated.
|
||||
- Integration: master merge `99a90ddfb3364d6672198887198602b84dd19e58`;
|
||||
post-merge dispatch planner `cases=10 iterations=20000 elapsed_ms=12.291`,
|
||||
twelve adjacent M2/resource/lifetime/facade/internal-access/manifest checks,
|
||||
documentation and coordination all passed; checkpoint dry-run retained `7/7` plans.
|
||||
@@ -0,0 +1,98 @@
|
||||
# M03-RND-M2-BUILD-QUEUE-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-QUEUE-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-build-queue`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-build-queue`
|
||||
|
||||
## Outcome
|
||||
|
||||
Replace loader-owned raw M2 build-job Dictionary/FIFO fields with typed job and
|
||||
queue state while preserving cursor, rotation, stale-key and lifetime behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Move tile eligibility, resource readiness/retry or scheduler policy.
|
||||
- Create/free Nodes, Meshes, MultiMeshes or animated instances inside queue state.
|
||||
- Change grouping, batch planning, materialization, transform order or visuals.
|
||||
- Change M2 cache versions, diagnostics text or Editor persistence.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_build_job.gd`,
|
||||
`src/render/m2/m2_build_queue.gd`,
|
||||
`src/tools/verify_m2_build_queue.gd`,
|
||||
`docs/modules/m2-build-queue.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 build/
|
||||
materializer/cache/shutdown/facade/internal-access verifiers and specs,
|
||||
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- A job retains tile key, root, exact groups Dictionary, insertion-order
|
||||
`groups.keys()` snapshot, group index, transform offset and batch serial.
|
||||
- FIFO keeps duplicate/stale keys; front rotation moves exactly one key to tail.
|
||||
- Job erase does not erase FIFO entries, preserving external-cancel stale cleanup.
|
||||
- Progress adopts group index, transform offset and serial atomically.
|
||||
- Queue release never frees engine objects; loader retains finish/cancel cleanup.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted static materializer package on master `81e33a9`
|
||||
- Blocks: extracting remaining M2 build/resource orchestration
|
||||
- External state: Node validity and SceneTree destruction remain loader APIs
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated invalid/FIFO/duplicate/stale/rotation/cursor/lifetime/
|
||||
diagnostics/source/timing verifier; M2 planner/materializer/cache/prototype/
|
||||
shutdown/facade/internal-access/manifest regressions; docs/coordination/dry-run gates
|
||||
- Fixtures: synthetic Node3D roots and ordered transform-group Dictionaries
|
||||
- Fidelity evidence: exact existing keyed state, FIFO and cursor transitions
|
||||
- Performance budget: 100 cycles of 256 enqueue/rotate/erase operations under one second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs
|
||||
- Module spec with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent build/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important names: `M2BuildJob`, `M2BuildQueue`
|
||||
- Simplest solution: two small RefCounted types mirroring accepted WMO queue pattern
|
||||
- Rejected complexity: signals, generic queue base, callbacks and engine destruction
|
||||
- Unavoidable complexity: keyed jobs and FIFO keys intentionally permit stale entries
|
||||
- Measured optimization evidence: bounded synthetic lifecycle loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: integration accepted
|
||||
- Done: implementation, verification, documentation and worktree handoff
|
||||
- Next: extract the next unclaimed M2 resource-dispatch seam
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `4a8338f2f0503c2ba31c495e00a696d359518c06`
|
||||
- Results: cold editor parse `exit=0`; M2 build queue `cases=13 iterations=100
|
||||
elapsed_ms=66.247`; all autonomous headless regressions `52/52`; internal
|
||||
access `private_symbols=30`; baseline manifest/dry-run `7/7`; documentation
|
||||
`module_specs=39`; coordination passed with 30 historical expired warnings.
|
||||
- Remaining risks: proprietary ADT visual/p95/p99 evidence is unavailable;
|
||||
resource readiness/dispatch, permits, materializer choice and root destruction
|
||||
intentionally remain loader-owned.
|
||||
- Documentation updated: new `m2-build-queue.md` with API/I/O, data-flow,
|
||||
lifecycle, sequence, dependency and ownership diagrams; world renderer,
|
||||
batch planner, module registry, RENDER and M03 Evidence updated.
|
||||
- Integration: master merge `1dc013e5c2597d5e6f26f44807f5f0a943bf1517`;
|
||||
post-merge queue `cases=13 iterations=100 elapsed_ms=65.998`, ten adjacent
|
||||
M2/lifetime/facade/internal-access/manifest checks, documentation and
|
||||
coordination all passed; checkpoint dry-run retained `7/7` plans.
|
||||
@@ -0,0 +1,98 @@
|
||||
# M03-RND-M2-BUILD-RESOURCE-SNAPSHOT-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-RESOURCE-SNAPSHOT-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-build-resource-snapshot`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-build-resource-snapshot`
|
||||
|
||||
## Outcome
|
||||
|
||||
Replace loose per-step M2 animated/static resource variables with a typed
|
||||
observation snapshot consumed by the dispatch planner and materializer adapter.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Move ResourceLoader I/O, cache lookup/request execution or retry ownership.
|
||||
- Own or free prototype Nodes or Mesh Resources.
|
||||
- Change dispatch priority, batch sizing, queue ordering, permits or visuals.
|
||||
- Change M2 cache paths, formats, allowlists or native animation selection.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_build_resource_snapshot.gd`,
|
||||
`src/tools/verify_m2_build_resource_snapshot.gd`,
|
||||
`docs/modules/m2-build-resource-snapshot.md`, this claim
|
||||
- Shared/hotspots: `src/render/m2/m2_build_dispatch_planner.gd`, its verifier/spec,
|
||||
`src/scenes/streaming/streaming_world_loader.gd`, M2 queue/materializer/cache
|
||||
specs and tests, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
|
||||
`RENDER.md`, `targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Snapshot retains normalized path, optional animated prototype, animation
|
||||
request state, optional static Mesh and terminal missing-model state.
|
||||
- Static observation is adopted only after animation is not pending, the batch
|
||||
is positive and no animated prototype exists.
|
||||
- Accessors borrow exact engine references without transferring/freeing ownership.
|
||||
- Diagnostics expose only path and scalar availability flags.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted M2 dispatch planner on master `b113db0`
|
||||
- Blocks: extracting M2 resource observation/request service ownership
|
||||
- External state: prototype/cache/pipeline/resource lifetime remains loader-owned
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated identity/adoption/diagnostic/lifetime/source/timing verifier;
|
||||
dispatch/queue/batch/materializer/cache/pipeline/shutdown/facade/internal-access/
|
||||
manifest regressions; documentation, coordination and checkpoint dry-run gates
|
||||
- Fixtures: synthetic Node3D prototype and ArrayMesh resources
|
||||
- Fidelity evidence: exact resource presence/pending/missing values and branch order
|
||||
- Performance budget: 20,000 snapshot construct/adopt/read cycles under one second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs
|
||||
- Module spec with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent dispatch/queue/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name: `M2BuildResourceSnapshot`
|
||||
- Simplest solution: one small RefCounted value holder with explicit accessors
|
||||
- Rejected complexity: generic resource union, signals, callbacks and cache ownership
|
||||
- Unavoidable complexity: two-phase animation then optional static observation
|
||||
- Measured optimization evidence: bounded synthetic lifecycle loop
|
||||
|
||||
## Status
|
||||
|
||||
- State: integration accepted
|
||||
- Done: implementation, verification, documentation and worktree handoff
|
||||
- Next: extract the M2 resource observation/request producer service
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ec1b90f1e44037572e3e5052e6dedc016c29859a`
|
||||
- Results: resource snapshot `cases=12 iterations=20000 elapsed_ms=42.995`;
|
||||
dispatch planner remained green; all autonomous headless regressions `54/54`;
|
||||
internal access `private_symbols=30`; baseline manifest/dry-run `7/7`;
|
||||
documentation `module_specs=41`; coordination passed with 30 historical warnings.
|
||||
- Remaining risks: proprietary ADT visual/p95/p99 evidence is unavailable;
|
||||
resource lookup/request execution, action execution, permits and engine lifetime
|
||||
intentionally remain loader-owned.
|
||||
- Documentation updated: new `m2-build-resource-snapshot.md` with API/I/O,
|
||||
data-flow, lifecycle, sequence, dependency and ownership diagrams; dispatch/
|
||||
queue/world renderer specs, module registry, RENDER and M03 Evidence updated.
|
||||
- Integration: master merge `4354834c50ae426640c721041af67d5265e82205`;
|
||||
post-merge snapshot `cases=12 iterations=20000 elapsed_ms=42.211`, dispatch
|
||||
planner and twelve adjacent resource/materializer/lifetime/facade/internal-
|
||||
access/manifest checks, documentation and coordination all passed; checkpoint
|
||||
dry-run retained `7/7` plans.
|
||||
@@ -0,0 +1,91 @@
|
||||
# M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-cached-animation-resource-observer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-cached-animation-resource-observer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move cached-GLB animated M2 lookup, allow/deny and primitive/schema safety
|
||||
selection, threaded request admission and static-only transition from the loader
|
||||
into a service that produces `M2BuildResourceSnapshot`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Move native GryphonRoost raw-data build, debug logging or finalize drains.
|
||||
- Own/free prototype Nodes or change cache/pipeline ownership.
|
||||
- Change allow/deny defaults, primitive limits, candidate order or GLB rules.
|
||||
- Change batching, permits, cache formats, profiles or visible output.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_cached_animation_resource_observer.gd`,
|
||||
`src/tools/verify_m2_cached_animation_resource_observer.gd`,
|
||||
`docs/modules/m2-cached-animation-resource-observer.md`, this claim
|
||||
- Shared: loader, M2 snapshot/animation pipeline/prototype specs and verifiers,
|
||||
world renderer, module registry, `RENDER.md` and M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Cached animated prototype wins; static-only and pending states do not request.
|
||||
- Empty allowlist rejects; denylist wins; matching is stripped/case-insensitive.
|
||||
- Historical nested/lowercase/basename `.glb` order remains exact.
|
||||
- Candidate requires animations, primitive limit and accepted empty or
|
||||
`pivot_prefix_v1` schema; other schemas are rejected.
|
||||
- Successful/`ERR_BUSY` request is remembered and reported pending; no usable
|
||||
candidate or request failure marks animation static.
|
||||
- Service produces a snapshot borrowing the exact prototype and frees nothing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted static observer `5ebf4de`, snapshot and animation pipeline
|
||||
- Blocks: native animation observation and remaining finalize extraction
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic cache/pipeline/snapshot lifecycle, policy/path/GLB metadata fixtures,
|
||||
source boundaries and timing; adjacent M2/renderer gates; full headless suite
|
||||
and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, integration and post-merge acceptance
|
||||
- Next: native M2 animation observation extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `df87619` (`render: extract cached M2 animation resource observer`)
|
||||
- Results: observer PASS (`33` cases, `20,000` iterations, `61.745ms`);
|
||||
full autonomous suite PASS (`56/56`); renderer checkpoint dry-run PASS (`7/7`);
|
||||
internal-access PASS (`30` private symbols); documentation PASS (`43` module
|
||||
specs); coordination PASS with `34` historical warnings.
|
||||
- Remaining risks: successful asynchronous admission is covered by source and
|
||||
existing pipeline regressions because an undrained unit request leaks; generated
|
||||
GLB metadata does not prove Godot import fidelity; private traversal, p95/p99,
|
||||
leak and paired visual evidence remain unavailable; native observation and
|
||||
terminal ResourceLoader polling/finalize remain loader-owned.
|
||||
- Documentation updated: new cached observer API/I/O/data-flow/state/sequence/
|
||||
dependency/ownership specification; M2 snapshot, batch/dispatch, animation
|
||||
pipeline/prototype, world-renderer, module registry, `RENDER.md` and M03 Evidence.
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge: `f79e064` (`merge: cached M2 animation resource observer`)
|
||||
- Post-merge: observer `33` cases / `20,000` iterations / `61.514ms`;
|
||||
animation pipeline `11` / `66.049ms`; snapshot `12` / `39.947ms`;
|
||||
dispatch `10` / `17.915ms`; prototype cache `16` / `35.053ms`; shutdown,
|
||||
facade, internal-access `30`, manifest `7/7`, documentation `43` and
|
||||
coordination passed. Checkpoint dry-run completed `7/7`; expected private
|
||||
Azeroth/character assets remained unavailable.
|
||||
@@ -0,0 +1,84 @@
|
||||
# M03-RND-M2-MESH-RESOURCE-FINALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-RESOURCE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-mesh-resource-finalizer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-mesh-resource-finalizer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move static M2 ResourceLoader status polling, terminal Resource/Mesh extraction,
|
||||
runtime Mesh preparation, Mesh-cache adoption and missing outcome from the
|
||||
loader into one dedicated finalization service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change static cache path selection or threaded request admission.
|
||||
- Merge static and animated terminal services behind a generic callback layer.
|
||||
- Own permits, MultiMesh materialization, prototype Nodes or SceneTree roots.
|
||||
- Change material refresh version, rebuild rules, cache formats or visible output.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_mesh_resource_finalizer.gd`,
|
||||
`src/tools/verify_m2_mesh_resource_finalizer.gd`,
|
||||
`docs/modules/m2-mesh-resource-finalizer.md`, this claim
|
||||
- Shared: loader, Mesh pipeline/cache/extractor/runtime-finalizer/raw-repository/
|
||||
prototype specs and verifiers, world renderer, registry, `RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Pending requests are polled in existing Dictionary insertion order.
|
||||
- Empty Resource paths are discarded and marked missing immediately.
|
||||
- Only LOADED/FAILED statuses enter completion-order finalize FIFO.
|
||||
- One permit pops exactly one terminal record, including skipped/cached records.
|
||||
- Existing cached Mesh suppresses terminal Resource retrieval.
|
||||
- Loaded Resource uses exact first-Mesh extraction and current refresh-version path.
|
||||
- Raw M2 data loads only when the extracted Mesh is stale.
|
||||
- Prepared Mesh is stored by existing replacement semantics; failures mark missing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted Mesh pipeline/cache/extractor/runtime finalizer/raw repository
|
||||
- Blocks: remaining loader-owned M2 materialization and orchestration cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic polling/FIFO/skip/load/extract/refresh/store/missing/source/timing
|
||||
contracts; adjacent M2/renderer gates; full headless suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: next unclaimed M03 renderer extraction package
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ddeb708` (`render: extract M2 Mesh resource finalizer`)
|
||||
- Results: finalizer PASS `cases=31 iterations=1000 elapsed_ms=0.767`;
|
||||
adjacent Mesh pipeline/cache/extractor/runtime-finalizer/raw-repository/
|
||||
prototype/shutdown checks passed; autonomous headless suite `59/59`;
|
||||
documentation `46`; coordination passed with `34` historical warnings;
|
||||
checkpoint dry-run `7/7`.
|
||||
- Remaining risks: terminal instantiation/raw parsing/rebuild remain synchronous
|
||||
main-thread work behind existing permits; no private-asset visual, leak,
|
||||
descriptor-pressure or p95/p99 evidence was added.
|
||||
- Documentation updated: new full module specification with API/I/O and
|
||||
data-flow/state/sequence/dependency diagrams; adjacent M2 module specs,
|
||||
world-renderer registry/spec, `RENDER.md` and M03 Evidence.
|
||||
- Merge: `34b7000` (`merge: M2 Mesh resource finalizer`)
|
||||
- Post-merge: finalizer PASS `cases=31 iterations=1000 elapsed_ms=0.826`;
|
||||
adjacent M2 pipeline/cache/extractor/runtime/repository/prototype/shutdown,
|
||||
facade, internal-access `30`, manifest `7/7`, documentation `46` and
|
||||
coordination gates passed.
|
||||
@@ -0,0 +1,84 @@
|
||||
# M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-native-animation-resource-observer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-native-animation-resource-observer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move native GryphonRoost M2 animation candidate selection, synchronous raw read,
|
||||
native animated build, prototype/static-only adoption and debug diagnostics from
|
||||
the loader into a dedicated observer.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change the exact native candidate predicate or add more animated models.
|
||||
- Move cached-GLB observation, ResourceLoader polling/finalize or playback.
|
||||
- Own attached instances, consume permits or change queue/batch behavior.
|
||||
- Change native parsing/building, cache formats, profiles or visible output.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_native_animation_resource_observer.gd`,
|
||||
`src/tools/verify_m2_native_animation_resource_observer.gd`,
|
||||
`docs/modules/m2-native-animation-resource-observer.md`, this claim
|
||||
- Shared: loader, M2 cached observer/snapshot/raw repository/prototype specs and
|
||||
verifiers, world renderer, module registry, `RENDER.md` and M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Only case-insensitive paths containing `gryphonroost` are native candidates.
|
||||
- Cached animated prototype wins; static-only state suppresses repeated reads.
|
||||
- Empty raw data or animated surfaces marks animation static.
|
||||
- Builder result must be non-null with at least one child; rejection marks static.
|
||||
- Successful result is adopted through first-wins prototype cache semantics.
|
||||
- Existing `M2_NATIVE_ANIM_CACHE` fields and debug gating remain exact.
|
||||
- Observer attaches/frees no Node and starts no async work.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted cached observer `6f385ed`, raw repository and prototype state
|
||||
- Blocks: remaining ResourceLoader polling/finalize extraction
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic candidate/cache/static/raw/build/adoption/lifetime/logging/source and
|
||||
timing contracts; adjacent M2/renderer gates; full headless suite and checkpoint
|
||||
dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: remaining cached ResourceLoader terminal polling/finalize extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `1cb0101`
|
||||
- Results: observer `33` cases / `20,000` timing iterations; all `57/57`
|
||||
autonomous headless regressions; facade/internal-access/shutdown/manifest;
|
||||
documentation `44`; coordination; checkpoint dry-run `7/7`.
|
||||
- Remaining risks: private asset animation/visual/leak/p95/p99 evidence is absent;
|
||||
native parse/build stays synchronous; historical childless rejected Node remains
|
||||
unfreed; cached ResourceLoader terminal polling/finalize stays loader-owned.
|
||||
- Documentation updated: new native observer API/I/O/data-flow/state/sequence/
|
||||
dependency/ownership spec; cached observer, snapshot, dispatch, repository,
|
||||
prototype cache, world renderer, module registry, `RENDER.md` and M03 Evidence.
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge: `d37c799`
|
||||
- Post-merge: native/cached observers, repository, cache, snapshot, dispatch,
|
||||
shutdown, facade, internal-access `30`, manifest `7/7`, documentation `44`,
|
||||
coordination and checkpoint dry-run `7/7` passed on master.
|
||||
@@ -0,0 +1,106 @@
|
||||
# M03-RND-M2-STATIC-BATCH-MATERIALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-STATIC-BATCH-MATERIALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-static-batch-materializer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-static-batch-materializer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Extract main-thread static M2 `MultiMesh` construction, render-property setup
|
||||
and batch attachment from `StreamingWorldLoader`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change M2 grouping, batch sizing, build cursors, budgets or retry behavior.
|
||||
- Change Mesh loading, refresh, cache ownership or missing-model outcomes.
|
||||
- Own animated M2 instances, Editor persistence or tile cleanup.
|
||||
- Introduce spatial cells or change transform order, visibility or shadows.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_static_batch_materializer.gd`,
|
||||
`src/tools/verify_m2_static_batch_materializer.gd`,
|
||||
`docs/modules/m2-static-batch-materializer.md`, this claim
|
||||
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 build/
|
||||
Mesh-cache/facade/internal-access/manifest verifiers and specs,
|
||||
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
|
||||
`targets/03-renderer-facade.md`
|
||||
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- `MultiMesh.TRANSFORM_3D`, exact Mesh identity, instance count and transform
|
||||
slice order remain unchanged.
|
||||
- Batch node name remains model basename plus serial.
|
||||
- Visibility end/margin and shadow setting remain exact renderer inputs.
|
||||
- A valid batch attaches once to the supplied M2 parent and returns its node.
|
||||
- Loader retains Editor-owner assignment, build cursor, budgets and retries.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted animated materializer package on master `86814de`
|
||||
- Blocks: further M2 build-job orchestration extraction
|
||||
- External state: Godot MultiMesh and SceneTree mutation remain main-thread APIs
|
||||
|
||||
## Verification
|
||||
|
||||
- Commands: dedicated invalid/name/mesh/count/order/render/attachment/source/timing
|
||||
verifier; M2 build/cache/prototype/animated/shutdown/material/facade/internal-
|
||||
access/manifest regressions; docs/coordination/dry-run gates
|
||||
- Fixtures: synthetic ArrayMesh and ordered Transform3D slices
|
||||
- Fidelity evidence: exact existing MultiMesh construction and render settings
|
||||
- Performance budget: materialize 10,000 transforms under one second
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs
|
||||
- Module spec with inputs/outputs, ownership and source map
|
||||
- Data-flow, lifecycle/state, sequence and dependency diagrams
|
||||
- Adjacent build/cache/world-renderer/module-registry/RENDER updates
|
||||
|
||||
## Simplicity and naming
|
||||
|
||||
- Important name introduced: `M2StaticBatchMaterializer`
|
||||
- Simplest solution: synchronous main-thread RefCounted returning attached node
|
||||
- Rejected complexity: signals, async queue, callbacks and generic materializer base
|
||||
- Unavoidable complexity: MultiMesh is a Godot Resource owned through its node
|
||||
- Measured optimization evidence: bounded synthetic transform upload
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready for integration
|
||||
- Done: materializer, loader adapter, verifier and required documentation
|
||||
- Next: integrator merge and post-merge acceptance
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `252927d`
|
||||
- Results: materializer passes 11 invalid/node/Mesh/render/attachment/source
|
||||
cases and 10,000 transform API writes in 0.552 ms; all 51 autonomous headless
|
||||
verifiers pass, while the proprietary ADT placement probe is unavailable
|
||||
without `data/extracted`; checkpoint dry-run passes 7/7; documentation and
|
||||
coordination gates pass; loader-private inventory remains 30.
|
||||
- Remaining risks: headless dummy timing excludes GPU upload cost; private asset
|
||||
traversal/visual comparison, spatial-cell culling, descriptor pressure, leaks
|
||||
and p95/p99 remain unavailable; no original-client comparison is claimed.
|
||||
- Documentation updated: inline API; `m2-static-batch-materializer.md` with
|
||||
data-flow, lifecycle, sequence and dependency diagrams; build/cache/world-
|
||||
renderer specs, module registry and `RENDER.md`.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-STATIC-BATCH-MATERIALIZER-001:252927d -->
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge commit: `5e82daa`
|
||||
- Post-merge results: static materializer `cases=11 instances=10000
|
||||
elapsed_ms=1.327`; build/cache/animated materializer, shutdown, facade,
|
||||
internal-access `30`, checkpoint dry-run `7/7`, documentation and coordination
|
||||
gates passed.
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-STATIC-BATCH-MATERIALIZER-001:5e82daa -->
|
||||
@@ -0,0 +1,86 @@
|
||||
# M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-m2-static-build-resource-observer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-static-build-resource-observer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move static M2 build cache lookup, threaded request selection and terminal
|
||||
missing transition from the loader into a service that fills
|
||||
`M2BuildResourceSnapshot`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Move animation/native prototype observation or finalize drains.
|
||||
- Own/free Meshes, Nodes or cache entries.
|
||||
- Change path candidate order, GLB filtering, retry behavior, permits or visuals.
|
||||
- Change cache formats, profiles or batching.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/m2/m2_static_build_resource_observer.gd`,
|
||||
`src/tools/verify_m2_static_build_resource_observer.gd`,
|
||||
`docs/modules/m2-static-build-resource-observer.md`, this claim
|
||||
- Shared: loader, M2 snapshot/dispatch/pipeline/cache specs and verifiers,
|
||||
world renderer, module registry, RENDER and M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Cached Mesh wins; terminal missing returns missing; existing pending request waits.
|
||||
- Candidate order remains normalized/lowercase nested then basename per extension.
|
||||
- `.tscn` precedes `.glb`; pivot-prefix GLB is skipped for static build.
|
||||
- Successful/`ERR_BUSY` request is remembered; no usable candidate marks missing.
|
||||
- Snapshot adopts exact Mesh/missing result; service never frees engine objects.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted resource snapshot `032a256`
|
||||
- Blocks: remaining animated resource observer extraction
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic cache/pipeline/snapshot lifecycle, path order, source boundaries and timing;
|
||||
adjacent M2 and renderer gates; full headless suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, integration and post-merge acceptance
|
||||
- Next: remaining animated resource observer extraction
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `7cb3e34` (`render: extract static M2 build resource observer`)
|
||||
- Results: observer PASS (`11` cases, `20,000` iterations, `64.654ms`);
|
||||
full autonomous suite PASS (`55/55`); renderer checkpoint dry-run PASS (`7/7`);
|
||||
internal-access PASS (`30` private symbols); documentation PASS (`42` module
|
||||
specs); coordination PASS with `30` historical warnings.
|
||||
- Remaining risks: successful asynchronous admission is covered by source and
|
||||
existing pipeline regressions because an undrained unit request leaks; private
|
||||
asset traversal, p95/p99, leak and visual evidence remain unavailable;
|
||||
animated observation and static finalize polling remain loader-owned.
|
||||
- Documentation updated: new observer module API/I/O/data-flow/state/sequence/
|
||||
dependency/ownership specification; M2 snapshot, pipeline/cache, world-renderer,
|
||||
module registry, `RENDER.md` and M03 Evidence updated.
|
||||
|
||||
## Integration
|
||||
|
||||
- Merge: `a043c79` (`merge: static M2 build resource observer`)
|
||||
- Post-merge: observer `11` cases / `20,000` iterations / `64.202ms`;
|
||||
snapshot `12` / `42.895ms`; dispatch `10` / `18.096ms`; Mesh pipeline
|
||||
`11` / `73.615ms`; Mesh cache `9` / `40.229ms`; prototype cache `16` /
|
||||
`35.947ms`; shutdown, facade, internal-access `30`, manifest `7/7`,
|
||||
documentation `42` and coordination passed. Checkpoint dry-run completed
|
||||
`7/7`; expected private Azeroth/character assets remained unavailable.
|
||||
@@ -0,0 +1,88 @@
|
||||
# M03-RND-WMO-RENDER-GROUP-MATERIALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-GROUP-MATERIALIZER-001:sindo-main-codex:2026-08-03 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-wmo-render-group-materializer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-wmo-render-group-materializer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move one-step WMO MeshInstance3D and MultiMeshInstance3D creation, render
|
||||
settings and attachment from the loader into one main-thread materializer.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change WMO placement, resource loading, build-step planning or queue state.
|
||||
- Change runtime Mesh finalization, WMOBuilder material behavior or cache formats.
|
||||
- Change permits, visibility distances, shadow policy or visible output.
|
||||
- Generalize M2 and WMO materialization behind a shared abstraction.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/wmo/wmo_render_group_materializer.gd`,
|
||||
`src/tools/verify_wmo_render_group_materializer.gd`,
|
||||
`docs/modules/wmo-render-group-materializer.md`, this claim
|
||||
- Shared: loader, renderer module registry/specification, `RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Mesh and MultiMesh resources retain exact identity.
|
||||
- Names and optional transforms preserve existing indexed fallback behavior.
|
||||
- Shadow and positive visibility-range settings are applied unchanged.
|
||||
- The supplied WMO root becomes the sole SceneTree owner of the created node.
|
||||
- Editor persisted ownership remains an explicit loader composition policy.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted WMO render build queue/planner and runtime Mesh finalizer
|
||||
- Blocks: remaining loader-owned WMO runtime traversal cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic Mesh/MultiMesh identity, naming, transform, rendering, attachment,
|
||||
invalid-input, ownership, source and bounded-timing contracts; adjacent WMO and
|
||||
renderer gates; full autonomous suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: integrator review and merge
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `f4d5e29` (`render: extract WMO render group materializer`)
|
||||
- Results: materializer PASS `cases=37 iterations=1000 elapsed_ms=2.771`;
|
||||
autonomous headless suite `63/64`, with only the proprietary
|
||||
`verify_adt_m2_placements.gd` probe unavailable because `data/extracted` is
|
||||
absent; cold/editor parse completed without script diagnostics after restoring
|
||||
ignored generated/native worktree artifacts; checkpoint dry-run retained
|
||||
`7/7`; documentation passed with `50` module specifications; coordination
|
||||
passed with `77` historical expired-claim warnings.
|
||||
- Fidelity: exact Mesh/MultiMesh identity, indexed/fallback presentation,
|
||||
render settings, attachment and scheduler/queue boundaries are unchanged.
|
||||
No private-asset or original-client visual parity claim is added.
|
||||
- Remaining risks: synchronous main-thread Node creation remains; no private WMO
|
||||
visual comparison, long traversal, leak/GPU or p95/p99 evidence.
|
||||
- Documentation: new full module specification with API/I/O and data-flow,
|
||||
sequence/dependency diagrams; renderer registry/source map and `RENDER.md`
|
||||
updated.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-GROUP-MATERIALIZER-001:f4d5e29 -->
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-GROUP-MATERIALIZER-001:705354d -->
|
||||
|
||||
- Merge: `705354d` (`merge: WMO render group materializer`)
|
||||
- Post-merge: materializer PASS `cases=37 iterations=1000 elapsed_ms=3.236`;
|
||||
all nine adjacent WMO services, shutdown, materials, facade, internal-access
|
||||
`30`, manifest `7/7`, documentation `50` and coordination passed.
|
||||
@@ -0,0 +1,79 @@
|
||||
# M03-RND-WMO-RENDER-RESOURCE-FINALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-RESOURCE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-wmo-render-resource-finalizer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-wmo-render-resource-finalizer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move lightweight WMO render-cache terminal ResourceLoader polling, script/format
|
||||
validation and Resource/missing publication from the loader into one service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change `.res` cache path selection or threaded request admission.
|
||||
- Own WMO build permits, queues, placement state, Nodes or materialization.
|
||||
- Change `WMOStreamingResource.FORMAT_VERSION`, fallback order or visible output.
|
||||
- Combine render Resource and PackedScene finalization behind a generic callback.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/wmo/wmo_render_resource_finalizer.gd`,
|
||||
`src/tools/verify_wmo_render_resource_finalizer.gd`,
|
||||
`docs/modules/wmo-render-resource-finalizer.md`, this claim
|
||||
- Shared: loader, WMO render cache state spec/verifier, world renderer registry/spec,
|
||||
`RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Pending requests are polled in detached Dictionary insertion order.
|
||||
- Only LOADED/FAILED statuses complete requests.
|
||||
- Failed, null, wrong-script and stale-format Resources publish missing outcome.
|
||||
- Current exact-script Resources are adopted without duplication.
|
||||
- Request admission, reset/shutdown drain order and cache lifetime are unchanged.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted WMO render Resource cache state
|
||||
- Blocks: remaining loader-owned WMO orchestration/materialization cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic status/order/load/validation/adoption/source/timing contracts;
|
||||
adjacent WMO/renderer gates; full headless suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: next unclaimed M03 renderer extraction package
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `a86f8f2` (`render: extract WMO render resource finalizer`)
|
||||
- Results: finalizer PASS `cases=24 iterations=1000 elapsed_ms=3.938`;
|
||||
adjacent WMO cache/queue/planner/registry/resolver/scene-cache/shutdown checks
|
||||
passed; autonomous headless suite `60/60`; documentation `47`; coordination
|
||||
passed with `34` historical warnings; checkpoint dry-run `7/7`.
|
||||
- Remaining risks: terminal ResourceLoader polling remains a synchronous
|
||||
main-thread boundary; no serialized private WMO, corrupt-cache, visual, leak,
|
||||
descriptor-pressure or p95/p99 evidence was added.
|
||||
- Documentation updated: new full module specification with API/I/O and
|
||||
data-flow/state/sequence/dependency diagrams; adjacent WMO cache/world-renderer
|
||||
specs, module registry, `RENDER.md` and M03 Evidence.
|
||||
- Merge: `1acddab` (`merge: WMO render resource finalizer`)
|
||||
- Post-merge: finalizer PASS `cases=24 iterations=1000 elapsed_ms=3.777`;
|
||||
WMO cache/queue/planner/registry/resolver/scene-cache/shutdown, facade,
|
||||
internal-access `30`, manifest and checkpoint dry-run `7/7`, documentation
|
||||
`47` and coordination gates passed.
|
||||
@@ -0,0 +1,78 @@
|
||||
# M03-RND-WMO-RUNTIME-MESH-FINALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-WMO-RUNTIME-MESH-FINALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-wmo-runtime-mesh-finalizer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-wmo-runtime-mesh-finalizer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move cached WMO runtime Mesh material refresh versioning, surface iteration and
|
||||
material-definition reconstruction from the loader into one service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change WMO traversal, placement, build-job scheduling or Node materialization.
|
||||
- Change WMOBuilder shader/material behavior or cache formats.
|
||||
- Change texture path ordering, metadata names or visible output.
|
||||
- Generalize M2 and WMO finalization behind a shared abstraction.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/wmo/wmo_runtime_mesh_finalizer.gd`,
|
||||
`src/tools/verify_wmo_runtime_mesh_finalizer.gd`,
|
||||
`docs/modules/wmo-runtime-mesh-finalizer.md`, this claim
|
||||
- Shared: loader, WMO build/cache and world renderer specs/verifiers,
|
||||
`RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Null meshes remain null; accepted meshes retain exact Resource identity.
|
||||
- Refresh metadata version `10` remains the admission boundary.
|
||||
- Only stale `ArrayMesh` surfaces with cached WMO texture metadata are rebuilt.
|
||||
- Texture indices remain compact and ordered texture0, texture1, texture2.
|
||||
- WMO flags, shader, blend mode and cached colors are forwarded unchanged.
|
||||
- Scene traversal, builder material semantics and shutdown order are unchanged.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted WMO render and scene Resource finalizers
|
||||
- Blocks: remaining loader-owned WMO runtime materialization cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic identity/version/type/surface/metadata/color/definition/source/timing
|
||||
contracts; adjacent WMO/renderer gates; full suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: next unclaimed M03 renderer extraction package
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ae40e3f` (`render: extract WMO runtime Mesh finalizer`)
|
||||
- Results: finalizer PASS `cases=27 iterations=1000 elapsed_ms=0.402` in the
|
||||
full run; autonomous headless suite `63/63`; documentation `49`; coordination
|
||||
passed with `34` historical warnings; checkpoint dry-run `7/7`.
|
||||
- Remaining risks: WMOBuilder surface mutation remains synchronous main-thread
|
||||
work; no private WMO corpus, visual comparison, leak/GPU or p95/p99 evidence.
|
||||
- Documentation updated: new full module specification with API/I/O and
|
||||
data-flow/state/sequence/dependency diagrams; renderer registry/source map,
|
||||
`RENDER.md` and M03 Evidence.
|
||||
- Merge: `d65ebee` (`merge: WMO runtime Mesh finalizer`)
|
||||
- Post-merge: finalizer PASS `cases=27 iterations=1000 elapsed_ms=0.202`;
|
||||
WMO scene/render cache/finalizer/queue/planner/registry/resolver, shutdown,
|
||||
materials, facade, internal-access `30`, manifest and checkpoint dry-run
|
||||
`7/7`, documentation `49` and coordination gates passed.
|
||||
@@ -0,0 +1,93 @@
|
||||
# M03-RND-WMO-RUNTIME-SCENE-PREPARER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-WMO-RUNTIME-SCENE-PREPARER-001:sindo-main-codex:2026-08-03 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-wmo-runtime-scene-preparer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-wmo-runtime-scene-preparer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move cached/live WMO subtree preparation—recursive Mesh finalization, direct
|
||||
Occluders-child policy and recursive shadow enabling—from the loader into one
|
||||
stateless main-thread service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change scene/prototype loading, validation, placement or attachment.
|
||||
- Change WMO material refresh rules, cache versions or builder behavior.
|
||||
- Change occlusion/shadow defaults or add visibility/portal behavior.
|
||||
- Change queue, scheduler, ownership or shutdown lifecycle.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/wmo/wmo_runtime_scene_preparer.gd`,
|
||||
`src/tools/verify_wmo_runtime_scene_preparer.gd`,
|
||||
`docs/modules/wmo-runtime-scene-preparer.md`, this claim
|
||||
- Shared: loader, renderer module registry/specification, `RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Cached preparation finalizes every MeshInstance3D Mesh and non-null
|
||||
MultiMeshInstance3D Mesh in depth-first traversal order.
|
||||
- Live preparation does not cross the runtime Mesh finalizer boundary.
|
||||
- Disabled occlusion removes only the direct child named `Occluders` and queues
|
||||
it for deletion; enabled occlusion retains it.
|
||||
- Enabled shadows recursively set every GeometryInstance3D to ON; disabled
|
||||
shadows preserve existing per-node values.
|
||||
- The service borrows the subtree and retains no Node or Resource.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted WMO runtime Mesh finalizer
|
||||
- Blocks: remaining loader-owned WMO scene/prototype orchestration cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic cached/live traversal, exact resource/order, occluder, shadow,
|
||||
invalid-input, ownership, source and bounded-timing contracts; adjacent WMO,
|
||||
renderer, documentation and coordination gates; checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: integrator review and merge
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `ffed91c` (`render: extract WMO runtime scene preparer`)
|
||||
- Results: preparer PASS `cases=31 iterations=1000 elapsed_ms=3.669`;
|
||||
updated Mesh-finalizer boundary PASS `cases=28 iterations=1000
|
||||
elapsed_ms=0.204`; headless suite `64/65` with no unexpected failures and
|
||||
only the proprietary ADT placement probe unavailable without `data/extracted`;
|
||||
editor parse had zero script diagnostics; checkpoint dry-run retained `7/7`;
|
||||
documentation passed with `51` module specifications; coordination passed
|
||||
with `77` historical expired-claim warnings.
|
||||
- Fidelity: cached/live preparation distinction, exact Mesh traversal order,
|
||||
direct `Occluders` lookup/removal and enabled/preserved shadow semantics are
|
||||
unchanged. No private-asset or original-client parity claim is added.
|
||||
- Remaining risks: recursive Resource/SceneTree mutation remains synchronous;
|
||||
no asset-backed WMO portal/room, visual, leak/GPU or p95/p99 evidence.
|
||||
- Documentation: new full module specification with API/I/O and data-flow,
|
||||
sequence/dependency diagrams; Mesh-finalizer ownership spec, renderer registry,
|
||||
world-renderer source map and `RENDER.md` updated.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RUNTIME-SCENE-PREPARER-001:ffed91c -->
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RUNTIME-SCENE-PREPARER-001:57d0a9f -->
|
||||
|
||||
- Merge: `57d0a9f` (`merge: WMO runtime scene preparer`)
|
||||
- Post-merge: preparer PASS `cases=31 iterations=1000 elapsed_ms=3.486`;
|
||||
Mesh finalizer `cases=28`/`0.199ms`; all nine adjacent WMO services, shutdown,
|
||||
materials, facade, internal-access `30`, manifest `7/7`, documentation `51`
|
||||
and coordination passed.
|
||||
@@ -0,0 +1,90 @@
|
||||
# M03-RND-WMO-SCENE-INSTANCE-FACTORY-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-WMO-SCENE-INSTANCE-FACTORY-001:sindo-main-codex:2026-08-03 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-wmo-scene-instance-factory`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-wmo-scene-instance-factory`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move WMO cached-scene instantiation/currentness validation and live-prototype
|
||||
duplication with shared name/placement application into one main-thread factory.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change cache lookup/admission, ResourceLoader or prototype construction.
|
||||
- Change runtime Mesh/material/occluder/shadow preparation.
|
||||
- Change attachment, placement registry, queues, permits or lifetime.
|
||||
- Change scene cache version rules or placement formulas.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/wmo/wmo_scene_instance_factory.gd`,
|
||||
`src/tools/verify_wmo_scene_instance_factory.gd`,
|
||||
`docs/modules/wmo-scene-instance-factory.md`, this claim
|
||||
- Shared: loader, adjacent WMO verifier/spec, renderer registry/specification,
|
||||
`RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Null scenes/prototypes and non-Node3D instantiation/duplication return null.
|
||||
- Cached instances failing the injected currentness validator are synchronously freed.
|
||||
- Accepted instances retain exact descendant Resource identities.
|
||||
- Both paths apply `relative_path.get_file().get_basename()` and the exact
|
||||
placement-resolver Transform3D.
|
||||
- The factory returns detached roots and retains no Node or Resource.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted WMO placement resolver and runtime scene preparer
|
||||
- Blocks: remaining loader-owned WMO cache/prototype orchestration cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic cached/live type, validation, freeing, exact identity, name,
|
||||
transform, dependency/order, ownership, source and bounded-timing contracts;
|
||||
adjacent WMO/renderer gates and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: ready
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: integrator review and merge
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `7e97b19` (`render: extract WMO scene instance factory`)
|
||||
- Results: factory PASS `cases=41 iterations=1000 elapsed_ms=5.297`;
|
||||
placement resolver dependency PASS `cases=10 iterations=20000
|
||||
elapsed_ms=28.740`; suite `65/66` with no unexpected failures and only the
|
||||
proprietary ADT placement probe unavailable; editor parse had zero script
|
||||
diagnostics; checkpoint dry-run retained `7/7`; documentation passed with
|
||||
`52` module specifications; coordination passed with `77` historical warnings.
|
||||
- Fidelity: valid cached/live cache-validation distinction, basename, exact
|
||||
Transform3D and descendant Resource identity are unchanged. Invalid non-Node3D
|
||||
cached roots are now freed synchronously, fixing an error-path leak without
|
||||
changing valid visible output.
|
||||
- Remaining risks: instantiation/duplication remains synchronous; no private WMO
|
||||
portal/room, visual, long-traversal, leak/GPU or p95/p99 evidence.
|
||||
- Documentation: new full factory module specification; placement resolver
|
||||
consumers/sequence, renderer registry/source map and `RENDER.md` updated.
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-SCENE-INSTANCE-FACTORY-001:7e97b19 -->
|
||||
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-SCENE-INSTANCE-FACTORY-001:541279e -->
|
||||
|
||||
- Merge: `541279e` (`merge: WMO scene instance factory`)
|
||||
- Post-merge: factory PASS `cases=41 iterations=1000 elapsed_ms=5.308`;
|
||||
placement resolver `cases=10`/`31.207ms`; runtime preparation/finalization,
|
||||
adjacent WMO services, shutdown, materials, facade, internal-access `30`,
|
||||
manifest `7/7`, documentation `52` and coordination passed.
|
||||
@@ -0,0 +1,80 @@
|
||||
# M03-RND-WMO-SCENE-RESOURCE-FINALIZER-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M03-RND-WMO-SCENE-RESOURCE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex`
|
||||
- Target: M03 Renderer Facade and Safe Extraction
|
||||
- Branch: `work/sindo-main-codex/m03-wmo-scene-resource-finalizer`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-wmo-scene-resource-finalizer`
|
||||
|
||||
## Outcome
|
||||
|
||||
Move cached WMO PackedScene terminal ResourceLoader polling, probe validation,
|
||||
probe lifetime and scene/missing publication from the loader into one service.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Change `.tscn` path selection, file-size admission or request start behavior.
|
||||
- Own live fallback, placement/build jobs, attached Nodes or scheduler permits.
|
||||
- Change WMOBuilder cache metadata/version rules or visible output.
|
||||
- Merge PackedScene and lightweight render Resource finalization generically.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `src/render/wmo/wmo_scene_resource_finalizer.gd`,
|
||||
`src/tools/verify_wmo_scene_resource_finalizer.gd`,
|
||||
`docs/modules/wmo-scene-resource-finalizer.md`, this claim
|
||||
- Shared: loader, WMO scene cache state spec/verifier, world renderer registry/spec,
|
||||
`RENDER.md`, M03 Evidence
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- Pending requests are polled in detached Dictionary insertion order.
|
||||
- Only LOADED/FAILED statuses complete requests.
|
||||
- Failed, null, non-PackedScene, non-Node3D and stale scenes publish missing.
|
||||
- Accepted PackedScenes retain exact Resource identity.
|
||||
- Validation instantiates once and releases accepted/rejected Node3D probes once.
|
||||
- Admission, oversize logging, live fallback and shutdown order are unchanged.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: accepted WMO scene Resource cache state
|
||||
- Blocks: remaining loader-owned WMO live fallback/materialization cleanup
|
||||
|
||||
## Verification
|
||||
|
||||
- Synthetic status/order/type/probe/validation/lifetime/adoption/source/timing
|
||||
contracts; adjacent WMO/renderer gates; full suite and checkpoint dry-run.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
|
||||
diagrams; adjacent renderer docs and M03 Evidence.
|
||||
|
||||
## Status
|
||||
|
||||
- State: accepted
|
||||
- Done: implementation, verification and documentation
|
||||
- Next: next unclaimed M03 renderer extraction package
|
||||
- Blocked by:
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `7779e9e` (`render: extract WMO scene resource finalizer`)
|
||||
- Results: finalizer PASS `cases=26 iterations=1000 elapsed_ms=4.186`;
|
||||
adjacent WMO scene/render cache/finalizer/queue/planner/registry/resolver and
|
||||
shutdown checks passed; autonomous headless suite `61/61`; documentation `48`;
|
||||
coordination passed with `34` historical warnings; checkpoint dry-run `7/7`.
|
||||
- Remaining risks: PackedScene probe validation remains synchronous main-thread
|
||||
work; no serialized private WMO stale/oversize, long leak, visual,
|
||||
descriptor-pressure or p95/p99 evidence was added.
|
||||
- Documentation updated: new full module specification with API/I/O and
|
||||
data-flow/state/sequence/dependency diagrams; adjacent WMO cache/world-renderer
|
||||
specs, module registry, `RENDER.md` and M03 Evidence.
|
||||
- Merge: `6a0f9bd` (`merge: WMO scene resource finalizer`)
|
||||
- Post-merge: finalizer PASS `cases=26 iterations=1000 elapsed_ms=3.970`;
|
||||
WMO scene/render cache/finalizer/queue/planner/registry/resolver/shutdown,
|
||||
facade, internal-access `30`, manifest and checkpoint dry-run `7/7`,
|
||||
documentation `48` and coordination gates passed.
|
||||
@@ -0,0 +1,95 @@
|
||||
# M04-RND-FIDELITY-ROADMAP-001
|
||||
|
||||
<!-- OPENWC_CLAIM:M04-RND-FIDELITY-ROADMAP-001:sindo-main-codex-renderer-roadmap:2026-08-04 -->
|
||||
<!-- OPENWC_INTEGRATION:ACCEPTED:M04-RND-FIDELITY-ROADMAP-001:afbc4fa -->
|
||||
|
||||
## Owner
|
||||
|
||||
- Agent ID: `sindo-main-codex-renderer-roadmap`
|
||||
- Target: M04 Renderer Fidelity and Graphics Foundation
|
||||
- Branch: `work/sindo-main-codex-renderer-roadmap/m04-renderer-fidelity-plan`
|
||||
- Worktree: `C:\Users\sindo\open-wc-worktrees\m04-renderer-fidelity-plan`
|
||||
|
||||
## Outcome
|
||||
|
||||
Insert an evidence-driven renderer-fidelity milestone immediately after M03 and
|
||||
shift the existing M04–M13 executable targets to M05–M14 without losing their
|
||||
content, dependencies or status semantics.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Implement renderer behavior or claim visual parity in this package.
|
||||
- Capture or commit proprietary original-client screenshots.
|
||||
- Begin the shifted Editor, content, network or gameplay milestones.
|
||||
|
||||
## Paths
|
||||
|
||||
- Exclusive: `targets/04-renderer-fidelity.md`, this claim
|
||||
- Shared/hotspot: all numbered target filenames and cross-target dependencies,
|
||||
`targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, `targets/roadmap/`,
|
||||
`docs/ROADMAP.md`, renderer/testing/feature documentation where milestone IDs
|
||||
or fidelity gates are normative
|
||||
|
||||
## Contracts and data
|
||||
|
||||
- M03 remains `DONE`; new M04 becomes the only `ACTIVE` target.
|
||||
- Existing executable targets shift monotonically: old M04–M13 become M05–M14.
|
||||
- The new renderer gate uses original build-12340 captures as the authoritative
|
||||
oracle and treats Noggit as a secondary composition/reference tool.
|
||||
- Proprietary images remain outside Git; manifests, metadata, hashes, metrics and
|
||||
human approval records are repository evidence.
|
||||
- `Blizzlike335` and opt-in enhanced/racing graphics remain explicit profiles.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Requires: completed M03 renderer facade and closeout contracts.
|
||||
- Blocks: renderer-fidelity implementation and all shifted milestones.
|
||||
|
||||
## Verification
|
||||
|
||||
- Coordination and documentation gates; target marker/index consistency; link
|
||||
and milestone-reference audit; `git diff --check`.
|
||||
|
||||
## Fidelity evidence
|
||||
|
||||
- Planning evidence only: exact local client build `3.3.5.12340`, seven existing
|
||||
paired checkpoints and the user-proposed two-viewpoint-per-location corpus.
|
||||
No new parity claim is made.
|
||||
|
||||
## Documentation deliverables
|
||||
|
||||
- Executable M04 target with reference, static, temporal, subsystem, performance
|
||||
and acceptance gates; shifted target dependencies; development/subsystem
|
||||
roadmap and testing/feature-map alignment.
|
||||
|
||||
## Status
|
||||
|
||||
- State: integrated
|
||||
- Done: inserted M04 renderer fidelity, shifted old M04–M13 to M05–M14,
|
||||
updated dependencies, normative roadmaps, profile boundaries and test policy
|
||||
- Next: begin the M04 reference manifest/capture contract work package when the
|
||||
user supplies or approves the original-client CSV/capture corpus
|
||||
- Blocked by:
|
||||
|
||||
<!-- OPENWC_HANDOFF:READY:M04-RND-FIDELITY-ROADMAP-001:f77f50c -->
|
||||
|
||||
## Handoff
|
||||
|
||||
- Commit: `f77f50c` on
|
||||
`work/sindo-main-codex-renderer-roadmap/m04-renderer-fidelity-plan`.
|
||||
- Outcome: M03 remains `DONE`; new M04 is the only `ACTIVE` target; Editor,
|
||||
content, server, world editor, network, gameplay, playable client, quest,
|
||||
completeness and dungeon targets moved intact to M05–M14.
|
||||
- Contracts: original build-12340 captures are authoritative; Noggit is a
|
||||
secondary composition reference; proprietary pixels remain outside Git;
|
||||
Blizzlike/Enhanced/Racing graphics profiles are explicit boundaries.
|
||||
- Verification: coordination passed `targets=15 active=1`; documentation passed
|
||||
`module_specs=53 required_files=7`; target sequence passed `M00–M14`; old
|
||||
executable-target links `0`; `git diff --check` passed.
|
||||
- Fidelity: planning and corpus provenance only; no renderer implementation or
|
||||
new parity claim is included.
|
||||
- Documentation: executable M04, target index/dependency graph, renderer
|
||||
subsystem plan, high-level roadmap, architecture, testing/tooling/coding/
|
||||
documentation policies and affected future milestone references updated.
|
||||
- Cache/migration: no runtime/cache/data format changes. The exact CSV schema is
|
||||
intentionally deferred to the first M04 contract work package.
|
||||
@@ -9,6 +9,12 @@ OpenWC состоит из двух продуктов на общей плат
|
||||
|
||||
Оба продукта используют общие канонические модели, импорт данных и renderer preview, но не разделяют UI, lifecycle и права на изменение данных.
|
||||
|
||||
Планируемый racing fork является третьим потребителем graphics foundation. Он
|
||||
может использовать world streaming, materials, lighting, shadows, liquids,
|
||||
characters, animation и effects contracts, но не зависит от WoW network,
|
||||
gameplay state или proprietary asset repository. Его улучшения выбираются
|
||||
отдельным `Racing` graphics profile и не меняют `Blizzlike335`.
|
||||
|
||||
## Архитектурные принципы
|
||||
|
||||
1. Сервер авторитетен для боя, ресурсов, инвентаря, квестового прогресса и общего мира.
|
||||
@@ -57,6 +63,11 @@ TrinityCore/AzerothCore ◄── Network Adapter ◄── Runtime Client
|
||||
|
||||
Renderer получает `StreamingFocus`, `WorldVisualSnapshot` и presentation-команды. Он НЕ ДОЛЖЕН читать packets, SQL или gameplay input.
|
||||
|
||||
`GraphicsProfile` выбирается на composition boundary и задаёт material,
|
||||
lighting, shadow, liquid, effects, distance и post-processing capabilities.
|
||||
Внутренние shader/services не смешивают Blizzlike/Enhanced/Racing policy через
|
||||
неявные глобальные switches.
|
||||
|
||||
### UI
|
||||
|
||||
Владеет login/realm/character screens, HUD и FrameXML/Lua-compatible presentation. UI читает immutable view models и отправляет intents. Lua API получает capability-based facade; прямой доступ к network, filesystem и editor API запрещён.
|
||||
@@ -157,6 +168,7 @@ addons/
|
||||
- `ServerSchemaAdapter` — inspect, import, diff, generate, validate.
|
||||
- `ContentTypeDescriptor` — schema, inspector, validator, compiler.
|
||||
- `WorldRenderer` — streaming focus и entity presentation.
|
||||
- `GraphicsProfile` — explicit Blizzlike/Enhanced/Racing visual capabilities.
|
||||
- `GameplaySystem` — commands/events без scene dependency.
|
||||
- `EditorTool` — selection, command creation и gizmo, без прямой записи.
|
||||
- `TestFixtureProvider` — обезличенные packets, DB snapshots и content fixtures.
|
||||
|
||||
@@ -197,7 +197,7 @@ func request_streaming_tile_load(request: StreamingTileLoadRequest) -> void:
|
||||
- Предпочитать именованные predicates длинным boolean expressions.
|
||||
- Branch по capability/profile должен быть локальным и типизированным.
|
||||
- Не распространять `if core == "azerothcore"` по проекту; использовать adapter/capability.
|
||||
- Не смешивать Blizzlike и Enhanced branches в каждом shader/service: выбирать profile/strategy на boundary.
|
||||
- Не смешивать Blizzlike, Enhanced и Racing branches в каждом shader/service: выбирать profile/strategy на boundary.
|
||||
- Pattern/table-driven mapping предпочтительнее сотен одинаковых `if`, если таблица остаётся читаемой и валидируемой.
|
||||
|
||||
## Comments
|
||||
|
||||
@@ -181,7 +181,7 @@ stateDiagram-v2
|
||||
- Uniform/global parameter: coordinate/color space, range, units и producer.
|
||||
- Material profile: supported WoW shader/blend modes и approximations.
|
||||
- Expensive branch/texture dependency имеет cost/fallback note.
|
||||
- Blizzlike и Enhanced behavior документируются отдельно.
|
||||
- Blizzlike, Enhanced и Racing behavior документируются отдельно.
|
||||
|
||||
### Network codecs
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Gameplay state, network session, renderer world, editor session, caches конк
|
||||
- Visibility ranges/HLOD, occlusion и automatic mesh LOD использовать совместно по профилю.
|
||||
- Unique materials/textures минимизировать; descriptor/resource counts являются budget metric.
|
||||
- RenderingServer RIDs имеют явного владельца и освобождаются в deterministic shutdown test.
|
||||
- Shader/material profiles разделяют Blizzlike и Enhanced; runtime не компилирует тяжёлые варианты при пересечении ADT boundary.
|
||||
- Shader/material profiles разделяют Blizzlike, Enhanced и Racing; runtime не компилирует тяжёлые варианты при пересечении ADT boundary.
|
||||
|
||||
## EditorPlugin lifecycle
|
||||
|
||||
|
||||
+15
-1
@@ -2,6 +2,11 @@
|
||||
|
||||
Reference-код используется для исследования форматов, поведения и архитектурных вариантов. Он не определяет API OpenWC и не копируется без проверки лицензии, корректности и соответствия Godot.
|
||||
|
||||
Актуальные branches и pinned commits локальных Git-референсов зафиксированы в
|
||||
[`reference/README.md`](../reference/README.md#git-reference-revisions). Gitlinks
|
||||
точно фиксируют ревизию, а `.gitmodules` задаёт canonical remote и ветку
|
||||
для контролируемого обновления.
|
||||
|
||||
## Основные источники
|
||||
|
||||
### OpenWC renderer
|
||||
@@ -17,12 +22,13 @@ Reference-код используется для исследования фор
|
||||
|
||||
### WoWee
|
||||
|
||||
- Исследование обновлено до `master` commit `607ea3b8369851014721416293f8e95dfbe64eec` (2026-09-05), относительно прежнего reviewed pin `8456c236b57140e98667d6d8188f5cd1cc226daf`.
|
||||
- `reference/WoWee/docs/architecture.md` — разделение renderer/network/game/UI/pipeline.
|
||||
- `reference/WoWee/tools/editor/FORMAT_SPEC.md` — open formats, coordinates, collision, packaging и SQL export.
|
||||
- `reference/WoWee/TESTING.md` — единая точка запуска тестов, fixtures, sanitizers и CI discipline.
|
||||
- `reference/WoWee/EXPANSION_GUIDE.md` — protocol/data profile separation.
|
||||
|
||||
Используем: детерминированные authoring formats, headless parity, adapter profiles и validation-first pipeline. Не принимаем автоматически конкретные форматы или заявленную полноту реализации.
|
||||
Особенно полезны: WotLK M2 track/footstep fixtures, liquid mask/surface-grid tests, shader-interface checks, retained widget tree, единый XML→Lua `CreateFrame` path, поэлементный takeover default UI и большая система headless/static FrameXML/Lua audits. Не переносим монолитный Lua/game binding, unknown-API fallback, Vulkan-specific ownership или native-padding `.w*` formats. Particle/render rules, placement rotations и UI semantics требуют независимых build-12340 fixtures; часть real-asset tests WoWee пропускает отсутствие assets как success. Лицензия содержит дополнительный запрет commercial-game use, поэтому WoWee остаётся research-only reference без копирования или вендоринга кода. Полная evaluation card: [`TOOLING_CATALOG.md`](TOOLING_CATALOG.md#wowee--карточка-референса).
|
||||
|
||||
### Noggit Red
|
||||
|
||||
@@ -51,6 +57,14 @@ Reference-код используется для исследования фор
|
||||
|
||||
Не переносим напрямую старый browser stack, WebSocket proxy, React/Three.js abstractions или pipeline server. Заявленное поведение проверяем по TrinityCore/AzerothCore и оригинальному клиенту; proof-of-concept не является спецификацией полноты.
|
||||
|
||||
### Benilla
|
||||
|
||||
- [`reference/benilla`](../reference/benilla) ([upstream](https://github.com/samwhosung/benilla)) — pinned research submodule с независимым клиентом WoW 1.12.1 build 5875 на Rust/Bevy; исследование OpenWC зафиксировано на commit `bc1a2428dd7e00ca8abbfb8f0bf53750dae7b123`.
|
||||
- Renderer references: M2 pose/palette animation, global sequences, material/pass ordering, общий dynamic-effect stream, particles, model particles, ribbons, WMO portal visibility, lighting, sky/weather и streaming.
|
||||
- UI references: отдельный engine-free TOC/FrameXML/Lua core, template/layout/widget model, deterministic event/`OnUpdate` ordering, legacy+modern handler arguments, sandbox, SavedVariables и большой набор compatibility tests.
|
||||
|
||||
Используем decomposition, algorithms, diagnostics и test ideas. Не переносим Bevy/ECS architecture и не считаем Vanilla behavior доказательством WotLK: M2/DBC/layout/API/security differences повторно проверяются по WoW 3.3.5a build 12340. Benilla пока не является oracle для third-party addons или secure/taint semantics; его Lua 5.1 runtime содержит compatibility work для Lua 5.0 target. Полная evaluation card находится в [`TOOLING_CATALOG.md`](TOOLING_CATALOG.md#benilla--карточка-референса).
|
||||
|
||||
### recast-rs
|
||||
|
||||
- [`wowemulation-dev/recast-rs`](https://github.com/wowemulation-dev/recast-rs) — Rust-порт Recast/Detour: navmesh generation, tiled pathfinding, spatial queries, crowd simulation и dynamic obstacles.
|
||||
|
||||
+27
-14
@@ -18,7 +18,20 @@
|
||||
|
||||
Готово, когда render sandbox сохраняет текущее качество, а domain тестируется без scene tree.
|
||||
|
||||
## M1 — Content Project и Editor shell
|
||||
## M1 — Renderer fidelity и graphics foundation
|
||||
|
||||
- Original-client build-12340 corpus: две базовые позиции выбранной локации,
|
||||
settings/time/weather/camera metadata, static и temporal evidence.
|
||||
- Точные color, terrain, M2/WMO material, sky/fog/weather и shadow rules.
|
||||
- MH2O/MCLQ/MLIQ liquids, GPU animation, characters, particles и ribbons.
|
||||
- Раздельные `Blizzlike335` и opt-in `Enhanced/Racing` graphics profiles.
|
||||
- Paired semantic-region comparison, human approval и traversal/GPU budgets.
|
||||
|
||||
Готово, когда утверждённая capture matrix не содержит неклассифицированных
|
||||
визуально различимых gaps, а общие graphics contracts пригодны клиенту, Editor
|
||||
preview и racing fork.
|
||||
|
||||
## M2 — Content Project и Editor shell
|
||||
|
||||
- `addons/openwc_editor` с workspace и docks.
|
||||
- Content Project schema, stable IDs, save/load/migration.
|
||||
@@ -28,7 +41,7 @@
|
||||
|
||||
Готово, когда небольшой synthetic project можно создать, изменить, undo, перезапустить Editor и получить идентичное состояние.
|
||||
|
||||
## M2 — Server inspector и adapters
|
||||
## M3 — Server inspector и adapters
|
||||
|
||||
- TrinityCore/AzerothCore connection profiles.
|
||||
- Schema detection и capabilities.
|
||||
@@ -38,7 +51,7 @@
|
||||
|
||||
Готово, когда одна сущность round-trip проходит оба поддерживаемых adapter profile без молчаливой потери данных.
|
||||
|
||||
## M3 — World Editor MVP
|
||||
## M4 — World Editor MVP
|
||||
|
||||
- Map viewport и coordinate overlays.
|
||||
- Server spawn visualization.
|
||||
@@ -48,7 +61,7 @@
|
||||
|
||||
Готово, когда NPC размещается в Editor и появляется на тестовом core в ожидаемой позиции.
|
||||
|
||||
## M4 — Quest vertical slice
|
||||
## M5 — Quest vertical slice
|
||||
|
||||
- Quest form и chain graph.
|
||||
- Kill/collect/explore objectives, giver/ender, rewards и localization.
|
||||
@@ -57,7 +70,7 @@
|
||||
|
||||
Готово, когда созданный в Editor квест полностью проходится клиентом.
|
||||
|
||||
## M5 — Playable network client
|
||||
## M6 — Playable network client
|
||||
|
||||
- Auth, realm, character selection и world session.
|
||||
- Entity/update fields и world spawn.
|
||||
@@ -66,7 +79,7 @@
|
||||
|
||||
Готово, когда клиент стабильно входит в мир, перемещается и видит синхронизированные entities.
|
||||
|
||||
## M6 — Core gameplay
|
||||
## M7 — Core gameplay
|
||||
|
||||
- Combat/spells/auras/death.
|
||||
- Inventory/equipment/loot/vendors.
|
||||
@@ -76,7 +89,7 @@
|
||||
|
||||
Готово, когда базовый leveling loop проходит без внешнего клиента.
|
||||
|
||||
## M7 — Dungeon authoring
|
||||
## M8 — Dungeon authoring
|
||||
|
||||
- DungeonPackage, encounters, triggers, doors и spawn groups.
|
||||
- SmartAI/script skeleton generation.
|
||||
@@ -85,7 +98,7 @@
|
||||
|
||||
Готово, когда custom dungeon собирается, разворачивается и проходится группой на test core.
|
||||
|
||||
## M8 — Compatibility и completeness
|
||||
## M9 — Compatibility и completeness
|
||||
|
||||
- Feature matrix WoW 3.3.5a.
|
||||
- Addon compatibility tiers.
|
||||
@@ -98,17 +111,17 @@
|
||||
При равной ценности порядок такой:
|
||||
|
||||
1. безопасность данных и воспроизводимость;
|
||||
2. корректность протокола и authoritative state;
|
||||
3. пользовательский vertical slice;
|
||||
4. diagnostics и testability;
|
||||
5. frame pacing;
|
||||
6. визуальная точность и polish.
|
||||
2. original-client renderer fidelity и frame pacing текущего M04;
|
||||
3. корректность протокола и authoritative state;
|
||||
4. пользовательский vertical slice;
|
||||
5. diagnostics и testability;
|
||||
6. opt-in визуальные улучшения после Blizzlike evidence.
|
||||
|
||||
## Не делать раньше времени
|
||||
|
||||
- прямую запись в production DB;
|
||||
- универсальный visual scripting для любой C++ механики;
|
||||
- массовую реализацию Lua API без работающего UI slice;
|
||||
- большой rewrite существующего renderer;
|
||||
- полный custom renderer до bounded shader/backend spike и profiler evidence;
|
||||
- multi-expansion abstraction до устойчивого профиля 3.3.5a;
|
||||
- proprietary asset packaging в репозитории.
|
||||
|
||||
@@ -44,8 +44,8 @@ ID записывается в claim, ветке, PR/MR и handoff. Нельзя
|
||||
```text
|
||||
M01-FND-COORDS-001
|
||||
M03-RND-SCHEDULER-001
|
||||
M08-NET-SRP-001
|
||||
M12-UIA-LUA-SPIKE-001
|
||||
M09-NET-SRP-001
|
||||
M13-UIA-LUA-SPIKE-001
|
||||
```
|
||||
|
||||
Program codes определены в [`../targets/DEVELOPMENT_ROADMAP.md`](../targets/DEVELOPMENT_ROADMAP.md).
|
||||
@@ -214,7 +214,7 @@ Merge order:
|
||||
|
||||
```text
|
||||
fnd(M01): add canonical coordinate mapper
|
||||
net(M08): decode auth challenge safely
|
||||
net(M09): decode auth challenge safely
|
||||
rnd(M03): extract streaming target planner
|
||||
test(M00): add paired checkpoint manifest
|
||||
```
|
||||
|
||||
@@ -54,6 +54,42 @@
|
||||
- dense WMO/M2, water, character equipment и UI scale matrices.
|
||||
- navmesh overlay checkpoints и bake/query budgets для больших tiles/dungeons.
|
||||
|
||||
Для M03 renderer closeout сравнение выполняется на точных M00/M03 revisions с
|
||||
одинаковыми viewport, rendering backend и полным cache inventory. Короткий
|
||||
протокол агрегирует повторные captures медианой каждого показателя; независимый
|
||||
протокол использует увеличенное десятисекундное окно. Регрессия считается
|
||||
воспроизводимой, только если один и тот же checkpoint/pass/metric превышает
|
||||
неизменённый 10% budget в обоих протоколах:
|
||||
|
||||
```powershell
|
||||
tools/compare_render_performance.ps1 -BaselineReport <m00-reports> -CandidateReport <m03-reports> -OutputReport <repeated.json>
|
||||
tools/compare_render_performance.ps1 -BaselineReport <m00-long.json> -CandidateReport <m03-long.json> -OutputReport <long.json>
|
||||
tools/verify_render_performance_stability.ps1 -RepeatedSampleComparison <repeated.json> -LongWindowComparison <long.json> -OutputReport <stability.json>
|
||||
```
|
||||
|
||||
Локальные превышения одного протокола сохраняются как diagnostics; gate падает
|
||||
только на повторяемой регрессии. Полный контракт и схема evidence описаны в
|
||||
[`modules/renderer-closeout-verification.md`](modules/renderer-closeout-verification.md).
|
||||
|
||||
M04 renderer fidelity использует оригинальный клиент build 12340 как
|
||||
authoritative visual oracle. Noggit допускается как дополнительный reference
|
||||
композиции/placements и Editor UX, но не подтверждает lighting, shadows, liquids,
|
||||
materials, animation или effects.
|
||||
|
||||
Reference corpus импортируется через versioned CSV manifest. Для каждой
|
||||
выбранной локации базово снимаются `wide` и `ground` viewpoints; отдельные
|
||||
specialized captures добавляются для уникальных interior, liquid, shadow,
|
||||
character и effect policies. Capture metadata фиксирует WoW/server coordinates,
|
||||
доступные camera fields, time, weather, graphics profile, viewport, artifact
|
||||
name и provenance. Отсутствующее значение хранится как `Unknown`, а не
|
||||
восстанавливается предположением.
|
||||
|
||||
Proprietary screenshots/video остаются вне Git. Repository evidence включает
|
||||
schema, SHA-256, static/temporal metrics, semantic region classification и human
|
||||
approval. Static parity требует согласованных geometry/framing и material/light/
|
||||
shadow/liquid regions; temporal parity отдельно проверяет phase, duration,
|
||||
trajectory, UV motion и emitter lifetime.
|
||||
|
||||
### Navigation compatibility
|
||||
|
||||
- Golden synthetic meshes проверяют slope, climb, radius erosion, holes, tiled seams и off-mesh connections.
|
||||
|
||||
+40
-2
@@ -40,13 +40,15 @@ Decision/ADR:
|
||||
| OpenWC native loaders | ADOPTED | MPQ/BLP/ADT/WDT/M2/WMO | Текущий import/render pipeline | Неполная fidelity форматов |
|
||||
| StormLib | ADOPTED | MPQ | Чтение архивов через native extension | Version/license/update audit |
|
||||
| WowUnreal | REFERENCE | Полный клиент | Coverage, acceptance criteria, networking/UI research | Unreal-specific design |
|
||||
| WoWee | REFERENCE | Клиент/editor/formats | Architecture, editor workflows, tests, open formats | Заявления требуют независимой проверки |
|
||||
| Noggit Red | REFERENCE | World editor | Terrain/placement UX, UID workflows | Не Godot architecture |
|
||||
| WoWee | REFERENCE | WotLK client/render/UI/editor | M2/liquid fixtures, retained FrameXML UI, Lua diagnostics, progressive UI takeover и authoring validation | Modified MIT запрещает commercial-game use; fidelity claims требуют build-12340 oracle |
|
||||
| Original WoW 3.3.5a build 12340 client | ADOPTED | Visual/behavior oracle | Private static/temporal capture corpus, settings and paired comparison | Proprietary artifacts remain outside Git; camera/settings provenance must be explicit |
|
||||
| Noggit Red | REFERENCE | World editor | Terrain/placement UX, UID workflows и secondary render-composition reference | Не Godot architecture; не authoritative lighting/material/shadow/liquid oracle |
|
||||
| open-realm | REFERENCE | Formats/runtime | Независимая проверка parsers/render behavior | Другая архитектура и coverage |
|
||||
| whoa | REFERENCE | Client behavior | 3.3.5a runtime semantics и fixtures | Лицензия и переносимость отдельных решений |
|
||||
| wow.export | REFERENCE | Asset conversion | M2/WMO/material/export edge cases | Web-specific pipeline |
|
||||
| Blender WoW Studio | REFERENCE | Authoring/conversion | WMO/M2/ADT authoring knowledge | Blender-specific UI/data model |
|
||||
| [Wowser](https://github.com/wowserhq/wowser) | REFERENCE | 3.3.5a web client | Auth/realm/character/world protocol, binary parsing, asset pipeline и render research | Старый JS/WebGL proof-of-concept, неполный клиент |
|
||||
| [Benilla](https://github.com/samwhosung/benilla) | REFERENCE | WoW 1.12.1 client/render/UI | M2 GPU animation, materials, particles/ribbons, WMO portals, FrameXML/Lua architecture и compatibility-test ideas | Vanilla build 5875 и Bevy-specific implementation не доказывают WotLK build-12340 fidelity |
|
||||
| [warcraft-rs](https://github.com/wowemulation-dev/warcraft-rs) | CANDIDATE | WoW formats/CLI | Independent MPQ/DBC/BLP/ADT/WDT/WDL/M2/WMO validation и conversion oracle | Rust/tool duplication; claims require fixtures |
|
||||
| [WoWDBDefs](https://github.com/wowdev/WoWDBDefs) | REFERENCE | Client DB schemas | Versioned DBC definitions и typed-code generation input | Definitions still require build-specific validation |
|
||||
| [wow_dbc](https://github.com/gtker/wow_dbc) | REFERENCE | DBC | 1.12/2.4.3/3.3.5 read/write и SQLite conversion ideas | Older release, Rust integration unnecessary by default |
|
||||
@@ -57,6 +59,23 @@ Decision/ADR:
|
||||
| [Keira3](https://github.com/azerothcore/Keira3) | REFERENCE | AzerothCore DB editor | Field semantics, SQL generation и DB editor UX | AGPL; schema-specific web architecture |
|
||||
| [WowBench](https://sourceforge.net/projects/wowbench/) | REFERENCE | WoW UI/API | Offline XML/Lua API emulation и addon test ideas | Старый и неполный implementation |
|
||||
|
||||
## WoWee — карточка референса
|
||||
|
||||
- **Name / URL:** [WoWee](https://github.com/WoWee-Dev/WoWee), локально `reference/WoWee`.
|
||||
- **Status:** `REFERENCE`; код не подключается как dependency и не вендорится.
|
||||
- **Reviewed update:** `master` от `626243e937fb93965fa583a6507ed5a1aa7dda4b` до `8456c236b57140e98667d6d8188f5cd1cc226daf` (2026-08-12): 2557 commits. Raw diff содержит 1622 files, 164940 additions и 1595923 deletions, но объём deletions в основном создают удалённые generated SQL/data/build artifacts; полезный signal сосредоточен в renderer, UI/FrameXML, pipeline и tests.
|
||||
- **Checkout refresh:** текущий upstream checkout — `607ea3b8369851014721416293f8e95dfbe64eec` (2026-09-05). Это обновление pins не является новой fidelity evaluation; релевантные изменения требуют отдельного bounded review перед использованием.
|
||||
- **Renderer findings:** новый общий M2 track sampler отделяет global-sequence time от animation sequence и добавляет finite-value guards, но Hermite/Bezier пока линейно аппроксимируются. Централизованный M2+skin/external-`.anim` loader и selective animation loading полезны как pattern для границы parser/resolver. Новые water-mask и water-surface-grid tests фиксируют LSB-first 8x8 chunk masks, solid fallback, rotated WMO liquid projection, inclusive far edge и degenerate axes. Vertex-layout tests сверяют CPU declarations с shader inputs. GPU lifetime/deferred-release решения полезны концептуально, но Vulkan implementation не переносится в Godot.
|
||||
- **Animation/effects evidence:** WotLK asset tests покрывают color/alpha tracks, независимый wrap global sequences и `$FSD` footstep events. Отсутствующие private assets в части тестов дают `SUCCEED`, поэтому зелёный run не доказывает, что fixture реально исполнялся. Particle/ribbon delta в основном добавляет sampling, batching, descriptor reuse и diagnostics; flame/smoke/ribbon поведение содержит эвристики и не заменяет build-12340 capture oracle. Для архитектуры shared effect stream Benilla остаётся более сильным secondary reference.
|
||||
- **Placement warning:** WoWee прямо оставляет rotation order наклонённых doodad placements нерешённым; имеющийся test различает только upright yaw. OpenWC не меняет calibrated transforms без tilted `MDDF`/`MODF` fixture из build 12340.
|
||||
- **UI/FrameXML findings:** retained widget tree отделён от renderer, сохраняет WoW bottom-left/y-up coordinates до единственного draw-boundary flip и покрывает anchors, draw order, hit testing, visibility, scroll, controls и ownership headless tests. XML компилируется в Lua и проходит тот же `CreateFrame`/template path, что Lua-created widgets. Progressive takeover передаёт отдельные default-UI элементы FrameXML с явными bridges для portrait/model/minimap/world-map content. Это сильная модель поэтапной миграции M05, но не готовая архитектура OpenWC.
|
||||
- **Lua/tooling findings:** runtime ограничивает стандартные библиотеки, ставит instruction-hook timeout, копирует listener list перед dispatch, ограничивает event recursion, нормализует event argument types и диагностирует stack/source/line. Большой headless corpus и статические audits проверяют XML emission, templates, `$parent`, handler arity, event order/arity, missing APIs, nil arithmetic, globals и keybinding takeover. Однако `lua_engine.cpp` монолитен, unknown-API fallback маскирует отсутствующие контракты, а настоящих taint/secure execution semantics нет; OpenWC сохраняет отдельный `LuaRuntime`, view models/intents и fail-closed compatibility tiers.
|
||||
- **Pipeline/editor findings:** общий bounded binary-I/O layer, finite vertex sanitization, DXT block tests, streaming manifest parse и единые validation/save reports полезны как test/validation patterns. Собственные `.w*` formats зависят от native padding/endianness и не подходят как canonical portable OpenWC artifacts без отдельной спецификации.
|
||||
- **License/data risk:** repository LICENSE — MIT с дополнительным запретом использовать software как основу или компонент commercial video games без письменного разрешения; original music имеет отдельный all-rights-reserved notice. Поэтому допустимы research, decomposition и независимо реализованные fixtures; копирование/вендоринг кода требует предварительного legal review.
|
||||
- **Fidelity limits:** проект WotLK-aware и использует build 12340, но остаётся active WIP. В исследованном diff нет систематического paired original-client visual corpus; часть tests повторяет internal implementation, а README перечисляет runtime regressions. WoWee — источник гипотез и test cases, не oracle. Oracle OpenWC остаётся оригинальный клиент 3.3.5a build 12340.
|
||||
- **Bounded adoption:** для M04 приоритетны обязательные (не silent-skip) build-12340 fixtures для M2 global-sequence/color-alpha sampling, MH2O/MCLQ/MLIQ masks и rotated WMO water, CPU-mesh/shader interface verification и tilted placement order. Для M05 — engine-free widget/layout core, единый XML/Lua creation path, progressive takeover и headless audit taxonomy. Любое поведение принимается только после paired capture/fixture с provenance и hash.
|
||||
- **Decision / ADR:** `REFERENCE`, не dependency. ADR нужен, если WoWee-inspired решение меняет публичный renderer/UI/Lua contract, artifact schema, engine boundary или вводит third-party code.
|
||||
|
||||
## rilua — карточка кандидата
|
||||
|
||||
- **Problem solved:** Lua 5.1.1 VM, bytecode, embedding и официальный compatibility corpus для addon runtime.
|
||||
@@ -77,6 +96,25 @@ Decision/ADR:
|
||||
- **Known gaps:** proof-of-concept, неполный gameplay/UI, browser WebSocket proxy вместо native TCP, устаревший JS ecosystem (React 0.14/Three.js 0.77 era), архитектура не переносится напрямую в Godot.
|
||||
- **Decision policy:** использовать для cross-check и test ideas; не добавлять Node/Web dependencies и не копировать browser-specific abstractions в OpenWC.
|
||||
|
||||
## Benilla — карточка референса
|
||||
|
||||
- **Name / URL:** [samwhosung/benilla](https://github.com/samwhosung/benilla), локально `reference/benilla`.
|
||||
- **Status:** `REFERENCE`; исходники доступны как pinned research submodule, но не подключены к build/runtime как dependency и не вендорятся.
|
||||
- **Pinned research revision:** `bc1a2428dd7e00ca8abbfb8f0bf53750dae7b123` (проверен 2026-09-05). Репозиторий публикуется автором как squashed snapshots из private tree, поэтому при повторном исследовании revision фиксируется заново.
|
||||
- **Problem studied:** полный независимый клиент WoW 1.12.1 build 5875 на Rust/Bevy, включая streamed world renderer, M2 animation/materials, WMO portal visibility, liquids/sky/weather, particles/ribbons/spell visuals и собственный TOC/FrameXML/Lua UI runtime.
|
||||
- **Planned boundary:** использовать только как независимый источник алгоритмов, decomposition, diagnostics и test cases. OpenWC сохраняет свои `WorldRenderFacade`, Godot main-thread/finalization boundaries, `LuaRuntime`, immutable UI view models/intents и build-12340 contracts; прямой Bevy/ECS или Rust-type bridge не вводится.
|
||||
- **Useful renderer source map:** `crates/benilla-world/src/model_render*`, `rig_anim*`, `rig_palette*`, `particles*`, `ribbons.rs`, `wmo_portal*`, `lighting*`, `weather*`, `terrain_stream*` и `shaders/wow_effect.wgsl`; format-side M2 animation/particle/ribbon tracks находятся в `crates/benilla-formats/src/models/anim.rs`, `particles.rs` и `ribbons.rs`.
|
||||
- **Useful UI/Lua source map:** engine-free crate `crates/benilla-ui`: `toc.rs`, `framexml.rs`, `loader/*`, `layout.rs`, `widget/*`, `order.rs`, `script/event.rs`, `script/tick.rs`, `script/saved.rs`, sandbox/stdlib bindings и compatibility tests. Особенно полезны сохранение document/load order, bottom-up `OnLoad`, nested-safe restore legacy globals `this/event/argN`, deterministic event/`OnUpdate` order и разделение plain host state от engine adapter.
|
||||
- **License:** `MIT OR Apache-2.0`; конкретное заимствование кода требует сохранения license/attribution и отдельной проверки совместимости с лицензиями OpenWC и third-party dependencies.
|
||||
- **Platforms/toolchain:** stable Rust, Bevy `0.18.1`, `mlua 0.11` с vendored Lua 5.1 и локальным `lua-src` patch pipeline. Это research stack, не предлагаемый OpenWC toolchain.
|
||||
- **Fidelity evidence:** исходники содержат подробные build-5875 byte-law annotations, corpus tests, parser fixtures и temporal/render probes. Сам приватный reverse-engineering corpus не входит в repository, а опубликованный код ориентирован на Vanilla 1.12.1, поэтому ни один его результат сам по себе не является evidence для WoW 3.3.5a build 12340.
|
||||
- **Known gaps:** README прямо ограничивает Lua built-in UI и пока не заявляет third-party addon support; taint/secure execution отсутствуют; runtime использует Lua 5.1 с точечными Lua-5.0 compatibility patches для Vanilla; renderer и scheduling зависят от Bevy render phases/ECS. Layouts, M2 records, shader flags, spell visuals, UI API и security semantics должны повторно проверяться для WotLK.
|
||||
- **Security/data risks:** Lua sandbox удаляет filesystem/OS/package/debug/native reach и принимает только text chunks, но без taint/protected-action модели этого недостаточно для 3.3.5a addons. Proprietary client assets и private RE artifacts не копируются; тесты OpenWC используют легально полученные локальные data и разрешённые metadata/fixtures.
|
||||
- **Performance evidence:** Benilla документирует collapsed rig pose/palette arrays вместо десятков тысяч bone entities и один shared effect vertex/index stream вместо per-emitter dynamic meshes; source comments приводят локальные measurements (включая около 145 mesh changes/frame до shared stream). Эти числа не воспроизведены на Godot и служат только гипотезой для bounded spike.
|
||||
- **Spike and acceptance criteria:** (1) renderer — на build-12340 M2 fixtures сравнить bone palette/global sequences, material ordering, particle plane/sphere/spline emitters, head/tail/model particles и ribbons по phase/duration/lifetime; затем проверить Godot-friendly shared effect buffer/pool без нарушения M03 budgets; (2) UI — прогнать WotLK TOC/FrameXML/Lua corpus на document order, inheritance, anchors, handler calling convention, event order, `OnUpdate`, SavedVariables, restricted libraries и error text; secure actions/taint имеют отдельный обязательный corpus. Любое принятое решение требует original-client paired evidence, p50/p95/p99 и regression fixtures.
|
||||
- **Pinned version/update policy:** исследование и ссылки привязаны к указанному commit. Обновление проводится вручную по diff релевантных directories; moving `main` не становится новым oracle автоматически.
|
||||
- **Decision / ADR:** `REFERENCE`, не dependency. ADR требуется только если Benilla-inspired решение меняет публичный renderer/effects/UI/Lua contract, cache/schema, engine boundary или добавляет dependency.
|
||||
|
||||
## recast-rs — карточка кандидата
|
||||
|
||||
- **Problem solved:** Recast navmesh generation, Detour queries, tiled navigation и dynamic obstacles.
|
||||
|
||||
@@ -21,10 +21,23 @@
|
||||
| M2 placement transform resolver | Implemented | [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md) |
|
||||
| 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 build dispatch planner | Implemented extraction | [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md) |
|
||||
| M2 build resource snapshot | Implemented extraction | [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md) |
|
||||
| M2 static build resource observer | Implemented extraction | [`m2-static-build-resource-observer.md`](m2-static-build-resource-observer.md) |
|
||||
| M2 cached animation resource observer | Implemented extraction | [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.md) |
|
||||
| M2 native animation resource observer | Implemented extraction | [`m2-native-animation-resource-observer.md`](m2-native-animation-resource-observer.md) |
|
||||
| M2 build queue | Implemented extraction | [`m2-build-queue.md`](m2-build-queue.md) |
|
||||
| M2 static batch materializer | Implemented extraction | [`m2-static-batch-materializer.md`](m2-static-batch-materializer.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 playback controller | Implemented extraction | [`m2-animation-playback-controller.md`](m2-animation-playback-controller.md) |
|
||||
| M2 animated instance materializer | Implemented extraction | [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.md) |
|
||||
| M2 animation load pipeline state | Implemented extraction | [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md) |
|
||||
| M2 animation resource finalizer | Implemented extraction | [`m2-animation-resource-finalizer.md`](m2-animation-resource-finalizer.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) |
|
||||
| M2 mesh resource extractor | Implemented extraction | [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md) |
|
||||
| M2 mesh resource finalizer | Implemented extraction | [`m2-mesh-resource-finalizer.md`](m2-mesh-resource-finalizer.md) |
|
||||
| M2 runtime mesh finalizer | Implemented extraction | [`m2-runtime-mesh-finalizer.md`](m2-runtime-mesh-finalizer.md) |
|
||||
| M2 raw model repository | Implemented extraction | [`m2-raw-model-repository.md`](m2-raw-model-repository.md) |
|
||||
| M2 prototype cache state | Implemented extraction | [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md) |
|
||||
@@ -33,10 +46,17 @@
|
||||
| WMO render build step planner | Implemented extraction | [`wmo-render-build-step-planner.md`](wmo-render-build-step-planner.md) |
|
||||
| WMO render build queue | Implemented extraction | [`wmo-render-build-queue.md`](wmo-render-build-queue.md) |
|
||||
| WMO render Resource cache state | Implemented extraction | [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md) |
|
||||
| WMO render Resource finalizer | Implemented extraction | [`wmo-render-resource-finalizer.md`](wmo-render-resource-finalizer.md) |
|
||||
| WMO scene Resource cache state | Implemented extraction | [`wmo-scene-resource-cache-state.md`](wmo-scene-resource-cache-state.md) |
|
||||
| WMO scene Resource finalizer | Implemented extraction | [`wmo-scene-resource-finalizer.md`](wmo-scene-resource-finalizer.md) |
|
||||
| WMO runtime Mesh finalizer | Implemented extraction | [`wmo-runtime-mesh-finalizer.md`](wmo-runtime-mesh-finalizer.md) |
|
||||
| WMO render group materializer | Implemented extraction | [`wmo-render-group-materializer.md`](wmo-render-group-materializer.md) |
|
||||
| WMO runtime scene preparer | Implemented extraction | [`wmo-runtime-scene-preparer.md`](wmo-runtime-scene-preparer.md) |
|
||||
| WMO scene instance factory | Implemented extraction | [`wmo-scene-instance-factory.md`](wmo-scene-instance-factory.md) |
|
||||
| Third-person camera | Implemented | [`third-person-camera.md`](third-person-camera.md) |
|
||||
| Character presentation | Implemented boundary / Partial fidelity | [`character-presentation.md`](character-presentation.md) |
|
||||
| Renderer | Partial | [`world-renderer.md`](world-renderer.md), [`../../RENDER.md`](../../RENDER.md) |
|
||||
| Renderer closeout verification | Implemented | [`renderer-closeout-verification.md`](renderer-closeout-verification.md) |
|
||||
| World entity presentation | Implemented boundary / Prototype visuals | [`world-entity-presentation.md`](world-entity-presentation.md) |
|
||||
| Streaming target planner | Implemented | [`streaming-target-planner.md`](streaming-target-planner.md) |
|
||||
| Render budget scheduler | Implemented | [`render-budget-scheduler.md`](render-budget-scheduler.md) |
|
||||
|
||||
@@ -241,7 +241,7 @@ runtime data migration.
|
||||
| Current starter outfit | Partial | Existing resolver reused unchanged | Needs extracted DBC fixture and client comparison |
|
||||
| Current skin/geoset composition | Partial | Existing components reused | Full equipment/customization fidelity incomplete |
|
||||
| Build-12340 animation semantics | Planned | No original-client fixture | Capture animation IDs/transitions/timing |
|
||||
| Runtime equipment/network updates | Planned | No snapshot contract | M08/M09/M12 work |
|
||||
| Runtime equipment/network updates | Planned | No snapshot contract | M09/M10/M13 work |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ separate versioned movement snapshot contract.
|
||||
|
||||
- Terrain/collision policy can consume displacement without changing input or velocity ownership.
|
||||
- A future server-aware predictor may replace this controller behind the scene composition boundary.
|
||||
- A typed movement snapshot may expose deterministic replay state when M08/M09 require it.
|
||||
- A typed movement snapshot may expose deterministic replay state when M09/M10 require it.
|
||||
- A future application profile can map to this narrow capability value without
|
||||
coupling the movement controller to the application shell.
|
||||
|
||||
@@ -252,8 +252,8 @@ separate versioned movement snapshot contract.
|
||||
| Typed sprint/free-flight exclusion | Implemented | Pure and real-scene profile regressions | Application shell must select profile explicitly later |
|
||||
| Terrain height query | Implemented | Typed `TerrainQuery` and injected player regression | Ground-snap policy remains scene-owned |
|
||||
| Terrain collision movement policy | Planned | Height-only query does not model collision | Add slopes/holes/collision later |
|
||||
| Jump/fall/swim | Planned | M02/M09 roadmap | Requires terrain/liquid and server contracts |
|
||||
| Prediction/reconciliation | Planned | M08/M09 roadmap | Requires movement snapshot/network contract |
|
||||
| Jump/fall/swim | Planned | M02/M10 roadmap | Requires terrain/liquid and server contracts |
|
||||
| Prediction/reconciliation | Planned | M09/M10 roadmap | Requires movement snapshot/network contract |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# M2 Animated Instance Materializer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001` |
|
||||
| Owners | Animated M2 duplicate/batch construction, render settings, playback startup and attachment |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animated-instance-materializer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Imported GLB and native experimental animated M2 instances |
|
||||
|
||||
## Purpose
|
||||
|
||||
Materialize one ordered animated M2 build batch on the main thread. The service
|
||||
duplicates an accepted prototype, preserves historical names and transforms,
|
||||
applies visibility/shadow settings recursively, starts native/imported playback,
|
||||
attaches a non-empty batch and returns detached native diagnostic entries.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Plan build batches, advance job cursors or consume render budgets.
|
||||
- Load, finalize, select or cache animated prototypes.
|
||||
- Select animations or implement native animator deformation.
|
||||
- Format logs, normalize paths, assign Editor owners or clean up tile roots.
|
||||
- Materialize static M2 `MultiMesh` batches.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader adapter] --> Materializer[M2AnimatedInstanceMaterializer]
|
||||
Prototype[Accepted animated prototype] --> Materializer
|
||||
Plan[Transforms and batch slice] --> Materializer
|
||||
Materializer --> Playback[M2AnimationPlaybackController]
|
||||
Materializer --> Finalizer[M2AnimatedSceneFinalizer traversal]
|
||||
Materializer --> Batch[Attached animated batch]
|
||||
Materializer --> Diagnostics[Detached diagnostic entries]
|
||||
```
|
||||
|
||||
Allowed dependencies are Godot Node/Node3D/GeometryInstance3D APIs and the
|
||||
accepted animated finalizer and playback services. Resource loading, files,
|
||||
workers, caches, scheduler policy, Editor ownership and `MultiMesh` are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `materialize_batch(parent, path, prototype, transforms, start, count, serial, visibility_end, visibility_margin, cast_shadows, native_script, collect_diagnostics)` | Command/query | Build, start and attach one ordered animated batch | Main thread; borrowed inputs, parent owns result | Invalid/empty input or all failed duplicates returns `{}` |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | M2 parent root | Loader build job | Batch attachment | Borrowed Node3D | Tile/job lifetime |
|
||||
| Input | Relative path, start/count and serial | Batch planner/loader adapter | Names, phase and slice | Copied scalars | One call |
|
||||
| Input | Accepted animated prototype | Prototype cache | Duplicate source | Borrowed Node3D | Cache-owned |
|
||||
| Input | Ordered transforms | Placement grouping/build job | Instance transforms | Borrowed Array | Job lifetime |
|
||||
| Input | Visibility/shadow settings | Renderer configuration | Recursive render mutation | Copied scalars | One call |
|
||||
| Input | Native animator Script identity | Loader composition | Playback controller | Borrowed Script | Application lifetime |
|
||||
| Output | `batch_root` | Materializer | Parent SceneTree and loader Editor-owner adapter | Parent-owned Node3D | Tile lifetime |
|
||||
| Output | `native_diagnostics` entries | Playback controller/materializer | Loader log adapter | Caller-owned Array/Dictionaries | Debug call |
|
||||
|
||||
Side effects are Node duplication, instance naming/transform assignment,
|
||||
recursive geometry mutation, playback mutation and SceneTree attachment. The
|
||||
service retains no batch, prototype, transform or diagnostic references.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Slice[Ordered transform slice] --> Duplicate[Duplicate prototype with signals, groups and scripts]
|
||||
Duplicate --> Identity[Assign historical name and transform]
|
||||
Identity --> Render[Apply visibility margin and shadow recursively]
|
||||
Render --> NativeCopy[Copy native animator runtime fields]
|
||||
NativeCopy --> AttachInstance[Attach duplicate to detached batch]
|
||||
AttachInstance --> Players[Inventory AnimationPlayers]
|
||||
Players --> Start[Start deterministic native/imported playback]
|
||||
Start --> More{More slice entries?}
|
||||
More -->|yes| Duplicate
|
||||
More -->|no, non-empty| AttachBatch[Attach batch to supplied parent]
|
||||
Start --> Diagnostics[Tag detached states with instance index]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Validating
|
||||
Validating --> Empty: invalid input
|
||||
Validating --> Building: valid slice
|
||||
Building --> Building: next successful duplicate
|
||||
Building --> Empty: no successful duplicates
|
||||
Building --> Attached: non-empty batch
|
||||
Empty --> [*]
|
||||
Attached --> [*]: parent/tile owner releases subtree
|
||||
```
|
||||
|
||||
The `RefCounted` service is stateless; lifecycle labels describe each batch call.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant M as M2AnimatedInstanceMaterializer
|
||||
participant F as M2AnimatedSceneFinalizer
|
||||
participant P as M2AnimationPlaybackController
|
||||
participant R as M2 parent root
|
||||
L->>M: materialize_batch(slice, prototype, render settings)
|
||||
loop ordered instance
|
||||
M->>M: duplicate, name, transform, render settings
|
||||
M->>P: copy native animator fields
|
||||
M->>F: animation_players_in_subtree(duplicate)
|
||||
M->>P: start_instance_playback(path, index, players, debug)
|
||||
end
|
||||
M->>R: add_child(non-empty batch)
|
||||
M-->>L: batch root and indexed diagnostics
|
||||
L->>L: format logs and assign Editor owner
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Materializer[M2AnimatedInstanceMaterializer]
|
||||
Materializer --> Playback[M2AnimationPlaybackController]
|
||||
Materializer --> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Materializer --> Engine[Node3D / GeometryInstance3D / SceneTree APIs]
|
||||
Loader --> EditorOwner[Editor ownership adapter]
|
||||
Loader --> BuildJob[Build cursor and budget]
|
||||
Materializer -. no dependency .-> Cache[Prototype/cache/load state]
|
||||
Materializer -. no dependency .-> MultiMesh[Static M2 materialization]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Loader/build state borrows the accepted prototype and ordered transform Array.
|
||||
- Each successful duplicate becomes a child of the detached batch immediately.
|
||||
- A non-empty batch transfers to the supplied parent; the parent owns its lifetime.
|
||||
- A batch with no successful duplicates is queued for deletion and no reference returns.
|
||||
- Playback/finalizer services borrow each duplicate only during synchronous calls.
|
||||
- Returned diagnostic entries contain the stable instance index and a detached state.
|
||||
- All Node duplication, mutation and attachment is main-thread-only.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null parent/prototype | Entry guard | Return `{}` without mutation | Contract verifier | Correct composition/cache state |
|
||||
| Empty transforms or non-positive count | Entry guard | Return `{}` | Contract verifier | Planner supplies a non-empty slice |
|
||||
| Duplicate is not Node3D | Cast result | Skip that entry and preserve order of successes | Child-count contract | Repair prototype scene root |
|
||||
| Every duplicate fails | Empty batch check | Queue empty batch, return `{}` | Empty-result contract | Rebuild/import prototype |
|
||||
| Shutdown during/after call | Outside service | Call is synchronous; loader owns cancellation and subtree release | Shutdown regression | Loader cancels job/releases parent |
|
||||
|
||||
Transform bounds remain a planner/build-job precondition exactly as before the
|
||||
extraction; this service does not clamp or silently reorder an invalid slice.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Duplicate flags | Signals + groups + scripts | All | No | Preserves historical instance behavior |
|
||||
| Visibility end/margin | Loader renderer settings / chunk size | All | Yes, between calls | Applied to every GeometryInstance3D descendant |
|
||||
| Shadow mode | Loader `m2_cast_shadows` | All | Yes, between calls | Enables/disables descendant shadow casting |
|
||||
| Native diagnostics | Loader `debug_streaming` | Debug | Yes | Collects indexed state only when requested |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No data is serialized and no cache/schema version changes. Editor owner
|
||||
assignment remains a loader concern after attachment, so generated-scene
|
||||
persistence behavior and migration remain unchanged.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- The materializer emits no logs, metrics or debug views.
|
||||
- Native state remains detached and is tagged with the absolute instance index.
|
||||
- Loader preserves the existing normalized-path `M2_NATIVE_ANIMATOR` log text.
|
||||
- No correlation ID is introduced; path/index remains the existing identity.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_animated_instance_materializer.gd` covers invalid input, names,
|
||||
order, transforms, recursive visibility/margin/shadows, playback, native field
|
||||
references, indexed diagnostics, prototype immutability and ownership boundaries.
|
||||
- Playback/finalizer/pipeline/build/prototype/shutdown/material/facade/internal-
|
||||
access regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact extraction of existing flags/order/settings/calls;
|
||||
no original-client visual comparison or proprietary asset result is claimed.
|
||||
- Performance budget: 1,000 synthetic Node3D instances under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
A later facade-owned animated presentation command may supply the same explicit
|
||||
batch contract. Async SceneTree mutation, callbacks and a generic scene factory
|
||||
are intentionally excluded until measurements show a need.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Ordered animated instance/batch materialization | Implemented extraction | Synthetic batch contract verifier | Proprietary asset traversal pending |
|
||||
| Recursive visibility/shadow settings | Implemented extraction | Nested geometry fixture | Visual distance/shadow comparison pending |
|
||||
| Native/imported playback startup | Delegated to implemented controller | Playback + materializer verifiers | Full WoW animation-state mapping pending |
|
||||
| Editor persistence | Existing loader-owned | Source boundary and editor helper | Editor integration fixture remains separate |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- The existing build planner must continue to guarantee valid transform bounds.
|
||||
- Default animation selection and native deformation retain their documented gaps.
|
||||
- No private asset, paired original-client, descriptor-pressure, leak or p95/p99 run exists.
|
||||
- Recursive render mutation remains proportional to duplicate subtree size.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_animated_instance_materializer.gd` | Duplicate, render mutation, playback startup and non-empty batch attachment |
|
||||
| `src/render/m2/m2_animation_playback_controller.gd` | Phase, selection and native/imported playback mutation |
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | AnimationPlayer inventory for each duplicate |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Build adapter, diagnostic formatting and Editor ownership |
|
||||
| `src/tools/verify_m2_animated_instance_materializer.gd` | Batch/render/playback/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-animation-playback-controller.md`](m2-animation-playback-controller.md)
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.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)
|
||||
@@ -0,0 +1,212 @@
|
||||
# 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
|
||||
ResourceFinalizer[M2AnimationResourceFinalizer] -->|loaded Resource and material source| Finalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer -->|accepted Node3D and player count| ResourceFinalizer
|
||||
ResourceFinalizer --> 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 | Animation resource finalizer | 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 | Resource-finalizer 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 R as M2AnimationResourceFinalizer
|
||||
participant F as M2AnimatedSceneFinalizer
|
||||
participant C as M2PrototypeCacheState
|
||||
R->>F: instantiate_candidate(Resource)
|
||||
F-->>R: detached Node3D or null
|
||||
R->>F: repair_materials(candidate, source)
|
||||
R->>F: finalize_candidate(candidate)
|
||||
alt accepted
|
||||
F-->>R: exact Node3D and player count
|
||||
R->>C: adopt animated prototype
|
||||
else rejected
|
||||
F-->>R: empty; candidate freed
|
||||
R->>C: mark animation static
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
ResourceFinalizer[M2AnimationResourceFinalizer] --> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer --> Engine[PackedScene / Node3D / Mesh / Material / AnimationPlayer]
|
||||
ResourceFinalizer --> Resource[ResourceLoader]
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
ResourceFinalizer --> 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 resource-finalizer/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 | Resource finalizer 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 | Resource finalizer 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`; accepted instance/batch materialization is owned
|
||||
by `M2AnimatedInstanceMaterializer`.
|
||||
|
||||
## 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_animated_instance_materializer.gd` | Per-duplicate player inventory consumer and batch owner |
|
||||
| `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/render/m2/m2_animation_resource_finalizer.gd` | I/O, candidate composition, adoption and logs |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Permits and material-source lookup |
|
||||
| `src/tools/verify_m2_animated_scene_finalizer.gd` | Scene/material/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-animation-playback-controller.md`](m2-animation-playback-controller.md)
|
||||
- [`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)
|
||||
@@ -0,0 +1,231 @@
|
||||
# M2 Animation Load Pipeline State
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-ANIMATION-LOAD-PIPELINE-001` |
|
||||
| Owners | Animated M2 threaded request records and terminal finalize FIFO |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animation-load-pipeline`, 2026-07-17 |
|
||||
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
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; cached animation eligibility and request admission
|
||||
belong to `M2CachedAnimationResourceObserver`; terminal I/O/outcomes belong to
|
||||
`M2AnimationResourceFinalizer`, while validation and retained Node lifetime
|
||||
belong to `M2AnimatedSceneFinalizer` and `M2PrototypeCacheState`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Select cache paths, interpret ResourceLoader statuses or perform I/O.
|
||||
- Decide animated/static fallback or own prototype Nodes.
|
||||
- Instantiate PackedScenes, repair materials or consume render permits.
|
||||
- Merge the distinct animated and static-Mesh pipelines.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Observer[M2CachedAnimationResourceObserver] --> IO[ResourceLoader request]
|
||||
Observer --> State[M2AnimationLoadPipelineState]
|
||||
Finalizer[M2AnimationResourceFinalizer] --> IO
|
||||
Finalizer --> State
|
||||
State -->|detached pending records| Finalizer
|
||||
Finalizer -->|opaque terminal status| State
|
||||
State -->|completion FIFO| Finalizer
|
||||
Loader[StreamingWorldLoader] --> Finalizer
|
||||
Loader --> Budget[M2_ANIMATION_FINALIZE permit]
|
||||
Loader --> Prototype[M2PrototypeCacheState]
|
||||
```
|
||||
|
||||
Allowed dependencies are value containers, Strings and opaque integer statuses.
|
||||
ResourceLoader, workers, scheduler, Node/Resource/Mesh ownership and other
|
||||
renderer services are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `remember_request(normalized_relative_path, resource_path)` | Command/query | Insert one pending request | Renderer main thread; until transition/clear | Empty/duplicate false |
|
||||
| `has_request(path)` | Query | Pending-request dedupe | Main thread | Empty/unknown false |
|
||||
| `request_records_snapshot()` | Query | Detached pending records in insertion order | Main thread; caller-owned | None |
|
||||
| `complete_request(path, terminal_status)` | Command/query | Move copied record into completion FIFO | Main thread after poll | Unknown false |
|
||||
| `discard_request(path)` | Command/query | Remove without finalization | Main thread | Unknown false |
|
||||
| `has_finalize_record()` / `pop_finalize_record()` | Query/command | Drain completion-order FIFO | Main-thread budget drain | Empty pop returns `{}` |
|
||||
| `total_work_count()` | Query | Pending plus finalize metric | Main thread | None |
|
||||
| `pending_request_count()` / `finalize_record_count()` | Query | Stage diagnostics | Main thread | None |
|
||||
| `clear()` | Command | Drop bookkeeping after caller I/O drain/reset | Main thread | Idempotent; no I/O drain |
|
||||
| `diagnostic_snapshot()` | Query | Detached paths/status records | Main thread | No Resources exposed |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Normalized M2 path and GLB Resource path | Loader request adapter | Pending map | Copied Strings | Until completion/discard/clear |
|
||||
| Input | Opaque terminal status | Resource finalizer | Finalize FIFO | Integer value | Until pop/clear |
|
||||
| Output | Detached pending records | State | Resource finalizer/shutdown adapter | Caller-owned copies | One pass |
|
||||
| Output | Oldest completion record | State | Resource finalizer | Transferred Dictionary | One finalize attempt |
|
||||
| Output | Detached diagnostics | State | Verifier/future metrics | Caller-owned copies | Snapshot lifetime |
|
||||
|
||||
Side effects are limited to collection mutation and retaining String/integer values.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Successful threaded request] --> Remember[Remember request]
|
||||
Remember --> Poll[Resource finalizer polls detached snapshot]
|
||||
Poll --> Terminal{Loaded or failed?}
|
||||
Terminal -->|no| Poll
|
||||
Terminal -->|yes| Complete[Complete with opaque status]
|
||||
Complete --> FIFO[Finalize FIFO]
|
||||
FIFO --> Permit{Permit available?}
|
||||
Permit -->|no| FIFO
|
||||
Permit -->|yes| Pop[Pop oldest record]
|
||||
Pop --> Finalize[Resource finalizer loads/instantiates or marks static]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Absent
|
||||
Absent --> Pending: remember
|
||||
Pending --> TerminalQueued: complete
|
||||
Pending --> Absent: discard or clear
|
||||
TerminalQueued --> Absent: pop or clear
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant O as CachedAnimationObserver
|
||||
participant L as StreamingWorldLoader
|
||||
participant F as AnimationResourceFinalizer
|
||||
participant R as ResourceLoader
|
||||
participant S as M2AnimationLoadPipelineState
|
||||
participant P as M2PrototypeCacheState
|
||||
O->>R: load_threaded_request(GLB)
|
||||
O->>S: remember_request(path, GLB)
|
||||
loop frames
|
||||
L->>F: poll terminal requests
|
||||
F->>S: request_records_snapshot()
|
||||
F->>R: load_threaded_get_status(GLB)
|
||||
end
|
||||
F->>S: complete_request(path, status)
|
||||
L->>F: prepare after permit
|
||||
F->>S: pop_finalize_record()
|
||||
F->>R: load_threaded_get(GLB)
|
||||
F->>P: adopt animated prototype or mark static
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Main thread serializes all mutation.
|
||||
- State owns only request/finalize Dictionaries with copied paths and statuses.
|
||||
- Loader drains pending ResourceLoader paths before orderly shutdown clear.
|
||||
- Resource finalizer owns terminal I/O/outcomes and composes scene validation;
|
||||
prototype state owns accepted detached Nodes and static-only outcomes.
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Observer[M2CachedAnimationResourceObserver] --> State[M2AnimationLoadPipelineState]
|
||||
Observer --> Resource[ResourceLoader request]
|
||||
Finalizer[M2AnimationResourceFinalizer] --> State[M2AnimationLoadPipelineState]
|
||||
Finalizer --> Resource
|
||||
Loader[StreamingWorldLoader] --> Finalizer
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Loader --> Prototype[M2PrototypeCacheState]
|
||||
State -. no dependency .-> Resource
|
||||
State -. no dependency .-> Budget
|
||||
State -. no dependency .-> Prototype
|
||||
```
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty/duplicate request | State guard | Reject unchanged | Contract verifier | Correct caller or request later |
|
||||
| Request start/cache miss | Cached observer | No insertion; mark static | Observer contract | Cache correction/reload |
|
||||
| Non-terminal status | Loader | Keep pending | Existing metric | Poll next frame |
|
||||
| Failed terminal load | Popped status | Loader marks static | Existing behavior | Future map/session reload |
|
||||
| Empty defensive path | Loader poll | Discard and mark static | Source contract | Correct producer |
|
||||
| Shutdown | Loader drains pending paths | Clear state | Source/shutdown regressions | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| `enable_m2_animated_instances` | `true` | Existing renderer profile | Yes | Enables caller request path |
|
||||
| `m2_animation_finalize_ops_per_tick` | `1` | Quality/custom | Yes | Bounds caller FIFO drain |
|
||||
| Animated allow/deny/primitive rules | Existing values | Existing renderer profile | Yes | Observer filters before state insertion |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
State is not serialized. Cache formats and prototype lifetimes are unchanged;
|
||||
no rebake or migration is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- `total_work_count()` preserves all three historical `m2_animation` metrics.
|
||||
- Snapshots expose only paths and opaque statuses, never Resources or Nodes.
|
||||
- Normalized M2 path is the correlation key; existing loader logs are unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_animation_load_pipeline_state.gd` covers validation, dedupe,
|
||||
insertion order, completion FIFO, opaque status, discard, detached snapshots,
|
||||
source boundaries and 100-by-256 timing.
|
||||
- Adjacent renderer, scheduler, prototype and shutdown regressions cover callers.
|
||||
- Fidelity evidence is exact state/lifecycle extraction; no visual 3.3.5a parity
|
||||
or proprietary asset-backed claim is made.
|
||||
- Performance budget: 25,600 request/complete/pop transitions under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
ResourceLoader polling/finalization now belongs to a sibling service without
|
||||
changing this value-only state contract.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Animated request/finalize state | Implemented extraction | Synthetic contract/source/timing verifier | Asset-backed traversal/leak/p95/p99 pending |
|
||||
| Cached request admission and GLB selection | Implemented in observer | Policy/GLB/source verifier | Asset-backed traversal pending |
|
||||
| Terminal ResourceLoader polling | Implemented finalizer extraction | Status/order/source regressions | Asset-backed traversal pending |
|
||||
| Animated prototype outcomes | Implemented extraction | Prototype cache verifier | Asset-backed animation fidelity pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Dictionary records preserve the existing dynamic ResourceLoader boundary.
|
||||
- `clear()` does not drain I/O; caller ordering remains mandatory.
|
||||
- No private asset traversal, leak/descriptor-pressure or paired-client run is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending records, completion FIFO and metrics |
|
||||
| `src/render/m2/m2_animation_resource_finalizer.gd` | Terminal polling, Resource load and prototype outcome |
|
||||
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Eligibility, GLB selection and request admission |
|
||||
| `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` | Permit loop, material lookup and composition |
|
||||
| `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-animation-resource-finalizer.md`](m2-animation-resource-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)
|
||||
- [`../../RENDER.md`](../../RENDER.md)
|
||||
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|
||||
@@ -0,0 +1,221 @@
|
||||
# M2 Animation Playback Controller
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-ANIMATION-PLAYBACK-001` |
|
||||
| Owners | Per-instance AnimationPlayer/native animator playback mutation |
|
||||
| Last verified | Worktree `work/sindo-main-codex-m03-integrator/m03-closeout`, 2026-08-02 |
|
||||
| Profiles/capabilities | Imported GLB and native experimental animated M2 instances |
|
||||
|
||||
## Purpose
|
||||
|
||||
Apply deterministic playback to one already duplicated M2 instance: copy native
|
||||
animator runtime fields, derive a stable phase, prepare/phase native animators,
|
||||
select an imported animation and configure linear looping/playback/seek.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Duplicate, name, transform or attach instances/batch roots.
|
||||
- Apply visibility, shadows or Editor ownership.
|
||||
- Load/finalize/cache animated scenes or decide eligibility.
|
||||
- Change animation priorities, phase formula or native animator implementation.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Prototype[Animated prototype] --> Materializer[M2AnimatedInstanceMaterializer]
|
||||
Materializer -->|source and duplicate| Playback[M2AnimationPlaybackController]
|
||||
Finalizer[M2AnimatedSceneFinalizer player inventory] --> Playback
|
||||
Playback --> Native[M2NativeAnimator mutation]
|
||||
Playback --> Imported[AnimationPlayer mutation]
|
||||
```
|
||||
|
||||
Allowed dependencies are supplied Nodes, Script identity, AnimationPlayer and
|
||||
Animation resources. ResourceLoader, files, workers, caches, scheduler,
|
||||
MultiMesh, SceneTree attachment and application layers are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `copy_native_animator_data(source_root, target_root, script)` | Command | Copy five runtime fields in depth-first pair order | Main thread; borrowed nodes | Null/script mismatch yields no pairs |
|
||||
| `start_instance_playback(root, path, index, script, players, collect_diagnostics)` | Command/query | Start native/imported playback and optionally return native state | Main thread; one instance | Null/empty inventories no-op |
|
||||
| `native_animators_in_subtree(root, script)` | Query | Exact-script depth-first inventory | Main thread; borrowed nodes | Null root/script returns empty |
|
||||
| `phase_for_instance(path, index)` | Pure query | Stable phase in `[0, 1)` | Any thread; scalar | Historical hash behavior retained |
|
||||
| `choose_default_animation(player, path)` | Pure engine query | Apply exact ordinary/fish/bird/fallback priority | Main thread; borrowed player | Null/no animations returns empty |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Prototype and duplicate subtrees | M2 animated instance materializer | Native field copy | Borrowed Nodes | One duplicate |
|
||||
| Input | Exact native animator Script | Loader composition | Native inventory | Borrowed Script | Call-local |
|
||||
| Input | Relative path and instance index | Build batch adapter | Phase/selection | Copied values | One start |
|
||||
| Input | AnimationPlayer inventory | Animated scene finalizer traversal | Imported playback | Borrowed players | One start |
|
||||
| Output | Mutated native/imported playback | Controller | Rendered instance | Nodes retain state/resources | Instance lifetime |
|
||||
| Output | Detached native diagnostic records | Controller | Loader log adapter | Caller-owned Dictionaries | Debug call |
|
||||
|
||||
Side effects are native field assignment, phased preparation calls, animation loop
|
||||
mutation, play and seek. The service retains no inputs.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Identity[Path and index] --> Phase[Stable hash phase]
|
||||
NativeInventory[Exact-script native inventory] --> Prepare[prepare local runtime mesh]
|
||||
Phase --> Prepare
|
||||
Players[AnimationPlayers] --> Select[Choose path-specific default]
|
||||
Select --> Loop[Set every animation LOOP_LINEAR]
|
||||
Loop --> Play[Play selected name]
|
||||
Phase --> Seek[Seek positive-length selection]
|
||||
Prepare --> Diagnostics{Debug requested?}
|
||||
Diagnostics -->|yes| Snapshot[Detached runtime state]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Unstarted
|
||||
Unstarted --> Prepared: copy native data and start
|
||||
Prepared --> Playing: native phase and/or AnimationPlayer play
|
||||
Playing --> Playing: repeated deterministic start
|
||||
Playing --> [*]: instance owner releases subtree
|
||||
```
|
||||
|
||||
The controller itself is stateless; lifecycle labels describe borrowed instance state.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant M as M2AnimatedInstanceMaterializer
|
||||
participant F as M2AnimatedSceneFinalizer
|
||||
participant P as M2AnimationPlaybackController
|
||||
participant N as M2NativeAnimator
|
||||
participant A as AnimationPlayer
|
||||
M->>P: copy_native_animator_data(prototype, duplicate, script)
|
||||
M->>F: animation_players_in_subtree(duplicate)
|
||||
F-->>M: ordered players
|
||||
M->>P: start_instance_playback(path, index, players, debug)
|
||||
P->>N: prepare_runtime_at_phase
|
||||
P->>A: choose, loop, play and seek
|
||||
P-->>M: optional detached native diagnostics
|
||||
M-->>M: tag states with instance index
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Materializer[M2AnimatedInstanceMaterializer]
|
||||
Materializer --> Playback[M2AnimationPlaybackController]
|
||||
Materializer --> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Playback --> Engine[Node / Script / AnimationPlayer / Animation]
|
||||
Materializer --> Batch[SceneTree batch materialization]
|
||||
Loader --> NativeScript[M2NativeAnimator script]
|
||||
Playback -. no dependency .-> ResourceLoader
|
||||
Playback -. no dependency .-> Cache[M2PrototypeCacheState]
|
||||
Playback -. no dependency .-> Scheduler[RenderBudgetScheduler]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Materializer owns prototype duplication, instance/batch roots and SceneTree attachment.
|
||||
- Materializer uses finalizer traversal for borrowed AnimationPlayer references.
|
||||
- Controller borrows Nodes/Script/players only for the synchronous call.
|
||||
- Native arrays are assigned by reference exactly as before extraction.
|
||||
- Diagnostic Dictionaries are deep-duplicated before return.
|
||||
- Main thread performs all engine-object mutation; pure phase math is thread-safe.
|
||||
- A duplicated native animator resolves and duplicates its local Mesh, applies
|
||||
phase and deforms once before attachment. Its later `_ready()` is idempotent.
|
||||
- Preparation allocates an empty instance-local ArrayMesh because deformation
|
||||
immediately rebuilds every surface from retained native arrays. Captured
|
||||
immutable Material resources remain shared and are reapplied.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Missing roots/script | Inventory guard | No native copy/start | Contract fixture | Correct composition |
|
||||
| Fewer target animators | Pair-count minimum | Copy available pairs only | Native copy fixture | Rebuild import/prototype |
|
||||
| No matching name | Ordered fallback | Substring, first name, then empty | Selection fixtures | Add compatible animation |
|
||||
| Empty animation selection | Empty result | Skip loop/play/seek for that player | Selection fixture | Static/native path continues |
|
||||
| Zero-length selected animation | Length guard | Play without seek | Playback contract | Imported timing remains engine-owned |
|
||||
| Cancellation/shutdown | Outside service | No retained state | N/A | Loader owns subtree release |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Phase buckets | 1000 | All | No | Stable per path/index desynchronization |
|
||||
| Loop mode | `LOOP_LINEAR` | All | No | Every available imported animation loops |
|
||||
| Native diagnostics | `debug_streaming` | Debug | Yes | Samples state only when requested |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No state or format is serialized. GLB/native cache formats, prototype lifetime
|
||||
and material versions are unchanged; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Optional native states remain detached and contain existing prepared,
|
||||
processing, mesh, bone, surface and length fields.
|
||||
- Loader retains `M2_NATIVE_ANIMATOR` log formatting and path normalization.
|
||||
- Controller emits no logs or metrics.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_animation_playback_controller.gd` covers exact phase, ordinary/
|
||||
fish/bird priorities, substring/first fallback, loop/play/seek, native exact-
|
||||
script order, five-field copy, single-rebuild phased preparation, idempotent
|
||||
ready, detached diagnostics and boundaries.
|
||||
- Finalizer/build/prototype/material/shutdown regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact policy/mutation extraction; no private asset or
|
||||
original-client animation comparison is claimed.
|
||||
- Performance budget: 20,000 phase-and-selection pairs under one second.
|
||||
|
||||
## Extension points
|
||||
|
||||
Server-driven animation state may later replace default selection through a
|
||||
separate presentation contract; this fallback controller must remain available
|
||||
for world doodads and compatibility fixtures.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Imported default selection/playback | Implemented extraction | Synthetic priority/play/seek verifier | Asset-backed animation-name corpus pending |
|
||||
| Native data/phase startup | Implemented extraction | Exact-reference/native state verifier | Native visual fidelity remains experimental |
|
||||
| Instance/batch materialization | Implemented extraction | Materializer contract verifier | Asset-backed traversal pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Hash phase intentionally depends on existing Godot String hashing behavior.
|
||||
- Default-name heuristics are not a complete WoW animation-state mapping.
|
||||
- Native CPU deformation remains proportional to vertex count and is unsuitable
|
||||
for large numbers of independently animated instances without a future GPU path.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_animation_playback_controller.gd` | Phase, selection, native copy/start and imported playback |
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | Accepted prototype/player inventory |
|
||||
| `src/scenes/streaming/m2_native_animator.gd` | Experimental native deformation runtime |
|
||||
| `src/render/m2/m2_animated_instance_materializer.gd` | Instance duplication/attachment and playback composition |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Build adapter, diagnostic formatting and Editor ownership |
|
||||
| `src/tools/verify_m2_animation_playback_controller.gd` | Policy/mutation/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.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)
|
||||
@@ -0,0 +1,249 @@
|
||||
# M2 Animation Resource Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-ANIMATION-RESOURCE-FINALIZER-001` |
|
||||
| Owners | Cached animated Resource polling, terminal load and prototype outcome |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animation-resource-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Drain cached animated M2 ResourceLoader work without keeping terminal I/O and
|
||||
prototype outcome logic in `StreamingWorldLoader`. The service polls pending
|
||||
requests, prepares a detached scene candidate and completes material repair,
|
||||
validation, cache adoption or static-only fallback.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Select cached GLB paths, apply allow/deny policy or admit threaded requests.
|
||||
- Select/build native animated M2 prototypes.
|
||||
- Look up the static material prototype used for repair.
|
||||
- Own render permits, build jobs, playback, instances or SceneTree roots.
|
||||
- Change ResourceLoader ordering, cache formats, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Observer[M2CachedAnimationResourceObserver] --> Pipeline[M2AnimationLoadPipelineState]
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2AnimationResourceFinalizer]
|
||||
Pipeline --> Finalizer
|
||||
ResourceLoader --> Finalizer
|
||||
Finalizer --> SceneFinalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer --> Cache[M2PrototypeCacheState]
|
||||
Loader --> Material[Material prototype lookup]
|
||||
Material --> Finalizer
|
||||
```
|
||||
|
||||
The two-phase API preserves the historical ordering: a loaded Resource must
|
||||
instantiate a candidate before the loader performs material-prototype lookup.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `poll_terminal_requests(pipeline, cache)` | I/O command | Move LOADED/FAILED requests to completion FIFO | Invalid composition returns zero |
|
||||
| `prepare_next_candidate(pipeline, cache)` | I/O command/query | Pop one record, load Resource and instantiate candidate | Skip/failed/invalid outcome returns empty Dictionary |
|
||||
| `finalize_prepared_candidate(preparation, material_source, cache, debug)` | Command/query | Repair, validate, adopt/log or mark static-only | Invalid/finalization failure returns null |
|
||||
| `load_threaded_get_status(path)` | I/O adapter | Production status query with injectable test seam | Returns ResourceLoader status |
|
||||
| `load_threaded_get(path)` | I/O adapter | Production terminal Resource retrieval with injectable test seam | May return null |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Pending/finalize records | Animation pipeline | Finalizer | Pipeline-owned copied Dictionaries |
|
||||
| Input | ResourceLoader status/Resource | Godot ResourceLoader | Finalizer | Resource reference for one operation |
|
||||
| Input | Material source root | Loader lookup | Scene finalizer | Borrowed exact Node3D |
|
||||
| Input | Prototype/static-only state | Prototype cache | Finalizer | Borrowed service; loader session |
|
||||
| Output | Prepared candidate Dictionary | Finalizer | Loader/finalizer completion | Caller-owned, immediate operation |
|
||||
| Output | Canonical animated prototype | Prototype cache | Loader build path | Borrowed exact Node3D |
|
||||
| Output | Static-only outcome | Finalizer | Prototype cache | Copied path; loader session |
|
||||
| Output | Success diagnostic | Finalizer | Runtime log | Debug-gated one-line record |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Poll[Poll pending records in insertion order] --> Path{Resource path empty?}
|
||||
Path -->|yes| Discard[Discard and mark static-only]
|
||||
Path -->|no| Status{LOADED or FAILED?}
|
||||
Status -->|no| Pending[Keep pending]
|
||||
Status -->|yes| Complete[Append completion FIFO]
|
||||
Complete --> Permit[Loader consumes one finalize permit]
|
||||
Permit --> Pop[Pop oldest terminal record]
|
||||
Pop --> Existing{Empty/cached/static-only?}
|
||||
Existing -->|yes| Skip[Finish permit operation]
|
||||
Existing -->|no| Loaded{Status LOADED?}
|
||||
Loaded -->|no| Mark[Mark static-only]
|
||||
Loaded -->|yes| Get[Get terminal Resource]
|
||||
Get --> Instantiate[Instantiate detached candidate]
|
||||
Instantiate --> Valid{Candidate exists?}
|
||||
Valid -->|no| Mark
|
||||
Valid -->|yes| Material[Loader resolves material source]
|
||||
Material --> Repair[Repair and validate]
|
||||
Repair --> Prototype{Prototype accepted?}
|
||||
Prototype -->|no| Mark
|
||||
Prototype -->|yes| Adopt[Adopt and optionally log]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Polling
|
||||
Polling --> Pending: nonterminal status
|
||||
Polling --> TerminalQueued: LOADED or FAILED
|
||||
Polling --> StaticOnly: empty Resource path
|
||||
TerminalQueued --> Skipped: cached/static/invalid record
|
||||
TerminalQueued --> StaticOnly: failed load or invalid candidate
|
||||
TerminalQueued --> Prepared: detached candidate
|
||||
Prepared --> Adopted: repair/finalize succeeds
|
||||
Prepared --> StaticOnly: validation fails
|
||||
Pending --> [*]
|
||||
Skipped --> [*]
|
||||
StaticOnly --> [*]
|
||||
Adopted --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant F as AnimationResourceFinalizer
|
||||
participant P as AnimationLoadPipelineState
|
||||
participant R as ResourceLoader
|
||||
participant S as AnimatedSceneFinalizer
|
||||
participant C as PrototypeCacheState
|
||||
L->>F: poll_terminal_requests(P, C)
|
||||
F->>R: load_threaded_get_status(path)
|
||||
F->>P: complete terminal request
|
||||
L->>L: consume M2_ANIMATION_FINALIZE permit
|
||||
L->>F: prepare_next_candidate(P, C)
|
||||
F->>P: pop oldest finalize record
|
||||
F->>R: load_threaded_get(path)
|
||||
F->>S: instantiate_candidate(Resource)
|
||||
alt candidate available
|
||||
F-->>L: normalized/path/candidate
|
||||
L->>L: resolve material prototype
|
||||
L->>F: finalize_prepared_candidate(...)
|
||||
F->>S: repair_materials + finalize_candidate
|
||||
F->>C: adopt prototype or mark static-only
|
||||
else rejected
|
||||
F->>C: mark static-only
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Finalizer[M2AnimationResourceFinalizer] --> ResourceLoader
|
||||
Finalizer --> Pipeline[M2AnimationLoadPipelineState]
|
||||
Finalizer --> SceneFinalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer --> Cache[M2PrototypeCacheState]
|
||||
Loader[StreamingWorldLoader] --> Finalizer
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
Loader --> MaterialLookup[Material prototype lookup]
|
||||
Finalizer -. no dependency .-> Scheduler
|
||||
Finalizer -. no dependency .-> BuildQueue[M2BuildQueue]
|
||||
Finalizer -. no dependency .-> Playback[M2AnimationPlaybackController]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- All methods run synchronously on the renderer main thread.
|
||||
- Pipeline owns pending and completion records; one loader permit pops one record.
|
||||
- The prepared Dictionary temporarily owns the detached candidate reference. The
|
||||
loader must call completion synchronously after material lookup.
|
||||
- Existing scene finalizer frees rejected candidate roots. Prototype cache owns
|
||||
accepted detached prototypes until final shutdown.
|
||||
- Service retains only scene-finalizer and optional test-adapter references. The
|
||||
production ResourceLoader path creates no self-reference or retained Resource.
|
||||
- Loader retains scheduler permits, material lookup and all instance/SceneTree work.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Missing service dependency | Return zero/empty/null | Correct renderer composition |
|
||||
| Empty Resource path | Discard pending request and mark static-only | Repair cache and start new session |
|
||||
| Nonterminal status | Keep request pending | Poll next tick |
|
||||
| FAILED terminal status | Pop one record and mark static-only | Repair cache and start new session |
|
||||
| Existing cached/static state | Pop and skip without terminal Resource get | None |
|
||||
| Null/unsupported Resource | Mark static-only | Repair imported cache |
|
||||
| Candidate validation failure | Scene finalizer frees candidate; mark static-only | Repair animation/cache |
|
||||
| Tile cancellation | No direct transition; shared cache work continues | Loader queue remains authoritative |
|
||||
| Shutdown | Loader drains ResourceLoader before pipeline/cache clear | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The module adds no setting. Existing cached-animation allow/deny policy,
|
||||
`m2_animation_finalize_ops_per_tick`, debug flag and scheduler lane remain exact.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No persistence, schema or cache path changes are introduced. Existing `.glb`
|
||||
imports and `pivot_prefix_v1` eligibility remain owned by the cached observer.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
Successful debug-enabled adoption emits the unchanged `M2_ANIM_CACHE` record
|
||||
with normalized path, Resource cache path and AnimationPlayer count. Pipeline
|
||||
work metrics remain pending plus completion FIFO size.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated verifier covers pending/loaded/failed polling order, empty path,
|
||||
completion FIFO, cached/static skips, terminal Resource get, candidate identity,
|
||||
material repair arguments, adoption, rejection, source ownership and timing.
|
||||
- Pipeline, scene-finalizer, prototype-cache, cached-observer, shutdown, facade,
|
||||
internal-access and baseline regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact lifecycle/I/O extraction only; no private asset,
|
||||
original-client animation, visual, leak-pressure or p95/p99 claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
Material-prototype lookup may later become a dedicated resource observer, which
|
||||
would allow the entire two-phase operation to compose outside the loader. Static
|
||||
Mesh Resource finalization remains separate to avoid premature generalization.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Animated request terminal polling | Implemented extraction | Status/order/source verifier | Asset-backed long traversal pending |
|
||||
| Animated candidate finalization/outcome | Implemented extraction | Identity/repair/adoption verifier | Private visual/leak/p95/p99 pending |
|
||||
| Material prototype lookup | Loader-owned | Existing renderer regressions | Dedicated observer optional |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Terminal Resource get, instantiation and repair remain synchronous main-thread work.
|
||||
- Prepared candidate ownership relies on immediate completion by the loader adapter.
|
||||
- Private asset traversal, descriptor pressure, leak and p95/p99 evidence is absent.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_animation_resource_finalizer.gd` | Poll/load/prepare/finalize/cache/log service |
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending and completion FIFO ownership |
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | Candidate instantiation/repair/validation/rejection cleanup |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Accepted prototype/static-only ownership |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Permits, material lookup and synchronous composition |
|
||||
| `src/tools/verify_m2_animation_resource_finalizer.gd` | Polling/finalization/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
|
||||
- [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.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)
|
||||
@@ -32,8 +32,8 @@ flowchart LR
|
||||
Loader --> Planner
|
||||
Planner --> Plan[Detached batch plan]
|
||||
Plan --> Loader
|
||||
Loader --> Ready{Resource ready?}
|
||||
Ready --> Materialize[Animated or MultiMesh materialization]
|
||||
Loader --> Dispatch[M2BuildDispatchPlanner]
|
||||
Dispatch --> Materialize[Animated or MultiMesh materialization]
|
||||
Loader --> Budget[RenderBudgetScheduler permit]
|
||||
```
|
||||
|
||||
@@ -102,8 +102,10 @@ sequenceDiagram
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The planner owns only call-local scalar values and the returned Dictionary.
|
||||
- The loader owns build-job Dictionaries, queue ordering, tile checks, resource
|
||||
readiness/retry, serial numbers and group-index mutation.
|
||||
- `M2BuildQueue` owns typed jobs, FIFO ordering, serial numbers and group/offset
|
||||
cursors. `M2BuildDispatchPlanner` owns the pure resource-state action decision;
|
||||
the loader owns tile checks and native-first orchestration, while static and
|
||||
cached animation observers own their observation/retry phases.
|
||||
- Materializers own main-thread Node/MultiMesh construction under loader roots.
|
||||
- The scheduler owns the frame-local `M2_BUILD` counter.
|
||||
- Pure planning is thread-safe, though the current adapter calls it on main thread.
|
||||
@@ -149,8 +151,8 @@ queue depth, build activity and hitch observability.
|
||||
|
||||
## Extension points
|
||||
|
||||
- A later package may define typed build-job state once queue/resource transitions
|
||||
are extracted together with explicit cancellation and recovery.
|
||||
- Remaining native resource observation may be extracted while retaining the
|
||||
dispatch and typed build-job/FIFO contracts.
|
||||
- Spatial-cell batching must use measured culling/performance evidence and must
|
||||
not silently change this model-path batch cursor.
|
||||
|
||||
@@ -159,12 +161,15 @@ queue depth, build activity and hitch observability.
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
|
||||
| Build queue/resource state machine | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
|
||||
| Typed build queue/cursor state | Implemented extraction | M2 build queue lifecycle verifier | Asset-backed traversal pending |
|
||||
| Resource dispatch decision | Implemented extraction | M2 build dispatch planner verifier | Asset-backed traversal pending |
|
||||
| Resource observation/requests | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
|
||||
| Spatial-cell batching | Planned | Renderer roadmap | Culling evidence/design pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- The result remains a Dictionary while loader build jobs are untyped Dictionaries.
|
||||
- The planner result remains a Dictionary; `M2BuildQueue` adopts its scalar
|
||||
cursor values through one explicit progress call.
|
||||
- Negative offsets are preserved rather than rejected to avoid a hidden behavior change.
|
||||
- Planning timing does not measure resource loading or materialization.
|
||||
- Private asset-backed p95/p99 and visual comparison remain unavailable.
|
||||
@@ -174,7 +179,12 @@ queue depth, build activity and hitch observability.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Build job, queue, resources, materialization and budgets |
|
||||
| `src/render/m2/m2_build_dispatch_planner.gd` | Pure observed-state action/transition planning |
|
||||
| `src/render/m2/m2_build_queue.gd` | Typed pending jobs, FIFO order and cursor ownership |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Tile checks, resource readiness, progress adoption, materializer adapters and budgets |
|
||||
| `src/render/m2/m2_static_batch_materializer.gd` | Planned static-slice MultiMesh construction and attachment |
|
||||
| `src/render/m2/m2_animation_playback_controller.gd` | Animated-instance phase/selection/native/imported playback |
|
||||
| `src/render/m2/m2_animated_instance_materializer.gd` | Planned animated-slice duplication, playback startup and attachment |
|
||||
| `src/render/m2/m2_placement_grouper.gd` | Produces grouped transform arrays |
|
||||
| `src/tools/verify_m2_build_batch_planner.gd` | Formula, source and timing regression |
|
||||
|
||||
@@ -182,6 +192,7 @@ queue depth, build activity and hitch observability.
|
||||
|
||||
- [`m2-placement-grouper.md`](m2-placement-grouper.md)
|
||||
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md)
|
||||
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.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)
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
# M2 Build Dispatch Planner
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-BUILD-DISPATCH-PLANNER-001` |
|
||||
| Owners | Pure M2 resource-state to build-action decision |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-dispatch-planner`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
||||
|
||||
## Purpose
|
||||
|
||||
Select one M2 build action after renderer observers produce animation/static
|
||||
resource state. The planner makes wait priority, materializer selection and the
|
||||
historical missing-model serial transition explicit without owning resources or
|
||||
engine work.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Locate, request, load, cache or validate M2 resources.
|
||||
- Size batches, own build jobs/queues or consume renderer permits.
|
||||
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
|
||||
- Change transforms, ordering, render settings, profiles or visible output.
|
||||
- Add spatial-cell batching or M2 format/fidelity behavior.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader resource observations] --> Planner[M2BuildDispatchPlanner]
|
||||
Planner --> Action[Detached action and transition plan]
|
||||
Action --> Loader
|
||||
Loader --> Queue[M2BuildQueue rotate/progress]
|
||||
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
||||
Loader --> Static[M2StaticBatchMaterializer]
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
```
|
||||
|
||||
The planner depends only on `RefCounted`, scalar values, `StringName` constants
|
||||
and a fresh Dictionary. ResourceLoader, caches, Nodes, Meshes, RenderingServer,
|
||||
SceneTree, files, workers, mutexes, gameplay, network and Editor APIs are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `ACTION_WAIT_FOR_ANIMATION` | Constant | Pending animation request blocks all other actions | Immutable | None |
|
||||
| `ACTION_MATERIALIZE_ANIMATED` | Constant | Build the selected slice from animated prototype | Immutable | None |
|
||||
| `ACTION_WAIT_FOR_STATIC_MESH` | Constant | Static resource is unresolved and queue must rotate | Immutable | None |
|
||||
| `ACTION_MATERIALIZE_STATIC` | Constant | Build the selected slice from prepared static Mesh | Immutable | None |
|
||||
| `ACTION_ADVANCE_WITHOUT_MATERIALIZATION` | Constant | Empty or terminally missing slice advances without a Node | Immutable | None |
|
||||
| `plan_step(batch_count, resource_snapshot)` | Pure query | Return action, queue-rotation and serial-transition values | Any thread; call-local result | Null snapshot waits for static Mesh; non-positive count advances |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Planned batch count | `M2BuildBatchPlanner` through loader | Dispatch planner | Integer copy | One call |
|
||||
| Input | Typed animated/static availability, pending and missing observations | Loader / `M2BuildResourceSnapshot` | Dispatch planner | Borrowed snapshot; engine references are not read | One call |
|
||||
| Output | Action | Dispatch planner | Loader materializer/wait branch | Immutable StringName | One operation |
|
||||
| Output | `rotate_queue` | Dispatch planner | Loader queue adapter | Detached boolean | One operation |
|
||||
| Output | `increment_batch_serial` | Dispatch planner | Loader progress adapter | Detached boolean | One operation |
|
||||
|
||||
The output Dictionary is newly allocated and caller-owned. The planner retains
|
||||
no state, resource or engine-object reference.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Input[Observed state] --> AnimationPending{Animation request pending?}
|
||||
AnimationPending -->|yes| WaitAnimation[Wait animation; rotate]
|
||||
AnimationPending -->|no| Positive{Batch count positive?}
|
||||
Positive -->|no| AdvanceEmpty[Advance; keep serial]
|
||||
Positive -->|yes| Animated{Animated prototype?}
|
||||
Animated -->|yes| BuildAnimated[Materialize animated; increment serial]
|
||||
Animated -->|no| StaticReady{Static Mesh ready?}
|
||||
StaticReady -->|yes| BuildStatic[Materialize static; increment serial]
|
||||
StaticReady -->|no| Missing{Model terminally missing?}
|
||||
Missing -->|yes| AdvanceMissing[Advance without Node; increment serial]
|
||||
Missing -->|no| WaitStatic[Wait static Mesh; rotate]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Observe
|
||||
Observe --> Waiting: animation/static unresolved
|
||||
Observe --> Materializing: animated/static resource ready
|
||||
Observe --> Advancing: empty or terminally missing batch
|
||||
Waiting --> [*]: detached wait plan
|
||||
Materializing --> [*]: detached materialization plan
|
||||
Advancing --> [*]: detached advance plan
|
||||
```
|
||||
|
||||
The planner itself is stateless. Queue rotation, cursor adoption, cancellation,
|
||||
map reset and shutdown remain external lifecycle transitions.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant B as M2BuildBatchPlanner
|
||||
participant D as M2BuildDispatchPlanner
|
||||
participant Q as M2BuildQueue
|
||||
participant M as M2 materializer
|
||||
participant S as RenderBudgetScheduler
|
||||
L->>L: observe animation request/prototype
|
||||
alt animation not pending
|
||||
L->>B: plan_batch
|
||||
L->>L: lookup/request static Mesh when required
|
||||
end
|
||||
L->>D: plan_step(batch count, resource snapshot)
|
||||
D-->>L: action, rotate flag, serial flag
|
||||
alt wait action
|
||||
L->>Q: rotate_front
|
||||
else materialize action
|
||||
L->>M: materialize selected slice
|
||||
L->>Q: adopt progress and serial
|
||||
else advance action
|
||||
L->>Q: adopt progress and optional serial
|
||||
end
|
||||
L->>S: consume one M2_BUILD permit
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Dispatch[M2BuildDispatchPlanner]
|
||||
Loader --> Batch[M2BuildBatchPlanner]
|
||||
Loader --> Queue[M2BuildQueue]
|
||||
Loader --> Caches[M2 cache/pipeline state]
|
||||
Loader --> Materializers[M2 materializers]
|
||||
Dispatch --> Snapshot[M2BuildResourceSnapshot accessors]
|
||||
Dispatch --> Values[Integers + booleans + StringName]
|
||||
Dispatch -. no dependency .-> Engine[Node / Mesh / ResourceLoader]
|
||||
Dispatch -. no dependency .-> State[Queue / cache / scheduler state]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Planner owns call-local comparisons and the returned Dictionary only.
|
||||
- `M2BuildResourceSnapshot` owns the typed call-local observation contract;
|
||||
dispatch reads availability/pending/missing accessors but no engine references.
|
||||
- Loader owns native-first observation order, action execution and permit use;
|
||||
native/static/cached-animation observers own their resource phases.
|
||||
- `M2BuildQueue` owns pending jobs, FIFO keys and progress cursors.
|
||||
- Materializers own main-thread scene construction under loader-owned roots.
|
||||
- Pure calls are thread-safe; the current loader adapter calls on main thread.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Animation request pending | Boolean observation | Wait wins even if other flags are true | Priority fixture | Retry when queue returns front |
|
||||
| Non-positive batch | Count comparison | Advance without serial increment | Zero/negative fixtures | Next group/cursor operation |
|
||||
| Static Mesh unresolved | No Mesh and not missing | Rotate without serial increment | Retry fixture | Loader request/cache completes |
|
||||
| Static model missing | Terminal missing flag | Advance without Node and increment serial | Missing fixture | Later group continues |
|
||||
| Contradictory static ready/missing | Both true | Ready Mesh wins | Priority fixture | Loader normally prevents contradiction |
|
||||
| Tile cancellation/root invalid | Outside planner | Loader cancels before dispatch | Queue/shutdown regressions | Eligible tile may requeue |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The planner adds no setting. Existing animated/static batch limits, animation
|
||||
enable flag, visibility/shadow settings and `M2_BUILD` permits remain external.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No state is serialized. No ADT/M2 cache format or version changes; no rebake or
|
||||
migration is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
The planner emits no logs. Its verifier reports the action matrix, source
|
||||
boundaries and bounded 20,000-call timing. Existing loader queue/hitch metrics
|
||||
remain unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_build_dispatch_planner.gd` covers wait priority, zero/negative
|
||||
counts, animated/static priority, unresolved retry, terminal missing, detached
|
||||
results, loader/dependency boundaries and bounded timing.
|
||||
- Queue, batch planner, materializers, caches, shutdown, facade, internal-access
|
||||
and checkpoint tests protect adjacent behavior.
|
||||
- Fidelity evidence is exact branch/transition extraction only. No private asset
|
||||
or original-client visual comparison is claimed.
|
||||
|
||||
## Extension points
|
||||
|
||||
Static, cached animated and native animated observation now live behind sibling
|
||||
services that produce `M2BuildResourceSnapshot`. Generic callbacks, signals and a shared state-machine
|
||||
framework remain intentionally excluded.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| M2 build action selection | Implemented extraction | Priority/matrix/source/timing verifier | Asset-backed traversal pending |
|
||||
| Static/cached/native animation observation | Implemented separately | Observer/cache/pipeline regressions | Asset-backed traversal pending |
|
||||
| Queue/cursor state | Implemented separately | M2 build queue verifier | Asset-backed traversal pending |
|
||||
| Materialization | Implemented separately | Static/animated materializer verifiers | GPU/p95/p99 evidence pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Native prototype attempts remain synchronous through the observer; loader still
|
||||
owns static request I/O and terminal animated ResourceLoader polling.
|
||||
- Synthetic timing does not measure ResourceLoader, Node or GPU work.
|
||||
- Private traversal, leak, visual and p95/p99 evidence remains unavailable.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_build_dispatch_planner.gd` | Pure action priority and transition plan |
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step resource observation contract |
|
||||
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached animation observation/request phase |
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native candidate/read/build/cache observation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Observation order, action execution, permits and engine lifetime |
|
||||
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor plan |
|
||||
| `src/render/m2/m2_build_queue.gd` | Pending jobs, FIFO and cursor ownership |
|
||||
| `src/tools/verify_m2_build_dispatch_planner.gd` | Matrix, boundary and timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-build-queue.md`](m2-build-queue.md)
|
||||
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
|
||||
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.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)
|
||||
@@ -0,0 +1,265 @@
|
||||
# M2 Build Queue
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M03 / `M03-RND-M2-BUILD-QUEUE-001` |
|
||||
| Owners | Pending M2 build-job records, FIFO tile keys and build cursors |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-queue`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
||||
|
||||
## Purpose
|
||||
|
||||
Own typed pending M2 build state outside `StreamingWorldLoader`: a keyed job
|
||||
retains its M2 root, grouped transforms, insertion-order group keys and three
|
||||
progress cursors, while a separate FIFO retains scheduling order and stale keys.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Decide tile eligibility, detail radius, resource readiness or retries.
|
||||
- Consume scheduler permits or plan batch sizes.
|
||||
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
|
||||
- Load/cache resources, group placements or mutate tile state.
|
||||
- Change model order, transform order, render settings or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Groups[M2PlacementGrouper result] --> Loader[StreamingWorldLoader adapter]
|
||||
Loader --> Queue[M2BuildQueue]
|
||||
Queue --> Job[M2BuildJob]
|
||||
Queue --> Loader
|
||||
Loader --> Planner[M2BuildBatchPlanner]
|
||||
Loader --> Dispatch[M2BuildDispatchPlanner]
|
||||
Loader --> Static[M2StaticBatchMaterializer]
|
||||
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
```
|
||||
|
||||
Allowed dependencies are String, Dictionary, Array, RefCounted and a strong
|
||||
Node3D reference. SceneTree destruction, materializers, Mesh/MultiMesh,
|
||||
RenderingServer/RIDs, ResourceLoader/files, workers/mutexes, scheduler policy,
|
||||
gameplay, network and Editor APIs are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
### `M2BuildJob`
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `tile_key()` | Query | Immutable keyed identity | Main thread; job lifetime | None |
|
||||
| `root()` | Query | Borrow strongly retained M2 root | Main thread; no ownership transfer | May be externally invalidated |
|
||||
| `groups()` | Query | Borrow exact grouped-transform Dictionary | Main thread; job lifetime | None |
|
||||
| `group_keys()` | Query | Borrow enqueue-time insertion-order key snapshot | Main thread; job lifetime | None |
|
||||
| `group_index()` | Query | Current model-path cursor | Main thread | None |
|
||||
| `transform_offset()` | Query | Current transform cursor within group | Main thread | None |
|
||||
| `batch_serial()` | Query | Current batch naming serial | Main thread | None |
|
||||
| `adopt_progress(group_index, offset, serial)` | Command | Atomically replace all progress values | Main thread | Raw integer semantics retained |
|
||||
| `diagnostic_snapshot()` | Query | Detached scalar/count state | Any serialized caller | Omits engine references |
|
||||
|
||||
### `M2BuildQueue`
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `enqueue(tile_key, root, groups)` | Command | Replace keyed job and append FIFO key | Main thread; until erase/clear | Invalid/empty input returns false |
|
||||
| `has_pending/front_key/pop_front/rotate_front` | Commands/queries | Inspect, drain or rotate FIFO independently of job validity | Main thread | Empty returns false/empty String |
|
||||
| `has_job/job_for` | Queries | Inspect keyed record, including stale-key distinction | Main thread | Missing returns false/null |
|
||||
| `root_for/groups_for/group_keys_for` | Queries | Borrow current job inputs | Main thread | Stale returns null/empty collection |
|
||||
| `group_index_for/transform_offset_for/batch_serial_for` | Queries | Read current progress | Main thread | Stale returns zero |
|
||||
| `adopt_progress(tile_key, group_index, offset, serial)` | Command | Atomically update current job progress | Main thread | Stale returns false |
|
||||
| `erase_job(tile_key)` | Command | Remove keyed job but preserve FIFO keys | Main thread | Unknown returns false |
|
||||
| `job_keys()` | Query | Detached active-key snapshot for loader cleanup | Main thread | Empty returns empty Array |
|
||||
| `clear()` | Command | Release all job and FIFO references | Main thread | Idempotent; never frees Nodes |
|
||||
| `pending_count/active_job_count` | Queries | FIFO and keyed-job metrics | Main thread | None |
|
||||
| `diagnostic_snapshot()` | Query | Detached FIFO and sorted job diagnostics | Main thread | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Tile key and M2 root | Loader group-result finalizer | Queue/job | Copied String; strong borrowed Node reference | Until erase/clear |
|
||||
| Input | Grouped transforms Dictionary | M2PlacementGrouper through loader mailbox | Job | Exact strong reference | Job lifetime |
|
||||
| Input | `groups.keys()` order | Job constructor | Loader group selection | Job-owned Array snapshot | Job lifetime |
|
||||
| Input | Next group/offset/serial | Planner/materializer adapter | Job progress | Copied integers | Until next adoption |
|
||||
| Output | Front/rotated/popped tile key | Queue | Loader scheduling loop | Copied String | One drain step |
|
||||
| Output | Borrowed root/groups/group keys | Job/queue | Loader readiness/materialization | No ownership transfer | One drain step |
|
||||
| Output | Detached diagnostics/key snapshots | Queue/job | Tests/metrics/cleanup adapter | Caller-owned collections | Call result |
|
||||
|
||||
Side effects are keyed/FIFO collection mutation, strong-reference retention and
|
||||
cursor mutation. No engine object is created, destroyed or attached.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Enqueue[Tile key, root and grouped transforms] --> Job[Create or replace M2BuildJob]
|
||||
Enqueue --> FIFO[Append tile key to FIFO]
|
||||
FIFO --> Front[Borrow front key]
|
||||
Front --> Current{Current keyed job exists?}
|
||||
Current -->|no| Pop[Pop stale key]
|
||||
Current -->|yes| Loader[Loader eligibility/readiness/materialization]
|
||||
Loader -->|resource pending| Rotate[Move front key to tail]
|
||||
Loader -->|operation complete| Progress[Adopt group, offset and serial]
|
||||
Loader -->|finish/cancel| Erase[Erase job only]
|
||||
Erase --> Pop
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Absent
|
||||
Absent --> Queued: enqueue valid job and FIFO key
|
||||
Queued --> Rotated: resource pending
|
||||
Rotated --> Queued: returns to FIFO front
|
||||
Queued --> Progressed: adopt progress
|
||||
Progressed --> Queued: group remains
|
||||
Queued --> StaleQueued: erase job before FIFO pop
|
||||
Progressed --> StaleQueued: finish or cancel
|
||||
StaleQueued --> Absent: pop stale key
|
||||
Queued --> Absent: clear
|
||||
StaleQueued --> Absent: clear
|
||||
```
|
||||
|
||||
Duplicate enqueue replaces the current keyed job and adds another FIFO entry;
|
||||
later erase can therefore leave multiple stale entries, matching historical raw state.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant G as M2 group-result drain
|
||||
participant Q as M2BuildQueue
|
||||
participant L as StreamingWorldLoader
|
||||
participant P as M2BuildBatchPlanner
|
||||
participant M as M2 materializer
|
||||
G->>Q: enqueue(tile key, root, groups)
|
||||
L->>Q: front_key and has_job
|
||||
Q-->>L: borrowed root/groups/keys and cursors
|
||||
L->>P: plan selected transform slice
|
||||
alt resource pending
|
||||
L->>Q: rotate_front
|
||||
else materializable
|
||||
L->>M: materialize selected slice
|
||||
L->>Q: adopt_progress(next index, offset, serial)
|
||||
else cancelled or complete
|
||||
L->>L: free/queue-free root if required
|
||||
L->>Q: erase_job
|
||||
L->>Q: pop_front
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Queue[M2BuildQueue]
|
||||
Queue --> Job[M2BuildJob]
|
||||
Job --> EngineBase[Node3D strong reference]
|
||||
Loader --> Planner[M2BuildBatchPlanner]
|
||||
Loader --> Materializers[Static and animated materializers]
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
Queue -. no dependency .-> SceneMutation[SceneTree destruction/attachment]
|
||||
Queue -. no dependency .-> Resources[Mesh / MultiMesh / ResourceLoader]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Queue owns keyed job records and FIFO String entries.
|
||||
- Job retains the exact groups Dictionary, a fresh group-key snapshot and root.
|
||||
- Root ownership remains with the loader/tile SceneTree; queue release never frees it.
|
||||
- Loader validates `is_instance_valid`, frees empty/aborted roots and mutates tile state.
|
||||
- `M2BuildDispatchPlanner` selects wait/materializer/advance actions without
|
||||
borrowing queue-owned engine references.
|
||||
- All current operations run on the renderer main thread; no mutex is required.
|
||||
- Group worker results cross their existing mutex mailbox before enqueue.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Invalid enqueue input | Entry guard | Reject without mutation | Contract fixture | Correct caller composition |
|
||||
| Duplicate tile key | Keyed assignment | Replace job and append FIFO key | Duplicate fixture | Stale entries drain normally |
|
||||
| Stale FIFO key | `has_job == false` | Loader pops and continues without permit | Stale fixture | No action required |
|
||||
| Resource pending | Loader cache state | Rotate one front key and consume existing permit | Rotation fixture/build diagnostics | Retry when key returns front |
|
||||
| Root externally invalid | Loader validity check | Erase job and pop key | Loader source contract | Tile may regroup/requeue later |
|
||||
| Tile cancelled | Loader policy | Queue-free root, erase job; FIFO key drains/pops | Shutdown/lifetime regression | Eligible tile may requeue |
|
||||
| World clear | Loader job-key snapshot | Cancel roots, then clear queue | Shutdown regression | New map starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The queue introduces no settings. Existing static/animated batch limits,
|
||||
visibility/shadow settings, resource caches and `M2_BUILD` permits remain loader,
|
||||
planner, materializer and scheduler contracts.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No state is serialized. No ADT/M2 cache format or version changes; no rebake or
|
||||
migration is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- `pending_count` preserves the existing FIFO-backed `m2_build` queue metric.
|
||||
- `active_job_count` distinguishes keyed jobs from stale/duplicate FIFO entries.
|
||||
- Diagnostic snapshots retain FIFO order and sort job records by tile key.
|
||||
- Job diagnostics expose cursors/counts but never root/groups references.
|
||||
- The module emits no logs.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_build_queue.gd` covers invalid input, exact groups/root retention,
|
||||
FIFO, duplicate replacement, rotation, stale entries, atomic progress, erase/
|
||||
clear engine lifetime, detached sorted diagnostics and loader boundaries.
|
||||
- Planner, static/animated materializer, Mesh pipeline/cache/prototype, shutdown,
|
||||
facade, internal-access and checkpoint regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact state-transition extraction; visual rules are not changed.
|
||||
- Performance budget: 100 cycles of 256 enqueue/rotate/erase operations under one second.
|
||||
- No original-client or proprietary asset comparison is claimed for bookkeeping state.
|
||||
|
||||
## Extension points
|
||||
|
||||
Remaining resource readiness and dispatch logic may later move behind explicit
|
||||
commands once its cancellation semantics are independently fixed. A generic
|
||||
queue base, signals and callbacks are intentionally excluded.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Typed keyed M2 jobs and FIFO | Implemented extraction | Lifecycle/order/source/timing verifier | Asset-backed traversal pending |
|
||||
| Cursor/serial ownership | Implemented extraction | Atomic progress fixtures | Typed batch-plan result remains Dictionary |
|
||||
| Root destruction | Existing loader-owned | Lifetime/source/shutdown regressions | Keep outside state service |
|
||||
| Resource dispatch decision | Implemented separately | Dispatch planner verifier | Asset-backed traversal pending |
|
||||
| Resource observation/requests | Existing loader-owned | Adjacent cache/build tests | Safe extraction remains |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Grouped transforms remain untyped Dictionary/Array data.
|
||||
- Queue permits duplicate and stale FIFO keys intentionally for compatibility.
|
||||
- Job accessors return borrowed mutable groups/key collections; current loader is the sole consumer.
|
||||
- Private asset traversal, long-flight p95/p99 and leak evidence remain unavailable.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_build_job.gd` | Strong root/groups references, key snapshot and progress cursors |
|
||||
| `src/render/m2/m2_build_queue.gd` | Keyed job ownership, FIFO/stale/rotation lifecycle and diagnostics |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Eligibility, readiness, permits, materialization, tile state and root cleanup |
|
||||
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor-plan calculation |
|
||||
| `src/render/m2/m2_build_dispatch_planner.gd` | Resource-state action and transition planning |
|
||||
| `src/tools/verify_m2_build_queue.gd` | Lifecycle/order/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||
- [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md)
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
|
||||
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.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)
|
||||
@@ -0,0 +1,239 @@
|
||||
# M2 Build Resource Snapshot
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-BUILD-RESOURCE-SNAPSHOT-001` |
|
||||
| Owners | Per-build-step observed M2 resource references and outcome flags |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-resource-snapshot`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
|
||||
|
||||
## Purpose
|
||||
|
||||
Carry the resource observations for one M2 build operation as a typed contract:
|
||||
normalized path, optional animated prototype, pending-animation state, optional
|
||||
static Mesh and terminal missing-model state. The snapshot never owns engine lifetime.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Locate, request, load, validate or cache M2 resources.
|
||||
- Decide dispatch action, batch count, queue order or permit consumption.
|
||||
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
|
||||
- Change animation allowlists, native candidate rules or cache paths/formats.
|
||||
- Persist observations beyond one build operation.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Native[M2NativeAnimationResourceObserver] --> Snapshot[M2BuildResourceSnapshot]
|
||||
Cached[M2CachedAnimationResourceObserver] --> Snapshot
|
||||
Static[M2StaticBuildResourceObserver] --> Snapshot
|
||||
Snapshot --> Dispatch[M2BuildDispatchPlanner]
|
||||
Snapshot --> Loader
|
||||
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
||||
Loader --> Static[M2StaticBatchMaterializer]
|
||||
```
|
||||
|
||||
The snapshot depends only on `RefCounted`, String, booleans and borrowed Node3D/
|
||||
Mesh references. ResourceLoader, cache/pipeline state, SceneTree mutation,
|
||||
RenderingServer/RIDs, files, workers, mutexes, gameplay, network and Editor APIs
|
||||
are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| Constructor `(normalized_path, animated_prototype, animation_pending)` | Command | Capture first-phase animation observation | Main thread; one build operation | Values retained exactly |
|
||||
| `normalized_relative_path()` | Query | Return observed normalized path | Snapshot lifetime | None |
|
||||
| `animated_prototype()` | Query | Borrow exact animated prototype | Main thread; no ownership transfer | May be null |
|
||||
| `has_animated_prototype()` | Query | Report prototype availability | Any serialized caller | None |
|
||||
| `animation_request_pending()` | Query | Report pending animation request | Any serialized caller | None |
|
||||
| `adopt_static_observation(mesh, missing)` | Command | Replace second-phase static observation | Main thread | Values retained exactly, including contradictory inputs |
|
||||
| `static_mesh()` | Query | Borrow exact prepared Mesh | Main thread; no ownership transfer | May be null |
|
||||
| `has_static_mesh()` | Query | Report static Mesh availability | Any serialized caller | None |
|
||||
| `static_model_missing()` | Query | Report terminal missing outcome | Any serialized caller | None |
|
||||
| `diagnostic_snapshot()` | Query | Return detached path/availability flags | Any serialized caller | Omits engine references |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Normalized relative path | Loader path adapter | Snapshot/diagnostics | Copied String | Snapshot lifetime |
|
||||
| Input | Optional animated prototype | Native/cached animation observer | Snapshot/materializer adapter | Borrowed exact Node3D reference | One build operation |
|
||||
| Input | Animation request pending | Cached animation observer | Snapshot/dispatch planner | Boolean copy | One build operation |
|
||||
| Input | Optional prepared static Mesh | Mesh cache/request path | Snapshot/materializer adapter | Borrowed exact Mesh reference | One build operation |
|
||||
| Input | Static model terminally missing | Prototype cache state | Snapshot/dispatch planner | Boolean copy | One build operation |
|
||||
| Output | Availability/pending/missing values | Snapshot | Dispatch planner | Scalar reads | One dispatch plan |
|
||||
| Output | Borrowed prototype or Mesh | Snapshot | Loader materializer adapter | No ownership transfer | One materialization call |
|
||||
| Output | Detached diagnostics | Snapshot | Tests/future metrics | Caller-owned Dictionary | Call result |
|
||||
|
||||
The snapshot is released after the build-loop operation. Releasing or replacing
|
||||
it never frees the borrowed Node3D or Mesh.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Path[Normalized M2 path] --> Construct[Create snapshot]
|
||||
Animated[Animated prototype or null] --> Construct
|
||||
Pending[Animation request pending] --> Construct
|
||||
Construct --> PendingCheck{Animation pending?}
|
||||
PendingCheck -->|yes| Dispatch[Dispatch planner]
|
||||
PendingCheck -->|no| Batch[Batch planner]
|
||||
Batch --> StaticNeeded{Positive static batch?}
|
||||
StaticNeeded -->|yes| Adopt[Adopt Mesh and missing observation]
|
||||
StaticNeeded -->|no| Dispatch
|
||||
Adopt --> Dispatch
|
||||
Dispatch --> Borrow[Loader borrows selected resource]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> AnimationObserved: construct
|
||||
AnimationObserved --> AnimationObserved: animation pending or animated path
|
||||
AnimationObserved --> StaticObserved: adopt static result
|
||||
StaticObserved --> StaticObserved: replace static result
|
||||
AnimationObserved --> Released: operation ends
|
||||
StaticObserved --> Released: operation ends
|
||||
Released --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant S as M2BuildResourceSnapshot
|
||||
participant B as M2BuildBatchPlanner
|
||||
participant D as M2BuildDispatchPlanner
|
||||
participant M as M2 materializer
|
||||
L->>L: delegate native animated prototype observation
|
||||
L->>S: construct native result
|
||||
opt no native prototype
|
||||
L->>L: delegate cached animation observer
|
||||
L->>S: receive cached/pending snapshot
|
||||
end
|
||||
alt animation not pending
|
||||
L->>B: plan batch using snapshot availability
|
||||
opt positive static batch
|
||||
L->>L: lookup/request static Mesh
|
||||
L->>S: adopt_static_observation
|
||||
end
|
||||
end
|
||||
L->>D: plan_step(batch count, snapshot)
|
||||
D-->>L: action plan
|
||||
opt materialization action
|
||||
L->>S: borrow prototype or Mesh
|
||||
L->>M: materialize selected slice
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Snapshot[M2BuildResourceSnapshot]
|
||||
Dispatch[M2BuildDispatchPlanner] --> Snapshot
|
||||
Snapshot --> Values[String and booleans]
|
||||
Snapshot --> Borrowed[Borrowed Node3D and Mesh]
|
||||
Snapshot -. no dependency .-> IO[ResourceLoader / FileAccess]
|
||||
Snapshot -. no dependency .-> State[Cache / pipeline / queue / scheduler]
|
||||
Snapshot -. no dependency .-> Mutation[SceneTree / RenderingServer]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Snapshot owns only copied scalar state and temporary references.
|
||||
- Prototype/cache services retain resource state; the native observer owns its
|
||||
phase while loader owns native-first ordering among resource observers.
|
||||
- Scene roots remain loader/tile-owned; Mesh lifetime follows cache/resource refs.
|
||||
- Dispatch planner reads only snapshot accessors and never mutates the snapshot.
|
||||
- Current construction/adoption occurs on renderer main thread.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty path | Exact String retained | No validation or hidden normalization | Contract fixture | Loader already normalizes input |
|
||||
| Null prototype/Mesh | Availability accessor false | Dispatch uses pending/missing state | Contract fixture | Loader request/cache path retries |
|
||||
| Mesh and missing both true | Exact adoption | Both retained; dispatch gives ready Mesh priority | Contradiction fixture | Loader normally prevents combination |
|
||||
| Static observation replaced | Explicit adoption | Latest Mesh/missing values win | Replacement fixture | None required |
|
||||
| Borrowed Node externally freed | Outside snapshot | Loader validity/lifecycle gates remain authoritative | Shutdown regressions | Tile may cancel/requeue |
|
||||
| Tile cancellation | Outside snapshot | Ephemeral snapshot releases references | Queue/shutdown regressions | Eligible tile may rebuild |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The snapshot adds no setting. Existing animation enable/allow/deny rules, batch
|
||||
limits, cache paths, render settings and scheduler permits remain external.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No observation is serialized. No ADT/M2 cache version changes and no rebake or
|
||||
migration are required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
`diagnostic_snapshot()` exposes normalized path and four availability/outcome
|
||||
flags without Node/Mesh references. The module emits no logs. Existing loader
|
||||
queue/hitch metrics remain unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_build_resource_snapshot.gd` covers exact path/reference identity,
|
||||
defaults, static adoption/replacement/clear, contradictory flags, detached
|
||||
diagnostics, engine lifetime, loader/dispatch boundaries and bounded timing.
|
||||
- Dispatch, queue, batch, materializer, cache/pipeline, shutdown, facade,
|
||||
internal-access and checkpoint regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact observation transfer only; no private asset or
|
||||
original-client visual claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
`M2CachedAnimationResourceObserver` produces the cached animated phase and
|
||||
`M2StaticBuildResourceObserver` produces the static phase. Native animation is
|
||||
produced by `M2NativeAnimationResourceObserver`. The value stays independent of all
|
||||
producers and of generic callback frameworks.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Typed per-step M2 resource observation | Implemented extraction | Identity/adoption/lifetime/source/timing verifier | Asset-backed traversal pending |
|
||||
| Dispatch decision | Implemented separately | Dispatch planner verifier | Asset-backed traversal pending |
|
||||
| Static lookup/request execution | Implemented separately | Static observer verifier | Asset-backed traversal pending |
|
||||
| Cached animated lookup/request execution | Implemented separately | Cached observer verifier | Asset-backed traversal pending |
|
||||
| Native animated lookup/build execution | Implemented observer extraction | Native observer lifecycle/source verifier | Asset-backed traversal pending |
|
||||
| Engine lifetime/materialization | Loader/materializer-owned | Lifetime/materializer regressions | GPU/p95/p99 evidence pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Static observation is mutable during its short two-phase construction.
|
||||
- Raw Node3D/Mesh references remain necessary for current materializer APIs.
|
||||
- Native parsing/build remains synchronous; loader still owns terminal polling.
|
||||
- Synthetic timing does not measure resource, Node or GPU work.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step resource observations and diagnostics |
|
||||
| `src/render/m2/m2_build_dispatch_planner.gd` | Snapshot-to-action planning |
|
||||
| `src/render/m2/m2_static_build_resource_observer.gd` | Static snapshot production and requests |
|
||||
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached animated snapshot production and requests |
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native prototype observation producer |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Observer ordering and materializer borrowing |
|
||||
| `src/tools/verify_m2_build_resource_snapshot.gd` | Identity/adoption/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md)
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||
- [`m2-build-queue.md`](m2-build-queue.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)
|
||||
@@ -0,0 +1,242 @@
|
||||
# M2 Cached Animation Resource Observer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001` |
|
||||
| Owners | Cached animated M2 eligibility, request admission and initial snapshot |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-cached-animation-resource-observer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Produce the cached-animation phase of `M2BuildResourceSnapshot`: reuse an exact
|
||||
animated prototype, wait for a pending request, admit the first safe GLB request,
|
||||
or record a terminal static-only animation outcome without changing behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Load/build native GryphonRoost animation or emit its diagnostics.
|
||||
- Poll/finalize threaded Resources or instantiate/fix imported scenes.
|
||||
- Own/free prototype Nodes, snapshots, pipeline entries or cache state.
|
||||
- Materialize instances, consume permits or change animation policy defaults.
|
||||
- Change GLB layout, cache format, batching, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Native[M2NativeAnimationResourceObserver]
|
||||
Native -->|no prototype| Observer[M2CachedAnimationResourceObserver]
|
||||
Prototype[M2PrototypeCacheState] --> Observer
|
||||
Pipeline[M2AnimationLoadPipelineState] --> Observer
|
||||
Resource[ResourceLoader and GLB JSON] --> Observer
|
||||
Observer --> Snapshot[M2BuildResourceSnapshot]
|
||||
Snapshot --> Dispatch[M2BuildDispatchPlanner]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `observe(path, cache_dir, max_primitives, allow, deny, prototype_state, pipeline_state, debug)` | Command/query | Produce cached prototype/pending/static-only snapshot | Invalid input returns empty non-pending snapshot |
|
||||
| `find_eligible_glb_cache_path(...)` | I/O query | Select first existing safe historical candidate | Returns empty String |
|
||||
| `is_animation_path_allowed(path, allow, deny)` | Pure query | Apply stripped case-insensitive substring policy | Empty path/allowlist rejects; deny wins |
|
||||
| `cache_resource_paths(cache_dir, path)` | Pure query | Return nested/lowercase/basename GLB candidates | Empty values yield fewer candidates |
|
||||
| `glb_cache_is_safe_for_runtime_animation(path, max, debug)` | I/O query | Validate animations, primitive limit and schema | Invalid/missing GLB rejects |
|
||||
| `read_glb_json(path)` | I/O query | Read version-2 GLB JSON chunk | Returns empty Dictionary |
|
||||
| `glb_primitive_count(gltf)` | Pure query | Count primitives across meshes | Invalid entries contribute zero |
|
||||
| `glb_animation_schema(gltf)` | Pure query | Read OpenWC schema marker | Missing marker returns empty String |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Normalized M2 path/cache directory | Loader | Observer | Copied Strings; one observation |
|
||||
| Input | Primitive cap and allow/deny patterns | Loader exports | Observer | Copied/read-only call values |
|
||||
| Input | Prototype and request states | M2 state services | Observer | Borrowed services; map/session |
|
||||
| Input | Existing GLB cache file | Offline cache pipeline | Observer | Read-only file access |
|
||||
| Output | Resource snapshot | Observer | Loader/dispatch/materializer | Caller-owned snapshot; one build step |
|
||||
| Output | Borrowed exact prototype | Prototype state | Snapshot/materializer | No ownership transfer |
|
||||
| Output | Threaded request record | Observer | Animation pipeline | Pipeline-owned copied paths |
|
||||
| Output | Static-only transition | Observer | Prototype state | State-owned copied path |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Observe normalized path] --> Valid{Valid composition?}
|
||||
Valid -->|no| Empty[Empty non-pending snapshot]
|
||||
Valid -->|yes| Cached{Prototype cached?}
|
||||
Cached -->|yes| Ready[Snapshot with exact prototype]
|
||||
Cached -->|no| Static{Already static-only?}
|
||||
Static -->|yes| Empty
|
||||
Static -->|no| Pending{Request exists?}
|
||||
Pending -->|yes| Wait[Pending snapshot]
|
||||
Pending -->|no| Policy{Allow and not deny?}
|
||||
Policy -->|no| Mark[Mark static-only]
|
||||
Policy -->|yes| Candidate[Historical GLB candidates]
|
||||
Candidate --> Safe{Animations, primitive cap, schema safe?}
|
||||
Safe -->|no usable| Mark
|
||||
Safe -->|yes| Request[Threaded request]
|
||||
Request -->|OK or busy| Remember[Remember and return pending snapshot]
|
||||
Request -->|error| Mark
|
||||
Mark --> Empty
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Observing
|
||||
Observing --> Cached
|
||||
Observing --> Pending
|
||||
Observing --> Requested
|
||||
Observing --> StaticOnly
|
||||
Observing --> Rejected
|
||||
Cached --> [*]
|
||||
Pending --> [*]
|
||||
Requested --> [*]
|
||||
StaticOnly --> [*]
|
||||
Rejected --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant O as CachedAnimationObserver
|
||||
participant C as PrototypeCacheState
|
||||
participant P as AnimationLoadPipelineState
|
||||
participant R as ResourceLoader
|
||||
participant S as M2BuildResourceSnapshot
|
||||
L->>L: attempt native candidate
|
||||
alt no native prototype
|
||||
L->>O: observe(path, cache/policy/state)
|
||||
O->>C: find animated / static-only
|
||||
O->>P: has request
|
||||
opt eligible cache candidate
|
||||
O->>R: exists + GLB JSON + threaded request
|
||||
O->>P: remember request
|
||||
end
|
||||
O-->>L: snapshot
|
||||
else native prototype
|
||||
L->>S: construct native snapshot
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Observer[M2CachedAnimationResourceObserver] --> Snapshot[M2BuildResourceSnapshot]
|
||||
Observer --> Prototype[M2PrototypeCacheState]
|
||||
Observer --> Pipeline[M2AnimationLoadPipelineState]
|
||||
Observer --> ResourceLoader
|
||||
Observer --> FileAccess[GLB header and JSON]
|
||||
Observer -. no dependency .-> Native[M2 native builder/repository]
|
||||
Observer -. no dependency .-> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Observer -. no dependency .-> Scheduler[RenderBudgetScheduler]
|
||||
Observer -. no dependency .-> SceneTree
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Observer retains no service, request, snapshot, Resource or Node reference.
|
||||
- Prototype state retains accepted Nodes and static-only paths until shutdown.
|
||||
- Pipeline state retains copied request paths until terminal polling/finalize.
|
||||
- Snapshot borrows the exact cached prototype; observer never frees it.
|
||||
- Resource existence/request and synchronous GLB JSON inspection run on the
|
||||
renderer main thread, matching the previous loader behavior.
|
||||
- The sibling native observer owns the native build attempt and the animation
|
||||
resource finalizer owns terminal polling/finalize. Loader retains permits,
|
||||
material lookup, materialization and SceneTree mutation.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Empty path/dependency | Return empty non-pending snapshot without mutation | Correct composition |
|
||||
| Cached prototype | Return exact reference immediately | None |
|
||||
| Existing request | Return pending without duplicate admission | Retry after terminal drain |
|
||||
| Disallowed/denied path | Mark static-only | New loader session/configuration |
|
||||
| Missing/invalid/unsafe GLB | Continue candidates, then mark static-only | Repair/rebake cache and restart session |
|
||||
| Request error | Mark static-only | New loader session/cache repair |
|
||||
| Tile cancellation | Observer retains nothing | Loader queue remains authoritative |
|
||||
| Shutdown | Loader drains requests before state clear | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The service consumes existing `m2_cache_dir`, `m2_animated_max_primitives`,
|
||||
`m2_animated_allowlist_patterns`, `m2_animated_denylist_patterns` and debug flag.
|
||||
It adds no setting and preserves runtime mutability and defaults.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No persistence or cache format changes are introduced. Existing `.glb` candidate
|
||||
layout and OpenWC schema markers are read unchanged; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
Unsupported non-empty schemas preserve the historical optional
|
||||
`M2_ANIM_REJECT` debug line. Snapshot and pipeline diagnostics remain detached;
|
||||
queue and hitch metrics are unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated verifier covers invalid/cached identity/static/pending/missing
|
||||
lifecycle, allow/deny semantics, exact path order, generated GLB header/JSON,
|
||||
animation presence, primitive cap, accepted/rejected schemas, source boundaries
|
||||
and bounded timing.
|
||||
- Snapshot, dispatch, pipeline, prototype, shutdown, facade, internal-access and
|
||||
checkpoint regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact policy/I/O extraction. No original-client visual or
|
||||
animation parity claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
Native and cached observation remain sibling services. Terminal ResourceLoader
|
||||
polling/finalization uses the dedicated finalizer while retaining pipeline state.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Cached animation observation/request | Implemented extraction | Lifecycle/policy/GLB/source/timing verifier | Asset-backed traversal pending |
|
||||
| Native animation observation | Implemented sibling extraction | Native observer lifecycle/source verifier | Asset-backed traversal pending |
|
||||
| Animated finalize/preparation | Implemented finalizer extraction | Pipeline/finalizer regressions | Asset-backed traversal pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- A successful asynchronous request is not started by the unit fixture because
|
||||
leaving it undrained leaks process work; source and pipeline tests cover admission.
|
||||
- Generated GLB metadata fixtures validate policy, not Godot import fidelity.
|
||||
- Private traversal, leak/descriptor pressure, p95/p99 and paired visuals remain
|
||||
unavailable without private assets.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached GLB policy, request and snapshot production |
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step observation value |
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending request/finalize state |
|
||||
| `src/render/m2/m2_animation_resource_finalizer.gd` | Terminal polling/load/finalize outcomes |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and static-only outcomes |
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native-first candidate/read/build/cache observation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, fallback order, finalize and execution |
|
||||
| `src/tools/verify_m2_cached_animation_resource_observer.gd` | Lifecycle/policy/GLB/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-native-animation-resource-observer.md`](m2-native-animation-resource-observer.md)
|
||||
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
|
||||
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md)
|
||||
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-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)
|
||||
@@ -78,7 +78,7 @@ Side effects are limited to collection mutation and retaining String/integer val
|
||||
flowchart TD
|
||||
Start[Successful ResourceLoader request] --> Remember[remember request]
|
||||
Remember --> Snapshot[request records snapshot]
|
||||
Snapshot --> Poll[Loader polls status]
|
||||
Snapshot --> Poll[Mesh resource finalizer polls status]
|
||||
Poll --> Active{In progress?}
|
||||
Active -->|yes| Snapshot
|
||||
Active -->|no| Complete[complete request with terminal status]
|
||||
@@ -86,7 +86,7 @@ flowchart TD
|
||||
FIFO --> Permit{Loader permit available?}
|
||||
Permit -->|no| FIFO
|
||||
Permit -->|yes| Pop[Pop oldest terminal record]
|
||||
Pop --> Finalize[Loader gets Resource and delegates first-Mesh extraction]
|
||||
Pop --> Finalize[Mesh resource finalizer gets Resource and extracts first Mesh]
|
||||
Finalize --> Prepare[M2RuntimeMeshFinalizer prepares Mesh]
|
||||
Prepare --> Adopt[Loader adopts Mesh or marks prototype outcome state]
|
||||
```
|
||||
@@ -113,6 +113,7 @@ again only if loader cache/missing rules permit it.
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as M2MeshResourceFinalizer
|
||||
participant Resource as ResourceLoader
|
||||
participant State as M2MeshLoadPipelineState
|
||||
participant Budget as RenderBudgetScheduler
|
||||
@@ -120,14 +121,16 @@ sequenceDiagram
|
||||
Resource-->>Loader: OK or ERR_BUSY
|
||||
Loader->>State: remember_request(normalized, cache path)
|
||||
loop frames
|
||||
Loader->>State: request_records_snapshot()
|
||||
Loader->>Resource: load_threaded_get_status(path)
|
||||
Loader->>Finalizer: poll_terminal_requests(state, prototype cache)
|
||||
Finalizer->>State: request_records_snapshot()
|
||||
Finalizer->>Resource: load_threaded_get_status(path)
|
||||
end
|
||||
Loader->>State: complete_request(normalized, terminal status)
|
||||
Finalizer->>State: complete_request(normalized, terminal status)
|
||||
Loader->>Budget: try_consume_permit(M2_MESH_FINALIZE)
|
||||
Loader->>State: pop_finalize_record()
|
||||
Loader->>Resource: load_threaded_get(path)
|
||||
Loader->>Loader: extract/refresh/adopt Mesh or mark missing
|
||||
Loader->>Finalizer: finalize_next_resource(state, caches, directory)
|
||||
Finalizer->>State: pop_finalize_record()
|
||||
Finalizer->>Resource: load_threaded_get(path)
|
||||
Finalizer->>Finalizer: extract/refresh/adopt Mesh or mark missing
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
@@ -135,7 +138,9 @@ sequenceDiagram
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> State[M2MeshLoadPipelineState]
|
||||
Loader --> Resource[ResourceLoader]
|
||||
Loader --> MeshFinalizer[M2MeshResourceFinalizer]
|
||||
MeshFinalizer --> State
|
||||
MeshFinalizer --> Resource[ResourceLoader]
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Loader --> MeshCache[M2 Mesh resource and prototype outcome cache states]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
@@ -148,7 +153,9 @@ flowchart TB
|
||||
|
||||
- Main thread serializes all state mutation and snapshots.
|
||||
- State owns only request/finalize Dictionaries containing Strings and status integers.
|
||||
- Loader owns ResourceLoader request lifetime and drains active paths before shutdown clear.
|
||||
- `M2MeshResourceFinalizer` owns terminal status polling, terminal Resource
|
||||
retrieval, first-Mesh extraction, runtime preparation and cache/missing adoption.
|
||||
- Loader owns request admission, scheduler permits and shutdown drain ordering.
|
||||
- `M2MeshResourceCacheState` owns prepared static Mesh references and
|
||||
`M2RuntimeMeshFinalizer` owns refresh/rebuild/fallback. The loader owns shared
|
||||
adoption decisions, raw I/O and remaining engine resources; the prototype
|
||||
@@ -161,9 +168,9 @@ flowchart TB
|
||||
|---|---|---|---|---|
|
||||
| Empty/duplicate request | State guard | Reject unchanged | Contract verifier | Correct caller/request later |
|
||||
| Request start failure | Loader return code | No state insert; mark missing | Existing loader behavior | Cache/source correction |
|
||||
| Non-terminal status | Loader poll | Keep pending | Existing queue metric | Poll next frame |
|
||||
| Terminal load failure | Status in popped record | Loader marks missing | Existing missing behavior | World/cache reload |
|
||||
| Empty defensive path | Loader before poll | Discard and mark missing | Source regression | Correct request producer |
|
||||
| Non-terminal status | Finalizer poll | Keep pending | Existing queue metric | Poll next frame |
|
||||
| Terminal load failure | Status in popped record | Finalizer marks missing | Existing missing behavior | World/cache reload |
|
||||
| Empty defensive path | Finalizer before poll | Discard and mark missing | Source regression | Correct request producer |
|
||||
| Shutdown | Loader drains pending Resource paths | Clear state | Shutdown verifier | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
@@ -206,7 +213,8 @@ rebuild policy are unchanged; no migration or rebake is required.
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static M2 request/finalize state | Implemented extraction | Contract/source/timing verifier | Asset-backed long traversal pending |
|
||||
| ResourceLoader I/O | Existing loader-owned | Shutdown/material regressions | I/O adapter extraction optional |
|
||||
| Static request admission I/O | Implemented in observer | Observer/shutdown regressions | Asset-backed traversal pending |
|
||||
| Static request polling/finalize I/O | Implemented extraction | Finalizer contract/source/timing verifier | Asset-backed traversal pending |
|
||||
| Mesh cache | Implemented extraction | Mesh resource cache state verifier | Asset-backed memory/leak run pending |
|
||||
| First-Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
|
||||
| Mesh preparation | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
|
||||
@@ -224,11 +232,12 @@ rebuild policy are unchanged; no migration or rebake is required.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending records, terminal FIFO and metrics |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Terminal polling, Resource extraction, preparation and adoption |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared static Mesh references and final clear |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene lifetime |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Missing-model outcome retention |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Cache path choice, I/O polling, permits and adoption decisions |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Cache path choice, request admission, permits and composition |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Downstream stale Mesh rebuild decision |
|
||||
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Lifecycle/boundary/timing regression |
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ flowchart LR
|
||||
IO --> Extract[M2MeshResourceExtractor]
|
||||
Extract --> Prepare[M2RuntimeMeshFinalizer]
|
||||
Prepare --> Cache
|
||||
Loader --> Build[M2 MultiMesh materialization]
|
||||
Loader --> Build[M2StaticBatchMaterializer]
|
||||
```
|
||||
|
||||
The cache may retain `Mesh` resources and copied normalized-path Strings. It has
|
||||
@@ -103,9 +103,9 @@ sequenceDiagram
|
||||
participant Pipeline as M2MeshLoadPipelineState
|
||||
Loader->>Cache: find/has normalized path
|
||||
alt cache miss
|
||||
Loader->>Pipeline: request/poll/finalize record
|
||||
Loader->>Loader: ResourceLoader get; delegate extract + prepare
|
||||
Loader->>Cache: store_mesh(path, prepared Mesh)
|
||||
Loader->>Pipeline: request record
|
||||
Loader->>Finalizer: poll/finalize one permitted record
|
||||
Finalizer->>Cache: store_mesh(path, prepared Mesh)
|
||||
end
|
||||
Cache-->>Loader: exact retained Mesh
|
||||
Loader->>Loader: materialize static M2 batch
|
||||
@@ -118,7 +118,8 @@ flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Cache[M2MeshResourceCacheState]
|
||||
Loader --> Pipeline[M2MeshLoadPipelineState]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> Builder[M2Builder and MultiMesh]
|
||||
Loader --> Builder[M2Builder]
|
||||
Loader --> Materializer[M2StaticBatchMaterializer]
|
||||
Cache --> Mesh[Godot Mesh Resource]
|
||||
Cache -. no dependency .-> Pipeline
|
||||
Cache -. no dependency .-> Classifier
|
||||
@@ -132,7 +133,9 @@ flowchart TB
|
||||
- Borrowed Mesh lookups do not transfer ownership or duplicate resources.
|
||||
- `M2MeshResourceExtractor` owns first-Mesh selection and temporary PackedScene
|
||||
instances. `M2PrototypeCacheState` owns missing/prototype/animated state; the
|
||||
loader owns MultiMeshes and adoption decisions.
|
||||
static materializer owns MultiMesh construction/attachment;
|
||||
`M2MeshResourceFinalizer` owns resource adoption, while the loader owns
|
||||
build-job and scheduler-permit decisions.
|
||||
- The loader drains asynchronous work before the final cache clear.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
@@ -184,7 +187,7 @@ the historical Mesh cache had no queue contribution or log site.
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Prepared static M2 Mesh cache | Implemented extraction | Contract/source/timing verifier | Asset-backed memory/leak run pending |
|
||||
| M2 Mesh request lifecycle | Implemented extraction | Pipeline state verifier | ResourceLoader I/O remains loader-owned |
|
||||
| M2 Mesh request lifecycle | Implemented extraction | Pipeline/finalizer verifiers | Asset-backed traversal pending |
|
||||
| M2 Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
|
||||
| M2 Mesh preparation | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
|
||||
|
||||
@@ -201,8 +204,9 @@ the historical Mesh cache had no queue contribution or log site.
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh references and final clear |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene lifetime |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Prepared Mesh producer and cache adoption |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype references and negative lookup outcomes |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, raw/resource I/O and materialization |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, request admission, permits and materialization |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending request and terminal finalize records |
|
||||
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Cache ownership/lifetime/boundary/timing regression |
|
||||
|
||||
@@ -92,27 +92,27 @@ No state or Resource reference is retained between calls.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as M2MeshResourceFinalizer
|
||||
participant Extractor as M2MeshResourceExtractor
|
||||
participant Scene as PackedScene
|
||||
Loader->>Extractor: extract_first_mesh(loaded Resource)
|
||||
Finalizer->>Extractor: extract_first_mesh(loaded Resource)
|
||||
alt direct Mesh
|
||||
Extractor-->>Loader: same Mesh reference
|
||||
Extractor-->>Finalizer: same Mesh reference
|
||||
else PackedScene
|
||||
Extractor->>Scene: instantiate()
|
||||
Scene-->>Extractor: temporary root
|
||||
Extractor->>Extractor: depth-first first-Mesh search
|
||||
Extractor->>Extractor: temporary_root.free()
|
||||
Extractor-->>Loader: Mesh or null
|
||||
Extractor-->>Finalizer: Mesh or null
|
||||
end
|
||||
Loader->>Loader: prepare and cache Mesh or mark missing
|
||||
Finalizer->>Finalizer: prepare and cache Mesh or mark missing
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Extractor[M2MeshResourceExtractor]
|
||||
Finalizer[M2MeshResourceFinalizer] --> Extractor[M2MeshResourceExtractor]
|
||||
Extractor --> Types[Resource / PackedScene / Node / Mesh]
|
||||
Loader --> Pipeline[M2MeshLoadPipelineState]
|
||||
Loader --> Cache[M2MeshResourceCacheState]
|
||||
@@ -168,8 +168,8 @@ normalized M2 path. Its verifier measures traversal time and temporary Node coun
|
||||
|
||||
## Extension points
|
||||
|
||||
- `M2RuntimeMeshFinalizer` consumes the Mesh returned here and the loader stores
|
||||
its result in `M2MeshResourceCacheState`.
|
||||
- `M2MeshResourceFinalizer` passes the Mesh to `M2RuntimeMeshFinalizer` and
|
||||
stores its result in `M2MeshResourceCacheState`.
|
||||
- Broader generic scene traversal is intentionally excluded until another real
|
||||
consumer requires the same exact contract.
|
||||
|
||||
@@ -194,8 +194,9 @@ normalized M2 path. Its verifier measures traversal time and temporary Node coun
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh traversal and temporary PackedScene lifetime |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Terminal Resource consumer and extraction caller |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and missing/static-only lookup state |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | ResourceLoader/raw I/O, cache decisions and materialization |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Request admission, permits and materialization |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh retention |
|
||||
| `src/tools/verify_m2_mesh_resource_extractor.gd` | Resource/order/lifetime/boundary/timing regression |
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
# M2 Mesh Resource Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-MESH-RESOURCE-FINALIZER-001` |
|
||||
| Owners | Static M2 terminal Resource I/O, Mesh preparation and cache outcome |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-resource-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing cached/static MultiMesh M2 path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Drain static M2 ResourceLoader work and publish a prepared Mesh or missing-model
|
||||
outcome without keeping terminal I/O and refresh logic in `StreamingWorldLoader`.
|
||||
The same preparation method serves threaded and synchronous Mesh consumers.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Select cache candidates or admit threaded ResourceLoader requests.
|
||||
- Own scheduler permits, prototype Nodes, MultiMeshes or SceneTree roots.
|
||||
- Change first-Mesh traversal, refresh version, rebuild rules or raw parser.
|
||||
- Merge static and animated terminal services through callbacks/frameworks.
|
||||
- Change cache formats, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Observer[M2StaticBuildResourceObserver] --> Pipeline[M2MeshLoadPipelineState]
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2MeshResourceFinalizer]
|
||||
Pipeline --> Finalizer
|
||||
ResourceLoader --> Finalizer
|
||||
Finalizer --> Extractor[M2MeshResourceExtractor]
|
||||
Finalizer --> Runtime[M2RuntimeMeshFinalizer]
|
||||
Finalizer --> Raw[M2RawModelRepository]
|
||||
Finalizer --> MeshCache[M2MeshResourceCacheState]
|
||||
Finalizer --> Prototype[M2PrototypeCacheState]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `poll_terminal_requests(pipeline, prototype_cache)` | I/O command | Move LOADED/FAILED records into completion FIFO | Invalid composition returns zero |
|
||||
| `finalize_next_resource(pipeline, mesh_cache, prototype_cache, extracted_dir)` | I/O command | Pop one record and publish Mesh/missing outcome | Empty/invalid composition returns false |
|
||||
| `prepare_mesh_for_runtime(path, mesh, extracted_dir)` | I/O preparation | Preserve current Mesh or load raw data and refresh stale Mesh | Null returns null |
|
||||
| `load_threaded_get_status(path)` | I/O adapter | Production status query with injectable test seam | Returns ResourceLoader status |
|
||||
| `load_threaded_get(path)` | I/O adapter | Production terminal Resource retrieval with injectable test seam | May return null |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Pending/finalize records | Mesh pipeline | Finalizer | Pipeline-owned copied Dictionaries |
|
||||
| Input | Terminal Resource | ResourceLoader | Mesh extractor | Borrowed one operation |
|
||||
| Input | Extracted/cached Mesh | Mesh extractor/loader consumer | Runtime preparation | Borrowed Resource reference |
|
||||
| Input | Raw M2 Dictionary | Raw repository | Runtime finalizer | Call-local value |
|
||||
| Output | Prepared Mesh | Runtime finalizer | Mesh cache/loader consumer | Resource reference |
|
||||
| Output | Missing-model outcome | Finalizer | Prototype cache | Copied path; loader session |
|
||||
| Output | Processed-record flag | Finalizer | Loader permit loop/tests | Boolean value |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Poll[Poll pending records in insertion order] --> Path{Resource path empty?}
|
||||
Path -->|yes| Discard[Discard and mark missing]
|
||||
Path -->|no| Status{LOADED or FAILED?}
|
||||
Status -->|no| Pending[Keep pending]
|
||||
Status -->|yes| Complete[Append completion FIFO]
|
||||
Complete --> Permit[Loader consumes M2_MESH_FINALIZE permit]
|
||||
Permit --> Pop[Pop oldest terminal record]
|
||||
Pop --> Cached{Empty path or Mesh cached?}
|
||||
Cached -->|yes| Skip[Finish one permit operation]
|
||||
Cached -->|no| Loaded{Status LOADED?}
|
||||
Loaded -->|no| Missing[Mark model missing]
|
||||
Loaded -->|yes| Get[Get terminal Resource]
|
||||
Get --> Extract[Extract first Mesh]
|
||||
Extract --> Found{Mesh found?}
|
||||
Found -->|no| Missing
|
||||
Found -->|yes| Current{Refresh version current?}
|
||||
Current -->|yes| Store[Store exact Mesh]
|
||||
Current -->|no| Raw[Read raw M2 data]
|
||||
Raw --> Refresh[Finalize/rebuild/fallback Mesh]
|
||||
Refresh --> Store
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Polling
|
||||
Polling --> Pending: nonterminal status
|
||||
Polling --> TerminalQueued: LOADED or FAILED
|
||||
Polling --> Missing: empty Resource path
|
||||
TerminalQueued --> Skipped: empty/cached record
|
||||
TerminalQueued --> Missing: failed load or no Mesh
|
||||
TerminalQueued --> Extracted: Mesh found
|
||||
Extracted --> Current: refresh version current
|
||||
Extracted --> Refreshing: stale Mesh
|
||||
Refreshing --> Prepared: rebuild or fallback
|
||||
Current --> Cached
|
||||
Prepared --> Cached
|
||||
Pending --> [*]
|
||||
Skipped --> [*]
|
||||
Missing --> [*]
|
||||
Cached --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant F as M2MeshResourceFinalizer
|
||||
participant P as M2MeshLoadPipelineState
|
||||
participant R as ResourceLoader
|
||||
participant E as M2MeshResourceExtractor
|
||||
participant N as M2RuntimeMeshFinalizer
|
||||
participant C as M2MeshResourceCacheState
|
||||
L->>F: poll_terminal_requests(P, prototype cache)
|
||||
F->>R: load_threaded_get_status(path)
|
||||
F->>P: complete terminal request
|
||||
L->>L: consume M2_MESH_FINALIZE permit
|
||||
L->>F: finalize_next_resource(...)
|
||||
F->>P: pop oldest record
|
||||
F->>R: load_threaded_get(path)
|
||||
F->>E: extract_first_mesh(Resource)
|
||||
F->>N: requires_raw_data_for_refresh(Mesh)
|
||||
opt stale Mesh
|
||||
F->>F: read raw M2 data
|
||||
F->>N: finalize_mesh(...)
|
||||
end
|
||||
F->>C: store prepared Mesh
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Finalizer[M2MeshResourceFinalizer] --> ResourceLoader
|
||||
Finalizer --> Pipeline[M2MeshLoadPipelineState]
|
||||
Finalizer --> Extractor[M2MeshResourceExtractor]
|
||||
Finalizer --> Runtime[M2RuntimeMeshFinalizer]
|
||||
Finalizer --> Raw[M2RawModelRepository]
|
||||
Finalizer --> MeshCache[M2MeshResourceCacheState]
|
||||
Finalizer --> Prototype[M2PrototypeCacheState]
|
||||
Loader[StreamingWorldLoader] --> Finalizer
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
Finalizer -. no dependency .-> Scheduler
|
||||
Finalizer -. no dependency .-> MultiMesh
|
||||
Finalizer -. no dependency .-> SceneTree
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- All operations run synchronously on the renderer main thread.
|
||||
- Pipeline owns pending/completion records; one permit pops one terminal record.
|
||||
- Extractor owns temporary PackedScene instance cleanup and returns a Mesh reference.
|
||||
- Runtime finalizer owns stale/current/rebuild/fallback rules; raw repository owns
|
||||
only call-local native I/O.
|
||||
- Mesh cache retains prepared Mesh references until final shutdown; prototype
|
||||
cache retains missing-model paths.
|
||||
- Service retains dependency references but no pending record, Resource, Mesh or
|
||||
raw Dictionary between calls. Loader retains permits and materialization.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Missing composition dependency | Return zero/false; preparation keeps Mesh where possible | Correct composition |
|
||||
| Empty Resource path | Discard request and mark missing | Repair cache and restart session |
|
||||
| Nonterminal status | Keep pending | Poll next tick |
|
||||
| FAILED status | Pop and mark missing | Repair cache and restart session |
|
||||
| Existing cached Mesh | Pop and skip terminal Resource get | None |
|
||||
| Null/unsupported/no-Mesh Resource | Mark missing | Repair imported cache |
|
||||
| Raw read/rebuild unavailable | Runtime finalizer marks/reuses original Mesh | Existing fallback |
|
||||
| Tile cancellation | Shared cache work continues | Loader queue remains authoritative |
|
||||
| Shutdown | Loader drains ResourceLoader before pipeline/cache clear | New loader starts empty |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The service consumes the existing extracted directory. The scheduler retains
|
||||
`m2_mesh_finalize_ops_per_tick`; material refresh version remains `2`.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No cache path, schema or version changes are introduced. Existing `.tscn/.glb`
|
||||
Resources and in-memory refresh metadata remain compatible; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
The service adds no log. Existing pending-plus-finalize metrics and refresh
|
||||
metadata remain unchanged. Normalized relative path remains the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated verifier covers status insertion order, empty-path discard, FIFO,
|
||||
failed/cached/null outcomes, terminal get, exact Mesh identity, stale-only raw
|
||||
read, exact finalizer arguments, cache adoption, source ownership and timing.
|
||||
- Pipeline/cache/extractor/runtime-finalizer/raw-repository/prototype/shutdown,
|
||||
facade, internal-access and baseline regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact lifecycle/I/O extraction only; no private asset,
|
||||
original-client visual, leak-pressure or p95/p99 claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
Further extraction can move static prototype/material-source construction without
|
||||
changing this terminal Resource contract. Animated finalization remains separate
|
||||
because its candidate-before-material ordering and Node ownership differ.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static terminal polling/load | Implemented extraction | Status/FIFO/load/source verifier | Asset-backed traversal pending |
|
||||
| Mesh extraction/preparation/cache outcome | Implemented extraction | Identity/raw/finalizer/store verifier | Private leak/p95/p99 pending |
|
||||
| Static MultiMesh materialization | Separate implemented service | Materializer verifier | GPU/asset-backed evidence pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Terminal Resource get, extraction and stale rebuild remain synchronous main-thread work.
|
||||
- Prepared Mesh cache remains unbounded until final shutdown.
|
||||
- Private asset traversal, descriptor pressure, leak and p95/p99 evidence is absent.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Poll/load/extract/prepare/cache/missing service |
|
||||
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending and completion FIFO ownership |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh and temporary-instance ownership |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback rules |
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Optional native raw M2 boundary |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh ownership |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Missing-model outcome ownership |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Permit loop and materialization composition |
|
||||
| `src/tools/verify_m2_mesh_resource_finalizer.gd` | Polling/preparation/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md)
|
||||
- [`m2-runtime-mesh-finalizer.md`](m2-runtime-mesh-finalizer.md)
|
||||
- [`m2-raw-model-repository.md`](m2-raw-model-repository.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)
|
||||
@@ -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)
|
||||
@@ -28,7 +28,9 @@ paths and paths whose animation fallback is static-only.
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> State[M2PrototypeCacheState]
|
||||
Observer[M2 resource observers] --> State
|
||||
Resource[ResourceLoader / raw builders] --> Loader
|
||||
Resource --> Observer
|
||||
State --> Static[Static prototype Node3D]
|
||||
State --> Animated[Animated prototype Node3D]
|
||||
State --> Missing[Missing-model path set]
|
||||
@@ -62,7 +64,7 @@ Mesh traversal and other application layers are forbidden.
|
||||
| Input | Normalized non-empty M2 path | Loader normalization | Cache state | Copied String key | Shutdown lifetime |
|
||||
| Input | Detached static Node3D | Cached scene/raw builder adapter | Cache state | Adopted on success | Until final shutdown |
|
||||
| Input | Detached animated Node3D | GLB/native animation adapter | Cache state | Adopted on success | Until final shutdown |
|
||||
| Input | Missing/static-only outcome | Loader failure/fallback adapter | Cache state | Boolean set entry | Until final shutdown |
|
||||
| Input | Missing/static-only outcome | Resource observers/finalizers | Cache state | Boolean set entry | Until final shutdown |
|
||||
| Output | Canonical prototype Node3D | Cache state | Loader instance/material adapter | Borrowed exact reference | One lookup/use |
|
||||
| Output | Detached path-only snapshot | Cache state | Tests/diagnostics | Fresh caller-owned arrays | One query |
|
||||
|
||||
@@ -135,11 +137,12 @@ sequenceDiagram
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> State[M2PrototypeCacheState]
|
||||
Observer[M2 resource observers] --> State
|
||||
State --> Node3D
|
||||
Loader --> Raw[M2RawModelRepository]
|
||||
Loader --> ResourceLoader
|
||||
Observer --> ResourceLoader
|
||||
Loader --> StaticBuilder[M2Builder]
|
||||
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
|
||||
Observer --> AnimatedBuilder[M2NativeAnimatedBuilder]
|
||||
State -. no dependency .-> Raw
|
||||
State -. no dependency .-> ResourceLoader
|
||||
State -. no dependency .-> StaticBuilder
|
||||
@@ -165,7 +168,7 @@ flowchart TB
|
||||
| Unknown positive lookup | Map miss | Return null | Path snapshot | Loader continues existing load/fallback |
|
||||
| Duplicate candidate | Occupied valid path | Release candidate; return first | Identity fixture | None required |
|
||||
| Model source/load failure | Loader result | Mark missing outcome | Existing fallback behavior | New loader session/source repair |
|
||||
| Animation unavailable/unsafe | Loader policy/result | Mark static-only outcome | Existing fallback behavior | New loader session/cache repair |
|
||||
| Animation unavailable/unsafe | Observer/native result | Mark static-only outcome | Existing fallback behavior | New loader session/cache repair |
|
||||
| Shutdown | Loader lifecycle | Release positive and clear all state | Shutdown verifier | New loader begins empty |
|
||||
| Cancellation | Not owned | No state transition inside this service | N/A | Loader drains/cancels before shutdown clear |
|
||||
|
||||
@@ -177,8 +180,8 @@ flowchart TB
|
||||
| Negative cache lifetime | Final loader shutdown | All | No | Preserves historical fallback suppression |
|
||||
| Eviction capacity | Unbounded historical behavior | All | No | No mid-session Node destruction |
|
||||
|
||||
Animation enablement, candidate/allow/deny rules, cache paths and per-frame
|
||||
permits remain loader configuration.
|
||||
Animation enablement, candidate/allow/deny rules and cache paths remain loader
|
||||
configuration consumed by the cached observer. Per-frame permits remain loader-owned.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
@@ -188,7 +191,10 @@ and native M2 formats are unchanged; no migration or rebake is introduced.
|
||||
## Diagnostics and observability
|
||||
|
||||
- `diagnostic_snapshot` exposes four sorted path arrays without Node references.
|
||||
- Existing `M2_ANIM_CACHE` and native animation logs remain loader-owned.
|
||||
- Cached eligibility rejection logging belongs to the cached observer; native
|
||||
success logging belongs to the native observer and cached terminal success
|
||||
logging belongs to the animation resource finalizer. Static terminal missing
|
||||
outcomes are produced by `M2MeshResourceFinalizer`.
|
||||
- Existing renderer queue metrics remain unchanged because these tables never
|
||||
contributed work counts.
|
||||
- Normalized relative path remains the correlation key.
|
||||
@@ -208,8 +214,8 @@ and native M2 formats are unchanged; no migration or rebake is introduced.
|
||||
## Extension points
|
||||
|
||||
Eviction or byte/count budgets require measured memory evidence and explicit
|
||||
prototype-user lifetime rules. Animation request-state extraction can consume
|
||||
this service without moving ResourceLoader or builder ownership into it.
|
||||
prototype-user lifetime rules. Cached and native animation observers consume
|
||||
this state without moving ResourceLoader or builder ownership into cache state.
|
||||
|
||||
## Capability status
|
||||
|
||||
@@ -233,13 +239,18 @@ this service without moving ResourceLoader or builder ownership into it.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Positive Node ownership, negative path state and shutdown release |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, I/O/build/fallback decisions and cache adapters |
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Animated raw/build and positive/static-only transitions |
|
||||
| `src/render/m2/m2_animation_resource_finalizer.gd` | Cached terminal positive/static-only transitions |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Static terminal missing transitions |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, observer order, remaining I/O/fallback adapters |
|
||||
| `src/tools/verify_m2_prototype_cache_state.gd` | Admission/identity/lifecycle/source/timing regression |
|
||||
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Integrated final-shutdown release regression |
|
||||
|
||||
## 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)
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
|
||||
@@ -27,11 +27,15 @@ static or animated Dictionary consumed by existing builders and classifiers.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
|
||||
MeshFinalizer[M2MeshResourceFinalizer] --> Repository[M2RawModelRepository]
|
||||
Loader[StreamingWorldLoader] --> Repository
|
||||
Observer[M2NativeAnimationResourceObserver] --> Repository
|
||||
Repository --> File[Extracted M2 file]
|
||||
Repository --> Native[ClassDB M2Loader]
|
||||
Native --> Raw[Raw Dictionary]
|
||||
Raw --> MeshFinalizer
|
||||
Raw --> Loader
|
||||
Raw --> Observer
|
||||
Loader --> Builder[Existing M2 builders/finalizer]
|
||||
```
|
||||
|
||||
@@ -53,8 +57,8 @@ renderer policy and other application layers are forbidden.
|
||||
| Input | Extracted directory String | Loader configuration | Repository path resolution | Copied value | One call |
|
||||
| Input | Already-normalized relative M2 path String | Loader normalization | Repository | Copied value | One call |
|
||||
| Input | Extracted `.m2` bytes | Local legal extraction | Native M2Loader | File-owned | Native call |
|
||||
| Output | Static raw M2 Dictionary | Native `load_m2` | Loader/finalizer/M2Builder | Fresh native result | One caller operation |
|
||||
| Output | Animated raw M2 Dictionary | Native `load_m2_animated` | Loader/animated builder | Fresh native result | One caller operation |
|
||||
| Output | Static raw M2 Dictionary | Native `load_m2` | Mesh resource finalizer/M2Builder | Fresh native result | One caller operation |
|
||||
| Output | Animated raw M2 Dictionary | Native `load_m2_animated` | Native animation observer/builder | Fresh native result | One caller operation |
|
||||
| Output | Empty Dictionary | Repository guards | Loader fallback and prototype outcome adapter | Fresh value | One failed call |
|
||||
|
||||
Side effects are limited to file-existence inspection, synchronous native file
|
||||
@@ -98,18 +102,18 @@ No request, result or failure state survives a call.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Caller as Loader or NativeAnimationObserver
|
||||
participant Repo as M2RawModelRepository
|
||||
participant File as FileAccess
|
||||
participant Native as M2Loader
|
||||
Loader->>Repo: load static/animated(directory, path)
|
||||
Caller->>Repo: load static/animated(directory, path)
|
||||
Repo->>File: file_exists(globalized joined path)
|
||||
alt dependency or file unavailable
|
||||
Repo-->>Loader: empty Dictionary
|
||||
Repo-->>Caller: empty Dictionary
|
||||
else available
|
||||
Repo->>Native: instantiate and call exact native method
|
||||
Native-->>Repo: Variant
|
||||
Repo-->>Loader: Dictionary or empty Dictionary
|
||||
Repo-->>Caller: Dictionary or empty Dictionary
|
||||
end
|
||||
```
|
||||
|
||||
@@ -118,13 +122,14 @@ sequenceDiagram
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
|
||||
Observer[M2NativeAnimationResourceObserver] --> Repository
|
||||
Repository --> ProjectSettings
|
||||
Repository --> FileAccess
|
||||
Repository --> ClassDB
|
||||
ClassDB --> Native[M2Loader extension]
|
||||
Loader --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> StaticBuilder[M2Builder]
|
||||
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
|
||||
Observer --> AnimatedBuilder[M2NativeAnimatedBuilder]
|
||||
Repository -. no dependency .-> Finalizer
|
||||
Repository -. no dependency .-> StaticBuilder
|
||||
Repository -. no dependency .-> Cache[Renderer caches/queues]
|
||||
@@ -133,7 +138,9 @@ flowchart TB
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The repository owns only call-local path, native instance and result values.
|
||||
- The loader owns path normalization and fallback selection;
|
||||
- The loader owns path normalization and fallback selection; the Mesh resource
|
||||
finalizer owns static refresh reads, while the native observer owns the
|
||||
animated raw-read/build decision;
|
||||
`M2PrototypeCacheState` owns prototype/negative adoption.
|
||||
- Native `M2Loader` owns parsing behavior and returns a new Dictionary value.
|
||||
- Calls are synchronous on the caller's thread; current renderer callers use the
|
||||
@@ -211,6 +218,8 @@ measured work packages rather than expansion of this repository.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Stateless native class/file/method boundary |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Static refresh repository consumer |
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Animated raw-data consumer and builder adapter |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and negative-result retention |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Path normalization, fallback decisions and result consumers |
|
||||
| `src/native/src/m2_loader.cpp` | Native static/animated parsing implementation |
|
||||
|
||||
@@ -27,9 +27,8 @@ UV-rotation cases and returning the historical original-Mesh fallback.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Extractor[M2MeshResourceExtractor] --> Loader[StreamingWorldLoader]
|
||||
Loader -->|is raw data required?| Finalizer[M2RuntimeMeshFinalizer]
|
||||
Loader --> Raw[M2RawModelRepository]
|
||||
ResourceFinalizer[M2MeshResourceFinalizer] -->|is raw data required?| Finalizer[M2RuntimeMeshFinalizer]
|
||||
ResourceFinalizer --> Raw[M2RawModelRepository]
|
||||
Raw --> Finalizer
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Finalizer --> Builder[M2Builder]
|
||||
@@ -54,8 +53,8 @@ Nodes outside temporary rebuild roots and other application layers are forbidden
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Extracted Mesh and normalized M2 path | Loader/extractor adapter | Finalizer | Borrowed Mesh reference/String copy | One call |
|
||||
| Input | Already-loaded raw M2 Dictionary | `M2RawModelRepository` through loader adapter | Classifier/M2Builder | Caller-owned value container | One call |
|
||||
| Input | Extracted Mesh and normalized M2 path | Mesh resource finalizer | Finalizer | Borrowed Mesh reference/String copy | One call |
|
||||
| Input | Already-loaded raw M2 Dictionary | `M2RawModelRepository` through resource finalizer | Classifier/M2Builder | Caller-owned value container | One call |
|
||||
| Input | Extracted directory path | Loader configuration | M2Builder texture resolution | Copied String | One rebuild |
|
||||
| Output | Original or rebuilt current Mesh | Finalizer | Cache/prototype adapter | Borrowed/new Resource reference | Cache may retain |
|
||||
|
||||
@@ -101,24 +100,24 @@ map/reset clear site calls `clear()`.
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant ResourceFinalizer as M2MeshResourceFinalizer
|
||||
participant Finalizer as M2RuntimeMeshFinalizer
|
||||
participant Raw as M2Loader boundary
|
||||
participant Classifier as RebuildClassifier
|
||||
participant Builder as M2Builder
|
||||
Loader->>Finalizer: requires_raw_data_for_refresh(mesh)
|
||||
ResourceFinalizer->>Finalizer: requires_raw_data_for_refresh(mesh)
|
||||
alt current Mesh
|
||||
Finalizer-->>Loader: false; reuse Mesh
|
||||
Finalizer-->>ResourceFinalizer: false; reuse Mesh
|
||||
else stale Mesh
|
||||
Finalizer-->>Loader: true
|
||||
Loader->>Raw: load static raw Dictionary
|
||||
Loader->>Finalizer: finalize_mesh(path, mesh, raw, extracted dir)
|
||||
Finalizer-->>ResourceFinalizer: true
|
||||
ResourceFinalizer->>Raw: load static raw Dictionary
|
||||
ResourceFinalizer->>Finalizer: finalize_mesh(path, mesh, raw, extracted dir)
|
||||
Finalizer->>Classifier: needs_runtime_mesh_rebuild(path, raw)
|
||||
opt rebuild required
|
||||
Finalizer->>Builder: build(raw, extracted dir)
|
||||
Finalizer->>Finalizer: extract first Mesh; free prototype
|
||||
end
|
||||
Finalizer-->>Loader: rebuilt or marked fallback Mesh
|
||||
Finalizer-->>ResourceFinalizer: rebuilt or marked fallback Mesh
|
||||
end
|
||||
```
|
||||
|
||||
@@ -126,12 +125,12 @@ sequenceDiagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
ResourceFinalizer[M2MeshResourceFinalizer] --> Finalizer[M2RuntimeMeshFinalizer]
|
||||
Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier]
|
||||
Finalizer --> Extractor[M2MeshResourceExtractor]
|
||||
Finalizer --> Builder[M2Builder]
|
||||
Loader --> Raw[M2RawModelRepository]
|
||||
Loader --> Cache[M2MeshResourceCacheState]
|
||||
ResourceFinalizer --> Raw[M2RawModelRepository]
|
||||
ResourceFinalizer --> Cache[M2MeshResourceCacheState]
|
||||
Finalizer -. no dependency .-> Raw
|
||||
Finalizer -. no dependency .-> Cache
|
||||
```
|
||||
@@ -139,7 +138,8 @@ flowchart TB
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main thread executes metadata changes and M2Builder work.
|
||||
- `M2RawModelRepository` owns raw M2 file/native calls; loader supplies its value result.
|
||||
- `M2RawModelRepository` owns raw M2 file/native calls; the Mesh resource
|
||||
finalizer supplies its value result.
|
||||
- Finalizer owns classifier memoization and temporary rebuild prototype lifetime.
|
||||
- M2Builder owns construction rules; extractor selects the first rebuilt Mesh.
|
||||
- Cache/prototype adapters decide where the returned Mesh reference is retained.
|
||||
@@ -208,7 +208,8 @@ logs/queue metrics are unchanged. Normalized M2 path remains the correlation key
|
||||
|---|---|
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh version, classification, rebuild and fallback |
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Raw-file/native I/O and Dictionary result |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Repository call and returned-Mesh adoption |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Repository call and returned-Mesh adoption |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Service composition, permits and materialization |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Memoized billboard/UV-rotation predicate |
|
||||
| `src/render/m2/m2_mesh_resource_extractor.gd` | First rebuilt-Mesh selection |
|
||||
| `src/tools/verify_m2_runtime_mesh_finalizer.gd` | Transition/rebuild/boundary/timing regression |
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
# M2 Static Batch Materializer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M03 / `M03-RND-M2-STATIC-BATCH-MATERIALIZER-001` |
|
||||
| Owners | Static M2 MultiMesh construction, render settings and attachment |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-static-batch-materializer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing static M2 placement path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Build and attach one static M2 `MultiMeshInstance3D` from an already prepared
|
||||
Mesh and an ordered transform slice. The service preserves the existing
|
||||
transform format, Mesh identity, batch name, visibility and shadow behavior.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Group placements, size batches or advance build-job cursors.
|
||||
- Load, refresh, build or cache Mesh resources.
|
||||
- Consume render permits, rotate queues or decide retries/cancellation.
|
||||
- Assign Editor owners or release tile roots.
|
||||
- Materialize animated instances or introduce spatial-cell batching.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Groups[Ordered transform group] --> Loader[StreamingWorldLoader build adapter]
|
||||
MeshCache[Prepared Mesh cache] --> Loader
|
||||
Planner[M2BuildBatchPlanner] --> Slice[Start and count]
|
||||
Slice --> Loader
|
||||
Loader --> Materializer[M2StaticBatchMaterializer]
|
||||
Materializer --> Batch[Attached MultiMeshInstance3D]
|
||||
Loader --> EditorOwner[Editor owner adapter]
|
||||
```
|
||||
|
||||
Allowed dependencies are Godot `Mesh`, `MultiMesh`, `MultiMeshInstance3D`,
|
||||
`GeometryInstance3D` and main-thread SceneTree APIs. Files, ResourceLoader,
|
||||
workers, RenderingServer calls, caches, build jobs, scheduler policy, gameplay,
|
||||
network and Editor ownership are forbidden.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `materialize_batch(parent, path, mesh, transforms, start, count, serial, visibility_end, visibility_margin, cast_shadows)` | Command/query | Create, configure and attach one static MultiMesh batch | Main thread; borrowed inputs, parent owns result | Invalid/empty input returns null; transform bounds are a caller precondition |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | M2 parent root | Loader build job | Batch attachment | Borrowed Node3D | Tile/job lifetime |
|
||||
| Input | Relative model path and serial | Loader build adapter | Batch node name | Copied values | One call |
|
||||
| Input | Prepared static Mesh | M2 Mesh resource cache/finalizer | MultiMesh | Borrowed Resource; exact reference retained by MultiMesh | Cache and batch lifetimes |
|
||||
| Input | Ordered transforms, start and count | Placement group and batch plan | MultiMesh transform buffer | Borrowed Array/scalars | One call |
|
||||
| Input | Visibility end/margin and shadow flag | Renderer configuration | MultiMeshInstance3D properties | Copied scalars | One call |
|
||||
| Output | Attached `MultiMeshInstance3D` | Materializer | Parent SceneTree and loader Editor-owner adapter | Parent-owned node/resource | Tile lifetime |
|
||||
|
||||
Side effects are MultiMesh allocation, transform-buffer mutation,
|
||||
MultiMeshInstance3D allocation/configuration and SceneTree attachment. The
|
||||
service retains no direct reference after return.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Validate[Validate parent, Mesh, transforms and count] --> MultiMesh[Allocate TRANSFORM_3D MultiMesh]
|
||||
MultiMesh --> MeshIdentity[Assign exact prepared Mesh]
|
||||
MeshIdentity --> Count[Set instance count]
|
||||
Count --> Upload[Upload transforms start plus ordered batch offset]
|
||||
Upload --> Node[Create named MultiMeshInstance3D]
|
||||
Node --> Render[Apply shadow and optional visibility end/margin]
|
||||
Render --> Attach[Attach once to supplied parent]
|
||||
Attach --> Return[Return attached node]
|
||||
Validate -->|invalid| Null[Return null without attachment]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Validating
|
||||
Validating --> Rejected: invalid or empty input
|
||||
Validating --> Building: valid slice
|
||||
Building --> Configured: transforms and render properties set
|
||||
Configured --> Attached: parent add_child
|
||||
Rejected --> [*]
|
||||
Attached --> [*]: parent/tile owner releases subtree
|
||||
```
|
||||
|
||||
The `RefCounted` service is stateless; states describe a single call.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant P as M2BuildBatchPlanner
|
||||
participant M as M2StaticBatchMaterializer
|
||||
participant R as M2 parent root
|
||||
L->>P: plan_batch(transform count, offset, static limits)
|
||||
P-->>L: start/count and cursor transition
|
||||
L->>L: resolve prepared Mesh or rotate pending job
|
||||
L->>M: materialize_batch(parent, Mesh, transform slice, render settings)
|
||||
M->>M: allocate MultiMesh and upload ordered transforms
|
||||
M->>R: add_child(MultiMeshInstance3D)
|
||||
M-->>L: attached node
|
||||
L->>L: assign optional Editor owner
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Materializer[M2StaticBatchMaterializer]
|
||||
Planner[M2BuildBatchPlanner] --> Loader
|
||||
MeshState[M2MeshResourceCacheState] --> Loader
|
||||
Materializer --> Engine[Mesh / MultiMesh / MultiMeshInstance3D]
|
||||
Loader --> EditorOwner[Editor ownership]
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
Materializer -. no dependency .-> ResourceIO[ResourceLoader / files]
|
||||
Materializer -. no dependency .-> BuildState[Build jobs / queues / caches]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The caller borrows a prepared Mesh and transform Array for the synchronous call.
|
||||
- The new MultiMesh retains the exact Mesh Resource reference.
|
||||
- The new MultiMeshInstance3D retains the MultiMesh and transfers to the parent.
|
||||
- Parent/tile teardown owns node and Resource release through the SceneTree.
|
||||
- Loader assigns an Editor owner after successful attachment when configured.
|
||||
- All Resource and SceneTree mutation is main-thread-only.
|
||||
- No RID is acquired or released directly by this service.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null parent or Mesh | Entry guard | Return null without allocation/attachment | Synthetic contract fixture | Correct build composition/cache state |
|
||||
| Empty transforms or non-positive count | Entry guard | Return null | Synthetic contract fixture | Planner supplies non-empty slice |
|
||||
| Invalid transform bounds | Caller precondition | Historical indexed access behavior remains | Build planner/source regression | Repair job cursor/group state |
|
||||
| Mesh pending | Outside service | Loader rotates job without calling service | Existing build/cache diagnostics | Async completion retries job |
|
||||
| Tile cancelled | Outside service | Loader cancels before materialization or releases parent later | Shutdown regression | Eligible tile may requeue |
|
||||
|
||||
The service does not swallow invalid bounds or silently clamp/reorder transforms,
|
||||
because doing so would change the accepted build-planner contract.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
||||
|---|---|---|---|---|
|
||||
| Transform format | `MultiMesh.TRANSFORM_3D` | All | No | Preserves full 3D M2 placements |
|
||||
| Static batch count | Loader `m2_multimesh_batch_size` through planner | Quality preset | Yes, between plans | Controls per-batch transform count |
|
||||
| Visibility end/margin | Loader M2 range / chunk size | Quality preset | Yes, between calls | Applied only when end is positive |
|
||||
| Shadow mode | Loader `m2_cast_shadows` | Quality preset | Yes, between calls | Selects ON/OFF casting setting |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No state is serialized and no cache/schema version changes. The service consumes
|
||||
the accepted prepared Mesh cache contract; no rebake or migration is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- The service emits no logs, metrics or debug views.
|
||||
- Existing loader queue/hitch metrics continue to account for `m2build` work.
|
||||
- Path/serial remain visible through the unchanged batch node name.
|
||||
- Editor ownership remains observable through the existing loader adapter.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_m2_static_batch_materializer.gd` covers invalid inputs, node naming,
|
||||
exact Mesh identity, transform format/count, ordered slice source contract,
|
||||
visibility, shadows, single attachment, ownership boundaries and timing.
|
||||
- Build planner, Mesh cache/extractor/finalizer, prototype, animated materializer,
|
||||
shutdown, materials, facade and internal-access regressions protect neighbors.
|
||||
- The headless dummy renderer does not round-trip per-instance transforms through
|
||||
`MultiMesh.get_instance_transform`; exact ordered upload is protected by the
|
||||
source contract and render checkpoints remain the observable regression gate.
|
||||
- Fidelity evidence is exact extraction only; no new build-12340 parity claim.
|
||||
- Performance budget: 10,000 transform API writes under one second in headless.
|
||||
|
||||
## Extension points
|
||||
|
||||
Measured spatial-cell batching may later provide smaller transform slices while
|
||||
reusing this materializer. A generic static/animated base class, async mutation
|
||||
and callback framework are intentionally excluded.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Static M2 MultiMesh materialization | Implemented extraction | Synthetic node/resource/render/source/timing verifier | Asset-backed traversal pending |
|
||||
| Transform ordering | Implemented extraction | Exact source contract and existing checkpoints | Headless per-instance getter unavailable |
|
||||
| Editor persistence | Existing loader-owned | Source boundary and editor helper | Editor integration fixture remains separate |
|
||||
| Spatial-cell batching | Planned | Renderer roadmap | Culling/performance design and evidence pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Materialization remains synchronous main-thread work under the existing permit.
|
||||
- MultiMesh transform upload timing in a headless dummy renderer does not measure GPU cost.
|
||||
- No private asset traversal, visual comparison, descriptor-pressure, leak or p95/p99 run exists.
|
||||
- Current model-path batching is not culling-driven spatial-cell batching.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_static_batch_materializer.gd` | Static MultiMesh allocation, transform upload, render setup and attachment |
|
||||
| `src/render/m2/m2_build_batch_planner.gd` | Static/animated batch count and cursor planning |
|
||||
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared static Mesh reference ownership |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Resource readiness, job/budget adapter and Editor ownership |
|
||||
| `src/tools/verify_m2_static_batch_materializer.gd` | Node/resource/render/source/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.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)
|
||||
@@ -0,0 +1,211 @@
|
||||
# M2 Static Build Resource Observer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001` |
|
||||
| Owners | Static build Mesh lookup, request selection and missing transition |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-static-build-resource-observer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Existing static MultiMesh M2 build path |
|
||||
|
||||
## Purpose
|
||||
|
||||
Produce the static phase of `M2BuildResourceSnapshot`: reuse a prepared Mesh,
|
||||
wait for an existing request, start the first eligible cache request, or record
|
||||
a terminal missing model while preserving historical path and GLB rules.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Observe animated/native prototypes or finalize threaded loads.
|
||||
- Own/free Meshes, Nodes, cache entries or snapshot lifetime.
|
||||
- Materialize instances, plan batches, rotate queues or consume permits.
|
||||
- Change cache format, candidate order, profiles or visible output.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> Observer[M2StaticBuildResourceObserver]
|
||||
MeshCache[M2MeshResourceCacheState] --> Observer
|
||||
Missing[M2PrototypeCacheState] --> Observer
|
||||
Pipeline[M2MeshLoadPipelineState] --> Observer
|
||||
Observer --> Snapshot[M2BuildResourceSnapshot]
|
||||
Snapshot --> Dispatch[M2BuildDispatchPlanner]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Errors |
|
||||
|---|---|---|---|
|
||||
| `observe(snapshot, path, cache_dir, mesh_cache, prototype_cache, pipeline)` | Command/query | Adopt cached/pending/requested/missing static result | Invalid inputs return `rejected` |
|
||||
| `cache_resource_paths(cache_dir, path, extensions)` | Pure query | Return unique historical nested/lowercase/basename candidates | Empty values yield fewer/empty candidates |
|
||||
| `glb_animation_schema(path)` | Read query | Read OpenWC schema marker from GLB JSON | Invalid/missing GLB returns empty String |
|
||||
| `OUTCOME_*` | Constants | Stable diagnostic transition identifiers | None |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
||||
|---|---|---|---|---|
|
||||
| Input | Normalized M2 path/cache directory | Loader | Observer | Copied Strings; one observation |
|
||||
| Input | Mesh/missing/request states | M2 state services | Observer | Borrowed services; map/session |
|
||||
| Input/output | Resource snapshot | Loader | Observer/dispatch/materializer | Borrowed; one build operation |
|
||||
| Output | Cached Mesh or missing/pending values | Observer | Snapshot | Exact borrowed Mesh/scalars |
|
||||
| Output | Threaded request record | Observer | Mesh pipeline | Pipeline-owned path record |
|
||||
| Output | Outcome StringName | Observer | Tests/future diagnostics | Immutable scalar |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Observe static path] --> Cached{Mesh cached?}
|
||||
Cached -->|yes| AdoptMesh[Adopt exact Mesh; cached]
|
||||
Cached -->|no| Missing{Already missing?}
|
||||
Missing -->|yes| AdoptMissing[Adopt missing]
|
||||
Missing -->|no| Pending{Request pending?}
|
||||
Pending -->|yes| AdoptPending[Adopt unresolved; pending]
|
||||
Pending -->|no| Candidates[Generate tscn then glb candidates]
|
||||
Candidates --> Eligible{Exists and not pivot-prefix GLB?}
|
||||
Eligible -->|yes| Request[Threaded request]
|
||||
Request -->|OK or busy| Remember[Remember request; requested]
|
||||
Eligible -->|none/error| MarkMissing[Mark/adopt missing]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Observing
|
||||
Observing --> Cached
|
||||
Observing --> Pending
|
||||
Observing --> Requested
|
||||
Observing --> Missing
|
||||
Observing --> Rejected
|
||||
Cached --> [*]
|
||||
Pending --> [*]
|
||||
Requested --> [*]
|
||||
Missing --> [*]
|
||||
Rejected --> [*]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant O as StaticBuildResourceObserver
|
||||
participant C as Mesh/prototype cache state
|
||||
participant P as MeshLoadPipelineState
|
||||
participant R as ResourceLoader
|
||||
participant S as M2BuildResourceSnapshot
|
||||
L->>O: observe(snapshot, path, cache_dir, states)
|
||||
O->>C: cached/missing lookup
|
||||
alt cache hit or missing
|
||||
O->>S: adopt static observation
|
||||
else pending
|
||||
O->>P: has_request
|
||||
O->>S: adopt unresolved observation
|
||||
else request candidate
|
||||
O->>R: exists + load_threaded_request
|
||||
O->>P: remember_request
|
||||
O->>S: adopt unresolved observation
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Observer[M2StaticBuildResourceObserver] --> ResourceLoader
|
||||
Observer --> FileAccess[GLB header/JSON read]
|
||||
Observer --> MeshCache[M2MeshResourceCacheState]
|
||||
Observer --> PrototypeCache[M2PrototypeCacheState]
|
||||
Observer --> Pipeline[M2MeshLoadPipelineState]
|
||||
Observer --> Snapshot[M2BuildResourceSnapshot]
|
||||
Observer -. no dependency .-> SceneTree
|
||||
Observer -. no dependency .-> Materializers
|
||||
Observer -. no dependency .-> Scheduler
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Observer retains no request, cache, snapshot or engine reference.
|
||||
- Cache/pipeline services retain their existing state and resource ownership.
|
||||
- Snapshot borrows the exact cached Mesh; observer never frees it.
|
||||
- Resource existence/request and GLB inspection run on renderer main thread.
|
||||
- `M2MeshResourceFinalizer` performs terminal polling, Resource extraction,
|
||||
Mesh preparation and cache/missing adoption from the loader permit loop.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| State | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Invalid dependency/path | Return `rejected`; no mutation | Correct composition |
|
||||
| Existing request | Return `pending`; no duplicate request | Retry when queue rotates |
|
||||
| Pivot-prefix GLB | Skip as static candidate | Continue next candidate |
|
||||
| Request error | Stop historical search and mark missing | Later reset/new session |
|
||||
| No candidate | Mark/adopt terminal missing | Cache content becomes available in new session |
|
||||
| Tile cancellation | Observer retains nothing | Loader/queue cancellation remains authoritative |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The observer consumes existing `m2_cache_dir`. It adds no profile, cache version,
|
||||
batch, visibility, shadow or scheduler setting.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No new persistence is introduced. Existing `.tscn/.glb` layout, pipeline records
|
||||
and missing-cache lifetime are unchanged; no rebake is required.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
Returned outcomes distinguish rejected/cached/pending/requested/missing without
|
||||
logging. Existing queue and hitch metrics remain unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- Dedicated verifier covers invalid, cached identity, missing, pending, path
|
||||
order/deduplication, absent GLB schema, source ownership and bounded timing.
|
||||
- Snapshot/dispatch/pipeline/cache/shutdown/facade/internal-access/checkpoint
|
||||
regressions protect adjacent behavior.
|
||||
- No original-client visual claim is made; this is bookkeeping/I/O extraction.
|
||||
|
||||
## Extension points
|
||||
|
||||
Animated/native observation may become a separate producer of the animation
|
||||
phase. Shared cache candidate/GLB inspection can later be reused without adding
|
||||
a generic resource framework.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap |
|
||||
|---|---|---|---|
|
||||
| Static build lookup/request production | Implemented extraction | Lifecycle/path/source/timing verifier | Asset-backed traversal pending |
|
||||
| Static finalize/preparation | Implemented extraction | Mesh resource finalizer verifier | Asset-backed traversal pending |
|
||||
| Animated observation | Existing loader-owned | Animation regressions | Separate producer pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Successful request behavior is protected by source plus existing pipeline
|
||||
tests; starting an undrained asynchronous request in the unit fixture would leak.
|
||||
- GLB schema parser reads synchronously, matching the previous loader behavior.
|
||||
- Private traversal, p95/p99, leak and visual evidence remain unavailable.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_static_build_resource_observer.gd` | Static cache/request/missing observation |
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed observation result |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Terminal polling, preparation and adoption |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, permits and action execution |
|
||||
| `src/tools/verify_m2_static_build_resource_observer.gd` | Lifecycle/path/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
||||
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
||||
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
||||
- [`world-renderer.md`](world-renderer.md)
|
||||
- [`../../RENDER.md`](../../RENDER.md)
|
||||
@@ -0,0 +1,183 @@
|
||||
# Renderer Closeout Verification
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target/work package | M03 / `M03-QAR-INTEGRATOR-CLOSEOUT-001` |
|
||||
| Owner | Renderer structural, cache and performance acceptance gates |
|
||||
| Last verified | Worktree `work/sindo-main-codex-m03-integrator/m03-closeout`, 2026-08-02 |
|
||||
| Profile | `Blizzlike335`; quality preset `High` for checkpoint evidence |
|
||||
|
||||
## Purpose
|
||||
|
||||
Provide reproducible closeout checks for the M03 renderer decomposition. The
|
||||
checks prove that worker boundaries remain CPU-only, main-thread frame steps are
|
||||
budgeted, cache versions remain explicit, and paired M00/M03 reports stay within
|
||||
the agreed 10% performance budgets.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Claim pixel-level parity with the original build-12340 client.
|
||||
- Replace subsystem unit verifiers or long-traversal release tests.
|
||||
- Generate, mutate or migrate production cache payloads.
|
||||
- Hide incompatible environments or cache inventories by normalizing reports.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
M00[M00 commit capture] --> Reports[Render checkpoint reports]
|
||||
M03[M03 commit capture] --> Reports
|
||||
Reports --> Comparator[compare_render_performance.ps1]
|
||||
Sources[Renderer source and cache versions] --> Contracts[verify_renderer_closeout_contracts.gd]
|
||||
Comparator --> Stability[repeatability gate across short and long windows]
|
||||
Stability --> Evidence[JSON comparison and exit code]
|
||||
Contracts --> Evidence
|
||||
Evidence --> Target[M03 Evidence / DONE decision]
|
||||
```
|
||||
|
||||
The capture command owns SceneTree execution and PNG/report writes. The
|
||||
PowerShell comparator is read-only except for its requested JSON output. The
|
||||
GDScript contract verifier reads source files and creates no renderer resources.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Preconditions | Failure |
|
||||
|---|---|---|---|---|
|
||||
| `compare_render_performance.ps1 -BaselineReport <paths> -CandidateReport <paths> [-OutputReport <path>]` | CLI | Compare one report or median of repeated reports | Same schema, profile, environment, cache contract/inventory and result keys | Exit 1 and enumerate incompatible fields or budget regressions |
|
||||
| `verify_render_performance_stability.ps1 -RepeatedSampleComparison <path> -LongWindowComparison <path>` | CLI | Reject only a metric regression reproduced by both independent protocols | Both comparator reports contain the same 84 metrics | Exit 1 with every repeatable result/metric key |
|
||||
| `verify_renderer_closeout_contracts.gd` | Godot CLI | Check worker/main-thread/cache/converter source contracts | Project parses and referenced sources exist | Exit 1 with named contract failure |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | M00/M03 `report.json` paths | `capture_render_checkpoints.gd` | Comparator | Filesystem-owned immutable evidence | One comparison |
|
||||
| Input | Renderer/tool GDScript sources | Repository | Contract verifier | Read-only | One verifier run |
|
||||
| Output | 84 metric comparisons and failures | Comparator | Integrator/CI | Optional JSON plus process output | Evidence retention |
|
||||
| Output | Repeatable and protocol-local regression inventories | Stability gate | Integrator/CI | Optional JSON plus process output | Evidence retention |
|
||||
| Output | Structural pass/fail summary | Contract verifier | Integrator/CI | Process output | One run |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Read[Read every supplied report] --> Compatible{Metadata and inventory match?}
|
||||
Compatible -->|no| Fail[Exit 1 with exact mismatch]
|
||||
Compatible -->|yes| Median[Median each metric per checkpoint/pass]
|
||||
Median --> Pair[Pair 14 result keys]
|
||||
Pair --> Budget[Compare load, p95, p99, hitch and memory]
|
||||
Budget --> Json[Optional comparison JSON]
|
||||
Budget --> Cohorts[Repeated-sample and long-window comparisons]
|
||||
Cohorts --> Repeated{Same metric fails both?}
|
||||
Repeated -->|no| Pass[Exit 0 with protocol-local diagnostics]
|
||||
Repeated -->|yes| Fail
|
||||
```
|
||||
|
||||
## Lifecycle and sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant I as Integrator
|
||||
participant B as M00 worktree
|
||||
participant C as M03 worktree
|
||||
participant R as Checkpoint capture
|
||||
participant G as Performance comparator
|
||||
I->>B: capture repeated baseline samples
|
||||
B->>R: same viewport, driver and cache inventory
|
||||
I->>C: capture repeated candidate samples
|
||||
C->>R: same viewport, driver and cache inventory
|
||||
I->>G: baseline paths plus candidate paths
|
||||
G->>G: compatibility checks and per-metric medians
|
||||
G-->>I: short-window and long-window JSON evidence
|
||||
I->>G: verify stability across both protocols
|
||||
G-->>I: repeatable-regression pass/fail
|
||||
```
|
||||
|
||||
There is no persistent state machine. Each invocation is read, validate,
|
||||
aggregate, compare and terminate.
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Reports and source files are borrowed read-only for one process.
|
||||
- Median aggregation deep-copies the first report and never rewrites inputs.
|
||||
- Rendering remains owned by the GUI capture process on Godot's main thread.
|
||||
- The contract verifier is headless and does not instantiate the streaming world.
|
||||
- The optional comparison report is wholly owned by the caller-selected path.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure | Behavior | Recovery |
|
||||
|---|---|---|
|
||||
| Missing/malformed report | Terminate with path/shape error | Regenerate that capture |
|
||||
| Environment/cache mismatch | Fail before accepting metrics | Recapture both commits on the same machine and cache |
|
||||
| Missing/duplicate checkpoint | Fail result-key validation | Repair manifest/capture completeness |
|
||||
| Metric over budget | Record baseline, candidate, limit and percentage | Diagnose named checkpoint/lane; rerun only after a code or evidence correction |
|
||||
| Interrupted GUI capture | No complete report is accepted | Remove/ignore partial output and rerun |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The comparator reads thresholds from the baseline report. M03 uses 10% maximum
|
||||
regression for load time, frame p95, frame p99, maximum hitch, static memory and
|
||||
video memory. Repeated input paths are optional; when supplied, each side is
|
||||
reduced independently to the median for every metric.
|
||||
|
||||
Closeout uses two independent protocols: repeated three-second captures and a
|
||||
ten-second measurement window. A regression is accepted as real only when the
|
||||
same checkpoint/pass/metric exceeds its unchanged 10% budget in both protocols.
|
||||
Protocol-local failures remain in the JSON as noise diagnostics rather than
|
||||
being discarded.
|
||||
|
||||
## Persistence, cache and migrations
|
||||
|
||||
The comparison JSON uses schema version 1 and contains source paths, sample
|
||||
counts, revisions, all metric pairs and failures. It is evidence, not a runtime
|
||||
cache. Renderer cache versions are read from the manifest/source contracts; this
|
||||
module performs no migration or invalidation.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Success reports result pairs, comparison count and budget percentage.
|
||||
- Failure output names every checkpoint/pass, metric, values and limit.
|
||||
- The structural verifier reports worker count, frame-step count and cache-version count.
|
||||
- Capture reports retain PNG hashes, queue snapshots, environment and cache inventory.
|
||||
|
||||
## Verification, fidelity and performance
|
||||
|
||||
- `verify_renderer_closeout_contracts.gd` covers four worker boundaries, fifteen
|
||||
frame steps, seven cache versions and the nested M2 GLB output contract.
|
||||
- `compare_render_performance.ps1` compares 14 cold/warm result pairs and 84 metrics.
|
||||
- `verify_render_performance_stability.ps1` requires metric-key agreement and
|
||||
rejects any budget regression reproduced by both sampling protocols.
|
||||
- M00 and M03 must be captured from their exact commits against the same cache
|
||||
inventory; old reports with a different inventory are rejected.
|
||||
- PNG hashes and asset-backed coverage prove that terrain, ADT boundaries, dense
|
||||
M2, large WMO, liquid, animated M2 and sky were rendered. They do not prove
|
||||
original-client pixel parity without human/reference-image approval.
|
||||
|
||||
## Extension points
|
||||
|
||||
CI may retain reports and comparison JSON as artifacts. A future release gate may
|
||||
add driver-version metadata, long-traversal samples or approved visual-diff
|
||||
thresholds without changing runtime renderer contracts.
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Godot reports the rendering API but not the installed NVIDIA driver version.
|
||||
- A 0.5-second historical M00 measurement window requires repeated median samples.
|
||||
- Original-client screenshots are not part of the repository evidence set.
|
||||
- Long-traversal descriptor pressure remains a later quality/release gate.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `tools/compare_render_performance.ps1` | Compatibility, median aggregation and metric budgets |
|
||||
| `tools/verify_render_performance_stability.ps1` | Cross-protocol repeatability acceptance |
|
||||
| `src/tools/verify_renderer_closeout_contracts.gd` | Structural source/cache/converter contracts |
|
||||
| `src/tools/capture_render_checkpoints.gd` | Asset-backed GUI capture and report generation |
|
||||
| `src/tools/render_baseline_manifest.json` | Coverage, viewport, cache contract and budgets |
|
||||
| `targets/00-render-baseline.md` | Accepted M00 measurement protocol |
|
||||
| `targets/03-renderer-facade.md` | M03 acceptance and Evidence |
|
||||
@@ -227,7 +227,7 @@ Existing ADT/native/cache format versions remain unchanged.
|
||||
| Renderer loaded-mesh diagnostic backend | Implemented | M03 facade typed sample and detached snapshot contract | Not composed into gameplay; triangle Mesh ray is diagnostic only |
|
||||
| Authoritative renderer/physics backend | Planned | Boundary permits replacement | Define holes/slopes/collision/readiness semantics before gameplay composition |
|
||||
| Holes/slopes/collision | Planned | Outside height-only contract | Later movement/physics package |
|
||||
| Liquid/swim query | Planned | Outside contract | M09/M12 world gameplay |
|
||||
| Liquid/swim query | Planned | Outside contract | M10/M13 world gameplay |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
| Status | Implemented |
|
||||
| Target/work package | M03 / `M03-RND-WMO-PLACEMENT-RESOLVER-001` |
|
||||
| Owners | Pure WMO cache-key, placement-identity and world-transform rules |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-placement-resolver`, 2026-07-17 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-scene-instance-factory`, 2026-08-01 |
|
||||
| Profiles/capabilities | Existing ADT/WDT WMO placement paths |
|
||||
|
||||
## Purpose
|
||||
@@ -31,14 +31,17 @@ live-prototype instance paths.
|
||||
flowchart LR
|
||||
Parsed[ADT/WDT WMO placement] --> Loader[StreamingWorldLoader adapter]
|
||||
Loader --> Resolver[WmoPlacementResolver]
|
||||
Loader --> Factory[WmoSceneInstanceFactory]
|
||||
Factory --> Resolver
|
||||
Resolver --> CacheKey[Normalized cache key]
|
||||
Resolver --> Identity[Registry unique key]
|
||||
Resolver --> Transform[World Transform3D]
|
||||
CacheKey --> Cache[Loader WMO caches/requests]
|
||||
Identity --> Registry[WmoPlacementRegistry]
|
||||
Transform --> RenderRoot[Lightweight render root]
|
||||
Transform --> Scene[Cached scene instance]
|
||||
Transform --> Live[Live prototype instance]
|
||||
Transform --> Factory
|
||||
Factory --> Scene[Cached scene instance]
|
||||
Factory --> Live[Live prototype instance]
|
||||
```
|
||||
|
||||
Allowed dependencies are Dictionary/String values and Godot `Vector3`, `Basis`
|
||||
@@ -62,7 +65,7 @@ WorkerThreadPool, mutexes, files, gameplay, network and editor UI are forbidden.
|
||||
| Input | Tile key and placement index | Loader build job | Synthetic identity fallback | Copied scalar/String | Registry entry lifetime |
|
||||
| Output | Normalized relative path | Resolver | Render/scene cache and load-request maps | New String value | Request/cache lookup |
|
||||
| Output | `uid:*` or `tile:*:*` key | Resolver | `WmoPlacementRegistry` and loader ref arrays | New String value | Until unregister/reset |
|
||||
| Output | World `Transform3D` | Resolver | Three WMO instance adapters | Value copy | Instance lifetime after assignment |
|
||||
| Output | World `Transform3D` | Resolver | Lightweight render-root adapter and cached/live instance factory | Value copy | Instance lifetime after assignment |
|
||||
|
||||
The resolver retains no source Dictionary, output or engine resource.
|
||||
|
||||
@@ -91,6 +94,7 @@ and shutdown require no resolver operation.
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Factory as WmoSceneInstanceFactory
|
||||
participant Resolver as WmoPlacementResolver
|
||||
participant Registry as WmoPlacementRegistry
|
||||
participant Instance as Render/cached/live instance
|
||||
@@ -98,9 +102,15 @@ sequenceDiagram
|
||||
Resolver-->>Loader: cache key
|
||||
Loader->>Resolver: resolve_unique_key(placement, tile, index)
|
||||
Resolver-->>Registry: identity adopted by loader
|
||||
Loader->>Resolver: resolve_world_transform(placement)
|
||||
Resolver-->>Loader: value Transform3D
|
||||
Loader->>Instance: assign transform and attach/build
|
||||
alt lightweight render root
|
||||
Loader->>Resolver: resolve_world_transform(placement)
|
||||
Resolver-->>Loader: value Transform3D
|
||||
else cached/live instance
|
||||
Loader->>Factory: create with placement
|
||||
Factory->>Resolver: resolve_world_transform(placement)
|
||||
Resolver-->>Factory: value Transform3D
|
||||
end
|
||||
Loader->>Instance: attach/build prepared instance
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
@@ -109,7 +119,9 @@ sequenceDiagram
|
||||
- `WmoPlacementRegistry` owns placement-key reference sets. The loader owns its
|
||||
key-to-Node map, cache/load-request state, jobs/queues, resource fallback and
|
||||
cancellation.
|
||||
- The loader and builders own every Node/Mesh/MultiMesh/material/RID lifecycle.
|
||||
- `WmoSceneInstanceFactory` owns detached cached/live candidate roots until
|
||||
rejection or transfer; the loader/builders own attachment and remaining
|
||||
Node/Mesh/MultiMesh/material/RID lifecycle.
|
||||
- Pure calls are thread-safe; current consumers execute on the main thread.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
# 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)
|
||||
@@ -15,7 +15,8 @@
|
||||
Own the mutually exclusive cached, missing and pending states for normalized
|
||||
lightweight-WMO render paths outside the monolithic streamer. The state holder
|
||||
accepts only caller-validated `Resource` references and records cache paths for
|
||||
threaded requests whose I/O lifecycle remains in `StreamingWorldLoader`.
|
||||
threaded requests whose terminal I/O lifecycle belongs to
|
||||
`WmoRenderResourceFinalizer`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
@@ -34,7 +35,8 @@ flowchart LR
|
||||
State -->|cached Resource| Loader
|
||||
Loader -->|cache path| ResourceLoader[Godot ResourceLoader]
|
||||
ResourceLoader -->|status and loaded Resource| Loader
|
||||
Loader --> Validate[Script and FORMAT_VERSION validation]
|
||||
Loader --> Finalizer[WmoRenderResourceFinalizer]
|
||||
Finalizer --> Validate[Script and FORMAT_VERSION validation]
|
||||
Validate -->|accepted Resource or missing| State
|
||||
Loader --> Fallback[Cached scene or live-prototype fallback]
|
||||
```
|
||||
@@ -53,8 +55,8 @@ or editor dependency. Cache validation stays at the I/O boundary in the loader.
|
||||
| `has_request(path)` | Query | Test pending threaded-request state | Renderer main thread | Empty returns false |
|
||||
| `remember_request(path, cache_path)` | Command/query | Record one loader-started request | Renderer main thread; until terminal/reset | Invalid or occupied state returns false |
|
||||
| `request_paths_snapshot()` | Query | Copy pending normalized/cache-path mapping | Poll or shutdown drain | Detached Dictionary |
|
||||
| `complete_request_with_resource(path, resource)` | Command/query | Remove pending request and adopt caller-validated Resource | Terminal loader poll | Unknown/null returns false |
|
||||
| `complete_request_as_missing(path)` | Command/query | Remove pending request and adopt negative state | Terminal loader poll | Unknown returns false |
|
||||
| `complete_request_with_resource(path, resource)` | Command/query | Remove pending request and adopt caller-validated Resource | Terminal finalizer poll | Unknown/null returns false |
|
||||
| `complete_request_as_missing(path)` | Command/query | Remove pending request and adopt negative state | Terminal finalizer poll | Unknown returns false |
|
||||
| `clear_transient_state()` | Command | Clear pending and missing while retaining accepted Resources | Map reset/request drain | Idempotent |
|
||||
| `clear_all()` | Command | Release Resources, pending and missing | Final runtime cache release | Idempotent |
|
||||
| `pending_request_count()` | Query | Preserve renderer queue metric contribution | Renderer diagnostics | None |
|
||||
@@ -81,11 +83,11 @@ flowchart TD
|
||||
Blocked -->|yes| Null[Return null; loader waits/falls back]
|
||||
Blocked -->|no| Start[Loader starts threaded request]
|
||||
Start --> Remember[remember_request]
|
||||
Remember --> Poll[Loader polls detached request snapshot]
|
||||
Remember --> Poll[Finalizer polls detached request snapshot]
|
||||
Poll --> Terminal{Loaded or failed?}
|
||||
Terminal -->|no| Poll
|
||||
Terminal -->|failed| Missing[complete as missing]
|
||||
Terminal -->|loaded| Validate[Loader validates script/version]
|
||||
Terminal -->|loaded| Validate[Finalizer validates script/version]
|
||||
Validate -->|valid| Adopt[complete with Resource]
|
||||
Validate -->|invalid| Missing
|
||||
```
|
||||
@@ -113,28 +115,31 @@ stateDiagram-v2
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as WmoRenderResourceFinalizer
|
||||
participant State as WmoRenderResourceCacheState
|
||||
participant RL as ResourceLoader
|
||||
Loader->>State: cached/missing/pending queries
|
||||
Loader->>RL: exists + load_threaded_request(cache path)
|
||||
Loader->>State: remember_request(normalized, cache path)
|
||||
loop renderer tick
|
||||
Loader->>State: request_paths_snapshot()
|
||||
Loader->>RL: load_threaded_get_status(cache path)
|
||||
Loader->>Finalizer: poll_terminal_requests(State)
|
||||
Finalizer->>State: request_paths_snapshot()
|
||||
Finalizer->>RL: load_threaded_get_status(cache path)
|
||||
end
|
||||
alt load failed
|
||||
Loader->>State: complete_request_as_missing(normalized)
|
||||
Finalizer->>State: complete_request_as_missing(normalized)
|
||||
else loaded
|
||||
Loader->>RL: load_threaded_get(cache path)
|
||||
Loader->>Loader: validate script and FORMAT_VERSION
|
||||
Loader->>State: complete with Resource or as missing
|
||||
Finalizer->>RL: load_threaded_get(cache path)
|
||||
Finalizer->>Finalizer: validate script and FORMAT_VERSION
|
||||
Finalizer->>State: complete with Resource or as missing
|
||||
end
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The state owns three Dictionaries and strong references to accepted Resources.
|
||||
- The loader owns normalization, cache paths, ResourceLoader calls and validation.
|
||||
- The loader owns normalization, cache paths and request admission; the finalizer
|
||||
owns terminal ResourceLoader calls and validation.
|
||||
- All mutation is serialized by the renderer main-thread lookup/drain lifecycle.
|
||||
- No mutex or callback is needed; detached request snapshots permit safe removal.
|
||||
- The loader/build queue borrow Resources without transferring ownership.
|
||||
@@ -158,8 +163,8 @@ format version, request scheduling and WMO build budgets remain loader-owned.
|
||||
## Persistence, cache and migration
|
||||
|
||||
The state is runtime-only and serializes nothing. `WMOStreamingResource` script
|
||||
identity and `FORMAT_VERSION` validation remain unchanged in the loader; no cache
|
||||
migration or rebuild is introduced by this extraction.
|
||||
identity and `FORMAT_VERSION` validation remain unchanged in the finalizer; no
|
||||
cache migration or rebuild is introduced by this extraction.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
@@ -189,7 +194,7 @@ states; Resource references and cache file paths are not exposed. No logs emit.
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Lightweight WMO render Resource state | Implemented extraction | Lifecycle/source/timing and shutdown verifiers | Asset-backed traversal/leak evidence pending |
|
||||
| Cache script/version validation | Preserved in loader | Source boundary and WMO regressions | Dedicated corrupt-cache fixture could follow |
|
||||
| Cache script/version validation | Implemented extraction | Finalizer source and synthetic validation verifier | Serialized corrupt-cache fixture could follow |
|
||||
| Cached WMO scene state | Implemented extraction | Scene-cache lifecycle/source/timing verifier | Asset-backed traversal/leak evidence pending |
|
||||
| WMO materialization | Partial/loader-owned | Queue/planner regressions | Further safe extraction |
|
||||
|
||||
@@ -206,7 +211,8 @@ states; Resource references and cache file paths are not exposed. No logs emit.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_render_resource_cache_state.gd` | Resource/missing/request state and resets |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalize, request, poll, validate, fallback and shutdown I/O |
|
||||
| `src/render/wmo/wmo_render_resource_finalizer.gd` | Terminal polling, script/format validation and publication |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Normalize, request admission, fallback and shutdown order |
|
||||
| `src/tools/verify_wmo_render_resource_cache_state.gd` | State, boundary and timing regression |
|
||||
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Loader final Resource ownership regression |
|
||||
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# WMO Render Resource Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-WMO-RENDER-RESOURCE-FINALIZER-001` |
|
||||
| Owners | Lightweight WMO terminal status polling, Resource validation and cache/missing publication |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-render-resource-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Profile-independent lightweight WMO render-cache finalization |
|
||||
|
||||
## Purpose
|
||||
|
||||
Finalize pending lightweight WMO `.res` cache requests outside
|
||||
`StreamingWorldLoader`: poll terminal status, retrieve a loaded Resource, enforce
|
||||
exact script/current-format validation and publish either the exact Resource or
|
||||
the historical missing outcome.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Select normalized WMO/cache paths or start threaded requests.
|
||||
- Finalize cached WMO PackedScenes or live WMOBuilder prototypes.
|
||||
- Own cache lifetime, placements, build queues, scheduler permits or Nodes.
|
||||
- Change `WMOStreamingResource.FORMAT_VERSION`, fallback or visible behavior.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] -->|compose and tick| Finalizer[WmoRenderResourceFinalizer]
|
||||
Cache[WmoRenderResourceCacheState] -->|pending snapshot| Finalizer
|
||||
Finalizer --> ResourceLoader
|
||||
Finalizer -->|validated Resource or missing| Cache
|
||||
Cache --> Build[WMO render build queue]
|
||||
```
|
||||
|
||||
The service may depend on `ResourceLoader`, the injected expected Script and the
|
||||
render Resource cache-state API. File selection, FileAccess, SceneTree, WMOBuilder,
|
||||
placement, scheduler and application layers remain outside it.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `poll_terminal_requests(cache_state)` | Command/query | Poll all pending paths once and publish terminal outcomes | Renderer main thread; stateless between calls | Null state returns zero; non-terminal retained |
|
||||
| `is_current_render_resource(resource)` | Query | Enforce exact injected Script and minimum format | Renderer main thread; stateless | Null/wrong/stale false |
|
||||
| `load_threaded_get_status(path)` | Boundary query | Read opaque threaded status | Renderer main thread; injectable in tests | ResourceLoader semantics |
|
||||
| `load_threaded_get(path)` | Boundary query | Retrieve terminal Resource | Renderer main thread; injectable in tests | Null accepted as failed outcome |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Detached normalized-path to `.res` path Dictionary | Render cache state | Finalizer polling | Caller-owned copy | One poll pass |
|
||||
| Input | Opaque ResourceLoader status | ResourceLoader | Finalizer | Integer value | One path poll |
|
||||
| Input | Terminal Resource | ResourceLoader | Validator | Borrowed reference | One completion |
|
||||
| Input | Expected Script/minimum format | Loader composition | Validator | Borrowed/value | Finalizer lifetime |
|
||||
| Output | Exact validated Resource | Finalizer | Render cache state | Cache adopts strong reference | Until full clear |
|
||||
| Output | Missing transition | Finalizer | Render cache state | Path-only state | Until transient clear |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Snapshot[Detached pending snapshot] --> Next[Next path in insertion order]
|
||||
Next --> Poll[Poll ResourceLoader status]
|
||||
Poll --> Terminal{Loaded or failed?}
|
||||
Terminal -->|no| Retain[Retain pending]
|
||||
Terminal -->|failed| Missing[Complete as missing]
|
||||
Terminal -->|loaded| Get[Get terminal Resource]
|
||||
Get --> Validate{Exact script and format current?}
|
||||
Validate -->|yes| Adopt[Complete with exact Resource]
|
||||
Validate -->|no| Missing
|
||||
Retain --> Next
|
||||
Adopt --> Next
|
||||
Missing --> Next
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Polling
|
||||
Polling --> Pending: non-terminal status
|
||||
Polling --> Missing: failed/null/wrong/stale
|
||||
Polling --> Cached: exact script and current format
|
||||
Pending --> Polling: later tick
|
||||
Missing --> [*]
|
||||
Cached --> [*]
|
||||
```
|
||||
|
||||
The finalizer retains no per-path state. The sibling cache state owns all
|
||||
Pending/Cached/Missing lifetime and reset transitions.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as WmoRenderResourceFinalizer
|
||||
participant Cache as WmoRenderResourceCacheState
|
||||
participant RL as ResourceLoader
|
||||
Loader->>Finalizer: poll_terminal_requests(Cache)
|
||||
Finalizer->>Cache: request_paths_snapshot()
|
||||
loop insertion-ordered paths
|
||||
Finalizer->>RL: load_threaded_get_status(cache path)
|
||||
alt non-terminal
|
||||
Finalizer->>Finalizer: retain request
|
||||
else failed
|
||||
Finalizer->>Cache: complete_request_as_missing(path)
|
||||
else loaded
|
||||
Finalizer->>RL: load_threaded_get(cache path)
|
||||
Finalizer->>Finalizer: exact Script and format validation
|
||||
Finalizer->>Cache: complete with Resource or missing
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[WmoRenderResourceFinalizer]
|
||||
Loader --> Cache[WmoRenderResourceCacheState]
|
||||
Finalizer --> Cache
|
||||
Finalizer --> ResourceLoader
|
||||
Finalizer --> Script[WMOStreamingResource Script contract]
|
||||
Finalizer -. no dependency .-> FileAccess
|
||||
Finalizer -. no dependency .-> WMOBuilder
|
||||
Finalizer -. no dependency .-> Node
|
||||
Finalizer -. no dependency .-> Scheduler
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main thread serializes polling and cache-state publication.
|
||||
- Finalizer owns no Resource, request, Node, RID or file lifetime.
|
||||
- Cache state adopts accepted exact Resource references until its full clear.
|
||||
- Loader owns request admission, fallback/build orchestration and shutdown order.
|
||||
- Detached snapshots allow terminal cache-state mutation during iteration.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null cache state | Guard | Return zero | Contract verifier | Correct composition |
|
||||
| Non-terminal request | Status | Retain pending | Pending count | Poll next tick |
|
||||
| Failed request | Terminal status | Complete missing | Negative cache | Transient reset/rebuilt cache |
|
||||
| Null/wrong-script Resource | Validation | Complete missing | Synthetic fixture | Rebuild cache and reset |
|
||||
| Stale format | Format below injected minimum | Complete missing | Synthetic fixture | Rebuild cache and reset |
|
||||
| Shutdown | Loader lifecycle | Drain then cache clear | Shutdown verifier | New loader starts absent |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The loader injects the exact `WMOStreamingResource` Script and its
|
||||
`FORMAT_VERSION`. The service introduces no setting, profile, permit or cache
|
||||
format. WMO cache paths and build budgets remain loader-owned.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The module serializes nothing and changes no cache format. It only enforces the
|
||||
existing script identity and current-or-newer format rule before runtime cache
|
||||
adoption, so no migration or rebake is introduced.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- `poll_terminal_requests` returns terminal completion count for tests/future metrics.
|
||||
- Existing cache pending count and renderer `wmobuild` metrics are unchanged.
|
||||
- The service emits no logs; normalized relative path remains the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_wmo_render_resource_finalizer.gd`: null/non-terminal, insertion order,
|
||||
failed/load boundary, null/wrong/stale rejection, current/newer identity,
|
||||
source ownership and 1,000 terminal polls under one second.
|
||||
- Cache-state, WMO queue/planner/registry/resolver, shutdown, facade,
|
||||
internal-access and baseline regressions protect adjacent behavior.
|
||||
- Fidelity evidence is exact orchestration extraction; no asset-backed visual or
|
||||
original-client parity claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
- A legal current/stale `.res` fixture can extend validation evidence unchanged.
|
||||
- PackedScene finalization remains separate because it instantiates a probe Node
|
||||
and uses WMOBuilder metadata rather than script/format Resource fields.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Lightweight WMO terminal polling | Implemented extraction | Status/order/source/timing verifier | Asset-backed traversal pending |
|
||||
| Script/format validation | Implemented extraction | Wrong/stale/current/newer fixtures | Legal serialized fixture pending |
|
||||
| Resource/missing publication | Implemented extraction | Exact identity/negative-state fixtures | Asset-backed lifetime/leak run pending |
|
||||
| WMO materialization | Loader-owned | Existing build regressions | Further safe extraction pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- ResourceLoader status/get remains synchronous main-thread boundary polling.
|
||||
- No proprietary WMO corpus, serialized corrupt-version fixture, leak run,
|
||||
traversal p95/p99 or paired original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_render_resource_finalizer.gd` | Terminal polling, validation and publication |
|
||||
| `src/render/wmo/wmo_render_resource_cache_state.gd` | Resource/missing/request ownership and resets |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, request admission, fallback and build orchestration |
|
||||
| `src/resources/wmo_streaming_resource.gd` | Serialized cache contract and format version |
|
||||
| `src/tools/verify_wmo_render_resource_finalizer.gd` | Terminal-I/O/validation/source/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md)
|
||||
- [`wmo-render-build-queue.md`](wmo-render-build-queue.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)
|
||||
@@ -0,0 +1,224 @@
|
||||
# WMO Runtime Mesh Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-WMO-RUNTIME-MESH-FINALIZER-001` |
|
||||
| Owners | Cached WMO runtime Mesh refresh version, surface iteration and material reconstruction |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-runtime-mesh-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Profile-independent cached WMO material refresh |
|
||||
|
||||
## Purpose
|
||||
|
||||
Finalize stale cached WMO runtime Mesh materials outside
|
||||
`StreamingWorldLoader`: preserve exact Mesh identity, stamp the historical
|
||||
refresh version and reconstruct eligible surface materials through WMOBuilder.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Traverse scene Nodes or own Mesh/MultiMesh attachment and lifetime.
|
||||
- Select WMO cache paths, poll ResourceLoader or schedule render build jobs.
|
||||
- Change WMOBuilder shaders, textures, blend rules or cache serialization.
|
||||
- Share an abstraction with the behaviorally different M2 finalizer.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader render build step] -->|Mesh plus extracted directory| Finalizer[WmoRuntimeMeshFinalizer]
|
||||
Preparer[WmoRuntimeScenePreparer cached traversal] -->|Mesh plus extracted directory| Finalizer
|
||||
Finalizer -->|material definition plus compact texture paths| Builder[WMOBuilder material boundary]
|
||||
Builder -->|rebuilt Material| Finalizer
|
||||
Finalizer -->|same Mesh identity| Loader
|
||||
Finalizer -->|same Mesh identity| Preparer
|
||||
```
|
||||
|
||||
The runtime scene preparer owns cached-scene traversal; the loader owns render
|
||||
build-step selection and composition. The finalizer owns only the in-place
|
||||
Resource operation and depends on an injected WMO material builder.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `finalize_mesh(mesh, extracted_directory)` | Command/query | Stamp and refresh a stale Mesh in place | Renderer main thread; stateless between calls | Null returns null; unsupported Mesh returns exact identity |
|
||||
| `rebuild_cached_material(material, extracted_directory)` | Query/boundary | Reconstruct one metadata-bearing cached WMO Material | Renderer main thread; borrowed input | Null/unmarked/missing builder returns null |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | `Mesh` and refresh metadata | Cached WMO scene/render group | Finalizer | Borrowed Resource | Call duration; identity retained |
|
||||
| Input | Extracted content directory | Loader configuration | WMOBuilder | Borrowed String value | Call duration |
|
||||
| Input | Texture paths, WMO flags/shader/blend and cached colors | Surface Material metadata/parameters | Finalizer | Borrowed values | One surface rebuild |
|
||||
| Output | Compact texture-path array and material definition | Finalizer | WMOBuilder | Detached values | One builder call |
|
||||
| Output | Rebuilt Material | WMOBuilder | ArrayMesh surface | Surface adopts exact Resource | Mesh lifetime |
|
||||
| Output | Exact input Mesh | Finalizer | Runtime scene preparer or loader build step | Caller retains ownership | Existing cache/scene lifetime |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Mesh[Borrowed Mesh] --> Null{Null?}
|
||||
Null -->|yes| ReturnNull[Return null]
|
||||
Null -->|no| Current{Refresh version at least 10?}
|
||||
Current -->|yes| ReturnSame[Return exact Mesh]
|
||||
Current -->|no| Stamp[Stamp version 10]
|
||||
Stamp --> Array{ArrayMesh?}
|
||||
Array -->|no| ReturnSame
|
||||
Array -->|yes| Surface[Iterate surfaces]
|
||||
Surface --> Eligible{Material has texture0 metadata?}
|
||||
Eligible -->|no| Keep[Keep exact Material]
|
||||
Eligible -->|yes| Definition[Compact paths and copy metadata/colors]
|
||||
Definition --> Builder[WMOBuilder build material]
|
||||
Builder --> Result{Non-null result?}
|
||||
Result -->|yes| Adopt[Replace surface Material]
|
||||
Result -->|no| Keep
|
||||
Keep --> Surface
|
||||
Adopt --> Surface
|
||||
Surface -->|done| ReturnSame
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Stale: missing/version below 10
|
||||
[*] --> Current: version at least 10
|
||||
Stale --> Current: stamp before optional surface refresh
|
||||
Current --> Current: subsequent calls are identity-only
|
||||
```
|
||||
|
||||
The version stamp is intentionally applied before type/material eligibility, as
|
||||
in the extracted loader behavior. The finalizer retains no Mesh or Material.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Preparer as WmoRuntimeScenePreparer
|
||||
participant Finalizer as WmoRuntimeMeshFinalizer
|
||||
participant Mesh as ArrayMesh
|
||||
participant Builder as WMOBuilder
|
||||
alt lightweight render build step
|
||||
Loader->>Finalizer: finalize_mesh(mesh, extracted_directory)
|
||||
else cached scene traversal
|
||||
Preparer->>Finalizer: finalize_mesh(mesh, extracted_directory)
|
||||
end
|
||||
Finalizer->>Mesh: read/stamp refresh metadata
|
||||
loop each stale ArrayMesh surface
|
||||
Finalizer->>Mesh: surface_get_material(index)
|
||||
alt cached WMO metadata exists
|
||||
Finalizer->>Builder: _build_material(definition, compact paths, directory)
|
||||
Builder-->>Finalizer: rebuilt Material or null
|
||||
Finalizer->>Mesh: surface_set_material when non-null
|
||||
end
|
||||
end
|
||||
Finalizer-->>Loader: exact Mesh identity for build path
|
||||
Finalizer-->>Preparer: exact Mesh identity for cached traversal path
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[WmoRuntimeMeshFinalizer]
|
||||
Preparer[WmoRuntimeScenePreparer] --> Finalizer
|
||||
Finalizer --> Engine[Mesh / ArrayMesh / Material]
|
||||
Finalizer --> Builder[Injected WMO material builder]
|
||||
Finalizer -. no dependency .-> Nodes[Node traversal/lifetime]
|
||||
Finalizer -. no dependency .-> RL[ResourceLoader]
|
||||
Finalizer -. no dependency .-> Queue[Build queue/scheduler]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- The renderer main thread serializes Mesh metadata and surface mutation.
|
||||
- The caller owns Mesh identity, scene traversal, attachment and destruction.
|
||||
- ArrayMesh surfaces adopt only non-null builder results; otherwise the exact
|
||||
cached Material remains attached.
|
||||
- The service retains no Resource, Node, RID, file, queue or per-path state.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null Mesh | Guard | Return null | Synthetic contract | Correct caller input |
|
||||
| Current Mesh | Version metadata | Return exact identity, no builder call | Identity/version contract | Version bump makes it stale |
|
||||
| Unsupported Mesh subtype | Type check after stamp | Return exact identity | PrimitiveMesh contract | No surface work required |
|
||||
| Null/unmarked Material | Metadata guard | Preserve exact surface value | Surface contract | Rebuild cache with metadata |
|
||||
| Missing builder/null result | Boundary guard/result | Preserve exact Material | Synthetic contract | Correct composition/retry after version bump |
|
||||
| Shutdown | Loader lifecycle | No retained work to cancel | Shutdown suite | New loader composes a new service |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
Refresh version `10` and metadata key `wow_wmo_material_refresh_version` are the
|
||||
existing runtime compatibility boundary. No project setting, profile, permit or
|
||||
feature flag is introduced.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
The module writes only the existing runtime Mesh metadata stamp and serializes
|
||||
nothing. It does not change WMO scene/render cache formats or trigger rebakes.
|
||||
A future material-rule change must deliberately bump the refresh version.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
The finalizer emits no logs and allocates no metrics. Existing `wmobuild` queue
|
||||
metrics and loader diagnostics remain the operational correlation surface.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_wmo_runtime_mesh_finalizer.gd`: null/current/non-ArrayMesh identity,
|
||||
version stamp, null/unmarked surface retention, compact path indices,
|
||||
flags/shader/blend/default and shader colors, exact builder adoption,
|
||||
missing-builder behavior, source ownership and 1,000 current calls under one second.
|
||||
- Adjacent WMO queue/cache/finalizer, shutdown, material and baseline regressions
|
||||
protect orchestration and visible output.
|
||||
- Fidelity evidence is exact behavior-preserving extraction; no private asset or
|
||||
original-client visual-parity claim is added.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Asset-backed WMO fixtures may compare reconstructed surface parameters without
|
||||
changing the service contract.
|
||||
- A deliberate material refresh change may bump the owned version with old/new
|
||||
fixtures and paired visual evidence.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Runtime refresh admission | Implemented extraction | Identity/version/type contracts | Serialized cache fixture pending |
|
||||
| Cached material definition reconstruction | Implemented extraction | Path/metadata/color contracts | Asset-backed visual comparison pending |
|
||||
| Scene traversal | Runtime scene preparer-owned | Cached/live traversal regressions | Asset-backed traversal pending |
|
||||
| Node materialization | Loader/group-materializer-owned | Existing WMO regressions | Further safe extraction pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Surface mutation and WMOBuilder material construction remain synchronous
|
||||
main-thread work.
|
||||
- No proprietary WMO corpus, long leak run, GPU timing, traversal p95/p99 or
|
||||
paired build-12340 visual capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_runtime_mesh_finalizer.gd` | Refresh admission, surface iteration and material definition reconstruction |
|
||||
| `addons/mpq_extractor/loaders/wmo_builder.gd` | WMO shader/material construction semantics |
|
||||
| `src/render/wmo/wmo_runtime_scene_preparer.gd` | Cached scene traversal and finalizer delegation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, build jobs, placement and lifetime |
|
||||
| `src/tools/verify_wmo_runtime_mesh_finalizer.gd` | Identity/version/material/source/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`wmo-render-build-queue.md`](wmo-render-build-queue.md)
|
||||
- [`wmo-render-resource-finalizer.md`](wmo-render-resource-finalizer.md)
|
||||
- [`wmo-scene-resource-finalizer.md`](wmo-scene-resource-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)
|
||||
@@ -0,0 +1,211 @@
|
||||
# WMO Runtime Scene Preparer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target | M03 Renderer Facade and Safe Extraction |
|
||||
| Work package | `M03-RND-WMO-RUNTIME-SCENE-PREPARER-001` |
|
||||
| Owner | Render |
|
||||
| Last verified | 2026-08-01 |
|
||||
|
||||
## Purpose
|
||||
|
||||
`WmoRuntimeScenePreparer` applies the existing post-instantiation rules to a
|
||||
borrowed WMO subtree on the renderer main thread. Cached scenes receive recursive
|
||||
Mesh finalization before render policy; live-built duplicates receive render
|
||||
policy only.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- load, validate, instantiate, duplicate, place or attach a WMO scene;
|
||||
- change `WmoRuntimeMeshFinalizer` material/version rules;
|
||||
- own the supplied subtree or decide Editor persistence;
|
||||
- change cache formats, queue progress, permits or shutdown;
|
||||
- implement portal/room visibility or recursive occluder discovery.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
The loader distinguishes validated cached PackedScene instances from duplicated
|
||||
live-built prototypes. That distinction remains explicit through two public
|
||||
methods so the live path does not gain cached-Mesh refresh behavior.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Cached[Validated cached WMO instance] --> Loader[StreamingWorldLoader]
|
||||
Live[Duplicated live-built WMO instance] --> Loader
|
||||
Loader -->|prepare_cached_instance| Preparer[WmoRuntimeScenePreparer]
|
||||
Loader -->|prepare_live_instance| Preparer
|
||||
Preparer -->|cached only| MeshFinalizer[WmoRuntimeMeshFinalizer]
|
||||
Preparer --> Policy[Direct Occluders policy + recursive shadow enabling]
|
||||
Policy --> Borrowed[Borrowed WMO Node3D subtree]
|
||||
Loader --> Placement[Placement/attachment/registry lifecycle]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Role | Thread/lifetime | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `prepare_cached_instance(instance, extracted_directory, enable_occlusion_culling, cast_shadows)` | Finalize cached subtree Meshes, then apply render policy | Renderer main thread; stateless after return | Null/freed root returns false |
|
||||
| `prepare_live_instance(instance, enable_occlusion_culling, cast_shadows)` | Apply render policy without Mesh finalization | Renderer main thread; stateless after return | Null/freed root returns false |
|
||||
|
||||
Both methods borrow the root and return a success flag. Disabled occlusion removes
|
||||
only the direct child named `Occluders`, matching the previous loader lookup.
|
||||
Enabled shadows set all descendant `GeometryInstance3D` nodes to ON. Disabled
|
||||
shadows preserve every existing value rather than forcing OFF.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Cached or live-built WMO `Node3D` | Loader scene/prototype adapter | Preparer | Borrowed exact subtree | One main-thread call |
|
||||
| Input | Extracted directory | Loader configuration | Runtime Mesh finalizer via preparer | Borrowed String | Cached preparation call |
|
||||
| Input | Occlusion/shadow policy | Loader quality configuration | Preparer | Scalar values | One call |
|
||||
| Internal | Mesh/MultiMesh Mesh reference | Borrowed subtree traversal | `WmoRuntimeMeshFinalizer` | Exact Resource reference; not retained by preparer | One cached call |
|
||||
| Output | Success flag | Preparer | Loader | Value | Immediate |
|
||||
| Side effect | Mesh refresh, optional child removal and shadow mutation | Preparer | Borrowed subtree | SceneTree remains loader/placement-owned | Until subtree release |
|
||||
|
||||
No filesystem, ResourceLoader, worker, RID, cache, queue, placement, attachment,
|
||||
Editor-owner or diagnostic side effect is introduced.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Input[Borrowed root + path kind + policies] --> Valid{Root valid?}
|
||||
Valid -->|no| False[Return false]
|
||||
Valid -->|yes cached| Walk[Parent-before-children traversal]
|
||||
Valid -->|yes live| Policy
|
||||
Walk --> Mesh{MeshInstance or non-null MultiMesh?}
|
||||
Mesh -->|yes| Finalize[WmoRuntimeMeshFinalizer.finalize_mesh]
|
||||
Mesh -->|no| Next[Continue]
|
||||
Finalize --> Next
|
||||
Next --> Policy[Apply direct Occluders and shadow policies]
|
||||
Policy --> True[Return true]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Preparer as WmoRuntimeScenePreparer
|
||||
participant Finalizer as WmoRuntimeMeshFinalizer
|
||||
participant Root as Borrowed WMO subtree
|
||||
alt cached scene
|
||||
Loader->>Preparer: prepare_cached_instance(root, directory, policies)
|
||||
loop parent-before-children Mesh traversal
|
||||
Preparer->>Finalizer: finalize_mesh(exact Mesh, directory)
|
||||
end
|
||||
else live-built duplicate
|
||||
Loader->>Preparer: prepare_live_instance(root, policies)
|
||||
end
|
||||
Preparer->>Root: optional direct Occluders removal
|
||||
Preparer->>Root: optional recursive shadow ON
|
||||
Preparer-->>Loader: true/false
|
||||
Loader->>Loader: place, attach, register and own lifetime
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Preparer[WmoRuntimeScenePreparer]
|
||||
Preparer --> Finalizer[WmoRuntimeMeshFinalizer]
|
||||
Preparer --> Engine[Node3D / GeometryInstance3D / Mesh / MultiMesh]
|
||||
Preparer -. no dependency .-> IO[ResourceLoader / FileAccess]
|
||||
Preparer -. no dependency .-> Queue[WMO queues / RenderBudgetScheduler]
|
||||
Preparer -. no dependency .-> Placement[WmoPlacementResolver / Registry]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Calls are main-thread only because Mesh Resources and SceneTree nodes mutate.
|
||||
- The loader/placement registry retains ownership of the root and descendants.
|
||||
- Mesh finalization receives exact borrowed Resource identities.
|
||||
- A removed direct `Occluders` child is detached and `queue_free()`d as before.
|
||||
- The service retains no Node, Resource, RID, path, collection or state after return.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Recovery |
|
||||
|---|---|---|---|
|
||||
| Null/freed root | Guard | Return false, no traversal | Caller drops/retries stale placement |
|
||||
| Missing finalizer composition | Null dependency | Skip Mesh refresh but apply render policy | Fix renderer composition before cached use |
|
||||
| Null Mesh | Exact finalizer call for MeshInstance; finalizer handles null | Continue traversal | Cache rebuild/finalizer diagnostics |
|
||||
| Null MultiMesh or its Mesh | Guard/finalizer contract | Skip null MultiMesh; finalizer handles null Mesh | Continue safely |
|
||||
| Occlusion disabled | Direct-child lookup | Detach and queue-free `Occluders` | Reinstantiate to restore subtree |
|
||||
| Shadows disabled | Policy branch | Preserve current node settings | Re-run with enabled policy if required |
|
||||
| Placement cancellation/shutdown | Loader lifecycle | Service has no retained work to cancel | Existing subtree release/drain order |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The service introduces no setting. It receives existing
|
||||
`enable_occlusion_culling` and `wmo_cast_shadows` values. The direct-child name
|
||||
`Occluders` and shadow-ON behavior are compatibility rules, not new capabilities.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No serialized format or cache version changes. Cached Mesh refresh metadata
|
||||
continues to belong to `WmoRuntimeMeshFinalizer`; no rebake or migration is needed.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
The preparer emits no log or metric. Loader placement/build metrics and the Mesh
|
||||
finalizer contracts remain the diagnostic surfaces.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_wmo_runtime_scene_preparer.gd` covers cached parent-before-children
|
||||
Mesh/MultiMesh identity, directory forwarding, null MultiMesh, direct versus
|
||||
nested `Occluders`, enabled/preserved shadows, live finalizer suppression,
|
||||
missing dependency, invalid roots, source boundaries and 1,000 traversals.
|
||||
- Adjacent WMO finalizer/cache/queue/shutdown and checkpoint checks protect the
|
||||
unchanged lifecycle and presentation behavior.
|
||||
- Fidelity evidence is exact behavior-preserving extraction; no asset-backed or
|
||||
original-client 3.3.5a visual-parity claim is added.
|
||||
|
||||
The synthetic budget is 1,000 two-Mesh cached preparations under one second.
|
||||
Asset-backed CPU/GPU p95/p99, traversal and leak measurements remain pending.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Asset-backed traversal can validate material identity and subtree lifetime
|
||||
without expanding this service contract.
|
||||
- Portal/room visibility requires its own documented WMO service and evidence;
|
||||
it must not be hidden inside generic subtree preparation.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Cached WMO subtree Mesh finalization | Implemented extraction | Exact traversal/resource/directory contract | Asset-backed visual/GPU p95/p99 pending |
|
||||
| Live/cached render policy | Implemented extraction | Direct occluder and recursive shadow contract | Portal/room and asset-backed traversal pending |
|
||||
| Placement/attachment/lifetime | Loader-owned | Existing registry/shutdown regressions | Further orchestration extraction pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Recursive Mesh finalization and shadow mutation remain synchronous main-thread work.
|
||||
- The missing-finalizer branch degrades safely for isolated tests but production
|
||||
composition must always inject `WmoRuntimeMeshFinalizer`.
|
||||
- No private WMO corpus, portal/room behavior, long traversal, leak/GPU timing or
|
||||
paired original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_runtime_scene_preparer.gd` | Cached/live subtree traversal and render policy |
|
||||
| `src/render/wmo/wmo_runtime_mesh_finalizer.gd` | Borrowed Mesh refresh/version/material rules |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, instantiation, placement, attachment and lifetime |
|
||||
| `src/tools/verify_wmo_runtime_scene_preparer.gd` | Synthetic traversal/policy/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`wmo-runtime-mesh-finalizer.md`](wmo-runtime-mesh-finalizer.md)
|
||||
- [`wmo-scene-resource-finalizer.md`](wmo-scene-resource-finalizer.md)
|
||||
- [`wmo-render-group-materializer.md`](wmo-render-group-materializer.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)
|
||||
@@ -0,0 +1,219 @@
|
||||
# WMO Scene Instance Factory
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented |
|
||||
| Target | M03 Renderer Facade and Safe Extraction |
|
||||
| Work package | `M03-RND-WMO-SCENE-INSTANCE-FACTORY-001` |
|
||||
| Owner | Render |
|
||||
| Last verified | 2026-08-01 |
|
||||
|
||||
## Purpose
|
||||
|
||||
`WmoSceneInstanceFactory` creates detached WMO `Node3D` roots from validated
|
||||
cached `PackedScene` resources or live-built prototypes. It owns cache-currentness
|
||||
validation, basename assignment and canonical placement-resolver delegation.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- look up/load/cache PackedScenes or build live WMO prototypes;
|
||||
- apply Mesh/material/occluder/shadow runtime preparation;
|
||||
- attach nodes, assign Editor ownership or manage placement references;
|
||||
- own queues, permits, cache versions or world teardown;
|
||||
- define WMO placement formulas or scene-cache currentness rules.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
The loader selects cached versus live sources. The factory creates a detached
|
||||
instance and applies identity/placement. `WmoRuntimeScenePreparer` then applies
|
||||
path-specific presentation policy before the loader attaches/registers the root.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Cache[WMO PackedScene cache] --> Loader[StreamingWorldLoader]
|
||||
Prototype[Live WMO prototype cache/build] --> Loader
|
||||
Loader --> Factory[WmoSceneInstanceFactory]
|
||||
Validator[WMOBuilder scene-cache validator] --> Factory
|
||||
Resolver[WmoPlacementResolver] --> Factory
|
||||
Factory --> Detached[Detached WMO Node3D]
|
||||
Detached --> Preparer[WmoRuntimeScenePreparer]
|
||||
Preparer --> Loader
|
||||
Loader --> Scene[Attachment and placement registry]
|
||||
```
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Role | Thread/lifetime | Failure behavior |
|
||||
|---|---|---|---|
|
||||
| `is_cached_node_current(node)` | Delegate one Node to the injected cache validator | Renderer main thread; no retention | Null/missing validator returns false |
|
||||
| `instantiate_cached_scene(relative_path, scene, placement)` | Instantiate, type-check, validate, name and place a cached scene | Renderer main thread; detached result caller-owned | Invalid input/root/stale/dependency returns null; created rejected roots freed |
|
||||
| `duplicate_live_prototype(relative_path, prototype, placement)` | Duplicate, name and place a live prototype | Renderer main thread; detached result caller-owned | Null/missing resolver/unexpected duplicate returns null |
|
||||
|
||||
The cached path validates before placement. The live path deliberately skips the
|
||||
scene-cache validator. Both paths use `get_file().get_basename()` and the exact
|
||||
`WmoPlacementResolver.resolve_world_transform` result.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Cached `PackedScene` or live prototype `Node3D` | Loader cache/build adapters | Factory | Borrowed Resource/Node | One main-thread call |
|
||||
| Input | Relative WMO path and placement Dictionary | Loader placement job | Factory | Borrowed values | One call |
|
||||
| Internal | Candidate root | PackedScene instantiate/prototype duplicate | Validator/factory | Factory-owned until accepted | One call |
|
||||
| Output | Detached named/placed `Node3D` | Factory | Runtime scene preparer/loader | Ownership transfers to caller | Until attachment/release |
|
||||
| Output | Currentness bool | Validator via factory | Loader cache admission | Value | Immediate |
|
||||
|
||||
Side effects are limited to scene instantiation/duplication, candidate name and
|
||||
transform mutation, and synchronous free of rejected candidates. No attachment,
|
||||
filesystem, ResourceLoader, worker, RID, queue, cache or Editor-owner mutation.
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Source[PackedScene or live prototype] --> Create{Cached or live?}
|
||||
Create -->|cached| Instantiate[PackedScene.instantiate]
|
||||
Create -->|live| Duplicate[prototype.duplicate]
|
||||
Instantiate --> Type{Node3D?}
|
||||
Duplicate --> Type
|
||||
Type -->|no| Free[Free created candidate and return null]
|
||||
Type -->|yes cached| Current{Cache current?}
|
||||
Type -->|yes live| Identity[Apply basename]
|
||||
Current -->|no| Free
|
||||
Current -->|yes| Identity
|
||||
Identity --> Resolve[WmoPlacementResolver]
|
||||
Resolve --> Return[Return detached Node3D]
|
||||
```
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Factory as WmoSceneInstanceFactory
|
||||
participant Validator as WMOBuilder
|
||||
participant Resolver as WmoPlacementResolver
|
||||
alt cached source
|
||||
Loader->>Factory: instantiate_cached_scene(path, scene, placement)
|
||||
Factory->>Factory: instantiate and require Node3D
|
||||
Factory->>Validator: is_scene_cache_current(root)
|
||||
Validator-->>Factory: current/stale
|
||||
else live source
|
||||
Loader->>Factory: duplicate_live_prototype(path, prototype, placement)
|
||||
Factory->>Factory: duplicate and require Node3D
|
||||
end
|
||||
Factory->>Factory: assign basename
|
||||
Factory->>Resolver: resolve_world_transform(placement)
|
||||
Resolver-->>Factory: exact Transform3D
|
||||
Factory-->>Loader: detached Node3D or null
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Factory[WmoSceneInstanceFactory]
|
||||
Factory --> Validator[Injected WMO scene-cache validator]
|
||||
Factory --> Resolver[Injected WmoPlacementResolver]
|
||||
Factory --> Engine[PackedScene / Node3D / Transform3D]
|
||||
Factory -. no dependency .-> Preparation[WmoRuntimeScenePreparer]
|
||||
Factory -. no dependency .-> IO[ResourceLoader / FileAccess]
|
||||
Factory -. no dependency .-> Queue[WMO queues / scheduler]
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Calls are renderer-main-thread only because PackedScene/Node APIs mutate.
|
||||
- The source scene/prototype remains caller/cache-owned.
|
||||
- The factory owns a newly created root until rejection or successful return.
|
||||
- Successful return transfers detached-root ownership to the caller.
|
||||
- Descendant Mesh/Material Resources retain engine duplicate/instantiate identity.
|
||||
- The factory retains only injected stateless dependencies, never Nodes/Resources.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Recovery |
|
||||
|---|---|---|---|
|
||||
| Null scene/prototype | Guard | Return null without allocation | Correct caller source |
|
||||
| Missing validator | Currentness guard | Cached candidate rejected/freed | Fix composition |
|
||||
| Missing resolver | Guard before creation | Return null without allocation | Fix composition |
|
||||
| Non-Node3D root | Runtime type check | Free candidate and return null | Rebuild invalid cache/source |
|
||||
| Stale cached root | Injected validator | Free candidate; skip placement | Rebuild cache/current metadata |
|
||||
| Placement cancellation | Loader lifecycle | Detached/attached result released by caller | Existing retry path |
|
||||
| Shutdown | No retained candidates | Nothing to drain | Existing loader teardown |
|
||||
|
||||
The non-Node3D cached rejection now frees the created invalid root synchronously.
|
||||
Normal admitted caches already enforce Node3D through the scene finalizer, so this
|
||||
closes an error-path lifetime leak without changing valid rendered output.
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
No new settings. Cache-currentness rules belong to the injected WMOBuilder
|
||||
boundary; placement formulas belong to `WmoPlacementResolver`.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No format/version change and no rebake. The factory reads no files and writes no
|
||||
metadata. Existing cache validator version policy remains authoritative.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
The factory emits no logs or metrics. Loader cache/placement metrics and
|
||||
synthetic rejection contracts remain the diagnostic surfaces.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_wmo_scene_instance_factory.gd` covers cached validation-before-placement,
|
||||
exact accepted root/descendant Resource identity, stale-root free, non-Node3D
|
||||
rejection, live validator suppression, detached ownership, dependencies,
|
||||
basename/Transform3D application, source boundaries and 1,000 duplicates.
|
||||
- Adjacent scene finalizer, placement resolver, runtime preparer, shutdown and
|
||||
checkpoint regressions protect lifecycle and visible output.
|
||||
- Fidelity evidence is behavior-preserving extraction for valid inputs. The
|
||||
invalid non-Node3D free is a lifetime fix, not a visual 3.3.5a change.
|
||||
|
||||
The synthetic budget requires 1,000 simple live duplicates in under one second.
|
||||
Asset-backed CPU/GPU p95/p99 and long-traversal evidence remain pending.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Asset-backed cached/live instances can compare placement and lifetime without
|
||||
changing the factory API.
|
||||
- New source kinds should be separate explicit methods only when their validation
|
||||
and identity semantics differ materially.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Cached WMO instantiation | Implemented extraction | Type/currentness/name/placement/lifetime contract | Serialized asset-backed cache corpus pending |
|
||||
| Live prototype duplication | Implemented extraction | Identity/name/placement/validator-suppression contract | Asset-backed traversal/leak evidence pending |
|
||||
| Runtime preparation | Separate implemented service | Runtime scene preparer regression | Visual/GPU p95/p99 pending |
|
||||
| Attachment/registry lifetime | Loader-owned | Existing WMO placement/shutdown regressions | Further orchestration extraction pending |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- Scene instantiation/duplication remains synchronous main-thread work.
|
||||
- No private WMO corpus, portal/room behavior, long traversal, leak/GPU timing or
|
||||
paired original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_scene_instance_factory.gd` | Cached/live creation, validation, identity and placement |
|
||||
| `src/render/wmo/wmo_placement_resolver.gd` | Canonical WMO placement Transform3D |
|
||||
| `src/render/wmo/wmo_runtime_scene_preparer.gd` | Post-factory cached/live render preparation |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Source selection, cache/prototype lookup, attachment and lifetime |
|
||||
| `src/tools/verify_wmo_scene_instance_factory.gd` | Synthetic type/identity/lifetime/boundary/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`wmo-placement-resolver.md`](wmo-placement-resolver.md)
|
||||
- [`wmo-scene-resource-finalizer.md`](wmo-scene-resource-finalizer.md)
|
||||
- [`wmo-runtime-scene-preparer.md`](wmo-runtime-scene-preparer.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 mutually exclusive cached, missing and pending states for normalized WMO
|
||||
scene paths. The state accepts only caller-validated `PackedScene` references and
|
||||
records `.tscn` paths for threaded requests whose I/O, size limit and cache
|
||||
metadata validation remain in `StreamingWorldLoader`.
|
||||
records `.tscn` paths for threaded requests. Size admission remains in
|
||||
`StreamingWorldLoader`; terminal I/O/probe validation belongs to
|
||||
`WmoSceneResourceFinalizer`.
|
||||
|
||||
## Non-goals
|
||||
|
||||
@@ -33,8 +34,8 @@ flowchart LR
|
||||
Loader --> State[WmoSceneResourceCacheState]
|
||||
Loader --> Size[File existence and size limit]
|
||||
Size --> ResourceLoader[Godot ResourceLoader]
|
||||
ResourceLoader --> Loader
|
||||
Loader --> Probe[Instantiate, metadata/version check, free]
|
||||
ResourceLoader --> Finalizer[WmoSceneResourceFinalizer]
|
||||
Finalizer --> Probe[Instantiate, metadata/version check, free]
|
||||
Probe -->|accepted PackedScene or missing| State
|
||||
Loader --> Live[Live-prototype fallback]
|
||||
```
|
||||
@@ -82,7 +83,7 @@ flowchart TD
|
||||
Check -->|allowed| Request[Start threaded request]
|
||||
Request -->|error| Mark
|
||||
Request -->|accepted| Remember[remember_request]
|
||||
Remember --> Poll[Loader polls snapshot]
|
||||
Remember --> Poll[Scene Resource finalizer polls snapshot]
|
||||
Poll -->|failure| CompleteMissing[complete as missing]
|
||||
Poll -->|loaded| Validate[Instantiate and validate cache metadata]
|
||||
Validate -->|valid| CompleteScene[complete with scene]
|
||||
@@ -114,24 +115,27 @@ stateDiagram-v2
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant State as WmoSceneResourceCacheState
|
||||
participant Finalizer as WmoSceneResourceFinalizer
|
||||
participant RL as ResourceLoader
|
||||
Loader->>State: cached/missing/pending queries
|
||||
Loader->>Loader: exists and wmo_max_runtime_scene_mb check
|
||||
Loader->>RL: load_threaded_request(.tscn)
|
||||
Loader->>State: remember_request(normalized, path)
|
||||
loop renderer tick
|
||||
Loader->>State: request_paths_snapshot()
|
||||
Loader->>RL: load_threaded_get_status(path)
|
||||
Loader->>Finalizer: poll_terminal_requests(State)
|
||||
Finalizer->>State: request_paths_snapshot()
|
||||
Finalizer->>RL: load_threaded_get_status(path)
|
||||
end
|
||||
Loader->>RL: load_threaded_get(path)
|
||||
Loader->>Loader: instantiate, validate WMO metadata, free probe
|
||||
Loader->>State: complete with scene or as missing
|
||||
Finalizer->>RL: load_threaded_get(path)
|
||||
Finalizer->>Finalizer: instantiate, validate WMO metadata, free probe
|
||||
Finalizer->>State: complete with scene or as missing
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- State owns three Dictionaries and strong references to accepted PackedScenes.
|
||||
- Loader owns paths, file measurement, requests, validation and all Node lifetime.
|
||||
- Loader owns paths, file measurement, request admission and placed Nodes;
|
||||
finalizer owns terminal I/O and call-local validation-probe lifetime.
|
||||
- All mutation is serialized on the renderer main thread; no mutex is required.
|
||||
- Detached request snapshots allow terminal removal while polling.
|
||||
- Scene instantiation borrows the PackedScene and does not transfer cache ownership.
|
||||
@@ -143,7 +147,7 @@ sequenceDiagram
|
||||
| Missing `.tscn` | Loader ResourceLoader existence check | Direct missing state | Transient reset permits later retry |
|
||||
| Oversize `.tscn` | Loader byte limit | Missing plus unchanged debug log | Raise limit/rebuild, then reset |
|
||||
| Request-start/load failure | Loader error/status | Direct or terminal missing | Reset and retry later |
|
||||
| Wrong type/stale metadata | Loader PackedScene/probe validation | Terminal missing; probe freed | Rebuild cache and reset |
|
||||
| Wrong type/stale metadata | Finalizer PackedScene/probe validation | Terminal missing; probe freed | Rebuild cache and reset |
|
||||
| Shutdown while pending | Loader drains snapshot | Clear transient, then full cache release | New loader starts absent |
|
||||
|
||||
## Configuration and capabilities
|
||||
@@ -184,8 +188,9 @@ in the loader; the state emits no logs.
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Cached WMO PackedScene state | Implemented extraction | Lifecycle/source/timing and shutdown verifiers | Asset-backed traversal/leak evidence pending |
|
||||
| Size and cache metadata validation | Preserved in loader | Source boundary and WMO regressions | Oversize/stale asset fixtures could follow |
|
||||
| ResourceLoader I/O and live fallback | Partial/loader-owned | Existing runtime behavior | Separate extraction if justified |
|
||||
| Size admission | Preserved in loader | Source boundary and WMO regressions | Oversize asset fixture could follow |
|
||||
| Terminal I/O and cache validation | Implemented extraction | Finalizer type/lifetime/source/timing verifier | Serialized stale fixture pending |
|
||||
| Live fallback | Loader-owned | Existing runtime behavior | Separate extraction if justified |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
@@ -199,7 +204,8 @@ in the loader; the state emits no logs.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_scene_resource_cache_state.gd` | Scene/missing/request state and resets |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Paths, file limit, requests, validation, fallback and Node lifetime |
|
||||
| `src/render/wmo/wmo_scene_resource_finalizer.gd` | Terminal I/O, probe validation/lifetime and publication |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Paths, file limit, request admission, fallback and placed Nodes |
|
||||
| `src/tools/verify_wmo_scene_resource_cache_state.gd` | State, boundary and timing regression |
|
||||
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Loader final cache ownership regression |
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
# WMO Scene Resource Finalizer
|
||||
|
||||
## Metadata
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Status | Implemented extraction |
|
||||
| Target/work package | M03 / `M03-RND-WMO-SCENE-RESOURCE-FINALIZER-001` |
|
||||
| Owners | Cached WMO PackedScene terminal polling, validation-probe lifetime and scene/missing publication |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-scene-resource-finalizer`, 2026-07-18 |
|
||||
| Profiles/capabilities | Profile-independent cached WMO `.tscn` finalization |
|
||||
|
||||
## Purpose
|
||||
|
||||
Finalize pending cached WMO `.tscn` requests outside `StreamingWorldLoader`:
|
||||
poll terminal status, retrieve a PackedScene, instantiate one validation probe,
|
||||
apply existing WMOBuilder cache metadata rules, release the probe and publish
|
||||
either the exact scene or the historical missing outcome.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Select paths, measure file size or start ResourceLoader requests.
|
||||
- Instantiate placed WMO scenes or execute live WMOBuilder fallback.
|
||||
- Own cache lifetime, placements, build queues, permits or attached Nodes.
|
||||
- Change cache metadata/version rules, fallback order or visible behavior.
|
||||
|
||||
## Context and boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] -->|compose and tick| Finalizer[WmoSceneResourceFinalizer]
|
||||
Cache[WmoSceneResourceCacheState] -->|pending snapshot| Finalizer
|
||||
Finalizer --> ResourceLoader
|
||||
Finalizer --> Probe[Temporary Node3D probe]
|
||||
Probe --> Validator[WMOBuilder cache validator]
|
||||
Finalizer -->|PackedScene or missing| Cache
|
||||
Cache --> Fallback[Placed cached scene or live fallback]
|
||||
```
|
||||
|
||||
The service may depend on ResourceLoader, PackedScene/Node3D lifetime and the
|
||||
injected cache validator. FileAccess, size policy, placement, attached SceneTree,
|
||||
scheduler and application layers remain outside it.
|
||||
|
||||
## Public API
|
||||
|
||||
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
||||
|---|---|---|---|---|
|
||||
| `poll_terminal_requests(cache_state)` | Command/query | Poll every pending scene path once and publish terminal outcomes | Renderer main thread; stateless between calls | Null state returns zero; non-terminal retained |
|
||||
| `is_scene_cache_current(scene)` | Command/query | Instantiate, validate and release one probe | Renderer main thread; call-local Node | Null/no validator/wrong root/stale false |
|
||||
| `load_threaded_get_status(path)` | Boundary query | Read threaded status | Renderer main thread; injectable tests | ResourceLoader semantics |
|
||||
| `load_threaded_get(path)` | Boundary query | Retrieve terminal Resource | Renderer main thread; injectable tests | Null/wrong type rejected |
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Detached normalized-path to `.tscn` path Dictionary | Scene cache state | Finalizer polling | Caller-owned copy | One pass |
|
||||
| Input | Opaque load status/terminal Resource | ResourceLoader | Finalizer | Value/borrowed Resource | One path |
|
||||
| Input | Cached WMO PackedScene | ResourceLoader | Probe validator | Borrowed reference | One completion |
|
||||
| Input | Metadata validator | Loader composition | Finalizer | Borrowed Object | Finalizer lifetime |
|
||||
| Output | Exact validated PackedScene | Finalizer | Scene cache state | Cache adopts strong reference | Until full clear |
|
||||
| Output | Missing transition | Finalizer | Scene cache state | Path-only state | Until transient clear |
|
||||
| Side effect | Temporary validation Node | PackedScene/finalizer | Validator | Finalizer-owned | Freed before return |
|
||||
|
||||
## Data flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Snapshot[Detached pending snapshot] --> Poll[Poll next status in insertion order]
|
||||
Poll --> Terminal{Loaded or failed?}
|
||||
Terminal -->|no| Retain[Retain pending]
|
||||
Terminal -->|failed| Missing[Complete missing]
|
||||
Terminal -->|loaded| Get[Get Resource]
|
||||
Get --> Type{PackedScene?}
|
||||
Type -->|no| Missing
|
||||
Type -->|yes| Instantiate[Instantiate one probe]
|
||||
Instantiate --> Root{Node3D root?}
|
||||
Root -->|no| FreeRejected[Free rejected Node] --> Missing
|
||||
Root -->|yes| Validate[WMOBuilder metadata validation]
|
||||
Validate --> Free[Free Node3D probe]
|
||||
Free --> Current{Current?}
|
||||
Current -->|yes| Adopt[Adopt exact PackedScene]
|
||||
Current -->|no| Missing
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Polling
|
||||
Polling --> Pending: non-terminal
|
||||
Polling --> Probing: loaded PackedScene
|
||||
Polling --> Missing: failed/null/wrong type
|
||||
Probing --> Missing: wrong root or stale metadata
|
||||
Probing --> Cached: current metadata
|
||||
Probing --> Released: probe freed before outcome
|
||||
Released --> Missing
|
||||
Released --> Cached
|
||||
Pending --> Polling: later tick
|
||||
```
|
||||
|
||||
The finalizer retains no request or Node. Cache state owns Pending/Cached/Missing
|
||||
lifetime; every instantiated probe is synchronously released before return.
|
||||
|
||||
## Main sequence
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Loader as StreamingWorldLoader
|
||||
participant Finalizer as WmoSceneResourceFinalizer
|
||||
participant Cache as WmoSceneResourceCacheState
|
||||
participant RL as ResourceLoader
|
||||
participant Validator as WMOBuilder validator
|
||||
Loader->>Finalizer: poll_terminal_requests(Cache)
|
||||
Finalizer->>Cache: request_paths_snapshot()
|
||||
loop insertion-ordered paths
|
||||
Finalizer->>RL: load_threaded_get_status(path)
|
||||
alt failed
|
||||
Finalizer->>Cache: complete_request_as_missing(path)
|
||||
else loaded
|
||||
Finalizer->>RL: load_threaded_get(path)
|
||||
Finalizer->>Finalizer: instantiate Node3D probe
|
||||
Finalizer->>Validator: is_scene_cache_current(probe)
|
||||
Finalizer->>Finalizer: free probe
|
||||
Finalizer->>Cache: complete with exact scene or missing
|
||||
else non-terminal
|
||||
Finalizer->>Finalizer: retain request
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
## Dependency diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[WmoSceneResourceFinalizer]
|
||||
Loader --> Cache[WmoSceneResourceCacheState]
|
||||
Finalizer --> Cache
|
||||
Finalizer --> ResourceLoader
|
||||
Finalizer --> PackedScene
|
||||
Finalizer --> Node3D
|
||||
Finalizer --> Validator[WMOBuilder cache validator]
|
||||
Finalizer -. no dependency .-> FileAccess
|
||||
Finalizer -. no dependency .-> Placement
|
||||
Finalizer -. no dependency .-> Scheduler
|
||||
```
|
||||
|
||||
## Ownership, threading and resources
|
||||
|
||||
- Renderer main thread serializes polling, instantiation and cache publication.
|
||||
- Finalizer owns every call-local validation probe and frees it before return.
|
||||
- Non-Node3D rejected roots are also freed, closing the prior leak edge.
|
||||
- Cache state adopts accepted exact PackedScene references until full clear.
|
||||
- Loader owns admission/size policy, live fallback, placed Nodes and shutdown order.
|
||||
|
||||
## Errors, cancellation and recovery
|
||||
|
||||
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null cache state | Guard | Return zero | Contract verifier | Correct composition |
|
||||
| Non-terminal | Status | Retain pending | Pending count | Poll later |
|
||||
| Failed/null/wrong Resource | Status/type | Complete missing | Synthetic fixture | Reset/rebuild cache |
|
||||
| Non-Node3D root | Probe type | Free probe; complete missing | Type/lifetime fixture | Rebuild cache |
|
||||
| Stale metadata | Injected validator | Free probe; complete missing | Validation fixture | Rebuild/reset |
|
||||
| Shutdown | Loader lifecycle | Drain then clear cache | Shutdown verifier | New loader starts absent |
|
||||
|
||||
## Configuration and capabilities
|
||||
|
||||
The loader injects the existing WMOBuilder cache validator. `wmo_cache_dir`,
|
||||
`wmo_max_runtime_scene_mb`, debug logging, request timing and WMO budgets remain
|
||||
loader-owned. No setting, profile or cache version is added.
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
No data is serialized. Existing WMOBuilder metadata/version acceptance is called
|
||||
unchanged before adoption. The rejected-root lifetime fix needs no migration or
|
||||
rebake and does not alter accepted cache output.
|
||||
|
||||
## Diagnostics and observability
|
||||
|
||||
- Poll returns terminal completion count for tests/future metrics.
|
||||
- Existing pending count and renderer `wmobuild` metrics remain unchanged.
|
||||
- No logs are emitted; oversize debug logging remains at loader admission.
|
||||
- Normalized WMO relative path remains the correlation key.
|
||||
|
||||
## Verification
|
||||
|
||||
- `verify_wmo_scene_resource_finalizer.gd`: null/non-terminal, insertion order,
|
||||
failed/load boundary, null/wrong/non-Node3D/stale rejection, current exact
|
||||
identity, accepted/rejected probe release, source ownership and timing.
|
||||
- Scene cache, render finalizer/cache, WMO queue/planner/registry/resolver,
|
||||
shutdown, facade, internal-access and baseline regressions cover neighbors.
|
||||
- Fidelity evidence is exact orchestration extraction plus rejected-root leak
|
||||
repair; no asset-backed visual or original-client parity claim is made.
|
||||
|
||||
## Extension points
|
||||
|
||||
- Legal current/stale serialized `.tscn` fixtures can extend evidence unchanged.
|
||||
- Placed-scene preparation stays separate because it mutates an attached WMO
|
||||
instance rather than a call-local validation probe.
|
||||
|
||||
## Capability status
|
||||
|
||||
| Capability | Status | Evidence | Gap/next step |
|
||||
|---|---|---|---|
|
||||
| Cached WMO scene terminal polling | Implemented extraction | Status/order/source/timing verifier | Asset-backed traversal pending |
|
||||
| Probe validation/lifetime | Implemented extraction | Current/stale/type/free fixtures | Serialized fixture pending |
|
||||
| Scene/missing publication | Implemented extraction | Exact identity/negative fixtures | Asset-backed lifetime/leak run pending |
|
||||
| File-size admission/live fallback | Loader-owned | Existing scene-cache regressions | Separate extraction if justified |
|
||||
|
||||
## Known gaps and risks
|
||||
|
||||
- PackedScene instantiation/metadata validation remains synchronous main-thread work.
|
||||
- No proprietary WMO corpus, serialized stale/oversize fixture, long leak run,
|
||||
traversal p95/p99 or paired original-client capture is included.
|
||||
|
||||
## Source map
|
||||
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/wmo/wmo_scene_resource_finalizer.gd` | Terminal polling, probe validation/lifetime and publication |
|
||||
| `src/render/wmo/wmo_scene_resource_cache_state.gd` | Scene/missing/request ownership and resets |
|
||||
| `src/scenes/streaming/streaming_world_loader.gd` | Admission/size policy, live fallback and placed Node ownership |
|
||||
| `addons/mpq_extractor/loaders/wmo_builder.gd` | Existing cache metadata/version validation |
|
||||
| `src/tools/verify_wmo_scene_resource_finalizer.gd` | Terminal/probe/lifetime/source/timing regression |
|
||||
|
||||
## Related decisions and references
|
||||
|
||||
- [`wmo-scene-resource-cache-state.md`](wmo-scene-resource-cache-state.md)
|
||||
- [`wmo-render-resource-finalizer.md`](wmo-render-resource-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)
|
||||
+172
-21
@@ -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-prototype-cache`, 2026-07-17 |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-scene-instance-factory`, 2026-08-01 |
|
||||
| Profiles/capabilities | `Performance`, `Balanced`, `High`, `Custom`; Blizzlike fidelity incomplete |
|
||||
|
||||
## Purpose
|
||||
@@ -54,6 +54,14 @@ flowchart LR
|
||||
M2Transform --> Loader
|
||||
Loader --> M2Batch[M2BuildBatchPlanner]
|
||||
M2Batch --> Loader
|
||||
Loader --> M2Dispatch[M2BuildDispatchPlanner]
|
||||
M2Dispatch --> Loader
|
||||
Loader --> M2Resources[M2BuildResourceSnapshot]
|
||||
M2Resources --> M2Dispatch
|
||||
Loader --> M2Queue[M2BuildQueue]
|
||||
M2Queue --> Loader
|
||||
Loader --> M2Static[M2StaticBatchMaterializer]
|
||||
M2Static --> Scene
|
||||
Loader --> WmoPlacement[WmoPlacementResolver]
|
||||
WmoPlacement --> Loader
|
||||
Loader --> WmoRegistry[WmoPlacementRegistry]
|
||||
@@ -62,6 +70,12 @@ flowchart LR
|
||||
WmoBuildStep --> Loader
|
||||
Loader --> WmoBuildQueue[WmoRenderBuildQueue]
|
||||
WmoBuildQueue --> Loader
|
||||
Loader --> WmoGroupMaterializer[WmoRenderGroupMaterializer]
|
||||
WmoGroupMaterializer --> Scene
|
||||
Loader --> WmoScenePreparer[WmoRuntimeScenePreparer]
|
||||
WmoScenePreparer --> Scene
|
||||
Loader --> WmoInstanceFactory[WmoSceneInstanceFactory]
|
||||
WmoInstanceFactory --> WmoScenePreparer
|
||||
Native --> Parsed[Parsed tile/model data]
|
||||
Parsed --> Loader
|
||||
Loader --> Scene[SceneTree nodes]
|
||||
@@ -132,19 +146,37 @@ from externally reading/writing loader-private queue, task, cache and tile-state
|
||||
| `M2PlacementTransformResolver.resolve_basis/resolve_origin_offset` | Internal pure M2 service | Resolves regular and calibrated model-specific ADT placement transforms | Worker/main thread; stateless | Unknown paths use regular basis and zero offset |
|
||||
| `M2PlacementGrouper.group_placements` | Internal pure M2 service | Validates and groups ordered tile-local placement transforms by normalized path | Worker/main thread; stateless | Invalid variants/name IDs/empty paths are skipped |
|
||||
| `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 |
|
||||
| `M2BuildDispatchPlanner.plan_step` | Internal pure M2 service | Selects wait, animated/static materialization or no-Node advancement from observed resource state | Main/any thread; stateless | Pending animation has priority; unresolved static Mesh waits |
|
||||
| `M2BuildResourceSnapshot` | Internal M2 value contract | Carries per-step normalized path, animated/static references and pending/missing observations | Renderer main thread; one build operation | Values retained exactly; release never frees resources |
|
||||
| `M2StaticBuildResourceObserver.observe` | Internal M2 I/O service | Produces cached/pending/requested/missing static snapshot phase | Renderer main thread; stateless | Invalid composition rejected; no candidate marks missing |
|
||||
| `M2CachedAnimationResourceObserver.observe` | Internal M2 I/O service | Produces cached/pending/static-only animated snapshot phase | Renderer main thread; stateless | Invalid composition returns empty snapshot; no safe candidate marks static-only |
|
||||
| `M2BuildQueue` / `M2BuildJob` | Internal M2 pending-state service | Own typed root/groups/cursor jobs and FIFO/stale tile keys | Renderer main thread; map session | Invalid enqueue rejected; stale keys drain independently of jobs |
|
||||
| `M2StaticBatchMaterializer.materialize_batch` | Internal M2 scene-materialization service | Builds and attaches one prepared-Mesh MultiMesh transform slice | Renderer main thread; stateless after each call | Invalid/empty input returns null; bounds are caller precondition |
|
||||
| `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 |
|
||||
| `M2AnimationResourceFinalizer` | Internal M2 terminal-I/O service | Polls cached animation requests, loads/finalizes scenes and publishes prototype outcome | Renderer main thread; stateless across calls | Invalid/failed candidates mark static-only |
|
||||
| `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 |
|
||||
| `M2AnimationPlaybackController` | Internal M2 playback service | Applies stable phase, default imported animation and native animator startup | Renderer main thread; stateless after each call | Missing inventories/names no-op through historical fallbacks |
|
||||
| `M2AnimatedInstanceMaterializer` | Internal M2 scene-materialization service | Duplicates ordered animated instances, applies render settings, starts playback and attaches a non-empty batch | Renderer main thread; stateless after each call | Invalid/empty input or all failed duplicates returns empty |
|
||||
| `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 |
|
||||
| `M2MeshResourceFinalizer` | Internal M2 terminal-I/O service | Polls static requests, extracts/prepares terminal Meshes and publishes cache/missing outcomes | Renderer main thread; stateless across calls | Invalid/failed Resources mark missing; one call pops at most one terminal record |
|
||||
| `M2RuntimeMeshFinalizer` | Internal M2 preparation service | Owns refresh version, rebuild classification, M2Builder rebuild and fallback | Renderer main thread; decisions cached until reset | Null returns null; missing/failed rebuild marks and reuses original Mesh |
|
||||
| `M2RawModelRepository` | Internal M2 native repository | Reads static/animated raw Dictionaries through exact M2Loader methods | Synchronous; stateless | Invalid/unavailable/non-Dictionary result returns empty Dictionary |
|
||||
| `M2PrototypeCacheState` | Internal M2 prototype cache | Owns detached static/animated Nodes and missing/static-only outcomes | Renderer main thread; final shutdown | Invalid admission rejected; first valid prototype wins |
|
||||
| `M2NativeAnimationResourceObserver` | Internal native M2 resource observer | Selects GryphonRoost, reads/builds and publishes prototype/static-only outcome | Synchronous renderer main thread; stateless | Invalid/unavailable candidates return null; failures mark static-only |
|
||||
| `WmoPlacementResolver.normalize_relative_path/resolve_unique_key/resolve_world_transform` | Internal pure WMO service | Resolves cache key, registry identity and world transform | Main/any thread; stateless | Missing UID uses tile/index fallback; transform fields use historical defaults |
|
||||
| `WmoPlacementRegistry.add_reference/release_reference/contains/active_count/diagnostic_snapshot/clear` | Internal WMO service | Owns placement-key to tile/global reference sets | Renderer main thread; map session | Empty/unknown/non-owner input is rejected without mutation |
|
||||
| `WmoRenderBuildStepPlanner.plan_step` | Internal pure WMO service | Selects one mesh-first lightweight render-group operation and next cursors | Main/any thread; stateless | Raw integer comparisons are preserved without clamping |
|
||||
| `WmoRenderBuildQueue` / `WmoRenderBuildJob` | Internal WMO pending-state service | Owns typed root/resource/cursor jobs and FIFO placement keys | Renderer main thread; map session | Invalid enqueue rejected; duplicate/stale behavior preserved |
|
||||
| `WmoRenderGroupMaterializer.materialize_mesh_group/materialize_multimesh_group` | Internal WMO scene-materialization service | Creates, configures and attaches one indexed lightweight render group | Renderer main thread; stateless after each call | Invalid parent/resource/index returns null without attachment |
|
||||
| `WmoRuntimeScenePreparer.prepare_cached_instance/prepare_live_instance` | Internal WMO subtree-preparation service | Preserves cached/live Mesh-finalization distinction, direct occluder policy and recursive shadow enabling | Renderer main thread; stateless after each call | Null/freed root returns false |
|
||||
| `WmoSceneInstanceFactory.instantiate_cached_scene/duplicate_live_prototype` | Internal WMO instance-creation service | Validates/creates detached cached/live roots and applies shared name/placement | Renderer main thread; stateless after each call | Invalid/stale/dependency failure returns null and frees created rejects |
|
||||
| `WmoRenderResourceCacheState` | Internal WMO cache-state service | Owns validated Resources, negative entries and pending cache paths | Renderer main thread; map/cache session | Invalid/occupied request and unknown completion are rejected |
|
||||
| `WmoRenderResourceFinalizer` | Internal WMO terminal-I/O service | Polls lightweight render requests, validates script/format and publishes Resource/missing outcomes | Renderer main thread; stateless across calls | Non-terminal retained; failed/null/wrong/stale complete missing |
|
||||
| `WmoSceneResourceCacheState` | Internal WMO cache-state service | Owns validated PackedScenes, negative entries and pending `.tscn` paths | Renderer main thread; map/cache session | Direct missing and terminal request transitions remain distinct |
|
||||
| `WmoSceneResourceFinalizer` | Internal WMO terminal-I/O service | Polls cached scene requests, validates/frees probes and publishes scene/missing outcomes | Renderer main thread; stateless across calls | Failed/wrong/stale scenes complete missing; probes always released |
|
||||
| `AdtWaterLoadPipelineState` | Internal liquid async-state service | Owns ADT water FIFO/dedupe, active task IDs and mutex result mailbox | Main-thread state; worker result publication | Invalid/duplicate requests rejected; clear does not interrupt workers |
|
||||
| `AdtWaterSceneFinalizer.attach_water_scene` | Internal liquid main-thread service | Builds and attaches one existing-format ADT Water subtree | Main thread; stateless, returned Node tile-owned | Empty/invalid/dry input returns null without attachment |
|
||||
|
||||
@@ -175,14 +207,25 @@ loader configuration remains transitional composition data, not a caller API.
|
||||
| Internal transform | Rotation/path/scale | Loader or grouper / `M2PlacementTransformResolver` | Group/placeholder/instance transforms | Value-only Basis/Vector3 | One placement |
|
||||
| Internal grouping | Tile origin, M2 names and placements | Loader / `M2PlacementGrouper` | Loader worker result/build job | Fresh Dictionary/Transform3D arrays | One grouping task |
|
||||
| Internal batch plan | Transform count/offset, path kind and limits | Loader / `M2BuildBatchPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One build operation |
|
||||
| Internal M2 dispatch plan | Batch count and observed animation/static resource state | Loader / `M2BuildDispatchPlanner` | Loader queue/materializer/progress adapter | Fresh scalar/action Dictionary | One build operation |
|
||||
| Internal M2 resource observation | Normalized path, optional prototype/Mesh and pending/missing flags | Loader / M2 observers / `M2BuildResourceSnapshot` | Dispatch planner and loader materializer adapter | Snapshot borrows exact engine references | One build operation |
|
||||
| Internal M2 pending build | Tile key, M2 root, grouped transforms and cursors | Loader / `M2BuildQueue` | Loader readiness, planner and materializer adapters | Queue-owned job/keys and strong references | Until finish/cancel/clear/replacement |
|
||||
| Internal static M2 materialization | Parent, prepared Mesh, ordered transform slice and render settings | Loader / `M2StaticBatchMaterializer` | Attached MultiMeshInstance3D | Parent owns node/MultiMesh; exact Mesh reference retained | One main-thread build batch |
|
||||
| Internal WMO placement | Path, MODF placement, tile/index | Loader / `WmoPlacementResolver` | WMO caches, registry and three instance adapters | Value-only String/Transform3D | Lookup/placement lifetime |
|
||||
| Internal WMO ownership | Resolved placement key and tile/global reference key | Loader / `WmoPlacementRegistry` | Loader create/retain/final-free decisions | Registry-owned String sets; detached diagnostics | Map session or final release |
|
||||
| Internal WMO build step | Mesh/MultiMesh counts and job cursors | Loader / `WmoRenderBuildStepPlanner` | Loader materialization/cursor adapter | Fresh scalar Dictionary | One group operation |
|
||||
| Internal WMO pending build | Placement key, Node3D root, WMO Resource and cursors | Loader / `WmoRenderBuildQueue` | Loader drain and step planner adapter | Queue-owned job and strong references | Until cancel/clear/replacement |
|
||||
| Internal WMO group materialization | Parent root, exact Mesh/MultiMesh, indexed metadata and render settings | Loader / `WmoRenderGroupMaterializer` | Attached geometry node | Parent owns node and exact Resource reference | One main-thread group operation |
|
||||
| Internal WMO subtree preparation | Cached/live root, extracted directory and render policies | Loader / `WmoRuntimeScenePreparer` | Borrowed subtree and runtime Mesh finalizer | Loader/placement owns subtree; preparer retains nothing | One main-thread instance preparation |
|
||||
| Internal WMO instance creation | Cached PackedScene or live prototype, path and placement | Loader / `WmoSceneInstanceFactory` | Runtime scene preparer and attachment adapter | Factory owns candidate until detached-root transfer | One main-thread creation |
|
||||
| Internal WMO render cache | Normalized path, cache path and validated Resource | Loader / `WmoRenderResourceCacheState` | Loader lookup, ResourceLoader poll and build queue | State-owned Resource/path references; detached request snapshots | Until transient/full clear |
|
||||
| Internal WMO scene cache | Normalized path, `.tscn` path and validated PackedScene | Loader / `WmoSceneResourceCacheState` | Loader lookup, request poll and scene instantiation | State-owned PackedScene/path references; detached request snapshots | Until transient/full clear |
|
||||
| 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 raw M2 read | Extracted directory and normalized relative path | Loader/native observer via `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 | Cached observer / 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 animated M2 playback | Prototype/instance native animators, path/index and player inventory | Materializer/finalizer / `M2AnimationPlaybackController` | Native and imported animation state | Borrowed Nodes; detached optional diagnostics | One duplicated instance startup |
|
||||
| Internal animated M2 materialization | Parent, prototype, ordered transform slice and render settings | Loader / `M2AnimatedInstanceMaterializer` | Attached animated batch and indexed diagnostics | Parent owns batch; caller owns detached diagnostics | One main-thread build batch |
|
||||
| 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 |
|
||||
@@ -231,7 +274,12 @@ flowchart TD
|
||||
M2Registry --> M2Grouper[M2PlacementGrouper]
|
||||
M2Transform[M2PlacementTransformResolver] --> M2Grouper
|
||||
M2Grouper --> M2Batch[M2BuildBatchPlanner]
|
||||
M2Batch --> M2
|
||||
M2Grouper --> M2Queue[M2BuildQueue]
|
||||
M2Queue --> M2Batch
|
||||
M2Batch --> M2Resources[M2BuildResourceSnapshot]
|
||||
M2Resources --> M2Dispatch[M2BuildDispatchPlanner]
|
||||
M2Dispatch --> M2Static[M2StaticBatchMaterializer]
|
||||
M2Static --> M2
|
||||
R --> WmoPlacement[WmoPlacementResolver]
|
||||
WmoPlacement --> WmoRegistry[WmoPlacementRegistry]
|
||||
WmoRegistry --> WmoBuildQueue[WmoRenderBuildQueue]
|
||||
@@ -355,19 +403,43 @@ sequenceDiagram
|
||||
grouper owns worker-path final transforms; direct placeholder/instance
|
||||
transforms and every build/render side effect remain loader-owned.
|
||||
- `M2PlacementGrouper` is stateless and owns only call-local grouped transforms.
|
||||
The loader retains tasks, mutex/result queues, stale checks and build state.
|
||||
- `M2BuildBatchPlanner` is stateless and owns only call-local scalar plans. The
|
||||
loader retains queue/resource transitions, cursor adoption and materialization.
|
||||
The loader retains tasks, mutex/result queues and stale-result checks; accepted
|
||||
groups enter `M2BuildQueue` as typed pending jobs.
|
||||
- `M2BuildBatchPlanner` is stateless and owns only call-local scalar plans.
|
||||
`M2BuildDispatchPlanner` is stateless and owns only call-local action/transition
|
||||
plans after the loader observes resource availability. `M2BuildResourceSnapshot`
|
||||
carries those per-step observations and borrows exact prototype/Mesh references
|
||||
without controlling engine lifetime.
|
||||
`M2BuildQueue` owns typed pending jobs, FIFO/stale keys, grouped-transform
|
||||
references and group/offset/serial cursors without freeing engine objects.
|
||||
`M2StaticBuildResourceObserver` owns static Mesh lookup/request admission and
|
||||
snapshot adoption. `M2CachedAnimationResourceObserver` owns cached animated
|
||||
prototype lookup, GLB policy/request admission and snapshot production.
|
||||
`M2NativeAnimationResourceObserver` owns native candidate selection, raw read,
|
||||
detached build and prototype/static-only cache outcome.
|
||||
`M2StaticBatchMaterializer` owns static MultiMesh construction/attachment; the
|
||||
loader retains action execution, cursor adoption, root cleanup, budgets and
|
||||
Editor ownership.
|
||||
- `WmoPlacementResolver` is stateless and owns only call-local cache-key,
|
||||
identity and transform values. `WmoPlacementRegistry` owns only placement-key
|
||||
reference sets. `WmoRenderBuildStepPlanner` owns only a call-local operation
|
||||
and cursor plan. `WmoRenderBuildQueue` owns typed pending jobs, FIFO keys and
|
||||
strong root/resource references without freeing engine objects.
|
||||
`WmoRenderGroupMaterializer` owns indexed MeshInstance3D/MultiMeshInstance3D
|
||||
creation, render settings and attachment without retaining engine objects.
|
||||
`WmoRuntimeScenePreparer` owns cached-only Mesh traversal/finalization plus the
|
||||
shared direct-Occluders and recursive shadow policies for cached/live roots.
|
||||
`WmoSceneInstanceFactory` owns cached/live detached-root creation, cache
|
||||
validation, basename and placement application; loader retains source lookup,
|
||||
runtime preparation, attachment and lifetime.
|
||||
`WmoRenderResourceCacheState` owns validated render Resources, negative entries
|
||||
and pending cache paths. `WmoSceneResourceCacheState` similarly owns validated
|
||||
PackedScenes, negative entries and pending `.tscn` paths. The loader retains
|
||||
ResourceLoader/FileAccess I/O, size and cache-version validation, live fallback,
|
||||
materialization, permits, validity reactions and every Node lifecycle action.
|
||||
and pending cache paths; `WmoRenderResourceFinalizer` owns its terminal
|
||||
ResourceLoader polling and script/format validation. `WmoSceneResourceCacheState`
|
||||
similarly owns validated PackedScenes, negative entries and pending `.tscn`
|
||||
paths; `WmoSceneResourceFinalizer` owns terminal ResourceLoader I/O and
|
||||
validation-probe lifetime. The loader retains request admission, FileAccess
|
||||
size checks, live fallback, Mesh finalization, permits, Editor ownership,
|
||||
validity reactions and every placed-Node lifecycle action.
|
||||
- `AdtWaterLoadPipelineState` owns pending request order/deduplication, opaque
|
||||
active task IDs and the worker-safe parsed-result mailbox. The loader retains
|
||||
WorkerThreadPool start/wait, ADTLoader parsing, concurrency/finalize permits,
|
||||
@@ -378,18 +450,27 @@ sequenceDiagram
|
||||
for billboard/UV-rotation material refresh and is composed by the runtime Mesh
|
||||
finalizer. The raw repository loads value data; loader retains Mesh adoption.
|
||||
- `M2MeshLoadPipelineState` owns static M2 pending Resource paths, opaque
|
||||
terminal statuses and completion-order finalize FIFO. The loader retains cache
|
||||
path selection, ResourceLoader calls, permits and adoption decisions; prototype
|
||||
cache state owns shared missing outcomes.
|
||||
terminal statuses and completion-order finalize FIFO. The static observer owns
|
||||
cache path selection, request admission and initial snapshot adoption. The
|
||||
Mesh resource finalizer owns ResourceLoader polling/finalize and terminal
|
||||
adoption; loader retains permits and composition, while prototype cache state
|
||||
owns shared missing outcomes.
|
||||
- `M2AnimationLoadPipelineState` owns animated M2 pending Resource paths, opaque
|
||||
terminal statuses and completion-order finalize FIFO. The cached animation
|
||||
observer owns allow/deny/path/GLB selection, request admission and initial
|
||||
snapshot production. The native observer owns synchronous native build; the
|
||||
animation resource finalizer owns cached terminal polling/load/finalize and
|
||||
adoption. Loader retains permits and material-prototype lookup.
|
||||
- `M2MeshResourceCacheState` owns prepared static Mesh references and releases
|
||||
them at the existing final-shutdown site. Prototype state and materialization
|
||||
belong to the sibling cache service and loader respectively.
|
||||
- `M2MeshResourceExtractor` owns depth-first first-Mesh selection and temporary
|
||||
PackedScene instance destruction. The loader retains ResourceLoader I/O,
|
||||
cache/missing adoption and materialization.
|
||||
PackedScene instance destruction. The static observer admits ResourceLoader
|
||||
requests and initial cache/missing adoption; the Mesh resource finalizer owns
|
||||
terminal extraction/adoption, while the loader retains materialization.
|
||||
- `M2RuntimeMeshFinalizer` owns refresh version `2`, classifier lifetime,
|
||||
M2Builder rebuild and original-Mesh fallback. The loader loads raw data only
|
||||
after the finalizer reports that a cached Mesh is stale.
|
||||
M2Builder rebuild and original-Mesh fallback. `M2MeshResourceFinalizer` loads
|
||||
raw data only after the runtime finalizer reports that a cached Mesh is stale.
|
||||
- `M2RawModelRepository` owns FileAccess/ClassDB availability and the exact
|
||||
`load_m2`/`load_m2_animated` calls. The loader retains normalization, fallback
|
||||
order and every result consumer; `M2PrototypeCacheState` retains outcomes.
|
||||
@@ -496,6 +577,15 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
transition, completion/raw integer behavior, source ownership and bounded timing.
|
||||
- WMO render build queue contract: typed references/cursors, FIFO, duplicate
|
||||
replacement, stale-front cleanup, cancel/clear engine lifetime and bounded timing.
|
||||
- WMO render group materializer contract: exact Resource identity, indexed and
|
||||
fallback names/transforms, render settings, attachment, source ownership and
|
||||
bounded main-thread timing.
|
||||
- WMO runtime scene preparer contract: cached/live finalizer distinction,
|
||||
exact Mesh traversal order, direct occluder removal, recursive shadow policy,
|
||||
ownership boundaries and bounded main-thread timing.
|
||||
- WMO scene instance factory contract: cached validation-before-placement,
|
||||
stale/type rejection lifetime, live validator suppression, exact descendant
|
||||
Resource identity, naming/placement and bounded main-thread timing.
|
||||
- WMO render Resource cache contract: invalid/duplicate request rejection,
|
||||
validated/missing terminal transitions, transient/full reset, detached sorted
|
||||
diagnostics, loader-owned version validation and bounded timing.
|
||||
@@ -534,17 +624,36 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
|---|---|---|---|
|
||||
| ADT streaming/terrain | Partial | M00 checkpoints and current scenes | Fidelity/performance gaps remain |
|
||||
| Static M2 placement | Partial | MultiMesh/dedupe probes | Full materials/animation/effects |
|
||||
| M2 static batch materializer | Implemented extraction | Synthetic node/Mesh/render/source/timing contract | Asset-backed traversal/visual/GPU p95/p99 pending |
|
||||
| M2 unique placement registry | Implemented extraction | Scene-free ownership/lifecycle/timing contract and historical `uid:11785` smoke | Group/build/tasks/finalization and asset-backed p95/p99 remain pending |
|
||||
| M2 placement transform resolver | Implemented extraction | Scene-free formula/source/timing contract across three consumers | Asset-backed visual recheck and general placement parity pending |
|
||||
| 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 build batch planner | Implemented extraction | Scene-free limit/count/cursor/source/timing contract | Spatial cells and asset-backed p95/p99 remain pending |
|
||||
| M2 build dispatch planner | Implemented extraction | Scene-free priority/action/transition/source/timing contract | Resource observation/orchestration and asset-backed traversal remain pending |
|
||||
| M2 build resource snapshot | Implemented extraction | Typed identity/adoption/lifetime/source/timing contract | Resource observation service and asset-backed traversal remain pending |
|
||||
| M2 static build resource observer | Implemented extraction | Cache/pending/path/request/source/timing contract | Asset-backed traversal pending |
|
||||
| M2 cached animation resource observer | Implemented extraction | Cache/pending/policy/GLB/request/source/timing contract | Asset-backed traversal pending |
|
||||
| M2 native animation resource observer | Implemented extraction | Candidate/cache/raw/build/adoption/source/timing contract | Asset-backed traversal/leak/p95/p99 pending |
|
||||
| M2 animation resource finalizer | Implemented extraction | Status/FIFO/load/candidate/repair/adoption/source/timing contract | Asset-backed traversal/leak/p95/p99 pending |
|
||||
| M2 mesh resource finalizer | Implemented extraction | Status/FIFO/load/extract/prepare/adoption/source/timing contract | Asset-backed traversal/leak/p95/p99 pending |
|
||||
| M2 build queue | Implemented extraction | Typed lifecycle/FIFO/progress/lifetime/source/timing contract | Resource dispatch and asset-backed traversal 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 animation playback controller | Implemented extraction | Synthetic phase/selection/loop/native-copy/source/timing contract | Asset-backed animation timing/name/native comparison pending |
|
||||
| M2 animated instance materializer | Implemented extraction | Synthetic order/transform/render/playback/attachment/source/timing contract | Asset-backed traversal/visual/leak/p95/p99 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 |
|
||||
| WMO render build step planner | Implemented extraction | Scene-free order/cursor/source/timing contract | Asset-backed traversal p95/p99 pending |
|
||||
| WMO render build queue | Implemented extraction | Typed lifecycle/order/ownership/source/timing contract | Materialization and asset-backed traversal/leak evidence pending |
|
||||
| WMO render Resource cache state | Implemented extraction | Scene-free lifecycle/exclusivity/source/timing plus shutdown contract | ResourceLoader I/O and asset-backed traversal/leak evidence pending |
|
||||
| WMO scene Resource cache state | Implemented extraction | Scene-free lifecycle/direct-missing/source/timing plus shutdown contract | ResourceLoader/live-fallback extraction and asset-backed traversal/leak evidence pending |
|
||||
| WMO render build queue | Implemented extraction | Typed lifecycle/order/ownership/source/timing contract | Asset-backed traversal/leak evidence pending |
|
||||
| WMO render group materializer | Implemented extraction | Synthetic Resource/name/transform/render/attachment/source/timing contract | Asset-backed visual/leak/GPU p95/p99 pending |
|
||||
| WMO runtime scene preparer | Implemented extraction | Synthetic cached/live traversal/occluder/shadow/source/timing contract | Asset-backed visual/leak/GPU p95/p99 pending |
|
||||
| WMO scene instance factory | Implemented extraction | Synthetic cached/live type/identity/lifetime/name/placement/source/timing contract | Serialized/asset-backed traversal/leak evidence pending |
|
||||
| WMO render Resource cache state | Implemented extraction | Scene-free lifecycle/exclusivity/source/timing plus shutdown contract | Asset-backed traversal/leak evidence pending |
|
||||
| WMO render Resource finalizer | Implemented extraction | Status/order/script/format/adoption/source/timing contract | Serialized/asset-backed corrupt-cache and leak evidence pending |
|
||||
| WMO scene Resource cache state | Implemented extraction | Scene-free lifecycle/direct-missing/source/timing plus shutdown contract | Asset-backed traversal/leak evidence pending |
|
||||
| WMO scene Resource finalizer | Implemented extraction | Status/order/type/probe/lifetime/adoption/source/timing contract | Serialized stale/oversize and asset-backed evidence pending |
|
||||
| WMO runtime Mesh finalizer | Implemented extraction | Identity/version/material-definition/source/timing contract | Asset-backed visual/leak/GPU/p95/p99 evidence pending |
|
||||
| WMO rendering | Partial | Cached group rendering | Portals/rooms/material parity |
|
||||
| ADT water load pipeline state | Implemented extraction | Scene-free FIFO/task/thread/source/timing contract | Parse/finalization and asset-backed traversal/leak evidence pending |
|
||||
| Liquids | Partial | MH2O/MLIQ paths | LiquidType/depth/shore fidelity |
|
||||
@@ -599,12 +708,32 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/render/liquid/adt_water_load_pipeline_state.gd` | ADT water request/task/result bookkeeping and worker-safe mailbox |
|
||||
| `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_animation_resource_finalizer.gd` | Cached animation terminal polling/load/finalize outcomes |
|
||||
| `src/render/m2/m2_animated_scene_finalizer.gd` | Animated scene candidate ownership, material repair and player validation |
|
||||
| `src/render/m2/m2_animation_playback_controller.gd` | Per-instance phase, selection, native copy/start and imported playback |
|
||||
| `src/render/m2/m2_animated_instance_materializer.gd` | Animated duplicate/render/playback startup and non-empty batch attachment |
|
||||
| `src/render/m2/m2_static_batch_materializer.gd` | Static MultiMesh construction, render setup and attachment |
|
||||
| `src/render/m2/m2_build_dispatch_planner.gd` | M2 wait/materializer/advance action priority and transition plan |
|
||||
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step animated/static observations and borrowed references |
|
||||
| `src/render/m2/m2_static_build_resource_observer.gd` | Static Mesh lookup/request/missing snapshot production |
|
||||
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached animated GLB policy/request/snapshot production |
|
||||
| `src/render/m2/m2_build_job.gd` | M2 root/groups references and group/offset/serial progress |
|
||||
| `src/render/m2/m2_build_queue.gd` | Keyed pending jobs and FIFO/stale tile-key lifecycle |
|
||||
| `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 |
|
||||
| `src/render/m2/m2_mesh_resource_finalizer.gd` | Static terminal polling, Mesh preparation and cache/missing adoption |
|
||||
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Runtime refresh version, rebuild classification/build and fallback |
|
||||
| `src/render/m2/m2_raw_model_repository.gd` | Stateless static/animated native M2 file boundary |
|
||||
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native candidate/read/build/cache observation |
|
||||
| `src/render/m2/m2_prototype_cache_state.gd` | Detached prototype ownership and negative lookup outcomes |
|
||||
| `src/render/wmo/wmo_render_resource_finalizer.gd` | Lightweight WMO terminal polling, validation and publication |
|
||||
| `src/render/wmo/wmo_scene_resource_finalizer.gd` | Cached WMO terminal polling, probe validation/lifetime and publication |
|
||||
| `src/render/wmo/wmo_runtime_mesh_finalizer.gd` | Cached WMO runtime refresh admission, surface iteration and material reconstruction |
|
||||
| `src/render/wmo/wmo_render_group_materializer.gd` | Indexed lightweight WMO geometry-node creation, render setup and attachment |
|
||||
| `src/render/wmo/wmo_runtime_scene_preparer.gd` | Cached/live WMO subtree Mesh traversal and render policy |
|
||||
| `src/render/wmo/wmo_scene_instance_factory.gd` | Cached/live detached-root creation, validation, identity and placement |
|
||||
| `src/render/streaming/streaming_target_planner.gd` | Scene-free wanted/retained ADT target calculation |
|
||||
| `src/render/streaming/streaming_target_policy.gd` | Immutable renderer radius/prefetch policy |
|
||||
| `src/render/streaming/streaming_target_plan.gd` | Immutable planner result with read-only tile-key sets |
|
||||
@@ -620,12 +749,31 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/tools/verify_adt_water_load_pipeline_state.gd` | ADT water FIFO/task/thread/boundary/timing regression |
|
||||
| `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_animation_resource_finalizer.gd` | Animated M2 polling/load/finalize/adoption/boundary/timing regression |
|
||||
| `src/tools/verify_m2_mesh_resource_finalizer.gd` | Static M2 polling/extraction/preparation/adoption/boundary/timing regression |
|
||||
| `src/tools/verify_m2_animated_scene_finalizer.gd` | Animated M2 candidate/material/player/lifetime/boundary/timing regression |
|
||||
| `src/tools/verify_m2_animation_playback_controller.gd` | Animated M2 phase/selection/playback/native/boundary/timing regression |
|
||||
| `src/tools/verify_m2_animated_instance_materializer.gd` | Animated M2 order/render/playback/attachment/boundary/timing regression |
|
||||
| `src/tools/verify_m2_static_batch_materializer.gd` | Static M2 node/Mesh/render/attachment/boundary/timing regression |
|
||||
| `src/tools/verify_m2_build_dispatch_planner.gd` | M2 dispatch priority/action/transition/boundary/timing regression |
|
||||
| `src/tools/verify_m2_build_resource_snapshot.gd` | M2 observation identity/adoption/lifetime/boundary/timing regression |
|
||||
| `src/tools/verify_m2_static_build_resource_observer.gd` | Static lookup/request/path/boundary/timing regression |
|
||||
| `src/tools/verify_m2_cached_animation_resource_observer.gd` | Cached animation lifecycle/policy/GLB/boundary/timing regression |
|
||||
| `src/tools/verify_m2_build_queue.gd` | M2 job/FIFO/progress/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 |
|
||||
| `src/tools/verify_m2_runtime_mesh_finalizer.gd` | M2 current/stale/rebuild/fallback/boundary/timing regression |
|
||||
| `src/tools/verify_m2_raw_model_repository.gd` | M2 invalid/missing/native-boundary/dependency/timing regression |
|
||||
| `src/tools/verify_m2_native_animation_resource_observer.gd` | Native candidate/cache/raw/build/adoption/boundary/timing regression |
|
||||
| `src/tools/verify_m2_prototype_cache_state.gd` | M2 prototype identity/negative/lifecycle/boundary/timing regression |
|
||||
| `src/tools/verify_wmo_render_resource_finalizer.gd` | WMO render status/order/validation/adoption/boundary/timing regression |
|
||||
| `src/tools/verify_wmo_scene_resource_finalizer.gd` | WMO scene status/order/probe/lifetime/adoption/boundary/timing regression |
|
||||
| `src/tools/verify_wmo_runtime_mesh_finalizer.gd` | WMO Mesh identity/version/material-definition/boundary/timing regression |
|
||||
| `src/tools/verify_wmo_render_group_materializer.gd` | WMO render-group Resource/name/transform/render/attachment/boundary/timing regression |
|
||||
| `src/tools/verify_wmo_runtime_scene_preparer.gd` | WMO cached/live traversal/occluder/shadow/boundary/timing regression |
|
||||
| `src/tools/verify_wmo_scene_instance_factory.gd` | WMO cached/live type/identity/lifetime/name/placement/boundary/timing regression |
|
||||
| `src/tools/verify_streaming_target_planner.gd` | Planner behavior, dependency and bounded timing regression |
|
||||
| `src/tools/verify_render_budget_scheduler.gd` | Scheduler bounds, shared-lane priority, cancellation and timing regression |
|
||||
| `src/tools/verify_renderer_internal_access.gd` | Gameplay/EditorPlugin/registered renderer-tool boundary gate derived from private streamer fields |
|
||||
@@ -637,6 +785,9 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/native/src/*_loader.cpp` | Native binary parsing |
|
||||
| `src/tools/build_*cache.gd`, `src/tools/bake_*cache.gd` | Offline cache generation |
|
||||
| `tools/run_render_baseline.ps1` | Unified M00 baseline runner |
|
||||
| `tools/compare_render_performance.ps1` | Exact-environment single/repeated report comparator |
|
||||
| `tools/verify_render_performance_stability.ps1` | Repeated-sample/long-window repeatability gate |
|
||||
| `src/tools/verify_renderer_closeout_contracts.gd` | Worker, main-thread, cache-version and nested-GLB closeout contracts |
|
||||
| `src/tools/compare_render_checkpoints.gd` | Offline JPG/PNG paired-image perceptual metrics and JSON pass/fail report |
|
||||
| `src/tools/verify_render_runtime_cache_shutdown.gd` | Headless ownership regression for detached runtime prototypes, resource caches and empty liquid roots |
|
||||
| `src/tools/capture_render_checkpoints.gd` | Deterministic no-roll checkpoint camera, performance and visual capture |
|
||||
|
||||
@@ -6,6 +6,29 @@ Godot project scanning is disabled for this directory by `.gdignore`. Reference
|
||||
assets remain available to Git, text search and external tooling, but Godot must
|
||||
not generate `.import` sidecars inside nested reference repositories.
|
||||
|
||||
## Git reference revisions
|
||||
|
||||
Remote branches were fetched with pruning and the checked-out default branches
|
||||
were fast-forwarded on 2026-09-05. The parent repository pins the exact commits
|
||||
through gitlinks; `.gitmodules` records the branch used for an intentional future
|
||||
`git submodule update --remote`. Inspect local state before advancing the parent
|
||||
gitlink pins; checked-out branches in this refresh used `git pull --ff-only`.
|
||||
|
||||
| Reference | Canonical branch | Pinned commit |
|
||||
| --- | --- | --- |
|
||||
| `open-realm` | `main` | `950a1e8c1343a6e2139cc448f779cb2957a8d3f8` |
|
||||
| `whoa` | `master` | `ea1345636045635ac601b7d525eb0c38c6c0f6dd` |
|
||||
| `WoWee` | `master` | `607ea3b8369851014721416293f8e95dfbe64eec` |
|
||||
| `WowUnreal` | `main` | `c2a4b9827b2e7672e799085c5bf631d37c4ca5d3` |
|
||||
| `wow.export` | `main` | `c2fd7bde36a712be78a5da896c995b84fbfa2545` |
|
||||
| `benilla` | `main` | `bc1a2428dd7e00ca8abbfb8f0bf53750dae7b123` |
|
||||
| `blender-wow-studio/.../pywowlib` | `master` | `55276dc5c2195da7fe136638a2a59716622f8c65` |
|
||||
|
||||
`blender-wow-studio-3.4-1.1.0_Experimental` and `noggit-red` are tracked
|
||||
snapshots, not top-level gitlinks. They do not have a local branch and require a
|
||||
separate, provenance-checked snapshot refresh instead of a blind pull. The
|
||||
embedded `pywowlib` gitlink is maintained independently as listed above.
|
||||
|
||||
- `blender-wow-studio-3.4-1.1.0_Experimental/` - Blender add-on sources for M2/WMO import/export reference.
|
||||
- `wow.export/` - wow.export source tree and Blender OBJ importer reference.
|
||||
- `noggit-red/` - Noggit RED source tree from `https://gitlab.com/dirtbikercj/noggit-red`, used as a reference for WoW 3.3.5 map editing, ADT/WMO/M2 placement behavior, UID handling and editor workflows.
|
||||
|
||||
+1
-1
Submodule reference/WoWee updated: 626243e937...607ea3b836
Submodule
+1
Submodule reference/benilla added at bc1a2428dd
+1
-1
Submodule reference/open-realm updated: c7ed0545c8...950a1e8c13
+1
-1
Submodule reference/whoa updated: 74bc963a1c...ea13456360
+1
-1
Submodule reference/wow.export updated: 7f8d4f8784...c2fd7bde36
@@ -0,0 +1,120 @@
|
||||
class_name M2AnimatedInstanceMaterializer
|
||||
extends RefCounted
|
||||
|
||||
## Duplicates one ordered animated M2 batch, applies instance render settings,
|
||||
## starts playback and attaches a non-empty batch to the supplied parent.
|
||||
## All calls mutate Godot scene objects and therefore belong on the main thread.
|
||||
|
||||
const M2_ANIMATED_SCENE_FINALIZER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_animated_scene_finalizer.gd"
|
||||
)
|
||||
const M2_ANIMATION_PLAYBACK_CONTROLLER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_animation_playback_controller.gd"
|
||||
)
|
||||
|
||||
var _animated_scene_finalizer := M2_ANIMATED_SCENE_FINALIZER_SCRIPT.new()
|
||||
var _animation_playback_controller := M2_ANIMATION_PLAYBACK_CONTROLLER_SCRIPT.new()
|
||||
|
||||
|
||||
## Returns the attached batch root and detached diagnostic entries. Invalid or
|
||||
## empty input, and a batch whose duplicates all fail, returns an empty result.
|
||||
## The caller owns the returned/attached batch through `m2_parent_root`.
|
||||
func materialize_batch(
|
||||
m2_parent_root: Node3D,
|
||||
relative_path: String,
|
||||
prototype: Node3D,
|
||||
transforms: Array,
|
||||
start_index: int,
|
||||
instance_count: int,
|
||||
batch_serial: int,
|
||||
visibility_range_end: float,
|
||||
visibility_range_margin: float,
|
||||
cast_shadows: bool,
|
||||
native_animator_script: Script,
|
||||
collect_native_diagnostics: bool
|
||||
) -> Dictionary:
|
||||
if (
|
||||
m2_parent_root == null
|
||||
or transforms.is_empty()
|
||||
or instance_count <= 0
|
||||
or prototype == null
|
||||
):
|
||||
return {}
|
||||
|
||||
var model_name := relative_path.get_file().get_basename()
|
||||
var batch_root := Node3D.new()
|
||||
batch_root.name = "%s_anim_%d" % [model_name, batch_serial]
|
||||
var diagnostic_entries: Array[Dictionary] = []
|
||||
for batch_offset in instance_count:
|
||||
var instance_index := start_index + batch_offset
|
||||
var instance := prototype.duplicate(
|
||||
Node.DUPLICATE_SIGNALS | Node.DUPLICATE_GROUPS | Node.DUPLICATE_SCRIPTS
|
||||
) as Node3D
|
||||
if instance == null:
|
||||
continue
|
||||
instance.name = "%s_%d" % [model_name, instance_index]
|
||||
instance.transform = transforms[instance_index]
|
||||
_apply_visibility_range_recursive(
|
||||
instance,
|
||||
visibility_range_end,
|
||||
visibility_range_margin
|
||||
)
|
||||
_apply_shadow_cast_recursive(instance, cast_shadows)
|
||||
_animation_playback_controller.copy_native_animator_data(
|
||||
prototype,
|
||||
instance,
|
||||
native_animator_script
|
||||
)
|
||||
batch_root.add_child(instance)
|
||||
var animation_players := _animated_scene_finalizer.animation_players_in_subtree(
|
||||
instance
|
||||
)
|
||||
var native_diagnostics := _animation_playback_controller.start_instance_playback(
|
||||
instance,
|
||||
relative_path,
|
||||
instance_index,
|
||||
native_animator_script,
|
||||
animation_players,
|
||||
collect_native_diagnostics
|
||||
)
|
||||
for native_diagnostic in native_diagnostics:
|
||||
diagnostic_entries.append({
|
||||
"instance_index": instance_index,
|
||||
"state": native_diagnostic,
|
||||
})
|
||||
|
||||
if batch_root.get_child_count() <= 0:
|
||||
batch_root.queue_free()
|
||||
return {}
|
||||
m2_parent_root.add_child(batch_root)
|
||||
return {
|
||||
"batch_root": batch_root,
|
||||
"native_diagnostics": diagnostic_entries,
|
||||
}
|
||||
|
||||
|
||||
func _apply_visibility_range_recursive(
|
||||
node: Node,
|
||||
range_end: float,
|
||||
range_end_margin: float
|
||||
) -> void:
|
||||
if range_end <= 0.0:
|
||||
return
|
||||
if node is GeometryInstance3D:
|
||||
var geometry := node as GeometryInstance3D
|
||||
geometry.visibility_range_end = range_end
|
||||
geometry.visibility_range_end_margin = range_end_margin
|
||||
for child in node.get_children():
|
||||
_apply_visibility_range_recursive(child, range_end, range_end_margin)
|
||||
|
||||
|
||||
func _apply_shadow_cast_recursive(node: Node, cast_shadows: bool) -> void:
|
||||
if node is GeometryInstance3D:
|
||||
var geometry := node as GeometryInstance3D
|
||||
geometry.cast_shadow = (
|
||||
GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
||||
if cast_shadows
|
||||
else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
)
|
||||
for child in node.get_children():
|
||||
_apply_shadow_cast_recursive(child, cast_shadows)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dkgy3rvsmlkvm
|
||||
@@ -0,0 +1,115 @@
|
||||
class_name M2AnimatedSceneFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Instantiates and validates detached animated M2 scene candidates and repairs
|
||||
## their surface materials from an existing static material prototype.
|
||||
## The caller owns ResourceLoader I/O, permits and prototype cache adoption.
|
||||
|
||||
|
||||
## Instantiates one PackedScene as a detached Node3D candidate. Unsupported
|
||||
## Resources return null; a created non-Node3D root is freed before return.
|
||||
func instantiate_candidate(resource: Resource) -> Node3D:
|
||||
if not (resource is PackedScene):
|
||||
return null
|
||||
var candidate_root := (resource as PackedScene).instantiate()
|
||||
if candidate_root is Node3D:
|
||||
return candidate_root as Node3D
|
||||
if candidate_root != null:
|
||||
candidate_root.free()
|
||||
return null
|
||||
|
||||
|
||||
## Applies the historical depth-first source-to-target material mapping. A null
|
||||
## source or target leaves the candidate unchanged.
|
||||
func repair_materials(animated_root: Node3D, material_source_root: Node3D) -> void:
|
||||
if animated_root == null or material_source_root == null:
|
||||
return
|
||||
var source_meshes := mesh_instances_in_subtree(material_source_root)
|
||||
var target_meshes := mesh_instances_in_subtree(animated_root)
|
||||
if source_meshes.is_empty() or target_meshes.is_empty():
|
||||
return
|
||||
|
||||
var fallback_material := _first_mesh_material(source_meshes)
|
||||
for target_index in target_meshes.size():
|
||||
var target := target_meshes[target_index]
|
||||
if target == null or target.mesh == null:
|
||||
continue
|
||||
var source := source_meshes[mini(target_index, source_meshes.size() - 1)]
|
||||
for surface_index in target.mesh.get_surface_count():
|
||||
var material := _mesh_instance_surface_material(source, surface_index)
|
||||
if material == null:
|
||||
material = fallback_material
|
||||
if material != null:
|
||||
target.set_surface_override_material(surface_index, material)
|
||||
|
||||
|
||||
## Accepts a detached candidate only when it contains an AnimationPlayer.
|
||||
## Rejected candidates are freed synchronously. A successful result transfers
|
||||
## the exact prototype reference and its AnimationPlayer count to the caller.
|
||||
func finalize_candidate(candidate_root: Node3D) -> Dictionary:
|
||||
if candidate_root == null:
|
||||
return {}
|
||||
var animation_players := animation_players_in_subtree(candidate_root)
|
||||
if animation_players.is_empty():
|
||||
candidate_root.free()
|
||||
return {}
|
||||
return {
|
||||
"prototype": candidate_root,
|
||||
"animation_player_count": animation_players.size(),
|
||||
}
|
||||
|
||||
|
||||
## Returns MeshInstance3D descendants in depth-first preorder, including root.
|
||||
func mesh_instances_in_subtree(root: Node) -> Array[MeshInstance3D]:
|
||||
var mesh_instances: Array[MeshInstance3D] = []
|
||||
if root != null:
|
||||
_collect_mesh_instances(root, mesh_instances)
|
||||
return mesh_instances
|
||||
|
||||
|
||||
## Returns AnimationPlayer descendants in depth-first preorder, including root.
|
||||
func animation_players_in_subtree(root: Node) -> Array[AnimationPlayer]:
|
||||
var animation_players: Array[AnimationPlayer] = []
|
||||
if root != null:
|
||||
_collect_animation_players(root, animation_players)
|
||||
return animation_players
|
||||
|
||||
|
||||
func _collect_mesh_instances(node: Node, result: Array[MeshInstance3D]) -> void:
|
||||
if node is MeshInstance3D:
|
||||
result.append(node as MeshInstance3D)
|
||||
for child in node.get_children():
|
||||
_collect_mesh_instances(child, result)
|
||||
|
||||
|
||||
func _collect_animation_players(node: Node, result: Array[AnimationPlayer]) -> void:
|
||||
if node is AnimationPlayer:
|
||||
result.append(node as AnimationPlayer)
|
||||
for child in node.get_children():
|
||||
_collect_animation_players(child, result)
|
||||
|
||||
|
||||
func _first_mesh_material(meshes: Array[MeshInstance3D]) -> Material:
|
||||
for mesh_instance in meshes:
|
||||
if mesh_instance == null or mesh_instance.mesh == null:
|
||||
continue
|
||||
for surface_index in mesh_instance.mesh.get_surface_count():
|
||||
var material := _mesh_instance_surface_material(mesh_instance, surface_index)
|
||||
if material != null:
|
||||
return material
|
||||
return null
|
||||
|
||||
|
||||
func _mesh_instance_surface_material(
|
||||
mesh_instance: MeshInstance3D,
|
||||
surface_index: int
|
||||
) -> Material:
|
||||
if mesh_instance == null or mesh_instance.mesh == null:
|
||||
return null
|
||||
if surface_index < mesh_instance.get_surface_override_material_count():
|
||||
var override_material := mesh_instance.get_surface_override_material(surface_index)
|
||||
if override_material != null:
|
||||
return override_material
|
||||
if surface_index < mesh_instance.mesh.get_surface_count():
|
||||
return mesh_instance.mesh.surface_get_material(surface_index)
|
||||
return null
|
||||
@@ -0,0 +1 @@
|
||||
uid://cjjim3v675w66
|
||||
@@ -0,0 +1,102 @@
|
||||
class_name M2AnimationLoadPipelineState
|
||||
extends RefCounted
|
||||
|
||||
## Owns animated M2 threaded-load request records and the terminal finalize FIFO.
|
||||
## The caller owns ResourceLoader I/O, permits, Nodes and prototype outcomes.
|
||||
|
||||
var _request_by_normalized_path: Dictionary = {}
|
||||
var _finalize_records: Array[Dictionary] = []
|
||||
|
||||
|
||||
## Records one normalized M2 path and ResourceLoader path. Empty or duplicate
|
||||
## values are rejected without mutation.
|
||||
func remember_request(normalized_relative_path: String, resource_path: String) -> bool:
|
||||
if normalized_relative_path.is_empty() or resource_path.is_empty():
|
||||
return false
|
||||
if _request_by_normalized_path.has(normalized_relative_path):
|
||||
return false
|
||||
_request_by_normalized_path[normalized_relative_path] = {
|
||||
"normalized": normalized_relative_path,
|
||||
"path": resource_path,
|
||||
}
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether one normalized path currently has a pending threaded request.
|
||||
func has_request(normalized_relative_path: String) -> bool:
|
||||
return (
|
||||
not normalized_relative_path.is_empty()
|
||||
and _request_by_normalized_path.has(normalized_relative_path)
|
||||
)
|
||||
|
||||
|
||||
## Returns detached pending records in their existing Dictionary insertion order.
|
||||
func request_records_snapshot() -> Array[Dictionary]:
|
||||
var records: Array[Dictionary] = []
|
||||
for request_variant in _request_by_normalized_path.values():
|
||||
var request: Dictionary = request_variant
|
||||
records.append(request.duplicate())
|
||||
return records
|
||||
|
||||
|
||||
## Removes one pending request and appends its terminal status to the finalize
|
||||
## FIFO. Unknown paths are rejected without mutation.
|
||||
func complete_request(normalized_relative_path: String, terminal_status: int) -> bool:
|
||||
if not _request_by_normalized_path.has(normalized_relative_path):
|
||||
return false
|
||||
var request: Dictionary = _request_by_normalized_path[normalized_relative_path]
|
||||
_request_by_normalized_path.erase(normalized_relative_path)
|
||||
var finalize_record := request.duplicate()
|
||||
finalize_record["status"] = terminal_status
|
||||
_finalize_records.append(finalize_record)
|
||||
return true
|
||||
|
||||
|
||||
## Removes one pending request without enqueuing finalization.
|
||||
func discard_request(normalized_relative_path: String) -> bool:
|
||||
return _request_by_normalized_path.erase(normalized_relative_path)
|
||||
|
||||
|
||||
## Returns whether a terminal record is waiting for budgeted finalization.
|
||||
func has_finalize_record() -> bool:
|
||||
return not _finalize_records.is_empty()
|
||||
|
||||
|
||||
## Removes and returns the oldest terminal record. Empty means none is ready.
|
||||
func pop_finalize_record() -> Dictionary:
|
||||
if _finalize_records.is_empty():
|
||||
return {}
|
||||
return _finalize_records.pop_front()
|
||||
|
||||
|
||||
## Returns pending plus terminal-finalize work for existing renderer metrics.
|
||||
func total_work_count() -> int:
|
||||
return _request_by_normalized_path.size() + _finalize_records.size()
|
||||
|
||||
|
||||
## Returns the pending request count.
|
||||
func pending_request_count() -> int:
|
||||
return _request_by_normalized_path.size()
|
||||
|
||||
|
||||
## Returns the terminal finalize FIFO count.
|
||||
func finalize_record_count() -> int:
|
||||
return _finalize_records.size()
|
||||
|
||||
|
||||
## Clears request/finalize bookkeeping. The caller must drain ResourceLoader
|
||||
## requests before orderly shutdown clear.
|
||||
func clear() -> void:
|
||||
_request_by_normalized_path.clear()
|
||||
_finalize_records.clear()
|
||||
|
||||
|
||||
## Returns detached request and finalize records without loaded Resources/Nodes.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
var finalize_records: Array[Dictionary] = []
|
||||
for record in _finalize_records:
|
||||
finalize_records.append(record.duplicate())
|
||||
return {
|
||||
"requests": request_records_snapshot(),
|
||||
"finalize_records": finalize_records,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://h5rk6njgaujd
|
||||
@@ -0,0 +1,117 @@
|
||||
class_name M2AnimationPlaybackController
|
||||
extends RefCounted
|
||||
|
||||
## Applies deterministic playback to one animated M2 instance and copies the
|
||||
## runtime fields of exact-script native animators. The materializer owns
|
||||
## instance duplication/attachment; its caller owns diagnostic log formatting.
|
||||
|
||||
|
||||
## Copies the five historical runtime fields between matching native animators
|
||||
## in depth-first order. References are intentionally retained exactly.
|
||||
func copy_native_animator_data(
|
||||
source_root: Node,
|
||||
target_root: Node,
|
||||
native_animator_script: Script
|
||||
) -> void:
|
||||
var source_animators := native_animators_in_subtree(source_root, native_animator_script)
|
||||
var target_animators := native_animators_in_subtree(target_root, native_animator_script)
|
||||
for animator_index in range(mini(source_animators.size(), target_animators.size())):
|
||||
var source := source_animators[animator_index]
|
||||
var target := target_animators[animator_index]
|
||||
target.mesh_instance_path = source.mesh_instance_path
|
||||
target.bones = source.bones
|
||||
target.surfaces = source.surfaces
|
||||
target.animation_length = source.animation_length
|
||||
target.playback_speed = source.playback_speed
|
||||
|
||||
|
||||
## Prepares native animators, applies deterministic phase, and starts supplied
|
||||
## AnimationPlayers with the historical loop/name/seek rules. Runtime debug
|
||||
## state is sampled only when requested and returned as detached Dictionaries.
|
||||
func start_instance_playback(
|
||||
root: Node,
|
||||
relative_path: String,
|
||||
instance_index: int,
|
||||
native_animator_script: Script,
|
||||
animation_players: Array[AnimationPlayer],
|
||||
collect_native_diagnostics: bool
|
||||
) -> Array[Dictionary]:
|
||||
var phase := phase_for_instance(relative_path, instance_index)
|
||||
var native_diagnostics: Array[Dictionary] = []
|
||||
for animator in native_animators_in_subtree(root, native_animator_script):
|
||||
if animator.has_method("prepare_runtime_at_phase"):
|
||||
animator.prepare_runtime_at_phase(phase)
|
||||
else:
|
||||
if animator.has_method("prepare_runtime"):
|
||||
animator.prepare_runtime()
|
||||
animator.set_phase(phase)
|
||||
if collect_native_diagnostics and animator.has_method("runtime_debug_state"):
|
||||
var diagnostic_variant = animator.runtime_debug_state()
|
||||
if diagnostic_variant is Dictionary:
|
||||
native_diagnostics.append((diagnostic_variant as Dictionary).duplicate(true))
|
||||
|
||||
for player in animation_players:
|
||||
if player == null:
|
||||
continue
|
||||
var animation_name := choose_default_animation(player, relative_path)
|
||||
if animation_name.is_empty():
|
||||
continue
|
||||
for available_name in player.get_animation_list():
|
||||
var available_animation := player.get_animation(available_name)
|
||||
if available_animation != null:
|
||||
available_animation.loop_mode = Animation.LOOP_LINEAR
|
||||
player.play(animation_name)
|
||||
var selected_animation := player.get_animation(animation_name)
|
||||
if selected_animation != null and selected_animation.length > 0.0:
|
||||
player.seek(selected_animation.length * phase, true)
|
||||
return native_diagnostics
|
||||
|
||||
|
||||
## Returns exact-script native animators in depth-first preorder.
|
||||
func native_animators_in_subtree(
|
||||
root: Node,
|
||||
native_animator_script: Script
|
||||
) -> Array[Node]:
|
||||
var native_animators: Array[Node] = []
|
||||
if root != null and native_animator_script != null:
|
||||
_collect_native_animators(root, native_animator_script, native_animators)
|
||||
return native_animators
|
||||
|
||||
|
||||
## Returns the existing stable path/index phase in the inclusive-lower,
|
||||
## exclusive-upper range [0, 1).
|
||||
func phase_for_instance(relative_path: String, instance_index: int) -> float:
|
||||
return float(abs(("%s:%d" % [relative_path, instance_index]).hash()) % 1000) / 1000.0
|
||||
|
||||
|
||||
## Chooses the historical default animation name for ordinary, fish and bird
|
||||
## paths, then case-insensitive substring and first-name fallbacks.
|
||||
func choose_default_animation(player: AnimationPlayer, relative_path: String = "") -> String:
|
||||
if player == null:
|
||||
return ""
|
||||
var lower_path := relative_path.to_lower()
|
||||
var candidates: Array[String] = ["Stand", "Idle", "Run", "Walk"]
|
||||
if lower_path.contains("fish"):
|
||||
candidates = ["Run", "Walk", "Swim", "Stand", "Idle", "Death"]
|
||||
elif lower_path.contains("eagle") or lower_path.contains("bird") or lower_path.contains("gull"):
|
||||
candidates = ["Run", "Walk", "Swim", "Stand", "Idle"]
|
||||
for candidate in candidates:
|
||||
if player.has_animation(candidate):
|
||||
return candidate
|
||||
var available_names := player.get_animation_list()
|
||||
for candidate in ["run", "walk", "swim", "stand", "idle", "loop"]:
|
||||
for available_name in available_names:
|
||||
if String(available_name).to_lower().contains(candidate):
|
||||
return String(available_name)
|
||||
return String(available_names[0]) if not available_names.is_empty() else ""
|
||||
|
||||
|
||||
func _collect_native_animators(
|
||||
node: Node,
|
||||
native_animator_script: Script,
|
||||
result: Array[Node]
|
||||
) -> void:
|
||||
if node.get_script() == native_animator_script:
|
||||
result.append(node)
|
||||
for child in node.get_children():
|
||||
_collect_native_animators(child, native_animator_script, result)
|
||||
@@ -0,0 +1 @@
|
||||
uid://c6oln217sml80
|
||||
@@ -0,0 +1,187 @@
|
||||
class_name M2AnimationResourceFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Polls cached animated M2 ResourceLoader requests and finalizes terminal scenes.
|
||||
## The caller owns permits, material-source lookup and SceneTree materialization.
|
||||
|
||||
const M2_ANIMATED_SCENE_FINALIZER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_animated_scene_finalizer.gd"
|
||||
)
|
||||
|
||||
var _animated_scene_finalizer: RefCounted
|
||||
var _resource_loader_adapter: Object
|
||||
|
||||
|
||||
func _init(
|
||||
animated_scene_finalizer: RefCounted = null,
|
||||
resource_loader_adapter: Object = null
|
||||
) -> void:
|
||||
_animated_scene_finalizer = animated_scene_finalizer
|
||||
if _animated_scene_finalizer == null:
|
||||
_animated_scene_finalizer = M2_ANIMATED_SCENE_FINALIZER_SCRIPT.new()
|
||||
_resource_loader_adapter = resource_loader_adapter
|
||||
|
||||
|
||||
## Moves terminal threaded requests into the pipeline finalize FIFO.
|
||||
## Empty resource paths retain the historical immediate static-only outcome.
|
||||
func poll_terminal_requests(
|
||||
load_pipeline_state: RefCounted,
|
||||
prototype_cache_state: RefCounted
|
||||
) -> int:
|
||||
if load_pipeline_state == null or prototype_cache_state == null:
|
||||
return 0
|
||||
var completed_request_count := 0
|
||||
var request_records: Array = load_pipeline_state.call(
|
||||
"request_records_snapshot"
|
||||
)
|
||||
for request_variant in request_records:
|
||||
if not (request_variant is Dictionary):
|
||||
continue
|
||||
var request: Dictionary = request_variant
|
||||
var normalized_relative_path := String(request.get("normalized", ""))
|
||||
var resource_path := String(request.get("path", ""))
|
||||
if resource_path.is_empty():
|
||||
load_pipeline_state.call(
|
||||
"discard_request",
|
||||
normalized_relative_path
|
||||
)
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
completed_request_count += 1
|
||||
continue
|
||||
var load_status := load_threaded_get_status(resource_path)
|
||||
if (
|
||||
load_status != ResourceLoader.THREAD_LOAD_LOADED
|
||||
and load_status != ResourceLoader.THREAD_LOAD_FAILED
|
||||
):
|
||||
continue
|
||||
load_pipeline_state.call(
|
||||
"complete_request",
|
||||
normalized_relative_path,
|
||||
load_status
|
||||
)
|
||||
completed_request_count += 1
|
||||
return completed_request_count
|
||||
|
||||
|
||||
## Pops one terminal record and prepares a detached animated scene candidate.
|
||||
## An empty result means the record was skipped or resolved as static-only.
|
||||
func prepare_next_candidate(
|
||||
load_pipeline_state: RefCounted,
|
||||
prototype_cache_state: RefCounted
|
||||
) -> Dictionary:
|
||||
if load_pipeline_state == null or prototype_cache_state == null:
|
||||
return {}
|
||||
var terminal_record: Dictionary = load_pipeline_state.call(
|
||||
"pop_finalize_record"
|
||||
)
|
||||
if terminal_record.is_empty():
|
||||
return {}
|
||||
var normalized_relative_path := String(
|
||||
terminal_record.get("normalized", "")
|
||||
)
|
||||
if (
|
||||
normalized_relative_path.is_empty()
|
||||
or bool(prototype_cache_state.call(
|
||||
"has_animated_prototype",
|
||||
normalized_relative_path
|
||||
))
|
||||
or bool(prototype_cache_state.call(
|
||||
"is_animation_static",
|
||||
normalized_relative_path
|
||||
))
|
||||
):
|
||||
return {}
|
||||
if int(terminal_record.get(
|
||||
"status",
|
||||
ResourceLoader.THREAD_LOAD_FAILED
|
||||
)) != ResourceLoader.THREAD_LOAD_LOADED:
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
return {}
|
||||
|
||||
var resource_path := String(terminal_record.get("path", ""))
|
||||
var loaded_resource := load_threaded_get(resource_path)
|
||||
var candidate := _animated_scene_finalizer.call(
|
||||
"instantiate_candidate",
|
||||
loaded_resource
|
||||
) as Node3D
|
||||
if candidate == null:
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
return {}
|
||||
return {
|
||||
"normalized": normalized_relative_path,
|
||||
"path": resource_path,
|
||||
"candidate": candidate,
|
||||
}
|
||||
|
||||
|
||||
## Repairs, validates and adopts one prepared candidate. The preparation must be
|
||||
## completed synchronously so its detached candidate always receives an owner.
|
||||
func finalize_prepared_candidate(
|
||||
preparation: Dictionary,
|
||||
material_source_root: Node3D,
|
||||
prototype_cache_state: RefCounted,
|
||||
debug_logging_enabled: bool = false
|
||||
) -> Node3D:
|
||||
if prototype_cache_state == null:
|
||||
return null
|
||||
var normalized_relative_path := String(preparation.get("normalized", ""))
|
||||
var candidate := preparation.get("candidate", null) as Node3D
|
||||
if normalized_relative_path.is_empty() or candidate == null:
|
||||
return null
|
||||
_animated_scene_finalizer.call(
|
||||
"repair_materials",
|
||||
candidate,
|
||||
material_source_root
|
||||
)
|
||||
var finalization: Dictionary = _animated_scene_finalizer.call(
|
||||
"finalize_candidate",
|
||||
candidate
|
||||
)
|
||||
var prototype := finalization.get("prototype", null) as Node3D
|
||||
if prototype == null:
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
return null
|
||||
var canonical_prototype := prototype_cache_state.call(
|
||||
"adopt_animated_prototype",
|
||||
normalized_relative_path,
|
||||
prototype
|
||||
) as Node3D
|
||||
if debug_logging_enabled:
|
||||
print("M2_ANIM_CACHE path=%s cache=%s players=%d" % [
|
||||
normalized_relative_path,
|
||||
String(preparation.get("path", "")),
|
||||
int(finalization.get("animation_player_count", 0)),
|
||||
])
|
||||
return canonical_prototype
|
||||
|
||||
|
||||
## Production ResourceLoader status adapter; injectable in synthetic tests.
|
||||
func load_threaded_get_status(resource_path: String) -> int:
|
||||
if _resource_loader_adapter != null:
|
||||
return int(_resource_loader_adapter.call(
|
||||
"load_threaded_get_status",
|
||||
resource_path
|
||||
))
|
||||
return ResourceLoader.load_threaded_get_status(resource_path)
|
||||
|
||||
|
||||
## Production ResourceLoader terminal-result adapter; injectable in tests.
|
||||
func load_threaded_get(resource_path: String) -> Resource:
|
||||
if _resource_loader_adapter != null:
|
||||
return _resource_loader_adapter.call(
|
||||
"load_threaded_get",
|
||||
resource_path
|
||||
) as Resource
|
||||
return ResourceLoader.load_threaded_get(resource_path)
|
||||
@@ -0,0 +1 @@
|
||||
uid://2rnsjucg3dul
|
||||
@@ -0,0 +1,47 @@
|
||||
class_name M2BuildDispatchPlanner
|
||||
extends RefCounted
|
||||
|
||||
## Selects the next M2 build action from already observed resource state.
|
||||
## The planner retains no state or engine-object references.
|
||||
|
||||
const ACTION_WAIT_FOR_ANIMATION := &"wait_for_animation"
|
||||
const ACTION_MATERIALIZE_ANIMATED := &"materialize_animated"
|
||||
const ACTION_WAIT_FOR_STATIC_MESH := &"wait_for_static_mesh"
|
||||
const ACTION_MATERIALIZE_STATIC := &"materialize_static"
|
||||
const ACTION_ADVANCE_WITHOUT_MATERIALIZATION := &"advance_without_materialization"
|
||||
|
||||
|
||||
## Returns a detached action/transition plan for one M2 build operation.
|
||||
func plan_step(
|
||||
batch_count: int,
|
||||
resource_snapshot: RefCounted
|
||||
) -> Dictionary:
|
||||
if resource_snapshot == null:
|
||||
return _wait_plan(ACTION_WAIT_FOR_STATIC_MESH)
|
||||
if bool(resource_snapshot.call("animation_request_pending")):
|
||||
return _wait_plan(ACTION_WAIT_FOR_ANIMATION)
|
||||
if batch_count <= 0:
|
||||
return _advance_plan(ACTION_ADVANCE_WITHOUT_MATERIALIZATION, false)
|
||||
if bool(resource_snapshot.call("has_animated_prototype")):
|
||||
return _advance_plan(ACTION_MATERIALIZE_ANIMATED, true)
|
||||
if bool(resource_snapshot.call("has_static_mesh")):
|
||||
return _advance_plan(ACTION_MATERIALIZE_STATIC, true)
|
||||
if bool(resource_snapshot.call("static_model_missing")):
|
||||
return _advance_plan(ACTION_ADVANCE_WITHOUT_MATERIALIZATION, true)
|
||||
return _wait_plan(ACTION_WAIT_FOR_STATIC_MESH)
|
||||
|
||||
|
||||
func _wait_plan(action: StringName) -> Dictionary:
|
||||
return {
|
||||
"action": action,
|
||||
"rotate_queue": true,
|
||||
"increment_batch_serial": false,
|
||||
}
|
||||
|
||||
|
||||
func _advance_plan(action: StringName, increment_batch_serial: bool) -> Dictionary:
|
||||
return {
|
||||
"action": action,
|
||||
"rotate_queue": false,
|
||||
"increment_batch_serial": increment_batch_serial,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://df0bv1x2x4j2x
|
||||
@@ -0,0 +1,78 @@
|
||||
class_name M2BuildJob
|
||||
extends RefCounted
|
||||
|
||||
## Holds one M2 build cursor, grouped transforms and a strong root reference.
|
||||
## Releasing this record never frees the referenced Node.
|
||||
|
||||
var _tile_key: String
|
||||
var _root: Node3D
|
||||
var _groups: Dictionary
|
||||
var _group_keys: Array
|
||||
var _group_index: int = 0
|
||||
var _transform_offset: int = 0
|
||||
var _batch_serial: int = 0
|
||||
|
||||
|
||||
func _init(tile_key: String, root: Node3D, groups: Dictionary) -> void:
|
||||
_tile_key = tile_key
|
||||
_root = root
|
||||
_groups = groups
|
||||
_group_keys = groups.keys()
|
||||
|
||||
|
||||
## Returns the immutable tile identity used by the queue.
|
||||
func tile_key() -> String:
|
||||
return _tile_key
|
||||
|
||||
|
||||
## Returns the strongly referenced M2 scene root without transferring ownership.
|
||||
func root() -> Node3D:
|
||||
return _root
|
||||
|
||||
|
||||
## Returns the exact grouped-transform Dictionary supplied at enqueue time.
|
||||
func groups() -> Dictionary:
|
||||
return _groups
|
||||
|
||||
|
||||
## Returns the insertion-order group-key snapshot created at enqueue time.
|
||||
func group_keys() -> Array:
|
||||
return _group_keys
|
||||
|
||||
|
||||
## Returns the current model-path group cursor.
|
||||
func group_index() -> int:
|
||||
return _group_index
|
||||
|
||||
|
||||
## Returns the current transform offset within the selected model-path group.
|
||||
func transform_offset() -> int:
|
||||
return _transform_offset
|
||||
|
||||
|
||||
## Returns the next static/animated batch serial used for node naming.
|
||||
func batch_serial() -> int:
|
||||
return _batch_serial
|
||||
|
||||
|
||||
## Atomically adopts all progress values after one planned build operation.
|
||||
func adopt_progress(
|
||||
next_group_index: int,
|
||||
next_transform_offset: int,
|
||||
next_batch_serial: int
|
||||
) -> void:
|
||||
_group_index = next_group_index
|
||||
_transform_offset = next_transform_offset
|
||||
_batch_serial = next_batch_serial
|
||||
|
||||
|
||||
## Returns detached scalar/count diagnostics without engine-object references.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
return {
|
||||
"tile_key": _tile_key,
|
||||
"group_index": _group_index,
|
||||
"transform_offset": _transform_offset,
|
||||
"batch_serial": _batch_serial,
|
||||
"group_count": _group_keys.size(),
|
||||
"has_root": _root != null,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://drawyd5qmel7j
|
||||
@@ -0,0 +1,169 @@
|
||||
class_name M2BuildQueue
|
||||
extends RefCounted
|
||||
|
||||
## Owns pending M2 build-job records and FIFO tile keys. Stale FIFO keys are
|
||||
## intentional: erasing a job never removes its queued key.
|
||||
|
||||
const JOB_SCRIPT := preload("res://src/render/m2/m2_build_job.gd")
|
||||
|
||||
var _jobs_by_tile_key: Dictionary = {}
|
||||
var _queued_tile_keys: Array[String] = []
|
||||
|
||||
|
||||
## Enqueues a typed job. Duplicate tile keys replace the keyed job and append
|
||||
## another FIFO entry, matching raw Dictionary assignment plus Array append.
|
||||
func enqueue(tile_key: String, root: Node3D, groups: Dictionary) -> bool:
|
||||
if tile_key.is_empty() or root == null or groups.is_empty():
|
||||
return false
|
||||
_jobs_by_tile_key[tile_key] = JOB_SCRIPT.new(tile_key, root, groups)
|
||||
_queued_tile_keys.append(tile_key)
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether at least one FIFO key, including a stale key, is pending.
|
||||
func has_pending() -> bool:
|
||||
return not _queued_tile_keys.is_empty()
|
||||
|
||||
|
||||
## Returns the front FIFO tile key, or an empty String when the queue is empty.
|
||||
func front_key() -> String:
|
||||
if _queued_tile_keys.is_empty():
|
||||
return ""
|
||||
return _queued_tile_keys.front()
|
||||
|
||||
|
||||
## Pops and returns the front FIFO key, or an empty String when already empty.
|
||||
func pop_front() -> String:
|
||||
if _queued_tile_keys.is_empty():
|
||||
return ""
|
||||
return _queued_tile_keys.pop_front()
|
||||
|
||||
|
||||
## Moves the front FIFO key to the tail and reports whether a key existed.
|
||||
func rotate_front() -> bool:
|
||||
if _queued_tile_keys.is_empty():
|
||||
return false
|
||||
_queued_tile_keys.append(_queued_tile_keys.pop_front())
|
||||
return true
|
||||
|
||||
|
||||
## Returns whether a tile key still has a current job record.
|
||||
func has_job(tile_key: String) -> bool:
|
||||
return _jobs_by_tile_key.has(tile_key)
|
||||
|
||||
|
||||
## Returns the current job as its engine base type, or null for a stale key.
|
||||
func job_for(tile_key: String) -> RefCounted:
|
||||
return _jobs_by_tile_key.get(tile_key, null) as RefCounted
|
||||
|
||||
|
||||
## Returns the borrowed root for a current job, or null for a stale key.
|
||||
func root_for(tile_key: String) -> Node3D:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return null
|
||||
return job.call("root") as Node3D
|
||||
|
||||
|
||||
## Returns the exact grouped-transform Dictionary, or an empty Dictionary.
|
||||
func groups_for(tile_key: String) -> Dictionary:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return {}
|
||||
return job.call("groups") as Dictionary
|
||||
|
||||
|
||||
## Returns the borrowed group-key snapshot, or an empty Array for a stale key.
|
||||
func group_keys_for(tile_key: String) -> Array:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return []
|
||||
return job.call("group_keys") as Array
|
||||
|
||||
|
||||
## Returns the current model-path group cursor, or zero for a stale key.
|
||||
func group_index_for(tile_key: String) -> int:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return 0
|
||||
return int(job.call("group_index"))
|
||||
|
||||
|
||||
## Returns the current in-group transform offset, or zero for a stale key.
|
||||
func transform_offset_for(tile_key: String) -> int:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return 0
|
||||
return int(job.call("transform_offset"))
|
||||
|
||||
|
||||
## Returns the current batch serial, or zero for a stale key.
|
||||
func batch_serial_for(tile_key: String) -> int:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return 0
|
||||
return int(job.call("batch_serial"))
|
||||
|
||||
|
||||
## Atomically adopts all progress values for a current job.
|
||||
func adopt_progress(
|
||||
tile_key: String,
|
||||
next_group_index: int,
|
||||
next_transform_offset: int,
|
||||
next_batch_serial: int
|
||||
) -> bool:
|
||||
var job := job_for(tile_key)
|
||||
if job == null:
|
||||
return false
|
||||
job.call(
|
||||
"adopt_progress",
|
||||
next_group_index,
|
||||
next_transform_offset,
|
||||
next_batch_serial
|
||||
)
|
||||
return true
|
||||
|
||||
|
||||
## Erases only the keyed job and intentionally leaves every FIFO entry stale.
|
||||
## Returns whether an active record existed. Engine objects are never freed.
|
||||
func erase_job(tile_key: String) -> bool:
|
||||
var had_job := _jobs_by_tile_key.has(tile_key)
|
||||
_jobs_by_tile_key.erase(tile_key)
|
||||
return had_job
|
||||
|
||||
|
||||
## Returns a detached snapshot of active job keys for loader-owned cleanup.
|
||||
func job_keys() -> Array:
|
||||
return _jobs_by_tile_key.keys()
|
||||
|
||||
|
||||
## Releases all job/key references without freeing referenced engine objects.
|
||||
func clear() -> void:
|
||||
_jobs_by_tile_key.clear()
|
||||
_queued_tile_keys.clear()
|
||||
|
||||
|
||||
## Returns FIFO entry count, including duplicate and stale keys.
|
||||
func pending_count() -> int:
|
||||
return _queued_tile_keys.size()
|
||||
|
||||
|
||||
## Returns active keyed job count, excluding stale FIFO keys.
|
||||
func active_job_count() -> int:
|
||||
return _jobs_by_tile_key.size()
|
||||
|
||||
|
||||
## Returns detached FIFO order and tile-key-sorted scalar job diagnostics.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
var tile_keys: Array = _jobs_by_tile_key.keys()
|
||||
tile_keys.sort()
|
||||
var jobs: Array[Dictionary] = []
|
||||
for tile_key_variant in tile_keys:
|
||||
var tile_key := String(tile_key_variant)
|
||||
var job := job_for(tile_key)
|
||||
if job != null:
|
||||
jobs.append(job.call("diagnostic_snapshot") as Dictionary)
|
||||
return {
|
||||
"queued_tile_keys": _queued_tile_keys.duplicate(),
|
||||
"jobs": jobs,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://57qrpiqqgpr4
|
||||
@@ -0,0 +1,72 @@
|
||||
class_name M2BuildResourceSnapshot
|
||||
extends RefCounted
|
||||
|
||||
## Holds one M2 build-step resource observation without owning engine lifetime.
|
||||
|
||||
var _normalized_relative_path: String
|
||||
var _animated_prototype: Node3D
|
||||
var _animation_request_pending: bool
|
||||
var _static_mesh: Mesh = null
|
||||
var _static_model_missing: bool = false
|
||||
|
||||
|
||||
func _init(
|
||||
normalized_relative_path: String,
|
||||
animated_prototype: Node3D,
|
||||
animation_request_pending: bool
|
||||
) -> void:
|
||||
_normalized_relative_path = normalized_relative_path
|
||||
_animated_prototype = animated_prototype
|
||||
_animation_request_pending = animation_request_pending
|
||||
|
||||
|
||||
## Returns the normalized M2 path observed by the loader.
|
||||
func normalized_relative_path() -> String:
|
||||
return _normalized_relative_path
|
||||
|
||||
|
||||
## Returns the borrowed animated prototype without transferring ownership.
|
||||
func animated_prototype() -> Node3D:
|
||||
return _animated_prototype
|
||||
|
||||
|
||||
## Returns whether an animated prototype was observed.
|
||||
func has_animated_prototype() -> bool:
|
||||
return _animated_prototype != null
|
||||
|
||||
|
||||
## Returns whether the animation request remains pending.
|
||||
func animation_request_pending() -> bool:
|
||||
return _animation_request_pending
|
||||
|
||||
|
||||
## Adopts the optional static Mesh and terminal missing-model observation.
|
||||
func adopt_static_observation(static_mesh: Mesh, static_model_missing: bool) -> void:
|
||||
_static_mesh = static_mesh
|
||||
_static_model_missing = static_model_missing
|
||||
|
||||
|
||||
## Returns the borrowed static Mesh without transferring ownership.
|
||||
func static_mesh() -> Mesh:
|
||||
return _static_mesh
|
||||
|
||||
|
||||
## Returns whether a prepared static Mesh was observed.
|
||||
func has_static_mesh() -> bool:
|
||||
return _static_mesh != null
|
||||
|
||||
|
||||
## Returns whether the static model lookup reached a terminal missing outcome.
|
||||
func static_model_missing() -> bool:
|
||||
return _static_model_missing
|
||||
|
||||
|
||||
## Returns detached path/availability diagnostics without engine references.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
return {
|
||||
"normalized_relative_path": _normalized_relative_path,
|
||||
"has_animated_prototype": has_animated_prototype(),
|
||||
"animation_request_pending": _animation_request_pending,
|
||||
"has_static_mesh": has_static_mesh(),
|
||||
"static_model_missing": _static_model_missing,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cgleoy15rtmby
|
||||
@@ -0,0 +1,238 @@
|
||||
class_name M2CachedAnimationResourceObserver
|
||||
extends RefCounted
|
||||
|
||||
## Observes or requests the cached-GLB animated phase of one M2 build step.
|
||||
|
||||
const M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_build_resource_snapshot.gd"
|
||||
)
|
||||
|
||||
|
||||
## Returns a snapshot containing the exact cached prototype or pending state.
|
||||
## Invalid composition and terminal failures produce a non-pending snapshot.
|
||||
func observe(
|
||||
normalized_relative_path: String,
|
||||
cache_directory: String,
|
||||
maximum_primitive_count: int,
|
||||
allowlist_patterns: PackedStringArray,
|
||||
denylist_patterns: PackedStringArray,
|
||||
prototype_cache_state: RefCounted,
|
||||
load_pipeline_state: RefCounted,
|
||||
debug_logging_enabled: bool = false
|
||||
) -> RefCounted:
|
||||
var empty_snapshot: RefCounted = M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
|
||||
normalized_relative_path,
|
||||
null,
|
||||
false
|
||||
)
|
||||
if (
|
||||
normalized_relative_path.is_empty()
|
||||
or prototype_cache_state == null
|
||||
or load_pipeline_state == null
|
||||
):
|
||||
return empty_snapshot
|
||||
|
||||
var cached_prototype := prototype_cache_state.call(
|
||||
"find_animated_prototype",
|
||||
normalized_relative_path
|
||||
) as Node3D
|
||||
if cached_prototype != null:
|
||||
return M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
|
||||
normalized_relative_path,
|
||||
cached_prototype,
|
||||
false
|
||||
)
|
||||
if bool(prototype_cache_state.call(
|
||||
"is_animation_static",
|
||||
normalized_relative_path
|
||||
)):
|
||||
return empty_snapshot
|
||||
if bool(load_pipeline_state.call("has_request", normalized_relative_path)):
|
||||
return M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
|
||||
normalized_relative_path,
|
||||
null,
|
||||
true
|
||||
)
|
||||
|
||||
var cache_resource_path := find_eligible_glb_cache_path(
|
||||
normalized_relative_path,
|
||||
cache_directory,
|
||||
maximum_primitive_count,
|
||||
allowlist_patterns,
|
||||
denylist_patterns,
|
||||
debug_logging_enabled
|
||||
)
|
||||
if cache_resource_path.is_empty():
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
return empty_snapshot
|
||||
|
||||
var request_error := ResourceLoader.load_threaded_request(
|
||||
cache_resource_path,
|
||||
"",
|
||||
false,
|
||||
ResourceLoader.CACHE_MODE_REUSE
|
||||
)
|
||||
if request_error == OK or request_error == ERR_BUSY:
|
||||
load_pipeline_state.call(
|
||||
"remember_request",
|
||||
normalized_relative_path,
|
||||
cache_resource_path
|
||||
)
|
||||
return M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
|
||||
normalized_relative_path,
|
||||
null,
|
||||
true
|
||||
)
|
||||
|
||||
prototype_cache_state.call("mark_animation_static", normalized_relative_path)
|
||||
return empty_snapshot
|
||||
|
||||
|
||||
## Returns the first eligible historical `.glb` cache candidate.
|
||||
func find_eligible_glb_cache_path(
|
||||
normalized_relative_path: String,
|
||||
cache_directory: String,
|
||||
maximum_primitive_count: int,
|
||||
allowlist_patterns: PackedStringArray,
|
||||
denylist_patterns: PackedStringArray,
|
||||
debug_logging_enabled: bool = false
|
||||
) -> String:
|
||||
if not is_animation_path_allowed(
|
||||
normalized_relative_path,
|
||||
allowlist_patterns,
|
||||
denylist_patterns
|
||||
):
|
||||
return ""
|
||||
for cache_resource_path in cache_resource_paths(
|
||||
cache_directory,
|
||||
normalized_relative_path
|
||||
):
|
||||
if not ResourceLoader.exists(cache_resource_path):
|
||||
continue
|
||||
if glb_cache_is_safe_for_runtime_animation(
|
||||
cache_resource_path,
|
||||
maximum_primitive_count,
|
||||
debug_logging_enabled
|
||||
):
|
||||
return cache_resource_path
|
||||
return ""
|
||||
|
||||
|
||||
## Applies the historical stripped, case-insensitive allow/deny substring policy.
|
||||
func is_animation_path_allowed(
|
||||
normalized_relative_path: String,
|
||||
allowlist_patterns: PackedStringArray,
|
||||
denylist_patterns: PackedStringArray
|
||||
) -> bool:
|
||||
if normalized_relative_path.is_empty() or allowlist_patterns.is_empty():
|
||||
return false
|
||||
var lowercase_path := normalized_relative_path.to_lower()
|
||||
var allowed := false
|
||||
for pattern in allowlist_patterns:
|
||||
var needle := String(pattern).strip_edges().to_lower()
|
||||
if not needle.is_empty() and lowercase_path.contains(needle):
|
||||
allowed = true
|
||||
break
|
||||
if not allowed:
|
||||
return false
|
||||
for pattern in denylist_patterns:
|
||||
var needle := String(pattern).strip_edges().to_lower()
|
||||
if not needle.is_empty() and lowercase_path.contains(needle):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
## Returns historical nested/lowercase/basename `.glb` candidates without I/O.
|
||||
func cache_resource_paths(
|
||||
cache_directory: String,
|
||||
relative_path: String
|
||||
) -> PackedStringArray:
|
||||
var normalized := relative_path.replace("\\", "/")
|
||||
var lowercase := normalized.to_lower()
|
||||
var stems := [
|
||||
normalized.get_basename(),
|
||||
lowercase.get_basename(),
|
||||
normalized.get_file().get_basename(),
|
||||
lowercase.get_file().get_basename(),
|
||||
]
|
||||
var result := PackedStringArray()
|
||||
for stem in stems:
|
||||
if stem.is_empty():
|
||||
continue
|
||||
var cache_resource_path := cache_directory.path_join(stem + ".glb")
|
||||
if not result.has(cache_resource_path):
|
||||
result.append(cache_resource_path)
|
||||
return result
|
||||
|
||||
|
||||
## Checks animation presence, primitive limit and accepted OpenWC schema.
|
||||
func glb_cache_is_safe_for_runtime_animation(
|
||||
cache_resource_path: String,
|
||||
maximum_primitive_count: int,
|
||||
debug_logging_enabled: bool = false
|
||||
) -> bool:
|
||||
var gltf := read_glb_json(cache_resource_path)
|
||||
if gltf.is_empty():
|
||||
return false
|
||||
var animations: Array = gltf.get("animations", [])
|
||||
if animations.is_empty():
|
||||
return false
|
||||
var primitive_count := glb_primitive_count(gltf)
|
||||
if maximum_primitive_count > 0 and primitive_count > maximum_primitive_count:
|
||||
return false
|
||||
var schema := glb_animation_schema(gltf)
|
||||
if schema == "pivot_prefix_v1" or schema.is_empty():
|
||||
return true
|
||||
if debug_logging_enabled:
|
||||
print(
|
||||
"M2_ANIM_REJECT schema=%s primitives=%d cache=%s"
|
||||
% [schema, primitive_count, cache_resource_path]
|
||||
)
|
||||
return false
|
||||
|
||||
|
||||
## Reads the JSON chunk of a version-2 GLB or returns an empty Dictionary.
|
||||
func read_glb_json(cache_resource_path: String) -> Dictionary:
|
||||
var absolute_path := ProjectSettings.globalize_path(cache_resource_path)
|
||||
if not FileAccess.file_exists(absolute_path):
|
||||
return {}
|
||||
var file := FileAccess.open(absolute_path, FileAccess.READ)
|
||||
if file == null or file.get_length() < 20:
|
||||
return {}
|
||||
var magic := file.get_32()
|
||||
var version := file.get_32()
|
||||
file.get_32()
|
||||
if magic != 0x46546c67 or version != 2:
|
||||
return {}
|
||||
var json_length := int(file.get_32())
|
||||
var chunk_type := file.get_32()
|
||||
if json_length <= 0 or chunk_type != 0x4e4f534a:
|
||||
return {}
|
||||
var parsed: Variant = JSON.parse_string(
|
||||
file.get_buffer(json_length).get_string_from_utf8()
|
||||
)
|
||||
return parsed as Dictionary if parsed is Dictionary else {}
|
||||
|
||||
|
||||
## Counts all glTF Mesh primitives using the historical safety rule.
|
||||
func glb_primitive_count(gltf: Dictionary) -> int:
|
||||
var count := 0
|
||||
var meshes: Array = gltf.get("meshes", [])
|
||||
for mesh_variant in meshes:
|
||||
if mesh_variant is Dictionary:
|
||||
var primitives: Array = (mesh_variant as Dictionary).get(
|
||||
"primitives",
|
||||
[]
|
||||
)
|
||||
count += primitives.size()
|
||||
return count
|
||||
|
||||
|
||||
## Returns the OpenWC animation schema marker from parsed glTF metadata.
|
||||
func glb_animation_schema(gltf: Dictionary) -> String:
|
||||
var asset: Dictionary = gltf.get("asset", {})
|
||||
var extras: Dictionary = asset.get("extras", {})
|
||||
return String(extras.get("openwc_m2_anim_schema", ""))
|
||||
@@ -0,0 +1 @@
|
||||
uid://d0ptej41k2smt
|
||||
@@ -0,0 +1,182 @@
|
||||
class_name M2MeshResourceFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Polls static M2 ResourceLoader requests and publishes prepared Mesh outcomes.
|
||||
## The caller owns permits, pipeline/cache lifetime and MultiMesh materialization.
|
||||
|
||||
var _mesh_resource_extractor: RefCounted
|
||||
var _runtime_mesh_finalizer: RefCounted
|
||||
var _raw_model_repository: RefCounted
|
||||
var _resource_loader_adapter: Object
|
||||
|
||||
|
||||
func _init(
|
||||
mesh_resource_extractor: RefCounted,
|
||||
runtime_mesh_finalizer: RefCounted,
|
||||
raw_model_repository: RefCounted,
|
||||
resource_loader_adapter: Object = null
|
||||
) -> void:
|
||||
_mesh_resource_extractor = mesh_resource_extractor
|
||||
_runtime_mesh_finalizer = runtime_mesh_finalizer
|
||||
_raw_model_repository = raw_model_repository
|
||||
_resource_loader_adapter = resource_loader_adapter
|
||||
|
||||
|
||||
## Moves terminal threaded requests into the pipeline finalize FIFO.
|
||||
## Empty resource paths retain the historical immediate missing-model outcome.
|
||||
func poll_terminal_requests(
|
||||
load_pipeline_state: RefCounted,
|
||||
prototype_cache_state: RefCounted
|
||||
) -> int:
|
||||
if load_pipeline_state == null or prototype_cache_state == null:
|
||||
return 0
|
||||
var completed_request_count := 0
|
||||
var request_records: Array = load_pipeline_state.call(
|
||||
"request_records_snapshot"
|
||||
)
|
||||
for request_variant in request_records:
|
||||
if not (request_variant is Dictionary):
|
||||
continue
|
||||
var request: Dictionary = request_variant
|
||||
var normalized_relative_path := String(request.get("normalized", ""))
|
||||
var resource_path := String(request.get("path", ""))
|
||||
if resource_path.is_empty():
|
||||
load_pipeline_state.call(
|
||||
"discard_request",
|
||||
normalized_relative_path
|
||||
)
|
||||
prototype_cache_state.call(
|
||||
"mark_model_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
completed_request_count += 1
|
||||
continue
|
||||
var load_status := load_threaded_get_status(resource_path)
|
||||
if (
|
||||
load_status != ResourceLoader.THREAD_LOAD_LOADED
|
||||
and load_status != ResourceLoader.THREAD_LOAD_FAILED
|
||||
):
|
||||
continue
|
||||
load_pipeline_state.call(
|
||||
"complete_request",
|
||||
normalized_relative_path,
|
||||
load_status
|
||||
)
|
||||
completed_request_count += 1
|
||||
return completed_request_count
|
||||
|
||||
|
||||
## Pops and finalizes one terminal record. A true result means a record was
|
||||
## consumed, including cached, failed and invalid outcomes.
|
||||
func finalize_next_resource(
|
||||
load_pipeline_state: RefCounted,
|
||||
mesh_resource_cache_state: RefCounted,
|
||||
prototype_cache_state: RefCounted,
|
||||
extracted_directory: String
|
||||
) -> bool:
|
||||
if (
|
||||
load_pipeline_state == null
|
||||
or mesh_resource_cache_state == null
|
||||
or prototype_cache_state == null
|
||||
):
|
||||
return false
|
||||
var terminal_record: Dictionary = load_pipeline_state.call(
|
||||
"pop_finalize_record"
|
||||
)
|
||||
if terminal_record.is_empty():
|
||||
return false
|
||||
var normalized_relative_path := String(
|
||||
terminal_record.get("normalized", "")
|
||||
)
|
||||
if (
|
||||
normalized_relative_path.is_empty()
|
||||
or bool(mesh_resource_cache_state.call(
|
||||
"has_mesh",
|
||||
normalized_relative_path
|
||||
))
|
||||
):
|
||||
return true
|
||||
if int(terminal_record.get(
|
||||
"status",
|
||||
ResourceLoader.THREAD_LOAD_FAILED
|
||||
)) != ResourceLoader.THREAD_LOAD_LOADED:
|
||||
prototype_cache_state.call(
|
||||
"mark_model_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
return true
|
||||
|
||||
var resource_path := String(terminal_record.get("path", ""))
|
||||
var loaded_resource := load_threaded_get(resource_path)
|
||||
var mesh := _mesh_resource_extractor.call(
|
||||
"extract_first_mesh",
|
||||
loaded_resource
|
||||
) as Mesh
|
||||
if mesh == null:
|
||||
prototype_cache_state.call(
|
||||
"mark_model_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
return true
|
||||
mesh_resource_cache_state.call(
|
||||
"store_mesh",
|
||||
normalized_relative_path,
|
||||
prepare_mesh_for_runtime(
|
||||
normalized_relative_path,
|
||||
mesh,
|
||||
extracted_directory
|
||||
)
|
||||
)
|
||||
return true
|
||||
|
||||
|
||||
## Applies the existing refresh-version/raw-data/rebuild path to one Mesh.
|
||||
func prepare_mesh_for_runtime(
|
||||
normalized_relative_path: String,
|
||||
mesh: Mesh,
|
||||
extracted_directory: String
|
||||
) -> Mesh:
|
||||
if mesh == null:
|
||||
return null
|
||||
if (
|
||||
_runtime_mesh_finalizer == null
|
||||
or not bool(_runtime_mesh_finalizer.call(
|
||||
"requires_raw_data_for_refresh",
|
||||
mesh
|
||||
))
|
||||
):
|
||||
return mesh
|
||||
var raw_model_data: Dictionary = {}
|
||||
if _raw_model_repository != null:
|
||||
raw_model_data = _raw_model_repository.call(
|
||||
"load_static_model_data",
|
||||
extracted_directory,
|
||||
normalized_relative_path
|
||||
)
|
||||
return _runtime_mesh_finalizer.call(
|
||||
"finalize_mesh",
|
||||
normalized_relative_path,
|
||||
mesh,
|
||||
raw_model_data,
|
||||
extracted_directory
|
||||
) as Mesh
|
||||
|
||||
|
||||
## Production ResourceLoader status adapter; injectable in synthetic tests.
|
||||
func load_threaded_get_status(resource_path: String) -> int:
|
||||
if _resource_loader_adapter != null:
|
||||
return int(_resource_loader_adapter.call(
|
||||
"load_threaded_get_status",
|
||||
resource_path
|
||||
))
|
||||
return ResourceLoader.load_threaded_get_status(resource_path)
|
||||
|
||||
|
||||
## Production ResourceLoader terminal-result adapter; injectable in tests.
|
||||
func load_threaded_get(resource_path: String) -> Resource:
|
||||
if _resource_loader_adapter != null:
|
||||
return _resource_loader_adapter.call(
|
||||
"load_threaded_get",
|
||||
resource_path
|
||||
) as Resource
|
||||
return ResourceLoader.load_threaded_get(resource_path)
|
||||
@@ -0,0 +1 @@
|
||||
uid://de4xl1fywvqfw
|
||||
@@ -0,0 +1,113 @@
|
||||
class_name M2NativeAnimationResourceObserver
|
||||
extends RefCounted
|
||||
|
||||
## Resolves the existing native GryphonRoost animated M2 prototype path.
|
||||
|
||||
const M2_NATIVE_ANIMATED_BUILDER_SCRIPT := preload(
|
||||
"res://addons/mpq_extractor/loaders/m2_native_animated_builder.gd"
|
||||
)
|
||||
|
||||
var _native_animated_builder: Object
|
||||
|
||||
|
||||
func _init(native_animated_builder: Object = M2_NATIVE_ANIMATED_BUILDER_SCRIPT) -> void:
|
||||
_native_animated_builder = native_animated_builder
|
||||
|
||||
|
||||
## Returns whether the normalized path uses the historical native animation path.
|
||||
func is_native_animation_candidate(normalized_relative_path: String) -> bool:
|
||||
return normalized_relative_path.to_lower().contains("gryphonroost")
|
||||
|
||||
|
||||
## Returns the exact cached or newly adopted native animated prototype.
|
||||
## Rejected native candidates are marked static-only for the renderer session.
|
||||
func observe(
|
||||
normalized_relative_path: String,
|
||||
extracted_directory: String,
|
||||
raw_model_repository: RefCounted,
|
||||
prototype_cache_state: RefCounted,
|
||||
debug_logging_enabled: bool = false
|
||||
) -> Node3D:
|
||||
if (
|
||||
normalized_relative_path.is_empty()
|
||||
or not is_native_animation_candidate(normalized_relative_path)
|
||||
or raw_model_repository == null
|
||||
or prototype_cache_state == null
|
||||
):
|
||||
return null
|
||||
|
||||
var cached_prototype := prototype_cache_state.call(
|
||||
"find_animated_prototype",
|
||||
normalized_relative_path
|
||||
) as Node3D
|
||||
if cached_prototype != null:
|
||||
return cached_prototype
|
||||
if bool(prototype_cache_state.call(
|
||||
"is_animation_static",
|
||||
normalized_relative_path
|
||||
)):
|
||||
return null
|
||||
|
||||
var animated_model_data: Dictionary = raw_model_repository.call(
|
||||
"load_animated_model_data",
|
||||
extracted_directory,
|
||||
normalized_relative_path
|
||||
)
|
||||
var animated_surfaces: Array = animated_model_data.get(
|
||||
"animated_surfaces",
|
||||
[]
|
||||
)
|
||||
if animated_model_data.is_empty() or animated_surfaces.is_empty():
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
return null
|
||||
|
||||
var prototype := build_animated_prototype(
|
||||
animated_model_data,
|
||||
extracted_directory
|
||||
)
|
||||
if prototype == null or prototype.get_child_count() <= 0:
|
||||
prototype_cache_state.call(
|
||||
"mark_animation_static",
|
||||
normalized_relative_path
|
||||
)
|
||||
return null
|
||||
|
||||
prototype = prototype_cache_state.call(
|
||||
"adopt_animated_prototype",
|
||||
normalized_relative_path,
|
||||
prototype
|
||||
) as Node3D
|
||||
if debug_logging_enabled:
|
||||
print(
|
||||
(
|
||||
"M2_NATIVE_ANIM_CACHE path=%s surfaces=%d bones=%d "
|
||||
+ "anim_id=%d seq=%d score=%d length=%.2f"
|
||||
)
|
||||
% [
|
||||
normalized_relative_path,
|
||||
animated_surfaces.size(),
|
||||
(animated_model_data.get("bones", []) as Array).size(),
|
||||
int(animated_model_data.get("animation_id", -1)),
|
||||
int(animated_model_data.get("animation_sequence_index", -1)),
|
||||
int(animated_model_data.get("animation_activity_score", 0)),
|
||||
float(animated_model_data.get("animation_length", 0.0)),
|
||||
]
|
||||
)
|
||||
return prototype
|
||||
|
||||
|
||||
## Builds one prototype through the exact native animated builder dependency.
|
||||
func build_animated_prototype(
|
||||
animated_model_data: Dictionary,
|
||||
extracted_directory: String
|
||||
) -> Node3D:
|
||||
if _native_animated_builder == null:
|
||||
return null
|
||||
return _native_animated_builder.call(
|
||||
"build",
|
||||
animated_model_data,
|
||||
extracted_directory
|
||||
) as Node3D
|
||||
@@ -0,0 +1 @@
|
||||
uid://bc3kgi23usncx
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user