pub const TEMPLATE: &str = "#version 450\n\n// ============================================================================\n// CelestialSim custom-surface template (runtime-compiled by Godot\'s\n// RenderingDevice). The user\'s terrain code is spliced in at the user-code\n// marker line below by crate::custom_surface::assemble_source (exactly one\n// occurrence of that marker may exist in this file).\n//\n// One invocation = one tile texel of one chunk in the realize batch. We\n// reconstruct the texel\'s world DIRECTION with the SAME face-barycentric ---\n// gnomonic mapping the built-in realize/bake shaders use (normalize of the\n// linear corner blend --- NEVER slerp), call the user\'s terrain functions, and\n// write the three per-slot surface buffers the realize/bake shaders already\n// read when surface_enabled == 1:\n// surface_height[g] = displacement as a FRACTION of radius (realize clamps\n// to >= 0 and does radius * (1 + h * height_scale))\n// surface_color[g] = rgba8 albedo, little-endian (R = byte 0)\n// surface_normal[g] = rgba8 world normal, encoded * 0.5 + 0.5\n// with g = slot * tile_res^2 + ty * tile_res + tx.\n// ============================================================================\n\nlayout(local_size_x = 64) in;\n\n// std430 --- byte-identical to ChunkGpu (src/chunk_descriptors.rs, 96 bytes) and\n// the chunks binding of ChunkRealize/ChunkTileBake.\nstruct ChunkGpu {\n vec4 frame_a; // xyz = face corner A, w = sphere radius (0 => flat face)\n vec4 frame_b; // xyz = face corner B\n vec4 frame_c; // xyz = face corner C\n vec2 bary0; // chunk corner 0 in FACE-barycentric (wb, wc)\n vec2 bary1; // chunk corner 1\n vec2 bary2; // chunk corner 2\n uint slot; // vertex-pool / atlas slot\n uint level; // LOD level (quadtree depth)\n uint res; // edge resolution\n uint _pad0;\n uint _pad1;\n uint _pad2;\n};\n\nlayout(set = 0, binding = 0, std430) readonly buffer Chunks { ChunkGpu chunks[]; };\nlayout(set = 0, binding = 1, std430) buffer SurfColor { uint surface_color[]; };\nlayout(set = 0, binding = 2, std430) buffer SurfHeight { float surface_height[]; };\nlayout(set = 0, binding = 3, std430) buffer SurfNormal { uint surface_normal[]; };\nlayout(set = 0, binding = 4, std430) readonly buffer Params {\n uint chunk_count;\n uint tile_res;\n float water_height;\n float height_scale;\n // Up to 16 generic user params \u{2014} one per `@export var name: float` on the\n // GDScript builder, spliced in as `#define NAME (P.cels_user[i])` below.\n float cels_user[16];\n} P;\n\n// ---- globals the user\'s terrain functions may read (set per texel in main) ---\nfloat CELS_RADIUS; // this chunk\'s sphere radius (world units)\nfloat CELS_WATER_HEIGHT; // normalized sea level 0..1 (layer\'s water_height)\nfloat CELS_HEIGHT_SCALE; // geometry displacement multiplier (layer\'s height_scale)\n\nuint cels_pack_rgba8(vec3 c) {\n uvec3 q = uvec3(clamp(c, 0.0, 1.0) * 255.0 + 0.5);\n return q.x | (q.y << 8) | (q.z << 16) | (255u << 24);\n}\n\n// Per-param #defines (one per `@export var name: float` on the builder) are\n// spliced at this marker, BEFORE the user code that references them.\n// __CELS_USER_DEFINES__\n\n// ============================================================================\n// USER CODE --- must define:\n// float terrain_height(vec3 dir); // displacement fraction of radius\n// vec3 terrain_color (vec3 dir, float h); // albedo 0..1\n// and MAY define (guard with #define CELS_CUSTOM_NORMAL):\n// vec3 terrain_normal(vec3 dir, float h); // world normal\n// ============================================================================\n// __CELS_USER_CODE__\n// ============================================================================\n\n#ifndef CELS_CUSTOM_NORMAL\n// Auto normal: finite-difference the user\'s height field over a small angular\n// step, bending the two chunk-tangent chords into the surface (matches the\n// built-in procedural bake). Uses the FULL relief so mountains shade strongly\n// even when height_scale keeps the silhouette round.\nvec3 cels_auto_normal(vec3 dir, float h0) {\n vec3 up = abs(dir.y) < 0.99 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);\n vec3 tg = normalize(cross(up, dir));\n vec3 bt = cross(dir, tg);\n const float eps = 0.001;\n vec3 d1 = normalize(dir + tg * eps);\n vec3 d2 = normalize(dir + bt * eps);\n float R = max(CELS_RADIUS, 1.0);\n vec3 p0 = dir * (R * (1.0 + h0));\n vec3 p1 = d1 * (R * (1.0 + terrain_height(d1)));\n vec3 p2 = d2 * (R * (1.0 + terrain_height(d2)));\n vec3 n = normalize(cross(p1 - p0, p2 - p0));\n return dot(n, dir) < 0.0 ? -n : n;\n}\n#endif\n\nvoid main() {\n uint gid = gl_GlobalInvocationID.x;\n uint tr = P.tile_res;\n uint tile_texels = tr * tr;\n uint total = P.chunk_count * tile_texels;\n if (tr == 0u || gid >= total) {\n return;\n }\n\n uint ci = gid / tile_texels; // chunk index within the batch\n uint texel = gid % tile_texels; // local tile texel\n uint tx = texel % tr;\n uint ty = texel / tr;\n\n // Texel-centre chunk-local (u, v); the square\'s lower-left triangle\n // (u + v <= 1) covers the chunk. Out-of-triangle texels fold onto the\n // diagonal so edge vertices / the bilinear atlas read valid data.\n float u = (float(tx) + 0.5) / float(tr);\n float v = (float(ty) + 0.5) / float(tr);\n if (u + v > 1.0) {\n float s = u + v;\n u /= s;\n v /= s;\n }\n\n ChunkGpu ch = chunks[ci];\n float radius = ch.frame_a.w;\n\n // chunk-local (u,v) -> face-barycentric -> gnomonic world direction.\n float wa = 1.0 - u - v;\n vec2 fbc = wa * ch.bary0 + u * ch.bary1 + v * ch.bary2;\n vec3 lin = ch.frame_a.xyz * (1.0 - fbc.x - fbc.y)\n + ch.frame_b.xyz * fbc.x\n + ch.frame_c.xyz * fbc.y;\n vec3 dir = normalize(lin);\n\n CELS_RADIUS = radius;\n CELS_WATER_HEIGHT = P.water_height;\n CELS_HEIGHT_SCALE = P.height_scale;\n\n float h = terrain_height(dir);\n vec3 col = terrain_color(dir, h);\n#ifdef CELS_CUSTOM_NORMAL\n vec3 nrm = normalize(terrain_normal(dir, h));\n#else\n vec3 nrm = cels_auto_normal(dir, h);\n#endif\n\n uint g = ch.slot * tile_texels + ty * tr + tx;\n surface_height[g] = h;\n surface_color[g] = cels_pack_rgba8(col);\n surface_normal[g] = cels_pack_rgba8(nrm * 0.5 + 0.5);\n}\n";Expand description
The library GLSL template. The user’s source replaces the
// __CELS_USER_CODE__ marker line.