# Copyright 2021 NVIDIA CORPORATION
# SPDX-License-Identifier: Apache-2.0
#
# Script to generate the GLSL compute functions
#
# uint autogeneratedGetCaseNumber(float sample000, float sample001, float sample010, float sample011,
#                                 float sample100, float sample101, float sample110, float sample111);
#
# which takes a cube of 8 samples (sample{x}{y}{z}) returns a value 0-255 indicating which marching cubes
# case this is, and,
#
# void autogeneratedGetCellTriangles(out McubesCell cell, in uint caseNumber, in uint denominator,
#                                    in float sample000, in float sample001, in float sample010, in float sample011,
#                                    in float sample100, in float sample101, in float sample110, in float sample111);
#
# which fills in just the triangles in the given McubesCell as appropriate for the given samples cube.
# The fixed point format is such that packNormalizedVec3(vec3(0), denominator) indicates the coordinate that sample000
# is at, and packNormalizedVec3(vec3(1), denominator) indicates the coordinate that sample111 is at.
#
# We depend on outside code to fill in McubesCell::offset and McubesGeometry::packedVertScale correctly.
#
# This is pretty nasty code, more of an augmentation of my brain than serious software.
# (as if I had written the autogenerated code manually and you are just looking at what would have been
# my private thought process).

# Bit meanings in caseNumber
sample000_positive_bit = 1
sample001_positive_bit = 2
sample010_positive_bit = 4
sample011_positive_bit = 8
sample100_positive_bit = 16
sample101_positive_bit = 32
sample110_positive_bit = 64
sample111_positive_bit = 128

bit_map = {
  "sample000" : sample000_positive_bit,
  "sample001" : sample001_positive_bit,
  "sample010" : sample010_positive_bit,
  "sample011" : sample011_positive_bit,
  "sample100" : sample100_positive_bit,
  "sample101" : sample101_positive_bit,
  "sample110" : sample110_positive_bit,
  "sample111" : sample111_positive_bit,
}

# Print out autogeneratedGetCaseNumber
print(
f"""\
// Copyright 2021 <NAME>
// SPDX-License-Identifier: Apache-2.0
// generated by shaders/generate_autogenerated_mcubes.py
// clang-format off
uint autogeneratedGetCaseNumber(float sample000, float sample001, float sample010, float sample011,
                                float sample100, float sample101, float sample110, float sample111)
{{
  int bits = (sample000 > 0.0 ? {sample000_positive_bit} : 0)
           | (sample001 > 0.0 ? {sample001_positive_bit} : 0)
           | (sample010 > 0.0 ? {sample010_positive_bit} : 0)
           | (sample011 > 0.0 ? {sample011_positive_bit} : 0)
           | (sample100 > 0.0 ? {sample100_positive_bit} : 0)
           | (sample101 > 0.0 ? {sample101_positive_bit} : 0)
           | (sample110 > 0.0 ? {sample110_positive_bit} : 0)
           | (sample111 > 0.0 ? {sample111_positive_bit} : 0);
  return uint(bits);
}}
""")

# Print out start of autogeneratedGetCellTriangles
print(
f"""
// Used shared memory for some thread-private variables. This makes the GLSL compiler not take forever.
uint sharedIntersect_00x[THREADS];
uint sharedIntersect_0x0[THREADS];
uint sharedIntersect_x00[THREADS];
uint sharedIntersect_01x[THREADS];
uint sharedIntersect_0x1[THREADS];
uint sharedIntersect_x01[THREADS];
uint sharedIntersect_10x[THREADS];
uint sharedIntersect_1x0[THREADS];
uint sharedIntersect_x10[THREADS];
uint sharedIntersect_11x[THREADS];
uint sharedIntersect_1x1[THREADS];
uint sharedIntersect_x11[THREADS];

// e.g. INTERSECT_00x indicates the interpolated intersection point (if any) from sample000 to sample001.
#define INTERSECT_00x sharedIntersect_00x[gl_LocalInvocationIndex]
#define INTERSECT_0x0 sharedIntersect_0x0[gl_LocalInvocationIndex]
#define INTERSECT_x00 sharedIntersect_x00[gl_LocalInvocationIndex]
#define INTERSECT_01x sharedIntersect_01x[gl_LocalInvocationIndex]
#define INTERSECT_0x1 sharedIntersect_0x1[gl_LocalInvocationIndex]
#define INTERSECT_x01 sharedIntersect_x01[gl_LocalInvocationIndex]
#define INTERSECT_10x sharedIntersect_10x[gl_LocalInvocationIndex]
#define INTERSECT_1x0 sharedIntersect_1x0[gl_LocalInvocationIndex]
#define INTERSECT_x10 sharedIntersect_x10[gl_LocalInvocationIndex]
#define INTERSECT_11x sharedIntersect_11x[gl_LocalInvocationIndex]
#define INTERSECT_1x1 sharedIntersect_1x1[gl_LocalInvocationIndex]
#define INTERSECT_x11 sharedIntersect_x11[gl_LocalInvocationIndex]

void autogeneratedGetCellTriangles(out McubesCell cell, in uint caseNumber, in uint denominator,
                                   in float sample000, in float sample001, in float sample010, in float sample011,
                                   in float sample100, in float sample101, in float sample110, in float sample111)
{{
""")

# Function for printing the computation for a INTERSECT_xxx variable.
def print_intersect_assignment(variable_name):
    assert variable_name.startswith("INTERSECT_") and len(variable_name) == 13
    last3 = variable_name[-3:]
    assert last3.count('x') == 1
    sample0_name = 'sample' + last3.replace('x', '0')
    sample1_name = 'sample' + last3.replace('x', '1')
    interpolant_calculation = f"{sample0_name} / ({sample0_name} - {sample1_name})"
    vec_x = interpolant_calculation if last3[0] == 'x' else last3[0]
    vec_y = interpolant_calculation if last3[1] == 'x' else last3[1]
    vec_z = interpolant_calculation if last3[2] == 'x' else last3[2]
    print(f"  {variable_name} = packNormalizedVec3(vec3({vec_x}, {vec_y}, {vec_z}), denominator);")

for name in ["INTERSECT_00x", "INTERSECT_0x0", "INTERSECT_x00",
             "INTERSECT_01x", "INTERSECT_0x1", "INTERSECT_x01",
             "INTERSECT_10x", "INTERSECT_1x0", "INTERSECT_x10",
             "INTERSECT_11x", "INTERSECT_1x1", "INTERSECT_x11"]:
    print_intersect_assignment(name)

print("  switch(caseNumber)\n{")

