53 lines
2.7 KiB
Text
53 lines
2.7 KiB
Text
# grass_rule.ludic — grass_cull.comp's bladeRules on the CPU: whether the ground at (x, z) grows blades, 0..1,
|
|
# for the migration that paints it into a map's blades density (keep the two in step). Every term here varies
|
|
# slower than a 2 m texel - the slope, the water's margin, the snow line, the photograph - so a painted texel
|
|
# holds it; what varies within one (each blade's hashes, the tussocks, the colour field) stays in the shader.
|
|
|
|
# the ground's keep at (x, z), under the kind in use and the map's water and snow
|
|
export function grass_rule_at(render3d_st: mut Render3dState, x: float, z: float) -> float {
|
|
let k = grass_kind(render3d_st)
|
|
let e = terrain_texel(render3d_st)
|
|
let y = terrain_height(render3d_st, x, z)
|
|
let gx = (terrain_height(render3d_st, x + e, z) - terrain_height(render3d_st, x - e, z)) / (2.0 * e)
|
|
let gz = (terrain_height(render3d_st, x, z + e) - terrain_height(render3d_st, x, z - e)) / (2.0 * e)
|
|
let ny = 1.0 / Math.sqrt(1.0 + gx * gx + gz * gz)
|
|
let snow = render3d_st.ter_snow_line
|
|
var ok = (1.0 - gr_smooth(k.slope_lo, k.slope_hi, 1.0 - ny)) * gr_smooth(0.0, 0.6, y - gr_water(render3d_st, x, z) - 0.15)
|
|
ok = ok * gr_smooth(snow - k.snow_in, snow - k.snow_out, y)
|
|
if ok <= 0.0 or render3d_st.ter_ortho_tex == 0 { return ok }
|
|
# the photograph varies inside a texel: its term averaged over four points of it
|
|
var o = 0.0
|
|
for s in 0 .. 4 {
|
|
let c = terrain_ortho(render3d_st, x + (float(s % 2) - 0.5) * e * 0.5, z + (float(s / 2) - 0.5) * e * 0.5)
|
|
let gb = gr_linear((c >> 8) & 255) - gr_linear(c & 255)
|
|
o = o + k.ortho_floor + k.ortho_gain * gr_smooth(0.0, k.ortho_green, gb)
|
|
}
|
|
return ok * o * 0.25
|
|
}
|
|
|
|
# the water line there: the sea's, or the carved lake's inside its ellipse (as gg_params hands them over)
|
|
function gr_water(render3d_st: Render3dState, x: float, z: float) -> float {
|
|
var lake = -100000.0
|
|
if render3d_st.ter_lake_ex != 0.0 { lake = render3d_st.ter_lake_level }
|
|
var wl = lake
|
|
if render3d_st.ter_sea_set { wl = render3d_st.ter_sea_level }
|
|
if render3d_st.ter_lake_ex > 0.0 {
|
|
let qx = (x - render3d_st.ter_lake_cx) / render3d_st.ter_lake_ex
|
|
let qz = (z - render3d_st.ter_lake_cz) / render3d_st.ter_lake_ez
|
|
if qx * qx + qz * qz < 1.0 { wl = Math.max(wl, lake) }
|
|
}
|
|
return wl
|
|
}
|
|
|
|
# GLSL's smoothstep, edges either way round as the shader's snow line has them
|
|
function gr_smooth(e0: float, e1: float, v: float) -> float {
|
|
let t = Math.clamp((v - e0) / (e1 - e0), 0.0, 1.0)
|
|
return t * t * (3.0 - 2.0 * t)
|
|
}
|
|
|
|
# an sRGB byte as the shader reads it from the photograph's sRGB texture: linear
|
|
function gr_linear(c: int) -> float {
|
|
let v = float(c) / 255.0
|
|
if v <= 0.04045 { return v / 12.92 }
|
|
return Math.pow((v + 0.055) / 1.055, 2.4)
|
|
}
|