render3d: ground_fill's candidate is a function of its own; solid layers with stable ids; the trample as clearing discs

ground_candidate answers (layer, chunk, band, cell) -> keep, x, z, scale, yaw, seed, wind from pure gf_*
steps and the density read between them; ground_fill draws its answers with the same operations in the
same order. A solid layer fills at step0 and band 0 whatever the camera, a far band drawing a stable
subset (draw 7 under (step0/step_b)^2), each thing an int id from (layer, chunk, cell), listed per chunk
into a caller's GroundSolids or answered by id. r3d_ground_clearing hands the trample over as discs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-30 15:23:53 +03:00
parent 740ac2a879
commit e535879262
7 changed files with 259 additions and 19 deletions

View file

@ -0,0 +1,68 @@
# ground_cand.ludic — one candidate of a ground layer: (layer, chunk, band, cell) to where it stands and what it
# draws with. The gf_* steps are pure (numbers in, a number out); ground_candidate reads the densities between
# them, and nothing but the densities' tile cache changes. The studio's groundFill.ts is these steps.
# a candidate, filled by the caller's own record (made once, reused)
export property GroundCand {
x: float = 0.0,
z: float = 0.0,
scale: float = 1.0,
yaw: float = 0.0,
seed: float = 0.0,
wind: float = 0.0,
cell: int = 0, # i * 65536 + j: the hashes' fifth input
id: int = -1 # a solid layer's stable id (ground_solid.ludic); -1 for cover
}
export function gf_cell(i: int, j: int) -> int { return i * 65536 + j }
# a cell's corner o, moved by the jitter's draw h (k 0 for x, 1 for z)
export function gf_along(o: float, i: int, h: float, jitter: float, step: float) -> float { return o + (float(i) + 0.5 + (h - 0.5) * jitter) * step }
# kept when draw 2 falls under the chance (the density, times the trample)
export function gf_keeps(g: GroundFill, cx: int, cz: int, band: int, e: int, keep: float) -> bool { return not (gf_hash(g.layer, cx, cz, band, e, 2) >= keep) }
# its size from the density's G at its place, and its own draw 6
export function gf_scale(g: GroundFill, cx: int, cz: int, band: int, e: int, gs: float) -> float {
var sc = Math.lerp(g.scale_lo, g.scale_hi, gs)
sc = sc * (1.0 + (gf_hash(g.layer, cx, cz, band, e, 6) * 2.0 - 1.0) * g.scale_var) * (1.0 + g.band_grow * float(band))
return sc
}
export function gf_yaw(g: GroundFill, cx: int, cz: int, band: int, e: int) -> float { return gf_hash(g.layer, cx, cz, band, e, 3) * 2.0 * PI }
export function gf_seed(g: GroundFill, cx: int, cz: int, band: int, e: int) -> float { return gf_hash(g.layer, cx, cz, band, e, 4) }
export function gf_wind(g: GroundFill, cx: int, cz: int, band: int, e: int) -> float { return Math.lerp(g.wind_lo, g.wind_hi, gf_hash(g.layer, cx, cz, band, e, 5)) }
# the chance at (x, z) after the trample: the game's callback, then the clearings it handed over as data
function gf_trampled(render3d_st: mut Render3dState, keep: float, x: float, z: float) -> float {
var k = keep
if render3d_st.gd_trample_cb != null { k = k * render3d_st.gd_trample_cb(x, z) }
if render3d_st.gd_clear_n > 0 { k = k * ground_clear_at(render3d_st, x, z) }
return k
}
# cell (i, j) of chunk (cx, cz) at `band` - the chunk's corner (x0, z0), its side `size` - into `out`: true when
# one stands there
export function ground_candidate(render3d_st: mut Render3dState, g: GroundFill, cx: int, cz: int, band: int, i: int, j: int, x0: float, z0: float, size: float, out: GroundCand) -> bool {
let step = gf_step(g, band)
if step <= 0.0 { return false }
let e = gf_cell(i, j)
let px = gf_along(x0, i, gf_hash(g.layer, cx, cz, band, e, 0), g.jitter, step)
let pz = gf_along(z0, j, gf_hash(g.layer, cx, cz, band, e, 1), g.jitter, step)
if px < x0 or px >= x0 + size or pz < z0 or pz >= z0 + size { return false }
var keep = ground_density_at(render3d_st, g.layer, px, pz, 0)
if keep <= 0.0 { return false }
if g.trample { keep = gf_trampled(render3d_st, keep, px, pz) }
if not gf_keeps(g, cx, cz, band, e, keep) { return false }
out.x = px
out.z = pz
out.cell = e
out.id = -1
out.scale = gf_scale(g, cx, cz, band, e, ground_density_at(render3d_st, g.layer, px, pz, 1))
out.yaw = gf_yaw(g, cx, cz, band, e)
out.seed = gf_seed(g, cx, cz, band, e)
out.wind = gf_wind(g, cx, cz, band, e)
return true
}