Skip to content

Instantly share code, notes, and snippets.

@Matt54
Created August 19, 2025 23:21
Show Gist options
  • Select an option

  • Save Matt54/1a5066c4434e11686df1e7d48a800945 to your computer and use it in GitHub Desktop.

Select an option

Save Matt54/1a5066c4434e11686df1e7d48a800945 to your computer and use it in GitHub Desktop.

Revisions

  1. Matt54 created this gist Aug 19, 2025.
    20 changes: 20 additions & 0 deletions MarchingCubesBlobParams.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    #ifndef MarchingCubesBlobParams_h
    #define MarchingCubesBlobParams_h

    #include <simd/simd.h>

    typedef struct {
    simd_float3 center;
    float radius;
    } Sphere;

    typedef struct {
    simd_uint3 cells;
    simd_float3 origin;
    simd_float3 cellSize;
    float isoLevel;
    uint32_t sphereCount;
    float smoothK;
    } MarchingCubesBlobParams;

    #endif /* MarchingCubesBlobParams_h */
    388 changes: 388 additions & 0 deletions MarchingCubesBlobView.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,388 @@
    import Metal
    import RealityKit
    import SwiftUI

    struct MarchingCubesBlobView: View {
    @State var entity: Entity?
    @State var mesh: LowLevelMesh?
    @State var positions: [SIMD3<Float>] = []
    @State var targets: [SIMD3<Float>] = []
    @State var radii: [Float] = []
    @State var speeds: [Float] = []
    @State var smoothK: Float = 0.06
    @State var speed: Float = 0.25
    @State var sphereCountUI: Int = 24
    @State var targetRadius: Float = 0.0125
    @State var radiusVariance: Float = 0.125
    @State var radianceRoughness: Float = 0.12
    @State var materialRoughness: Float = 0.33
    @State var eta: Float = 0.2
    @State var specular: Float = 0.12
    @State var timer: Timer?
    @State var lastFrameTime = CACurrentMediaTime()

    // Grid sizing (similar pattern to single-shape example)
    let volumeRadius: Float = 0.175
    var cellsPerAxis: UInt32 = 80
    var cells: SIMD3<UInt32> { SIMD3<UInt32>(cellsPerAxis, cellsPerAxis, cellsPerAxis) }
    var cellSize: SIMD3<Float> {
    let ratio = volumeRadius / Float(cellsPerAxis) * 2
    return SIMD3<Float>(ratio, ratio, ratio)
    }

    // Spheres
    let maxSpheres = 64
    struct SphereData {
    var center: SIMD3<Float>
    var radius: Float
    }

    // Metal
    let device: MTLDevice
    let commandQueue: MTLCommandQueue
    let computePipelineState: MTLComputePipelineState
    let vertexCountBuffer: MTLBuffer
    let spheresBuffer: MTLBuffer

    enum ShaderGraphParameter: String {
    case radianceRoughness
    case materialRoughness
    case eta
    case specular
    }

    init() {
    let device = MTLCreateSystemDefaultDevice()!
    self.device = device
    self.commandQueue = device.makeCommandQueue()!
    let library = device.makeDefaultLibrary()!
    let function = library.makeFunction(name: "generateMarchingCubesBlobMesh")!
    self.computePipelineState = try! device.makeComputePipelineState(function: function)
    self.vertexCountBuffer = device.makeBuffer(length: MemoryLayout<UInt32>.stride, options: .storageModeShared)!
    self.spheresBuffer = device.makeBuffer(length: maxSpheres * MemoryLayout<SphereData>.stride, options: .storageModeShared)!
    }

    var body: some View {
    VStack(spacing: 40) {
    RealityView { content in
    let maxCellCount = Int(cells.x * cells.y * cells.z)
    let vertexCapacity = 15 * maxCellCount
    let indexCapacity = vertexCapacity

    let lowLevelMesh = try! VertexPositionNormal.initializeMesh(vertexCapacity: vertexCapacity,
    indexCapacity: indexCapacity)
    let meshResource = try! await MeshResource(from: lowLevelMesh)
    let material = try! await getMaterial()
    let entity = ModelEntity(mesh: meshResource, materials: [material])
    content.add(entity)
    self.mesh = lowLevelMesh
    self.entity = entity

    await removeDefaultIBL()

    reseedSpheres(count: sphereCountUI)
    startTimer()
    }
    .onDisappear { stopTimer() }

    VStack {
    HStack {
    Text("Spheres: \(sphereCountUI)")
    Spacer()
    Slider(value: Binding(get: { Double(sphereCountUI) },
    set: { newVal in
    sphereCountUI = Int(newVal)
    reseedSpheres(count: sphereCountUI)
    }),
    in: 1...Double(maxSpheres), step: 1)
    .frame(width: 300)
    }
    HStack {
    Text("Target Radius: \(targetRadius, specifier: "%.4f")")
    Spacer()
    Slider(value: Binding(get: { Double(targetRadius) },
    set: { newVal in
    targetRadius = Float(newVal)
    reseedSpheres(count: sphereCountUI)
    }),
    in: 0.005...0.05)
    .frame(width: 300)
    }
    HStack {
    Text("Radius Variance: \(radiusVariance*100, specifier: "%.1f")%")
    Spacer()
    Slider(value: Binding(get: { Double(radiusVariance) },
    set: { newVal in
    radiusVariance = Float(newVal)
    reseedSpheres(count: sphereCountUI)
    }),
    in: 0.0...1.0)
    .frame(width: 300)
    }
    HStack {
    Text("Smooth K: \(smoothK, specifier: "%.3f")")
    Spacer()
    Slider(value: $smoothK, in: 0.0...0.12)
    .frame(width: 300)
    }
    HStack {
    Text("Speed: \(speed, specifier: "%.2f")")
    Spacer()
    Slider(value: $speed, in: 0.0...1.0)
    .frame(width: 300)
    }

    HStack {
    Text("Radiance Rough: \(radianceRoughness, specifier: "%.2f")")
    Spacer()
    Slider(value: $radianceRoughness, in: 0...0.4)
    .frame(width: 300)
    }
    .onChange(of: radianceRoughness) {
    try? setShaderGraphParameterValue(.radianceRoughness, value: radianceRoughness)
    }

    HStack {
    Text("Material Rough: \(materialRoughness, specifier: "%.2f")")
    Spacer()
    Slider(value: $materialRoughness, in: 0...0.4)
    .frame(width: 300)
    }
    .onChange(of: materialRoughness) {
    try? setShaderGraphParameterValue(.materialRoughness, value: materialRoughness)
    }

    HStack {
    Text("ETA: \(eta, specifier: "%.2f")")
    Spacer()
    Slider(value: $eta, in: 0...1)
    .frame(width: 300)
    }
    .onChange(of: eta) {
    try? setShaderGraphParameterValue(.eta, value: eta)
    }

    HStack {
    Text("Specular: \(specular, specifier: "%.2f")")
    Spacer()
    Slider(value: $specular, in: 0...1)
    .frame(width: 300)
    }
    .onChange(of: specular) {
    try? setShaderGraphParameterValue(.specular, value: specular)
    }
    }
    .frame(width: 500)
    .padding()
    .glassBackgroundEffect()
    }
    }
    }

    // MARK: Timer / Animation
    extension MarchingCubesBlobView {
    func startTimer() {
    stopTimer()
    lastFrameTime = CACurrentMediaTime()
    timer = Timer.scheduledTimer(withTimeInterval: 1.0 / 60.0, repeats: true) { _ in
    let now = CACurrentMediaTime()
    let deltaTime = max(0.0, now - lastFrameTime)
    lastFrameTime = now
    updateSpheres(deltaTime: Float(deltaTime))
    updateMesh()
    }
    }

    func stopTimer() {
    timer?.invalidate()
    timer = nil
    }
    }

    // MARK: Sphere Size and Position
    extension MarchingCubesBlobView {
    private func randomPositionWithPadding() -> SIMD3<Float> {
    let gridSizeWorldSpace = SIMD3<Float>(Float(cells.x), Float(cells.y), Float(cells.z)) * cellSize
    let minWorldSpace = -0.5 * gridSizeWorldSpace
    let maxWorldSpace = minWorldSpace + gridSizeWorldSpace

    // Add padding to prevent spheres from going too close to edges
    // smoothK increases size so we increase padding to compensate
    let padding: Float = volumeRadius * (0.3+smoothK*3)
    let paddedMin = minWorldSpace + padding
    let paddedMax = maxWorldSpace - padding

    return SIMD3<Float>(
    Float.random(in: paddedMin.x...paddedMax.x),
    Float.random(in: paddedMin.y...paddedMax.y),
    Float.random(in: paddedMin.z...paddedMax.z)
    )
    }

    func reseedSpheres(count: Int) {
    // Calculate min/max radius based on target and variance
    let varianceAmount = targetRadius * radiusVariance
    let minR = targetRadius - varianceAmount
    let maxR = targetRadius + varianceAmount

    positions = (0..<count).map { _ in randomPositionWithPadding() }
    targets = (0..<count).map { _ in randomPositionWithPadding() }
    radii = (0..<count).map { _ in Float.random(in: minR...maxR) }
    speeds = (0..<count).map { _ in Float.random(in: 0.5...1.5) }
    }

    func updateSpheres(deltaTime: Float) {
    guard positions.count == sphereCountUI else { return }

    for sphereIndex in 0..<sphereCountUI {
    let currentPosition = positions[sphereIndex]
    let targetPosition = targets[sphereIndex]
    var direction = targetPosition - currentPosition
    let distance = simd_length(direction)

    if distance < 1e-5 {
    targets[sphereIndex] = randomPositionWithPadding()
    continue
    }

    direction /= distance
    let movementStep = speed * speeds[sphereIndex] * deltaTime * volumeRadius * 1.5

    if movementStep >= distance {
    positions[sphereIndex] = targetPosition
    targets[sphereIndex] = randomPositionWithPadding()
    } else {
    positions[sphereIndex] = currentPosition + direction * movementStep
    }
    }

    // Write spheres to buffer
    let sphereDataPointer = spheresBuffer.contents().bindMemory(to: SphereData.self, capacity: maxSpheres)
    for sphereIndex in 0..<sphereCountUI {
    sphereDataPointer[sphereIndex] = SphereData(center: positions[sphereIndex], radius: radii[sphereIndex])
    }
    }
    }

    // MARK: Mesh
    extension MarchingCubesBlobView {
    func updateMesh() {
    guard let mesh = mesh,
    let commandBuffer = commandQueue.makeCommandBuffer(),
    let computeEncoder = commandBuffer.makeComputeCommandEncoder()
    else { return }

    let gridSizeWorldSpace = SIMD3<Float>(Float(cells.x), Float(cells.y), Float(cells.z)) * cellSize
    let gridMinCornerWorldSpace = -0.5 * gridSizeWorldSpace
    let gridMaxCornerWorldSpace = gridMinCornerWorldSpace + gridSizeWorldSpace

    var params = MarchingCubesBlobParams(
    cells: cells,
    origin: gridMinCornerWorldSpace,
    cellSize: cellSize,
    isoLevel: 0.0,
    sphereCount: UInt32(sphereCountUI),
    smoothK: smoothK
    )

    // Reset vertex counter
    vertexCountBuffer.contents().bindMemory(to: UInt32.self, capacity: 1).pointee = 0

    // Acquire GPU-backed mesh buffers
    let vertexBuffer = mesh.replace(bufferIndex: 0, using: commandBuffer)
    let indexBuffer = mesh.replaceIndices(using: commandBuffer)

    // Encode compute
    computeEncoder.setComputePipelineState(computePipelineState)
    computeEncoder.setBuffer(vertexBuffer, offset: 0, index: 0)
    computeEncoder.setBuffer(indexBuffer, offset: 0, index: 1)
    computeEncoder.setBuffer(vertexCountBuffer, offset: 0, index: 2)
    computeEncoder.setBytes(&params, length: MemoryLayout<MarchingCubesBlobParams>.stride, index: 3)
    computeEncoder.setBuffer(spheresBuffer, offset: 0, index: 4)

    let threadsPerThreadgroup = MTLSize(width: 8, height: 8, depth: 4)
    let threadgroups = MTLSize(
    width: (Int(cells.x) + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width,
    height: (Int(cells.y) + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height,
    depth: (Int(cells.z) + threadsPerThreadgroup.depth - 1) / threadsPerThreadgroup.depth
    )
    computeEncoder.dispatchThreadgroups(threadgroups, threadsPerThreadgroup: threadsPerThreadgroup)
    computeEncoder.endEncoding()

    commandBuffer.commit()
    commandBuffer.waitUntilCompleted()

    let vertexCount = Int(vertexCountBuffer.contents().bindMemory(to: UInt32.self, capacity: 1).pointee)
    mesh.parts.replaceAll([
    LowLevelMesh.Part(indexCount: vertexCount,
    topology: .triangle,
    bounds: BoundingBox(min: gridMinCornerWorldSpace, max: gridMaxCornerWorldSpace))
    ])
    }
    }

    // MARK: Material
    // See: https://developer.apple.com/documentation/visionos/implementing-adjustable-material-in-visionos
    extension MarchingCubesBlobView {
    func getMaterial() async throws -> ShaderGraphMaterial {
    let baseURL = URL(string: "https://matt54.github.io/Resources/")!
    let fullURL = baseURL.appendingPathComponent("GlassScene.usda")
    let data = try Data(contentsOf: fullURL)
    let materialPath: String = "/Root/GlassMaterial"
    var material = try await ShaderGraphMaterial(named: materialPath, from: data)

    try! material.setParameter(name: ShaderGraphParameter.radianceRoughness.rawValue,
    value: .float(radianceRoughness))

    try! material.setParameter(name: ShaderGraphParameter.materialRoughness.rawValue, value: .float(materialRoughness))
    try! material.setParameter(name: ShaderGraphParameter.eta.rawValue, value: .float(eta))
    try! material.setParameter(name: ShaderGraphParameter.specular.rawValue, value: .float(specular))
    return material
    }

    func setShaderGraphParameterValue(_ parameter: ShaderGraphParameter, value: Float) throws {
    guard let entity = entity else { return }
    guard let modelComponent = entity.components[ModelComponent.self] else { return }
    guard var material = modelComponent.materials.first as? ShaderGraphMaterial else { return }
    try material.setParameter(name: parameter.rawValue, value: .float(value))
    entity.components[ModelComponent.self]?.materials = [material]
    }
    }

    // MARK: Environment Lighting / IBL
    extension MarchingCubesBlobView {
    func removeDefaultIBL() async {
    guard let entity else { return }
    let cgImage = createSimpleIBLTexture(size: CGSize(width: 256, height: 128), color: CGColor(gray: 0, alpha: 0))!
    let greyscaleEnvironmentResource = try! await EnvironmentResource(equirectangular: cgImage)
    let source: ImageBasedLightComponent.Source = .single(greyscaleEnvironmentResource)
    let iblComponent = ImageBasedLightComponent(
    source: source,
    intensityExponent: 1
    )
    entity.components[ImageBasedLightComponent.self] = iblComponent
    entity.components.set(ImageBasedLightReceiverComponent(imageBasedLight: entity))
    }

    func createSimpleIBLTexture(size: CGSize, color: CGColor) -> CGImage? {
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)

    guard let context = CGContext(data: nil,
    width: Int(size.width),
    height: Int(size.height),
    bitsPerComponent: 8,
    bytesPerRow: 0,
    space: colorSpace,
    bitmapInfo: bitmapInfo.rawValue) else {
    return nil
    }

    context.setFillColor(color)
    context.fill(CGRect(origin: .zero, size: size))

    return context.makeImage()
    }
    }

    #Preview { MarchingCubesBlobView() }
    29 changes: 29 additions & 0 deletions VertexPositionNormal+Extensions.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    import Foundation
    import RealityKit

    extension VertexPositionNormal {
    static var vertexAttributes: [LowLevelMesh.Attribute] = [
    .init(semantic: .position, format: .float3, offset: MemoryLayout<Self>.offset(of: \.position)!),
    .init(semantic: .normal, format: .float3, offset: MemoryLayout<Self>.offset(of: \.normal)!)
    ]

    static var vertexLayouts: [LowLevelMesh.Layout] = [
    .init(bufferIndex: 0, bufferStride: MemoryLayout<Self>.stride)
    ]

    static var descriptor: LowLevelMesh.Descriptor {
    var desc = LowLevelMesh.Descriptor()
    desc.vertexAttributes = VertexPositionNormal.vertexAttributes
    desc.vertexLayouts = VertexPositionNormal.vertexLayouts
    desc.indexType = .uint32
    return desc
    }

    @MainActor static func initializeMesh(vertexCapacity: Int,
    indexCapacity: Int) throws -> LowLevelMesh {
    var desc = VertexPositionNormal.descriptor
    desc.vertexCapacity = vertexCapacity
    desc.indexCapacity = indexCapacity
    return try LowLevelMesh(descriptor: desc)
    }
    }
    11 changes: 11 additions & 0 deletions VertexPositionNormal.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    #include <simd/simd.h>

    #ifndef VertexPositionNormal_h
    #define VertexPositionNormal_h

    struct VertexPositionNormal {
    simd_float3 position;
    simd_float3 normal;
    };

    #endif /* VertexPositionNormal_h */
    38 changes: 38 additions & 0 deletions edgeTable.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    #ifndef edgeTable_h
    #define edgeTable_h

    constant int edgeTable[256]={
    0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c,
    0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
    0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c,
    0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
    0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c,
    0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
    0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac,
    0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
    0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c,
    0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
    0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc,
    0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
    0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c,
    0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
    0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc ,
    0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
    0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc,
    0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
    0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c,
    0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
    0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc,
    0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
    0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c,
    0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
    0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac,
    0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
    0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c,
    0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
    0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c,
    0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
    0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c,
    0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 };

    #endif /* edgeTable_h */
    135 changes: 135 additions & 0 deletions generateMarchingCubesBlobMesh.metal
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    #include <metal_stdlib>
    using namespace metal;

    #include "VertexPositionNormal.h"
    #include "MarchingCubesBlobParams.h"
    #include "edgeTable.h"
    #include "triTable.h"

    inline float3 interpolateIsoSurfacePosition(float3 pA, float valueA,
    float3 pB, float valueB,
    float isoLevel) {
    float denom = (valueB - valueA);
    float t = (isoLevel - valueA) / (denom + 1e-6f);
    return mix(pA, pB, clamp(t, 0.0f, 1.0f));
    }

    inline float sdf_sphere(float3 p, float3 c, float r) {
    return length(p - c) - r;
    }

    inline float smin(float a, float b, float k) {
    float h = clamp(0.5f + 0.5f * (b - a) / max(k, 1e-6f), 0.0f, 1.0f);
    return mix(b, a, h) - k * h * (1.0f - h);
    }

    inline float fieldValue(float3 worldPos,
    constant MarchingCubesBlobParams& P,
    device const Sphere* spheres) {
    float d = 1e9f;
    uint count = P.sphereCount;
    for (uint i = 0; i < count; ++i) {
    float di = sdf_sphere(worldPos, spheres[i].center, spheres[i].radius);
    d = smin(d, di, P.smoothK);
    }
    return d;
    }

    inline float3 estimateNormalFromField(float3 worldPos,
    constant MarchingCubesBlobParams& P,
    device const Sphere* spheres) {
    float h = 0.5f * min(P.cellSize.x, min(P.cellSize.y, P.cellSize.z));
    float fx = fieldValue(worldPos + float3(h,0,0), P, spheres) - fieldValue(worldPos - float3(h,0,0), P, spheres);
    float fy = fieldValue(worldPos + float3(0,h,0), P, spheres) - fieldValue(worldPos - float3(0,h,0), P, spheres);
    float fz = fieldValue(worldPos + float3(0,0,h), P, spheres) - fieldValue(worldPos - float3(0,0,h), P, spheres);
    return normalize(float3(fx, fy, fz) / (2.0f * h));
    }

    kernel void generateMarchingCubesBlobMesh(device VertexPositionNormal* outVertices [[buffer(0)]],
    device uint* outIndices [[buffer(1)]],
    device atomic_uint* outVertexCounter [[buffer(2)]],
    constant MarchingCubesBlobParams& P [[buffer(3)]],
    device const Sphere* spheres [[buffer(4)]],
    uint3 cellCoord [[thread_position_in_grid]]) {
    if (cellCoord.x >= P.cells.x || cellCoord.y >= P.cells.y || cellCoord.z >= P.cells.z) {
    return;
    }

    const int3 cornerOffsets[8] = {
    int3(0,0,0), int3(1,0,0), int3(1,1,0), int3(0,1,0),
    int3(0,0,1), int3(1,0,1), int3(1,1,1), int3(0,1,1)
    };

    const int2 edgeCornerIndexPairs[12] = {
    int2(0,1), int2(1,2), int2(2,3), int2(3,0),
    int2(4,5), int2(5,6), int2(6,7), int2(7,4),
    int2(0,4), int2(1,5), int2(2,6), int2(3,7)
    };

    const float3 cellOriginWS = P.origin + float3(cellCoord) * P.cellSize;

    float cornerScalar[8];
    float3 cornerPositionWS[8];
    for (int i = 0; i < 8; ++i) {
    float3 cp = cellOriginWS + float3(cornerOffsets[i]) * P.cellSize;
    cornerPositionWS[i] = cp;
    cornerScalar[i] = fieldValue(cp, P, spheres);
    }

    int cubeIndex = 0;
    if (cornerScalar[0] > P.isoLevel) cubeIndex |= 1;
    if (cornerScalar[1] > P.isoLevel) cubeIndex |= 2;
    if (cornerScalar[2] > P.isoLevel) cubeIndex |= 4;
    if (cornerScalar[3] > P.isoLevel) cubeIndex |= 8;
    if (cornerScalar[4] > P.isoLevel) cubeIndex |= 16;
    if (cornerScalar[5] > P.isoLevel) cubeIndex |= 32;
    if (cornerScalar[6] > P.isoLevel) cubeIndex |= 64;
    if (cornerScalar[7] > P.isoLevel) cubeIndex |= 128;

    int edgeMask = edgeTable[cubeIndex];
    if (edgeMask == 0) { return; }

    float3 edgeIntersectionWS[12];
    for (int edge = 0; edge < 12; ++edge) {
    if (edgeMask & (1 << edge)) {
    const int a = edgeCornerIndexPairs[edge].x;
    const int b = edgeCornerIndexPairs[edge].y;
    edgeIntersectionWS[edge] = interpolateIsoSurfacePosition(
    cornerPositionWS[a], cornerScalar[a],
    cornerPositionWS[b], cornerScalar[b],
    P.isoLevel
    );
    }
    }

    constant int* triangleEdgesRow = &triTable[cubeIndex][0];

    for (int triIdx = 0; triIdx < 16 && triangleEdgesRow[triIdx] != -1; triIdx += 3) {
    const int e0 = triangleEdgesRow[triIdx + 0];
    const int e1 = triangleEdgesRow[triIdx + 1];
    const int e2 = triangleEdgesRow[triIdx + 2];

    const float3 p0 = edgeIntersectionWS[e0];
    const float3 p1 = edgeIntersectionWS[e1];
    const float3 p2 = edgeIntersectionWS[e2];

    const float3 n0 = estimateNormalFromField(p0, P, spheres);
    const float3 n1 = estimateNormalFromField(p1, P, spheres);
    const float3 n2 = estimateNormalFromField(p2, P, spheres);

    const uint baseVertexIndex = atomic_fetch_add_explicit(outVertexCounter, (uint)3, memory_order_relaxed);

    outVertices[baseVertexIndex + 0].position = p0;
    outVertices[baseVertexIndex + 0].normal = n0;

    outVertices[baseVertexIndex + 1].position = p1;
    outVertices[baseVertexIndex + 1].normal = n1;

    outVertices[baseVertexIndex + 2].position = p2;
    outVertices[baseVertexIndex + 2].normal = n2;

    outIndices[baseVertexIndex + 0] = baseVertexIndex + 0;
    outIndices[baseVertexIndex + 1] = baseVertexIndex + 1;
    outIndices[baseVertexIndex + 2] = baseVertexIndex + 2;
    }
    }
    262 changes: 262 additions & 0 deletions triTable.h
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,262 @@
    #ifndef triTable_h
    #define triTable_h

    constant int triTable[256][16] =
    {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1},
    {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1},
    {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1},
    {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1},
    {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1},
    {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
    {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1},
    {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1},
    {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
    {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1},
    {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1},
    {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1},
    {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1},
    {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
    {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1},
    {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1},
    {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1},
    {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1},
    {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1},
    {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1},
    {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1},
    {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1},
    {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
    {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1},
    {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1},
    {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1},
    {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1},
    {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1},
    {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1},
    {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1},
    {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1},
    {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1},
    {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1},
    {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
    {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1},
    {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1},
    {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1},
    {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1},
    {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1},
    {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1},
    {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1},
    {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1},
    {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1},
    {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1},
    {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1},
    {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1},
    {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1},
    {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1},
    {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1},
    {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1},
    {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1},
    {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1},
    {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1},
    {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1},
    {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1},
    {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1},
    {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1},
    {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
    {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1},
    {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1},
    {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1},
    {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1},
    {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1},
    {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1},
    {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1},
    {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1},
    {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1},
    {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1},
    {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1},
    {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1},
    {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1},
    {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1},
    {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1},
    {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1},
    {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1},
    {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1},
    {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1},
    {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1},
    {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1},
    {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
    {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
    {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1},
    {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1},
    {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1},
    {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1},
    {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1},
    {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1},
    {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1},
    {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1},
    {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1},
    {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1},
    {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1},
    {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1},
    {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1},
    {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1},
    {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1},
    {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1},
    {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1},
    {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1},
    {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1},
    {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1},
    {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1},
    {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1},
    {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
    {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1},
    {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1},
    {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1},
    {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1},
    {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1},
    {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
    {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1},
    {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1},
    {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1},
    {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1},
    {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1},
    {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1},
    {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1},
    {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1},
    {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1},
    {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1},
    {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1},
    {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1},
    {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1},
    {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1},
    {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1},
    {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1},
    {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1},
    {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1},
    {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1},
    {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1},
    {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1},
    {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1},
    {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1},
    {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1},
    {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1},
    {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1},
    {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1},
    {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1},
    {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1},
    {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1},
    {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1},
    {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1},
    {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1},
    {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1},
    {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1},
    {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1},
    {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1},
    {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1},
    {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1},
    {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1},
    {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1},
    {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1},
    {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1},
    {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1},
    {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1},
    {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1},
    {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1},
    {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1},
    {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1},
    {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1},
    {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1},
    {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1},
    {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1},
    {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1},
    {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1},
    {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1},
    {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1},
    {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1},
    {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1},
    {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1},
    {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1},
    {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1},
    {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1},
    {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}};

    #endif /* triTable_h */