Both constants were worked out by hand and a byte off each: the tiles' bake wrote "LAAK" and "LDT2", which ludic bake --check refuses, and r3d_baked_read, holding the same wrong constant, would have refused ludic.base's correct files (the sun's shadow among them) while accepting its own. Now 0x4B41424C and 0x3254544C, checked against the strings; the unused LTT1 constant is gone. A re-bake is needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
4.1 KiB
Text
72 lines
4.1 KiB
Text
# terrain_tiles_cut.ludic — the whole height field and photograph written out tile by tile, once a map is
|
|
# made, and let go. The file is written fresh every time a map is made and never read by another
|
|
# start, so no older generator's tiles are ever read; its header goes in last, so a start that died
|
|
# writing it leaves a file whose magic is not there rather than one that looks whole.
|
|
|
|
const TT_VERSION: int = 1
|
|
const TT_HEAD: int = 64 # bytes: 16 words
|
|
|
|
@alloc_ok("a map being made: its tiles' file and their pools, once, not a frame")
|
|
function terrain_tiles_cut(render3d_st: mut Render3dState) -> void {
|
|
if render3d_st.tt_baked or render3d_st.tt_dir == "" or render3d_st.ter_heights == null { return }
|
|
let t0 = gl_now_us()
|
|
tt_close(render3d_st)
|
|
let path = `{render3d_st.tt_dir}/{render3d_st.tt_key}.tiles`
|
|
Fs.mkdir(render3d_st.tt_dir)
|
|
let f = file_open(path, "wb")
|
|
if f == null { tt_say_no_file(path); return }
|
|
# the check compares the tiles with the whole copy, so it runs on the quantized copy the write leaves
|
|
let bytes = ltt2_write(render3d_st, f, 0)
|
|
file_close(f)
|
|
if not tt_open_ltt2(render3d_st, path, 0) { tt_say_no_file(path); return }
|
|
if r3d_env_has(render3d_st, "R3D_TT_CHECK") { tt_check(render3d_st) }
|
|
ltt2_gpu_coarse(render3d_st)
|
|
tt_say_cut(path, bytes, (gl_now_us() - t0) / 1000)
|
|
}
|
|
|
|
# the last map's file let go (a world swap, or the map made again)
|
|
function tt_close(render3d_st: mut Render3dState) -> void {
|
|
if render3d_st.tt_file != null { file_close(render3d_st.tt_file); render3d_st.tt_file = null }
|
|
}
|
|
|
|
function tt_say_no_file(path: string) -> void { print(`r3d: terrain: cannot write {path}; the whole height field stays in memory`) }
|
|
function tt_say_cut(path: string, bytes: int, ms: int) -> void { print(`r3d: terrain: tiles written to {path} ({bytes / 1048576} MB, {ms} ms); the whole copies kept until terrain_tiles_build_done`) }
|
|
|
|
# R3D_TT_CHECK: the tiles' answer against the whole copy's at 4000 points (must be exact), and the
|
|
# coarse level's (what terrain_height_near falls back to) - while both are still here
|
|
@alloc_ok("a developer check, once, under R3D_TT_CHECK")
|
|
function tt_check(render3d_st: mut Render3dState) -> void {
|
|
var worst = 0.0
|
|
var worst_c = 0.0
|
|
var seed = 12345
|
|
let half = float(render3d_st.TERRAIN_HALF) * 0.98
|
|
for k in 0 .. 4000 {
|
|
seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF
|
|
let x = render3d_st.ter_ox + (float(seed % 100000) / 50000.0 - 1.0) * half
|
|
seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF
|
|
let z = render3d_st.ter_oz + (float(seed % 100000) / 50000.0 - 1.0) * half
|
|
let whole = terrain_height(render3d_st, x, z)
|
|
let keep = render3d_st.ter_heights
|
|
render3d_st.ter_heights = null
|
|
let tiled = terrain_height(render3d_st, x, z)
|
|
let scale = float(TERRAIN_RES) / float(render3d_st.TERRAIN_HALF * 2)
|
|
let coarse = tt_coarse_at(render3d_st, (x - render3d_st.ter_ox + float(render3d_st.TERRAIN_HALF)) * scale, (z - render3d_st.ter_oz + float(render3d_st.TERRAIN_HALF)) * scale)
|
|
render3d_st.ter_heights = keep
|
|
worst = Math.max(worst, Math.abs(tiled - whole))
|
|
worst_c = Math.max(worst_c, Math.abs(coarse - whole))
|
|
}
|
|
print(`r3d: terrain: tiles vs the whole copy at 4000 points: worst {worst} m; the coarse level: worst {worst_c} m`)
|
|
}
|
|
|
|
# The world's build places trees, rocks and cover over the whole map, asking exact heights
|
|
# everywhere before any ring exists: through the tiles that read the file some 25 000 times. So the
|
|
# whole copies stay through the build, and the game lets them go here when it is done (the end of
|
|
# world_things, and after a swap's terrain_reload). Queries answer the same before and after.
|
|
export function terrain_tiles_build_done(render3d_st: mut Render3dState) -> void {
|
|
if render3d_st.tt_file == null { return }
|
|
if render3d_st.ter_heights != null { free(render3d_st.ter_heights); render3d_st.ter_heights = null }
|
|
if render3d_st.ter_ortho_px != null { free(render3d_st.ter_ortho_px); render3d_st.ter_ortho_px = null }
|
|
render3d_st.tt_frame_reads = 0
|
|
tt_say_done()
|
|
}
|
|
function tt_say_done() -> void { print("r3d: terrain: the world is built; the whole height and photograph copies let go, tiles from here") }
|