1
\$\begingroup\$

So, well I've been trying to find out the reason of the following bug in my marching cubes implementation, but I can't figure it out. enter image description here

Now this is a two state implementation of MC (just two exact isovalues 0 or 1) and the isovalue of the cells at the blue triangles are 0 means that they don't have to be triangulated. But they are for some reason. Therefore they got a fallback texture. But it's been a long time since I'm trying to figure this out, so if you have any idea please share it with me. Here is the code of my MC implementation.

    public class MarchingCubes
    {
private Region region;
private Chunk chunk;

private Mesh mesh = new Mesh ();

private List<Vector3> vertices = new List<Vector3> ();
private List<int> triangles = new List<int> ();
private List<Vector2> uvs = new List<Vector2> ();
private ValPair[] pairs = new ValPair[8];
private Debugger debugger;

private int chunkSize = 16;
private float isolevel = 1f;

public MarchingCubes (Debugger debugger)
{
    this.debugger = debugger;
}

private float CornerValue (Vector3i pos)
{
    int x = pos.x;
    int y = pos.y;
    int z = pos.z;

    if (y < 0 || y >= 128)
        return 0f;

    if (x >= 0 && x < chunkSize && z >= 0 && z < chunkSize) {
        return chunk.GetValue (x, y, z);
    } else {
        Chunk neighbor = null;
        int ix = chunk.X (), iz = chunk.Z ();
        if (x < 0) {
            ix--;
            if (ix == -1)
                return 0f;
            x = 16;
        }
        if (z < 0) {
            iz--;
            if (iz == -1)
                return 0f;
            z = 16;
        }
        if (x >= chunkSize) {
            ix++;
            if (ix == region.regionSize)
                return 0f;
            x = 0;
        }
        if (z >= chunkSize) {
            iz++;
            if (iz == region.regionSize)
                return 0f;
            z = 0;
        }
        neighbor = region.GetChunk (ix, iz);
        return neighbor.GetValue (x, y, z);
    }
}

struct ValPair
{
    public Vector3 position;
    public float intsect;
}

public Mesh Render (Region region, Chunk chunk)
{
    this.region = region;
    this.chunk = chunk;

    vertices.Clear ();
    triangles.Clear ();
    uvs.Clear ();
    Cell cell;
    Block block;

    for (int x = 0; x < chunkSize; x++) {
        for (int z = 0; z < chunkSize; z++) {
            for (int y = 0; y <= chunk.GetRow(x, z).GetHeight(); y++) {
                cell = chunk.GetRow (x, z).GetCell (y);
                block = cell.GetBlock ();

                int cubeIndex = 0;

                for (int i = 0; i < 8; i++) {
                    pairs [i].intsect = CornerValue (new Vector3i (x, y, z) + offset [i]);
                    if (pairs [i].intsect < isolevel)
                        cubeIndex += 1 << i;
                }

                if (cubeIndex == 0 || cubeIndex == 255)
                    continue;

                for (int i = 0; i < 8; i++) {
                    pairs [i].position = (new Vector3i (x, y, z) + offset [i]).ToVector3 ();
                }

                Vector3[] vertlist = new Vector3[12];
                if ((edgeTable [cubeIndex] & 1) == 1)
                    vertlist [0] = VertexInterp (pairs [0], pairs [1]);
                if ((edgeTable [cubeIndex] & 2) == 2)
                    vertlist [1] = VertexInterp (pairs [1], pairs [2]);
                if ((edgeTable [cubeIndex] & 4) == 4)
                    vertlist [2] = VertexInterp (pairs [2], pairs [3]);
                if ((edgeTable [cubeIndex] & 8) == 8)
                    vertlist [3] = VertexInterp (pairs [3], pairs [0]);
                if ((edgeTable [cubeIndex] & 16) == 16)
                    vertlist [4] = VertexInterp (pairs [4], pairs [5]);
                if ((edgeTable [cubeIndex] & 32) == 32)
                    vertlist [5] = VertexInterp (pairs [5], pairs [6]);
                if ((edgeTable [cubeIndex] & 64) == 64)
                    vertlist [6] = VertexInterp (pairs [6], pairs [7]);
                if ((edgeTable [cubeIndex] & 128) == 128)
                    vertlist [7] = VertexInterp (pairs [7], pairs [4]);
                if ((edgeTable [cubeIndex] & 256) == 256)
                    vertlist [8] = VertexInterp (pairs [0], pairs [4]);
                if ((edgeTable [cubeIndex] & 512) == 512)
                    vertlist [9] = VertexInterp (pairs [1], pairs [5]);
                if ((edgeTable [cubeIndex] & 1024) == 1024)
                    vertlist [10] = VertexInterp (pairs [2], pairs [6]);
                if ((edgeTable [cubeIndex] & 2048) == 2048)
                    vertlist [11] = VertexInterp (pairs [3], pairs [7]);

                for (int i = 0; triTable [cubeIndex, i] != -1; i += 3) {
                    int index = vertices.Count;

                    vertices.Add (vertlist [triTable [cubeIndex, i]]);
                    vertices.Add (vertlist [triTable [cubeIndex, i + 1]]);
                    vertices.Add (vertlist [triTable [cubeIndex, i + 2]]);

                    float tec = (0.25f);
                    Vector2 uvBase;
                    if (debugger.useDebugUVs) {
                        uvBase = debugger.debugUV;
                    } else {
                        if (block != null) {
                            uvBase = block.UV;
                        } else {
                            uvBase = debugger.debugUV;
                        }

                        uvs.Add (uvBase);
                        uvs.Add (uvBase + new Vector2 (0, tec));
                        uvs.Add (uvBase + new Vector2 (tec, tec));

                        triangles.Add (index + 0);
                        triangles.Add (index + 1);
                        triangles.Add (index + 2);
                    }
                }
            }
        }
    }
    mesh.Clear ();
    mesh.vertices = vertices.ToArray ();
    mesh.triangles = triangles.ToArray ();
    mesh.uv = uvs.ToArray ();
    mesh.RecalculateNormals ();
    mesh.RecalculateBounds ();
    return mesh;
}

bool IsBitSet (int b, int pos)
{
    return ((b & pos) == pos);
}

Vector3 VertexInterp (ValPair pair1, ValPair pair2)
{
    float mu;
    Vector3 p = new Vector3 ();
    if (Mathf.Abs (isolevel - pair1.intsect) < 0.00001)
        return pair1.position;
    if (Mathf.Abs (isolevel - pair2.intsect) < 0.00001)
        return pair2.position;
    if (Mathf.Abs (pair1.intsect - pair2.intsect) < 0.00001)
        return pair1.position;

    mu = (isolevel - pair1.intsect) / (pair2.intsect - pair1.intsect);
    p.x = (float)(pair1.position.x + mu * (pair2.position.x - pair1.position.x));
    p.y = (float)(pair1.position.y + mu * (pair2.position.y - pair1.position.y));
    p.z = (float)(pair1.position.z + mu * (pair2.position.z - pair1.position.z));
    return p;
}

public Mesh ToMesh (Mesh mesh)
{
    if (mesh == null)
        mesh = new Mesh ();
    mesh.Clear ();
    mesh.vertices = vertices.ToArray ();
    mesh.triangles = triangles.ToArray ();
    mesh.uv = uvs.ToArray ();
    mesh.RecalculateNormals ();
    mesh.RecalculateBounds ();
    return mesh;
}

Any other rotated versions of the specific case are seeable. enter image description here

\$\endgroup\$
  • \$\begingroup\$ We can't help you without the code, you should post it automatically. \$\endgroup\$ – Bálint Jun 11 '17 at 15:46
  • \$\begingroup\$ That's right sorry i've added the code for the MC implementation. PS: A nevedből ítélve magyar vagy? \$\endgroup\$ – Statey Jun 11 '17 at 15:52
  • \$\begingroup\$ If you look at your terrain from underneath, do the missing triangles appear? If so, you just need to reverse the winding order of the vertices in that triangle to flip it to the other side. \$\endgroup\$ – DMGregory Jun 11 '17 at 16:50
  • \$\begingroup\$ No they don't appear. And the interesting part is that these are the only triangles that don't appear so... any rotated version of it appears. \$\endgroup\$ – Statey Jun 11 '17 at 16:59
  • \$\begingroup\$ Maybe you could add some small random values to your grid data, so no cell vertex lies on an exact 0 iso-level. I suspect it may be some edge case issue, like '<' vs. '<=' comparison. If this solves the issue you'll know where to look at. \$\endgroup\$ – kolenda Nov 22 '17 at 11:14
1
\$\begingroup\$

I came across the same problem when developing my own system - the bug came from the order of the triangles index. I know it can sounds odd, but try tweaking them and you will see that the rendering will change as well.

"Which side a triangle is visible from is determined by the orientation of its vertex indices. By default, if they are arranged in a clockwise direction the triangle is considered to be forward-facing and visible. Counter-clockwise triangles are discarded so we don't need to spend time rendering the insides of objects, which are typically not meant to be seen anyway."

For further reading, you can refer to http://catlikecoding.com/unity/tutorials/procedural-grid/

\$\endgroup\$
  • \$\begingroup\$ Well, I tried tweaking the triangle indexes, but it didn't help. What I figured out yet is that the cell's value at that triangle is 0 which is under the isolevel so it doesn't have to be rendered. This means that this bug is not because the triangle indexing order. \$\endgroup\$ – Statey Jun 11 '17 at 18:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.