unity-game-engine3dmeshmarching-cubes

Unity Mesh UV problem on the positive and negative x-axis on perpendicular walls


I am trying to make a 3D-voxel game that uses the marching cubes algorithm to procedurally generate a game world. This is working fine so far, except that, on exactly perpendicular sides on the positive and negative x sides of a given perpendicular piece of the world/chunk mesh, it looks like the uv coordinates aren't quite right as it just displays a solid color instead of the texture. [![view of debug-chunks and the buggy sides][1]][1]

At <2.> you can see the chunk wall how it is supposed to look like and at <1.> you can see the weird bug. This ONLY occurs on exactly perpendicular x-side triangles! Those meshes in the image are debug-chunks to show the problem. All the noise-to-terrain translation works fine and I don't get any bugs there. It's only the uvs that cause problems. I am using the following code to populate the Mesh.vertices, Mesh.triangles and Mesh.uv:

void MakeChunkMeshData()
{
    for (int x = 0; x < Variables.chunkSize.x - 1; x++)
    {
        for (int y = 0; y < Variables.chunkSize.y - 1; y++)
        {
            for (int z = 0; z < Variables.chunkSize.z - 1; z++)
            {
                byte[] cubeCornerSolidityValues = new byte[8]{
                    chunkMapSolidity[x, y, z],
                    chunkMapSolidity[x + 1, y, z],
                    chunkMapSolidity[x + 1, y + 1, z],
                    chunkMapSolidity[x, y + 1, z],

                    chunkMapSolidity[x, y, z + 1],
                    chunkMapSolidity[x + 1, y, z + 1],
                    chunkMapSolidity[x + 1, y + 1, z + 1],
                    chunkMapSolidity[x, y + 1, z + 1]
                };

                MarchOne(new Vector3Int(x, y, z), cubeCornerSolidityValues);
            }
        }
    }
}

void DrawChunk()
{
    chunkMesh.vertices = vertices.ToArray();
    chunkMesh.triangles = triangles.ToArray();
    chunkMesh.SetUVs(0, uvs.ToArray());
    chunkMesh.RecalculateNormals();
}

void ClearMesh()
{
    chunkMesh.Clear();
}

void ClearChunkMeshAndData()
{
    chunkMesh.Clear();
    uvs = new List<Vector2>();
    vertices = new List<Vector3>();
    triangles = new List<int>();
}

/// <summary>
/// cube contains bytes for each corner of the cube
/// </summary>
/// <param name="cube"></param>
/// <returns></returns>
int GetCubeConfiguration(byte[] cube)
{
    int u = 0;
    int result = 0;
    for(int corner = 0; corner < 8; corner++)
    {
        if (cube[corner] < Variables.solidityThreshold)
        {
            u++;
            result |= 1 << corner;
        }
    }
    return result;
}

Vector2[] getUvsPerTriangle(byte[] voxelTypes)
{
    int resId = voxelTypes[0];
    if (voxelTypes[1] == voxelTypes[2])
        resId = voxelTypes[1];
    resId = 1;

    Vector2 normalized = getUvCoordFromTextureIndex(resId) / Constants.TextureAtlasSizeTextures;
    float textureLength = 1f/Constants.TextureAtlasSizeTextures;
    
    Vector2[] result = new Vector2[3] {
        normalized + new Vector2(1,0) * textureLength,
        normalized + new Vector2(0,1) * textureLength,
        normalized + new Vector2(1,1) * textureLength
    };
    //Debug.Log(result);
    return result;
}

/// <summary>
/// returns the absolute x and y coordinates of the given texture in the atlas (example: [4, 1])
/// </summary>
/// <param name="textureIndex"></param>
/// <returns></returns>
Vector2 getUvCoordFromTextureIndex(int textureIndex)
{
    int x = textureIndex % Constants.TextureAtlasSizeTextures;
    int y = (textureIndex - x) / Constants.TextureAtlasSizeTextures;
    return new Vector2(x, y);
}

/// <summary>
/// takes the chunk-wide mesh data and adds its results after marching one cube to it.
/// </summary>
/// <returns></returns>
void MarchOne(Vector3Int offset, byte[] cube)
{
    int configuration = GetCubeConfiguration(cube);

    byte[] voxelTypes = new byte[3];

    int edge = 0;
    for (int i = 0; i < 5; i++) //loop at max 5 times (max number of triangles in one cube config)
    {
        for(int v = 0; v < 3; v++)  // loop 3 times through shit(count of vertices in a TRIangle, who would have thought...)
        {
            int cornerIndex = VoxelData.TriangleTable[configuration, edge];

            if (cornerIndex == -1)  // indicates the end of the list of vertices/triangles
                return;

            Vector3 vertex1 = lwTo.Vec3(VoxelData.EdgeTable[cornerIndex, 0]) + offset;
            Vector3 vertex2 = lwTo.Vec3(VoxelData.EdgeTable[cornerIndex, 1]) + offset;

            Vector3Int vertexIndex1 = lwTo.Vec3Int(VoxelData.EdgeTable[cornerIndex, 0]) + offset;
            Vector3Int vertexIndex2 = lwTo.Vec3Int(VoxelData.EdgeTable[cornerIndex, 1]) + offset;

            Vector3 vertexPosition;
            if (Variables.badGraphics)
            {
                vertexPosition = (vertex1 + vertex2) / 2f;
            }
            else
            {
                // currently using this "profile" 
                // this code determines the position of the vertices per triangle based on the value in chunkSolidityMap[,,]
                float vert1Solidity = chunkMapSolidity[vertexIndex1.x, vertexIndex1.y, vertexIndex1.z];
                float vert2Solidity = chunkMapSolidity[vertexIndex2.x, vertexIndex2.y, vertexIndex2.z];

                float difference = vert2Solidity - vert1Solidity;
                difference = (Variables.solidityThreshold - vert1Solidity) / difference;

                vertexPosition = vertex1 + ((vertex2 - vertex1) * difference);
            }
            vertices.Add(vertexPosition);
            triangles.Add(vertices.Count - 1);

            voxelTypes[v] = chunkMapVoxelTypes[vertexIndex1.x, vertexIndex1.y, vertexIndex1.z];

            edge++;
        }
        uvs.AddRange(getUvsPerTriangle(voxelTypes));
    }
}

EDIT: when only slightly rotating the chunks, the weird problem immediately disappears. I don't know why, but at least i now have some clue what's going on here. [1]: https://i.sstatic.net/kYnkl.jpg


Solution

  • Well, it turns out that this probably is some weird behavior from unity. When explicitly setting the mesh after the mesh population process as the meshFilters' mesh, it works just fine. Also, I had to call mesh.RecalculateTangents() to make it work.