# Analyze the square of 4 samples (one face of the 8-sample cube) and add any edges found to the edge_dictionary.
# The samples shall be passed as 4 sampleXXX variable names, in anticlocwise order (viewed from outside).
# This is in the form of a key:value pair, where the directed edge goes from key to value
# (both INTERSECT_XXX variable names).
def add_square_edges(bits, edge_dictionary, sample_sw_name, sample_se_name, sample_ne_name, sample_nw_name, hack):
    get_intersect_name = lambda sample_a_name, sample_b_name: \
        "INTERSECT_x" + sample_a_name[-2:] if sample_a_name[-3] != sample_b_name[-3] else \
        "INTERSECT_" + sample_a_name[-3] + 'x' + sample_a_name[-1] if sample_a_name[-2] != sample_b_name[-2] else \
        "INTERSECT_" + sample_a_name[-3:-1] + 'x'

    sw_bit = 1
    se_bit = 2
    ne_bit = 4
    nw_bit = 8

    s_intersect = get_intersect_name(sample_sw_name, sample_se_name)
    n_intersect = get_intersect_name(sample_nw_name, sample_ne_name)
    e_intersect = get_intersect_name(sample_se_name, sample_ne_name)
    w_intersect = get_intersect_name(sample_sw_name, sample_nw_name)

    square_bits = 0
    if bits & bit_map[sample_sw_name]: square_bits |= sw_bit
    if bits & bit_map[sample_se_name]: square_bits |= se_bit
    if bits & bit_map[sample_ne_name]: square_bits |= ne_bit
    if bits & bit_map[sample_nw_name]: square_bits |= nw_bit

    if square_bits == 0:
        pass
    elif square_bits == sw_bit:
        edge_dictionary[s_intersect] = w_intersect
    elif square_bits == se_bit:
        edge_dictionary[e_intersect] = s_intersect
    elif square_bits == sw_bit | se_bit:
        edge_dictionary[e_intersect] = w_intersect
    elif square_bits == ne_bit:
        edge_dictionary[n_intersect] = e_intersect
    elif square_bits == ne_bit | sw_bit:
        if hack & 1:
            edge_dictionary[n_intersect] = w_intersect
            edge_dictionary[s_intersect] = e_intersect
        else:
            edge_dictionary[n_intersect] = e_intersect
            edge_dictionary[s_intersect] = w_intersect
    elif square_bits == ne_bit | se_bit:
        edge_dictionary[n_intersect] = s_intersect
    elif square_bits == ne_bit | se_bit | sw_bit:
        edge_dictionary[n_intersect] = w_intersect
    elif square_bits == nw_bit:
        edge_dictionary[w_intersect] = n_intersect
    elif square_bits == nw_bit | sw_bit:
        edge_dictionary[s_intersect] = n_intersect
    elif square_bits == nw_bit | se_bit:
        if hack & 2:
            edge_dictionary[w_intersect] = s_intersect
            edge_dictionary[e_intersect] = n_intersect
        else:
            edge_dictionary[w_intersect] = n_intersect
            edge_dictionary[e_intersect] = s_intersect
    elif square_bits == nw_bit | se_bit | sw_bit:
        edge_dictionary[e_intersect] = n_intersect
    elif square_bits == nw_bit | ne_bit:
        edge_dictionary[w_intersect] = e_intersect
    elif square_bits == nw_bit | ne_bit | sw_bit:
        edge_dictionary[s_intersect] = e_intersect
    elif square_bits == nw_bit | ne_bit | se_bit:
        edge_dictionary[w_intersect] = s_intersect
    elif square_bits == sw_bit | se_bit | ne_bit | nw_bit:
        pass
    else:
        assert 0

# Collect the cube face edges for a given cube case, then assemble into a list of closed loops.
# This does not include edges that pass through the interior of the cube -- these will be tesselated later.
def collect_face_contours(case_bits, hack):
    edge_dictionary = {}
    # +x face
    add_square_edges(case_bits, edge_dictionary, "sample100", "sample110", "sample111", "sample101", hack)
    # -x face
    add_square_edges(case_bits, edge_dictionary, "sample000", "sample001", "sample011", "sample010", hack)
    # +y face
    add_square_edges(case_bits, edge_dictionary, "sample010", "sample011", "sample111", "sample110", hack)
    # -y face
    add_square_edges(case_bits, edge_dictionary, "sample000", "sample100", "sample101", "sample001", hack)
    # +z face
    add_square_edges(case_bits, edge_dictionary, "sample001", "sample101", "sample111", "sample011", hack)
    # -z face
    add_square_edges(case_bits, edge_dictionary, "sample000", "sample010", "sample110", "sample100", hack)

    contours = []
    while edge_dictionary:
        start, next_point = edge_dictionary.popitem()
        points = [start]
        while next_point != start:
            points.append(next_point)
            tmp = edge_dictionary[next_point]
            del edge_dictionary[next_point]
            next_point = tmp
        contours.append(points)
    return contours

# Tesselate contours.
# Given list of contours of length 3, 4, 5, or 6 points, arbitrarily tesselate those with > 3 points,
# and return a list of only 3-point contours.
def tesselate_contours(contours):
    triangles = []
    for point_list in contours:
        if len(point_list) == 3:
            triangles.append(tuple(point_list))
        elif len(point_list) == 4:
            triangles.append((point_list[0], point_list[1], point_list[2]))
            triangles.append((point_list[0], point_list[2], point_list[3]))
        elif len(point_list) == 5:
            triangles.append((point_list[0], point_list[1], point_list[2]))
            triangles.append((point_list[0], point_list[2], point_list[3]))
            triangles.append((point_list[0], point_list[3], point_list[4]))
        elif len(point_list) == 6:
            triangles.append((point_list[0], point_list[1], point_list[2]))
            triangles.append((point_list[2], point_list[3], point_list[4]))
            triangles.append((point_list[4], point_list[5], point_list[0]))
            triangles.append((point_list[0], point_list[2], point_list[4]))
        else:
            assert 0
    return triangles

