#version 450
// Edge classification compute shader for Dual Contouring terrain (Stage 2).
// For each voxel in the 32³ grid, check 3 edges (+X, +Y, +Z) for sign changes
// in the density field produced by Stage 1 (density_fill).
//
// A sign change on an edge means the isosurface crosses that edge,
// which will later require a vertex placement (QEF solve).
#define THREADS 256
layout(local_size_x = THREADS) in;
// ---- Buffers ----
layout(set = 0, binding = 0, std430) readonly buffer DensityBuf { float density[]; };
layout(set = 0, binding = 1, std430) writeonly buffer EdgeFlagBuf { uint edge_flags[]; };
// ---- Push constants ----
layout(push_constant) uniform PushConstants {
vec3 cell_origin; // 0: world-space origin of this chunk
float voxel_size; // 12: size of one voxel in world units
ivec3 grid_dims; // 16: voxel grid dimensions (32, 32, 32)
uint total_elements;
uint mesh_quality_flags;
uint _pc_pad0;
uint _pc_pad1;
uint _pc_pad2;
};
// Density grid is (V+1)³ where V = grid_dims.x (= 32), so stride = 33.
// density index: z * S * S + y * S + x where S = grid_dims.x + 1
void main() {
uint gid = gl_GlobalInvocationID.x;
if (gid >= total_elements)
return;
int V = grid_dims.x; // 32
int S = V + 1; // 33 (density grid stride)
// Decode linear voxel index to (x, y, z)
int z = int(gid) / (V * V);
int y = (int(gid) % (V * V)) / V;
int x = int(gid) % V;
// Density at corner (x, y, z)
float d0 = density[z * S * S + y * S + x];
// Check 3 edges for sign changes
// Edge 0: +X direction (x, y, z) -> (x+1, y, z)
float dx = density[z * S * S + y * S + (x + 1)];
edge_flags[gid * 3u + 0u] = ((d0 >= 0.0) != (dx >= 0.0)) ? 1u : 0u;
// Edge 1: +Y direction (x, y, z) -> (x, y+1, z)
float dy = density[z * S * S + (y + 1) * S + x];
edge_flags[gid * 3u + 1u] = ((d0 >= 0.0) != (dy >= 0.0)) ? 1u : 0u;
// Edge 2: +Z direction (x, y, z) -> (x, y, z+1)
float dz = density[(z + 1) * S * S + y * S + x];
edge_flags[gid * 3u + 2u] = ((d0 >= 0.0) != (dz >= 0.0)) ? 1u : 0u;
}