Files
open-wc/docs/modules/m2-raw-model-repository.md
T

227 lines
10 KiB
Markdown

# M2 Raw Model Repository
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-RAW-MODEL-REPOSITORY-001` |
| Owners | Native M2Loader availability, file existence and raw Dictionary loading |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-raw-model-repository`, 2026-07-17 |
| Profiles/capabilities | Static and native-animated raw M2 reads |
## Purpose
Provide one stateless boundary between renderer orchestration and the optional
native `M2Loader`. The repository resolves an extracted path and returns the raw
static or animated Dictionary consumed by existing builders and classifiers.
## Non-goals
- Normalize relative paths or choose `.tscn`/`.glb` cache fallbacks.
- Cache successful data, missing paths, Meshes, Nodes or animation decisions.
- Build geometry/materials, own queues or attach renderer objects.
- Change native parsing, cache formats, retry policy or visible behavior.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
Repository --> File[Extracted M2 file]
Repository --> Native[ClassDB M2Loader]
Native --> Raw[Raw Dictionary]
Raw --> Loader
Loader --> Builder[Existing M2 builders/finalizer]
```
Allowed dependencies are `ProjectSettings`, `FileAccess` and `ClassDB` for the
native file boundary. Resource caches, builders, SceneTree types, worker pools,
renderer policy and other application layers are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `load_static_model_data(extracted_directory, normalized_relative_path)` | Query | Invoke native `load_m2` for one existing extracted file | Synchronous; stateless | Returns `{}` for rejected/unavailable/invalid result |
| `load_animated_model_data(extracted_directory, normalized_relative_path)` | Query | Invoke native `load_m2_animated` for one existing extracted file | Synchronous; stateless | Returns `{}` for rejected/unavailable/invalid result |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| 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 | 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
parsing and creation of one temporary native loader instance per accepted call.
## Data flow
```mermaid
flowchart TD
Request[Directory plus normalized path] --> Empty{Path empty?}
Empty -->|yes| Reject[Return empty Dictionary]
Empty -->|no| Class{M2Loader registered?}
Class -->|no| Reject
Class -->|yes| Resolve[globalize directory/path join]
Resolve --> Exists{File exists?}
Exists -->|no| Reject
Exists -->|yes| Instance[Instantiate M2Loader]
Instance --> Method{Instance and method available?}
Method -->|no| Reject
Method -->|yes| Parse[Call static or animated method]
Parse --> Type{Dictionary result?}
Type -->|no| Reject
Type -->|yes| Return[Return raw Dictionary]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Ready: construct repository
Ready --> Resolving: load request
Resolving --> Ready: rejected input/unavailable dependency
Resolving --> Parsing: file and native method available
Parsing --> Ready: Dictionary or empty result returned
Ready --> [*]: caller releases reference
```
No request, result or failure state survives a call.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Repo as M2RawModelRepository
participant File as FileAccess
participant Native as M2Loader
Loader->>Repo: load static/animated(directory, path)
Repo->>File: file_exists(globalized joined path)
alt dependency or file unavailable
Repo-->>Loader: empty Dictionary
else available
Repo->>Native: instantiate and call exact native method
Native-->>Repo: Variant
Repo-->>Loader: Dictionary or empty Dictionary
end
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Repository[M2RawModelRepository]
Repository --> ProjectSettings
Repository --> FileAccess
Repository --> ClassDB
ClassDB --> Native[M2Loader extension]
Loader --> Finalizer[M2RuntimeMeshFinalizer]
Loader --> StaticBuilder[M2Builder]
Loader --> AnimatedBuilder[M2NativeAnimatedBuilder]
Repository -. no dependency .-> Finalizer
Repository -. no dependency .-> StaticBuilder
Repository -. no dependency .-> Cache[Renderer caches/queues]
```
## Ownership, threading and resources
- The repository owns only call-local path, native instance and result values.
- The loader owns path normalization and fallback selection;
`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
main thread. The module starts no tasks and touches no SceneTree objects.
- No file handle, native instance, Dictionary or engine Resource is retained.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty normalized path | String guard | Return `{}` | Dedicated verifier | Correct caller normalization |
| Native extension absent | `ClassDB.class_exists` | Return `{}` | Existing renderer fallback | Install/load extension and retry |
| Extracted file absent | `FileAccess.file_exists` | Return `{}` | Prototype cache records historical missing state through loader | Restore extraction and start a new loader session |
| Instance/method absent | Null/`has_method` guard | Return `{}` | Source contract verifier | Repair extension registration |
| Parse/non-Dictionary result | Result type check | Return `{}` | Existing loader fallback | Repair asset/parser and retry |
| Cancellation requested | Not supported inside synchronous native call | Call completes | None | Caller controls whether to start call |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Native class name | `M2Loader` | All | No | Exact GDExtension registration |
| Static method | `load_m2` | All | No | Existing static parser path |
| Animated method | `load_m2_animated` | Experimental animated path | No | Existing native animation parser |
| Extracted directory | Loader `extracted_dir` | Scene configuration | Yes, caller-owned | Root used for path join |
## Persistence, cache and migration
The module adds no persistence or cache. Native methods, normalized relative
paths and `.tscn/.glb`/material versions are unchanged, so no migration or rebake
is required.
## Diagnostics and observability
- Logs: none; loader retains existing fallback and debug output.
- Metrics: none; rejected-input timing is verifier-only.
- Debug views: none.
- Correlation ID: normalized relative M2 path in the caller.
## Verification
- `verify_m2_raw_model_repository.gd` covers empty/missing/repeated failures,
exact static/animated method ownership, three loader delegations, forbidden
dependencies and 10,000 empty-path rejects under one second.
- Adjacent finalizer/extractor/cache/pipeline/animation/material/shutdown tests
protect caller behavior and ownership.
- Fidelity evidence is structural: exact path join/globalization and native
method names are preserved. No proprietary M2 or original-client visual claim
is made by this extraction.
## Extension points
Future asset-backed verification may add a legal fixture around these value
contracts. Caching, asynchronous I/O and parser replacement require separate
measured work packages rather than expansion of this repository.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Static raw M2 read | Implemented extraction | Source/invalid/missing verifier | Legal success fixture pending |
| Native animated raw M2 read | Implemented extraction | Exact method/source contract | Legal animated fixture pending |
| Stateless failure behavior | Verified synthetic | Repeated missing call and source boundary | Asset-backed corrupt input pending |
## Known gaps and risks
- Native parsing remains synchronous and has no mid-call cancellation.
- The repository deliberately does not distinguish missing dependency, missing
file and parser failure; loader fallback behavior historically treats them alike.
- Proprietary asset traversal, descriptor pressure, p95/p99 and visual comparison
remain outside this structural extraction.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_raw_model_repository.gd` | Stateless native class/file/method boundary |
| `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 |
| `src/tools/verify_m2_raw_model_repository.gd` | Rejected-input/source/dependency/timing regression |
## Related decisions and references
- [`m2-runtime-mesh-finalizer.md`](m2-runtime-mesh-finalizer.md)
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
- [`world-renderer.md`](world-renderer.md)
- [`../../RENDER.md`](../../RENDER.md)
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)