# Print the code for a given case number.
def print_case(case_bits):
    print(f"  case {case_bits}:")
    max_point_list_length = 1000
    for hack in range(4):
        face_contours = collect_face_contours(case_bits, hack)
        hack_max_point_list_length = 0
        if face_contours:
            hack_max_point_list_length = max(len(point_list) for point_list in face_contours)
        if hack_max_point_list_length < max_point_list_length:
            max_point_list_length = hack_max_point_list_length
            best_face_contours = face_contours
        del face_contours

    face_contours = best_face_contours
    triangles = tesselate_contours(face_contours)
    print(f"    cell.vertexCount    = {len(triangles) * 3};")
    index = 0
    for t in triangles:
        print(f"    cell.packedVerts[{index + 0}] = {t[0]};")
        print(f"    cell.packedVerts[{index + 1}] = {t[1]};")
        print(f"    cell.packedVerts[{index + 2}] = {t[2]};")
        index += 3
    print(f"    break;")

for case_bits in range(256):
    print_case(case_bits)

# Print function end
print(
"""  }
}""")

# Copyright 2021 NVIDIA CORPORATION
# SPDX-License-Identifier: Apache-2.0
#
# Script to generate the GLSL compute functions
#
# uint autogeneratedGetCaseNumber(float sample000, float sample001, float sample010, float sample011,
#                                 float sample100, float sample101, float sample110, float sample111);
#
# which takes a cube of 8 samples (sample{x}{y}{z}) returns a value 0-255 indicating which marching cubes
# case this is, and,
#
# void autogeneratedGetCellTriangles(out McubesCell cell, in uint caseNumber, in uint denominator,
#                                    in float sample000, in float sample001, in float sample010, in float sample011,
#                                    in float sample100, in float sample101, in float sample110, in float sample111);
#
# which fills in just the triangles in the given McubesCell as appropriate for the given samples cube.
# The fixed point format is such that packNormalizedVec3(vec3(0), denominator) indicates the coordinate that sample000
# is at, and packNormalizedVec3(vec3(1), denominator) indicates the coordinate that sample111 is at.
#
# We depend on outside code to fill in McubesCell::offset and McubesGeometry::packedVertScale correctly.
#
# This is pretty nasty code, more of an augmentation of my brain than serious software.
# (as if I had written the autogenerated code manually and you are just looking at what would have been
# my private thought process).

# Bit meanings in caseNumber
sample000_positive_bit = 1
sample001_positive_bit = 2
sample010_positive_bit = 4
sample011_positive_bit = 8
sample100_positive_bit = 16
sample101_positive_bit = 32
sample110_positive_bit = 64
sample111_positive_bit = 128

bit_map = {
  "sample000" : sample000_positive_bit,
  "sample001" : sample001_positive_bit,
  "sample010" : sample010_positive_bit,
  "sample011" : sample011_positive_bit,
  "sample100" : sample100_positive_bit,
  "sample101" : sample101_positive_bit,
  "sample110" : sample110_positive_bit,
  "sample111" : sample111_positive_bit,
}

# Print out autogeneratedGetCaseNumber
print(
f"""\
// Copyright 2021 <NAME>
// SPDX-License-Identifier: Apache-2.0
// generated by shaders/generate_autogenerated_mcubes.py
// clang-format off
uint autogeneratedGetCaseNumber(float sample000, float sample001, float sample010, float sample011,
                                float sample100, float sample101, float sample110, float sample111)
{{
  int bits = (sample000 > 0.0 ? {sample000_positive_bit} : 0)
           | (sample001 > 0.0 ? {sample001_positive_bit} : 0)
           | (sample010 > 0.0 ? {sample010_positive_bit} : 0)
           | (sample011 > 0.0 ? {sample011_positive_bit} : 0)
           | (sample100 > 0.0 ? {sample100_positive_bit} : 0)
           | (sample101 > 0.0 ? {sample101_positive_bit} : 0)
           | (sample110 > 0.0 ? {sample110_positive_bit} : 0)
           | (sample111 > 0.0 ? {sample111_positive_bit} : 0);
  return uint(bits);
}}
""")

