render3d: terrain_baked_at - a map's terrain and sun shadow taken from its bakes at start and on a swap

The game names the tiles' and the shadow's bake files (key, version) before the map is made; r3d_init's
terrain step and terrain_reload then open them with terrain_from_baked / terrain_shadow_from_bytes and
generate nothing, and fall back to the survey (saying so once) when a bake is missing or stale. Without
the call nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 17:42:16 +03:00
parent f02eab5c51
commit 06d828a296
5 changed files with 53 additions and 2 deletions

View file

@ -831,6 +831,11 @@ export state Render3dState {
ter_shadow_tex: int = 0 # height-field sun shadow: R32F, the lowest lit height
stream_capture_c: Chunk = null # the bake's capture chunk (stream_baked.ludic)
sb_rec: floats = floats(8) # one decoded baked record, reused (stream_baked.ludic)
tb_tiles: string = "" # the map's baked tiles and shadow (terrain_baked_boot.ludic)
tb_shadow: string = ""
tb_key: string = ""
tb_version: int = 0
tb_shadow_ok: bool = false # the shadow came from its bake this map
tt_dir: string = "" # terrain_tiles_to: where a map's tiles are written ("" keeps the whole copies)
tt_key: string = ""
tt_file: pointer = null # the tiles' file, open for reading (terrain_tiles.ludic)

View file

@ -31,6 +31,7 @@ import "terrain_tiles.ludic"
import "terrain_tiles_cut.ludic"
import "terrain_ltt2_write.ludic"
import "terrain_ltt2_read.ludic"
import "terrain_baked_boot.ludic"
import "terrain_pages.ludic"
import "terrain_pages_frame.ludic"
import "terrain_pages_fill.ludic"

View file

@ -143,7 +143,7 @@ function r3d_load_step(render3d_st: mut Render3dState, i: int) -> bool {
daylight_init(render3d_st)
} else if t >= 0 and t < TERRAIN_INIT_STEPS {
if render3d_st.r3d_plate { return true }
if t == 0 {
if t == 0 and not terrain_baked_try(render3d_st) {
if render3d_st.r3d_dem_path != null { terrain_use_dem(render3d_st, render3d_st.r3d_dem_path, render3d_st.r3d_dem_min, render3d_st.r3d_dem_max, render3d_st.r3d_dem_base, render3d_st.r3d_dem_ox, render3d_st.r3d_dem_oz) }
if render3d_st.r3d_ortho_path != null { terrain_use_ortho(render3d_st, render3d_st.r3d_ortho_path) }
}

View file

@ -413,7 +413,7 @@ const TERRAIN_INIT_STEPS: int = 4
function terrain_init_step(render3d_st: mut Render3dState, i: int) -> void {
if i == 0 { if not render3d_st.tt_baked { terrain_generate(render3d_st) }; return }
# a baked terrain whose shadow came from its own bake (terrain_shadow_from_bytes) keeps it
if i == 1 { if not (render3d_st.tt_baked and render3d_st.ter_shadow_tex != 0) { terrain_bake_shadow(render3d_st) }; return }
if i == 1 { if not (render3d_st.tt_baked and render3d_st.tb_shadow_ok) { terrain_bake_shadow(render3d_st) }; return }
if i == 2 { terrain_load_textures(render3d_st); return }
terrain_init_finish(render3d_st)
}
@ -850,6 +850,14 @@ function terrain_reload(render3d_st: mut Render3dState, dem: string, emin: float
terrain_unload(render3d_st)
render3d_st.TERRAIN_HALF = half
render3d_st.ter_ox = ox; render3d_st.ter_oz = oz
render3d_st.r3d_dem_ox = ox; render3d_st.r3d_dem_oz = oz
# a swap to a map that has its bake: the tiles and the shadow read, the bounds from the tiles
if terrain_baked_try(render3d_st) {
if not render3d_st.tb_shadow_ok { terrain_bake_shadow(render3d_st) }
cdlod_bounds(render3d_st)
gpu_check(render3d_st, "terrain reload")
return
}
if len(dem) > 0 { terrain_use_dem(render3d_st, dem, emin, emax, base, ox, oz) }
if len(ortho) > 0 { terrain_use_ortho(render3d_st, ortho) }
terrain_generate(render3d_st)

View file

@ -0,0 +1,37 @@
# terrain_baked_boot.ludic — the game names its map's baked terrain (the tiles and the sun's shadow) before the
# map is made; the renderer's own start (r3d_init's terrain step) and terrain_reload then take it from
# them, and make the map from its survey as before when a bake is missing or not this version.
# the map's bakes: the tiles' file and the shadow's, under the key and version they were baked as.
# "" for a path turns that one off. Before r3d_init at boot, and before terrain_reload on a swap
export function terrain_baked_at(render3d_st: mut Render3dState, tiles: string, shadow: string, key: string, version: int) -> void {
render3d_st.tb_tiles = intern(tiles)
render3d_st.tb_shadow = intern(shadow)
render3d_st.tb_key = intern(key)
render3d_st.tb_version = version
}
# the map from its bake when there is one: true (nothing to generate), else false and nothing changed
function terrain_baked_try(render3d_st: mut Render3dState) -> bool {
render3d_st.tt_baked = false
if len(render3d_st.tb_tiles) == 0 { return false }
if not terrain_from_baked(render3d_st, render3d_st.tb_tiles, render3d_st.tb_key, render3d_st.tb_version, render3d_st.TERRAIN_HALF, render3d_st.r3d_dem_ox, render3d_st.r3d_dem_oz) {
tb_say_missing(render3d_st.tb_tiles)
return false
}
terrain_baked_shadow(render3d_st)
return true
}
# the sun's height-field shadow from its bake, when there is one (else terrain_init bakes it from the coarse level)
@alloc_ok("a bake read once at load")
function terrain_baked_shadow(render3d_st: mut Render3dState) -> void {
render3d_st.tb_shadow_ok = false
if len(render3d_st.tb_shadow) == 0 { return }
let b = r3d_baked_read(render3d_st.tb_shadow, render3d_st.tb_key, render3d_st.tb_version)
if b != null { render3d_st.tb_shadow_ok = terrain_shadow_from_bytes(render3d_st, b) }
if not render3d_st.tb_shadow_ok { tb_say_missing(render3d_st.tb_shadow) }
if b != null { free(b) }
}
function tb_say_missing(path: string) -> void { print(`bake: {path} missing or stale - the terrain made at run time (ludic bake)`) }