Stitch meshes with different level of detail

I have an implementation for generating a mesh using marching cubes. The issue arises when transitioning between meshes with higher vertex density (smaller scale meshes) and between meshes with lower density (larger scale meshes).

In these transitions, I need to stitch the vertices between those meshes.

To achieve this, I’ve decided that the smaller meshes should be stitched to the larger ones. For this purpose, the smaller meshes should have fewer vertices defined on their boundaries.

For this, I decided to skip vertices on the smaller meshes to plug/stitch on the bigger ones, I have this code:

for (var x = 0; x < chunkSize; x++)
    for (var y = 0; y < chunkSize; y++)
        for (var z = 0; z < chunkSize; z++)
        {
            // Skip vertices that must be stitched with bigger nodes.
            if (forward && z == chunkSize - 1 && (x % 2 != 0 || y % 2 != 0))
                continue;

            if (back && z == 0 && (x % 2 != 0 || y % 2 != 0))
                continue;

            if (right && x == chunkSize - 1 && (z % 2 != 0 || y % 2 != 0))
                continue;

            if (left && x == 0 && (z % 2 != 0 || y % 2 != 0))
                continue;

            if (top && y == chunkSize - 1 && (x % 2 != 0 || z % 2 != 0))
                continue;

            if (bottom && y == 0 && (x % 2 != 0 || z % 2 != 0))
                continue;

            var cube = CreateCube(x, y, z, voxels);
            MarchCube(new Vector3(x, y, z), cube, vertices, triangles);
        }

Drawn in Gizmos:

...

Each of the conditions contains a bool flag, forward, back, left, right, top, and bottom, which is determined through an enumeration, and I’ve verified that it works correctly.

Which can be visualized in the following GIF:

...

Source: https://gyazo.com/acc888b3a374e1d14ae60a98f8da72ca.mp4

The issue arises when (possibly) calling the MarchCube method; it doesn’t generate the required vertices and triangles, resulting in holes in the mesh (at its edges):

...

Without wiremesh shader:

...

This is the implementation of CreateCube and MarchCube:

  /// <summary>
        /// Calculate densities for voxel array
        /// </summary>
        /// <returns>Voxel values</returns>
        private float[,,] CalculateDensities(int lodLevel, INode<CubeNode> octreeNode)
        {
            var voxels = new float[vertexSize, vertexSize, vertexSize];

            for (var x = 0; x < vertexSize; x++)
                for (var y = 0; y < vertexSize; y++)
                    for (var z = 0; z < vertexSize; z++)
                    {
                        var pos = new Vector3(x * lodLevel, y * lodLevel, z * lodLevel);
                        pos += offset;

                        var density = GetDensity(pos);
                        voxels[x, y, z] = density;
                    }

            return voxels;
        }

        /// <summary>
        /// Get the values 8 neighbor values of the cube
        /// </summary>
        private static float[] CreateCube(int x, int y, int z, float[,,] voxels)
        {
            var cube = new float[8];

            for (var i = 0; i < 8; i++)
                cube[i] = voxels[x + Tables.VertexOffset[i, 0], y + Tables.VertexOffset[i, 1],
                    z + Tables.VertexOffset[i, 2]];

            return cube;
        }

        /// <summary>
        /// Find the point of intersection of the surface between points with values v1 and v2
        /// </summary>
        private static float GetOffset(float v1, float v2)
        {
            var delta = v2 - v1;

            if (Mathf.Abs(delta) < 0.0001f)
                return 0.5f;

            return (Target - v1) / delta;
        }

        /// <summary>
        /// Performs the Marching Cubes algorithm on a single cube
        /// </summary>
        private static void MarchCube(Vector3 pos, IReadOnlyList<float> cube, ICollection<Vector3> vertexList, ICollection<int> indexList)
        {
            var cubeIndex = 0;
            var edgeVertex = new Vector3[12];

            // Find vertices inside the surface
            for (var i = 0; i < 8; i++)
                if (cube[i] <= Target)
                    cubeIndex |= 1 << i;

            // Find edges intersected by surface
            var edgeFlags = Tables.EdgeTable[cubeIndex];

            // No intersection if cube is completely outside surface
            if (edgeFlags == 0)
                return;

            // Find intersection point with surface for each edge
            for (var i = 0; i < 12; i++)
                // When intersection for this edge exists
                if ((edgeFlags & (1 << i)) != 0)
                {
                    var offset = GetOffset(cube[Tables.EdgeConnection[i, 0]], cube[Tables.EdgeConnection[i, 1]]);

                    edgeVertex[i].x = pos.x + (Tables.VertexOffset[Tables.EdgeConnection[i, 0], 0] +
                                               offset * Tables.EdgeDirection[i, 0]);
                    edgeVertex[i].y = pos.y + (Tables.VertexOffset[Tables.EdgeConnection[i, 0], 1] +
                                               offset * Tables.EdgeDirection[i, 1]);
                    edgeVertex[i].z = pos.z + (Tables.VertexOffset[Tables.EdgeConnection[i, 0], 2] +
                                               offset * Tables.EdgeDirection[i, 2]);
                }

            // Store found triangles. Up to five per cube possible
            for (var i = 0; i < 5; i++)
            {
                // Stop when triangle list terminates with -1
                if (Tables.TriTable[cubeIndex, 3 * i] < 0)
                    break;

                var idx = vertexList.Count;

                for (var j = 0; j < 3; j++)
                {
                    var vertex = Tables.TriTable[cubeIndex, 3 * i + j];
                    indexList.Add(idx + Tables.WindingOrder[j]);
                    vertexList.Add(edgeVertex[vertex]);
                }
            }
        }

Based on: https://github.com/theSoenke/ProceduralTerrain/blob/master/Assets/ProceduralTerrain/Core/Scripts/Voxel/Meshing/MarchingCubes.cs#L31

Why are this holes generated?

Leave a Comment