| … | … | @@ -86,6 +86,7 @@ |
| 86 | 86 | #include <vulkan/vulkan.h> |
| 87 | 87 | |
| 88 | 88 | #include "erosion_cpp_shim.h" // brings in erosion_filter.inc as C++ |
| 89 | #include "erosion_wrapper_glsl.h" // the map wrapper, emitted once (see that file) |
| 89 | 90 | |
| 90 | 91 | #include <cstdio> |
| 91 | 92 | #include <cstdlib> |
| … | … | @@ -449,6 +450,42 @@ static uint32_t checksum(const std::string& s) { |
| 449 | 450 | return h; |
| 450 | 451 | } |
| 451 | 452 | |
| 453 | // The ONE place the map's constants above become wrapper parameters. It exists |
| 454 | // because it was got wrong immediately: the lookdev renderer hand-copied these |
| 455 | // values into its own defaults, MAP_FLOOR_Y moved from -1000 to -10900 in |
| 456 | // 156b2e4 the same morning, and the renderer was quietly 7.3 km out until its |
| 457 | // verify gate caught it. A second consumer must call THIS, not retype them. |
| 458 | static ErosionWrapperParams erosion_wrapper_params_from_map() { |
| 459 | ErosionWrapperParams wp; |
| 460 | wp.floor_y = MAP_FLOOR_Y; |
| 461 | wp.ceiling_y = MAP_CEILING_Y; |
| 462 | wp.terrain_height = MAP_TERRAIN_HEIGHT; |
| 463 | wp.mountain_scale = MAP_MOUNTAIN_SCALE; |
| 464 | wp.mountain_height = MAP_MOUNTAIN_HEIGHT; |
| 465 | wp.mountain_gain = MAP_MOUNTAIN_GAIN; |
| 466 | wp.mountain_variance = MAP_MOUNTAIN_VARIANCE; |
| 467 | wp.mountain_octaves = MAP_MOUNTAIN_OCT; |
| 468 | wp.voxel_size = MAP_VOXEL_SIZE; |
| 469 | wp.relief_scale = MAP_RELIEF_SCALE; |
| 470 | wp.relief_floor = MAP_RELIEF_FLOOR; |
| 471 | wp.detail_scale = MAP_DETAIL_SCALE; |
| 472 | wp.detail_height = MAP_DETAIL_HEIGHT; |
| 473 | wp.detail_gain = MAP_DETAIL_GAIN; |
| 474 | wp.detail_octaves = MAP_DETAIL_OCT; |
| 475 | wp.noise_lacunarity = MAP_NOISE_LACUNARITY; |
| 476 | wp.gn_scale = GN_SCALE; |
| 477 | wp.gn_gain = GN_GAIN; |
| 478 | wp.gn_detail = GN_DETAIL; |
| 479 | wp.en_height_scale = EN_HEIGHT_SCALE; |
| 480 | wp.en_strength = EN_STRENGTH; |
| 481 | wp.en_slope_power = EN_SLOPE_POWER; |
| 482 | wp.en_cell_scale = EN_CELL_SCALE; |
| 483 | wp.en_gain = EN_GAIN; |
| 484 | wp.en_octaves = EN_OCTAVES; |
| 485 | wp.en_erosion_scale = EN_EROSION_SCALE; |
| 486 | return wp; |
| 487 | } |
| 488 | |
| 452 | 489 | static std::string build_shader(const std::string& inc) { |
| 453 | 490 | std::string s = |
| 454 | 491 | "#version 450\n" |
| … | … | @@ -460,88 +497,20 @@ static std::string build_shader(const std::string& inc) { |
| 460 | 497 | s += inc; |
| 461 | 498 | s += "\n"; |
| 462 | 499 | |
| 463 | | // The two wrappers, expressed in GLSL exactly as the C++ above expresses |
| 464 | | // them. Kept adjacent to their C++ twins in this file so a change to one |
| 465 | | // that is not made to the other is visible in a single diff hunk. |
| 466 | | char cfg[4096]; |
| 467 | | snprintf(cfg, sizeof(cfg), |
| 468 | | "const float M_FLOOR_Y=%.9g, M_CEIL_Y=%.9g, M_TH=%.9g;\n" |
| 469 | | "const float M_MSCALE=%.9g, M_MHEIGHT=%.9g, M_MGAIN=%.9g;\n" |
| 470 | | "const float M_MVAR=%.9g;\n" |
| 471 | | "const float M_VOXEL=%.9g;\n" |
| 472 | | "const int M_MOCT=%d;\n" |
| 473 | | "const float M_RSCALE=%.9g, M_RFLOOR=%.9g;\n" |
| 474 | | "const float M_DSCALE=%.9g, M_DHEIGHT=%.9g, M_DGAIN=%.9g;\n" |
| 475 | | "const int M_DOCT=%d;\n" |
| 476 | | "const float M_LAC=%.9g;\n" |
| 477 | | "const float GN_SCALE=%.9g, GN_GAIN=%.9g;\n" |
| 478 | | "const int GN_DETAIL=%d;\n" |
| 479 | | "const float EN_HS=%.9g, EN_STR=%.9g, EN_SP=%.9g, EN_CS=%.9g, EN_GAIN=%.9g;\n" |
| 480 | | "const int EN_OCT=%d;\n" |
| 481 | | "const float EN_ESCALE=%.9g;\n", |
| 482 | | MAP_FLOOR_Y, MAP_CEILING_Y, MAP_TERRAIN_HEIGHT, |
| 483 | | MAP_MOUNTAIN_SCALE, MAP_MOUNTAIN_HEIGHT, MAP_MOUNTAIN_GAIN, |
| 484 | | MAP_MOUNTAIN_VARIANCE, MAP_VOXEL_SIZE, MAP_MOUNTAIN_OCT, |
| 485 | | MAP_RELIEF_SCALE, MAP_RELIEF_FLOOR, |
| 486 | | MAP_DETAIL_SCALE, MAP_DETAIL_HEIGHT, MAP_DETAIL_GAIN, MAP_DETAIL_OCT, |
| 487 | | MAP_NOISE_LACUNARITY, |
| 488 | | GN_SCALE, GN_GAIN, GN_DETAIL, |
| 489 | | EN_HEIGHT_SCALE, EN_STRENGTH, EN_SLOPE_POWER, EN_CELL_SCALE, EN_GAIN, EN_OCTAVES, |
| 490 | | EN_EROSION_SCALE); |
| 491 | | s += cfg; |
| 500 | // The two wrappers used to be written out here as GLSL text, adjacent to |
| 501 | // their C++ twins above. They now come from erosion_wrapper_glsl.h, which |
| 502 | // emits the identical string -- hoisted the moment a SECOND GPU consumer |
| 503 | // (the lookdev renderer) needed the same wrapper and would otherwise have |
| 504 | // become a third copy. The C++ twins above are deliberately NOT hoisted: |
| 505 | // this oracle's job is to compare a GLSL transcription against a C++ one, |
| 506 | // and sharing both halves would leave it comparing a thing with itself. |
| 507 | // |
| 508 | // Verified byte-for-byte at the hoist: 33840 bytes, checksum 1254537005, |
| 509 | // before and after. |
| 510 | ErosionWrapperParams wp = erosion_wrapper_params_from_map(); |
| 511 | s += erosion_wrapper_glsl(wp); |
| 492 | 512 | |
| 493 | 513 | s += |
| 494 | | "vec3 terrain_offset(float x, float z) {\n" |
| 495 | | " float inv_scale = 1.0 / M_TH;\n" |
| 496 | | " float mtn_lim = nyquist_octaves(M_TH / M_MSCALE, M_VOXEL, M_LAC);\n" |
| 497 | | " vec3 mtn = fractal_multifractal_2d_lod(vec2(x,z)*inv_scale*M_MSCALE, 1.0, M_MOCT, M_LAC, M_MGAIN, M_MVAR, mtn_lim);\n" |
| 498 | | " float relief_amp = 1.0;\n" |
| 499 | | " if (M_RSCALE > 0.0) {\n" |
| 500 | | " vec3 rel = fractal_noise_2d_lod(vec2(x,z)*inv_scale*M_RSCALE, 1.0, 2, 2.0, 0.5, nyquist_octaves(M_TH/M_RSCALE, M_VOXEL, 2.0));\n" |
| 501 | | " float t = clamp(rel.x*0.5+0.5, 0.0, 1.0);\n" |
| 502 | | " relief_amp = M_RFLOOR + (1.0-M_RFLOOR)*t;\n" |
| 503 | | " }\n" |
| 504 | | " float amp = M_MHEIGHT * relief_amp;\n" |
| 505 | | " return vec3(mtn.x, mtn.y*M_MSCALE, mtn.z*M_MSCALE) * (amp * 8.0);\n" |
| 506 | | "}\n" |
| 507 | | "float detail_offset(float x, float z) {\n" |
| 508 | | " if (M_DSCALE <= 0.0) return 0.0;\n" |
| 509 | | " float inv_scale = 1.0 / M_TH;\n" |
| 510 | | " float y_range = M_CEIL_Y - M_FLOOR_Y;\n" |
| 511 | | " vec3 det = fractal_noise_2d_lod(vec2(x,z)*inv_scale*M_DSCALE, 1.0, M_DOCT, M_LAC, M_DGAIN, nyquist_octaves(M_TH/M_DSCALE, M_VOXEL, M_LAC));\n" |
| 512 | | " return det.x * (M_DHEIGHT / y_range);\n" |
| 513 | | "}\n" |
| 514 | | "float y_mapped_of(float y, float det_off) {\n" |
| 515 | | " float y_range = M_CEIL_Y - M_FLOOR_Y;\n" |
| 516 | | " return (y - M_FLOOR_Y) * (EN_HS / y_range) - det_off * EN_HS;\n" |
| 517 | | "}\n" |
| 518 | | "float clip_d(float d, float y) {\n" |
| 519 | | " d = max(d, (M_FLOOR_Y - y) * 2.0);\n" |
| 520 | | " d = min(d, (M_CEIL_Y - y) * 2.0);\n" |
| 521 | | " return d;\n" |
| 522 | | "}\n" |
| 523 | | "float gpu_wrapper(float x, float y, float z) {\n" |
| 524 | | " float inv_scale = 1.0 / M_TH;\n" |
| 525 | | " float y_range = M_CEIL_Y - M_FLOOR_Y;\n" |
| 526 | | " float y_mapped = y_mapped_of(y, detail_offset(x, z));\n" |
| 527 | | " vec2 p = vec2(x*inv_scale, z*inv_scale);\n" |
| 528 | | " vec3 gn = fractal_noise_2d(p, GN_SCALE, GN_DETAIL, 2.0, GN_GAIN);\n" |
| 529 | | " vec2 en = erosion_node_eval(p, gn + terrain_offset(x,z), EN_HS, EN_STR, EN_SP, EN_CS, EN_OCT, EN_GAIN, 4.8, EN_ESCALE);\n" |
| 530 | | " float d = en.x - y_mapped;\n" |
| 531 | | " d *= (y_range / EN_HS);\n" |
| 532 | | " return clip_d(d, y);\n" |
| 533 | | "}\n" |
| 534 | | "float cpu_wrapper(float x, float y, float z) {\n" |
| 535 | | " float inv_scale = 1.0 / M_TH;\n" |
| 536 | | " float y_range = M_CEIL_Y - M_FLOOR_Y;\n" |
| 537 | | " float y_mapped = y_mapped_of(y, detail_offset(x, z));\n" |
| 538 | | " vec2 p = vec2(x*inv_scale, z*inv_scale);\n" |
| 539 | | " vec3 gn = fractal_noise_2d(p, GN_SCALE, GN_DETAIL, 2.0, GN_GAIN);\n" |
| 540 | | " vec2 en = erosion_node_eval(p, gn + terrain_offset(x,z), EN_HS, EN_STR, EN_SP, EN_CS, EN_OCT, EN_GAIN, 4.8, EN_ESCALE);\n" |
| 541 | | " float d = en.x - y_mapped;\n" |
| 542 | | " d *= (y_range / EN_HS);\n" |
| 543 | | " return clip_d(d, y);\n" |
| 544 | | "}\n" |
| 545 | 514 | "void main() {\n" |
| 546 | 515 | " uint i = gl_GlobalInvocationID.x;\n" |
| 547 | 516 | " if (i >= pc.count) return;\n" |