feat(render3d): replace the world at run time, water bodies, terrain_sea
terrain_reload/terrain_unload release one map's height field, survey, photograph and patch bounds and generate another at any TERRAIN_HALF; scatter_clear_all (with streams), actor_clear_all, col_reset and mesh_free empty the scene; water_body_add/water_bodies_clear draw several still-water planes with one reflecting; terrain_sea separates the coast's sea level from the carved lake's, and grass keeps off both. Fixes CDLOD patch bounds for TERRAIN_HALF != 4096 and water_init leaking a mesh and program per call. examples/rendering/reload.ludic. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
70bda0c4df
commit
a4a3d75cd9
13 changed files with 308 additions and 19 deletions
|
|
@ -68,6 +68,15 @@ var ter_lake_ez: int = 0
|
|||
function terrain_lake(level: int, cx: int, cz: int, ex: int, ez: int) -> void {
|
||||
ter_lake_level = level; ter_lake_cx = cx; ter_lake_cz = cz; ter_lake_ex = ex; ter_lake_ez = ez
|
||||
}
|
||||
# The SEA's level, for the coast, the strand and the shoreline shading, when it is not the
|
||||
# carved lake's. They were one number, which holds only while the lake is at sea level: a
|
||||
# lake eighty metres up the valley drowned the whole coast toward its own surface. Unset,
|
||||
# the sea is the lake exactly as before, so a map with one water line needs no call.
|
||||
var ter_sea_level: int = 0
|
||||
var ter_sea_set: bool = false
|
||||
function terrain_sea(level: int) -> void { ter_sea_level = level; ter_sea_set = true }
|
||||
# the level the generator scales the coast toward (it always read the lake's, set or not)
|
||||
function ter_sea_gen() -> int { if ter_sea_set { return ter_sea_level }; return ter_lake_level }
|
||||
# An island: land out to `r` from (cx, cz), then the terrain scaled down into the water
|
||||
# over `fall` metres and on down to a shelf. Scaling rather than blending to a fixed bed is
|
||||
# what makes the coastline come out of the terrain that is already there — low ground turns
|
||||
|
|
@ -229,6 +238,7 @@ function terrain_generate() -> void {
|
|||
u_f2(gl_uniform(p, "u_origin"), ter_ox, ter_oz)
|
||||
u_f4(gl_uniform(p, "u_lake"), ter_lake_cx, ter_lake_cz, ter_lake_ex, ter_lake_ez)
|
||||
u_f(gl_uniform(p, "u_lake_level"), ter_lake_level)
|
||||
u_f(gl_uniform(p, "u_sea_level"), ter_sea_gen())
|
||||
u_f4(gl_uniform(p, "u_isle"), ter_isle_cx, ter_isle_cz, ter_isle_r, ter_isle_fall)
|
||||
u_f(gl_uniform(p, "u_isle_mode"), fi(ter_isle_mode))
|
||||
u_f(gl_uniform(p, "u_dem_blur"), ter_dem_blur)
|
||||
|
|
@ -453,6 +463,10 @@ function terrain_bind_prog(p: int) -> void {
|
|||
var lake = fl(-100000.0)
|
||||
if ter_lake_ex != 0 { lake = ter_lake_level }
|
||||
u_f(gl_uniform(p, "u_lake_level"), lake)
|
||||
# the shoreline, forest and scree gates read the sea; unset it is what they always read
|
||||
var sea = lake
|
||||
if ter_sea_set { sea = ter_sea_level }
|
||||
u_f(gl_uniform(p, "u_sea_level"), sea)
|
||||
u_mat4(gl_uniform(p, "u_view"), cam_view)
|
||||
u_mat4(gl_uniform(p, "u_proj"), cam_proj)
|
||||
u_f2(gl_uniform(p, "u_origin"), ter_ox, ter_oz)
|
||||
|
|
@ -660,6 +674,12 @@ function cdlod_bounds() -> void {
|
|||
}
|
||||
}
|
||||
|
||||
# A patch's side in metres at a level. The quadtree is CD_LEAVES leaves a side over the whole
|
||||
# map, so a leaf is (2 * TERRAIN_HALF) / CD_LEAVES - 32 m only on the 8192 m map the numbers
|
||||
# were chosen for. Placing patches at a fixed 32 m put every bound in the wrong place on any
|
||||
# other TERRAIN_HALF.
|
||||
function cd_size(level: int) -> int { return f_mul(fi(32 << level), fr(TERRAIN_HALF * 2, 8192)) }
|
||||
|
||||
# does the patch's box come within r of the camera?
|
||||
function cd_within(x0: int, z0: int, size: int, ymin: int, ymax: int, r: int) -> bool {
|
||||
let dx = f_max(f_max(f_sub(x0, cam_pos[0]), f_sub(cam_pos[0], f_add(x0, size))), F_ZERO)
|
||||
|
|
@ -679,7 +699,7 @@ function cd_inside(x0: int, z0: int, size: int, ymin: int, ymax: int, r: int) ->
|
|||
}
|
||||
|
||||
function cdlod_draw(level: int, ix: int, iz: int) -> void {
|
||||
let size = fi(32 << level)
|
||||
let size = cd_size(level)
|
||||
let x0 = f_add(f_sub(ter_ox, fi(TERRAIN_HALF)), f_mul(fi(ix), size))
|
||||
let z0 = f_add(f_sub(ter_oz, fi(TERRAIN_HALF)), f_mul(fi(iz), size))
|
||||
# Which tier can run inside this patch. A patch that never comes within the split takes
|
||||
|
|
@ -723,7 +743,7 @@ function cdlod_draw(level: int, ix: int, iz: int) -> void {
|
|||
# its children, in which case each child either selects itself or is drawn at this level.
|
||||
function cdlod_select(level: int, ix: int, iz: int) -> bool {
|
||||
let n = CD_LEAVES >> level
|
||||
let size = fi(32 << level)
|
||||
let size = cd_size(level)
|
||||
let x0 = f_add(f_sub(ter_ox, fi(TERRAIN_HALF)), f_mul(fi(ix), size))
|
||||
let z0 = f_add(f_sub(ter_oz, fi(TERRAIN_HALF)), f_mul(fi(iz), size))
|
||||
let ymin = cd_min[level][iz * n + ix]
|
||||
|
|
@ -741,3 +761,42 @@ function cdlod_select(level: int, ix: int, iz: int) -> bool {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
# ---- another map at run time ------------------------------------------------------------
|
||||
# What belongs to ONE map - the generated height field and its CPU copy, the survey, the
|
||||
# photograph, the patch bounds and the scales cached from TERRAIN_HALF - is released here,
|
||||
# so a different survey can be generated in its place without restarting the process. What
|
||||
# belongs to the process stays: the patch mesh, the material textures, the programs, the
|
||||
# frame-sized targets and the shadow texture, whose size does not depend on the map.
|
||||
function terrain_unload() -> void {
|
||||
let ids = gl_scratch()
|
||||
if ter_height_tex != 0 { ids[0] = ter_height_tex; gl_delete_textures(1, ids); ter_height_tex = 0 }
|
||||
if ter_dem_tex != 0 { ids[0] = ter_dem_tex; gl_delete_textures(1, ids); ter_dem_tex = 0 }
|
||||
if ter_ortho_tex != 0 { ids[0] = ter_ortho_tex; gl_delete_textures(1, ids); ter_ortho_tex = 0 }
|
||||
if ter_heights != null { free(ter_heights); ter_heights = null }
|
||||
if ter_ortho_px != null { free(ter_ortho_px); ter_ortho_px = null }
|
||||
ter_ortho_w = 0
|
||||
if cd_min != null {
|
||||
for i in 0 .. len(cd_min) { free(cd_min[i]); free(cd_max[i]) }
|
||||
cd_min = null; cd_max = null
|
||||
}
|
||||
# derived from TERRAIN_HALF and cached on first use: stale ones would keep the old size
|
||||
ter_h_scale = 0; ter_o_scale = 0
|
||||
ter_carpet = 0
|
||||
ter_shadow_gen = -1; ter_shadow_yaw = 0x7fffffff
|
||||
}
|
||||
# Generate another map in place. Call terrain_lake / terrain_coast / terrain_island for it
|
||||
# first - the generator reads them - then this. `half` is the new TERRAIN_HALF in metres.
|
||||
# An empty `dem` generates the analytic ground (with ter_smooth) and an empty `ortho` drapes
|
||||
# no photograph. The shadow is rebaked and the patch bounds rebuilt before it returns.
|
||||
function terrain_reload(dem: string, emin: int, emax: int, base: int, ox: int, oz: int, ortho: string, half: int) -> void {
|
||||
terrain_unload()
|
||||
TERRAIN_HALF = half
|
||||
ter_ox = ox; ter_oz = oz
|
||||
if len(dem) > 0 { terrain_use_dem(dem, emin, emax, base, ox, oz) }
|
||||
if len(ortho) > 0 { terrain_use_ortho(ortho) }
|
||||
terrain_generate()
|
||||
terrain_bake_shadow()
|
||||
cdlod_bounds()
|
||||
gl_check("terrain reload")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue