From fa6c422f9b60e7a1080789790a8acfa591612ec7 Mon Sep 17 00:00:00 2001 From: Orkuncakilkaya Date: Wed, 16 Sep 2026 15:27:21 +0300 Subject: [PATCH] feat(render3d): terrain_lake_carve - a height map may carry its own lake bed Co-Authored-By: Claude Opus 5 --- changes/lake-water.md | 3 +++ .../ludic.render3d/shaders/heightgen.frag | 3 ++- .../shaders/spv/4be7acd9.frag.spv | Bin 16780 -> 16984 bytes .../ludic.render3d/shaders/spv/manifest.txt | 3 ++- packages/ludic.render3d/terrain.ludic | 9 +++++++++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/changes/lake-water.md b/changes/lake-water.md index 5625420f..c06a79a9 100644 --- a/changes/lake-water.md +++ b/changes/lake-water.md @@ -6,3 +6,6 @@ its lake no longer floods every hollow in the survey with that lake's level; and terrain's wet shore, forest and scree gates read the carved lake's line inside its outline (`u_lake`), the rule the grass already used, so a lake above the sea keeps its wet bank and no forest is painted down its bed. + +`terrain_lake_carve(false)` keeps a lake's line, outline and shore but skips carving its +bed, for a height map that already carries a shaped one. diff --git a/packages/ludic.render3d/shaders/heightgen.frag b/packages/ludic.render3d/shaders/heightgen.frag index 4da0e16e..0425698a 100644 --- a/packages/ludic.render3d/shaders/heightgen.frag +++ b/packages/ludic.render3d/shaders/heightgen.frag @@ -10,6 +10,7 @@ uniform float u_dem_base; // the elevation that becomes y = 0 uniform vec2 u_origin; // world x/z of the map's centre uniform vec4 u_lake; // a lake: centre x/z, half extents (zero = none) uniform float u_lake_level; // its surface height; the model records the surface, the bed is carved below it +uniform float u_lake_carve; // 0: the height map carries its own bed (terrain_lake_carve) uniform float u_sea_level; // the sea the coast falls into (terrain_sea); the lake's level unless set uniform vec4 u_isle; // an island: centre x/z, radius (or rim margin), falloff width uniform float u_isle_mode; // 0 none, 1 radial (terrain_island), 2 inward from the survey's edge (terrain_coast) @@ -50,7 +51,7 @@ void main() { float raw = demRaw(v_uv) * (u_dem_max - u_dem_min) + u_dem_min - u_dem_base; // the survey carries its own relief at this resolution; only the foot track is added h -= 0.25 * smoothstep(6.0, 1.5, pathDist(xz)); - if (u_lake.z > 0.0) { + if (u_lake.z > 0.0 && u_lake_carve > 0.5) { // the elevation model samples the water surface as flat ground: inside the lake's // outline sink it into a bed, deepest in the middle, with a gentle gravel ramp at the shore vec2 q = (xz - u_lake.xy) / u_lake.zw; diff --git a/packages/ludic.render3d/shaders/spv/4be7acd9.frag.spv b/packages/ludic.render3d/shaders/spv/4be7acd9.frag.spv index c660c39418df75b83df479a1e46c5fbe6242b3d2..9e9d1e97e08c13a2801444c2df76b646b4d21f96 100644 GIT binary patch delta 5226 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 diff --git a/packages/ludic.render3d/shaders/spv/manifest.txt b/packages/ludic.render3d/shaders/spv/manifest.txt index d8859f64..7955323c 100644 --- a/packages/ludic.render3d/shaders/spv/manifest.txt +++ b/packages/ludic.render3d/shaders/spv/manifest.txt @@ -157,7 +157,8 @@ U 4be7acd9 frag u_dem_base 776 float 1 0 U 4be7acd9 frag u_origin 784 vec2 1 0 U 4be7acd9 frag u_lake 800 vec4 1 0 U 4be7acd9 frag u_lake_level 816 float 1 0 -U 4be7acd9 frag u_sea_level 820 float 1 0 +U 4be7acd9 frag u_lake_carve 820 float 1 0 +U 4be7acd9 frag u_sea_level 824 float 1 0 U 4be7acd9 frag u_isle 832 vec4 1 0 U 4be7acd9 frag u_isle_mode 848 float 1 0 U 4be7acd9 frag u_dem_blur 852 float 1 0 diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index c4f56e70..98df4362 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -69,6 +69,12 @@ 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 } +# Whether the generator carves that bed. A height map that already carries a shaped bed +# turns it off and keeps the lake for everything else (its line, its outline, its shore): +# the carve drops the bed metres within the survey's last 0.9 m of shore, which reads as a +# cut edge on a lake whose own bank was shaped to come down gently. +var ter_lake_carve: bool = true +function terrain_lake_carve(on: bool) -> void { ter_lake_carve = on } # 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, @@ -238,6 +244,9 @@ function terrain_generate() -> void { u_f2(gpu_uniform(p, "u_origin"), ter_ox, ter_oz) u_f4(gpu_uniform(p, "u_lake"), ter_lake_cx, ter_lake_cz, ter_lake_ex, ter_lake_ez) u_f(gpu_uniform(p, "u_lake_level"), ter_lake_level) + var carve = F_ONE + if not ter_lake_carve { carve = F_ZERO } + u_f(gpu_uniform(p, "u_lake_carve"), carve) u_f(gpu_uniform(p, "u_sea_level"), ter_sea_gen()) u_f4(gpu_uniform(p, "u_isle"), ter_isle_cx, ter_isle_cz, ter_isle_r, ter_isle_fall) u_f(gpu_uniform(p, "u_isle_mode"), fi(ter_isle_mode))