diff --git a/packages/ludic.render3d/env.ludic b/packages/ludic.render3d/env.ludic index 4479bcf9..7d4ed7c8 100644 --- a/packages/ludic.render3d/env.ludic +++ b/packages/ludic.render3d/env.ludic @@ -848,6 +848,35 @@ export state Render3dState { tt_reads: int = 0 # tiles read from the file, over the run and this frame tt_frame_reads: int = 0 tt_warned: bool = false + tp_on: bool = false # the GPU pages the fine tiles round the camera (terrain_pages.ludic) + tp_page_tex: int = 0 # R32F tt_n a side: slot + 1 of each resident tile, 0 = the coarse level + tp_h_tex: int = 0 # the pool: heights, normals and photograph, a layer a slot + tp_n_tex: int = 0 + tp_o_tex: int = 0 + tp_dummy_page: int = 0 # bound while paging is off: an array sampler needs an array + tp_dummy_h: int = 0 + tp_dummy_n: int = 0 + tp_dummy_o: int = 0 + tp_slots: int = 0 + tp_reach: float = 0.0 # metres the pool was sized for + tp_page: floats = null # the page table's CPU mirror, uploaded whole when it changes + tp_tile_of: words = null # per slot: its tile, or -1 + tp_free: words = null # the free slots, a stack + tp_nfree: int = 0 + tp_dirty: bool = false + tp_cam_t: int = -1 # the camera's tile when residency last ran + tp_short: bool = false # a wanted tile was left for the next frame + tp_cand: words = null # this frame's loads, nearest first: their tiles, distances, slots + tp_cand_d: floats = null + tp_cand_s: words = null + tp_zero: words = null # the one layer a 2-D texture has, for gpu_layers_copy + tp_st_buf: long = 0 # the staging buffer the loads are written into, two halves + tp_st_mem: long = 0 + tp_st_ptr: pointer = null + tp_st_bytes: int = 0 + tp_loads: int = 0 # tiles loaded over the run, and this frame + tp_frame_loads: int = 0 + tp_bytes: int = 0 # the pool's and the page table's GPU bytes ter_shadow_prog_aux: int = 0 # the bake of the aux target (TS_AUX) ter_shadow_aux: int = 0 # beside it, RG16F: the occluder distance and the cloud mask ter_shadow_yaw: float = 1000000000.0 # the sky yaw it was baked for diff --git a/packages/ludic.render3d/gpu_vk.ludic b/packages/ludic.render3d/gpu_vk.ludic index ac0008e1..4de95209 100644 --- a/packages/ludic.render3d/gpu_vk.ludic +++ b/packages/ludic.render3d/gpu_vk.ludic @@ -815,7 +815,7 @@ function gvk_startup_state(render3d_st: mut Render3dState) -> void { render3d_st.gvk_pc_last = words(4096) for i in 0 .. 4096 { render3d_st.gvk_pc_last[i] = -1 } render3d_st.gg_rb = words(4); render3d_st.gg_cb = words(1); render3d_st.gg_bufs = words(3) - render3d_st.gg_texs = words(3); render3d_st.gg_pr = words(48) + render3d_st.gg_texs = words(7); render3d_st.gg_pr = words(52) if render3d_st.gpu_unit_2d == null { render3d_st.gpu_unit_2d = words(32) for i in 0 .. 32 { render3d_st.gpu_unit_2d[i] = -1 } diff --git a/packages/ludic.render3d/gpu_vk_layers.ludic b/packages/ludic.render3d/gpu_vk_layers.ludic new file mode 100644 index 00000000..78e8e7a9 --- /dev/null +++ b/packages/ludic.render3d/gpu_vk_layers.ludic @@ -0,0 +1,94 @@ +# gpu_vk_layers.ludic — what the terrain's page pool asks of Vulkan (terrain_pages.ludic): a staging +# buffer kept for the process, regions of it copied into chosen layers of an array texture inside the +# frame's own command buffer (no submit of their own), and a one-off linear blit for a coarse level. + +# a host-visible staging buffer of n bytes kept for good (the pool's uploads reuse it every frame) +@alloc_ok("made once per resource and kept for its life: the page pool's staging buffer") +function gpu_staging_keep(render3d_st: mut Render3dState, n: int) -> pointer { + gvk_flush(render3d_st) + let p = gvk_staging(render3d_st, n, VK_BUFFER_USAGE_TRANSFER_SRC_BIT) + if p == null { return null } + render3d_st.tp_st_buf = render3d_st.gvk_st_buf + render3d_st.tp_st_mem = render3d_st.gvk_st_mem + render3d_st.tp_st_bytes = n + return p +} +# ... and let go (a map whose tiles need a larger one) +function gpu_staging_drop(render3d_st: mut Render3dState) -> void { + if render3d_st.tp_st_buf == 0 { return } + gvk_flush(render3d_st) + let buf = render3d_st.gvk_st_buf + let mem = render3d_st.gvk_st_mem + render3d_st.gvk_st_buf = render3d_st.tp_st_buf + render3d_st.gvk_st_mem = render3d_st.tp_st_mem + gvk_staging_free(render3d_st) + render3d_st.gvk_st_buf = buf + render3d_st.gvk_st_mem = mem + let zero: long = 0 + render3d_st.tp_st_buf = zero; render3d_st.tp_st_mem = zero; render3d_st.tp_st_ptr = null; render3d_st.tp_st_bytes = 0 +} + +# the frame's command buffer, outside any pass, for copies recorded before the frame's draws +function gpu_upload_cb(render3d_st: mut Render3dState) -> pointer { + gvk_pass_end(render3d_st) + return gvk_frame_cb(render3d_st) +} + +# n regions of the kept staging buffer, each w x h texels starting at base + k * stride, into layer +# layers[k] of tex's level 0: one barrier each way around them all, recorded into cb +function gpu_layers_copy(render3d_st: mut Render3dState, cb: pointer, tex: int, w: int, h: int, base: int, stride: int, layers: words, n: int) -> void { + if n <= 0 or tex <= 0 or cb == null { return } + let image = render3d_st.gvk_tex_image[tex] + let all = render3d_st.gvk_tex_layers[tex] + let levels = render3d_st.gvk_tex_levels[tex] + gvk_barrier(render3d_st, cb, image, false, 0, levels, all, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + let sz = VkBufferImageCopy_sizeof + let bic = gvk_tmp(render3d_st, sz * n) + Vk.zero(bic, sz * n) + let sub = VkBufferImageCopy_imageSubresource + for k in 0 .. n { + let at: long = base + k * stride + Vk.put_i64(bic, k * sz + VkBufferImageCopy_bufferOffset, at) + Vk.put_i32(bic, k * sz + sub + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) + Vk.put_i32(bic, k * sz + sub + VkImageSubresourceLayers_baseArrayLayer, layers[k]) + Vk.put_i32(bic, k * sz + sub + VkImageSubresourceLayers_layerCount, 1) + Vk.put_i32(bic, k * sz + VkBufferImageCopy_imageExtent + VkExtent3D_width, w) + Vk.put_i32(bic, k * sz + VkBufferImageCopy_imageExtent + VkExtent3D_height, h) + Vk.put_i32(bic, k * sz + VkBufferImageCopy_imageExtent + VkExtent3D_depth, 1) + } + Vk.cmd_copy_buffer_to_image(cb, render3d_st.tp_st_buf, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, n, bic) + gvk_barrier(render3d_st, cb, image, false, 0, levels, all, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) +} + +# level 0 of src (sw x sh) filtered into level 0 of dst (dw x dh), once: a coarse level made on the GPU +function gpu_tex_blit(render3d_st: mut Render3dState, src: int, sw: int, sh: int, dst: int, dw: int, dh: int) -> bool { + gvk_flush(render3d_st) + let si = render3d_st.gvk_tex_image[src] + let di = render3d_st.gvk_tex_image[dst] + if si == 0 or di == 0 { return false } + let cb = gvk_once_begin(render3d_st) + gvk_barrier(render3d_st, cb, si, false, 0, 1, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) + gvk_barrier(render3d_st, cb, di, false, 0, 1, 1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) + let blit = gvk_tmp(render3d_st, VkImageBlit_sizeof) + Vk.zero(blit, VkImageBlit_sizeof) + Vk.put_i32(blit, VkImageBlit_srcSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) + Vk.put_i32(blit, VkImageBlit_srcSubresource + VkImageSubresourceLayers_layerCount, 1) + Vk.put_i32(blit, VkImageBlit_srcOffsets + VkOffset3D_sizeof + VkOffset3D_x, sw) + Vk.put_i32(blit, VkImageBlit_srcOffsets + VkOffset3D_sizeof + VkOffset3D_y, sh) + Vk.put_i32(blit, VkImageBlit_srcOffsets + VkOffset3D_sizeof + VkOffset3D_z, 1) + Vk.put_i32(blit, VkImageBlit_dstSubresource + VkImageSubresourceLayers_aspectMask, VK_IMAGE_ASPECT_COLOR_BIT) + Vk.put_i32(blit, VkImageBlit_dstSubresource + VkImageSubresourceLayers_layerCount, 1) + Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_x, dw) + Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_y, dh) + Vk.put_i32(blit, VkImageBlit_dstOffsets + VkOffset3D_sizeof + VkOffset3D_z, 1) + Vk.cmd_blit_image(cb, si, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, di, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blit, VK_FILTER_LINEAR) + gvk_barrier(render3d_st, cb, si, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + gvk_barrier(render3d_st, cb, di, false, 0, 1, 1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) + return gvk_once_end(render3d_st, cb) +} + +# pixels into level 0 of a texture made without them (tex_target), laid out as gvk_tex_upload takes them +function gpu_tex_fill(render3d_st: mut Render3dState, tex: int, ifmt: int, w: int, h: int, fmt: int, ty: int, data: pointer) -> bool { + gvk_flush(render3d_st) + return gvk_tex_upload(render3d_st, tex, ifmt, w, h, 1, fmt, ty, data) +} diff --git a/packages/ludic.render3d/grass_gpu.ludic b/packages/ludic.render3d/grass_gpu.ludic index 6849d712..732d0275 100644 --- a/packages/ludic.render3d/grass_gpu.ludic +++ b/packages/ludic.render3d/grass_gpu.ludic @@ -38,7 +38,7 @@ function gg_init__t(render3d_st: mut Render3dState) -> void { if not gpu_has_compute(render3d_st) or render3d_st.grass_prog == 0 { return } if r3d_env_has(render3d_st, "R3D_GRASS_GPU") and r3d_env(render3d_st, "R3D_GRASS_GPU") == "0" { return } render3d_st.gg_reset = gpu_compute(render3d_st, "grass_reset", 1) - render3d_st.gg_cull = gpu_compute_tex(render3d_st, "grass_cull", 3, 3) + render3d_st.gg_cull = gpu_compute_tex(render3d_st, "grass_cull", 3, 7) render3d_st.gg_prog = r3d_program(render3d_st, "grass_inst.vert", "model.frag", "#define FOLIAGE\n#define BLADE\n#define GBLADE\n#define GINST\n") if render3d_st.gg_reset == 0 or render3d_st.gg_cull == 0 or render3d_st.gg_prog == 0 { return } render3d_st.gg_tiles_buf = new []int @@ -125,13 +125,21 @@ function gg_cull_frame(render3d_st: mut Render3dState) -> void { bufs[0] = tb; bufs[1] = render3d_st.gg_out; bufs[2] = render3d_st.gg_cmds let texs = render3d_st.gg_texs texs[0] = render3d_st.ter_height_tex; texs[1] = render3d_st.ter_ortho_tex; texs[2] = render3d_st.ter_normal_tex - gpu_dispatch_tex(render3d_st, render3d_st.gg_cull, data_of(pr), 192, bufs, texs, (render3d_st.gg_max + 63) / 64, render3d_st.gg_n) + gg_page_texs(render3d_st, texs) + gpu_dispatch_tex(render3d_st, render3d_st.gg_cull, data_of(pr), 208, bufs, texs, (render3d_st.gg_max + 63) / 64, render3d_st.gg_n) } -# grass_cull.comp's Params, std140: 12 vec4s, into the block made once in gg_cull_frame +# the page pool's four, after the three whole-map ones (the stand-ins while paging is off) +function gg_page_texs(render3d_st: mut Render3dState, texs: words) -> void { + tp_dummies(render3d_st) + texs[3] = render3d_st.tp_dummy_page; texs[4] = render3d_st.tp_dummy_h; texs[5] = render3d_st.tp_dummy_n; texs[6] = render3d_st.tp_dummy_o + if render3d_st.tp_on { texs[3] = render3d_st.tp_page_tex; texs[4] = render3d_st.tp_h_tex; texs[5] = render3d_st.tp_n_tex; texs[6] = render3d_st.tp_o_tex } +} + +# grass_cull.comp's Params, std140: 13 vec4s, into the block made once in gg_cull_frame function gg_params(render3d_st: mut Render3dState) -> words { let pr = render3d_st.gg_pr - for i in 0 .. 48 { pr[i] = 0 } + for i in 0 .. 52 { pr[i] = 0 } if render3d_st.cam_planes != null { for i in 0 .. 16 { pr[i] = float_bits(render3d_st.cam_planes[i]) } } pr[16] = float_bits(render3d_st.cam_pos[0]); pr[17] = float_bits(render3d_st.cam_pos[1]); pr[18] = float_bits(render3d_st.cam_pos[2]); pr[19] = float_bits(grass_reach(render3d_st)) var ph = render3d_st.post_h @@ -148,6 +156,9 @@ function gg_params(render3d_st: mut Render3dState) -> words { pr[32] = float_bits(render3d_st.ter_ox); pr[33] = float_bits(render3d_st.ter_oz); pr[34] = float_bits(float(render3d_st.TERRAIN_HALF)) pr[36] = float_bits(GG_NEAR); pr[37] = float_bits(GG_MID) for b in 0 .. GG_BANDS { pr[40 + b] = gg_base(b); pr[44 + b] = gg_cap(b) } + # the page pool (terrain_pages.ludic): on in lev.w, and its dims as the terrain's shaders take them + if render3d_st.tp_on { pr[31] = float_bits(1.0) } + pr[48] = float_bits(float(render3d_st.tt_n)); pr[49] = float_bits(float(TT_TEX)); pr[50] = float_bits(float(render3d_st.tt_oside)); pr[51] = float_bits(float(TERRAIN_RES)) return pr } diff --git a/packages/ludic.render3d/r3d.ludic b/packages/ludic.render3d/r3d.ludic index 69eb70e3..abc6c9ac 100644 --- a/packages/ludic.render3d/r3d.ludic +++ b/packages/ludic.render3d/r3d.ludic @@ -12,6 +12,7 @@ import "gpu_manifest.ludic" import "gpu_vk.ludic" import "vkmem_report.ludic" import "gpu_vk_res.ludic" +import "gpu_vk_layers.ludic" import "gpu_vk_bc.ludic" import "texture_dds.ludic" import "prof.ludic" @@ -27,6 +28,10 @@ import "terrain.ludic" import "terrain_chunks.ludic" import "terrain_tiles.ludic" import "terrain_tiles_cut.ludic" +import "terrain_pages.ludic" +import "terrain_pages_coarse.ludic" +import "terrain_pages_frame.ludic" +import "terrain_pages_fill.ludic" import "overlay.ludic" import "shadow.ludic" import "post.ludic" diff --git a/packages/ludic.render3d/render.ludic b/packages/ludic.render3d/render.ludic index acf1e2d7..69aa0fa1 100644 --- a/packages/ludic.render3d/render.ludic +++ b/packages/ludic.render3d/render.ludic @@ -62,6 +62,7 @@ export function r3d_fog_wall(render3d_st: mut Render3dState, dist: float) -> voi fog_impostors(render3d_st) shadow_fog(render3d_st) fog_streams(render3d_st) + terrain_pages_fog(render3d_st) } # how far anything is drawn: the fog wall and a margin, or `far` (0: no limit of its own) without one function r3d_reach(render3d_st: Render3dState, far: float) -> float { @@ -227,6 +228,7 @@ function r3d_frame__t(render3d_st: mut Render3dState, time: float) -> void { render3d_st.r3d_test_frame += 1 vkmem_tick(render3d_st) tt_frame(render3d_st) + tp_frame(render3d_st) fog_at_tick(render3d_st) if render3d_st.r3d_test_resize == 0 and r3d_env_has(render3d_st, "R3D_RESIZE_AT") { render3d_st.r3d_test_resize = Text.to_int(r3d_env(render3d_st, "R3D_RESIZE_AT")) } # R3D_RESIZE_AT=: from frame n on, rebuild every screen-sized buffer every few diff --git a/packages/ludic.render3d/shaders/grass.mesh b/packages/ludic.render3d/shaders/grass.mesh index 6f2c6305..38c1ea7a 100644 --- a/packages/ludic.render3d/shaders/grass.mesh +++ b/packages/ludic.render3d/shaders/grass.mesh @@ -19,11 +19,91 @@ uniform float u_time; uniform sampler2D u_ts_height; // the baked terrain normal: x and z in RG16F, y rebuilt (a terrain normal always points up) uniform sampler2D u_ter_normal; -vec3 terNormal(vec2 uv) { vec2 xz = textureLod(u_ter_normal, uv, 0.0).rg; return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform vec2 u_ts_origin; uniform float u_ts_half; uniform sampler2D u_ortho; uniform float u_ortho_on; +// terpage.glsl, pasted (the reference copy and its rules are there) +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif +// ---- height (vertex and mesh stages) ----------------------------------------------------- +#ifndef TP_HEIGHT +#define TP_HEIGHT +#ifndef TP_HMAP +#define TP_HMAP u_ts_height +#endif +uniform sampler2DArray u_tp_h; +// bilinear height, fine where a tile is resident. Outside a fragment stage texture() IS level 0 +// (glslang emits the same explicit-lod sample), so this is the old read bit for bit. +float terHeight(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_h, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).r; + return textureLod(TP_HMAP, uv, 0.0).r; +} +// The B-spline through four bilinear taps (heightSmooth). Paging on, the texel is the FULL +// map's - the coarse map's own size would halve the surface's detail - and each tap finds its +// own tile, so a patch straddling two tiles, or a tile and the coarse map, stays one surface. +float terHeightSmooth(vec2 uv) { + vec2 res = u_tp_on > 0.5 ? vec2(u_tp_dims.w) : vec2(textureSize(TP_HMAP, 0)); + vec2 t = uv * res - 0.5; + vec2 f = fract(t); + vec2 i = floor(t); + vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0; + vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0; + vec2 w3 = f * f * f / 6.0; + vec2 w2 = 1.0 - w0 - w1 - w3; + vec2 s0 = w0 + w1, s1 = w2 + w3; + vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; + vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; + return (terHeight(vec2(o0.x, o0.y)) * s0.x + terHeight(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (terHeight(vec2(o0.x, o1.y)) * s0.x + terHeight(vec2(o1.x, o1.y)) * s1.x) * s1.y; +} +#endif +// ---- the baked normal (x, z; y is rebuilt by the caller) ---------------------------------- +#ifndef TP_NORMAL +#define TP_NORMAL +uniform sampler2DArray u_tp_nrm; +vec2 terNormalXZ(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ter_normal, uv).rg; + // taken before the per-pixel branch: a neighbour on another tile must not decide the level + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_nrm, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).rg; +#ifdef TP_FRAGMENT + return textureGrad(u_ter_normal, uv, dx, dy).rg; +#else + return textureLod(u_ter_normal, uv, 0.0).rg; +#endif +} +#endif +// ---- the photograph's measures (declares no sampler: a stage reading u_ortho blurred needs no tile) +#ifndef TP_ORTHO_LOD +#define TP_ORTHO_LOD +// the FULL photograph's texels a side, for filters that weigh by the texel (orthoSmooth) +vec2 tpOrthoRes() { return u_tp_on > 0.5 ? vec2(u_tp_dims.z * u_tp_dims.x) : vec2(textureSize(u_ortho, 0)); } +// A blurred read (textureLod 1..2.5) names a level of the FULL photograph; on the coarse map +// the same detail is that many levels lower, so the ground's far colour does not soften. +float tpOrthoLod(float lod) { + if (u_tp_on < 0.5) return lod; + return max(lod - log2(u_tp_dims.z * u_tp_dims.x / float(textureSize(u_ortho, 0).x)), 0.0); +} +#endif +vec3 terNormal(vec2 uv) { vec2 xz = terNormalXZ(uv); return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform float u_lake_level; uniform float u_sea_level; uniform vec4 u_lake; @@ -51,22 +131,6 @@ float bladeHash(ivec2 cell, int j, int k) { uint h = pcg(uint(cell.x + 32768) * 73856093u ^ uint(cell.y + 32768) * 19349663u ^ uint(j) * 83492791u ^ uint(k) * 2654435761u); return float(h) * (1.0 / 4294967295.0); } -// no implicit level of detail outside a fragment shader: every lookup names level 0 -float heightSmooth(sampler2D tex, vec2 uv) { - vec2 res = vec2(textureSize(tex, 0)); - vec2 t = uv * res - 0.5; - vec2 f = fract(t); - vec2 i = floor(t); - vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0; - vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0; - vec2 w3 = f * f * f / 6.0; - vec2 w2 = 1.0 - w0 - w1 - w3; - vec2 s0 = w0 + w1, s1 = w2 + w3; - vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; - vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; - return (textureLod(tex, vec2(o0.x, o0.y), 0.0).r * s0.x + textureLod(tex, vec2(o1.x, o0.y), 0.0).r * s1.x) * s0.y - + (textureLod(tex, vec2(o0.x, o1.y), 0.0).r * s0.x + textureLod(tex, vec2(o1.x, o1.y), 0.0).r * s1.x) * s1.y; -} void main() { int ti = int(gl_WorkGroupID.y); @@ -95,7 +159,7 @@ void main() { float life = 1.0 - smoothstep(0.8, 1.0, fj / max(count, 1.0)); vec2 huv = (xz - u_ts_origin) / (2.0 * u_ts_half) + 0.5; if (huv.x < 0.0 || huv.x > 1.0 || huv.y < 0.0 || huv.y > 1.0) continue; - vec4 ht = textureLod(u_ts_height, huv, 0.0); + vec4 ht = vec4(terHeight(huv)); // only .r is read vec4 croot = u_vp * vec4(xz.x, ht.r, xz.y, 1.0); if (croot.w < -1.0 || abs(croot.x) > croot.w * 1.25 + 1.5 || abs(croot.y) > croot.w * 1.4 + 1.5) continue; vec3 gn = terNormal(huv); @@ -104,12 +168,12 @@ void main() { if (u_lake.z > 0.0) { vec2 q = (xz - u_lake.xy) / u_lake.zw; if (dot(q, q) < 1.0) wl = max(wl, u_lake_level); } float ok = (1.0 - smoothstep(0.30, 0.55, 1.0 - gn.y)) * smoothstep(0.0, 0.6, ht.r - wl - 0.15) * smoothstep(u_snow_line - 80.0, u_snow_line - 200.0, ht.r); if (u_ortho_on > 0.5) { - vec3 oc = textureLod(u_ortho, huv, 1.5).rgb; + vec3 oc = textureLod(u_ortho, huv, tpOrthoLod(1.5)).rgb; ok *= 0.25 + 0.75 * smoothstep(0.0, 0.02, oc.g - max(oc.r, oc.b)); } if (h4 > ok) continue; bool far = dist > 300.0; - float h = far ? ht.r : heightSmooth(u_ts_height, huv); + float h = far ? ht.r : terHeightSmooth(huv); if (far) h += 0.03; if ((u_dbg & 1) != 0) h += 0.3; float seed = hv.x * 0.7 + hv.y * 0.3; diff --git a/packages/ludic.render3d/shaders/grass.vert b/packages/ludic.render3d/shaders/grass.vert index e434b24b..e3afa1c5 100644 --- a/packages/ludic.render3d/shaders/grass.vert +++ b/packages/ludic.render3d/shaders/grass.vert @@ -17,11 +17,91 @@ uniform float u_time; uniform sampler2D u_ts_height; // the baked terrain normal: x and z in RG16F, y rebuilt (a terrain normal always points up) uniform sampler2D u_ter_normal; -vec3 terNormal(vec2 uv) { vec2 xz = textureLod(u_ter_normal, uv, 0.0).rg; return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform vec2 u_ts_origin; uniform float u_ts_half; uniform sampler2D u_ortho; uniform float u_ortho_on; +// terpage.glsl, pasted (the reference copy and its rules are there) +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif +// ---- height (vertex and mesh stages) ----------------------------------------------------- +#ifndef TP_HEIGHT +#define TP_HEIGHT +#ifndef TP_HMAP +#define TP_HMAP u_ts_height +#endif +uniform sampler2DArray u_tp_h; +// bilinear height, fine where a tile is resident. Outside a fragment stage texture() IS level 0 +// (glslang emits the same explicit-lod sample), so this is the old read bit for bit. +float terHeight(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_h, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).r; + return textureLod(TP_HMAP, uv, 0.0).r; +} +// The B-spline through four bilinear taps (heightSmooth). Paging on, the texel is the FULL +// map's - the coarse map's own size would halve the surface's detail - and each tap finds its +// own tile, so a patch straddling two tiles, or a tile and the coarse map, stays one surface. +float terHeightSmooth(vec2 uv) { + vec2 res = u_tp_on > 0.5 ? vec2(u_tp_dims.w) : vec2(textureSize(TP_HMAP, 0)); + vec2 t = uv * res - 0.5; + vec2 f = fract(t); + vec2 i = floor(t); + vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0; + vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0; + vec2 w3 = f * f * f / 6.0; + vec2 w2 = 1.0 - w0 - w1 - w3; + vec2 s0 = w0 + w1, s1 = w2 + w3; + vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; + vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; + return (terHeight(vec2(o0.x, o0.y)) * s0.x + terHeight(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (terHeight(vec2(o0.x, o1.y)) * s0.x + terHeight(vec2(o1.x, o1.y)) * s1.x) * s1.y; +} +#endif +// ---- the baked normal (x, z; y is rebuilt by the caller) ---------------------------------- +#ifndef TP_NORMAL +#define TP_NORMAL +uniform sampler2DArray u_tp_nrm; +vec2 terNormalXZ(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ter_normal, uv).rg; + // taken before the per-pixel branch: a neighbour on another tile must not decide the level + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_nrm, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).rg; +#ifdef TP_FRAGMENT + return textureGrad(u_ter_normal, uv, dx, dy).rg; +#else + return textureLod(u_ter_normal, uv, 0.0).rg; +#endif +} +#endif +// ---- the photograph's measures (declares no sampler: a stage reading u_ortho blurred needs no tile) +#ifndef TP_ORTHO_LOD +#define TP_ORTHO_LOD +// the FULL photograph's texels a side, for filters that weigh by the texel (orthoSmooth) +vec2 tpOrthoRes() { return u_tp_on > 0.5 ? vec2(u_tp_dims.z * u_tp_dims.x) : vec2(textureSize(u_ortho, 0)); } +// A blurred read (textureLod 1..2.5) names a level of the FULL photograph; on the coarse map +// the same detail is that many levels lower, so the ground's far colour does not soften. +float tpOrthoLod(float lod) { + if (u_tp_on < 0.5) return lod; + return max(lod - log2(u_tp_dims.z * u_tp_dims.x / float(textureSize(u_ortho, 0).x)), 0.0); +} +#endif +vec3 terNormal(vec2 uv) { vec2 xz = terNormalXZ(uv); return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform float u_lake_level; uniform float u_sea_level; // the sea (terrain_sea); the lake's level when there is no separate sea uniform vec4 u_lake; // the carved lake: centre x/z, half extents (z = 0: none) @@ -58,21 +138,6 @@ float bladeHash(ivec2 cell, int j, int k) { uint h = pcg(uint(cell.x + 32768) * 73856093u ^ uint(cell.y + 32768) * 19349663u ^ uint(j) * 83492791u ^ uint(k) * 2654435761u); return float(h) * (1.0 / 4294967295.0); } -float heightSmooth(sampler2D tex, vec2 uv) { - vec2 res = vec2(textureSize(tex, 0)); - vec2 t = uv * res - 0.5; - vec2 f = fract(t); - vec2 i = floor(t); - vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0; - vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0; - vec2 w3 = f * f * f / 6.0; - vec2 w2 = 1.0 - w0 - w1 - w3; - vec2 s0 = w0 + w1, s1 = w2 + w3; - vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; - vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; - return (texture(tex, vec2(o0.x, o0.y)).r * s0.x + texture(tex, vec2(o1.x, o0.y)).r * s1.x) * s0.y - + (texture(tex, vec2(o0.x, o1.y)).r * s0.x + texture(tex, vec2(o1.x, o1.y)).r * s1.x) * s1.y; -} void cull() { gl_Position = vec4(0.0, 0.0, 2.0, 1.0); v_wpos = vec3(0.0); v_nrm = vec3(0.0, 1.0, 0.0); v_uv = vec2(0.0); v_seed = 0.0; v_rot = vec2(0.0, 1.0); v_hull = 1.0; v_quake = 0.0; v_tint = vec3(1.0); } // lighting.glsl's regionTint(wpos, 0.35) times model.frag's patchiness, at the root: three fbm // fields a blade pixel used to pay for, when the whole blade stands on one spot @@ -133,7 +198,7 @@ void main() { // the ground under it vec2 huv = (xz - u_ts_origin) / (2.0 * u_ts_half) + 0.5; if (huv.x < 0.0 || huv.x > 1.0 || huv.y < 0.0 || huv.y > 1.0) { cull(); return; } - vec4 ht = texture(u_ts_height, huv); + vec4 ht = vec4(terHeight(huv)); // only .r is read vec4 croot = u_vp * vec4(xz.x, ht.r, xz.y, 1.0); if (croot.w < -1.0 || abs(croot.x) > croot.w * 1.25 + 1.5 || abs(croot.y) > croot.w * 1.4 + 1.5) { cull(); return; } vec3 gn = terNormal(huv); @@ -144,7 +209,7 @@ void main() { if (u_lake.z > 0.0) { vec2 q = (xz - u_lake.xy) / u_lake.zw; if (dot(q, q) < 1.0) wl = max(wl, u_lake_level); } float ok = (1.0 - smoothstep(0.30, 0.55, 1.0 - gn.y)) * smoothstep(0.0, 0.6, ht.r - wl - 0.15) * smoothstep(u_snow_line - 80.0, u_snow_line - 200.0, ht.r); if (u_ortho_on > 0.5) { - vec3 oc = textureLod(u_ortho, huv, 1.5).rgb; + vec3 oc = textureLod(u_ortho, huv, tpOrthoLod(1.5)).rgb; // Is this ground vegetated, by the photograph? The test used to be green DOMINANCE - // g - max(r, b) - which is a test for lush green and nothing else. A dry alpine meadow // is yellow-green: its red is as high as its green, so the whole meadow scored zero and @@ -159,7 +224,7 @@ void main() { // the root on the drawn surface: the CDLOD mesh follows the B-spline to within // centimetres near the camera, so the smooth sample is the drawn height bool far = dist > 300.0; - float h = far ? ht.r : heightSmooth(u_ts_height, huv); + float h = far ? ht.r : terHeightSmooth(huv); if (far) h += 0.03; if ((u_dbg & 1) != 0) h += 0.3; // the blade: sized so that coverage stays level as the spacing grows diff --git a/packages/ludic.render3d/shaders/grass_cull.comp b/packages/ludic.render3d/shaders/grass_cull.comp index e8cde392..76d8445f 100644 --- a/packages/ludic.render3d/shaders/grass_cull.comp +++ b/packages/ludic.render3d/shaders/grass_cull.comp @@ -15,11 +15,12 @@ layout(set = 0, binding = 0) uniform Params { vec4 cam; // xyz the camera, w no blades past this (u_radius) vec4 dens; // x s0, y d0, z one pixel in radians, w the snow line vec4 lake; // the carved lake: centre x/z, half extents (z = 0: none) - vec4 lev; // x the lake's level, y the sea's, z the photograph on (1), w unused + vec4 lev; // x the lake's level, y the sea's, z the photograph on (1), w the page pool on (1) vec4 ts; // the height texture: origin x/z, half extent, unused vec4 bands; // outer distance of bands 0 and 1 (x, y); band 2 is the rest uvec4 base; // each band's first record in Out uvec4 cap; // ... and how many it holds + vec4 tp; // the page pool (u_tp_dims): tiles a side, height texels a tile, photograph texels a tile, height texels a side } pr; layout(set = 0, binding = 1) readonly buffer Tiles { vec4 tiles[]; }; // corner x/z, indices per cell, cells per side @@ -28,6 +29,12 @@ layout(set = 0, binding = 3) buffer Cmds { uint cmds[]; }; // VkDra layout(set = 0, binding = 4) uniform sampler2D u_height; // the height (terrain's u_ts_height) layout(set = 0, binding = 5) uniform sampler2D u_ortho; // the photograph layout(set = 0, binding = 6) uniform sampler2D u_ter_normal; // the normal: x and z, y rebuilt +// the page pool (terrain_pages.ludic): while it is on, the three above are the coarse whole-map level +// and the tiles round the camera are layers of these, addressed through the page table +layout(set = 0, binding = 7) uniform sampler2D u_tp_page; // slot + 1 per tile, 0 = coarse +layout(set = 0, binding = 8) uniform sampler2DArray u_tp_h; // (T + 2)^2 a layer, a texel of border +layout(set = 0, binding = 9) uniform sampler2DArray u_tp_nrm; +layout(set = 0, binding = 10) uniform sampler2DArray u_tp_ortho; // (S + 2)^2 a layer const float CELL = 4.0; // grass.ludic GRASS_CELL @@ -36,8 +43,35 @@ float bladeHash(ivec2 cell, int j, int k) { uint h = pcg(uint(cell.x + 32768) * 73856093u ^ uint(cell.y + 32768) * 19349663u ^ uint(j) * 83492791u ^ uint(k) * 2654435761u); return float(h) * (1.0 / 4294967295.0); } +// the resident tile's slot under a full-map uv, or -1 (then the coarse level answers) +float tpSlot(vec2 uv) { + if (pr.lev.w < 0.5) return -1.0; + ivec2 t = ivec2(floor(clamp(uv, 0.0, 0.999999) * pr.tp.x)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// where a full-map uv falls in its slot's layer of side T (+ a border of one) +vec3 tpUv(vec2 uv, float side, float slot) { + vec2 f = fract(clamp(uv, 0.0, 0.999999) * pr.tp.x); + return vec3((1.0 + f * side) / (side + 2.0), slot); +} +float groundH(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_h, tpUv(uv, pr.tp.y, s), 0.0).r; + return textureLod(u_height, uv, 0.0).r; +} +vec2 groundN(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_nrm, tpUv(uv, pr.tp.y, s), 0.0).rg; + return textureLod(u_ter_normal, uv, 0.0).rg; +} +// the photograph: a resident tile's layer has no mips, so it is read at its own level +vec3 groundO(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_ortho, tpUv(uv, pr.tp.z, s), 0.0).rgb; + return textureLod(u_ortho, uv, 1.5).rgb; +} float heightSmooth(vec2 uv) { - vec2 res = vec2(textureSize(u_height, 0)); + vec2 res = pr.lev.w > 0.5 ? vec2(pr.tp.w) : vec2(textureSize(u_height, 0)); vec2 t = uv * res - 0.5; vec2 f = fract(t); vec2 i = floor(t); @@ -48,8 +82,8 @@ float heightSmooth(vec2 uv) { vec2 s0 = w0 + w1, s1 = w2 + w3; vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; - return (textureLod(u_height, vec2(o0.x, o0.y), 0.0).r * s0.x + textureLod(u_height, vec2(o1.x, o0.y), 0.0).r * s1.x) * s0.y - + (textureLod(u_height, vec2(o0.x, o1.y), 0.0).r * s0.x + textureLod(u_height, vec2(o1.x, o1.y), 0.0).r * s1.x) * s1.y; + return (groundH(vec2(o0.x, o0.y)) * s0.x + groundH(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (groundH(vec2(o0.x, o1.y)) * s0.x + groundH(vec2(o1.x, o1.y)) * s1.x) * s1.y; } // grass.vert's bladeField: the region, the patchiness, the dry patches and the tussocks' shade vec3 bladeField(vec2 xz, float y) { @@ -89,18 +123,18 @@ void main() { float life = 1.0 - smoothstep(keep * 0.75, keep, r); vec2 huv = (xz - pr.ts.xy) / (2.0 * pr.ts.z) + 0.5; if (huv.x < 0.0 || huv.x > 1.0 || huv.y < 0.0 || huv.y > 1.0) return; - vec4 ht = textureLod(u_height, huv, 0.0); + vec4 ht = vec4(groundH(huv)); // the frustum, on a sphere round the blade (it stands at most 0.9 m tall) vec3 root = vec3(xz.x, ht.r, xz.y); for (int k = 0; k < 4; k++) { if (dot(pr.planes[k].xyz, root) + pr.planes[k].w < -1.0) return; } - vec2 gxz = textureLod(u_ter_normal, huv, 0.0).rg; + vec2 gxz = groundN(huv); vec3 gn = vec3(gxz.x, sqrt(max(1.0 - dot(gxz, gxz), 0.0)), gxz.y); float h3 = bladeHash(ci, j, 2), h4 = bladeHash(ci, j, 3); float wl = pr.lev.y; if (pr.lake.z > 0.0) { vec2 q = (xz - pr.lake.xy) / pr.lake.zw; if (dot(q, q) < 1.0) wl = max(wl, pr.lev.x); } float ok = (1.0 - smoothstep(0.30, 0.55, 1.0 - gn.y)) * smoothstep(0.0, 0.6, ht.r - wl - 0.15) * smoothstep(pr.dens.w - 80.0, pr.dens.w - 200.0, ht.r); if (pr.lev.z > 0.5) { - vec3 oc = textureLod(u_ortho, huv, 1.5).rgb; + vec3 oc = groundO(huv); ok *= 0.40 + 0.60 * smoothstep(0.0, 0.025, oc.g - oc.b); } if (h4 > ok) return; diff --git a/packages/ludic.render3d/shaders/model.vert b/packages/ludic.render3d/shaders/model.vert index 248164e5..946abd6b 100644 --- a/packages/ludic.render3d/shaders/model.vert +++ b/packages/ludic.render3d/shaders/model.vert @@ -24,8 +24,41 @@ uniform float u_ground; uniform sampler2D u_ts_height; uniform vec2 u_ts_origin; uniform float u_ts_half; -float heightSmooth(sampler2D tex, vec2 uv) { - vec2 res = vec2(textureSize(tex, 0)); +// terpage.glsl, pasted (the reference copy and its rules are there) +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif +// ---- height (vertex and mesh stages) ----------------------------------------------------- +#ifndef TP_HEIGHT +#define TP_HEIGHT +#ifndef TP_HMAP +#define TP_HMAP u_ts_height +#endif +uniform sampler2DArray u_tp_h; +// bilinear height, fine where a tile is resident. Outside a fragment stage texture() IS level 0 +// (glslang emits the same explicit-lod sample), so this is the old read bit for bit. +float terHeight(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_h, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).r; + return textureLod(TP_HMAP, uv, 0.0).r; +} +// The B-spline through four bilinear taps (heightSmooth). Paging on, the texel is the FULL +// map's - the coarse map's own size would halve the surface's detail - and each tap finds its +// own tile, so a patch straddling two tiles, or a tile and the coarse map, stays one surface. +float terHeightSmooth(vec2 uv) { + vec2 res = u_tp_on > 0.5 ? vec2(u_tp_dims.w) : vec2(textureSize(TP_HMAP, 0)); vec2 t = uv * res - 0.5; vec2 f = fract(t); vec2 i = floor(t); @@ -36,9 +69,10 @@ float heightSmooth(sampler2D tex, vec2 uv) { vec2 s0 = w0 + w1, s1 = w2 + w3; vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; - return (texture(tex, vec2(o0.x, o0.y)).r * s0.x + texture(tex, vec2(o1.x, o0.y)).r * s1.x) * s0.y - + (texture(tex, vec2(o0.x, o1.y)).r * s0.x + texture(tex, vec2(o1.x, o1.y)).r * s1.x) * s1.y; + return (terHeight(vec2(o0.x, o0.y)) * s0.x + terHeight(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (terHeight(vec2(o0.x, o1.y)) * s0.x + terHeight(vec2(o1.x, o1.y)) * s1.x) * s1.y; } +#endif out vec3 v_wpos; // The foliage prepass (depth.frag) and the lit pass after it compile this same source // into two programs and compare depths for equality: the position must come out @@ -149,7 +183,7 @@ void main() { vec3 w = p + i_pos.xyz; if (u_ground > 0.5) { vec2 huv = (i_pos.xz - u_ts_origin) / (2.0 * u_ts_half) + 0.5; - w.y = heightSmooth(u_ts_height, huv) - 0.03 + p.y; + w.y = terHeightSmooth(huv) - 0.03 + p.y; } v_wpos = w; v_nrm = n; diff --git a/packages/ludic.render3d/shaders/spv/01089da8.frag.spv b/packages/ludic.render3d/shaders/spv/01089da8.frag.spv index 61663acc..2f46226c 100644 Binary files a/packages/ludic.render3d/shaders/spv/01089da8.frag.spv and b/packages/ludic.render3d/shaders/spv/01089da8.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/01089da8.vert.spv b/packages/ludic.render3d/shaders/spv/01089da8.vert.spv index 498fe454..ff2c07b4 100644 Binary files a/packages/ludic.render3d/shaders/spv/01089da8.vert.spv and b/packages/ludic.render3d/shaders/spv/01089da8.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv b/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv index 29eedf8c..10ba90b0 100644 Binary files a/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv and b/packages/ludic.render3d/shaders/spv/1ce5c5d1.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/1ce5c5d1.vert.spv b/packages/ludic.render3d/shaders/spv/1ce5c5d1.vert.spv index f89bc657..19bc322b 100644 Binary files a/packages/ludic.render3d/shaders/spv/1ce5c5d1.vert.spv and b/packages/ludic.render3d/shaders/spv/1ce5c5d1.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv b/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv index ea4b0992..1d4e7626 100644 Binary files a/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv and b/packages/ludic.render3d/shaders/spv/1f95f31b.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/1f95f31b.vert.spv b/packages/ludic.render3d/shaders/spv/1f95f31b.vert.spv index c19cd525..915e9a5a 100644 Binary files a/packages/ludic.render3d/shaders/spv/1f95f31b.vert.spv and b/packages/ludic.render3d/shaders/spv/1f95f31b.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/254e7887.frag.spv b/packages/ludic.render3d/shaders/spv/254e7887.frag.spv index 6440345a..0d5765a3 100644 Binary files a/packages/ludic.render3d/shaders/spv/254e7887.frag.spv and b/packages/ludic.render3d/shaders/spv/254e7887.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/254e7887.vert.spv b/packages/ludic.render3d/shaders/spv/254e7887.vert.spv index 11f34814..b80a910d 100644 Binary files a/packages/ludic.render3d/shaders/spv/254e7887.vert.spv and b/packages/ludic.render3d/shaders/spv/254e7887.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/266d2401.frag.spv b/packages/ludic.render3d/shaders/spv/266d2401.frag.spv index fb442a61..7f0c77d9 100644 Binary files a/packages/ludic.render3d/shaders/spv/266d2401.frag.spv and b/packages/ludic.render3d/shaders/spv/266d2401.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/266d2401.vert.spv b/packages/ludic.render3d/shaders/spv/266d2401.vert.spv index 13699327..3472230e 100644 Binary files a/packages/ludic.render3d/shaders/spv/266d2401.vert.spv and b/packages/ludic.render3d/shaders/spv/266d2401.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv b/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv index 53f908f3..94c6e802 100644 Binary files a/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv and b/packages/ludic.render3d/shaders/spv/2e2908c8.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/2e2908c8.vert.spv b/packages/ludic.render3d/shaders/spv/2e2908c8.vert.spv index c19cd525..915e9a5a 100644 Binary files a/packages/ludic.render3d/shaders/spv/2e2908c8.vert.spv and b/packages/ludic.render3d/shaders/spv/2e2908c8.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/3b183d8f.frag.spv b/packages/ludic.render3d/shaders/spv/3b183d8f.frag.spv index 58399ad9..832410d2 100644 Binary files a/packages/ludic.render3d/shaders/spv/3b183d8f.frag.spv and b/packages/ludic.render3d/shaders/spv/3b183d8f.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/3b183d8f.vert.spv b/packages/ludic.render3d/shaders/spv/3b183d8f.vert.spv index f89bc657..19bc322b 100644 Binary files a/packages/ludic.render3d/shaders/spv/3b183d8f.vert.spv and b/packages/ludic.render3d/shaders/spv/3b183d8f.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/439a2253.frag.spv b/packages/ludic.render3d/shaders/spv/439a2253.frag.spv index 6abc721d..617a4d1b 100644 Binary files a/packages/ludic.render3d/shaders/spv/439a2253.frag.spv and b/packages/ludic.render3d/shaders/spv/439a2253.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/439a2253.vert.spv b/packages/ludic.render3d/shaders/spv/439a2253.vert.spv index a4e994fc..a2153f83 100644 Binary files a/packages/ludic.render3d/shaders/spv/439a2253.vert.spv and b/packages/ludic.render3d/shaders/spv/439a2253.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/4dda406a.frag.spv b/packages/ludic.render3d/shaders/spv/4dda406a.frag.spv index 4d2c6720..483029ff 100644 Binary files a/packages/ludic.render3d/shaders/spv/4dda406a.frag.spv and b/packages/ludic.render3d/shaders/spv/4dda406a.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/4dda406a.vert.spv b/packages/ludic.render3d/shaders/spv/4dda406a.vert.spv index f89bc657..19bc322b 100644 Binary files a/packages/ludic.render3d/shaders/spv/4dda406a.vert.spv and b/packages/ludic.render3d/shaders/spv/4dda406a.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/512004a1.frag.spv b/packages/ludic.render3d/shaders/spv/512004a1.frag.spv index 053a81df..6245b446 100644 Binary files a/packages/ludic.render3d/shaders/spv/512004a1.frag.spv and b/packages/ludic.render3d/shaders/spv/512004a1.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/512004a1.vert.spv b/packages/ludic.render3d/shaders/spv/512004a1.vert.spv index a3f0598e..f8dcb4d2 100644 Binary files a/packages/ludic.render3d/shaders/spv/512004a1.vert.spv and b/packages/ludic.render3d/shaders/spv/512004a1.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv b/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv index 68839b83..d45f5561 100644 Binary files a/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv and b/packages/ludic.render3d/shaders/spv/5e4589a1.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/5e4589a1.vert.spv b/packages/ludic.render3d/shaders/spv/5e4589a1.vert.spv index c19cd525..915e9a5a 100644 Binary files a/packages/ludic.render3d/shaders/spv/5e4589a1.vert.spv and b/packages/ludic.render3d/shaders/spv/5e4589a1.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/5e70d731.frag.spv b/packages/ludic.render3d/shaders/spv/5e70d731.frag.spv index d50c8228..ef659e9e 100644 Binary files a/packages/ludic.render3d/shaders/spv/5e70d731.frag.spv and b/packages/ludic.render3d/shaders/spv/5e70d731.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/5e70d731.vert.spv b/packages/ludic.render3d/shaders/spv/5e70d731.vert.spv index 1c261cd7..b763538f 100644 Binary files a/packages/ludic.render3d/shaders/spv/5e70d731.vert.spv and b/packages/ludic.render3d/shaders/spv/5e70d731.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/79b77ed9.frag.spv b/packages/ludic.render3d/shaders/spv/79b77ed9.frag.spv index 3a6709e2..6ff5390b 100644 Binary files a/packages/ludic.render3d/shaders/spv/79b77ed9.frag.spv and b/packages/ludic.render3d/shaders/spv/79b77ed9.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/79b77ed9.vert.spv b/packages/ludic.render3d/shaders/spv/79b77ed9.vert.spv index 16738e1c..7fc0ea7d 100644 Binary files a/packages/ludic.render3d/shaders/spv/79b77ed9.vert.spv and b/packages/ludic.render3d/shaders/spv/79b77ed9.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/86f427b0.frag.spv b/packages/ludic.render3d/shaders/spv/86f427b0.frag.spv index 071e1c42..e39fddf3 100644 Binary files a/packages/ludic.render3d/shaders/spv/86f427b0.frag.spv and b/packages/ludic.render3d/shaders/spv/86f427b0.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/86f427b0.vert.spv b/packages/ludic.render3d/shaders/spv/86f427b0.vert.spv index 33ce9d18..1bb582cf 100644 Binary files a/packages/ludic.render3d/shaders/spv/86f427b0.vert.spv and b/packages/ludic.render3d/shaders/spv/86f427b0.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/9dce3246.frag.spv b/packages/ludic.render3d/shaders/spv/9dce3246.frag.spv index 6abc721d..617a4d1b 100644 Binary files a/packages/ludic.render3d/shaders/spv/9dce3246.frag.spv and b/packages/ludic.render3d/shaders/spv/9dce3246.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/9dce3246.vert.spv b/packages/ludic.render3d/shaders/spv/9dce3246.vert.spv index 33ce9d18..1bb582cf 100644 Binary files a/packages/ludic.render3d/shaders/spv/9dce3246.vert.spv and b/packages/ludic.render3d/shaders/spv/9dce3246.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv b/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv index 76aaaa32..9b047653 100644 Binary files a/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv and b/packages/ludic.render3d/shaders/spv/a0f2dbac.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/a0f2dbac.vert.spv b/packages/ludic.render3d/shaders/spv/a0f2dbac.vert.spv index c19cd525..915e9a5a 100644 Binary files a/packages/ludic.render3d/shaders/spv/a0f2dbac.vert.spv and b/packages/ludic.render3d/shaders/spv/a0f2dbac.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b4587cd4.frag.spv b/packages/ludic.render3d/shaders/spv/b4587cd4.frag.spv index 0c3498a0..e14c9356 100644 Binary files a/packages/ludic.render3d/shaders/spv/b4587cd4.frag.spv and b/packages/ludic.render3d/shaders/spv/b4587cd4.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b4587cd4.vert.spv b/packages/ludic.render3d/shaders/spv/b4587cd4.vert.spv index 8c708ca4..5852c590 100644 Binary files a/packages/ludic.render3d/shaders/spv/b4587cd4.vert.spv and b/packages/ludic.render3d/shaders/spv/b4587cd4.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b603a056.frag.spv b/packages/ludic.render3d/shaders/spv/b603a056.frag.spv index 4d2c6720..483029ff 100644 Binary files a/packages/ludic.render3d/shaders/spv/b603a056.frag.spv and b/packages/ludic.render3d/shaders/spv/b603a056.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b603a056.vert.spv b/packages/ludic.render3d/shaders/spv/b603a056.vert.spv index 1c261cd7..b763538f 100644 Binary files a/packages/ludic.render3d/shaders/spv/b603a056.vert.spv and b/packages/ludic.render3d/shaders/spv/b603a056.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b8cff226.frag.spv b/packages/ludic.render3d/shaders/spv/b8cff226.frag.spv index 14ec85b6..2d8343b9 100644 Binary files a/packages/ludic.render3d/shaders/spv/b8cff226.frag.spv and b/packages/ludic.render3d/shaders/spv/b8cff226.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b8cff226.vert.spv b/packages/ludic.render3d/shaders/spv/b8cff226.vert.spv index 27d2d811..70fc4234 100644 Binary files a/packages/ludic.render3d/shaders/spv/b8cff226.vert.spv and b/packages/ludic.render3d/shaders/spv/b8cff226.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b964507f.frag.spv b/packages/ludic.render3d/shaders/spv/b964507f.frag.spv index 89825574..67605cd4 100644 Binary files a/packages/ludic.render3d/shaders/spv/b964507f.frag.spv and b/packages/ludic.render3d/shaders/spv/b964507f.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b964507f.vert.spv b/packages/ludic.render3d/shaders/spv/b964507f.vert.spv index a3f0598e..f8dcb4d2 100644 Binary files a/packages/ludic.render3d/shaders/spv/b964507f.vert.spv and b/packages/ludic.render3d/shaders/spv/b964507f.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b9fc2041.frag.spv b/packages/ludic.render3d/shaders/spv/b9fc2041.frag.spv index 61663acc..2f46226c 100644 Binary files a/packages/ludic.render3d/shaders/spv/b9fc2041.frag.spv and b/packages/ludic.render3d/shaders/spv/b9fc2041.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/b9fc2041.vert.spv b/packages/ludic.render3d/shaders/spv/b9fc2041.vert.spv index f89bc657..19bc322b 100644 Binary files a/packages/ludic.render3d/shaders/spv/b9fc2041.vert.spv and b/packages/ludic.render3d/shaders/spv/b9fc2041.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/bf3151c8.frag.spv b/packages/ludic.render3d/shaders/spv/bf3151c8.frag.spv index 654a886d..6d476127 100644 Binary files a/packages/ludic.render3d/shaders/spv/bf3151c8.frag.spv and b/packages/ludic.render3d/shaders/spv/bf3151c8.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/bf3151c8.vert.spv b/packages/ludic.render3d/shaders/spv/bf3151c8.vert.spv index 1c2a8d8c..d5b0880e 100644 Binary files a/packages/ludic.render3d/shaders/spv/bf3151c8.vert.spv and b/packages/ludic.render3d/shaders/spv/bf3151c8.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/bfa15301.frag.spv b/packages/ludic.render3d/shaders/spv/bfa15301.frag.spv index 61cf4741..60eb3ae6 100644 Binary files a/packages/ludic.render3d/shaders/spv/bfa15301.frag.spv and b/packages/ludic.render3d/shaders/spv/bfa15301.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/bfa15301.vert.spv b/packages/ludic.render3d/shaders/spv/bfa15301.vert.spv index 8b53c402..0a84027b 100644 Binary files a/packages/ludic.render3d/shaders/spv/bfa15301.vert.spv and b/packages/ludic.render3d/shaders/spv/bfa15301.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/cb13f3c6.frag.spv b/packages/ludic.render3d/shaders/spv/cb13f3c6.frag.spv index 14ec85b6..2d8343b9 100644 Binary files a/packages/ludic.render3d/shaders/spv/cb13f3c6.frag.spv and b/packages/ludic.render3d/shaders/spv/cb13f3c6.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/cb13f3c6.vert.spv b/packages/ludic.render3d/shaders/spv/cb13f3c6.vert.spv index d03040b7..dd6092db 100644 Binary files a/packages/ludic.render3d/shaders/spv/cb13f3c6.vert.spv and b/packages/ludic.render3d/shaders/spv/cb13f3c6.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/d48d6a48.frag.spv b/packages/ludic.render3d/shaders/spv/d48d6a48.frag.spv index 8eed5b60..f680ae9c 100644 Binary files a/packages/ludic.render3d/shaders/spv/d48d6a48.frag.spv and b/packages/ludic.render3d/shaders/spv/d48d6a48.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/d48d6a48.vert.spv b/packages/ludic.render3d/shaders/spv/d48d6a48.vert.spv index abfb1b0b..9cc1e17d 100644 Binary files a/packages/ludic.render3d/shaders/spv/d48d6a48.vert.spv and b/packages/ludic.render3d/shaders/spv/d48d6a48.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f2952361.frag.spv b/packages/ludic.render3d/shaders/spv/f2952361.frag.spv index c5c7991d..27253844 100644 Binary files a/packages/ludic.render3d/shaders/spv/f2952361.frag.spv and b/packages/ludic.render3d/shaders/spv/f2952361.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f2952361.vert.spv b/packages/ludic.render3d/shaders/spv/f2952361.vert.spv index c19cd525..915e9a5a 100644 Binary files a/packages/ludic.render3d/shaders/spv/f2952361.vert.spv and b/packages/ludic.render3d/shaders/spv/f2952361.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f34dce08.frag.spv b/packages/ludic.render3d/shaders/spv/f34dce08.frag.spv index 053a81df..6245b446 100644 Binary files a/packages/ludic.render3d/shaders/spv/f34dce08.frag.spv and b/packages/ludic.render3d/shaders/spv/f34dce08.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f34dce08.vert.spv b/packages/ludic.render3d/shaders/spv/f34dce08.vert.spv index 410a9edf..c661bb7c 100644 Binary files a/packages/ludic.render3d/shaders/spv/f34dce08.vert.spv and b/packages/ludic.render3d/shaders/spv/f34dce08.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f4382918.frag.spv b/packages/ludic.render3d/shaders/spv/f4382918.frag.spv index 1702b95d..ced0e8e1 100644 Binary files a/packages/ludic.render3d/shaders/spv/f4382918.frag.spv and b/packages/ludic.render3d/shaders/spv/f4382918.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f4382918.vert.spv b/packages/ludic.render3d/shaders/spv/f4382918.vert.spv index c19cd525..915e9a5a 100644 Binary files a/packages/ludic.render3d/shaders/spv/f4382918.vert.spv and b/packages/ludic.render3d/shaders/spv/f4382918.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f4a2b586.frag.spv b/packages/ludic.render3d/shaders/spv/f4a2b586.frag.spv index 47102fc6..1fd9940e 100644 Binary files a/packages/ludic.render3d/shaders/spv/f4a2b586.frag.spv and b/packages/ludic.render3d/shaders/spv/f4a2b586.frag.spv differ diff --git a/packages/ludic.render3d/shaders/spv/f4a2b586.vert.spv b/packages/ludic.render3d/shaders/spv/f4a2b586.vert.spv index 6a92a18d..38c604f5 100644 Binary files a/packages/ludic.render3d/shaders/spv/f4a2b586.vert.spv and b/packages/ludic.render3d/shaders/spv/f4a2b586.vert.spv differ diff --git a/packages/ludic.render3d/shaders/spv/grass_cull.comp.spv b/packages/ludic.render3d/shaders/spv/grass_cull.comp.spv index 46bcf895..30f2434f 100644 Binary files a/packages/ludic.render3d/shaders/spv/grass_cull.comp.spv and b/packages/ludic.render3d/shaders/spv/grass_cull.comp.spv differ diff --git a/packages/ludic.render3d/shaders/spv/manifest.txt b/packages/ludic.render3d/shaders/spv/manifest.txt index b93cc63c..beb7c065 100644 --- a/packages/ludic.render3d/shaders/spv/manifest.txt +++ b/packages/ludic.render3d/shaders/spv/manifest.txt @@ -926,7 +926,7 @@ T 74258189 u_tershadow 7 T 74258189 u_tershadow_aux 8 T 74258189 u_ts_height 9 P d48d6a48 grass.mesh model.frag #define FOLIAGE;#define BLADE;#define MESH; -B d48d6a48 vert 0 4388 +B d48d6a48 vert 0 4420 U d48d6a48 vert u_view 0 mat4 1 0 U d48d6a48 vert u_proj 64 mat4 1 0 U d48d6a48 vert u_vp 128 mat4 1 0 @@ -935,18 +935,20 @@ U d48d6a48 vert u_time 204 float 1 0 U d48d6a48 vert u_ts_origin 208 vec2 1 0 U d48d6a48 vert u_ts_half 216 float 1 0 U d48d6a48 vert u_ortho_on 220 float 1 0 -U d48d6a48 vert u_lake_level 224 float 1 0 -U d48d6a48 vert u_sea_level 228 float 1 0 -U d48d6a48 vert u_lake 240 vec4 1 0 -U d48d6a48 vert u_snow_line 256 float 1 0 -U d48d6a48 vert u_wind 260 float 1 0 -U d48d6a48 vert u_tile_cells 264 int 1 0 -U d48d6a48 vert u_tiles 272 vec4 256 16 -U d48d6a48 vert u_s0 4368 float 1 0 -U d48d6a48 vert u_d0 4372 float 1 0 -U d48d6a48 vert u_radius 4376 float 1 0 -U d48d6a48 vert u_px 4380 float 1 0 -U d48d6a48 vert u_dbg 4384 int 1 0 +U d48d6a48 vert u_tp_on 224 float 1 0 +U d48d6a48 vert u_tp_dims 240 vec4 1 0 +U d48d6a48 vert u_lake_level 256 float 1 0 +U d48d6a48 vert u_sea_level 260 float 1 0 +U d48d6a48 vert u_lake 272 vec4 1 0 +U d48d6a48 vert u_snow_line 288 float 1 0 +U d48d6a48 vert u_wind 292 float 1 0 +U d48d6a48 vert u_tile_cells 296 int 1 0 +U d48d6a48 vert u_tiles 304 vec4 256 16 +U d48d6a48 vert u_s0 4400 float 1 0 +U d48d6a48 vert u_d0 4404 float 1 0 +U d48d6a48 vert u_radius 4408 float 1 0 +U d48d6a48 vert u_px 4412 float 1 0 +U d48d6a48 vert u_dbg 4416 int 1 0 B d48d6a48 frag 1 980 U d48d6a48 frag u_cascade_vp 0 mat4 5 64 U d48d6a48 frag u_cascade_split 320 float 5 16 @@ -1005,9 +1007,12 @@ T d48d6a48 u_shadow 10 T d48d6a48 u_ter_normal 11 T d48d6a48 u_tershadow 12 T d48d6a48 u_tershadow_aux 13 -T d48d6a48 u_ts_height 14 +T d48d6a48 u_tp_h 14 +T d48d6a48 u_tp_nrm 15 +T d48d6a48 u_tp_page 16 +T d48d6a48 u_ts_height 17 P cb13f3c6 grass.vert model.frag #define FOLIAGE;#define BLADE;#define GBLADE; -B cb13f3c6 vert 0 324 +B cb13f3c6 vert 0 356 U cb13f3c6 vert u_view 0 mat4 1 0 U cb13f3c6 vert u_proj 64 mat4 1 0 U cb13f3c6 vert u_vp 128 mat4 1 0 @@ -1016,20 +1021,22 @@ U cb13f3c6 vert u_time 204 float 1 0 U cb13f3c6 vert u_ts_origin 208 vec2 1 0 U cb13f3c6 vert u_ts_half 216 float 1 0 U cb13f3c6 vert u_ortho_on 220 float 1 0 -U cb13f3c6 vert u_lake_level 224 float 1 0 -U cb13f3c6 vert u_sea_level 228 float 1 0 -U cb13f3c6 vert u_lake 240 vec4 1 0 -U cb13f3c6 vert u_snow_line 256 float 1 0 -U cb13f3c6 vert u_wind 260 float 1 0 -U cb13f3c6 vert u_push 272 vec3 1 0 -U cb13f3c6 vert u_tile 288 vec2 1 0 -U cb13f3c6 vert u_tile_cells 296 int 1 0 -U cb13f3c6 vert u_per_cell 300 int 1 0 -U cb13f3c6 vert u_s0 304 float 1 0 -U cb13f3c6 vert u_d0 308 float 1 0 -U cb13f3c6 vert u_radius 312 float 1 0 -U cb13f3c6 vert u_px 316 float 1 0 -U cb13f3c6 vert u_dbg 320 int 1 0 +U cb13f3c6 vert u_tp_on 224 float 1 0 +U cb13f3c6 vert u_tp_dims 240 vec4 1 0 +U cb13f3c6 vert u_lake_level 256 float 1 0 +U cb13f3c6 vert u_sea_level 260 float 1 0 +U cb13f3c6 vert u_lake 272 vec4 1 0 +U cb13f3c6 vert u_snow_line 288 float 1 0 +U cb13f3c6 vert u_wind 292 float 1 0 +U cb13f3c6 vert u_push 304 vec3 1 0 +U cb13f3c6 vert u_tile 320 vec2 1 0 +U cb13f3c6 vert u_tile_cells 328 int 1 0 +U cb13f3c6 vert u_per_cell 332 int 1 0 +U cb13f3c6 vert u_s0 336 float 1 0 +U cb13f3c6 vert u_d0 340 float 1 0 +U cb13f3c6 vert u_radius 344 float 1 0 +U cb13f3c6 vert u_px 348 float 1 0 +U cb13f3c6 vert u_dbg 352 int 1 0 I cb13f3c6 a_pos 0 vec3 I cb13f3c6 a_uv 2 vec2 B cb13f3c6 frag 1 980 @@ -1090,9 +1097,12 @@ T cb13f3c6 u_shadow 10 T cb13f3c6 u_ter_normal 11 T cb13f3c6 u_tershadow 12 T cb13f3c6 u_tershadow_aux 13 -T cb13f3c6 u_ts_height 14 +T cb13f3c6 u_tp_h 14 +T cb13f3c6 u_tp_nrm 15 +T cb13f3c6 u_tp_page 16 +T cb13f3c6 u_ts_height 17 P b8cff226 grass.vert model.frag #define FOLIAGE;#define BLADE;#define GBLADE;#define TILES; -B b8cff226 vert 0 4420 +B b8cff226 vert 0 4452 U b8cff226 vert u_view 0 mat4 1 0 U b8cff226 vert u_proj 64 mat4 1 0 U b8cff226 vert u_vp 128 mat4 1 0 @@ -1101,21 +1111,23 @@ U b8cff226 vert u_time 204 float 1 0 U b8cff226 vert u_ts_origin 208 vec2 1 0 U b8cff226 vert u_ts_half 216 float 1 0 U b8cff226 vert u_ortho_on 220 float 1 0 -U b8cff226 vert u_lake_level 224 float 1 0 -U b8cff226 vert u_sea_level 228 float 1 0 -U b8cff226 vert u_lake 240 vec4 1 0 -U b8cff226 vert u_snow_line 256 float 1 0 -U b8cff226 vert u_wind 260 float 1 0 -U b8cff226 vert u_push 272 vec3 1 0 -U b8cff226 vert u_tile 288 vec2 1 0 -U b8cff226 vert u_tile_cells 296 int 1 0 -U b8cff226 vert u_per_cell 300 int 1 0 -U b8cff226 vert u_tiles 304 vec4 256 16 -U b8cff226 vert u_s0 4400 float 1 0 -U b8cff226 vert u_d0 4404 float 1 0 -U b8cff226 vert u_radius 4408 float 1 0 -U b8cff226 vert u_px 4412 float 1 0 -U b8cff226 vert u_dbg 4416 int 1 0 +U b8cff226 vert u_tp_on 224 float 1 0 +U b8cff226 vert u_tp_dims 240 vec4 1 0 +U b8cff226 vert u_lake_level 256 float 1 0 +U b8cff226 vert u_sea_level 260 float 1 0 +U b8cff226 vert u_lake 272 vec4 1 0 +U b8cff226 vert u_snow_line 288 float 1 0 +U b8cff226 vert u_wind 292 float 1 0 +U b8cff226 vert u_push 304 vec3 1 0 +U b8cff226 vert u_tile 320 vec2 1 0 +U b8cff226 vert u_tile_cells 328 int 1 0 +U b8cff226 vert u_per_cell 332 int 1 0 +U b8cff226 vert u_tiles 336 vec4 256 16 +U b8cff226 vert u_s0 4432 float 1 0 +U b8cff226 vert u_d0 4436 float 1 0 +U b8cff226 vert u_radius 4440 float 1 0 +U b8cff226 vert u_px 4444 float 1 0 +U b8cff226 vert u_dbg 4448 int 1 0 I b8cff226 a_pos 0 vec3 I b8cff226 a_uv 2 vec2 B b8cff226 frag 1 980 @@ -1176,7 +1188,10 @@ T b8cff226 u_shadow 10 T b8cff226 u_ter_normal 11 T b8cff226 u_tershadow 12 T b8cff226 u_tershadow_aux 13 -T b8cff226 u_ts_height 14 +T b8cff226 u_tp_h 14 +T b8cff226 u_tp_nrm 15 +T b8cff226 u_tp_page 16 +T b8cff226 u_ts_height 17 P 3733fc38 impostor.vert impostor.frag B 3733fc38 vert 0 232 U 3733fc38 vert u_view 0 mat4 1 0 @@ -1306,7 +1321,7 @@ T 4d4fa91b u_tershadow 8 T 4d4fa91b u_tershadow_aux 9 T 4d4fa91b u_ts_height 10 P 9dce3246 model.vert bake.frag -B 9dce3246 vert 0 236 +B 9dce3246 vert 0 264 U 9dce3246 vert u_view 0 mat4 1 0 U 9dce3246 vert u_proj 64 mat4 1 0 U 9dce3246 vert u_light_vp 128 mat4 1 0 @@ -1317,8 +1332,10 @@ U 9dce3246 vert u_card_h 204 float 1 0 U 9dce3246 vert u_ground 208 float 1 0 U 9dce3246 vert u_ts_origin 216 vec2 1 0 U 9dce3246 vert u_ts_half 224 float 1 0 -U 9dce3246 vert u_model_h 228 float 1 0 -U 9dce3246 vert u_flutter 232 float 1 0 +U 9dce3246 vert u_tp_on 228 float 1 0 +U 9dce3246 vert u_tp_dims 240 vec4 1 0 +U 9dce3246 vert u_model_h 256 float 1 0 +U 9dce3246 vert u_flutter 260 float 1 0 I 9dce3246 i_rot 4 vec4 I 9dce3246 a_pos 0 vec3 I 9dce3246 i_pos 3 vec4 @@ -1369,9 +1386,11 @@ T 9dce3246 u_prefilter 6 T 9dce3246 u_shadow 7 T 9dce3246 u_tershadow 8 T 9dce3246 u_tershadow_aux 9 -T 9dce3246 u_ts_height 10 +T 9dce3246 u_tp_h 10 +T 9dce3246 u_tp_page 11 +T 9dce3246 u_ts_height 12 P 439a2253 model.vert bake.frag #define CARD; -B 439a2253 vert 0 236 +B 439a2253 vert 0 264 U 439a2253 vert u_view 0 mat4 1 0 U 439a2253 vert u_proj 64 mat4 1 0 U 439a2253 vert u_light_vp 128 mat4 1 0 @@ -1382,8 +1401,10 @@ U 439a2253 vert u_card_h 204 float 1 0 U 439a2253 vert u_ground 208 float 1 0 U 439a2253 vert u_ts_origin 216 vec2 1 0 U 439a2253 vert u_ts_half 224 float 1 0 -U 439a2253 vert u_model_h 228 float 1 0 -U 439a2253 vert u_flutter 232 float 1 0 +U 439a2253 vert u_tp_on 228 float 1 0 +U 439a2253 vert u_tp_dims 240 vec4 1 0 +U 439a2253 vert u_model_h 256 float 1 0 +U 439a2253 vert u_flutter 260 float 1 0 I 439a2253 i_rot 4 vec4 I 439a2253 a_pos 0 vec3 I 439a2253 i_pos 3 vec4 @@ -1434,9 +1455,11 @@ T 439a2253 u_prefilter 6 T 439a2253 u_shadow 7 T 439a2253 u_tershadow 8 T 439a2253 u_tershadow_aux 9 -T 439a2253 u_ts_height 10 +T 439a2253 u_tp_h 10 +T 439a2253 u_tp_page 11 +T 439a2253 u_ts_height 12 P 86f427b0 model.vert bake.frag #define FLOWER; -B 86f427b0 vert 0 236 +B 86f427b0 vert 0 264 U 86f427b0 vert u_view 0 mat4 1 0 U 86f427b0 vert u_proj 64 mat4 1 0 U 86f427b0 vert u_light_vp 128 mat4 1 0 @@ -1447,8 +1470,10 @@ U 86f427b0 vert u_card_h 204 float 1 0 U 86f427b0 vert u_ground 208 float 1 0 U 86f427b0 vert u_ts_origin 216 vec2 1 0 U 86f427b0 vert u_ts_half 224 float 1 0 -U 86f427b0 vert u_model_h 228 float 1 0 -U 86f427b0 vert u_flutter 232 float 1 0 +U 86f427b0 vert u_tp_on 228 float 1 0 +U 86f427b0 vert u_tp_dims 240 vec4 1 0 +U 86f427b0 vert u_model_h 256 float 1 0 +U 86f427b0 vert u_flutter 260 float 1 0 I 86f427b0 i_rot 4 vec4 I 86f427b0 a_pos 0 vec3 I 86f427b0 i_pos 3 vec4 @@ -1499,9 +1524,11 @@ T 86f427b0 u_prefilter 6 T 86f427b0 u_shadow 7 T 86f427b0 u_tershadow 8 T 86f427b0 u_tershadow_aux 9 -T 86f427b0 u_ts_height 10 +T 86f427b0 u_tp_h 10 +T 86f427b0 u_tp_page 11 +T 86f427b0 u_ts_height 12 P bfa15301 model.vert depth.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST; -B bfa15301 vert 0 236 +B bfa15301 vert 0 264 U bfa15301 vert u_view 0 mat4 1 0 U bfa15301 vert u_proj 64 mat4 1 0 U bfa15301 vert u_light_vp 128 mat4 1 0 @@ -1512,8 +1539,10 @@ U bfa15301 vert u_card_h 204 float 1 0 U bfa15301 vert u_ground 208 float 1 0 U bfa15301 vert u_ts_origin 216 vec2 1 0 U bfa15301 vert u_ts_half 224 float 1 0 -U bfa15301 vert u_model_h 228 float 1 0 -U bfa15301 vert u_flutter 232 float 1 0 +U bfa15301 vert u_tp_on 228 float 1 0 +U bfa15301 vert u_tp_dims 240 vec4 1 0 +U bfa15301 vert u_model_h 256 float 1 0 +U bfa15301 vert u_flutter 260 float 1 0 I bfa15301 i_rot 4 vec4 I bfa15301 a_pos 0 vec3 I bfa15301 i_pos 3 vec4 @@ -1563,9 +1592,11 @@ T bfa15301 u_prefilter 5 T bfa15301 u_shadow 6 T bfa15301 u_tershadow 7 T bfa15301 u_tershadow_aux 8 -T bfa15301 u_ts_height 9 +T bfa15301 u_tp_h 9 +T bfa15301 u_tp_page 10 +T bfa15301 u_ts_height 11 P 266d2401 model.vert depth.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE; -B 266d2401 vert 0 236 +B 266d2401 vert 0 264 U 266d2401 vert u_view 0 mat4 1 0 U 266d2401 vert u_proj 64 mat4 1 0 U 266d2401 vert u_light_vp 128 mat4 1 0 @@ -1576,8 +1607,10 @@ U 266d2401 vert u_card_h 204 float 1 0 U 266d2401 vert u_ground 208 float 1 0 U 266d2401 vert u_ts_origin 216 vec2 1 0 U 266d2401 vert u_ts_half 224 float 1 0 -U 266d2401 vert u_model_h 228 float 1 0 -U 266d2401 vert u_flutter 232 float 1 0 +U 266d2401 vert u_tp_on 228 float 1 0 +U 266d2401 vert u_tp_dims 240 vec4 1 0 +U 266d2401 vert u_model_h 256 float 1 0 +U 266d2401 vert u_flutter 260 float 1 0 I 266d2401 i_rot 4 vec4 I 266d2401 a_pos 0 vec3 I 266d2401 i_pos 3 vec4 @@ -1627,9 +1660,11 @@ T 266d2401 u_prefilter 5 T 266d2401 u_shadow 6 T 266d2401 u_tershadow 7 T 266d2401 u_tershadow_aux 8 -T 266d2401 u_ts_height 9 +T 266d2401 u_tp_h 9 +T 266d2401 u_tp_page 10 +T 266d2401 u_ts_height 11 P 01089da8 model.vert model.frag -B 01089da8 vert 0 236 +B 01089da8 vert 0 264 U 01089da8 vert u_view 0 mat4 1 0 U 01089da8 vert u_proj 64 mat4 1 0 U 01089da8 vert u_light_vp 128 mat4 1 0 @@ -1640,8 +1675,10 @@ U 01089da8 vert u_card_h 204 float 1 0 U 01089da8 vert u_ground 208 float 1 0 U 01089da8 vert u_ts_origin 216 vec2 1 0 U 01089da8 vert u_ts_half 224 float 1 0 -U 01089da8 vert u_model_h 228 float 1 0 -U 01089da8 vert u_flutter 232 float 1 0 +U 01089da8 vert u_tp_on 228 float 1 0 +U 01089da8 vert u_tp_dims 240 vec4 1 0 +U 01089da8 vert u_model_h 256 float 1 0 +U 01089da8 vert u_flutter 260 float 1 0 I 01089da8 i_rot 4 vec4 I 01089da8 a_pos 0 vec3 I 01089da8 i_pos 3 vec4 @@ -1699,9 +1736,11 @@ T 01089da8 u_prefilter 8 T 01089da8 u_shadow 9 T 01089da8 u_tershadow 10 T 01089da8 u_tershadow_aux 11 -T 01089da8 u_ts_height 12 +T 01089da8 u_tp_h 12 +T 01089da8 u_tp_page 13 +T 01089da8 u_ts_height 14 P 254e7887 model.vert model.frag #define CARD;#define CHEAP; -B 254e7887 vert 0 236 +B 254e7887 vert 0 264 U 254e7887 vert u_view 0 mat4 1 0 U 254e7887 vert u_proj 64 mat4 1 0 U 254e7887 vert u_light_vp 128 mat4 1 0 @@ -1712,8 +1751,10 @@ U 254e7887 vert u_card_h 204 float 1 0 U 254e7887 vert u_ground 208 float 1 0 U 254e7887 vert u_ts_origin 216 vec2 1 0 U 254e7887 vert u_ts_half 224 float 1 0 -U 254e7887 vert u_model_h 228 float 1 0 -U 254e7887 vert u_flutter 232 float 1 0 +U 254e7887 vert u_tp_on 228 float 1 0 +U 254e7887 vert u_tp_dims 240 vec4 1 0 +U 254e7887 vert u_model_h 256 float 1 0 +U 254e7887 vert u_flutter 260 float 1 0 I 254e7887 i_rot 4 vec4 I 254e7887 a_pos 0 vec3 I 254e7887 i_pos 3 vec4 @@ -1772,9 +1813,11 @@ T 254e7887 u_prefilter 8 T 254e7887 u_shadow 9 T 254e7887 u_tershadow 10 T 254e7887 u_tershadow_aux 11 -T 254e7887 u_ts_height 12 +T 254e7887 u_tp_h 12 +T 254e7887 u_tp_page 13 +T 254e7887 u_ts_height 14 P 1ce5c5d1 model.vert model.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST; -B 1ce5c5d1 vert 0 236 +B 1ce5c5d1 vert 0 264 U 1ce5c5d1 vert u_view 0 mat4 1 0 U 1ce5c5d1 vert u_proj 64 mat4 1 0 U 1ce5c5d1 vert u_light_vp 128 mat4 1 0 @@ -1785,8 +1828,10 @@ U 1ce5c5d1 vert u_card_h 204 float 1 0 U 1ce5c5d1 vert u_ground 208 float 1 0 U 1ce5c5d1 vert u_ts_origin 216 vec2 1 0 U 1ce5c5d1 vert u_ts_half 224 float 1 0 -U 1ce5c5d1 vert u_model_h 228 float 1 0 -U 1ce5c5d1 vert u_flutter 232 float 1 0 +U 1ce5c5d1 vert u_tp_on 228 float 1 0 +U 1ce5c5d1 vert u_tp_dims 240 vec4 1 0 +U 1ce5c5d1 vert u_model_h 256 float 1 0 +U 1ce5c5d1 vert u_flutter 260 float 1 0 I 1ce5c5d1 i_rot 4 vec4 I 1ce5c5d1 a_pos 0 vec3 I 1ce5c5d1 i_pos 3 vec4 @@ -1844,9 +1889,11 @@ T 1ce5c5d1 u_prefilter 8 T 1ce5c5d1 u_shadow 9 T 1ce5c5d1 u_tershadow 10 T 1ce5c5d1 u_tershadow_aux 11 -T 1ce5c5d1 u_ts_height 12 +T 1ce5c5d1 u_tp_h 12 +T 1ce5c5d1 u_tp_page 13 +T 1ce5c5d1 u_ts_height 14 P 5e70d731 model.vert model.frag #define FOLIAGE;#define WIND;#define ALPHA_TEST;#define NEAR_FADE; -B 5e70d731 vert 0 236 +B 5e70d731 vert 0 264 U 5e70d731 vert u_view 0 mat4 1 0 U 5e70d731 vert u_proj 64 mat4 1 0 U 5e70d731 vert u_light_vp 128 mat4 1 0 @@ -1857,8 +1904,10 @@ U 5e70d731 vert u_card_h 204 float 1 0 U 5e70d731 vert u_ground 208 float 1 0 U 5e70d731 vert u_ts_origin 216 vec2 1 0 U 5e70d731 vert u_ts_half 224 float 1 0 -U 5e70d731 vert u_model_h 228 float 1 0 -U 5e70d731 vert u_flutter 232 float 1 0 +U 5e70d731 vert u_tp_on 228 float 1 0 +U 5e70d731 vert u_tp_dims 240 vec4 1 0 +U 5e70d731 vert u_model_h 256 float 1 0 +U 5e70d731 vert u_flutter 260 float 1 0 I 5e70d731 i_rot 4 vec4 I 5e70d731 a_pos 0 vec3 I 5e70d731 i_pos 3 vec4 @@ -1916,9 +1965,11 @@ T 5e70d731 u_prefilter 8 T 5e70d731 u_shadow 9 T 5e70d731 u_tershadow 10 T 5e70d731 u_tershadow_aux 11 -T 5e70d731 u_ts_height 12 +T 5e70d731 u_tp_h 12 +T 5e70d731 u_tp_page 13 +T 5e70d731 u_ts_height 14 P f4a2b586 model.vert model.frag #define FOLIAGE;#define WIND;#define BLADE; -B f4a2b586 vert 0 252 +B f4a2b586 vert 0 280 U f4a2b586 vert u_view 0 mat4 1 0 U f4a2b586 vert u_proj 64 mat4 1 0 U f4a2b586 vert u_light_vp 128 mat4 1 0 @@ -1931,8 +1982,10 @@ U f4a2b586 vert u_cull 220 float 1 0 U f4a2b586 vert u_ground 224 float 1 0 U f4a2b586 vert u_ts_origin 232 vec2 1 0 U f4a2b586 vert u_ts_half 240 float 1 0 -U f4a2b586 vert u_model_h 244 float 1 0 -U f4a2b586 vert u_flutter 248 float 1 0 +U f4a2b586 vert u_tp_on 244 float 1 0 +U f4a2b586 vert u_tp_dims 256 vec4 1 0 +U f4a2b586 vert u_model_h 272 float 1 0 +U f4a2b586 vert u_flutter 276 float 1 0 I f4a2b586 i_rot 4 vec4 I f4a2b586 a_pos 0 vec3 I f4a2b586 i_pos 3 vec4 @@ -1994,9 +2047,11 @@ T f4a2b586 u_prefilter 8 T f4a2b586 u_shadow 9 T f4a2b586 u_tershadow 10 T f4a2b586 u_tershadow_aux 11 -T f4a2b586 u_ts_height 12 +T f4a2b586 u_tp_h 12 +T f4a2b586 u_tp_page 13 +T f4a2b586 u_ts_height 14 P b4587cd4 model.vert model.frag #define FOLIAGE;#define WIND;#define CARD; -B b4587cd4 vert 0 236 +B b4587cd4 vert 0 264 U b4587cd4 vert u_view 0 mat4 1 0 U b4587cd4 vert u_proj 64 mat4 1 0 U b4587cd4 vert u_light_vp 128 mat4 1 0 @@ -2007,8 +2062,10 @@ U b4587cd4 vert u_card_h 204 float 1 0 U b4587cd4 vert u_ground 208 float 1 0 U b4587cd4 vert u_ts_origin 216 vec2 1 0 U b4587cd4 vert u_ts_half 224 float 1 0 -U b4587cd4 vert u_model_h 228 float 1 0 -U b4587cd4 vert u_flutter 232 float 1 0 +U b4587cd4 vert u_tp_on 228 float 1 0 +U b4587cd4 vert u_tp_dims 240 vec4 1 0 +U b4587cd4 vert u_model_h 256 float 1 0 +U b4587cd4 vert u_flutter 260 float 1 0 I b4587cd4 i_rot 4 vec4 I b4587cd4 a_pos 0 vec3 I b4587cd4 i_pos 3 vec4 @@ -2067,9 +2124,11 @@ T b4587cd4 u_prefilter 8 T b4587cd4 u_shadow 9 T b4587cd4 u_tershadow 10 T b4587cd4 u_tershadow_aux 11 -T b4587cd4 u_ts_height 12 +T b4587cd4 u_tp_h 12 +T b4587cd4 u_tp_page 13 +T b4587cd4 u_ts_height 14 P 4dda406a model.vert model.frag #define FOLIAGE;#define WIND;#define EQ_PASS; -B 4dda406a vert 0 236 +B 4dda406a vert 0 264 U 4dda406a vert u_view 0 mat4 1 0 U 4dda406a vert u_proj 64 mat4 1 0 U 4dda406a vert u_light_vp 128 mat4 1 0 @@ -2080,8 +2139,10 @@ U 4dda406a vert u_card_h 204 float 1 0 U 4dda406a vert u_ground 208 float 1 0 U 4dda406a vert u_ts_origin 216 vec2 1 0 U 4dda406a vert u_ts_half 224 float 1 0 -U 4dda406a vert u_model_h 228 float 1 0 -U 4dda406a vert u_flutter 232 float 1 0 +U 4dda406a vert u_tp_on 228 float 1 0 +U 4dda406a vert u_tp_dims 240 vec4 1 0 +U 4dda406a vert u_model_h 256 float 1 0 +U 4dda406a vert u_flutter 260 float 1 0 I 4dda406a i_rot 4 vec4 I 4dda406a a_pos 0 vec3 I 4dda406a i_pos 3 vec4 @@ -2139,9 +2200,11 @@ T 4dda406a u_prefilter 8 T 4dda406a u_shadow 9 T 4dda406a u_tershadow 10 T 4dda406a u_tershadow_aux 11 -T 4dda406a u_ts_height 12 +T 4dda406a u_tp_h 12 +T 4dda406a u_tp_page 13 +T 4dda406a u_ts_height 14 P b603a056 model.vert model.frag #define FOLIAGE;#define WIND;#define EQ_PASS;#define NEAR_FADE; -B b603a056 vert 0 236 +B b603a056 vert 0 264 U b603a056 vert u_view 0 mat4 1 0 U b603a056 vert u_proj 64 mat4 1 0 U b603a056 vert u_light_vp 128 mat4 1 0 @@ -2152,8 +2215,10 @@ U b603a056 vert u_card_h 204 float 1 0 U b603a056 vert u_ground 208 float 1 0 U b603a056 vert u_ts_origin 216 vec2 1 0 U b603a056 vert u_ts_half 224 float 1 0 -U b603a056 vert u_model_h 228 float 1 0 -U b603a056 vert u_flutter 232 float 1 0 +U b603a056 vert u_tp_on 228 float 1 0 +U b603a056 vert u_tp_dims 240 vec4 1 0 +U b603a056 vert u_model_h 256 float 1 0 +U b603a056 vert u_flutter 260 float 1 0 I b603a056 i_rot 4 vec4 I b603a056 a_pos 0 vec3 I b603a056 i_pos 3 vec4 @@ -2211,9 +2276,11 @@ T b603a056 u_prefilter 8 T b603a056 u_shadow 9 T b603a056 u_tershadow 10 T b603a056 u_tershadow_aux 11 -T b603a056 u_ts_height 12 +T b603a056 u_tp_h 12 +T b603a056 u_tp_page 13 +T b603a056 u_ts_height 14 P 3b183d8f model.vert model.frag #define FOLIAGE;#define WIND;#define FLOWER; -B 3b183d8f vert 0 236 +B 3b183d8f vert 0 264 U 3b183d8f vert u_view 0 mat4 1 0 U 3b183d8f vert u_proj 64 mat4 1 0 U 3b183d8f vert u_light_vp 128 mat4 1 0 @@ -2224,8 +2291,10 @@ U 3b183d8f vert u_card_h 204 float 1 0 U 3b183d8f vert u_ground 208 float 1 0 U 3b183d8f vert u_ts_origin 216 vec2 1 0 U 3b183d8f vert u_ts_half 224 float 1 0 -U 3b183d8f vert u_model_h 228 float 1 0 -U 3b183d8f vert u_flutter 232 float 1 0 +U 3b183d8f vert u_tp_on 228 float 1 0 +U 3b183d8f vert u_tp_dims 240 vec4 1 0 +U 3b183d8f vert u_model_h 256 float 1 0 +U 3b183d8f vert u_flutter 260 float 1 0 I 3b183d8f i_rot 4 vec4 I 3b183d8f a_pos 0 vec3 I 3b183d8f i_pos 3 vec4 @@ -2283,9 +2352,11 @@ T 3b183d8f u_prefilter 8 T 3b183d8f u_shadow 9 T 3b183d8f u_tershadow 10 T 3b183d8f u_tershadow_aux 11 -T 3b183d8f u_ts_height 12 +T 3b183d8f u_tp_h 12 +T 3b183d8f u_tp_page 13 +T 3b183d8f u_ts_height 14 P 79b77ed9 model.vert model.frag #define SHADOW_PASS;#define WIND;#define CARD; -B 79b77ed9 vert 0 236 +B 79b77ed9 vert 0 264 U 79b77ed9 vert u_view 0 mat4 1 0 U 79b77ed9 vert u_proj 64 mat4 1 0 U 79b77ed9 vert u_light_vp 128 mat4 1 0 @@ -2296,8 +2367,10 @@ U 79b77ed9 vert u_card_h 204 float 1 0 U 79b77ed9 vert u_ground 208 float 1 0 U 79b77ed9 vert u_ts_origin 216 vec2 1 0 U 79b77ed9 vert u_ts_half 224 float 1 0 -U 79b77ed9 vert u_model_h 228 float 1 0 -U 79b77ed9 vert u_flutter 232 float 1 0 +U 79b77ed9 vert u_tp_on 228 float 1 0 +U 79b77ed9 vert u_tp_dims 240 vec4 1 0 +U 79b77ed9 vert u_model_h 256 float 1 0 +U 79b77ed9 vert u_flutter 260 float 1 0 I 79b77ed9 i_rot 4 vec4 I 79b77ed9 a_pos 0 vec3 I 79b77ed9 i_pos 3 vec4 @@ -2356,9 +2429,11 @@ T 79b77ed9 u_prefilter 8 T 79b77ed9 u_shadow 9 T 79b77ed9 u_tershadow 10 T 79b77ed9 u_tershadow_aux 11 -T 79b77ed9 u_ts_height 12 +T 79b77ed9 u_tp_h 12 +T 79b77ed9 u_tp_page 13 +T 79b77ed9 u_ts_height 14 P b9fc2041 model.vert model.frag #define WIND; -B b9fc2041 vert 0 236 +B b9fc2041 vert 0 264 U b9fc2041 vert u_view 0 mat4 1 0 U b9fc2041 vert u_proj 64 mat4 1 0 U b9fc2041 vert u_light_vp 128 mat4 1 0 @@ -2369,8 +2444,10 @@ U b9fc2041 vert u_card_h 204 float 1 0 U b9fc2041 vert u_ground 208 float 1 0 U b9fc2041 vert u_ts_origin 216 vec2 1 0 U b9fc2041 vert u_ts_half 224 float 1 0 -U b9fc2041 vert u_model_h 228 float 1 0 -U b9fc2041 vert u_flutter 232 float 1 0 +U b9fc2041 vert u_tp_on 228 float 1 0 +U b9fc2041 vert u_tp_dims 240 vec4 1 0 +U b9fc2041 vert u_model_h 256 float 1 0 +U b9fc2041 vert u_flutter 260 float 1 0 I b9fc2041 i_rot 4 vec4 I b9fc2041 a_pos 0 vec3 I b9fc2041 i_pos 3 vec4 @@ -2428,9 +2505,11 @@ T b9fc2041 u_prefilter 8 T b9fc2041 u_shadow 9 T b9fc2041 u_tershadow 10 T b9fc2041 u_tershadow_aux 11 -T b9fc2041 u_ts_height 12 +T b9fc2041 u_tp_h 12 +T b9fc2041 u_tp_page 13 +T b9fc2041 u_ts_height 14 P f34dce08 model.vert shadow.frag #define SHADOW_PASS; -B f34dce08 vert 0 236 +B f34dce08 vert 0 264 U f34dce08 vert u_view 0 mat4 1 0 U f34dce08 vert u_proj 64 mat4 1 0 U f34dce08 vert u_light_vp 128 mat4 1 0 @@ -2441,8 +2520,10 @@ U f34dce08 vert u_card_h 204 float 1 0 U f34dce08 vert u_ground 208 float 1 0 U f34dce08 vert u_ts_origin 216 vec2 1 0 U f34dce08 vert u_ts_half 224 float 1 0 -U f34dce08 vert u_model_h 228 float 1 0 -U f34dce08 vert u_flutter 232 float 1 0 +U f34dce08 vert u_tp_on 228 float 1 0 +U f34dce08 vert u_tp_dims 240 vec4 1 0 +U f34dce08 vert u_model_h 256 float 1 0 +U f34dce08 vert u_flutter 260 float 1 0 I f34dce08 i_rot 4 vec4 I f34dce08 a_pos 0 vec3 I f34dce08 i_pos 3 vec4 @@ -2492,9 +2573,11 @@ T f34dce08 u_prefilter 5 T f34dce08 u_shadow 6 T f34dce08 u_tershadow 7 T f34dce08 u_tershadow_aux 8 -T f34dce08 u_ts_height 9 +T f34dce08 u_tp_h 9 +T f34dce08 u_tp_page 10 +T f34dce08 u_ts_height 11 P 512004a1 model.vert shadow.frag #define SHADOW_PASS;#define WIND; -B 512004a1 vert 0 236 +B 512004a1 vert 0 264 U 512004a1 vert u_view 0 mat4 1 0 U 512004a1 vert u_proj 64 mat4 1 0 U 512004a1 vert u_light_vp 128 mat4 1 0 @@ -2505,8 +2588,10 @@ U 512004a1 vert u_card_h 204 float 1 0 U 512004a1 vert u_ground 208 float 1 0 U 512004a1 vert u_ts_origin 216 vec2 1 0 U 512004a1 vert u_ts_half 224 float 1 0 -U 512004a1 vert u_model_h 228 float 1 0 -U 512004a1 vert u_flutter 232 float 1 0 +U 512004a1 vert u_tp_on 228 float 1 0 +U 512004a1 vert u_tp_dims 240 vec4 1 0 +U 512004a1 vert u_model_h 256 float 1 0 +U 512004a1 vert u_flutter 260 float 1 0 I 512004a1 i_rot 4 vec4 I 512004a1 a_pos 0 vec3 I 512004a1 i_pos 3 vec4 @@ -2556,9 +2641,11 @@ T 512004a1 u_prefilter 5 T 512004a1 u_shadow 6 T 512004a1 u_tershadow 7 T 512004a1 u_tershadow_aux 8 -T 512004a1 u_ts_height 9 +T 512004a1 u_tp_h 9 +T 512004a1 u_tp_page 10 +T 512004a1 u_ts_height 11 P b964507f model.vert shadow.frag #define SHADOW_PASS;#define WIND;#define ALPHA_TEST; -B b964507f vert 0 236 +B b964507f vert 0 264 U b964507f vert u_view 0 mat4 1 0 U b964507f vert u_proj 64 mat4 1 0 U b964507f vert u_light_vp 128 mat4 1 0 @@ -2569,8 +2656,10 @@ U b964507f vert u_card_h 204 float 1 0 U b964507f vert u_ground 208 float 1 0 U b964507f vert u_ts_origin 216 vec2 1 0 U b964507f vert u_ts_half 224 float 1 0 -U b964507f vert u_model_h 228 float 1 0 -U b964507f vert u_flutter 232 float 1 0 +U b964507f vert u_tp_on 228 float 1 0 +U b964507f vert u_tp_dims 240 vec4 1 0 +U b964507f vert u_model_h 256 float 1 0 +U b964507f vert u_flutter 260 float 1 0 I b964507f i_rot 4 vec4 I b964507f a_pos 0 vec3 I b964507f i_pos 3 vec4 @@ -2620,7 +2709,9 @@ T b964507f u_prefilter 5 T b964507f u_shadow 6 T b964507f u_tershadow 7 T b964507f u_tershadow_aux 8 -T b964507f u_ts_height 9 +T b964507f u_tp_h 9 +T b964507f u_tp_page 10 +T b964507f u_ts_height 11 P 1b654972 overlay.vert overlay.frag B 1b654972 vert 0 8 U 1b654972 vert u_screen 0 vec2 1 0 @@ -3096,7 +3187,7 @@ T a64015f2 u_tershadow 7 T a64015f2 u_tershadow_aux 8 T a64015f2 u_ts_height 9 P 2e2908c8 terrain.vert terrain.frag -B 2e2908c8 vert 0 204 +B 2e2908c8 vert 0 224 U 2e2908c8 vert u_half 0 float 1 0 U 2e2908c8 vert u_view 16 mat4 1 0 U 2e2908c8 vert u_proj 80 mat4 1 0 @@ -3105,8 +3196,10 @@ U 2e2908c8 vert u_cam_pos 160 vec3 1 0 U 2e2908c8 vert u_node 176 vec3 1 0 U 2e2908c8 vert u_morph 192 vec2 1 0 U 2e2908c8 vert u_grid 200 float 1 0 +U 2e2908c8 vert u_tp_on 204 float 1 0 +U 2e2908c8 vert u_tp_dims 208 vec4 1 0 I 2e2908c8 a_xz 0 vec2 -B 2e2908c8 frag 1 968 +B 2e2908c8 frag 1 1000 U 2e2908c8 frag u_cascade_vp 0 mat4 5 64 U 2e2908c8 frag u_cascade_split 320 float 5 16 U 2e2908c8 frag u_cascade_range 400 float 5 16 @@ -3146,16 +3239,18 @@ U 2e2908c8 frag u_fog_wall 828 float 1 0 U 2e2908c8 frag u_half 832 float 1 0 U 2e2908c8 frag u_texel 836 float 1 0 U 2e2908c8 frag u_ortho_on 840 float 1 0 -U 2e2908c8 frag u_carpet_on 844 float 1 0 -U 2e2908c8 frag u_snow_line 848 float 1 0 -U 2e2908c8 frag u_lake_level 852 float 1 0 -U 2e2908c8 frag u_wet 856 float 1 0 -U 2e2908c8 frag u_sea_level 860 float 1 0 -U 2e2908c8 frag u_lake 864 vec4 1 0 -U 2e2908c8 frag u_origin 880 vec2 1 0 -U 2e2908c8 frag u_view 896 mat4 1 0 -U 2e2908c8 frag u_far_split 960 float 1 0 -U 2e2908c8 frag u_far_band 964 float 1 0 +U 2e2908c8 frag u_tp_on 844 float 1 0 +U 2e2908c8 frag u_tp_dims 848 vec4 1 0 +U 2e2908c8 frag u_carpet_on 864 float 1 0 +U 2e2908c8 frag u_snow_line 868 float 1 0 +U 2e2908c8 frag u_lake_level 872 float 1 0 +U 2e2908c8 frag u_wet 876 float 1 0 +U 2e2908c8 frag u_sea_level 880 float 1 0 +U 2e2908c8 frag u_lake 896 vec4 1 0 +U 2e2908c8 frag u_origin 912 vec2 1 0 +U 2e2908c8 frag u_view 928 mat4 1 0 +U 2e2908c8 frag u_far_split 992 float 1 0 +U 2e2908c8 frag u_far_band 996 float 1 0 T 2e2908c8 u_brdf 2 T 2e2908c8 u_carpet 3 T 2e2908c8 u_grass_a 4 @@ -3174,9 +3269,13 @@ T 2e2908c8 u_sunshadow 16 T 2e2908c8 u_ter_normal 17 T 2e2908c8 u_tershadow 18 T 2e2908c8 u_tershadow_aux 19 -T 2e2908c8 u_ts_height 20 +T 2e2908c8 u_tp_h 20 +T 2e2908c8 u_tp_nrm 21 +T 2e2908c8 u_tp_ortho 22 +T 2e2908c8 u_tp_page 23 +T 2e2908c8 u_ts_height 24 P 1f95f31b terrain.vert terrain.frag #define FAR_ONLY; -B 1f95f31b vert 0 204 +B 1f95f31b vert 0 224 U 1f95f31b vert u_half 0 float 1 0 U 1f95f31b vert u_view 16 mat4 1 0 U 1f95f31b vert u_proj 80 mat4 1 0 @@ -3185,8 +3284,10 @@ U 1f95f31b vert u_cam_pos 160 vec3 1 0 U 1f95f31b vert u_node 176 vec3 1 0 U 1f95f31b vert u_morph 192 vec2 1 0 U 1f95f31b vert u_grid 200 float 1 0 +U 1f95f31b vert u_tp_on 204 float 1 0 +U 1f95f31b vert u_tp_dims 208 vec4 1 0 I 1f95f31b a_xz 0 vec2 -B 1f95f31b frag 1 968 +B 1f95f31b frag 1 1000 U 1f95f31b frag u_cascade_vp 0 mat4 5 64 U 1f95f31b frag u_cascade_split 320 float 5 16 U 1f95f31b frag u_cascade_range 400 float 5 16 @@ -3226,16 +3327,18 @@ U 1f95f31b frag u_fog_wall 828 float 1 0 U 1f95f31b frag u_half 832 float 1 0 U 1f95f31b frag u_texel 836 float 1 0 U 1f95f31b frag u_ortho_on 840 float 1 0 -U 1f95f31b frag u_carpet_on 844 float 1 0 -U 1f95f31b frag u_snow_line 848 float 1 0 -U 1f95f31b frag u_lake_level 852 float 1 0 -U 1f95f31b frag u_wet 856 float 1 0 -U 1f95f31b frag u_sea_level 860 float 1 0 -U 1f95f31b frag u_lake 864 vec4 1 0 -U 1f95f31b frag u_origin 880 vec2 1 0 -U 1f95f31b frag u_view 896 mat4 1 0 -U 1f95f31b frag u_far_split 960 float 1 0 -U 1f95f31b frag u_far_band 964 float 1 0 +U 1f95f31b frag u_tp_on 844 float 1 0 +U 1f95f31b frag u_tp_dims 848 vec4 1 0 +U 1f95f31b frag u_carpet_on 864 float 1 0 +U 1f95f31b frag u_snow_line 868 float 1 0 +U 1f95f31b frag u_lake_level 872 float 1 0 +U 1f95f31b frag u_wet 876 float 1 0 +U 1f95f31b frag u_sea_level 880 float 1 0 +U 1f95f31b frag u_lake 896 vec4 1 0 +U 1f95f31b frag u_origin 912 vec2 1 0 +U 1f95f31b frag u_view 928 mat4 1 0 +U 1f95f31b frag u_far_split 992 float 1 0 +U 1f95f31b frag u_far_band 996 float 1 0 T 1f95f31b u_brdf 2 T 1f95f31b u_carpet 3 T 1f95f31b u_grass_a 4 @@ -3254,9 +3357,13 @@ T 1f95f31b u_sunshadow 16 T 1f95f31b u_ter_normal 17 T 1f95f31b u_tershadow 18 T 1f95f31b u_tershadow_aux 19 -T 1f95f31b u_ts_height 20 +T 1f95f31b u_tp_h 20 +T 1f95f31b u_tp_nrm 21 +T 1f95f31b u_tp_ortho 22 +T 1f95f31b u_tp_page 23 +T 1f95f31b u_ts_height 24 P f4382918 terrain.vert terrain.frag #define NEAR_ONLY; -B f4382918 vert 0 204 +B f4382918 vert 0 224 U f4382918 vert u_half 0 float 1 0 U f4382918 vert u_view 16 mat4 1 0 U f4382918 vert u_proj 80 mat4 1 0 @@ -3265,8 +3372,10 @@ U f4382918 vert u_cam_pos 160 vec3 1 0 U f4382918 vert u_node 176 vec3 1 0 U f4382918 vert u_morph 192 vec2 1 0 U f4382918 vert u_grid 200 float 1 0 +U f4382918 vert u_tp_on 204 float 1 0 +U f4382918 vert u_tp_dims 208 vec4 1 0 I f4382918 a_xz 0 vec2 -B f4382918 frag 1 968 +B f4382918 frag 1 1000 U f4382918 frag u_cascade_vp 0 mat4 5 64 U f4382918 frag u_cascade_split 320 float 5 16 U f4382918 frag u_cascade_range 400 float 5 16 @@ -3306,16 +3415,18 @@ U f4382918 frag u_fog_wall 828 float 1 0 U f4382918 frag u_half 832 float 1 0 U f4382918 frag u_texel 836 float 1 0 U f4382918 frag u_ortho_on 840 float 1 0 -U f4382918 frag u_carpet_on 844 float 1 0 -U f4382918 frag u_snow_line 848 float 1 0 -U f4382918 frag u_lake_level 852 float 1 0 -U f4382918 frag u_wet 856 float 1 0 -U f4382918 frag u_sea_level 860 float 1 0 -U f4382918 frag u_lake 864 vec4 1 0 -U f4382918 frag u_origin 880 vec2 1 0 -U f4382918 frag u_view 896 mat4 1 0 -U f4382918 frag u_far_split 960 float 1 0 -U f4382918 frag u_far_band 964 float 1 0 +U f4382918 frag u_tp_on 844 float 1 0 +U f4382918 frag u_tp_dims 848 vec4 1 0 +U f4382918 frag u_carpet_on 864 float 1 0 +U f4382918 frag u_snow_line 868 float 1 0 +U f4382918 frag u_lake_level 872 float 1 0 +U f4382918 frag u_wet 876 float 1 0 +U f4382918 frag u_sea_level 880 float 1 0 +U f4382918 frag u_lake 896 vec4 1 0 +U f4382918 frag u_origin 912 vec2 1 0 +U f4382918 frag u_view 928 mat4 1 0 +U f4382918 frag u_far_split 992 float 1 0 +U f4382918 frag u_far_band 996 float 1 0 T f4382918 u_brdf 2 T f4382918 u_carpet 3 T f4382918 u_grass_a 4 @@ -3334,9 +3445,13 @@ T f4382918 u_sunshadow 16 T f4382918 u_ter_normal 17 T f4382918 u_tershadow 18 T f4382918 u_tershadow_aux 19 -T f4382918 u_ts_height 20 +T f4382918 u_tp_h 20 +T f4382918 u_tp_nrm 21 +T f4382918 u_tp_ortho 22 +T f4382918 u_tp_page 23 +T f4382918 u_ts_height 24 P f2952361 terrain.vert terrain.frag #define SUN_INLINE; -B f2952361 vert 0 204 +B f2952361 vert 0 224 U f2952361 vert u_half 0 float 1 0 U f2952361 vert u_view 16 mat4 1 0 U f2952361 vert u_proj 80 mat4 1 0 @@ -3345,8 +3460,10 @@ U f2952361 vert u_cam_pos 160 vec3 1 0 U f2952361 vert u_node 176 vec3 1 0 U f2952361 vert u_morph 192 vec2 1 0 U f2952361 vert u_grid 200 float 1 0 +U f2952361 vert u_tp_on 204 float 1 0 +U f2952361 vert u_tp_dims 208 vec4 1 0 I f2952361 a_xz 0 vec2 -B f2952361 frag 1 968 +B f2952361 frag 1 1000 U f2952361 frag u_cascade_vp 0 mat4 5 64 U f2952361 frag u_cascade_split 320 float 5 16 U f2952361 frag u_cascade_range 400 float 5 16 @@ -3386,16 +3503,18 @@ U f2952361 frag u_fog_wall 828 float 1 0 U f2952361 frag u_half 832 float 1 0 U f2952361 frag u_texel 836 float 1 0 U f2952361 frag u_ortho_on 840 float 1 0 -U f2952361 frag u_carpet_on 844 float 1 0 -U f2952361 frag u_snow_line 848 float 1 0 -U f2952361 frag u_lake_level 852 float 1 0 -U f2952361 frag u_wet 856 float 1 0 -U f2952361 frag u_sea_level 860 float 1 0 -U f2952361 frag u_lake 864 vec4 1 0 -U f2952361 frag u_origin 880 vec2 1 0 -U f2952361 frag u_view 896 mat4 1 0 -U f2952361 frag u_far_split 960 float 1 0 -U f2952361 frag u_far_band 964 float 1 0 +U f2952361 frag u_tp_on 844 float 1 0 +U f2952361 frag u_tp_dims 848 vec4 1 0 +U f2952361 frag u_carpet_on 864 float 1 0 +U f2952361 frag u_snow_line 868 float 1 0 +U f2952361 frag u_lake_level 872 float 1 0 +U f2952361 frag u_wet 876 float 1 0 +U f2952361 frag u_sea_level 880 float 1 0 +U f2952361 frag u_lake 896 vec4 1 0 +U f2952361 frag u_origin 912 vec2 1 0 +U f2952361 frag u_view 928 mat4 1 0 +U f2952361 frag u_far_split 992 float 1 0 +U f2952361 frag u_far_band 996 float 1 0 T f2952361 u_brdf 2 T f2952361 u_carpet 3 T f2952361 u_grass_a 4 @@ -3414,9 +3533,13 @@ T f2952361 u_sunshadow 16 T f2952361 u_ter_normal 17 T f2952361 u_tershadow 18 T f2952361 u_tershadow_aux 19 -T f2952361 u_ts_height 20 +T f2952361 u_tp_h 20 +T f2952361 u_tp_nrm 21 +T f2952361 u_tp_ortho 22 +T f2952361 u_tp_page 23 +T f2952361 u_ts_height 24 P a0f2dbac terrain.vert terrain.frag #define SUN_INLINE;#define FAR_ONLY; -B a0f2dbac vert 0 204 +B a0f2dbac vert 0 224 U a0f2dbac vert u_half 0 float 1 0 U a0f2dbac vert u_view 16 mat4 1 0 U a0f2dbac vert u_proj 80 mat4 1 0 @@ -3425,8 +3548,10 @@ U a0f2dbac vert u_cam_pos 160 vec3 1 0 U a0f2dbac vert u_node 176 vec3 1 0 U a0f2dbac vert u_morph 192 vec2 1 0 U a0f2dbac vert u_grid 200 float 1 0 +U a0f2dbac vert u_tp_on 204 float 1 0 +U a0f2dbac vert u_tp_dims 208 vec4 1 0 I a0f2dbac a_xz 0 vec2 -B a0f2dbac frag 1 968 +B a0f2dbac frag 1 1000 U a0f2dbac frag u_cascade_vp 0 mat4 5 64 U a0f2dbac frag u_cascade_split 320 float 5 16 U a0f2dbac frag u_cascade_range 400 float 5 16 @@ -3466,16 +3591,18 @@ U a0f2dbac frag u_fog_wall 828 float 1 0 U a0f2dbac frag u_half 832 float 1 0 U a0f2dbac frag u_texel 836 float 1 0 U a0f2dbac frag u_ortho_on 840 float 1 0 -U a0f2dbac frag u_carpet_on 844 float 1 0 -U a0f2dbac frag u_snow_line 848 float 1 0 -U a0f2dbac frag u_lake_level 852 float 1 0 -U a0f2dbac frag u_wet 856 float 1 0 -U a0f2dbac frag u_sea_level 860 float 1 0 -U a0f2dbac frag u_lake 864 vec4 1 0 -U a0f2dbac frag u_origin 880 vec2 1 0 -U a0f2dbac frag u_view 896 mat4 1 0 -U a0f2dbac frag u_far_split 960 float 1 0 -U a0f2dbac frag u_far_band 964 float 1 0 +U a0f2dbac frag u_tp_on 844 float 1 0 +U a0f2dbac frag u_tp_dims 848 vec4 1 0 +U a0f2dbac frag u_carpet_on 864 float 1 0 +U a0f2dbac frag u_snow_line 868 float 1 0 +U a0f2dbac frag u_lake_level 872 float 1 0 +U a0f2dbac frag u_wet 876 float 1 0 +U a0f2dbac frag u_sea_level 880 float 1 0 +U a0f2dbac frag u_lake 896 vec4 1 0 +U a0f2dbac frag u_origin 912 vec2 1 0 +U a0f2dbac frag u_view 928 mat4 1 0 +U a0f2dbac frag u_far_split 992 float 1 0 +U a0f2dbac frag u_far_band 996 float 1 0 T a0f2dbac u_brdf 2 T a0f2dbac u_carpet 3 T a0f2dbac u_grass_a 4 @@ -3494,9 +3621,13 @@ T a0f2dbac u_sunshadow 16 T a0f2dbac u_ter_normal 17 T a0f2dbac u_tershadow 18 T a0f2dbac u_tershadow_aux 19 -T a0f2dbac u_ts_height 20 +T a0f2dbac u_tp_h 20 +T a0f2dbac u_tp_nrm 21 +T a0f2dbac u_tp_ortho 22 +T a0f2dbac u_tp_page 23 +T a0f2dbac u_ts_height 24 P 5e4589a1 terrain.vert terrain.frag #define SUN_INLINE;#define NEAR_ONLY; -B 5e4589a1 vert 0 204 +B 5e4589a1 vert 0 224 U 5e4589a1 vert u_half 0 float 1 0 U 5e4589a1 vert u_view 16 mat4 1 0 U 5e4589a1 vert u_proj 80 mat4 1 0 @@ -3505,8 +3636,10 @@ U 5e4589a1 vert u_cam_pos 160 vec3 1 0 U 5e4589a1 vert u_node 176 vec3 1 0 U 5e4589a1 vert u_morph 192 vec2 1 0 U 5e4589a1 vert u_grid 200 float 1 0 +U 5e4589a1 vert u_tp_on 204 float 1 0 +U 5e4589a1 vert u_tp_dims 208 vec4 1 0 I 5e4589a1 a_xz 0 vec2 -B 5e4589a1 frag 1 968 +B 5e4589a1 frag 1 1000 U 5e4589a1 frag u_cascade_vp 0 mat4 5 64 U 5e4589a1 frag u_cascade_split 320 float 5 16 U 5e4589a1 frag u_cascade_range 400 float 5 16 @@ -3546,16 +3679,18 @@ U 5e4589a1 frag u_fog_wall 828 float 1 0 U 5e4589a1 frag u_half 832 float 1 0 U 5e4589a1 frag u_texel 836 float 1 0 U 5e4589a1 frag u_ortho_on 840 float 1 0 -U 5e4589a1 frag u_carpet_on 844 float 1 0 -U 5e4589a1 frag u_snow_line 848 float 1 0 -U 5e4589a1 frag u_lake_level 852 float 1 0 -U 5e4589a1 frag u_wet 856 float 1 0 -U 5e4589a1 frag u_sea_level 860 float 1 0 -U 5e4589a1 frag u_lake 864 vec4 1 0 -U 5e4589a1 frag u_origin 880 vec2 1 0 -U 5e4589a1 frag u_view 896 mat4 1 0 -U 5e4589a1 frag u_far_split 960 float 1 0 -U 5e4589a1 frag u_far_band 964 float 1 0 +U 5e4589a1 frag u_tp_on 844 float 1 0 +U 5e4589a1 frag u_tp_dims 848 vec4 1 0 +U 5e4589a1 frag u_carpet_on 864 float 1 0 +U 5e4589a1 frag u_snow_line 868 float 1 0 +U 5e4589a1 frag u_lake_level 872 float 1 0 +U 5e4589a1 frag u_wet 876 float 1 0 +U 5e4589a1 frag u_sea_level 880 float 1 0 +U 5e4589a1 frag u_lake 896 vec4 1 0 +U 5e4589a1 frag u_origin 912 vec2 1 0 +U 5e4589a1 frag u_view 928 mat4 1 0 +U 5e4589a1 frag u_far_split 992 float 1 0 +U 5e4589a1 frag u_far_band 996 float 1 0 T 5e4589a1 u_brdf 2 T 5e4589a1 u_carpet 3 T 5e4589a1 u_grass_a 4 @@ -3574,9 +3709,13 @@ T 5e4589a1 u_sunshadow 16 T 5e4589a1 u_ter_normal 17 T 5e4589a1 u_tershadow 18 T 5e4589a1 u_tershadow_aux 19 -T 5e4589a1 u_ts_height 20 +T 5e4589a1 u_tp_h 20 +T 5e4589a1 u_tp_nrm 21 +T 5e4589a1 u_tp_ortho 22 +T 5e4589a1 u_tp_page 23 +T 5e4589a1 u_ts_height 24 P bf3151c8 terrain.vert tersun.frag -B bf3151c8 vert 0 204 +B bf3151c8 vert 0 224 U bf3151c8 vert u_half 0 float 1 0 U bf3151c8 vert u_view 16 mat4 1 0 U bf3151c8 vert u_proj 80 mat4 1 0 @@ -3585,8 +3724,10 @@ U bf3151c8 vert u_cam_pos 160 vec3 1 0 U bf3151c8 vert u_node 176 vec3 1 0 U bf3151c8 vert u_morph 192 vec2 1 0 U bf3151c8 vert u_grid 200 float 1 0 +U bf3151c8 vert u_tp_on 204 float 1 0 +U bf3151c8 vert u_tp_dims 208 vec4 1 0 I bf3151c8 a_xz 0 vec2 -B bf3151c8 frag 1 904 +B bf3151c8 frag 1 936 U bf3151c8 frag u_cascade_vp 0 mat4 5 64 U bf3151c8 frag u_cascade_split 320 float 5 16 U bf3151c8 frag u_cascade_range 400 float 5 16 @@ -3623,9 +3764,11 @@ U bf3151c8 frag u_time 816 float 1 0 U bf3151c8 frag u_fog_inscatter 820 float 1 0 U bf3151c8 frag u_fog_desat 824 float 1 0 U bf3151c8 frag u_fog_wall 828 float 1 0 -U bf3151c8 frag u_view 832 mat4 1 0 -U bf3151c8 frag u_far_split 896 float 1 0 -U bf3151c8 frag u_far_band 900 float 1 0 +U bf3151c8 frag u_tp_on 832 float 1 0 +U bf3151c8 frag u_tp_dims 848 vec4 1 0 +U bf3151c8 frag u_view 864 mat4 1 0 +U bf3151c8 frag u_far_split 928 float 1 0 +U bf3151c8 frag u_far_band 932 float 1 0 T bf3151c8 u_brdf 2 T bf3151c8 u_height 3 T bf3151c8 u_irradiance 4 @@ -3634,7 +3777,10 @@ T bf3151c8 u_shadow 6 T bf3151c8 u_ter_normal 7 T bf3151c8 u_tershadow 8 T bf3151c8 u_tershadow_aux 9 -T bf3151c8 u_ts_height 10 +T bf3151c8 u_tp_h 10 +T bf3151c8 u_tp_nrm 11 +T bf3151c8 u_tp_page 12 +T bf3151c8 u_ts_height 13 P 46e2325c water.vert water.frag B 46e2325c vert 0 152 U 46e2325c vert u_view 0 mat4 1 0 diff --git a/packages/ludic.render3d/shaders/terpage.glsl b/packages/ludic.render3d/shaders/terpage.glsl new file mode 100644 index 00000000..1bde1a7e --- /dev/null +++ b/packages/ludic.render3d/shaders/terpage.glsl @@ -0,0 +1,121 @@ +// terpage.glsl - the terrain maps read through a page table of fine tiles, over a coarse map. +// +// THE REFERENCE COPY. Nothing prepends this file: a stage is assembled from its variant's +// defines, wind.glsl, and for a fragment stage noise.glsl and lighting.glsl, and there is no +// #include - so each shader that reads the ground carries the sections it needs, pasted from +// here, and a change here is a change pasted into every copy (grep TP_HELPERS). Each section +// has its own guard, so a stage that ends up with two copies still compiles, and a stage only +// declares the arrays it reads: glslang keeps every declared sampler in the program's layout. +// +// Before a paste: TP_FRAGMENT in a fragment stage (its fallbacks keep their implicit level of +// detail), and TP_HMAP when the stage's height sampler is not u_ts_height (terrain's u_height). +// The ground normal needs u_ter_normal declared first, the photograph u_ortho. +// +// The contract with the runtime: u_tp_page is n x n R32F, slot + 1 for a +// resident tile and 0 for none; each array layer is a tile with a one-texel border taken from +// its neighbours, so linear filtering and the B-spline's taps cross a tile edge seamlessly. +// While paging is on, u_ts_height / u_height, u_ter_normal and u_ortho hold a COARSE whole-map +// level; while it is off they hold today's full maps and every helper here is today's read. + +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif + +// ---- height (vertex and mesh stages) ----------------------------------------------------- +#ifndef TP_HEIGHT +#define TP_HEIGHT +#ifndef TP_HMAP +#define TP_HMAP u_ts_height +#endif +uniform sampler2DArray u_tp_h; +// bilinear height, fine where a tile is resident. Outside a fragment stage texture() IS level 0 +// (glslang emits the same explicit-lod sample), so this is the old read bit for bit. +float terHeight(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_h, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).r; + return textureLod(TP_HMAP, uv, 0.0).r; +} +// The B-spline through four bilinear taps (heightSmooth). Paging on, the texel is the FULL +// map's - the coarse map's own size would halve the surface's detail - and each tap finds its +// own tile, so a patch straddling two tiles, or a tile and the coarse map, stays one surface. +float terHeightSmooth(vec2 uv) { + vec2 res = u_tp_on > 0.5 ? vec2(u_tp_dims.w) : vec2(textureSize(TP_HMAP, 0)); + vec2 t = uv * res - 0.5; + vec2 f = fract(t); + vec2 i = floor(t); + vec2 w0 = (1.0 - f) * (1.0 - f) * (1.0 - f) / 6.0; + vec2 w1 = (4.0 - 6.0 * f * f + 3.0 * f * f * f) / 6.0; + vec2 w3 = f * f * f / 6.0; + vec2 w2 = 1.0 - w0 - w1 - w3; + vec2 s0 = w0 + w1, s1 = w2 + w3; + vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; + vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; + return (terHeight(vec2(o0.x, o0.y)) * s0.x + terHeight(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (terHeight(vec2(o0.x, o1.y)) * s0.x + terHeight(vec2(o1.x, o1.y)) * s1.x) * s1.y; +} +#endif + +// ---- the baked normal (x, z; y is rebuilt by the caller) ---------------------------------- +#ifndef TP_NORMAL +#define TP_NORMAL +uniform sampler2DArray u_tp_nrm; +vec2 terNormalXZ(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ter_normal, uv).rg; + // taken before the per-pixel branch: a neighbour on another tile must not decide the level + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_nrm, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).rg; +#ifdef TP_FRAGMENT + return textureGrad(u_ter_normal, uv, dx, dy).rg; +#else + return textureLod(u_ter_normal, uv, 0.0).rg; +#endif +} +#endif + +// ---- the photograph ---------------------------------------------------------------------- +#ifndef TP_ORTHO +#define TP_ORTHO +uniform sampler2DArray u_tp_ortho; +// level-0 colour, fine where a tile is resident; the coarse map (with its mips) elsewhere +vec3 terOrtho(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ortho, uv).rgb; + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_ortho, vec3(tpUV(uv, u_tp_dims.z), s), 0.0).rgb; +#ifdef TP_FRAGMENT + return textureGrad(u_ortho, uv, dx, dy).rgb; +#else + return textureLod(u_ortho, uv, 0.0).rgb; +#endif +} +#endif + +// ---- the photograph's measures (declares no sampler: a stage reading u_ortho blurred needs no tile) +#ifndef TP_ORTHO_LOD +#define TP_ORTHO_LOD +// the FULL photograph's texels a side, for filters that weigh by the texel (orthoSmooth) +vec2 tpOrthoRes() { return u_tp_on > 0.5 ? vec2(u_tp_dims.z * u_tp_dims.x) : vec2(textureSize(u_ortho, 0)); } +// A blurred read (textureLod 1..2.5) names a level of the FULL photograph; on the coarse map +// the same detail is that many levels lower, so the ground's far colour does not soften. +float tpOrthoLod(float lod) { + if (u_tp_on < 0.5) return lod; + return max(lod - log2(u_tp_dims.z * u_tp_dims.x / float(textureSize(u_ortho, 0).x)), 0.0); +} +#endif diff --git a/packages/ludic.render3d/shaders/terrain.frag b/packages/ludic.render3d/shaders/terrain.frag index e6816fe9..f010880f 100644 --- a/packages/ludic.render3d/shaders/terrain.frag +++ b/packages/ludic.render3d/shaders/terrain.frag @@ -6,16 +6,84 @@ out vec4 o_color; #define ridged(p, o) 0.3 #define gnoise(p) 0.1 #endif -uniform sampler2D u_height; +// no u_height here: the vertex stage reads the ground, and every declared sampler counts +// against the stage's limit (MoltenVK) whether it is read or not // the baked terrain normal: x and z in RG16F, y rebuilt (a terrain normal always points up) uniform sampler2D u_ter_normal; -vec3 terNormal(vec2 uv) { vec2 xz = texture(u_ter_normal, uv).rg; return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform float u_half; uniform float u_texel; // height-map texel size in uv uniform sampler2D u_grass_d; uniform sampler2D u_grass_n; uniform sampler2D u_grass_a; uniform sampler2D u_ortho; uniform sampler2D u_sunshadow; // the sun visibility this pixel already has (tersun.frag) uniform float u_ortho_on; +#define TP_FRAGMENT +// terpage.glsl, pasted (the reference copy and its rules are there) +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif +// ---- the baked normal (x, z; y is rebuilt by the caller) ---------------------------------- +#ifndef TP_NORMAL +#define TP_NORMAL +uniform sampler2DArray u_tp_nrm; +vec2 terNormalXZ(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ter_normal, uv).rg; + // taken before the per-pixel branch: a neighbour on another tile must not decide the level + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_nrm, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).rg; +#ifdef TP_FRAGMENT + return textureGrad(u_ter_normal, uv, dx, dy).rg; +#else + return textureLod(u_ter_normal, uv, 0.0).rg; +#endif +} +#endif +// ---- the photograph ---------------------------------------------------------------------- +#ifndef TP_ORTHO +#define TP_ORTHO +uniform sampler2DArray u_tp_ortho; +// level-0 colour, fine where a tile is resident; the coarse map (with its mips) elsewhere +vec3 terOrtho(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ortho, uv).rgb; + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_ortho, vec3(tpUV(uv, u_tp_dims.z), s), 0.0).rgb; +#ifdef TP_FRAGMENT + return textureGrad(u_ortho, uv, dx, dy).rgb; +#else + return textureLod(u_ortho, uv, 0.0).rgb; +#endif +} +#endif +// ---- the photograph's measures (declares no sampler: a stage reading u_ortho blurred needs no tile) +#ifndef TP_ORTHO_LOD +#define TP_ORTHO_LOD +// the FULL photograph's texels a side, for filters that weigh by the texel (orthoSmooth) +vec2 tpOrthoRes() { return u_tp_on > 0.5 ? vec2(u_tp_dims.z * u_tp_dims.x) : vec2(textureSize(u_ortho, 0)); } +// A blurred read (textureLod 1..2.5) names a level of the FULL photograph; on the coarse map +// the same detail is that many levels lower, so the ground's far colour does not soften. +float tpOrthoLod(float lod) { + if (u_tp_on < 0.5) return lod; + return max(lod - log2(u_tp_dims.z * u_tp_dims.x / float(textureSize(u_ortho, 0).x)), 0.0); +} +#endif +vec3 terNormal(vec2 uv) { vec2 xz = terNormalXZ(uv); return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform sampler2D u_rock_d; uniform sampler2D u_rock_n; uniform sampler2D u_rock_a; uniform sampler2D u_snow_d; uniform sampler2D u_carpet; // the clump cards baked straight down (alpha = coverage) uniform float u_carpet_on; @@ -44,7 +112,7 @@ uniform vec2 u_origin; // world offset of the terrain grid // applied inside sunShadow() for every receiver in the scene. // bicubic (B-spline) sample through four bilinear taps: the 10 m photo pixels stop reading as squares vec3 orthoSmooth(vec2 uv) { - vec2 res = vec2(textureSize(u_ortho, 0)); + vec2 res = tpOrthoRes(); // the full photograph's texel, whichever map is bound vec2 t = uv * res - 0.5; vec2 f = fract(t); vec2 i = floor(t); @@ -52,8 +120,8 @@ vec3 orthoSmooth(vec2 uv) { vec2 w3 = f * f * f / 6.0, w2 = 1.0 - w0 - w1 - w3; vec2 s0 = w0 + w1, s1 = w2 + w3; vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res, o1 = (i + 1.0 + w3 / s1 + 0.5) / res; - return (texture(u_ortho, vec2(o0.x, o0.y)).rgb * s0.x + texture(u_ortho, vec2(o1.x, o0.y)).rgb * s1.x) * s0.y - + (texture(u_ortho, vec2(o0.x, o1.y)).rgb * s0.x + texture(u_ortho, vec2(o1.x, o1.y)).rgb * s1.x) * s1.y; + return (terOrtho(vec2(o0.x, o0.y)) * s0.x + terOrtho(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (terOrtho(vec2(o0.x, o1.y)) * s0.x + terOrtho(vec2(o1.x, o1.y)) * s1.x) * s1.y; } uniform mat4 u_view; @@ -210,7 +278,7 @@ vec3 dbg_n; vec3 dbg_alb; float dbg_shadow; vec3 dbg_mat; float fbmC(bool cheap, vec2 q, int o) { return cheap ? 0.0 : fbm(q, o); } float ridgedC(bool cheap, vec2 q, int o) { return cheap ? 0.35 : ridged(q, o); } float gnoiseC(bool cheap, vec2 q) { return cheap ? 0.0 : gnoise(q); } -vec3 orthoC(bool cheap, vec2 uv) { return cheap ? textureLod(u_ortho, uv, 1.0).rgb : orthoSmooth(uv); } +vec3 orthoC(bool cheap, vec2 uv) { return cheap ? textureLod(u_ortho, uv, tpOrthoLod(1.0)).rgb : orthoSmooth(uv); } vec4 carpetC(bool cheap, vec2 uv, vec2 dx, vec2 dy) { return cheap ? textureGrad(u_carpet, uv, dx, dy) : sampleCarpet(uv, dx, dy); } void matC(bool cheap, sampler2D d, sampler2D nm, sampler2D am, vec2 uv, vec2 dx, vec2 dy, out vec3 alb, out vec3 nrm, out vec3 arm) { if (cheap) samplePlain(d, nm, am, uv, dx, dy, alb, nrm, arm); else sampleMat(d, nm, am, uv, dx, dy, alb, nrm, arm); @@ -267,14 +335,14 @@ vec3 groundShade(vec3 p, vec3 N, float slope, float dist, float viewDepth, bool if (orthoOn) { oc = orthoC(cheap, v_huv); // (classified from a ~40 m blur: thresholding the raw 10 m pixels drew hard squares) - vec3 ocf = textureLod(u_ortho, v_huv, 2.0).rgb; + vec3 ocf = textureLod(u_ortho, v_huv, tpOrthoLod(2.0)).rgb; float gx = ocf.g - max(ocf.r, ocf.b); // (the photograph is sampled linear: sRGB 72 is 0.06, 92 is 0.11) forestW = smoothstep(0.12, 0.06, max(ocf.r, max(ocf.g, ocf.b))) * smoothstep(0.004, 0.012, gx) * smoothstep(waterLine(p.xz) + 0.8, waterLine(p.xz) + 1.8, p.y); // Classify from a ~60 m blur, never from the pixels: the survey's 10 m pixels carry // a foot trail as a broken line of bare ground, and thresholding them painted it // across the meadow as tan dashes (and, on the CPU, lined boulders up along it). - vec3 ocl = textureLod(u_ortho, v_huv, 2.5).rgb; + vec3 ocl = textureLod(u_ortho, v_huv, tpOrthoLod(2.5)).rgb; float mxc = max(ocl.r, max(ocl.g, ocl.b)), mnc = min(ocl.r, min(ocl.g, ocl.b)); float greenEx = ocl.g - max(ocl.r, ocl.b); screeC = smoothstep(0.008, -0.002, greenEx) * smoothstep(0.06, 0.12, mxc) * (1.0 - smoothstep(0.55, 0.75, mxc)) * smoothstep(waterLine(p.xz) + 0.2, waterLine(p.xz) + 1.2, p.y); diff --git a/packages/ludic.render3d/shaders/terrain.vert b/packages/ludic.render3d/shaders/terrain.vert index f348348d..6a0faea3 100644 --- a/packages/ludic.render3d/shaders/terrain.vert +++ b/packages/ludic.render3d/shaders/terrain.vert @@ -14,8 +14,42 @@ uniform vec3 u_cam_pos; uniform vec3 u_node; // x0, z0, size (m) uniform vec2 u_morph; // distance where the morph starts, and where it is complete uniform float u_grid; // cells per patch side -float heightSmooth(sampler2D tex, vec2 uv) { - vec2 res = vec2(textureSize(tex, 0)); +#define TP_HMAP u_height +// terpage.glsl, pasted (the reference copy and its rules are there) +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif +// ---- height (vertex and mesh stages) ----------------------------------------------------- +#ifndef TP_HEIGHT +#define TP_HEIGHT +#ifndef TP_HMAP +#define TP_HMAP u_ts_height +#endif +uniform sampler2DArray u_tp_h; +// bilinear height, fine where a tile is resident. Outside a fragment stage texture() IS level 0 +// (glslang emits the same explicit-lod sample), so this is the old read bit for bit. +float terHeight(vec2 uv) { + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_h, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).r; + return textureLod(TP_HMAP, uv, 0.0).r; +} +// The B-spline through four bilinear taps (heightSmooth). Paging on, the texel is the FULL +// map's - the coarse map's own size would halve the surface's detail - and each tap finds its +// own tile, so a patch straddling two tiles, or a tile and the coarse map, stays one surface. +float terHeightSmooth(vec2 uv) { + vec2 res = u_tp_on > 0.5 ? vec2(u_tp_dims.w) : vec2(textureSize(TP_HMAP, 0)); vec2 t = uv * res - 0.5; vec2 f = fract(t); vec2 i = floor(t); @@ -26,23 +60,24 @@ float heightSmooth(sampler2D tex, vec2 uv) { vec2 s0 = w0 + w1, s1 = w2 + w3; vec2 o0 = (i - 1.0 + w1 / s0 + 0.5) / res; vec2 o1 = (i + 1.0 + w3 / s1 + 0.5) / res; - return (texture(tex, vec2(o0.x, o0.y)).r * s0.x + texture(tex, vec2(o1.x, o0.y)).r * s1.x) * s0.y - + (texture(tex, vec2(o0.x, o1.y)).r * s0.x + texture(tex, vec2(o1.x, o1.y)).r * s1.x) * s1.y; + return (terHeight(vec2(o0.x, o0.y)) * s0.x + terHeight(vec2(o1.x, o0.y)) * s1.x) * s0.y + + (terHeight(vec2(o0.x, o1.y)) * s0.x + terHeight(vec2(o1.x, o1.y)) * s1.x) * s1.y; } +#endif out vec3 v_wpos; out vec2 v_huv; void main() { vec2 grid = a_xz * u_grid; vec2 xz = u_node.xy + a_xz * u_node.z; vec2 huv = (xz - u_origin) / (2.0 * u_half) + 0.5; - float h0 = texture(u_height, huv).r; + float h0 = terHeight(huv); float d = distance(vec3(xz.x, h0, xz.y), u_cam_pos); float k = clamp((d - u_morph.x) / max(u_morph.y - u_morph.x, 1.0), 0.0, 1.0); vec2 frac2 = fract(grid * 0.5) * 2.0; // 1 on odd vertices grid -= frac2 * k; xz = u_node.xy + grid / u_grid * u_node.z; huv = (xz - u_origin) / (2.0 * u_half) + 0.5; - float h = heightSmooth(u_height, huv); + float h = terHeightSmooth(huv); vec3 p = vec3(xz.x, h, xz.y); v_wpos = p; v_huv = huv; diff --git a/packages/ludic.render3d/shaders/tersun.frag b/packages/ludic.render3d/shaders/tersun.frag index f11c851b..9085ea63 100644 --- a/packages/ludic.render3d/shaders/tersun.frag +++ b/packages/ludic.render3d/shaders/tersun.frag @@ -11,10 +11,47 @@ in vec3 v_wpos; in vec2 v_huv; out float o_sh; -uniform sampler2D u_height; +// no u_height here: the vertex stage reads the ground, and every declared sampler counts +// against the stage's limit (MoltenVK) whether it is read or not // the baked terrain normal: x and z in RG16F, y rebuilt (a terrain normal always points up) uniform sampler2D u_ter_normal; -vec3 terNormal(vec2 uv) { vec2 xz = texture(u_ter_normal, uv).rg; return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } +#define TP_FRAGMENT +// terpage.glsl, pasted (the reference copy and its rules are there) +// ---- the page table ---------------------------------------------------------------------- +#ifndef TP_HELPERS +#define TP_HELPERS +uniform sampler2D u_tp_page; // R32F, a texel a tile: slot + 1, 0 = not resident +uniform float u_tp_on; // 1 while paging; 0 reads the whole maps exactly as before +uniform vec4 u_tp_dims; // tiles a side, height texels a tile, photo texels a tile, full height res +// the layer holding a full-map uv, or -1: paging off, off the map, or not resident +float tpSlot(vec2 uv) { + if (u_tp_on < 0.5 || uv.x < 0.0 || uv.y < 0.0 || uv.x >= 1.0 || uv.y >= 1.0) return -1.0; + ivec2 t = min(ivec2(floor(uv * u_tp_dims.x)), ivec2(int(u_tp_dims.x) - 1)); + return texelFetch(u_tp_page, t, 0).r - 1.0; +} +// a full-map uv inside its tile's layer of k texels a side, past the one-texel border +vec2 tpUV(vec2 uv, float k) { return (1.0 + fract(uv * u_tp_dims.x) * k) / (k + 2.0); } +#endif +// ---- the baked normal (x, z; y is rebuilt by the caller) ---------------------------------- +#ifndef TP_NORMAL +#define TP_NORMAL +uniform sampler2DArray u_tp_nrm; +vec2 terNormalXZ(vec2 uv) { +#ifdef TP_FRAGMENT + if (u_tp_on < 0.5) return texture(u_ter_normal, uv).rg; + // taken before the per-pixel branch: a neighbour on another tile must not decide the level + vec2 dx = dFdx(uv), dy = dFdy(uv); +#endif + float s = tpSlot(uv); + if (s >= 0.0) return textureLod(u_tp_nrm, vec3(tpUV(uv, u_tp_dims.y), s), 0.0).rg; +#ifdef TP_FRAGMENT + return textureGrad(u_ter_normal, uv, dx, dy).rg; +#else + return textureLod(u_ter_normal, uv, 0.0).rg; +#endif +} +#endif +vec3 terNormal(vec2 uv) { vec2 xz = terNormalXZ(uv); return vec3(xz.x, sqrt(max(1.0 - dot(xz, xz), 0.0)), xz.y); } uniform mat4 u_view; uniform float u_far_split; uniform float u_far_band; diff --git a/packages/ludic.render3d/terrain.ludic b/packages/ludic.render3d/terrain.ludic index 9217f4d6..dd509379 100644 --- a/packages/ludic.render3d/terrain.ludic +++ b/packages/ludic.render3d/terrain.ludic @@ -253,6 +253,7 @@ function terrain_generate__t(render3d_st: mut Render3dState) -> void { # The height field for shaders that place things on the ground (model.vert's u_ground) function terrain_bind_height(render3d_st: mut Render3dState, p: int) -> void { + tp_bind(render3d_st, p) r3d_bind_2d(render3d_st, p, "u_ts_height", 5, render3d_st.ter_height_tex) r3d_bind_2d(render3d_st, p, "u_ter_normal", 6, render3d_st.ter_normal_tex) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_ts_half"), float(render3d_st.TERRAIN_HALF)) @@ -287,6 +288,7 @@ function terrain_bake_pass(render3d_st: mut Render3dState, p: int, target: int) gpu_depth_test(render3d_st, false) gpu_blend(render3d_st, false) gpu_use_program(render3d_st, p) + tp_bind(render3d_st, p) r3d_bind_2d(render3d_st, p, "u_height", 0, render3d_st.ter_height_tex) r3d_bind_2d(render3d_st, p, "u_ter_normal", 7, render3d_st.ter_normal_tex) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_half"), float(render3d_st.TERRAIN_HALF)) @@ -400,6 +402,7 @@ function terrain_init(render3d_st: mut Render3dState) -> void { render3d_st.gvk_tag = was } function terrain_init__t(render3d_st: mut Render3dState) -> void { + tp_dummies(render3d_st) for i in 0 .. TERRAIN_INIT_STEPS { terrain_init_step(render3d_st, i) } } @@ -473,6 +476,7 @@ function terrain_draw_shadow(light_vp: floats) -> void { # selection can switch between them per patch without re-binding anything but the node. function terrain_bind_prog(render3d_st: mut Render3dState, p: int) -> void { gpu_use_program(render3d_st, p) + tp_bind(render3d_st, p) r3d_bind_2d(render3d_st, p, "u_height", 0, render3d_st.ter_height_tex) r3d_bind_2d(render3d_st, p, "u_ter_normal", 7, render3d_st.ter_normal_tex) r3d_bind_2d(render3d_st, p, "u_grass_d", 1, render3d_st.ter_tex[0]); r3d_bind_2d(render3d_st, p, "u_grass_n", 2, render3d_st.ter_tex[1]); r3d_bind_2d(render3d_st, p, "u_grass_a", 3, render3d_st.ter_tex[2]) @@ -582,6 +586,7 @@ function terrain_sun_pass(render3d_st: mut Render3dState, w: int, h: int, depth: gpu_clear(render3d_st, GL_COLOR_BUFFER_BIT) let p = render3d_st.ter_sun_prog gpu_use_program(render3d_st, p) + tp_bind(render3d_st, p) r3d_bind_2d(render3d_st, p, "u_height", 0, render3d_st.ter_height_tex) r3d_bind_2d(render3d_st, p, "u_ter_normal", 7, render3d_st.ter_normal_tex) u_f(render3d_st, gpu_uniform(render3d_st, p, "u_half"), float(render3d_st.TERRAIN_HALF)) @@ -822,6 +827,7 @@ function terrain_unload(render3d_st: mut Render3dState) -> void { if render3d_st.ter_normal_tex != 0 { gpu_tex_free(render3d_st, render3d_st.ter_normal_tex); render3d_st.ter_normal_tex = 0 } if render3d_st.ter_dem_tex != 0 { gpu_tex_free(render3d_st, render3d_st.ter_dem_tex); render3d_st.ter_dem_tex = 0 } if render3d_st.ter_ortho_tex != 0 { gpu_tex_free(render3d_st, render3d_st.ter_ortho_tex); render3d_st.ter_ortho_tex = 0 } + tp_pool_free(render3d_st) if render3d_st.ter_heights != null { free(render3d_st.ter_heights); render3d_st.ter_heights = null } tt_close(render3d_st) if render3d_st.ter_ortho_px != null { free(render3d_st.ter_ortho_px); render3d_st.ter_ortho_px = null } diff --git a/packages/ludic.render3d/terrain_pages.ludic b/packages/ludic.render3d/terrain_pages.ludic new file mode 100644 index 00000000..2a0e76ca --- /dev/null +++ b/packages/ludic.render3d/terrain_pages.ludic @@ -0,0 +1,148 @@ +# terrain_pages.ludic — the height field, its normals and the photograph on the GPU while the tiles are +# on (terrain_tiles.ludic): a coarse whole-map level (TT_COARSE a side) and, round the camera, a pool +# of fine tiles in three array textures addressed through a page table (u_tp_page: slot + 1 of a +# resident tile, 0 = read the coarse level). A shader reads a full-map uv as +# slot = texelFetch(u_tp_page, floor(uv * n)) - 1; slot uv = (1 + fract(uv * n) * T) / (T + 2) +# and each layer carries a one-texel border from its neighbours, so a bilinear tap never leaves it. +# Tiles off, nothing changes: the whole textures, u_tp_on = 0 and stand-in arrays bound. + +const TP_REACH_MAX: float = 900.0 # metres of fine ground round the camera, fog or none +const TP_LAYERS_MAX: int = 2048 # what a desktop Vulkan device takes in an array (the floor is 256) + +# the page pool's samplers and numbers for program p; every program that reads the ground calls it +function tp_bind(render3d_st: mut Render3dState, p: int) -> void { + tp_dummies(render3d_st) + var pg = render3d_st.tp_dummy_page + var h = render3d_st.tp_dummy_h + var nm = render3d_st.tp_dummy_n + var o = render3d_st.tp_dummy_o + var on = 0.0 + if render3d_st.tp_on { + pg = render3d_st.tp_page_tex; h = render3d_st.tp_h_tex; nm = render3d_st.tp_n_tex; o = render3d_st.tp_o_tex; on = 1.0 + } + r3d_bind_2d(render3d_st, p, "u_tp_page", 20, pg) + r3d_bind_tex(render3d_st, p, "u_tp_h", 21, GPU_TEX2D_ARRAY, h) + r3d_bind_tex(render3d_st, p, "u_tp_nrm", 22, GPU_TEX2D_ARRAY, nm) + r3d_bind_tex(render3d_st, p, "u_tp_ortho", 23, GPU_TEX2D_ARRAY, o) + u_f(render3d_st, gpu_uniform(render3d_st, p, "u_tp_on"), on) + u_f4(render3d_st, gpu_uniform(render3d_st, p, "u_tp_dims"), float(render3d_st.tt_n), float(TT_TEX), float(render3d_st.tt_oside), float(TERRAIN_RES)) +} + +# the stand-ins, once: an unbound sampler gets a white 2-D texture, which an array binding cannot take +@alloc_ok("made once: the stand-ins for the page pool's samplers") +function tp_dummies(render3d_st: mut Render3dState) -> void { + if render3d_st.tp_dummy_h != 0 { return } + let was = render3d_st.gvk_tag + render3d_st.gvk_tag = VKM_TERRAIN + let z = words(4) + for k in 0 .. 4 { z[k] = 0 } + render3d_st.tp_dummy_page = tex_target(render3d_st, 1, 1, GL_R32F, GL_RED, GL_FLOAT, GL_NEAREST) + gpu_tex_fill(render3d_st, render3d_st.tp_dummy_page, GL_R32F, 1, 1, GL_RED, GL_FLOAT, data_of(z)) + free(z) + render3d_st.tp_dummy_h = tp_array(render3d_st, GL_R32F, 1, 1) + render3d_st.tp_dummy_n = tp_array(render3d_st, GL_RG16F, 1, 1) + render3d_st.tp_dummy_o = tp_array(render3d_st, GL_SRGB8_ALPHA8, 1, 1) + render3d_st.gvk_tag = was +} + +# an array texture of `layers` side x side layers, no mips, read bilinear and clamped +function tp_array(render3d_st: mut Render3dState, ifmt: int, side: int, layers: int) -> int { + let t = gpu_tex_new(render3d_st) + gpu_tex_bind(render3d_st, GPU_TEX2D_ARRAY, t) + gpu_tex_image3d(render3d_st, ifmt, side, side, layers, GL_RED, GL_FLOAT, null) + gpu_tex_param(render3d_st, GPU_TEX2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + gpu_tex_param(render3d_st, GPU_TEX2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + gpu_tex_param(render3d_st, GPU_TEX2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) + gpu_tex_param(render3d_st, GPU_TEX2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) + return t +} + +# how far the fine tiles reach: the fog wall's reach when it is nearer than TP_REACH_MAX +function tp_reach_m(render3d_st: Render3dState) -> float { + if render3d_st.r3d_fog_wall > 0.0 { return Math.min(r3d_reach(render3d_st, 0.0), TP_REACH_MAX) } + return TP_REACH_MAX +} +function tp_tile_m(render3d_st: Render3dState) -> float { return float(TT_TEX) * terrain_texel(render3d_st) } +# the tiles within r and a one-tile ring, and a few spare +function tp_slots_for(render3d_st: Render3dState, r: float) -> int { + let a = r / tp_tile_m(render3d_st) + 1.5 + let n = int(PI * a * a) + 9 + return min(min(n, TP_LAYERS_MAX), render3d_st.tt_n * render3d_st.tt_n) +} +# the staging a frame's loads take: the page table, then TP_BUDGET tiles of each of the three +function tp_half_bytes(render3d_st: Render3dState) -> int { + let hs = TT_TEX + 2 + let os = render3d_st.tt_oside + 2 + return render3d_st.tt_n * render3d_st.tt_n * 4 + TP_BUDGET * (2 * hs * hs + os * os) * 4 +} + +# the pool, the page table (all 0) and the staging, (re)made for the reach the fog wall allows +@alloc_ok("a map made, or the fog wall moved: the page pool, once") +function tp_pool(render3d_st: mut Render3dState) -> void { + tp_pool_free(render3d_st) + let n = render3d_st.tt_n + let r = tp_reach_m(render3d_st) + let slots = tp_slots_for(render3d_st, r) + let hs = TT_TEX + 2 + let os = render3d_st.tt_oside + 2 + render3d_st.tp_h_tex = tp_array(render3d_st, GL_R32F, hs, slots) + render3d_st.tp_n_tex = tp_array(render3d_st, GL_RG16F, hs, slots) + render3d_st.tp_o_tex = tp_array(render3d_st, GL_SRGB8_ALPHA8, os, slots) + if render3d_st.tp_page == null or len(render3d_st.tp_page) != n * n { + if render3d_st.tp_page != null { free(render3d_st.tp_page) } + render3d_st.tp_page = floats(n * n) + } + for t in 0 .. n * n { render3d_st.tp_page[t] = 0.0 } + render3d_st.tp_page_tex = tex_target(render3d_st, n, n, GL_R32F, GL_RED, GL_FLOAT, GL_NEAREST) + gpu_tex_fill(render3d_st, render3d_st.tp_page_tex, GL_R32F, n, n, GL_RED, GL_FLOAT, data_of(render3d_st.tp_page)) + tp_slot_lists(render3d_st, slots) + if render3d_st.tp_st_bytes < 2 * tp_half_bytes(render3d_st) { + gpu_staging_drop(render3d_st) + render3d_st.tp_st_ptr = gpu_staging_keep(render3d_st, 2 * tp_half_bytes(render3d_st)) + } + render3d_st.tp_slots = slots; render3d_st.tp_reach = r + render3d_st.tp_cam_t = -1; render3d_st.tp_short = false; render3d_st.tp_dirty = false + render3d_st.tp_bytes = slots * (2 * hs * hs + os * os) * 4 + n * n * 4 + render3d_st.tp_on = render3d_st.tp_st_ptr != null and gpu_tex_ok(render3d_st, render3d_st.tp_h_tex) and gpu_tex_ok(render3d_st, render3d_st.tp_o_tex) + tp_say_pool(slots, r, render3d_st.tp_bytes, render3d_st.tp_on) +} + +# every slot free, and this frame's candidate lists (made once) +@alloc_ok("a map made, or the fog wall moved: the page pool's lists, once") +function tp_slot_lists(render3d_st: mut Render3dState, slots: int) -> void { + if render3d_st.tp_tile_of != null { free(render3d_st.tp_tile_of); free(render3d_st.tp_free) } + render3d_st.tp_tile_of = words(slots) + render3d_st.tp_free = words(slots) + for s in 0 .. slots { render3d_st.tp_tile_of[s] = -1; render3d_st.tp_free[s] = slots - 1 - s } + render3d_st.tp_nfree = slots + if render3d_st.tp_cand == null { + render3d_st.tp_cand = words(TP_BUDGET); render3d_st.tp_cand_s = words(TP_BUDGET); render3d_st.tp_cand_d = floats(TP_BUDGET) + render3d_st.tp_zero = words(1); render3d_st.tp_zero[0] = 0 + } +} + +# the pool and its page table let go (a world swap, or a remake); the coarse level is terrain_unload's +function tp_pool_free(render3d_st: mut Render3dState) -> void { + render3d_st.tp_on = false + if render3d_st.tp_page_tex != 0 { gpu_tex_free(render3d_st, render3d_st.tp_page_tex); render3d_st.tp_page_tex = 0 } + if render3d_st.tp_h_tex != 0 { gpu_tex_free(render3d_st, render3d_st.tp_h_tex); render3d_st.tp_h_tex = 0 } + if render3d_st.tp_n_tex != 0 { gpu_tex_free(render3d_st, render3d_st.tp_n_tex); render3d_st.tp_n_tex = 0 } + if render3d_st.tp_o_tex != 0 { gpu_tex_free(render3d_st, render3d_st.tp_o_tex); render3d_st.tp_o_tex = 0 } + render3d_st.tp_slots = 0; render3d_st.tp_bytes = 0 +} + +# r3d_fog_wall moved: a pool sized for another reach is made again (the page table starts empty) +function terrain_pages_fog(render3d_st: mut Render3dState) -> void { + if not render3d_st.tp_on { return } + let r = tp_reach_m(render3d_st) + render3d_st.tp_reach = r + render3d_st.tp_cam_t = -1 + if tp_slots_for(render3d_st, r) == render3d_st.tp_slots { return } + let was = render3d_st.gvk_tag + render3d_st.gvk_tag = VKM_TERRAIN + tp_pool(render3d_st) + render3d_st.gvk_tag = was +} + +@alloc_ok("a message, built only when it is said: a map made or the fog wall moved") +function tp_say_pool(slots: int, r: float, bytes: int, on: bool) -> void { print(`r3d: terrain: {slots} page slots for {int(r)} m round the camera ({bytes / 1024} KB on the GPU), paging {on}`) } diff --git a/packages/ludic.render3d/terrain_pages_coarse.ludic b/packages/ludic.render3d/terrain_pages_coarse.ludic new file mode 100644 index 00000000..4f084272 --- /dev/null +++ b/packages/ludic.render3d/terrain_pages_coarse.ludic @@ -0,0 +1,30 @@ +# terrain_pages_coarse.ludic — the whole-map level the page pool falls back to (terrain_pages.ludic), +# made on the GPU from the whole textures at the cut, before they go. + +# at the cut (terrain_tiles_cut): the coarse level made from the whole textures, which then go - the +# patch bounds and the sun's bake have already read them - and the pool made +function tp_cut(render3d_st: mut Render3dState) -> void { + let was = render3d_st.gvk_tag + render3d_st.gvk_tag = VKM_TERRAIN + tp_cut__t(render3d_st) + render3d_st.gvk_tag = was +} +function tp_cut__t(render3d_st: mut Render3dState) -> void { + if render3d_st.tt_file == null or render3d_st.tt_coarse == null or render3d_st.ter_ortho_tex == 0 { return } + let c = TT_COARSE + let h = tex_target(render3d_st, c, c, GL_R32F, GL_RED, GL_FLOAT, GL_LINEAR) + gpu_tex_fill(render3d_st, h, GL_R32F, c, c, GL_RED, GL_FLOAT, data_of(render3d_st.tt_coarse)) + let nm = tex_target(render3d_st, c, c, GL_RG16F, GL_RG, GL_FLOAT, GL_LINEAR) + gpu_tex_blit(render3d_st, render3d_st.ter_normal_tex, TERRAIN_RES, TERRAIN_RES, nm, c, c) + let o = tex_target(render3d_st, c, c, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR) + gpu_tex_blit(render3d_st, render3d_st.ter_ortho_tex, render3d_st.ter_ortho_w, render3d_st.ter_ortho_w, o, c, c) + gpu_tex_bind(render3d_st, GPU_TEX2D, o) + gpu_tex_param(render3d_st, GPU_TEX2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR) + gpu_tex_mips(render3d_st, GPU_TEX2D) + gpu_tex_paramf(render3d_st, GPU_TEX2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, render3d_st.tex_anisotropy) + gpu_tex_free(render3d_st, render3d_st.ter_height_tex) + gpu_tex_free(render3d_st, render3d_st.ter_normal_tex) + gpu_tex_free(render3d_st, render3d_st.ter_ortho_tex) + render3d_st.ter_height_tex = h; render3d_st.ter_normal_tex = nm; render3d_st.ter_ortho_tex = o + tp_pool(render3d_st) +} diff --git a/packages/ludic.render3d/terrain_pages_fill.ludic b/packages/ludic.render3d/terrain_pages_fill.ludic new file mode 100644 index 00000000..e4980b4a --- /dev/null +++ b/packages/ludic.render3d/terrain_pages_fill.ludic @@ -0,0 +1,74 @@ +# terrain_pages_fill.ludic — a tile's three layers written into the staging, border and all, and the +# frame's copies recorded (terrain_pages_frame.ludic). The staging half is laid out as the page table, +# then TP_BUDGET heights, TP_BUDGET normals and TP_BUDGET photographs. + +# the k-th load of the frame: tile t's heights and normals, (TT_TEX + 2) a side, each texel the one +# the whole copy held at that place, clamped at the map's edge +function tp_fill(render3d_st: mut Render3dState, t: int, base: int, k: int) -> void { + let n = render3d_st.tt_n + let hs = TT_TEX + 2 + let hb = hs * hs * 4 + let ho = base + n * n * 4 + k * hb + let no = base + n * n * 4 + TP_BUDGET * hb + k * hb + let i0 = (t % n) * TT_TEX - 1 + let j0 = (t / n) * TT_TEX - 1 + let p = render3d_st.tp_st_ptr + for b in 0 .. hs { + let tz = min(max(j0 + b, 0), TERRAIN_RES - 1) + for a in 0 .. hs { + let tx = min(max(i0 + a, 0), TERRAIN_RES - 1) + let o = (b * hs + a) * 4 + Vk.put_i32(p, ho + o, float_bits(ter_h(render3d_st, tx, tz))) + Vk.put_i32(p, no + o, ter_n(render3d_st, tx, tz)) + } + } + tp_fill_ortho(render3d_st, t, base + n * n * 4 + 2 * TP_BUDGET * hb, k) +} + +# ... and its photograph, (tt_oside + 2) a side, as sRGB RGBA8 bytes with alpha 255 +function tp_fill_ortho(render3d_st: mut Render3dState, t: int, at: int, k: int) -> void { + let n = render3d_st.tt_n + let side = render3d_st.tt_oside + let os = side + 2 + let top = n * side - 1 + let o0 = at + k * os * os * 4 + let i0 = (t % n) * side - 1 + let j0 = (t / n) * side - 1 + let p: pointer = render3d_st.tp_st_ptr + for b in 0 .. os { + let pz = min(max(j0 + b, 0), top) + for a in 0 .. os { + let v = ter_o(render3d_st, min(max(i0 + a, 0), top), pz) + let o = o0 + (b * os + a) * 4 + p[o] = (v >> 16) & 255 + p[o + 1] = (v >> 8) & 255 + p[o + 2] = v & 255 + p[o + 3] = 255 + } + } +} + +# the frame's k loads into their slots, then the page table if it changed: all in cb, no submit +function tp_upload(render3d_st: mut Render3dState, cb: pointer, base: int, k: int) -> void { + let n = render3d_st.tt_n + let hs = TT_TEX + 2 + let hb = hs * hs * 4 + let os = render3d_st.tt_oside + 2 + let p0 = base + n * n * 4 + let slots = render3d_st.tp_cand_s + gpu_layers_copy(render3d_st, cb, render3d_st.tp_h_tex, hs, hs, p0, hb, slots, k) + gpu_layers_copy(render3d_st, cb, render3d_st.tp_n_tex, hs, hs, p0 + TP_BUDGET * hb, hb, slots, k) + gpu_layers_copy(render3d_st, cb, render3d_st.tp_o_tex, os, os, p0 + 2 * TP_BUDGET * hb, os * os * 4, slots, k) + if not render3d_st.tp_dirty { return } + mem_copy(mem_off(render3d_st.tp_st_ptr, base), data_of(render3d_st.tp_page), n * n * 4) + gpu_layers_copy(render3d_st, cb, render3d_st.tp_page_tex, n, n, base, 0, render3d_st.tp_zero, 1) + render3d_st.tp_dirty = false +} + +# R3D_VKMEM: one line on the pool, next to the rest of the report +function tp_report(render3d_st: Render3dState) -> void { + if not render3d_st.tp_on { return } + tp_say_report(render3d_st.tp_slots - render3d_st.tp_nfree, render3d_st.tp_slots, render3d_st.tp_frame_loads, render3d_st.tp_loads, render3d_st.tp_bytes, render3d_st.tp_reach) +} +@alloc_ok("a report printed once, under R3D_VKMEM") +function tp_say_report(res: int, slots: int, now: int, all: int, bytes: int, r: float) -> void { print(`vkmem: terrain pages: {res} of {slots} slots resident, {now} loaded this frame, {all} over the run, {bytes / 1024} KB on the GPU, {int(r)} m round the camera`) } diff --git a/packages/ludic.render3d/terrain_pages_frame.ludic b/packages/ludic.render3d/terrain_pages_frame.ludic new file mode 100644 index 00000000..d39f1854 --- /dev/null +++ b/packages/ludic.render3d/terrain_pages_frame.ludic @@ -0,0 +1,124 @@ +# terrain_pages_frame.ludic — which fine tiles the GPU holds, once a frame (terrain_pages.ludic). Tiles +# within the reach and a tile of the camera are wanted, nearest first, at most TP_BUDGET loaded a frame; +# a resident tile goes only past the reach and two tiles, so walking a tile edge back and forth loads +# nothing. A load is written into the kept staging (the half the frame in flight is not reading) and +# copied into its slot inside the frame's own command buffer. Nothing here allocates. + +const TP_BUDGET: int = 8 + +function tp_frame(render3d_st: mut Render3dState) -> void { + render3d_st.tp_frame_loads = 0 + if not render3d_st.tp_on or render3d_st.cam_pos == null { return } + let tile_m = tp_tile_m(render3d_st) + let fx = (render3d_st.cam_pos[0] - render3d_st.ter_ox + float(render3d_st.TERRAIN_HALF)) / tile_m + let fz = (render3d_st.cam_pos[2] - render3d_st.ter_oz + float(render3d_st.TERRAIN_HALF)) / tile_m + let n = render3d_st.tt_n + let ct = min(max(int(Math.floor(fz)), 0), n - 1) * n + min(max(int(Math.floor(fx)), 0), n - 1) + # the same tile as last time with nothing left over: every wanted tile is in already + if ct == render3d_st.tp_cam_t and not render3d_st.tp_short { return } + render3d_st.tp_cam_t = ct + let rt = render3d_st.tp_reach / tile_m + tp_evict(render3d_st, fx, fz, rt + 2.0) + let m = tp_pick(render3d_st, fx, fz, rt + 1.0) + if m > 0 or render3d_st.tp_dirty { tp_load(render3d_st, m, fx, fz, rt + 1.0) } +} + +# tile t's centre from (fx, fz), in tiles +function tp_dist(t: int, n: int, fx: float, fz: float) -> float { + let dx = float(t % n) + 0.5 - fx + let dz = float(t / n) + 0.5 - fz + return Math.sqrt(dx * dx + dz * dz) +} + +# every resident tile farther than `out` let go +function tp_evict(render3d_st: mut Render3dState, fx: float, fz: float, out: float) -> void { + for s in 0 .. render3d_st.tp_slots { + let t = render3d_st.tp_tile_of[s] + if t >= 0 and tp_dist(t, render3d_st.tt_n, fx, fz) > out { tp_drop(render3d_st, s) } + } +} +function tp_drop(render3d_st: mut Render3dState, s: int) -> void { + render3d_st.tp_page[render3d_st.tp_tile_of[s]] = 0.0 + render3d_st.tp_tile_of[s] = -1 + render3d_st.tp_free[render3d_st.tp_nfree] = s + render3d_st.tp_nfree += 1 + render3d_st.tp_dirty = true +} + +# the nearest TP_BUDGET wanted tiles not in, into tp_cand; whether more were left is tp_short +function tp_pick(render3d_st: mut Render3dState, fx: float, fz: float, want: float) -> int { + let n = render3d_st.tt_n + var m = 0 + var missing = 0 + for j in max(int(Math.floor(fz - want)), 0) .. min(int(fz + want) + 1, n) { + for i in max(int(Math.floor(fx - want)), 0) .. min(int(fx + want) + 1, n) { + let t = j * n + i + if render3d_st.tp_page[t] != 0.0 { continue } + let d = tp_dist(t, n, fx, fz) + if d > want { continue } + missing += 1 + m = tp_cand_put(render3d_st, m, t, d) + } + } + render3d_st.tp_short = missing > m + return m +} +# tile t at distance d into the sorted candidates, the farthest falling off the end +function tp_cand_put(render3d_st: mut Render3dState, m: int, t: int, d: float) -> int { + var k = m + if k == TP_BUDGET { + if d >= render3d_st.tp_cand_d[k - 1] { return m } + k = TP_BUDGET - 1 + } + while k > 0 and render3d_st.tp_cand_d[k - 1] > d { + render3d_st.tp_cand[k] = render3d_st.tp_cand[k - 1] + render3d_st.tp_cand_d[k] = render3d_st.tp_cand_d[k - 1] + k -= 1 + } + render3d_st.tp_cand[k] = t + render3d_st.tp_cand_d[k] = d + return min(m + 1, TP_BUDGET) +} + +# a free slot, or the farthest resident tile past `keep` given up for one; -1 when every slot is wanted +function tp_take(render3d_st: mut Render3dState, fx: float, fz: float, keep: float) -> int { + if render3d_st.tp_nfree == 0 { + var far = -1 + var fd = keep + for s in 0 .. render3d_st.tp_slots { + let t = render3d_st.tp_tile_of[s] + if t < 0 { continue } + let d = tp_dist(t, render3d_st.tt_n, fx, fz) + if d > fd { fd = d; far = s } + } + if far < 0 { return -1 } + tp_drop(render3d_st, far) + } + render3d_st.tp_nfree -= 1 + return render3d_st.tp_free[render3d_st.tp_nfree] +} + +# the frame's loads written and copied, and the page table after them when it changed +function tp_load(render3d_st: mut Render3dState, m: int, fx: float, fz: float, keep: float) -> void { + # the frame's command buffer first: opening it waits for the frame that read this half last + let cb = gpu_upload_cb(render3d_st) + let base = (render3d_st.gvk_frame_no & 1) * tp_half_bytes(render3d_st) + # the tiles' reads from the file are the pool's own, not a frame asking too much (tt_frame) + let reads = render3d_st.tt_frame_reads + var k = 0 + for c in 0 .. m { + let s = tp_take(render3d_st, fx, fz, keep) + if s < 0 { render3d_st.tp_short = true; break } + let t = render3d_st.tp_cand[c] + tp_fill(render3d_st, t, base, k) + render3d_st.tp_cand_s[k] = s + render3d_st.tp_tile_of[s] = t + render3d_st.tp_page[t] = float(s + 1) + render3d_st.tp_dirty = true + k += 1 + } + render3d_st.tt_frame_reads = reads + render3d_st.tp_frame_loads = k + render3d_st.tp_loads += k + tp_upload(render3d_st, cb, base, k) +} diff --git a/packages/ludic.render3d/terrain_tiles_cut.ludic b/packages/ludic.render3d/terrain_tiles_cut.ludic index 8b4e9f08..89c6f8df 100644 --- a/packages/ludic.render3d/terrain_tiles_cut.ludic +++ b/packages/ludic.render3d/terrain_tiles_cut.ludic @@ -33,11 +33,11 @@ function terrain_tiles_cut(render3d_st: mut Render3dState) -> void { tt_open(render3d_st, path, n, side) tt_make_coarse(render3d_st) if r3d_env_has(render3d_st, "R3D_TT_CHECK") { tt_check(render3d_st) } - free(render3d_st.ter_heights); render3d_st.ter_heights = null - free(render3d_st.ter_ortho_px); render3d_st.ter_ortho_px = null + tp_cut(render3d_st) tt_say_cut(path, TT_HEAD + n * n * (TT_TEX * TT_TEX * 2 + side * side) * 4, (gl_now_us() - t0) / 1000) } + # the heights, tile after tile, each a TT_TEX-row block straight out of the whole copy; their checksum function tt_write_heights(render3d_st: Render3dState, f: pointer, n: int) -> int { var sum = 0 @@ -131,7 +131,7 @@ function tt_close(render3d_st: mut Render3dState) -> void { } function tt_say_no_file(path: string) -> void { print(`r3d: terrain: cannot write {path}; the whole height field stays in memory`) } -function tt_say_cut(path: string, bytes: int, ms: int) -> void { print(`r3d: terrain: tiles written to {path} ({bytes / 1048576} MB, {ms} ms); the whole copies let go`) } +function tt_say_cut(path: string, bytes: int, ms: int) -> void { print(`r3d: terrain: tiles written to {path} ({bytes / 1048576} MB, {ms} ms); the whole copies kept until terrain_tiles_build_done`) } # R3D_TT_CHECK: the tiles' answer against the whole copy's at 4000 points (must be exact), and the # coarse level's (what terrain_height_near falls back to) - while both are still here @@ -158,3 +158,16 @@ function tt_check(render3d_st: mut Render3dState) -> void { } print(`r3d: terrain: tiles vs the whole copy at 4000 points: worst {worst} m; the coarse level: worst {worst_c} m`) } + +# The world's build places trees, rocks and cover over the whole map, asking exact heights +# everywhere before any ring exists: through the tiles that read the file some 25 000 times. So the +# whole copies stay through the build, and the game lets them go here when it is done (the end of +# world_things, and after a swap's terrain_reload). Queries answer the same before and after. +export function terrain_tiles_build_done(render3d_st: mut Render3dState) -> void { + if render3d_st.tt_file == null { return } + if render3d_st.ter_heights != null { free(render3d_st.ter_heights); render3d_st.ter_heights = null } + if render3d_st.ter_ortho_px != null { free(render3d_st.ter_ortho_px); render3d_st.ter_ortho_px = null } + render3d_st.tt_frame_reads = 0 + tt_say_done() +} +function tt_say_done() -> void { print("r3d: terrain: the world is built; the whole height and photograph copies let go, tiles from here") } diff --git a/packages/ludic.render3d/vkmem_report.ludic b/packages/ludic.render3d/vkmem_report.ludic index b34696de..173d0569 100644 --- a/packages/ludic.render3d/vkmem_report.ludic +++ b/packages/ludic.render3d/vkmem_report.ludic @@ -50,6 +50,7 @@ function vkmem_report(render3d_st: Render3dState) -> void { vkmem_layers(render3d_st) vkmem_big_bufs(render3d_st) vkmem_say_ring(render3d_st.gvk_ring_peak, gvk_ring_bytes(render3d_st)) + tp_report(render3d_st) } # the largest images, with their owner, size and format