# M2 Runtime Mesh Finalizer ## Metadata | Field | Value | |---|---| | Status | Implemented extraction | | Target/work package | M03 / `M03-RND-M2-RUNTIME-MESH-FINALIZER-001` | | Owners | Stale Mesh refresh version, rebuild classification, M2Builder rebuild and fallback | | Last verified | Worktree `work/sindo-main-codex/m03-m2-runtime-mesh-finalizer`, 2026-07-17 | | Profiles/capabilities | Static cached and prototype M2 runtime material preparation | ## Purpose Finalize an extracted static M2 Mesh for runtime use by accepting already-loaded raw M2 data, preserving material refresh version `2`, rebuilding only billboard/ UV-rotation cases and returning the historical original-Mesh fallback. ## Non-goals - Read raw `.m2` files or call ClassDB/FileAccess/ResourceLoader. - Own request/finalize queues, Mesh/missing/prototype caches or MultiMeshes. - Select cache paths or extract a Mesh from the originally loaded Resource. - Change classifier predicates, builder materials, refresh version or profiles. ## Context and boundaries ```mermaid flowchart LR Extractor[M2MeshResourceExtractor] --> Loader[StreamingWorldLoader] Loader -->|is raw data required?| Finalizer[M2RuntimeMeshFinalizer] Loader --> Raw[M2RawModelRepository] Raw --> Finalizer Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier] Finalizer --> Builder[M2Builder] Builder --> Subtree[M2MeshResourceExtractor] Finalizer -->|prepared Mesh| Cache[M2MeshResourceCacheState] ``` The finalizer may use Mesh metadata, raw value Dictionaries, M2Builder and the two existing scene-free M2 services. Raw-file I/O, cache state, render permits, Nodes outside temporary rebuild roots and other application layers are forbidden. ## Public API | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | `requires_raw_data_for_refresh(mesh)` | Query | Tell loader whether stale Mesh needs raw-file lookup | Renderer main thread | Null/current false | | `finalize_mesh(path, mesh, raw_data, extracted_directory)` | Command/query | Return current, marked, rebuilt or fallback Mesh | Renderer main thread; one finalize operation | Null Mesh returns null; invalid rebuild falls back | | `clear()` | Command | Clear memoized classifier decisions | Map reset/final shutdown | Idempotent | | `cached_rebuild_decision_count()` | Diagnostic query | Expose memoized path-decision count | Tests/diagnostics | None | ## Inputs and outputs | 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 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 | Side effects are Mesh refresh metadata, classifier memoization and temporary M2Builder prototype creation/destruction during required rebuilds. ## Data flow ```mermaid flowchart TD Mesh[Extracted Mesh] --> Current{Refresh version >= 2?} Current -->|yes| ReturnOriginal[Return original unchanged] Current -->|no| NeedRaw[Repository supplies raw M2 data through loader] NeedRaw --> HasData{Raw data available?} HasData -->|no| Mark[Mark original version 2] HasData -->|yes| Classify{Billboard or UV rotation?} Classify -->|no| Mark Classify -->|yes| Build[M2Builder temporary prototype] Build --> Extract[M2MeshResourceExtractor first Mesh] Extract --> Rebuilt{Rebuilt Mesh exists?} Rebuilt -->|yes| ReturnRebuilt[Return rebuilt Mesh] Rebuilt -->|no| Mark Mark --> ReturnOriginal ``` ## Lifecycle/state ```mermaid stateDiagram-v2 [*] --> Current: Mesh metadata >= 2 [*] --> Stale: Mesh metadata < 2 Stale --> Current: empty/ordinary data marks original Stale --> Rebuilding: classifier requires rebuild Rebuilding --> Current: rebuilt Mesh returned Rebuilding --> Current: failure marks original fallback Current --> Current: later preparation is no-op ``` Classifier decisions are memoized per normalized path until either existing map/reset clear site calls `clear()`. ## Main sequence ```mermaid sequenceDiagram participant Loader as StreamingWorldLoader 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) alt current Mesh Finalizer-->>Loader: 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->>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 end ``` ## Dependency diagram ```mermaid flowchart TB Loader[StreamingWorldLoader] --> Finalizer[M2RuntimeMeshFinalizer] Finalizer --> Classifier[M2RuntimeMeshRebuildClassifier] Finalizer --> Extractor[M2MeshResourceExtractor] Finalizer --> Builder[M2Builder] Loader --> Raw[M2RawModelRepository] Loader --> Cache[M2MeshResourceCacheState] Finalizer -. no dependency .-> Raw Finalizer -. no dependency .-> Cache ``` ## 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. - 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. ## Errors, cancellation and recovery | Failure/state | Detection | Behavior | Diagnostic | Recovery | |---|---|---|---|---| | Null Mesh | Guard | Return null | Contract verifier | Caller marks missing | | Current Mesh | Metadata query | Return exact reference; no raw load | Contract verifier | None needed | | Raw file/parse unavailable | Empty Dictionary from loader | Mark/reuse original | Existing renderer behavior | Source/cache repair | | Ordinary stale Mesh | Classifier false | Mark/reuse original | Classifier verifier | None needed | | Builder produces no Mesh | Extractor null | Mark/reuse original | Finalizer fallback fixture | Source/builder repair | | Reset/shutdown | Loader lifecycle | Clear classifier decisions | Source/clear verifier | Recompute next session | ## Configuration and capabilities `MATERIAL_REFRESH_VERSION` remains `2` and is now owned by this finalizer. Finalize permits, cache paths, extracted directory configuration and quality profiles remain unchanged in the loader. ## Persistence, cache and migration No new format is serialized. Existing Mesh metadata key `wow_m2_material_refresh_version`, M2Builder `MATERIAL_FORMAT_VERSION == 2` and `.tscn/.glb` caches are unchanged; no migration or rebake is introduced. ## Diagnostics and observability `cached_rebuild_decision_count` exposes only a scalar count. Existing renderer logs/queue metrics are unchanged. Normalized M2 path remains the correlation key. ## Verification - `verify_m2_runtime_mesh_finalizer.gd`: null/current/raw requirement, empty and ordinary marking, synthetic triangle billboard rebuild, rebuild failure fallback, refresh metadata, classifier clear, source boundary and 100-by-256 bounded finalization. - Classifier/extractor/cache/pipeline/material/shutdown/M2 build regressions cover adjacent behavior. No asset-backed or original-client visual claim is made. ## Extension points - Asset-backed fixture coverage can be added without changing the finalizer's value-data contract. - Refresh versions above `2` require explicit cache/fidelity evidence and docs. ## Capability status | Capability | Status | Evidence | Gap/next step | |---|---|---|---| | Runtime Mesh refresh/fallback | Implemented extraction | Synthetic transition/rebuild verifier | Asset-backed corrupt/stale cache pending | | Rebuild predicate | Implemented extraction dependency | Classifier fixtures | Original-client material comparison pending | | Raw M2 refresh I/O | Implemented repository dependency | Repository source/invalid verifier | Legal success fixture pending | ## Known gaps and risks - Required rebuild remains synchronous main-thread work behind existing permits. - Synthetic geometry proves transition/build behavior, not full asset materials. - No proprietary M2 traversal, descriptor-pressure/leak, p95/p99 or paired original-client capture is included. ## Source map | Path | Responsibility | |---|---| | `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_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 | ## Related decisions and references - [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) - [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md) - [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.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)