1
\$\begingroup\$

I procedurally generate chunks and use the marching cube algorithm to create meshes in 3D. At first, I just created a gameObject for each cube (of course, I knew it was not optimized, but I wanted to see if the algorithm worked). For now I stock all gamesObject in child of "blocks" but it would be much more optimized to directly merge the meshes between them and only create a single gameObject. However, i did not find any way to directly merge the meshes between them.
So my question is: is there a way to directly merge meshes between them without having to use gameObject? (don't worry about texturing)

Here's the code:

    public static GameObject GenerateChunk(int[,,] map, int chunkSize, Vector3 chunkPosition)
    {
        GameObject blocks = new GameObject("BlockList");
        GameObject newMesh;
        int[] cubeVertices = new int[8];


        // create each mesh in a gameObject as child of "blocks"
        for (int x = 0; x < chunkSize; x++)
            for (int z = 0; z < chunkSize; z++)
                for (int y = 0; y < chunkSize; y++) {
                    /*
                    define the vertices (cubeVertices)
                    */
                    Vector3 cubePosition = new Vector3(chunkPosition.x + x, chunkPosition.y + y, chunkPosition.z + z);
                    newMesh = GenerateCube(cubePosition, cubeVertices);

                    if (newMesh != null)
                        newMesh.transform.parent = blocks.transform;
                }

        //combine every children of "blocks" and delete them
        MeshFilter[] filters = blocks.GetComponentsInChildren<MeshFilter>();
        List<CombineInstance> combine = new List<CombineInstance>();
        for (int i = 0; i < filters.Length; i++) {
            if (filters[i].sharedMesh == null)
                continue;
            for (int j = 0; j < filters[i].sharedMesh.subMeshCount; j++) {
                CombineInstance ci = new CombineInstance
                {
                    mesh = filters[i].sharedMesh,
                    subMeshIndex = j,
                    transform = filters[i].transform.localToWorldMatrix
                };
                combine.Add(ci);
            }
            Destroy(filters[i].gameObject);
        }

        //create the chunk and set the final mesh with the CombineMeshes function
        GameObject chunk = new GameObject("Chunk", typeof(MeshFilter), typeof(MeshRenderer));
        chunk.GetComponent<MeshFilter>().mesh.CombineMeshes(combine.ToArray(), true, true);
        Destroy(blocks);
        return chunk;
    }
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

Your code shown here doesn't actually depend on GameObjects - they're just serving as an expensive way for you to store a set of positions. So you could get something equivalent as follows, just using transformation matrices instead:

// You can assign the default Unity cube here so you have a template mesh to stamp-out.
public Mesh cubeMesh;

public static Mesh GenerateChunkMesh(int[,,] map, int chunkSize, Vector3Int chunkPosition)
{
    // Prep a list of mesh instances to stitch together.
    List<CombineInstance> instances = new List<CombineInstance>();

    // Prepare a "template" object with the attributes we want to apply to each block.
    CombineInstance instance;
    instance.mesh = cubeMesh;

    for (int x = 0; x < chunkSize; x++)
        for (int z = 0; z < chunkSize; z++)
            for (int y = 0; y < chunkSize; y++) {                    
                Vector3Int cubePosition = chunkPosition + new Vector3Int(x, y, z);

                // If this part of the map is empty, skip it without making a block.
                if(map[cubePosition.x, cubePosition.y, cubePosition.z] == 0)
                    continue;

                // Make a transformation matrix that positions the cube at this location.
                instance.transform = Matrix4x4.Translate(cubePosition);

                // Record "I want a cube at this position" in our work list.
                instances.Add(instance);
            }
        }
    }

    // Build a mesh that combines all of the requested cubes in our work list.
    var mesh = new Mesh();
    mesh.CombineMeshes(instances.ToArray(), true, true);

    return mesh;
}

But note that this is still rendering more geometry than you need, because you're not eliminating hidden faces. This answer outlines how you can do that with your own mesh generation routine.

\$\endgroup\$
0
\$\begingroup\$

ps: This is an answer for someone using marching cubes algorithm WITHOUT using Chunks. I tried the precedent solution for marching cubes but without chunks, and instance.transform = Matrix4x4.Translate(cubePosition); makes my every little cubes distanciated a bit, so to solve this, i used instead instance.transform = transform.localToWorldMatrix; whichi i found in the unity documentation example. Hopes, this helps someone :)

\$\endgroup\$
4
  • \$\begingroup\$ It sounds like you have a scale applied to your cubes, so the matrix is not a pure translation. \$\endgroup\$
    – DMGregory
    Commented May 1, 2021 at 12:30
  • \$\begingroup\$ well yeah, i made the vertexes in a way that i can specify the default step between them. will that create any probleme? \$\endgroup\$
    – TensaDev
    Commented May 1, 2021 at 14:19
  • \$\begingroup\$ Nope, it's just a different convention than the original Q&A covered. I wanted to clarify that this has nothing to do with chunks versus no chunks, just your choice to use a different grid spacing convention. \$\endgroup\$
    – DMGregory
    Commented May 1, 2021 at 14:35
  • \$\begingroup\$ Oh ok, you clarified to me too. i was Wrong. Thx \$\endgroup\$
    – TensaDev
    Commented May 1, 2021 at 14:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .