feat(render3d): replace the world at run time, water bodies, terrain_sea

terrain_reload/terrain_unload release one map's height field, survey, photograph
and patch bounds and generate another at any TERRAIN_HALF; scatter_clear_all
(with streams), actor_clear_all, col_reset and mesh_free empty the scene;
water_body_add/water_bodies_clear draw several still-water planes with one
reflecting; terrain_sea separates the coast's sea level from the carved lake's,
and grass keeps off both. Fixes CDLOD patch bounds for TERRAIN_HALF != 4096 and
water_init leaking a mesh and program per call. examples/rendering/reload.ludic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-13 00:03:40 +03:00
parent 70bda0c4df
commit a4a3d75cd9
13 changed files with 308 additions and 19 deletions

View file

@ -1048,3 +1048,40 @@ function carpet_bake(layers: []Layer, count: int, tile: int, res: int) -> int {
gl_check("carpet bake")
return tex
}
# ---- another map at run time ------------------------------------------------------------
# Every scattered layer released - its instance arrays and GL buffers, a card layer's own
# crossed-card mesh and baked atlas - and every stream feeding them. The models a layer drew
# belong to whoever loaded them and are kept, and so are the programs. A game rebuilding its
# world calls this, then places the new map's layers exactly as it did at boot.
function scatter_clear_all() -> void {
stream_clear_all()
if sc_layers == null { return }
let ids = gl_scratch()
for i in 0 .. len(sc_layers) {
let l = sc_layers[i]
if l.buf != 0 { ids[0] = l.buf; gl_delete_buffers(1, ids) }
if l.imp_buf != 0 { ids[0] = l.imp_buf; gl_delete_buffers(1, ids) }
if l.sh_buf != 0 { ids[0] = l.sh_buf; gl_delete_buffers(1, ids) }
if l.lod_buf != null { for k in 0 .. l.n_lods { ids[0] = l.lod_buf[k]; gl_delete_buffers(1, ids) } }
if l.inst != null { free(l.inst) }
if l.scratch != null { free(l.scratch) }
if l.tint != null { free(l.tint) }
if l.last_cam != null { free(l.last_cam) }
if l.gstart != null { free(l.gstart) }
if l.gsorted != null { free(l.gsorted) }
if l.gymin != null { free(l.gymin) }
if l.gymax != null { free(l.gymax) }
if l.vis != null { free(l.vis) }
if l.lod_dist != null { free(l.lod_dist); free(l.lod_card); free(l.lod_buf); free(l.n_lod) }
if l.lvl != null { free(l.lvl) }
# layer_cards built this layer's crossed card and its atlas itself
if l.card and l.n_lods == 0 and l.model != null { for k in 0 .. len(l.model.prims) { mesh_free(l.model.prims[k].mesh) } }
if l.card and l.atlas != null {
ids[0] = l.atlas.albedo; gl_delete_textures(1, ids)
ids[0] = l.atlas.normal; gl_delete_textures(1, ids)
}
}
sc_layers = new []Layer
sc_view_gen += 1
}