#version 450
// ============================================================================
// Stage 5: Surface Normals
//
// Template shader: the SDF body is injected at the GENERATOR_SDF_BODY marker
// by DCWorker at shader composition time.
//
// For each active edge intersection, compute the gradient of the density
// field at the intersection point via central differences using the full
// density evaluation function.
//
// Input:
// density[] — density grid (unused by noise SDF, used by grid SDF)
// edge_data[] — position set by Stage 4
// Params UBO — density evaluation parameters
// voronoi_data[] — voronoi cell data
// perm_tables[] — noise permutation tables
// csg_primitives — CSG primitive list
// counts[] — counts[0] = active edge count
//
// Output:
// edge_data[active].normal — updated with normalized gradient
//
// Dispatch: ceil(active_count / 256), 1, 1
// ============================================================================
#define THREADS 256
layout(local_size_x = THREADS, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 0, std430) readonly buffer DensityBuf { float density[]; };
layout(set = 0, binding = 3, std430) buffer EdgeData { float edge_data[]; };
// Density evaluation parameters (same layout as edge_intersect.glsl)
layout(set = 0, binding = 4, std140) uniform Params {
int grid_stride;
float voxel_scale;
float origin_x;
float origin_y;
float origin_z;
float primary_frequency;
float primary_strength;
float tunnel_frequency;
float tunnel_strength;
float tunnel_radius;
float ridge_frequency;
float ridge_strength;
float warp_frequency;
float warp_strength;
float detail_frequency;
float detail_strength;
float ground_bias;
float solid_floor_depth;
float sky_height;
float flat_center_radius;
float floor_strength;
float floor_height_min;
float floor_height_max;
float room_height;
float terrain_size;
int primary_octaves;
int tunnel_octaves;
int ridge_octaves;
int warp_octaves;
int detail_octaves;
int compute_normals;
int seed;
int voronoi_cell_count;
int use_noise;
int spherical;
float planet_radius;
float planet_center_x;
float planet_center_y;
float planet_center_z;
float island_radius;
};
layout(set = 0, binding = 5, std430) readonly buffer VoronoiBuf {
int voronoi_count;
float voronoi_data[];
};
layout(set = 0, binding = 6, std430) readonly buffer PermBuf {
int perm_tables[];
};
// CSG primitives
struct CSGPrimitive {
uint prim_type;
uint op;
uint group;
uint flags;
float pos_x, pos_y, pos_z;
float _pad0;
float size_x, size_y, size_z;
float _pad1;
};
layout(set = 0, binding = 7, std430) readonly buffer CSGBuf {
uint csg_count;
uint _csg_pad[3];
CSGPrimitive csg_primitives[];
};
layout(set = 0, binding = 8, std430) readonly buffer CountBuf { uint counts[]; };
layout(push_constant) uniform PushConstants {
vec3 cell_origin;
float voxel_size;
ivec3 grid_dims;
uint total_elements;
uint mesh_quality_flags;
uint _pc_pad0;
uint _pc_pad1;
uint _pc_pad2;
};
// =====================================================================
// SDF body — injected by DCWorker from generator GLSL
// Must define: float evaluate_density(float x, float y, float z)
// =====================================================================
// GENERATOR_SDF_BODY
// =====================================================================
// Main: compute normals via central differences on the full SDF
// =====================================================================
void main() {
uint gid = gl_GlobalInvocationID.x;
uint active_count = counts[0];
if (gid >= active_count) return;
uint base = gid * 8u;
// Read world-space position (set by edge_intersect)
float wx = edge_data[base + 0u];
float wy = edge_data[base + 1u];
float wz = edge_data[base + 2u];
// Central differences on the full SDF
float eps = voxel_size * 0.01;
vec3 n;
n.x = evaluate_density(wx + eps, wy, wz) - evaluate_density(wx - eps, wy, wz);
n.y = evaluate_density(wx, wy + eps, wz) - evaluate_density(wx, wy - eps, wz);
n.z = evaluate_density(wx, wy, wz + eps) - evaluate_density(wx, wy, wz - eps);
float len = length(n);
if (len > 1e-8) {
n /= len;
} else {
n = vec3(0.0, 1.0, 0.0); // fallback
}
edge_data[base + 3u] = n.x;
edge_data[base + 4u] = n.y;
edge_data[base + 5u] = n.z;
}