render3d: place_rec.ludic, the one encoder and decoder of a baked placement

prec_put / prec_get / prec_size (Physics' spec): PREC_FULL, 12 bytes, for trees, boulders and stones
(u16 x, z in 1/65536 of the chunk; u16 y in cm above y_base; u8 scale over the kind's lo..hi, yaw, seed,
wind), and PREC_PACKED, 7 bytes, for the stream kinds (56 bits, least significant first: x 12, z 12,
y 12 in cm, scale 6, yaw 6, seed 4, wind 4). An encode takes the cell a value falls in, a decode its
centre, so a round trip is stable; a record is read from a []byte at an offset, never a pointer.
stream_emit_baked and layer_chunk_put take (recs, at, fmt) and decode through it; the earlier sb7_*
pair is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-29 17:28:11 +03:00
parent 3f0bb07c16
commit 8589a5a7b4
6 changed files with 86 additions and 58 deletions

View file

@ -1,9 +1,6 @@
# layer_chunks.ludic — a fixed layer (trees, boulders, stones) held as the baked chunks that are in, not
# the whole map: a slab per chunk in the layer's instance list, put when the ring brings the chunk in
# and dropped when it goes, the list re-uploaded at most once a frame. A record is 12 bytes: u16 x, u16 z
# (1/65536 of the chunk), u16 y (cm above y_base), u8 scale (the kind's lo..hi), u8 yaw, u8 seed, u8 wind.
const LC_REC: int = 12
# and dropped when it goes, the list re-uploaded at most once a frame. A record's layout is place_rec.ludic's.
# the layer made ready for chunks of an n x n grid: its slab index, once at map load, and nothing in it
@alloc_ok("a map being loaded: a layer's chunk index, once")
@ -12,30 +9,23 @@ export function layer_chunks_begin(l: Layer, n: int) -> void {
if l.ck_off != null { free(l.ck_off); free(l.ck_cnt) }
l.ck_off = words(n * n); l.ck_cnt = words(n * n)
}
if l.lc_rec == null { l.lc_rec = floats(INST_FLOATS) }
for i in 0 .. n * n { l.ck_off[i] = -1; l.ck_cnt[i] = 0 }
l.count = 0
layer_chunks_changed(l)
}
# chunk `id`'s records put into the layer (replacing what it had); as many as the layer has room for
export function layer_chunk_put(l: Layer, id: int, recs: pointer, count: int, size: float, x0: float, z0: float, y_base: float, lo: float, hi: float) -> void {
# chunk `id`'s `count` records (format fmt, place_rec.ludic) at byte `at` of recs put into the layer,
# replacing what it had; as many as the layer has room for
export function layer_chunk_put(l: Layer, id: int, recs: []byte, at: int, fmt: int, count: int, size: float, x0: float, z0: float, y_base: float, lo: float, hi: float) -> void {
if l.ck_off == null or id < 0 or id >= len(l.ck_off) { return }
if l.ck_off[id] >= 0 { layer_chunk_drop(l, id) }
let n = min(count, l.cap - l.count)
if n <= 0 { return }
layer_room(l, l.count + n)
let span = size / 65536.0
let sr = (hi - lo) / 255.0
for i in 0 .. n {
let r = i * LC_REC
let o = (l.count + i) * INST_FLOATS
let yaw = float(recs[r + 7]) * (2.0 * PI / 256.0)
l.inst[o] = x0 + float(recs[r] | (recs[r + 1] << 8)) * span
l.inst[o + 1] = y_base + float(recs[r + 4] | (recs[r + 5] << 8)) * 0.01
l.inst[o + 2] = z0 + float(recs[r + 2] | (recs[r + 3] << 8)) * span
l.inst[o + 3] = lo + float(recs[r + 6]) * sr
l.inst[o + 4] = Math.sin(yaw); l.inst[o + 5] = Math.cos(yaw)
l.inst[o + 6] = float(recs[r + 8]) / 255.0; l.inst[o + 7] = float(recs[r + 9]) / 255.0
prec_get(fmt, recs, at, i, x0, z0, size, y_base, lo, hi, l.lc_rec)
for k in 0 .. INST_FLOATS { l.inst[(l.count + i) * INST_FLOATS + k] = l.lc_rec[k] }
}
l.ck_off[id] = l.count
l.ck_cnt[id] = n