# Print out start of autogeneratedGetCellTriangles
print(
f"""
// Used shared memory for some thread-private variables. This makes the GLSL compiler not take forever.
uint sharedIntersect_00x[THREADS];
uint sharedIntersect_0x0[THREADS];
uint sharedIntersect_x00[THREADS];
uint sharedIntersect_01x[THREADS];
uint sharedIntersect_0x1[THREADS];
uint sharedIntersect_x01[THREADS];
uint sharedIntersect_10x[THREADS];
uint sharedIntersect_1x0[THREADS];
uint sharedIntersect_x10[THREADS];
uint sharedIntersect_11x[THREADS];
uint sharedIntersect_1x1[THREADS];
uint sharedIntersect_x11[THREADS];

// e.g. INTERSECT_00x indicates the interpolated intersection point (if any) from sample000 to sample001.
#define INTERSECT_00x sharedIntersect_00x[gl_LocalInvocationIndex]
#define INTERSECT_0x0 sharedIntersect_0x0[gl_LocalInvocationIndex]
#define INTERSECT_x00 sharedIntersect_x00[gl_LocalInvocationIndex]
#define INTERSECT_01x sharedIntersect_01x[gl_LocalInvocationIndex]
#define INTERSECT_0x1 sharedIntersect_0x1[gl_LocalInvocationIndex]
#define INTERSECT_x01 sharedIntersect_x01[gl_LocalInvocationIndex]
#define INTERSECT_10x sharedIntersect_10x[gl_LocalInvocationIndex]
#define INTERSECT_1x0 sharedIntersect_1x0[gl_LocalInvocationIndex]
#define INTERSECT_x10 sharedIntersect_x10[gl_LocalInvocationIndex]
#define INTERSECT_11x sharedIntersect_11x[gl_LocalInvocationIndex]
#define INTERSECT_1x1 sharedIntersect_1x1[gl_LocalInvocationIndex]
#define INTERSECT_x11 sharedIntersect_x11[gl_LocalInvocationIndex]

void autogeneratedGetCellTriangles(out McubesCell cell, in uint caseNumber, in uint denominator,
                                   in float sample000, in float sample001, in float sample010, in float sample011,
                                   in float sample100, in float sample101, in float sample110, in float sample111)
{{
""")

# Function for printing the computation for a INTERSECT_xxx variable.
def print_intersect_assignment(variable_name):
    assert variable_name.startswith("INTERSECT_") and len(variable_name) == 13
    last3 = variable_name[-3:]
    assert last3.count('x') == 1
    sample0_name = 'sample' + last3.replace('x', '0')
    sample1_name = 'sample' + last3.replace('x', '1')
    interpolant_calculation = f"{sample0_name} / ({sample0_name} - {sample1_name})"
    vec_x = interpolant_calculation if last3[0] == 'x' else last3[0]
    vec_y = interpolant_calculation if last3[1] == 'x' else last3[1]
    vec_z = interpolant_calculation if last3[2] == 'x' else last3[2]
    print(f"  {variable_name} = packNormalizedVec3(vec3({vec_x}, {vec_y}, {vec_z}), denominator);")

for name in ["INTERSECT_00x", "INTERSECT_0x0", "INTERSECT_x00",
             "INTERSECT_01x", "INTERSECT_0x1", "INTERSECT_x01",
             "INTERSECT_10x", "INTERSECT_1x0", "INTERSECT_x10",
             "INTERSECT_11x", "INTERSECT_1x1", "INTERSECT_x11"]:
    print_intersect_assignment(name)

print("  switch(caseNumber)\n{")

