ludic/packages/ludic.render3d/fog_casters.ludic
Orkuncakilkaya 2ce2d997be render3d: the fog wall culls on the CPU - nothing past it costs a draw, a vertex or a caster
cam_sphere_visible refuses a sphere wholly past the wall (terrain patches, scatter cells, stream
chunks, grass tiles, water), actors past it are skipped for draws, casters and outlines, the grass
reach and the streams' generate-and-gather reach end at it, and the card shadows cast only from
the wall's share of a layer (fog_casters.ludic, rebuilt every 4 m). r3d_beyond_fog is exported for
the game to skip animating what will not be drawn. Render-only: no query of the ground or the world
changes, and off is the old frame (0 pixels over 8 against 160ce96, twice per side).

Draws per frame at the overlook: 2757 off, 1104 at 175 m, 484 at 30 m.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 15:38:00 +03:00

40 lines
1.7 KiB
Text

# fog_casters.ludic — under a fog wall, a layer's cards cast shadows only from where they can be seen.
# The impostor shadow list is every instance on the map; past the wall that is a vertex bill for
# nothing, so the casters inside the wall (and a shadow's throw past it) go into a list of their own.
const FOG_CAST_THROW: float = 30.0 # the longest shadow a tree throws back toward the camera
const FOG_CAST_STEP: float = 4.0 # the camera moves this far before the list is rebuilt
# the buffer and count the card shadows draw from: the whole list, or the wall's share of it
function fog_casters(render3d_st: mut Render3dState, l: Layer) -> int {
let w = render3d_st.r3d_fog_wall
if w <= 0.0 { return l.sh_buf }
let dx = render3d_st.cam_pos[0] - l.fog_x
let dz = render3d_st.cam_pos[2] - l.fog_z
if l.fog_wall == w and l.fog_src == l.n_sh and dx * dx + dz * dz < FOG_CAST_STEP * FOG_CAST_STEP { return l.fog_buf }
l.fog_wall = w; l.fog_src = l.n_sh
l.fog_x = render3d_st.cam_pos[0]; l.fog_z = render3d_st.cam_pos[2]
let far = w + R3D_FOG_MARGIN + FOG_CAST_THROW + FOG_CAST_STEP
let far2 = far * far
let src = l.inst
let dst = l.scratch
var n = 0
let total = Math.min(l.n_sh, l.count)
for i in 0 .. total {
let o = i * INST_FLOATS
let ex = src[o] - l.fog_x
let ez = src[o + 2] - l.fog_z
if ex * ex + ez * ez > far2 { continue }
let q = n * INST_FLOATS
for k in 0 .. INST_FLOATS { dst[q + k] = src[o + k] }
n += 1
}
l.fog_n = n
if n > 0 { gpu_buffer_upload(render3d_st, l.fog_buf, n * INST_FLOATS * 4, data_of(dst), GPU_DYNAMIC) }
return l.fog_buf
}
function fog_casters_n(render3d_st: Render3dState, l: Layer) -> int {
if render3d_st.r3d_fog_wall <= 0.0 { return l.n_sh }
return l.fog_n
}