Compare commits
78 Commits
c74b90a8ea
..
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 |
+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,6 +32,8 @@ 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.
|
||||
@@ -41,7 +43,13 @@ Paired run 2026-07-11 подтвердил крупный coordinate/placement g
|
||||
- `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.
|
||||
@@ -325,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.
|
||||
@@ -678,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;
|
||||
@@ -1058,6 +1102,39 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
- 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
|
||||
@@ -1098,12 +1175,70 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
|
||||
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.
|
||||
- `StreamingWorldLoader` retains GLB eligibility/cache selection, every
|
||||
- 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
|
||||
@@ -1164,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.
|
||||
|
||||
@@ -1178,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.
|
||||
@@ -1219,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
|
||||
@@ -1229,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
|
||||
@@ -1243,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,
|
||||
@@ -1317,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,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-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,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,6 +21,11 @@
|
||||
| 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) |
|
||||
@@ -28,9 +33,11 @@
|
||||
| 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) |
|
||||
@@ -39,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
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ and accept only candidates containing AnimationPlayer descendants.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] -->|loaded Resource and material source| Finalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer -->|accepted Node3D and player count| Loader
|
||||
Loader --> Cache[M2PrototypeCacheState]
|
||||
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,
|
||||
@@ -50,10 +50,10 @@ other application layers are forbidden.
|
||||
|
||||
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
||||
|---|---|---|---|---|---|
|
||||
| Input | Loaded Resource | Loader I/O adapter | Candidate instantiator | Borrowed Resource | One finalize permit |
|
||||
| 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 | Loader adoption/log adapter | Exact Node transferred | Shutdown cache lifetime |
|
||||
| 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
|
||||
@@ -91,20 +91,19 @@ stateDiagram-v2
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant L as StreamingWorldLoader
|
||||
participant R as M2AnimationResourceFinalizer
|
||||
participant F as M2AnimatedSceneFinalizer
|
||||
participant C as M2PrototypeCacheState
|
||||
L->>F: instantiate_candidate(Resource)
|
||||
F-->>L: detached Node3D or null
|
||||
L->>L: get static material prototype
|
||||
L->>F: repair_materials(candidate, source)
|
||||
L->>F: finalize_candidate(candidate)
|
||||
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-->>L: exact Node3D and player count
|
||||
L->>C: adopt animated prototype
|
||||
F-->>R: exact Node3D and player count
|
||||
R->>C: adopt animated prototype
|
||||
else rejected
|
||||
F-->>L: empty; candidate freed
|
||||
L->>C: mark animation static
|
||||
F-->>R: empty; candidate freed
|
||||
R->>C: mark animation static
|
||||
end
|
||||
```
|
||||
|
||||
@@ -112,11 +111,11 @@ sequenceDiagram
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Loader[StreamingWorldLoader] --> Finalizer[M2AnimatedSceneFinalizer]
|
||||
ResourceFinalizer[M2AnimationResourceFinalizer] --> Finalizer[M2AnimatedSceneFinalizer]
|
||||
Finalizer --> Engine[PackedScene / Node3D / Mesh / Material / AnimationPlayer]
|
||||
Loader --> Resource[ResourceLoader]
|
||||
ResourceFinalizer --> Resource[ResourceLoader]
|
||||
Loader --> Budget[RenderBudgetScheduler]
|
||||
Loader --> Prototype[M2PrototypeCacheState]
|
||||
ResourceFinalizer --> Prototype[M2PrototypeCacheState]
|
||||
Finalizer -. no dependency .-> Resource
|
||||
Finalizer -. no dependency .-> Budget
|
||||
Finalizer -. no dependency .-> Prototype
|
||||
@@ -126,7 +125,7 @@ flowchart TB
|
||||
|
||||
- Every method runs synchronously on the renderer main thread.
|
||||
- A valid candidate is detached and finalizer-owned until acceptance.
|
||||
- Acceptance transfers the exact Node3D to the loader/prototype cache path.
|
||||
- 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.
|
||||
|
||||
@@ -134,11 +133,11 @@ flowchart TB
|
||||
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Null/unsupported Resource | Type guard | Return null | Dedicated verifier | Loader marks static |
|
||||
| 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 | Loader marks static |
|
||||
| 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
|
||||
@@ -199,7 +198,8 @@ by `M2AnimatedInstanceMaterializer`.
|
||||
| `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/scenes/streaming/streaming_world_loader.gd` | I/O, permits, material source, adoption and logs |
|
||||
| `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
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
Own cross-frame bookkeeping between a successful animated M2 ResourceLoader
|
||||
request, terminal polling and budgeted main-thread scene finalization. This is
|
||||
an exact state extraction; animation eligibility and loading remain in
|
||||
`StreamingWorldLoader`, while scene finalization and retained Node lifecycle
|
||||
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
|
||||
@@ -29,11 +30,14 @@ belong to `M2AnimatedSceneFinalizer` and `M2PrototypeCacheState`.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Loader[StreamingWorldLoader] --> IO[ResourceLoader]
|
||||
Loader --> State[M2AnimationLoadPipelineState]
|
||||
State -->|detached pending records| Loader
|
||||
Loader -->|opaque terminal status| State
|
||||
State -->|completion FIFO| Loader
|
||||
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]
|
||||
```
|
||||
@@ -62,9 +66,9 @@ renderer services are forbidden.
|
||||
| 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 | Loader polling adapter | Finalize FIFO | Integer value | Until pop/clear |
|
||||
| Output | Detached pending records | State | Loader poll/shutdown adapter | Caller-owned copies | One pass |
|
||||
| Output | Oldest completion record | State | Loader finalizer | Transferred Dictionary | One finalize attempt |
|
||||
| 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.
|
||||
@@ -74,7 +78,7 @@ Side effects are limited to collection mutation and retaining String/integer val
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start[Successful threaded request] --> Remember[Remember request]
|
||||
Remember --> Poll[Loader polls detached snapshot]
|
||||
Remember --> Poll[Resource finalizer polls detached snapshot]
|
||||
Poll --> Terminal{Loaded or failed?}
|
||||
Terminal -->|no| Poll
|
||||
Terminal -->|yes| Complete[Complete with opaque status]
|
||||
@@ -82,7 +86,7 @@ flowchart TD
|
||||
FIFO --> Permit{Permit available?}
|
||||
Permit -->|no| FIFO
|
||||
Permit -->|yes| Pop[Pop oldest record]
|
||||
Pop --> Finalize[Loader loads/instantiates or marks static]
|
||||
Pop --> Finalize[Resource finalizer loads/instantiates or marks static]
|
||||
```
|
||||
|
||||
## Lifecycle/state
|
||||
@@ -100,20 +104,24 @@ stateDiagram-v2
|
||||
|
||||
```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
|
||||
L->>R: load_threaded_request(GLB)
|
||||
L->>S: remember_request(path, GLB)
|
||||
O->>R: load_threaded_request(GLB)
|
||||
O->>S: remember_request(path, GLB)
|
||||
loop frames
|
||||
L->>S: request_records_snapshot()
|
||||
L->>R: load_threaded_get_status(GLB)
|
||||
L->>F: poll terminal requests
|
||||
F->>S: request_records_snapshot()
|
||||
F->>R: load_threaded_get_status(GLB)
|
||||
end
|
||||
L->>S: complete_request(path, status)
|
||||
L->>S: pop_finalize_record() after permit
|
||||
L->>R: load_threaded_get(GLB)
|
||||
L->>P: adopt animated prototype or mark static
|
||||
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
|
||||
@@ -121,15 +129,18 @@ sequenceDiagram
|
||||
- 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.
|
||||
- Loader owns PackedScene instantiation and material repair; prototype state owns
|
||||
accepted detached Node references and static-only outcomes.
|
||||
- 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
|
||||
Loader[StreamingWorldLoader] --> State[M2AnimationLoadPipelineState]
|
||||
Loader --> Resource[ResourceLoader]
|
||||
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
|
||||
@@ -142,7 +153,7 @@ flowchart TB
|
||||
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
||||
|---|---|---|---|---|
|
||||
| Empty/duplicate request | State guard | Reject unchanged | Contract verifier | Correct caller or request later |
|
||||
| Request start/cache miss | Loader | No insertion; mark static | Existing loader path | Cache correction/reload |
|
||||
| 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 |
|
||||
@@ -154,7 +165,7 @@ flowchart TB
|
||||
|---|---|---|---|---|
|
||||
| `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 | Filter before state insertion |
|
||||
| Animated allow/deny/primitive rules | Existing values | Existing renderer profile | Yes | Observer filters before state insertion |
|
||||
|
||||
## Persistence, cache and migration
|
||||
|
||||
@@ -179,16 +190,16 @@ no rebake or migration is required.
|
||||
|
||||
## Extension points
|
||||
|
||||
ResourceLoader polling may later move behind a separate adapter without
|
||||
changing this value-only state contract. Animated-scene finalization is now a
|
||||
sibling service.
|
||||
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 |
|
||||
| ResourceLoader and GLB selection | Existing loader-owned | Adjacent renderer tests | I/O adapter extraction optional |
|
||||
| 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
|
||||
@@ -202,14 +213,17 @@ sibling service.
|
||||
| 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` | Eligibility, I/O, permits, instantiation and adoption |
|
||||
| `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)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
| 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-m2-animation-playback`, 2026-07-18 |
|
||||
| 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
|
||||
@@ -59,7 +59,7 @@ MultiMesh, SceneTree attachment and application layers are forbidden.
|
||||
| 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, prepare/phase calls, animation loop
|
||||
Side effects are native field assignment, phased preparation calls, animation loop
|
||||
mutation, play and seek. The service retains no inputs.
|
||||
|
||||
## Data flow
|
||||
@@ -67,13 +67,13 @@ mutation, play and seek. The service retains no inputs.
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Identity[Path and index] --> Phase[Stable hash phase]
|
||||
NativeInventory[Exact-script native inventory] --> Prepare[prepare runtime if available]
|
||||
Phase --> NativePhase[set native 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]
|
||||
NativePhase --> Diagnostics{Debug requested?}
|
||||
Prepare --> Diagnostics{Debug requested?}
|
||||
Diagnostics -->|yes| Snapshot[Detached runtime state]
|
||||
```
|
||||
|
||||
@@ -103,7 +103,7 @@ sequenceDiagram
|
||||
M->>F: animation_players_in_subtree(duplicate)
|
||||
F-->>M: ordered players
|
||||
M->>P: start_instance_playback(path, index, players, debug)
|
||||
P->>N: prepare_runtime and set_phase
|
||||
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
|
||||
@@ -132,6 +132,11 @@ flowchart TB
|
||||
- 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
|
||||
|
||||
@@ -168,7 +173,8 @@ and material versions are unchanged; no rebake is required.
|
||||
|
||||
- `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, phase, detached diagnostics and boundaries.
|
||||
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.
|
||||
@@ -192,7 +198,8 @@ for world doodads and compatibility fixtures.
|
||||
|
||||
- Hash phase intentionally depends on existing Godot String hashing behavior.
|
||||
- Default-name heuristics are not a complete WoW animation-state mapping.
|
||||
- No proprietary traversal, animation timing comparison, p95/p99 or paired-client run exists.
|
||||
- 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
|
||||
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -103,7 +103,9 @@ sequenceDiagram
|
||||
|
||||
- The planner owns only call-local scalar values and the returned Dictionary.
|
||||
- `M2BuildQueue` owns typed jobs, FIFO ordering, serial numbers and group/offset
|
||||
cursors. The loader owns tile checks, resource readiness/retry and adoption calls.
|
||||
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 extract resource readiness/dispatch while retaining the
|
||||
typed build-job and FIFO contracts defined by `M2BuildQueue`.
|
||||
- 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.
|
||||
|
||||
@@ -160,7 +162,8 @@ queue depth, build activity and hitch observability.
|
||||
|---|---|---|---|
|
||||
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
|
||||
| Typed build queue/cursor state | Implemented extraction | M2 build queue lifecycle verifier | Asset-backed traversal pending |
|
||||
| Resource readiness/dispatch | Remains in loader | Existing lifecycle regressions | Stateful extraction 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
|
||||
@@ -176,6 +179,7 @@ queue depth, build activity and hitch observability.
|
||||
| Path | Responsibility |
|
||||
|---|---|
|
||||
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
|
||||
| `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 |
|
||||
|
||||
@@ -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)
|
||||
@@ -33,6 +33,7 @@ flowchart LR
|
||||
Queue --> Job[M2BuildJob]
|
||||
Queue --> Loader
|
||||
Loader --> Planner[M2BuildBatchPlanner]
|
||||
Loader --> Dispatch[M2BuildDispatchPlanner]
|
||||
Loader --> Static[M2StaticBatchMaterializer]
|
||||
Loader --> Animated[M2AnimatedInstanceMaterializer]
|
||||
Loader --> Scheduler[RenderBudgetScheduler]
|
||||
@@ -171,6 +172,8 @@ flowchart TB
|
||||
- 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.
|
||||
|
||||
@@ -229,7 +232,8 @@ queue base, signals and callbacks are intentionally excluded.
|
||||
| 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 readiness/dispatch | Existing loader-owned | Adjacent cache/build tests | Safe extraction remains |
|
||||
| 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
|
||||
|
||||
@@ -246,11 +250,14 @@ queue base, signals and callbacks are intentionally excluded.
|
||||
| `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)
|
||||
|
||||
@@ -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 |
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -133,8 +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
|
||||
static materializer owns MultiMesh construction/attachment; the loader owns
|
||||
resource adoption and build-job 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
|
||||
@@ -186,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 |
|
||||
|
||||
@@ -203,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,7 +239,10 @@ 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 |
|
||||
|
||||
|
||||
@@ -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,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)
|
||||
+130
-22
@@ -7,7 +7,7 @@
|
||||
| Status | Partial |
|
||||
| Target/work package | M00 baseline; `M01-RND-STREAMING-FOCUS-001`; `M01-QAR-SERVER-SPAWN-RENDERER-001`; M03 facade/planner/scheduler/internal-access/ground/environment/entity packages; M03 terrain packages; M03 M2 packages; M03 WMO placement package |
|
||||
| Owners | Renderer workstream / milestone integrator |
|
||||
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animation-playback`, 2026-07-18 |
|
||||
| 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,10 @@ 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]
|
||||
@@ -66,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]
|
||||
@@ -136,25 +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 |
|
||||
|
||||
@@ -185,17 +207,22 @@ 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 animated M2 load | Normalized path, cached GLB path and opaque terminal status | Loader / `M2AnimationLoadPipelineState` | Loader poll/finalize adapters | State-owned String/status records; detached pending snapshots | Request through terminal finalize/reset |
|
||||
| Internal 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 |
|
||||
@@ -249,7 +276,9 @@ flowchart TD
|
||||
M2Grouper --> M2Batch[M2BuildBatchPlanner]
|
||||
M2Grouper --> M2Queue[M2BuildQueue]
|
||||
M2Queue --> M2Batch
|
||||
M2Batch --> M2Static[M2StaticBatchMaterializer]
|
||||
M2Batch --> M2Resources[M2BuildResourceSnapshot]
|
||||
M2Resources --> M2Dispatch[M2BuildDispatchPlanner]
|
||||
M2Dispatch --> M2Static[M2StaticBatchMaterializer]
|
||||
M2Static --> M2
|
||||
R --> WmoPlacement[WmoPlacementResolver]
|
||||
WmoPlacement --> WmoRegistry[WmoPlacementRegistry]
|
||||
@@ -377,21 +406,40 @@ sequenceDiagram
|
||||
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.
|
||||
`M2StaticBatchMaterializer` owns static MultiMesh construction/attachment;
|
||||
the loader retains resource transitions, cursor-adoption decisions,
|
||||
animated/static dispatch, root cleanup, budgets and Editor ownership.
|
||||
`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,
|
||||
@@ -402,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.
|
||||
@@ -520,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.
|
||||
@@ -562,7 +628,14 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| 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 |
|
||||
@@ -572,9 +645,15 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| 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 |
|
||||
@@ -630,18 +709,31 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/render/liquid/adt_water_scene_finalizer.gd` | Main-thread ADT water build, tile attachment and optional editor ownership |
|
||||
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Billboard/UV-rotation stale cached-mesh rebuild decision and memoization |
|
||||
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Animated M2 pending Resource paths, terminal statuses and finalize FIFO |
|
||||
| `src/render/m2/m2_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 |
|
||||
@@ -658,17 +750,30 @@ Exact exported settings and cache versions remain documented in [`../../RENDER.m
|
||||
| `src/tools/verify_adt_water_scene_finalizer.gd` | Synthetic ADT water scene/ownership/boundary/timing regression |
|
||||
| `src/tools/verify_m2_runtime_mesh_rebuild_classifier.gd` | M2 rebuild predicate/cache/boundary/timing regression |
|
||||
| `src/tools/verify_m2_animation_load_pipeline_state.gd` | Animated M2 request/terminal/FIFO/boundary/timing regression |
|
||||
| `src/tools/verify_m2_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 |
|
||||
@@ -680,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
@@ -39,9 +39,12 @@ func start_instance_playback(
|
||||
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"):
|
||||
animator.prepare_runtime()
|
||||
animator.set_phase(phase)
|
||||
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:
|
||||
|
||||
@@ -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,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
|
||||
@@ -0,0 +1,126 @@
|
||||
class_name M2StaticBuildResourceObserver
|
||||
extends RefCounted
|
||||
|
||||
## Resolves or requests one static M2 build Mesh and fills a resource snapshot.
|
||||
|
||||
const OUTCOME_REJECTED := &"rejected"
|
||||
const OUTCOME_CACHED := &"cached"
|
||||
const OUTCOME_PENDING := &"pending"
|
||||
const OUTCOME_REQUESTED := &"requested"
|
||||
const OUTCOME_MISSING := &"missing"
|
||||
|
||||
|
||||
## Observes cache/request state and adopts the exact static result into snapshot.
|
||||
func observe(
|
||||
resource_snapshot: RefCounted,
|
||||
normalized_relative_path: String,
|
||||
cache_directory: String,
|
||||
mesh_cache_state: RefCounted,
|
||||
prototype_cache_state: RefCounted,
|
||||
load_pipeline_state: RefCounted
|
||||
) -> StringName:
|
||||
if (
|
||||
resource_snapshot == null
|
||||
or normalized_relative_path.is_empty()
|
||||
or mesh_cache_state == null
|
||||
or prototype_cache_state == null
|
||||
or load_pipeline_state == null
|
||||
):
|
||||
return OUTCOME_REJECTED
|
||||
if bool(mesh_cache_state.call("has_mesh", normalized_relative_path)):
|
||||
resource_snapshot.call(
|
||||
"adopt_static_observation",
|
||||
mesh_cache_state.call("find_mesh", normalized_relative_path) as Mesh,
|
||||
false
|
||||
)
|
||||
return OUTCOME_CACHED
|
||||
if bool(prototype_cache_state.call("is_model_missing", normalized_relative_path)):
|
||||
resource_snapshot.call("adopt_static_observation", null, true)
|
||||
return OUTCOME_MISSING
|
||||
if bool(load_pipeline_state.call("has_request", normalized_relative_path)):
|
||||
resource_snapshot.call("adopt_static_observation", null, false)
|
||||
return OUTCOME_PENDING
|
||||
|
||||
for cache_resource_path in cache_resource_paths(
|
||||
cache_directory,
|
||||
normalized_relative_path,
|
||||
[".tscn", ".glb"]
|
||||
):
|
||||
if not ResourceLoader.exists(cache_resource_path):
|
||||
continue
|
||||
if (
|
||||
cache_resource_path.get_extension().to_lower() == "glb"
|
||||
and glb_animation_schema(cache_resource_path) == "pivot_prefix_v1"
|
||||
):
|
||||
continue
|
||||
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
|
||||
)
|
||||
resource_snapshot.call("adopt_static_observation", null, false)
|
||||
return OUTCOME_REQUESTED
|
||||
break
|
||||
|
||||
prototype_cache_state.call("mark_model_missing", normalized_relative_path)
|
||||
resource_snapshot.call("adopt_static_observation", null, true)
|
||||
return OUTCOME_MISSING
|
||||
|
||||
|
||||
## Returns historical nested/lowercase/basename cache candidates without I/O.
|
||||
func cache_resource_paths(
|
||||
cache_directory: String,
|
||||
relative_path: String,
|
||||
extensions: Array[String]
|
||||
) -> PackedStringArray:
|
||||
var normalized := relative_path.replace("\\", "/")
|
||||
var lower := normalized.to_lower()
|
||||
var stems := [
|
||||
normalized.get_basename(),
|
||||
lower.get_basename(),
|
||||
normalized.get_file().get_basename(),
|
||||
lower.get_file().get_basename(),
|
||||
]
|
||||
var result := PackedStringArray()
|
||||
for extension in extensions:
|
||||
for stem in stems:
|
||||
if stem.is_empty():
|
||||
continue
|
||||
var path := cache_directory.path_join(stem + extension)
|
||||
if not result.has(path):
|
||||
result.append(path)
|
||||
return result
|
||||
|
||||
|
||||
## Reads only the OpenWC animation schema marker from a GLB JSON chunk.
|
||||
func glb_animation_schema(cache_resource_path: String) -> String:
|
||||
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()
|
||||
)
|
||||
if not (parsed is Dictionary):
|
||||
return ""
|
||||
var asset: Dictionary = (parsed as Dictionary).get("asset", {})
|
||||
var extras: Dictionary = asset.get("extras", {})
|
||||
return String(extras.get("openwc_m2_anim_schema", ""))
|
||||
@@ -0,0 +1 @@
|
||||
uid://c0e3p80ld1dfb
|
||||
@@ -0,0 +1,101 @@
|
||||
class_name WmoRenderGroupMaterializer
|
||||
extends RefCounted
|
||||
|
||||
## Creates and attaches one lightweight cached WMO render group. Resource
|
||||
## finalization, build-step selection and queue progress remain caller-owned.
|
||||
## All methods mutate SceneTree nodes and must run on the renderer main thread.
|
||||
|
||||
|
||||
## Creates one MeshInstance3D for [param group_index], applies the indexed name
|
||||
## and optional transform contracts, then attaches it to [param wmo_parent_root].
|
||||
## The parent owns the returned node; the exact Mesh identity is retained.
|
||||
## Null/invalid parents, null meshes and negative indices return null.
|
||||
func materialize_mesh_group(
|
||||
wmo_parent_root: Node3D,
|
||||
mesh: Mesh,
|
||||
group_names: PackedStringArray,
|
||||
group_transforms: Array,
|
||||
group_index: int,
|
||||
visibility_range_end: float,
|
||||
visibility_range_end_margin: float,
|
||||
cast_shadows: bool) -> MeshInstance3D:
|
||||
if (
|
||||
wmo_parent_root == null
|
||||
or not is_instance_valid(wmo_parent_root)
|
||||
or mesh == null
|
||||
or group_index < 0
|
||||
):
|
||||
return null
|
||||
|
||||
var mesh_instance := MeshInstance3D.new()
|
||||
mesh_instance.name = (
|
||||
group_names[group_index]
|
||||
if group_index < group_names.size()
|
||||
else "Group_%d" % group_index
|
||||
)
|
||||
mesh_instance.mesh = mesh
|
||||
if group_index < group_transforms.size():
|
||||
mesh_instance.transform = group_transforms[group_index]
|
||||
_apply_render_settings(
|
||||
mesh_instance,
|
||||
visibility_range_end,
|
||||
visibility_range_end_margin,
|
||||
cast_shadows
|
||||
)
|
||||
wmo_parent_root.add_child(mesh_instance)
|
||||
return mesh_instance
|
||||
|
||||
|
||||
## Creates one MultiMeshInstance3D for [param group_index], applies the indexed
|
||||
## name and optional transform contracts, then attaches it to the supplied root.
|
||||
## The parent owns the returned node; the exact MultiMesh identity is retained.
|
||||
## Null/invalid parents, null MultiMeshes and negative indices return null.
|
||||
func materialize_multimesh_group(
|
||||
wmo_parent_root: Node3D,
|
||||
multimesh: MultiMesh,
|
||||
group_names: PackedStringArray,
|
||||
group_transforms: Array,
|
||||
group_index: int,
|
||||
visibility_range_end: float,
|
||||
visibility_range_end_margin: float,
|
||||
cast_shadows: bool) -> MultiMeshInstance3D:
|
||||
if (
|
||||
wmo_parent_root == null
|
||||
or not is_instance_valid(wmo_parent_root)
|
||||
or multimesh == null
|
||||
or group_index < 0
|
||||
):
|
||||
return null
|
||||
|
||||
var multimesh_instance := MultiMeshInstance3D.new()
|
||||
multimesh_instance.name = (
|
||||
group_names[group_index]
|
||||
if group_index < group_names.size()
|
||||
else "DoodadGroup_%d" % group_index
|
||||
)
|
||||
multimesh_instance.multimesh = multimesh
|
||||
if group_index < group_transforms.size():
|
||||
multimesh_instance.transform = group_transforms[group_index]
|
||||
_apply_render_settings(
|
||||
multimesh_instance,
|
||||
visibility_range_end,
|
||||
visibility_range_end_margin,
|
||||
cast_shadows
|
||||
)
|
||||
wmo_parent_root.add_child(multimesh_instance)
|
||||
return multimesh_instance
|
||||
|
||||
|
||||
func _apply_render_settings(
|
||||
geometry_instance: GeometryInstance3D,
|
||||
visibility_range_end: float,
|
||||
visibility_range_end_margin: float,
|
||||
cast_shadows: bool) -> void:
|
||||
geometry_instance.cast_shadow = (
|
||||
GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
||||
if cast_shadows
|
||||
else GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
)
|
||||
if visibility_range_end > 0.0:
|
||||
geometry_instance.visibility_range_end = visibility_range_end
|
||||
geometry_instance.visibility_range_end_margin = visibility_range_end_margin
|
||||
@@ -0,0 +1 @@
|
||||
uid://cu4tw6868rbkm
|
||||
@@ -0,0 +1,90 @@
|
||||
class_name WmoRenderResourceFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Polls lightweight WMO render-cache requests and publishes terminal outcomes.
|
||||
## Request admission, cache lifetime and WMO materialization remain caller-owned.
|
||||
|
||||
var _expected_resource_script: Script
|
||||
var _minimum_format_version: int
|
||||
var _resource_loader_adapter: Object
|
||||
|
||||
|
||||
func _init(
|
||||
expected_resource_script: Script,
|
||||
minimum_format_version: int,
|
||||
resource_loader_adapter: Object = null
|
||||
) -> void:
|
||||
_expected_resource_script = expected_resource_script
|
||||
_minimum_format_version = minimum_format_version
|
||||
_resource_loader_adapter = resource_loader_adapter
|
||||
|
||||
|
||||
## Polls a detached pending snapshot in insertion order. Only terminal requests
|
||||
## are removed and published as an exact Resource reference or missing outcome.
|
||||
func poll_terminal_requests(render_resource_cache_state: RefCounted) -> int:
|
||||
if render_resource_cache_state == null:
|
||||
return 0
|
||||
var completed_request_count := 0
|
||||
var request_paths: Dictionary = render_resource_cache_state.call(
|
||||
"request_paths_snapshot"
|
||||
)
|
||||
for normalized_relative_path_variant in request_paths.keys():
|
||||
var normalized_relative_path := String(normalized_relative_path_variant)
|
||||
var resource_path := String(request_paths[normalized_relative_path_variant])
|
||||
var load_status := load_threaded_get_status(resource_path)
|
||||
if (
|
||||
load_status != ResourceLoader.THREAD_LOAD_LOADED
|
||||
and load_status != ResourceLoader.THREAD_LOAD_FAILED
|
||||
):
|
||||
continue
|
||||
if load_status != ResourceLoader.THREAD_LOAD_LOADED:
|
||||
render_resource_cache_state.call(
|
||||
"complete_request_as_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
completed_request_count += 1
|
||||
continue
|
||||
var loaded_resource := load_threaded_get(resource_path)
|
||||
if is_current_render_resource(loaded_resource):
|
||||
render_resource_cache_state.call(
|
||||
"complete_request_with_resource",
|
||||
normalized_relative_path,
|
||||
loaded_resource
|
||||
)
|
||||
else:
|
||||
render_resource_cache_state.call(
|
||||
"complete_request_as_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
completed_request_count += 1
|
||||
return completed_request_count
|
||||
|
||||
|
||||
## Accepts only the exact configured script and its current-or-newer format.
|
||||
func is_current_render_resource(resource: Resource) -> bool:
|
||||
return (
|
||||
resource != null
|
||||
and _expected_resource_script != null
|
||||
and resource.get_script() == _expected_resource_script
|
||||
and int(resource.get("format_version")) >= _minimum_format_version
|
||||
)
|
||||
|
||||
|
||||
## Production ResourceLoader status boundary; injectable for 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 result boundary; injectable for synthetic 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://dtvba6g1grttk
|
||||
@@ -0,0 +1,105 @@
|
||||
class_name WmoRuntimeMeshFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Refreshes cached WMO runtime Mesh materials through the configured builder.
|
||||
## Scene traversal, placement and Node lifetime remain caller-owned.
|
||||
|
||||
const MATERIAL_REFRESH_VERSION := 10
|
||||
const MATERIAL_REFRESH_VERSION_METADATA := "wow_wmo_material_refresh_version"
|
||||
|
||||
var _wmo_material_builder: Object
|
||||
|
||||
|
||||
func _init(wmo_material_builder: Object) -> void:
|
||||
_wmo_material_builder = wmo_material_builder
|
||||
|
||||
|
||||
## Finalizes a stale Mesh in place and returns the exact input Resource identity.
|
||||
## Null remains null; already-current meshes do not cross the builder boundary.
|
||||
func finalize_mesh(mesh: Mesh, extracted_directory: String) -> Mesh:
|
||||
if mesh == null:
|
||||
return null
|
||||
if int(mesh.get_meta(MATERIAL_REFRESH_VERSION_METADATA, 0)) >= MATERIAL_REFRESH_VERSION:
|
||||
return mesh
|
||||
mesh.set_meta(MATERIAL_REFRESH_VERSION_METADATA, MATERIAL_REFRESH_VERSION)
|
||||
if not (mesh is ArrayMesh):
|
||||
return mesh
|
||||
|
||||
var array_mesh := mesh as ArrayMesh
|
||||
for surface_index in range(array_mesh.get_surface_count()):
|
||||
var rebuilt_material := rebuild_cached_material(
|
||||
array_mesh.surface_get_material(surface_index),
|
||||
extracted_directory
|
||||
)
|
||||
if rebuilt_material != null:
|
||||
array_mesh.surface_set_material(surface_index, rebuilt_material)
|
||||
return mesh
|
||||
|
||||
|
||||
## Rebuilds a material carrying the WMO cache metadata contract. Materials
|
||||
## without texture0 metadata and failed builder results remain unchanged by the caller.
|
||||
func rebuild_cached_material(
|
||||
material: Material,
|
||||
extracted_directory: String
|
||||
) -> Material:
|
||||
if (
|
||||
material == null
|
||||
or not material.has_meta("texture0_path")
|
||||
or _wmo_material_builder == null
|
||||
):
|
||||
return null
|
||||
|
||||
var texture_paths := PackedStringArray()
|
||||
var texture0_index := _append_texture_path(
|
||||
texture_paths,
|
||||
String(material.get_meta("texture0_path", ""))
|
||||
)
|
||||
var texture1_index := _append_texture_path(
|
||||
texture_paths,
|
||||
String(material.get_meta("texture1_path", ""))
|
||||
)
|
||||
var texture2_index := _append_texture_path(
|
||||
texture_paths,
|
||||
String(material.get_meta("texture2_path", ""))
|
||||
)
|
||||
|
||||
var diffuse_color := Color.WHITE
|
||||
var emissive_color := Color.BLACK
|
||||
var secondary_color := Color.WHITE
|
||||
if material is ShaderMaterial:
|
||||
var shader_material := material as ShaderMaterial
|
||||
var diffuse_value: Variant = shader_material.get_shader_parameter("diffuse_color")
|
||||
var emissive_value: Variant = shader_material.get_shader_parameter("emissive_color")
|
||||
var secondary_value: Variant = shader_material.get_shader_parameter("secondary_color")
|
||||
if diffuse_value is Color:
|
||||
diffuse_color = diffuse_value
|
||||
if emissive_value is Color:
|
||||
emissive_color = emissive_value
|
||||
if secondary_value is Color:
|
||||
secondary_color = secondary_value
|
||||
|
||||
var material_definition := {
|
||||
"texture0": texture0_index,
|
||||
"texture1": texture1_index,
|
||||
"texture2": texture2_index,
|
||||
"flags": int(material.get_meta("wow_flags", 0)),
|
||||
"shader": int(material.get_meta("wow_shader", 0)),
|
||||
"blend_mode": int(material.get_meta("wow_blend_mode", 0)),
|
||||
"diffuse_color": diffuse_color,
|
||||
"emissive_color": emissive_color,
|
||||
"color2": secondary_color,
|
||||
}
|
||||
return _wmo_material_builder.call(
|
||||
"_build_material",
|
||||
material_definition,
|
||||
texture_paths,
|
||||
extracted_directory
|
||||
) as Material
|
||||
|
||||
|
||||
func _append_texture_path(texture_paths: PackedStringArray, texture_path: String) -> int:
|
||||
if texture_path.is_empty():
|
||||
return -1
|
||||
var texture_index := texture_paths.size()
|
||||
texture_paths.append(texture_path)
|
||||
return texture_index
|
||||
@@ -0,0 +1 @@
|
||||
uid://bsxqkjuk77cgf
|
||||
@@ -0,0 +1,80 @@
|
||||
class_name WmoRuntimeScenePreparer
|
||||
extends RefCounted
|
||||
|
||||
## Applies the existing cached/live WMO subtree preparation rules on the
|
||||
## renderer main thread. Placement, attachment and subtree lifetime stay caller-owned.
|
||||
|
||||
var _runtime_mesh_finalizer: Object
|
||||
|
||||
|
||||
func _init(runtime_mesh_finalizer: Object) -> void:
|
||||
_runtime_mesh_finalizer = runtime_mesh_finalizer
|
||||
|
||||
|
||||
## Prepares an instantiated cached WMO scene by finalizing every borrowed Mesh,
|
||||
## then applying the historical direct Occluders-child and shadow policies.
|
||||
## Returns false for a null/freed root without mutating or retaining anything.
|
||||
func prepare_cached_instance(
|
||||
instance: Node3D,
|
||||
extracted_directory: String,
|
||||
enable_occlusion_culling: bool,
|
||||
cast_shadows: bool) -> bool:
|
||||
if instance == null or not is_instance_valid(instance):
|
||||
return false
|
||||
_finalize_meshes_in_subtree(instance, extracted_directory)
|
||||
_apply_runtime_render_policy(instance, enable_occlusion_culling, cast_shadows)
|
||||
return true
|
||||
|
||||
|
||||
## Prepares a duplicated live-built WMO without re-finalizing its Meshes.
|
||||
## Returns false for a null/freed root without mutating or retaining anything.
|
||||
func prepare_live_instance(
|
||||
instance: Node3D,
|
||||
enable_occlusion_culling: bool,
|
||||
cast_shadows: bool) -> bool:
|
||||
if instance == null or not is_instance_valid(instance):
|
||||
return false
|
||||
_apply_runtime_render_policy(instance, enable_occlusion_culling, cast_shadows)
|
||||
return true
|
||||
|
||||
|
||||
func _finalize_meshes_in_subtree(node: Node, extracted_directory: String) -> void:
|
||||
if _runtime_mesh_finalizer != null:
|
||||
if node is MeshInstance3D:
|
||||
_runtime_mesh_finalizer.call(
|
||||
"finalize_mesh",
|
||||
(node as MeshInstance3D).mesh,
|
||||
extracted_directory
|
||||
)
|
||||
elif node is MultiMeshInstance3D:
|
||||
var multimesh := (node as MultiMeshInstance3D).multimesh
|
||||
if multimesh != null:
|
||||
_runtime_mesh_finalizer.call(
|
||||
"finalize_mesh",
|
||||
multimesh.mesh,
|
||||
extracted_directory
|
||||
)
|
||||
for child in node.get_children():
|
||||
_finalize_meshes_in_subtree(child, extracted_directory)
|
||||
|
||||
|
||||
func _apply_runtime_render_policy(
|
||||
instance: Node3D,
|
||||
enable_occlusion_culling: bool,
|
||||
cast_shadows: bool) -> void:
|
||||
if not enable_occlusion_culling:
|
||||
var occluders := instance.get_node_or_null("Occluders")
|
||||
if occluders != null:
|
||||
instance.remove_child(occluders)
|
||||
occluders.queue_free()
|
||||
if cast_shadows:
|
||||
_enable_shadow_casting_recursive(instance)
|
||||
|
||||
|
||||
func _enable_shadow_casting_recursive(node: Node) -> void:
|
||||
if node is GeometryInstance3D:
|
||||
(node as GeometryInstance3D).cast_shadow = (
|
||||
GeometryInstance3D.SHADOW_CASTING_SETTING_ON
|
||||
)
|
||||
for child in node.get_children():
|
||||
_enable_shadow_casting_recursive(child)
|
||||
@@ -0,0 +1 @@
|
||||
uid://d1t0vlkco8kw8
|
||||
@@ -0,0 +1,73 @@
|
||||
class_name WmoSceneInstanceFactory
|
||||
extends RefCounted
|
||||
|
||||
## Creates detached cached/live WMO Node3D instances with the existing cache
|
||||
## validation, naming and world-placement rules. Runtime preparation is separate.
|
||||
|
||||
var _scene_cache_validator: Object
|
||||
var _placement_resolver: Object
|
||||
|
||||
|
||||
func _init(scene_cache_validator: Object, placement_resolver: Object) -> void:
|
||||
_scene_cache_validator = scene_cache_validator
|
||||
_placement_resolver = placement_resolver
|
||||
|
||||
|
||||
## Returns whether [param node] satisfies the injected WMO scene-cache contract.
|
||||
## Null nodes or missing validators return false without mutation.
|
||||
func is_cached_node_current(node: Node) -> bool:
|
||||
if node == null or _scene_cache_validator == null:
|
||||
return false
|
||||
return bool(_scene_cache_validator.call("is_scene_cache_current", node))
|
||||
|
||||
|
||||
## Instantiates, validates, names and places a cached WMO PackedScene. Rejected
|
||||
## instantiated roots are freed synchronously. The accepted detached Node3D is
|
||||
## caller-owned and retains its exact descendant Resource identities.
|
||||
func instantiate_cached_scene(
|
||||
relative_path: String,
|
||||
scene: PackedScene,
|
||||
placement: Dictionary) -> Node3D:
|
||||
if scene == null or _placement_resolver == null:
|
||||
return null
|
||||
var instantiated_root := scene.instantiate()
|
||||
if not (instantiated_root is Node3D):
|
||||
if instantiated_root != null:
|
||||
instantiated_root.free()
|
||||
return null
|
||||
var instance := instantiated_root as Node3D
|
||||
if not is_cached_node_current(instance):
|
||||
instance.free()
|
||||
return null
|
||||
_apply_identity_and_placement(instance, relative_path, placement)
|
||||
return instance
|
||||
|
||||
|
||||
## Duplicates, names and places a live-built WMO prototype. The detached result
|
||||
## is caller-owned. Null inputs, missing placement composition or an unexpected
|
||||
## non-Node3D duplicate return null; rejected duplicates are freed synchronously.
|
||||
func duplicate_live_prototype(
|
||||
relative_path: String,
|
||||
prototype: Node3D,
|
||||
placement: Dictionary) -> Node3D:
|
||||
if prototype == null or _placement_resolver == null:
|
||||
return null
|
||||
var duplicated_root := prototype.duplicate()
|
||||
if not (duplicated_root is Node3D):
|
||||
if duplicated_root != null:
|
||||
duplicated_root.free()
|
||||
return null
|
||||
var instance := duplicated_root as Node3D
|
||||
_apply_identity_and_placement(instance, relative_path, placement)
|
||||
return instance
|
||||
|
||||
|
||||
func _apply_identity_and_placement(
|
||||
instance: Node3D,
|
||||
relative_path: String,
|
||||
placement: Dictionary) -> void:
|
||||
instance.name = relative_path.get_file().get_basename()
|
||||
instance.transform = _placement_resolver.call(
|
||||
"resolve_world_transform",
|
||||
placement
|
||||
) as Transform3D
|
||||
@@ -0,0 +1 @@
|
||||
uid://c13w66d7uaf2n
|
||||
@@ -0,0 +1,97 @@
|
||||
class_name WmoSceneResourceFinalizer
|
||||
extends RefCounted
|
||||
|
||||
## Polls cached WMO PackedScene requests and publishes validated outcomes.
|
||||
## File-size admission, live fallback and attached Node lifetime remain caller-owned.
|
||||
|
||||
var _scene_cache_validator: Object
|
||||
var _resource_loader_adapter: Object
|
||||
|
||||
|
||||
func _init(
|
||||
scene_cache_validator: Object,
|
||||
resource_loader_adapter: Object = null
|
||||
) -> void:
|
||||
_scene_cache_validator = scene_cache_validator
|
||||
_resource_loader_adapter = resource_loader_adapter
|
||||
|
||||
|
||||
## Polls a detached pending snapshot in insertion order. Terminal requests are
|
||||
## published as the exact validated PackedScene or the historical missing state.
|
||||
func poll_terminal_requests(scene_resource_cache_state: RefCounted) -> int:
|
||||
if scene_resource_cache_state == null:
|
||||
return 0
|
||||
var completed_request_count := 0
|
||||
var request_paths: Dictionary = scene_resource_cache_state.call(
|
||||
"request_paths_snapshot"
|
||||
)
|
||||
for normalized_relative_path_variant in request_paths.keys():
|
||||
var normalized_relative_path := String(normalized_relative_path_variant)
|
||||
var resource_path := String(request_paths[normalized_relative_path_variant])
|
||||
var load_status := load_threaded_get_status(resource_path)
|
||||
if (
|
||||
load_status != ResourceLoader.THREAD_LOAD_LOADED
|
||||
and load_status != ResourceLoader.THREAD_LOAD_FAILED
|
||||
):
|
||||
continue
|
||||
if load_status != ResourceLoader.THREAD_LOAD_LOADED:
|
||||
scene_resource_cache_state.call(
|
||||
"complete_request_as_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
completed_request_count += 1
|
||||
continue
|
||||
var loaded_resource := load_threaded_get(resource_path)
|
||||
var loaded_scene := loaded_resource as PackedScene
|
||||
if is_scene_cache_current(loaded_scene):
|
||||
scene_resource_cache_state.call(
|
||||
"complete_request_with_scene",
|
||||
normalized_relative_path,
|
||||
loaded_scene
|
||||
)
|
||||
else:
|
||||
scene_resource_cache_state.call(
|
||||
"complete_request_as_missing",
|
||||
normalized_relative_path
|
||||
)
|
||||
completed_request_count += 1
|
||||
return completed_request_count
|
||||
|
||||
|
||||
## Instantiates one Node3D probe, delegates the existing WMOBuilder metadata
|
||||
## check and synchronously releases the probe before returning.
|
||||
func is_scene_cache_current(scene: PackedScene) -> bool:
|
||||
if scene == null or _scene_cache_validator == null:
|
||||
return false
|
||||
var instantiated_node := scene.instantiate()
|
||||
if not (instantiated_node is Node3D):
|
||||
if instantiated_node != null:
|
||||
instantiated_node.free()
|
||||
return false
|
||||
var instance := instantiated_node as Node3D
|
||||
var is_current := bool(_scene_cache_validator.call(
|
||||
"is_scene_cache_current",
|
||||
instance
|
||||
))
|
||||
instance.free()
|
||||
return is_current
|
||||
|
||||
|
||||
## Production ResourceLoader status boundary; injectable for 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 result boundary; injectable for synthetic 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://cp2r3tadn8l6q
|
||||
@@ -26,11 +26,13 @@ func setup(target_mesh_instance: MeshInstance3D, bone_data: Array, surface_data:
|
||||
_capture_materials()
|
||||
_make_mesh_unique()
|
||||
_rebuild_mesh(0.0)
|
||||
set_process(mesh != null and not bones.is_empty() and not surfaces.is_empty() and animation_length > 0.0)
|
||||
_prepared = _has_runtime_animation_data()
|
||||
set_process(_prepared)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
prepare_runtime()
|
||||
if not _prepared:
|
||||
prepare_runtime()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
@@ -41,24 +43,49 @@ func _process(delta: float) -> void:
|
||||
|
||||
|
||||
func set_phase(phase: float) -> void:
|
||||
if animation_length <= 0.0:
|
||||
_time = 0.0
|
||||
else:
|
||||
_time = fposmod(animation_length * phase, animation_length)
|
||||
_set_phase_time(phase)
|
||||
_rebuild_mesh(_time)
|
||||
|
||||
|
||||
func prepare_runtime() -> bool:
|
||||
return _prepare_runtime(false)
|
||||
|
||||
|
||||
## Rebinds a duplicated animator to its local mesh and applies its deterministic
|
||||
## phase with one deformation rebuild. This must happen before attachment so
|
||||
## _ready() can remain idempotent for already prepared runtime instances.
|
||||
func prepare_runtime_at_phase(phase: float) -> bool:
|
||||
_set_phase_time(phase)
|
||||
return _prepare_runtime(true)
|
||||
|
||||
|
||||
func _prepare_runtime(force_rebuild: bool) -> bool:
|
||||
if _prepared and not force_rebuild:
|
||||
set_process(true)
|
||||
return true
|
||||
_resolve_mesh_instance()
|
||||
if force_rebuild:
|
||||
_materials.clear()
|
||||
_capture_materials()
|
||||
_unique_mesh_ready = false
|
||||
_make_mesh_unique()
|
||||
_rebuild_mesh(_time)
|
||||
_prepared = mesh != null and not bones.is_empty() and not surfaces.is_empty() and animation_length > 0.0
|
||||
_prepared = _has_runtime_animation_data()
|
||||
set_process(_prepared)
|
||||
return _prepared
|
||||
|
||||
|
||||
func _has_runtime_animation_data() -> bool:
|
||||
return mesh != null and not bones.is_empty() and not surfaces.is_empty() and animation_length > 0.0
|
||||
|
||||
|
||||
func _set_phase_time(phase: float) -> void:
|
||||
if animation_length <= 0.0:
|
||||
_time = 0.0
|
||||
else:
|
||||
_time = fposmod(animation_length * phase, animation_length)
|
||||
|
||||
|
||||
func runtime_debug_state() -> Dictionary:
|
||||
return {
|
||||
"prepared": _prepared,
|
||||
@@ -81,11 +108,12 @@ func _resolve_mesh_instance() -> void:
|
||||
func _make_mesh_unique() -> void:
|
||||
if _unique_mesh_ready or mesh_instance == null or mesh_instance.mesh == null:
|
||||
return
|
||||
var duplicated := mesh_instance.mesh.duplicate(true) as ArrayMesh
|
||||
if duplicated == null:
|
||||
return
|
||||
mesh_instance.mesh = duplicated
|
||||
mesh = duplicated
|
||||
# _rebuild_mesh() replaces every surface from the retained native arrays, so
|
||||
# copying the source ArrayMesh would only duplicate data that is discarded.
|
||||
# Materials were captured before this call and are intentionally shared.
|
||||
var instance_mesh := ArrayMesh.new()
|
||||
mesh_instance.mesh = instance_mesh
|
||||
mesh = instance_mesh
|
||||
_unique_mesh_ready = true
|
||||
|
||||
|
||||
@@ -143,20 +171,20 @@ func _rebuild_mesh(time: float) -> void:
|
||||
continue
|
||||
var transform: Transform3D = bone_matrices[bone_index]
|
||||
skinned_pos += transform * base_vertices[vertex_index] * weight
|
||||
if normals.size() == base_normals.size():
|
||||
if not normals.is_empty():
|
||||
skinned_nrm += (transform.basis * base_normals[vertex_index]) * weight
|
||||
total_weight += weight
|
||||
if total_weight > 0.0:
|
||||
vertices[vertex_index] = skinned_pos / total_weight
|
||||
if normals.size() == base_normals.size():
|
||||
if not normals.is_empty():
|
||||
normals[vertex_index] = (skinned_nrm / total_weight).normalized()
|
||||
else:
|
||||
vertices[vertex_index] = base_vertices[vertex_index]
|
||||
if normals.size() == base_normals.size():
|
||||
if not normals.is_empty():
|
||||
normals[vertex_index] = base_normals[vertex_index]
|
||||
else:
|
||||
vertices[vertex_index] = base_vertices[vertex_index]
|
||||
if normals.size() == base_normals.size():
|
||||
if not normals.is_empty():
|
||||
normals[vertex_index] = base_normals[vertex_index]
|
||||
|
||||
var arrays := []
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -176,14 +176,14 @@ func _bake_glb_animation_cache(
|
||||
if not force and FileAccess.file_exists(abs_out_glb):
|
||||
return true
|
||||
var abs_converter := ProjectSettings.globalize_path(converter)
|
||||
var abs_output := ProjectSettings.globalize_path(output_dir)
|
||||
var converter_output_directory := abs_out_glb.get_base_dir()
|
||||
if not FileAccess.file_exists(abs_converter):
|
||||
push_warning("M2 GLB converter not found: %s" % converter)
|
||||
return false
|
||||
var stdout := []
|
||||
var exit_code := OS.execute(
|
||||
python_exe,
|
||||
[abs_converter, abs_m2, abs_output],
|
||||
[abs_converter, abs_m2, converter_output_directory],
|
||||
stdout,
|
||||
true,
|
||||
false)
|
||||
|
||||
@@ -5,6 +5,9 @@ extends SceneTree
|
||||
|
||||
const FINALIZER_SCRIPT := preload("res://src/render/m2/m2_animated_scene_finalizer.gd")
|
||||
const FINALIZER_PATH := "res://src/render/m2/m2_animated_scene_finalizer.gd"
|
||||
const RESOURCE_FINALIZER_PATH := (
|
||||
"res://src/render/m2/m2_animation_resource_finalizer.gd"
|
||||
)
|
||||
const MATERIALIZER_PATH := "res://src/render/m2/m2_animated_instance_materializer.gd"
|
||||
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
||||
|
||||
@@ -116,6 +119,9 @@ func _verify_material_mapping(failures: Array[String]) -> void:
|
||||
|
||||
func _verify_ownership_boundaries(failures: Array[String]) -> void:
|
||||
var finalizer_source := FileAccess.get_file_as_string(FINALIZER_PATH)
|
||||
var resource_finalizer_source := FileAccess.get_file_as_string(
|
||||
RESOURCE_FINALIZER_PATH
|
||||
)
|
||||
var materializer_source := FileAccess.get_file_as_string(MATERIALIZER_PATH)
|
||||
var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
|
||||
_expect_true(loader_source.contains("M2_ANIMATED_SCENE_FINALIZER_SCRIPT.new()"), "loader composes finalizer", failures)
|
||||
@@ -126,27 +132,41 @@ func _verify_ownership_boundaries(failures: Array[String]) -> void:
|
||||
]:
|
||||
_expect_false(loader_source.contains(removed_loader_function), "legacy helper removed: %s" % removed_loader_function, failures)
|
||||
for delegated_call in [
|
||||
"_m2_animated_scene_finalizer.instantiate_candidate(resource)",
|
||||
"_m2_animated_scene_finalizer.repair_materials(candidate, material_source)",
|
||||
"_m2_animated_scene_finalizer.finalize_candidate(candidate)",
|
||||
"_m2_animated_scene_finalizer.mesh_instances_in_subtree(root)",
|
||||
"\"instantiate_candidate\"",
|
||||
"\"repair_materials\"",
|
||||
"\"finalize_candidate\"",
|
||||
]:
|
||||
_expect_equal(loader_source.count(delegated_call), 1, "single loader delegation: %s" % delegated_call, failures)
|
||||
_expect_equal(
|
||||
resource_finalizer_source.count(delegated_call),
|
||||
1,
|
||||
"single resource-finalizer delegation: %s" % delegated_call,
|
||||
failures
|
||||
)
|
||||
_expect_equal(
|
||||
loader_source.count("_m2_animated_scene_finalizer.mesh_instances_in_subtree(root)"),
|
||||
1,
|
||||
"single loader mesh traversal delegation",
|
||||
failures
|
||||
)
|
||||
_expect_equal(
|
||||
materializer_source.count("_animated_scene_finalizer.animation_players_in_subtree("),
|
||||
1,
|
||||
"single materializer player-inventory delegation",
|
||||
failures
|
||||
)
|
||||
for retained_loader_rule in [
|
||||
"ResourceLoader.load_threaded_get(path)",
|
||||
for retained_renderer_rule in [
|
||||
"ResourceLoader.load_threaded_get(resource_path)",
|
||||
"RENDER_BUDGET_SCHEDULER_SCRIPT.M2_ANIMATION_FINALIZE",
|
||||
"_get_or_load_m2_material_prototype(normalized_rel)",
|
||||
"_m2_prototype_cache_state.adopt_animated_prototype(",
|
||||
"_m2_prototype_cache_state.mark_animation_static(normalized_rel)",
|
||||
"\"adopt_animated_prototype\"",
|
||||
"\"mark_animation_static\"",
|
||||
"M2_ANIM_CACHE path=%s cache=%s players=%d",
|
||||
]:
|
||||
_expect_true(loader_source.contains(retained_loader_rule), "loader retains %s" % retained_loader_rule, failures)
|
||||
_expect_true(
|
||||
(loader_source + resource_finalizer_source).contains(retained_renderer_rule),
|
||||
"renderer retains %s" % retained_renderer_rule,
|
||||
failures
|
||||
)
|
||||
for forbidden_dependency in [
|
||||
"ResourceLoader.",
|
||||
"FileAccess.",
|
||||
|
||||
@@ -5,6 +5,12 @@ extends SceneTree
|
||||
|
||||
const PIPELINE_SCRIPT := preload("res://src/render/m2/m2_animation_load_pipeline_state.gd")
|
||||
const PIPELINE_PATH := "res://src/render/m2/m2_animation_load_pipeline_state.gd"
|
||||
const OBSERVER_PATH := (
|
||||
"res://src/render/m2/m2_cached_animation_resource_observer.gd"
|
||||
)
|
||||
const RESOURCE_FINALIZER_PATH := (
|
||||
"res://src/render/m2/m2_animation_resource_finalizer.gd"
|
||||
)
|
||||
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
||||
|
||||
|
||||
@@ -80,21 +86,33 @@ func _verify_discard_metrics_clear_and_diagnostics(failures: Array[String]) -> v
|
||||
|
||||
func _verify_ownership_boundaries(failures: Array[String]) -> void:
|
||||
var pipeline_source := FileAccess.get_file_as_string(PIPELINE_PATH)
|
||||
var observer_source := FileAccess.get_file_as_string(OBSERVER_PATH)
|
||||
var resource_finalizer_source := FileAccess.get_file_as_string(
|
||||
RESOURCE_FINALIZER_PATH
|
||||
)
|
||||
var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
|
||||
_expect_true(loader_source.contains("M2_ANIMATION_LOAD_PIPELINE_STATE_SCRIPT.new()"), "loader composes pipeline state", failures)
|
||||
_expect_false(loader_source.contains("var _m2_animation_load_requests:"), "legacy request field removed", failures)
|
||||
_expect_false(loader_source.contains("var _m2_animation_finalize_queue:"), "legacy finalize field removed", failures)
|
||||
_expect_equal(loader_source.count("_m2_animation_load_pipeline_state.total_work_count()"), 3, "three existing metrics delegate", failures)
|
||||
_expect_equal(loader_source.count("_m2_animation_load_pipeline_state.clear()"), 2, "two existing clear sites delegate", failures)
|
||||
for retained_loader_rule in [
|
||||
"ResourceLoader.load_threaded_request(",
|
||||
"ResourceLoader.load_threaded_get_status(path)",
|
||||
"ResourceLoader.load_threaded_get(path)",
|
||||
_expect_true(
|
||||
observer_source.contains("ResourceLoader.load_threaded_request("),
|
||||
"cached observer owns request admission",
|
||||
failures
|
||||
)
|
||||
for retained_renderer_rule in [
|
||||
"ResourceLoader.load_threaded_get_status(resource_path)",
|
||||
"ResourceLoader.load_threaded_get(resource_path)",
|
||||
"RENDER_BUDGET_SCHEDULER_SCRIPT.M2_ANIMATION_FINALIZE",
|
||||
"_m2_prototype_cache_state.adopt_animated_prototype(",
|
||||
"_m2_prototype_cache_state.mark_animation_static(",
|
||||
"\"adopt_animated_prototype\"",
|
||||
"\"mark_animation_static\"",
|
||||
]:
|
||||
_expect_true(loader_source.contains(retained_loader_rule), "loader retains %s" % retained_loader_rule, failures)
|
||||
_expect_true(
|
||||
(loader_source + resource_finalizer_source).contains(retained_renderer_rule),
|
||||
"renderer retains %s" % retained_renderer_rule,
|
||||
failures
|
||||
)
|
||||
for forbidden_dependency in [
|
||||
"ResourceLoader.",
|
||||
"WorkerThreadPool.",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user