render3d: terrain_height_file - terrain_height's exact bilinear that never pages: a resident tile is read in place, any other from tt_file into four tiles of scratch of its own (in the state's defaults, forgotten when the file closes), so no slot is taken and no recency bumped - a sweep at boot no longer changes which tiles the cache holds, and so what is drawn from it. With the whole copy it is terrain_height. tests/height_file_test: equal to terrain_height over a grid across many tiles, resident and not, at tile corners and past the edge; a whole-map sweep leaves slot_of, tile_in, ref, the heights, the hand and the read counts unchanged
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
12553edb26
commit
c2fc8f78f2
5 changed files with 187 additions and 0 deletions
52
packages/ludic.render3d/terrain_height_file.ludic
Normal file
52
packages/ludic.render3d/terrain_height_file.ludic
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# terrain_height_file.ludic — terrain_height's exact answer that never pages: a tile not resident is
|
||||
# read from the file into a scratch of this query's own, so who asks and in what order never changes
|
||||
# which tiles the cache holds (and so never moves what is drawn from it). For wide sweeps at boot.
|
||||
const TF_KEEP: int = 4 # tiles the scratch keeps: a texel square at a corner spans four
|
||||
|
||||
export function terrain_height_file(render3d_st: mut Render3dState, x: float, z: float) -> float {
|
||||
if not ter_present(render3d_st) { return 0.0 }
|
||||
let scale = float(TERRAIN_RES) / float(render3d_st.TERRAIN_HALF * 2)
|
||||
let fx = (x - render3d_st.ter_ox + float(render3d_st.TERRAIN_HALF)) * scale
|
||||
let fz = (z - render3d_st.ter_oz + float(render3d_st.TERRAIN_HALF)) * scale
|
||||
let ix = min(max(int(Math.floor(fx)), 0), TERRAIN_RES - 2)
|
||||
let iz = min(max(int(Math.floor(fz)), 0), TERRAIN_RES - 2)
|
||||
let tx = Math.clamp(fx - float(ix), 0.0, 1.0)
|
||||
let tz = Math.clamp(fz - float(iz), 0.0, 1.0)
|
||||
let h00 = tf_texel(render3d_st, ix, iz)
|
||||
let h10 = tf_texel(render3d_st, ix + 1, iz)
|
||||
let h01 = tf_texel(render3d_st, ix, iz + 1)
|
||||
let h11 = tf_texel(render3d_st, ix + 1, iz + 1)
|
||||
return Math.lerp(Math.lerp(h00, h10, tx), Math.lerp(h01, h11, tx), tz)
|
||||
}
|
||||
|
||||
# a height texel: the whole copy's, a resident tile's, else the scratch's (read from the file there)
|
||||
function tf_texel(render3d_st: mut Render3dState, tx: int, tz: int) -> float {
|
||||
if render3d_st.ter_heights != null { return render3d_st.ter_heights[tz * TERRAIN_RES + tx] }
|
||||
let t = (tz >> TT_SHIFT) * render3d_st.tt_n + (tx >> TT_SHIFT)
|
||||
let at = (tz & TT_MASK) * TT_TEX + (tx & TT_MASK)
|
||||
let s = render3d_st.tt_slot_of[t]
|
||||
if s >= 0 { return render3d_st.tt_h[s * TT_TEX * TT_TEX + at] }
|
||||
return render3d_st.tf_h[tf_entry(render3d_st, t) * TT_TEX * TT_TEX + at]
|
||||
}
|
||||
|
||||
# the scratch entry holding tile t, read from the file (as tt_decode reads its heights) when none does
|
||||
function tf_entry(render3d_st: mut Render3dState, t: int) -> int {
|
||||
for e in 0 .. TF_KEEP { if render3d_st.tf_tile[e] == t + 1 { return e } }
|
||||
let e = render3d_st.tf_next
|
||||
render3d_st.tf_next = (e + 1) % TF_KEEP
|
||||
let n2 = TT_TEX * TT_TEX
|
||||
let raw = render3d_st.tf_raw
|
||||
file_seek(render3d_st.tt_file, render3d_st.tt_hoff + t * n2 * 2, 0)
|
||||
file_read(render3d_st.tt_file, data_of(raw), n2 * 2)
|
||||
let lo = render3d_st.tt_tab[t * 2]
|
||||
let step = render3d_st.tt_tab[t * 2 + 1]
|
||||
for i in 0 .. n2 { render3d_st.tf_h[e * n2 + i] = lo + float(raw[i * 2] | (raw[i * 2 + 1] << 8)) * step }
|
||||
render3d_st.tf_tile[e] = t + 1
|
||||
return e
|
||||
}
|
||||
|
||||
# the scratch forgotten: the tiles' file closed or replaced (another map's tile t is another tile)
|
||||
function tf_forget(render3d_st: mut Render3dState) -> void {
|
||||
for e in 0 .. TF_KEEP { render3d_st.tf_tile[e] = 0 }
|
||||
render3d_st.tf_next = 0
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue