Files
open-wc/docs/modules/m2-mesh-resource-extractor.md
T

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

  • M2RuntimeMeshFinalizer consumes the Mesh returned here and the loader stores its result in M2MeshResourceCacheState.
  • 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