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
Acceptance Criteria
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
Parent
eng2-p8-capabilities)ENG2-P8-10)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 inENG2-P7-01.Modules/types touched:
goud_engine/src/libs/graphics/renderer3d/terrain.rs(extendsENG2-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 throughload_async/process_loads(the pipelineENG2-P7-01wires 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):
libs/graphics/backend/.#[no_mangle] extern "C",#[repr(C)], null checks,// SAFETY:comments.Pattern to follow:
ENG2-P7-01's runtime wiring ofAssetServer::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
ENG2-P7-01) so loading distant chunks does not block the main thread.Acceptance Criteria
ENG2-P7-01async pipeline) with no measurable frame-time spike attributable to a terrain chunk load, per a scripted large-terrain scene.python codegen/validate_coverage.py.cargo check && cargo fmt --all -- --check && cargo clippy -- -D warningsclean;cargo testgreen;./codegen.sh && git diff --exit-code(drift gate)Breaking Change & Throne Follow-up
None — additive/internal (extends the
ENG2-P8-10terrain 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
goud_engine/src/libs/graphics/renderer3d/terrain.rs,goud_engine/src/ffi/renderer3d/terrain.rs(both fromENG2-P8-10)goud_engine/src/assets/server/(terrain-specific asset type/loader registration, if needed)sdks/*/for the new LOD/streaming FFI exportscodegen/goud_sdk.schema.jsonAgent Notes
mesh.?stream|texture.?stream|async.*load.*mesh(case-insensitive) acrossgoud_engine/srcreturns 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_loadsare implemented and unit-tested inassets/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 whyENG2-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.LODterminology: 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.Verification