OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / distribute / hypertps-win64 / shaders / voxel_classify.glsl

94 lines · glsl ref:
#version 450

// ============================================================================
// Stage 6: Voxel Classification
//
// For each voxel in the 32³ grid, check if it has ANY sign-change edge
// among its 12 edges (4 per axis shared with neighbors). If yes, this
// voxel is "active" and needs a QEF-solved vertex.
//
// Each voxel (x,y,z) owns 3 edges starting at it: +X, +Y, +Z.
// It also shares edges from neighbors:
//   (x-1,y,z).X, (x,y-1,z).Y, (x,y,z-1).Z
//   and the 6 remaining diagonal neighbors' edges.
//
// Simplified: just check the 12 edges of the cube [x,x+1]×[y,y+1]×[z,z+1].
//
// Input:  edge_flags[32³ × 3]
// Output: voxel_flags[32³] — 1 if active, 0 otherwise
//
// Note: We check a (voxels-1)³ grid because the QEF vertex for voxel (x,y,z)
// sits inside the cube formed by corners (x,y,z) to (x+1,y+1,z+1), which
// requires edges up to (x+1,y+1,z+1) — only valid for x,y,z < voxels-1.
// Boundary voxels (x=voxels-1, y=voxels-1, z=voxels-1) are zeroed explicitly
// so no CPU buffer_update() clear is needed (Phase B).
//
// Dispatch: ceil(voxels³ / 256), 1, 1
// ============================================================================

#define THREADS 256

layout(local_size_x = THREADS, local_size_y = 1, local_size_z = 1) in;

layout(set = 0, binding = 1, std430) readonly  buffer EdgeFlagsBuf  { uint edge_flags[];  };
layout(set = 0, binding = 4, std430) writeonly buffer VoxelFlagsBuf { uint voxel_flags[]; };

layout(push_constant) uniform PushConstants {
    vec3  cell_origin;
    float voxel_size;
    ivec3 grid_dims;       // voxel grid dims (e.g. 16,16,16)
    uint  total_elements;
    uint  mesh_quality_flags;
    uint  _pc_pad0;
    uint  _pc_pad1;
    uint  _pc_pad2;
};

// Get edge flag for edge starting at (ex,ey,ez) in direction dir (0=X,1=Y,2=Z)
uint get_edge(int ex, int ey, int ez, int dir, int voxels) {
    if (ex < 0 || ey < 0 || ez < 0 || ex >= voxels || ey >= voxels || ez >= voxels)
        return 0u;
    return edge_flags[(ez * voxels * voxels + ey * voxels + ex) * 3 + dir];
}

void main() {
    uint gid = gl_GlobalInvocationID.x;
    if (gid >= total_elements) return;

    int voxels = grid_dims.x;     // 16 (edge grid dimension)
    int inner = voxels - 1;       // 15 (range of valid QEF voxels)

    // Decode linear index in full voxels³ grid
    int z = int(gid) / (voxels * voxels);
    int rem = int(gid) % (voxels * voxels);
    int y = rem / voxels;
    int x = rem % voxels;

    // Boundary voxels: zero-fill (Phase B: eliminates CPU buffer_update clear)
    if (x >= inner || y >= inner || z >= inner) {
        voxel_flags[gid] = 0u;
        return;
    }

    // Check all 12 edges of the cube [x..x+1, y..y+1, z..z+1]
    // 4 X-axis edges
    uint has_edge =
        get_edge(x, y,   z,   0, voxels) |
        get_edge(x, y+1, z,   0, voxels) |
        get_edge(x, y,   z+1, 0, voxels) |
        get_edge(x, y+1, z+1, 0, voxels) |
    // 4 Y-axis edges
        get_edge(x,   y, z,   1, voxels) |
        get_edge(x+1, y, z,   1, voxels) |
        get_edge(x,   y, z+1, 1, voxels) |
        get_edge(x+1, y, z+1, 1, voxels) |
    // 4 Z-axis edges
        get_edge(x,   y,   z, 2, voxels) |
        get_edge(x+1, y,   z, 2, voxels) |
        get_edge(x,   y+1, z, 2, voxels) |
        get_edge(x+1, y+1, z, 2, voxels);

    // gid == z*voxels*voxels + y*voxels + x, so write directly
    voxel_flags[gid] = (has_edge != 0u) ? 1u : 0u;
}