9.0 KiB
M2 Mesh Resource Extractor
Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001 |
| Owners | First-Mesh selection and temporary PackedScene instance lifetime |
| Last verified | Worktree work/sindo-main-codex/m03-m2-mesh-resource-extractor, 2026-07-17 |
| Profiles/capabilities | Static M2 threaded cache and synchronous raw/prototype fallback paths |
Purpose
Select the first M2 Mesh from a direct Resource, PackedScene or existing Node
subtree while keeping traversal and temporary-node lifetime outside
StreamingWorldLoader.
Non-goals
- Request/load Resources or choose cache paths.
- Load raw M2 data, refresh materials or rebuild Meshes.
- Own Mesh/prototype/missing caches or pending/finalize queues.
- Create MultiMeshes, attach Nodes or consume render permits.
- Change first-Mesh selection order or visible output.
Context and boundaries
flowchart LR
Pipeline[M2 load pipeline] --> Loader[StreamingWorldLoader]
Loader --> Resource[Loaded Mesh or PackedScene Resource]
Resource --> Extractor[M2MeshResourceExtractor]
Extractor -->|first Mesh or null| Loader
Loader --> Prepare[M2RuntimeMeshFinalizer]
Prepare --> Cache[M2MeshResourceCacheState]
Prototype[Existing M2 Node subtree] --> Extractor
The extractor may inspect Resource, PackedScene, Node, MeshInstance3D
and Mesh engine types. It has no ResourceLoader, filesystem, worker, cache,
scheduler, builder, gameplay, network or editor dependency.
Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
extract_first_mesh(resource) |
Query with temporary ownership | Return direct Mesh or first Mesh from one PackedScene instance | Renderer main thread; temporary root freed before return | Null/unsupported/empty returns null |
find_first_mesh_in_subtree(root) |
Query | Return first Mesh in historical depth-first preorder | Renderer main thread; caller-owned subtree unchanged | Null/no Mesh returns null |
Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Direct Mesh Resource | Loader threaded finalizer | Extractor | Borrowed Resource | One call |
| Input | PackedScene Resource | Loader threaded finalizer | Extractor | Borrowed scene; extractor owns temporary instance | Temporary root until return |
| Input | Existing prototype/rebuild Node subtree | Loader builder/fallback | Extractor | Borrowed Node tree | One traversal |
| Output | First Mesh reference | Extractor | Loader preparation/cache adapter | Borrowed exact reference | Cache may retain later |
The only side effect is synchronous creation and immediate free() of a
temporary PackedScene instance.
Data flow
flowchart TD
Input[Resource or Node subtree] --> Direct{Direct Mesh?}
Direct -->|yes| Return[Return exact Mesh]
Direct -->|no| Packed{PackedScene?}
Packed -->|yes| Instantiate[Instantiate temporary root]
Packed -->|no| Traverse[Traverse caller-owned subtree]
Instantiate --> TraverseTemp[Depth-first preorder traversal]
TraverseTemp --> Free[Free temporary root]
Free --> ReturnOrNull[Return first Mesh or null]
Traverse --> ReturnOrNull
Lifecycle/state
stateDiagram-v2
[*] --> Stateless
Stateless --> TemporaryInstance: extract PackedScene
TemporaryInstance --> Traversing: instantiate succeeded
Traversing --> Stateless: free temporary root before return
Stateless --> Stateless: direct Mesh/subtree/invalid input
No state or Resource reference is retained between calls.
Main sequence
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Extractor as M2MeshResourceExtractor
participant Scene as PackedScene
Loader->>Extractor: extract_first_mesh(loaded Resource)
alt direct Mesh
Extractor-->>Loader: 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
end
Loader->>Loader: prepare and cache Mesh or mark missing
Dependency diagram
flowchart TB
Loader[StreamingWorldLoader] --> Extractor[M2MeshResourceExtractor]
Extractor --> Types[Resource / PackedScene / Node / Mesh]
Loader --> Pipeline[M2MeshLoadPipelineState]
Loader --> Cache[M2MeshResourceCacheState]
Loader --> Finalizer[M2RuntimeMeshFinalizer]
Extractor -. no dependency .-> Pipeline
Extractor -. no dependency .-> Cache
Extractor -. no dependency .-> Classifier
Ownership, threading and resources
- The renderer main thread performs all traversal and PackedScene instantiation.
- Direct and subtree Mesh references are borrowed without duplication.
- A PackedScene temporary root is extractor-owned and synchronously freed.
- Existing prototype/rebuild subtrees remain caller-owned and are never mutated.
- Cache retention remains a cache-state concern; runtime material preparation
belongs to
M2RuntimeMeshFinalizer.
Errors, cancellation and recovery
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Null/unsupported Resource | Type guard | Return null | Contract verifier | Loader marks missing/falls back |
| PackedScene instantiate failure | Null root | Return null | Existing loader behavior | Rebuild cache/source |
| No Mesh in subtree | Traversal exhaustion | Return null | Contract verifier | Loader marks missing/falls back |
| Null MeshInstance Mesh | Null traversal result | Parent continues with later siblings | Order verifier | Normal sibling search |
| Shutdown/cancel | No retained state | No action | Ownership docs | Caller owns surrounding job |
Configuration and capabilities
The module adds no settings or profile branches. Cache format order, material refresh version, render permits and batching remain unchanged outside it.
Persistence, cache and migration
The extractor is stateless and serializes nothing. Existing .tscn/.glb
formats, cache versions and rebuild metadata are unchanged; no migration or
rebake is required.
Diagnostics and observability
The module emits no logs and retains no counters. Loader correlation remains the normalized M2 path. Its verifier measures traversal time and temporary Node count.
Verification
verify_m2_mesh_resource_extractor.gd: direct/invalid Resources, depth-first child order, null-Mesh sibling continuation, subtree ownership, PackedScene exact Mesh identity, temporary Node release, source boundaries and 10,000 bounded traversals.- M2 pipeline/cache/classifier/material/shutdown/build regressions cover adjacent behavior. No asset-backed visual parity claim is made.
Extension points
M2RuntimeMeshFinalizerconsumes the Mesh returned here and the loader stores its result inM2MeshResourceCacheState.- Broader generic scene traversal is intentionally excluded until another real consumer requires the same exact contract.
Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Direct/PackedScene first-Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
| Prototype/rebuild subtree selection | Implemented extraction | Synthetic preorder verifier | Asset-backed M2 traversal pending |
| Material refresh/rebuild finalization | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
Known gaps and risks
- PackedScene instantiation remains synchronous main-thread work behind the existing finalize permit.
- The first Mesh rule intentionally ignores additional MeshInstances.
- No proprietary M2 corpus, corrupt cache, leak/descriptor-pressure, p95/p99 or paired original-client capture is included.
Source map
| Path | Responsibility |
|---|---|
src/render/m2/m2_mesh_resource_extractor.gd |
First-Mesh traversal and temporary PackedScene lifetime |
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/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 |