# Analyze the square of 4 samples (one face of the 8-sample cube) and add any edges found to the edge_dictionary.
# The samples shall be passed as 4 sampleXXX variable names, in anticlocwise order (viewed from outside).
# This is in the form of a key:value pair, where the directed edge goes from key to value
# (both INTERSECT_XXX variable names).
def add_square_edges(bits, edge_dictionary, sample_sw_name, sample_se_name, sample_ne_name, sample_nw_name, hack):
    get_intersect_name = lambda sample_a_name, sample_b_name: \
        "INTERSECT_x" + sample_a_name[-2:] if sample_a_name[-3] != sample_b_name[-3] else \
        "INTERSECT_" + sample_a_name[-3] + 'x' + sample_a_name[-1] if sample_a_name[-2] != sample_b_name[-2] else \
        "INTERSECT_" + sample_a_name[-3:-1] + 'x'

    sw_bit = 1
    se_bit = 2
    ne_bit = 4
    nw_bit = 8

    s_intersect = get_intersect_name(sample_sw_name, sample_se_name)
    n_intersect = get_intersect_name(sample_nw_name, sample_ne_name)
    e_intersect = get_intersect_name(sample_se_name, sample_ne_name)
    w_intersect = get_intersect_name(sample_sw_name, sample_nw_name)

    square_bits = 0
    if bits & bit_map[sample_sw_name]: square_bits |= sw_bit
    if bits & bit_map[sample_se_name]: square_bits |= se_bit
    if bits & bit_map[sample_ne_name]: square_bits |= ne_bit
    if bits & bit_map[sample_nw_name]: square_bits |= nw_bit

    if square_bits == 0:
        pass
    elif square_bits == sw_bit:
        edge_dictionary[s_intersect] = w_intersect
    elif square_bits == se_bit:
        edge_dictionary[e_intersect] = s_intersect
    elif square_bits == sw_bit | se_bit:
        edge_dictionary[e_intersect] = w_intersect
    elif square_bits == ne_bit:
        edge_dictionary[n_intersect] = e_intersect
    elif square_bits == ne_bit | sw_bit:
        if hack & 1:
            edge_dictionary[n_intersect] = w_intersect
            edge_dictionary[s_intersect] = e_intersect
        else:
            edge_dictionary[n_intersect] = e_intersect
            edge_dictionary[s_intersect] = w_intersect
    elif square_bits == ne_bit | se_bit:
        edge_dictionary[n_intersect] = s_intersect
    elif square_bits == ne_bit | se_bit | sw_bit:
        edge_dictionary[n_intersect] = w_intersect
    elif square_bits == nw_bit:
        edge_dictionary[w_intersect] = n_intersect
    elif square_bits == nw_bit | sw_bit:
        edge_dictionary[s_intersect] = n_intersect
    elif square_bits == nw_bit | se_bit:
        if hack & 2:
            edge_dictionary[w_intersect] = s_intersect
            edge_dictionary[e_intersect] = n_intersect
        else:
            edge_dictionary[w_intersect] = n_intersect
            edge_dictionary[e_intersect] = s_intersect
    elif square_bits == nw_bit | se_bit | sw_bit:
        edge_dictionary[e_intersect] = n_intersect
    elif square_bits == nw_bit | ne_bit:
        edge_dictionary[w_intersect] = e_intersect
    elif square_bits == nw_bit | ne_bit | sw_bit:
        edge_dictionary[s_intersect] = e_intersect
    elif square_bits == nw_bit | ne_bit | se_bit:
        edge_dictionary[w_intersect] = s_intersect
    elif square_bits == sw_bit | se_bit | ne_bit | nw_bit:
        pass
    else:
        assert 0

