Skip to content

ENG2-P8-11: Terrain LOD + texture/mesh streaming #809

Description

@aram-devdocs

Parent

  • Program: ENG2 — GoudEngine v2 Rebuild (see the pinned master tracking issue)
  • Phase / Milestone: Phase 8 — Capability Gaps (eng2-p8-capabilities)
  • Batch / Group: Batch 8.3 — Big-world, Group B (sequential after ENG2-P8-10)
  • Runbook spec: docs/src/runbook/phases/phase-8.md (committed with the roadmap)

Summary

Add distance-based mesh LOD for chunked heightmap terrain (ENG2-P8-10) plus asynchronous texture/mesh streaming so distant chunks load progressively without blocking the frame, using the Phase 7 async asset pipeline.

Architecture Context

Layer: Layer 4 (Engine, libs/graphics/renderer3d/terrain.rs) for LOD mesh generation; Layer 3 (Services, assets/) integration for streaming, reusing the async asset pipeline wired up in ENG2-P7-01.
Modules/types touched:

  • goud_engine/src/libs/graphics/renderer3d/terrain.rs (extends ENG2-P8-10) — multiple mesh resolutions per chunk, selected by camera distance each frame (or on chunk load).
  • goud_engine/src/assets/server/ — terrain chunk mesh/texture loads routed through load_async/process_loads (the pipeline ENG2-P7-01 wires into the runtime), not synchronous main-thread loads.
  • goud_engine/src/ffi/renderer3d/terrain.rs — new exports for LOD distance configuration and streaming status query.

Boundary constraints (only those that apply):

  • Raw GPU calls stay in libs/graphics/backend/.
  • FFI exports: #[no_mangle] extern "C", #[repr(C)], null checks, // SAFETY: comments.

Pattern to follow: ENG2-P7-01's runtime wiring of AssetServer::load_async/process_loads — terrain chunk streaming must be a consumer of that general-purpose async pipeline, not a bespoke terrain-only background-thread mechanism.

Scope

  • Distance-based mesh LOD: generate (or downsample) 2-3 resolution tiers per terrain chunk; select tier per chunk based on camera distance each frame or on a coarser cadence.
  • LOD transition handling: avoid popping artifacts at tier boundaries (documented approach — e.g. hysteresis band or geomorphing; a simple hysteresis band is an acceptable first cut).
  • Streaming integration: route terrain chunk mesh/texture generation and upload through the async asset pipeline (ENG2-P7-01) so loading distant chunks does not block the main thread.
  • FFI exports: configure LOD distance thresholds, query per-chunk streaming/load state.
  • Tests: spec test + unit tests for LOD tier selection (correct tier chosen at known distances) and a streaming integration test (chunk transitions from not-loaded -> loading -> loaded without blocking the calling thread).
  • Docs/rules updates: mdBook terrain page gains an LOD/streaming tuning section.

Acceptance Criteria

  • Terrain chunks at increasing camera distance select progressively lower mesh resolution tiers, verified against configured thresholds.
  • Chunk streaming loads occur off the main thread (via the ENG2-P7-01 async pipeline) with no measurable frame-time spike attributable to a terrain chunk load, per a scripted large-terrain scene.
  • FFI LOD/streaming exports have wrappers in all 10 SDKs and pass python codegen/validate_coverage.py.
  • cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings clean; cargo test green; ./codegen.sh && git diff --exit-code (drift gate)

Breaking Change & Throne Follow-up

None — additive/internal (extends the ENG2-P8-10 terrain component; no existing exports change).

Blocked By

ENG2-P8-10 (chunked heightmap terrain component — this issue adds LOD/streaming on top of that chunk representation and cannot exist independently of it), ENG2-P7-01 (wire async asset pipeline into the runtime; background decode — terrain streaming is a consumer of that pipeline, not a second background-loading mechanism).

Files Likely Touched

  • Modified: goud_engine/src/libs/graphics/renderer3d/terrain.rs, goud_engine/src/ffi/renderer3d/terrain.rs (both from ENG2-P8-10)
  • Modified: goud_engine/src/assets/server/ (terrain-specific asset type/loader registration, if needed)
  • Generated: SDK bindings under sdks/*/ for the new LOD/streaming FFI exports
  • Modified: codegen/goud_sdk.schema.json

Agent Notes

  • No mesh or texture streaming exists anywhere today: grep for mesh.?stream|texture.?stream|async.*load.*mesh (case-insensitive) across goud_engine/src returns zero matches. The only "async load" infrastructure in the engine is the general asset pipeline (assets/server/core.rs:86's comment: "Sender for background load results (used by native load_async)"; load_async/process_loads are implemented and unit-tested in assets/server/async_tests.rs), but per the roadmap's W5 finding, that pipeline currently has zero non-test callers in the runtime — it is real, tested code that nothing actually invokes yet. This is exactly why ENG2-P7-01 (wiring it into the runtime) is a hard blocker here: building terrain streaming against a pipeline that isn't wired into the actual frame loop would mean re-plumbing it twice.
  • Do not conflate this issue's LOD with the codebase's existing LOD terminology: every current LOD reference (libs/graphics/renderer3d/config.rs:80,195,199; core_model_animation/mod.rs:134,195) is animation-evaluation LOD (skipping/half-rating skeletal animation updates at distance) — a completely different mechanism operating on a completely different data path (bone matrices, not mesh geometry). This issue introduces the engine's first genuine geometric/mesh LOD system; do not attempt to reuse the animation LOD's distance-bucket code, since it is not built for mesh-resolution switching.
  • LOD transition popping is a known visual artifact class for heightmap terrain specifically (unlike the animation LOD, which is imperceptible by design) — the Scope explicitly requires a documented mitigation approach rather than leaving raw tier-snapping as the only option.

Verification

cargo check && cargo fmt --all -- --check && cargo clippy -- -D warnings
cargo test
./codegen.sh && git diff --exit-code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions