180 lines
7.8 KiB
Markdown
180 lines
7.8 KiB
Markdown
# Terrain Chunk LOD Planner
|
|
|
|
## Metadata
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Status | Implemented extraction |
|
|
| Target/work package | M03 / `M03-RND-TERRAIN-LOD-PLANNER-001` |
|
|
| Owners | Pure desired chunk-LOD calculation only |
|
|
| Last verified | Worktree `work/sindo-main-codex/m03-terrain-chunk-lod-planner`, 2026-07-16 |
|
|
| Profiles/capabilities | Existing Performance/Balanced/High/Custom radii |
|
|
|
|
## Purpose
|
|
|
|
Calculate the desired terrain LOD for each populated parsed ADT chunk from one
|
|
typed Godot-space focus and one immutable renderer-policy snapshot.
|
|
|
|
## Non-goals
|
|
|
|
- Own tile/chunk state, queues, budgets, tasks, Mesh, Node, material or RID.
|
|
- Select tile LOD, create/remove geometry or apply a completed plan.
|
|
- Normalize custom radii, change profile defaults or claim visual parity.
|
|
- Parse ADT data or convert between WoW and Godot coordinate spaces.
|
|
|
|
## Context and boundaries
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Focus[GodotWorldPosition] --> Planner[TerrainChunkLodPlanner]
|
|
Chunks[Parsed ADT chunk records] --> Planner
|
|
Config[Streamer LOD configuration] --> Policy[TerrainChunkLodPolicy]
|
|
Policy --> Planner
|
|
Planner --> Desired[chunk index to LOD 0/1/2]
|
|
Desired --> Loader[StreamingWorldLoader state adapter]
|
|
Loader --> Queue[Existing create/remove queues and budgets]
|
|
```
|
|
|
|
Allowed dependencies are the typed Godot position, immutable policy and
|
|
call-local Array/Dictionary/Vector3 values. SceneTree, RenderingServer, gameplay,
|
|
network, files, tasks and persistent caches are forbidden.
|
|
|
|
## Public API
|
|
|
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
|
|---|---|---|---|---|
|
|
| `TerrainChunkLodPolicy.new(...)` | Immutable value | Snapshot enabled flag, three raw radii and chunk size | Caller-owned; one planning refresh | Values are preserved without normalization |
|
|
| `TerrainChunkLodPlanner.plan(chunks, focus_position, policy)` | Pure query | Map populated source indices to LOD 0/1/2 | Synchronous; result caller-owned | Invalid/null typed inputs or disabled policy return empty |
|
|
|
|
The planner validates scripts by preloaded identity so it works from a cold
|
|
Godot global-class cache.
|
|
|
|
## Inputs and outputs
|
|
|
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
|
|---|---|---|---|---|---|
|
|
| Input | Parsed chunk array with `origin: Vector3` | ADT load result | Planner | Loader-owned; read only | One call |
|
|
| Input | `GodotWorldPosition` | Streamer focus adapter | Planner | Immutable caller reference | One call |
|
|
| Input | `TerrainChunkLodPolicy` | Streamer exported configuration | Planner | Immutable caller reference | One call |
|
|
| Output | `{source_chunk_index: 0/1/2}` | Planner | Loader desired state | Fresh caller dictionary | One call |
|
|
|
|
The Y coordinate is intentionally ignored. Empty chunk records and chunks beyond
|
|
the third radius are absent from the result.
|
|
|
|
## Data flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Start[plan] --> Valid{Typed inputs and enabled?}
|
|
Valid -->|no| Empty[Return empty dictionary]
|
|
Valid -->|yes| Radii[Square raw radius times chunk size]
|
|
Radii --> Iterate[Visit source chunk indices in order]
|
|
Iterate --> Populated{Record populated?}
|
|
Populated -->|no| Iterate
|
|
Populated -->|yes| Center[origin XZ plus half chunk]
|
|
Center --> Distance[Squared horizontal distance]
|
|
Distance --> Threshold{First inclusive threshold}
|
|
Threshold -->|LOD0/1/2| Record[Record source index]
|
|
Threshold -->|outside| Iterate
|
|
Record --> Iterate
|
|
Iterate --> Done[Return desired mapping]
|
|
```
|
|
|
|
## Lifecycle/state
|
|
|
|
The planner is stateless. A policy is constructed for one refresh, read during
|
|
synchronous planning and released. The returned dictionary is then owned by the
|
|
loader state. There is no cancellation, recovery state or retained resource.
|
|
|
|
## Main sequence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Stream as StreamingWorldLoader
|
|
participant Policy as TerrainChunkLodPolicy
|
|
participant Planner as TerrainChunkLodPlanner
|
|
Stream->>Policy: snapshot enabled/radii/chunk size
|
|
Stream->>Planner: plan(chunks, typed focus, policy)
|
|
Planner-->>Stream: fresh desired LOD dictionary
|
|
Stream->>Stream: existing tile state, queue and budget application
|
|
```
|
|
|
|
## Ownership, threading and resources
|
|
|
|
- Planner and policy own no engine resources, nodes, RIDs, tasks or caches.
|
|
- The loader retains parsed chunks, desired state and every application side effect.
|
|
- Current calls run synchronously on the renderer main thread; the pure inputs
|
|
also make isolated contract testing possible without a world scene.
|
|
|
|
## Errors, cancellation and recovery
|
|
|
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
|
|---|---|---|---|---|
|
|
| Null/wrong focus or policy script | Identity guard | Empty plan | Contract verifier | Supply typed values next refresh |
|
|
| Disabled chunk LOD | Policy flag | Empty plan; existing tile-LOD fallback applies | Existing renderer state | Enable profile option |
|
|
| Empty chunk record | Record guard | Preserve sparse source index; omit record | None | Not an error |
|
|
| Custom negative radius | No normalization | Existing squared-radius behavior is retained | Regression fixture | Change caller configuration if undesired |
|
|
|
|
There is no asynchronous work or cancellation token.
|
|
|
|
## Configuration and capabilities
|
|
|
|
The policy snapshots `use_chunk_terrain_lods`, `lod0_radius_chunks`,
|
|
`lod1_radius_chunks`, `lod2_radius_chunks` and the existing `CHUNK_SIZE`. Inclusive
|
|
squared thresholds and raw custom values deliberately preserve prior behavior.
|
|
|
|
## Persistence, cache and migration
|
|
|
|
No serialization or cache format changes. Existing baked terrain, control-splat
|
|
and splat cache versions remain unchanged and require no rebuild.
|
|
|
|
## Diagnostics and observability
|
|
|
|
The service emits no logs. The contract verifier reports semantic cases and a
|
|
bounded synthetic timing. Asset-backed p95/p99 remains milestone acceptance work.
|
|
|
|
## Verification
|
|
|
|
- `verify_terrain_chunk_lod_planner.gd`: center and vertical invariance,
|
|
inclusive thresholds, sparse/outside chunks, disabled/custom-negative policy,
|
|
loader delegation and 250-by-256 bounded planning.
|
|
- Renderer cache, facade, internal-access, baseline, scheduler, target-planner,
|
|
focus, ground-query and coordinate-boundary regressions remain required.
|
|
- Fidelity evidence is formula preservation only; no new build-12340 visual
|
|
equivalence claim is made.
|
|
|
|
## Extension points
|
|
|
|
- Add measured direction/velocity only through an explicit policy/contract change.
|
|
- Keep queue prioritization and LOD application as separately bounded services.
|
|
|
|
## Capability status
|
|
|
|
| Capability | Status | Evidence | Gap/next step |
|
|
|---|---|---|---|
|
|
| Pure chunk desired-LOD calculation | Implemented extraction | Asset-free semantic/source-boundary contract | Asset-backed p95/p99 pending |
|
|
| Queue/state/finalization ownership | Remains in loader | Source-boundary regression | Further terrain decomposition required |
|
|
|
|
## Known gaps and risks
|
|
|
|
- Raw negative and non-monotonic custom radii retain historical squared/ordered
|
|
semantics, even though such settings may be surprising.
|
|
- The planner is linear in parsed chunk count and allocates one result dictionary.
|
|
- Terrain build, quality upgrades and geometry lifecycle remain monolithic.
|
|
|
|
## Source map
|
|
|
|
| Path | Responsibility |
|
|
|---|---|
|
|
| `src/render/terrain/terrain_chunk_lod_policy.gd` | Immutable configuration snapshot |
|
|
| `src/render/terrain/terrain_chunk_lod_planner.gd` | Pure horizontal-distance plan |
|
|
| `src/scenes/streaming/streaming_world_loader.gd` | Composition and plan application adapter |
|
|
| `src/tools/verify_terrain_chunk_lod_planner.gd` | Contract, dependency and timing regression |
|
|
|
|
## Related decisions and references
|
|
|
|
- [`world-renderer.md`](world-renderer.md)
|
|
- [`terrain-quality-mesh-cache.md`](terrain-quality-mesh-cache.md)
|
|
- [`../../RENDER.md`](../../RENDER.md)
|
|
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
|