# Collect the cube face edges for a given cube case, then assemble into a list of closed loops.
# This does not include edges that pass through the interior of the cube -- these will be tesselated later.
def collect_face_contours(case_bits, hack):
    edge_dictionary = {}
    # +x face
    add_square_edges(case_bits, edge_dictionary, "sample100", "sample110", "sample111", "sample101", hack)
    # -x face
    add_square_edges(case_bits, edge_dictionary, "sample000", "sample001", "sample011", "sample010", hack)
    # +y face
    add_square_edges(case_bits, edge_dictionary, "sample010", "sample011", "sample111", "sample110", hack)
    # -y face
    add_square_edges(case_bits, edge_dictionary, "sample000", "sample100", "sample101", "sample001", hack)
    # +z face
    add_square_edges(case_bits, edge_dictionary, "sample001", "sample101", "sample111", "sample011", hack)
    # -z face
    add_square_edges(case_bits, edge_dictionary, "sample000", "sample010", "sample110", "sample100", hack)

    contours = []
    while edge_dictionary:
        start, next_point = edge_dictionary.popitem()
        points = [start]
        while next_point != start:
            points.append(next_point)
            tmp = edge_dictionary[next_point]
            del edge_dictionary[next_point]
            next_point = tmp
        contours.append(points)
    return contours

# Tesselate contours.
# Given list of contours of length 3, 4, 5, or 6 points, arbitrarily tesselate those with > 3 points,
# and return a list of only 3-point contours.
def tesselate_contours(contours):
    triangles = []
    for point_list in contours:
        if len(point_list) == 3:
            triangles.append(tuple(point_list))
        elif len(point_list) == 4:
            triangles.append((point_list[0], point_list[1], point_list[2]))
            triangles.append((point_list[0], point_list[2], point_list[3]))
        elif len(point_list) == 5:
            triangles.append((point_list[0], point_list[1], point_list[2]))
            triangles.append((point_list[0], point_list[2], point_list[3]))
            triangles.append((point_list[0], point_list[3], point_list[4]))
        elif len(point_list) == 6:
            triangles.append((point_list[0], point_list[1], point_list[2]))
            triangles.append((point_list[2], point_list[3], point_list[4]))
            triangles.append((point_list[4], point_list[5], point_list[0]))
            triangles.append((point_list[0], point_list[2], point_list[4]))
        else:
            assert 0
    return triangles

# Print the code for a given case number.
def print_case(case_bits):
    print(f"  case {case_bits}:")
    max_point_list_length = 1000
    for hack in range(4):
        face_contours = collect_face_contours(case_bits, hack)
        hack_max_point_list_length = 0
        if face_contours:
            hack_max_point_list_length = max(len(point_list) for point_list in face_contours)
        if hack_max_point_list_length < max_point_list_length:
            max_point_list_length = hack_max_point_list_length
            best_face_contours = face_contours
        del face_contours

    face_contours = best_face_contours
    triangles = tesselate_contours(face_contours)
    print(f"    cell.vertexCount    = {len(triangles) * 3};")
    index = 0
    for t in triangles:
        print(f"    cell.packedVerts[{index + 0}] = {t[0]};")
        print(f"    cell.packedVerts[{index + 1}] = {t[1]};")
        print(f"    cell.packedVerts[{index + 2}] = {t[2]};")
        index += 3
    print(f"    break;")

for case_bits in range(256):
    print_case(case_bits)

# Print function end
print(
"""  }
}""")

