| … | … | @@ -92,31 +92,40 @@ static HeightBlock read_hblk(const char* path) { |
| 92 | 92 | return b; |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | | // The lookup the shared wrapper's heightmap_gn requires. Bilinear, clamped at |
| 96 | | // the edge -- the apron ring means the clamp is only reached OUTSIDE the block's |
| 97 | | // footprint, so the central difference inside it always has real neighbours. |
| 98 | | static std::string hblk_sampler_glsl(const HeightBlock& b) { |
| 99 | | char buf[2048]; |
| 95 | // THE STORAGE the shared sampler reads -- and nothing else. |
| 96 | // |
| 97 | // The split of duties moved. The consumer declares STORAGE; the wrapper |
| 98 | // declares the SAMPLER: erosion_heightmap_sampler_glsl() emits hblk_texel_l() |
| 99 | // and hblk_sample(), and heightmap_gn calls those, not any function of ours. |
| 100 | // So the old flat bilinear hblk_height_m is gone rather than fixed -- nothing |
| 101 | // called it. (erosion_wrapper_glsl.h:115 still documents the pre-mip contract |
| 102 | // "the consumer must define float hblk_height_m(...)" and is stale.) Its body |
| 103 | // also still indexed hblk_data[] after the buffer was renamed to custom_data, |
| 104 | // which is what produced the undeclared-identifier errors that found this. |
| 105 | // |
| 106 | // THE WHOLE PYRAMID, not level 0. custom_data holds every mip level end to |
| 107 | // end and HBLK_OFF/HBLK_SIDE index into it. Uploading only level 0 is exactly |
| 108 | // #55 section 2.1: HBLK_OFF[1] then points one float past the end of the |
| 109 | // buffer, every sample coarser than the block's pitch reads out of bounds and |
| 110 | // comes back zero, and the terrain is flat wherever the LOD is coarse. |
| 111 | // |
| 112 | // voxel_scale is the sampler's LOD selector and has no declaration of its own |
| 113 | // anywhere: the wrapper emits M_VOXEL, a different name, and emits it AFTER |
| 114 | // this text, so it cannot be aliased here. It comes from the same |
| 115 | // wp.voxel_size that feeds M_VOXEL, so the two cannot disagree. |
| 116 | static std::string hblk_sampler_glsl(const HeightBlock& b, |
| 117 | const HeightBlockMips& m, |
| 118 | float voxel_size) { |
| 119 | char buf[1024]; |
| 100 | 120 | snprintf(buf, sizeof(buf), |
| 101 | 121 | "layout(set=0, binding=1) readonly buffer HBlk { float custom_data[]; };\n" |
| 102 | 122 | "const int HBLK_N = %d;\n" |
| 103 | 123 | "const float HBLK_OX = %.9g;\n" |
| 104 | 124 | "const float HBLK_OZ = %.9g;\n" |
| 105 | 125 | "const float HBLK_MPP = %.9g;\n" |
| 106 | | "float hblk_height_m(float x, float z) {\n" |
| 107 | | " float u = (x - HBLK_OX) / HBLK_MPP;\n" |
| 108 | | " float v = (z - HBLK_OZ) / HBLK_MPP;\n" |
| 109 | | " u = clamp(u, 0.0, float(HBLK_N - 1));\n" |
| 110 | | " v = clamp(v, 0.0, float(HBLK_N - 1));\n" |
| 111 | | " int i0 = int(floor(u)); int j0 = int(floor(v));\n" |
| 112 | | " int i1 = min(i0 + 1, HBLK_N - 1); int j1 = min(j0 + 1, HBLK_N - 1);\n" |
| 113 | | " float fu = u - float(i0); float fv = v - float(j0);\n" |
| 114 | | " float h00 = hblk_data[j0*HBLK_N + i0]; float h10 = hblk_data[j0*HBLK_N + i1];\n" |
| 115 | | " float h01 = hblk_data[j1*HBLK_N + i0]; float h11 = hblk_data[j1*HBLK_N + i1];\n" |
| 116 | | " return mix(mix(h00, h10, fu), mix(h01, h11, fu), fv);\n" |
| 117 | | "}\n", |
| 118 | | b.n, (double)b.ox, (double)b.oz, (double)b.mpp); |
| 119 | | return std::string(buf); |
| 126 | "const float voxel_scale = %.9g;\n", |
| 127 | b.n, (double)b.ox, (double)b.oz, (double)b.mpp, (double)voxel_size); |
| 128 | return std::string(buf) + erosion_heightmap_levels_glsl(m); |
| 120 | 129 | } |
| 121 | 130 | |
| 122 | 131 | struct PushC { |
| … | … | @@ -132,7 +141,8 @@ struct PushC { |
| 132 | 141 | static std::string build_render_shader(const std::string& inc, |
| 133 | 142 | const ErosionWrapperParams& wp, |
| 134 | 143 | int steps, int bisect, |
| 135 | | const HeightBlock* hb = nullptr) { |
| 144 | const HeightBlock* hb = nullptr, |
| 145 | const HeightBlockMips* hbm = nullptr) { |
| 136 | 146 | std::string s = |
| 137 | 147 | "#version 450\n" |
| 138 | 148 | "layout(local_size_x = 8, local_size_y = 8) in;\n" |
| … | … | @@ -143,8 +153,8 @@ static std::string build_render_shader(const std::string& inc, |
| 143 | 153 | "\n"; |
| 144 | 154 | s += inc; |
| 145 | 155 | s += "\n"; |
| 146 | | // Must precede the wrapper: heightmap_gn calls hblk_height_m. |
| 147 | | if (hb) s += hblk_sampler_glsl(*hb); |
| 156 | // Must precede the wrapper: the sampler the wrapper emits reads these. |
| 157 | if (hb) s += hblk_sampler_glsl(*hb, *hbm, wp.voxel_size); |
| 148 | 158 | s += erosion_wrapper_glsl(wp); |
| 149 | 159 | |
| 150 | 160 | char tail[2048]; |
| … | … | @@ -382,6 +392,7 @@ int main(int argc, char** argv) { |
| 382 | 392 | std::string inc = read_file(inc_path); |
| 383 | 393 | |
| 384 | 394 | HeightBlock hb; |
| 395 | HeightBlockMips hbm; |
| 385 | 396 | bool use_hblk = (hblk_path != nullptr); |
| 386 | 397 | if (use_hblk) { |
| 387 | 398 | hb = read_hblk(hblk_path); |
| … | … | @@ -393,16 +404,18 @@ int main(int argc, char** argv) { |
| 393 | 404 | ox = hb.ox + hb.mpp; |
| 394 | 405 | oz = hb.oz + hb.mpp; |
| 395 | 406 | } |
| 407 | hbm = erosion_heightmap_build_mips(hb.h.data(), hb.n); |
| 396 | 408 | printf("heightfield: %s\n", hblk_path); |
| 397 | 409 | printf(" %d x %d px at %g m/px, world x %.0f z %.0f\n", |
| 398 | 410 | hb.n, hb.n, hb.mpp, hb.ox, hb.oz); |
| 399 | 411 | } |
| 400 | 412 | |
| 401 | 413 | std::string src = build_render_shader(inc, wp, steps, bisect, |
| 402 | | use_hblk ? &hb : nullptr); |
| 414 | use_hblk ? &hb : nullptr, |
| 415 | use_hblk ? &hbm : nullptr); |
| 403 | 416 | |
| 404 | 417 | Renderer r; |
| 405 | | if (use_hblk) r.hblk = hb.h; |
| 418 | if (use_hblk) r.hblk = hbm.data; |
| 406 | 419 | r.build(src); |
| 407 | 420 | |
| 408 | 421 | if (verify) { |