# Copyright 2021 NVIDIA CORPORATION # SPDX-License-Identifier: Apache-2.0 # # Script to generate the GLSL compute functions # # uint autogeneratedGetCaseNumber(float sample000, float sample001, float sample010, float sample011, #                                 float sample100, float sample101, float sample110, float sample111); # # which takes a cube of 8 samples (sample{x}{y}{z}) returns a value 0-255 indicating which marching cubes # case this is, and, # # void autogeneratedGetCellTriangles(out McubesCell cell, in uint caseNumber, in uint denominator, #                                    in float sample000, in float sample001, in float sample010, in float sample011, #                                    in float sample100, in float sample101, in float sample110, in float sample111); # # which fills in just the triangles in the given McubesCell as appropriate for the given samples cube. # The fixed point format is such that packNormalizedVec3(vec3(0), denominator) indicates the coordinate that sample000 # is at, and packNormalizedVec3(vec3(1), denominator) indicates the coordinate that sample111 is at. # # We depend on outside code to fill in McubesCell::offset and McubesGeometry::packedVertScale correctly. # # This is pretty nasty code, more of an augmentation of my brain than serious software. # (as if I had written the autogenerated code manually and you are just looking at what would have been # my private thought process). # Bit meanings in caseNumber # Print out autogeneratedGetCaseNumber \ // Copyright 2021 <NAME> // SPDX-License-Identifier: Apache-2.0 // generated by shaders/generate_autogenerated_mcubes.py // clang-format off uint autogeneratedGetCaseNumber(float sample000, float sample001, float sample010, float sample011,                                 float sample100, float sample101, float sample110, float sample111) {{   int bits = (sample000 > 0.0 ? {sample000_positive_bit} : 0)            | (sample001 > 0.0 ? {sample001_positive_bit} : 0)            | (sample010 > 0.0 ? {sample010_positive_bit} : 0)            | (sample011 > 0.0 ? {sample011_positive_bit} : 0)            | (sample100 > 0.0 ? {sample100_positive_bit} : 0)            | (sample101 > 0.0 ? {sample101_positive_bit} : 0)            | (sample110 > 0.0 ? {sample110_positive_bit} : 0)            | (sample111 > 0.0 ? {sample111_positive_bit} : 0);   return uint(bits); }} # Print out start of autogeneratedGetCellTriangles // Used shared memory for some thread-private variables. This makes the GLSL compiler not take forever. uint sharedIntersect_00x[THREADS]; uint sharedIntersect_0x0[THREADS]; uint sharedIntersect_x00[THREADS]; uint sharedIntersect_01x[THREADS]; uint sharedIntersect_0x1[THREADS]; uint sharedIntersect_x01[THREADS]; uint sharedIntersect_10x[THREADS]; uint sharedIntersect_1x0[THREADS]; uint sharedIntersect_x10[THREADS]; uint sharedIntersect_11x[THREADS]; uint sharedIntersect_1x1[THREADS]; uint sharedIntersect_x11[THREADS];  // e.g. INTERSECT_00x indicates the interpolated intersection point (if any) from sample000 to sample001. #define INTERSECT_00x sharedIntersect_00x[gl_LocalInvocationIndex] #define INTERSECT_0x0 sharedIntersect_0x0[gl_LocalInvocationIndex] #define INTERSECT_x00 sharedIntersect_x00[gl_LocalInvocationIndex] #define INTERSECT_01x sharedIntersect_01x[gl_LocalInvocationIndex] #define INTERSECT_0x1 sharedIntersect_0x1[gl_LocalInvocationIndex] #define INTERSECT_x01 sharedIntersect_x01[gl_LocalInvocationIndex] #define INTERSECT_10x sharedIntersect_10x[gl_LocalInvocationIndex] #define INTERSECT_1x0 sharedIntersect_1x0[gl_LocalInvocationIndex] #define INTERSECT_x10 sharedIntersect_x10[gl_LocalInvocationIndex] #define INTERSECT_11x sharedIntersect_11x[gl_LocalInvocationIndex] #define INTERSECT_1x1 sharedIntersect_1x1[gl_LocalInvocationIndex] #define INTERSECT_x11 sharedIntersect_x11[gl_LocalInvocationIndex]  void autogeneratedGetCellTriangles(out McubesCell cell, in uint caseNumber, in uint denominator,                                    in float sample000, in float sample001, in float sample010, in float sample011,                                    in float sample100, in float sample101, in float sample110, in float sample111) {{ # Function for printing the computation for a INTERSECT_xxx variable. # Analyze the square of 4 samples (one face of the 8-sample cube) and add any edges found to the edge_dictionary. # The samples shall be passed as 4 sampleXXX variable names, in anticlocwise order (viewed from outside). # This is in the form of a key:value pair, where the directed edge goes from key to value # (both INTERSECT_XXX variable names). # Collect the cube face edges for a given cube case, then assemble into a list of closed loops. # This does not include edges that pass through the interior of the cube -- these will be tesselated later. # +x face # -x face # +y face # -y face # +z face # -z face # Tesselate contours. # Given list of contours of length 3, 4, 5, or 6 points, arbitrarily tesselate those with > 3 points, # and return a list of only 3-point contours. # Print the code for a given case number. # Print function end } }