Combining Meshes with subMeshes

How do you combine meshes with submeshes?

I have two meshes, and each mesh has two submeshes. What I want, after combining them, is one mesh, and two submeshes.

just found this question using google, and i found a simpler answer while trying to figure this out:

you can use the .subMeshIndex property on your CombineInstance-s, so for each submesh you want to combine you need to add a CombineInstance with the correct subMeshIndex.

this is how i did it:

(the script is placed on a parent GO, with an empty mesh filter, and a mesh renderer. the script combines child meshes on awake, while keeping the mesh in the same position as the originals were. the resulting mesh uses only 1 material)

        void Awake ()
        {
            // save the parent GO-s pos+rot
            Vector3 position = transform.position;
            Quaternion rotation = transform.rotation;

            // move to the origin for combining
            transform.position = Vector3.zero;
            transform.rotation = Quaternion.identity;

            MeshFilter[] filters = GetComponentsInChildren<MeshFilter> ();
            List<CombineInstance> combine = new List<CombineInstance> ();

            for (int i = 0; i < filters.Length; i++)
            {
                // skip the empty parent GO
                if (filters*.sharedMesh == null)*

continue;

// combine submeshes
for (int j = 0; j < filters*.sharedMesh.subMeshCount; j++)*
{
CombineInstance ci = new CombineInstance ();

ci.mesh = filters*.sharedMesh;*
ci.subMeshIndex = j;
ci.transform = filters*.transform.localToWorldMatrix;*

combine.Add (ci);
}

// disable child mesh GO-s
filters*.gameObject.SetActive (false);*
}

MeshFilter filter = GetComponent ();
filter.mesh = new Mesh ();
filter.mesh.CombineMeshes (combine.ToArray (), true, true);

// restore the parent GO-s pos+rot
transform.position = position;
transform.rotation = rotation;
}

you can combine meshes with with combinechildren script in standard assets. if you import standard assets you can find it in component menu in mesh sub menu. you can combine a mesh with it's children with this script. you can easily modify the code to do what you want because the main part of this script is the function that combines two meshes.

I commented on the previous answer, but I just tested my theory and it seems to work. To recap: I create a NEW mesh for each sub-mesh in an original mesh. I then combine these (sub-mesh) meshes. So for 4 objects that use identical 2-sub-mesh meshes, instead of getting a single combined mesh with 8 sub-meshes, I get 2 combined meshes, with 1 "sub-mesh" each. This brings my draw calls down from 17 to 6, in my test case.

Briefly, I performed 4 steps to combine objects that use meshes with multiple sub-meshes:

  1. Gather a list of GameObjects that I wish to combine.
  2. Go through each mesh filter in the GameObject list from step 1. Create and cache newly constructed Meshes from each sub-mesh (I used this: http://www.unifycommunity.com/wiki/index.php?title=MeshCreationHelper).
  3. For each mesh filter, create a list of CombineInstance data for EACH sub-mesh of the mesh. You'll want the CombineInstance.mesh to point to the cached (sub-mesh) Mesh created in step 2.
  4. For each sub-mesh list of CombineInstances, perform the combine.

Sorry if the wording is poor. Hope this helps.

Update: There appears to be a bug with the MeshCreationHelper script that I linked to above. I don't believe it's properly constructing the new triangle list. I edited the wiki discussion for that page, so you can view my suggested change via the discussion:

http://www.unifycommunity.com/wiki/index.php?title=Talk:MeshCreationHelper

One more suggestion for anyone coming here in the future if the other suggestions don’t suit you. You can do this very easily with the “Easy Mesh Combiner MT” plugin available in the Asset Store. Here is the link: Easy Mesh Combiner MT - Scene Mesh Merge, Atlasing Support & More | Game Toolkits | Unity Asset Store

The answer by @raziel_anarki almost does the trick. The only one little step left to go. Actually we need to combine all corresponding submeshes by index into separate combined meshes, and then combine them all as submeshes within the resulting mesh. Like this:

    // Combines all root's children's meshes into the root's mesh.
    public Mesh CombineMeshesWithSubmeshes(GameObject root)
    {

        // save the parent GO-s pos+rot
        Vector3 position = root.transform.position;
        Quaternion rotation = root.transform.rotation;

        // move to the origin for combining
        root.transform.position = Vector3.zero;
        root.transform.rotation = Quaternion.identity;

        MeshFilter rootMf;
        root.TryGetComponent<MeshFilter>(out rootMf);

        MeshFilter[] meshFilters = root.GetComponentsInChildren<MeshFilter>(); // root included

        List<CombineInstance> combineFinal = new List<CombineInstance>();
        Mesh mesh = null;

        // first find the maximal number of submeshes in each mesh
        int maxSubMeshes = 0;
        foreach (MeshFilter mf in meshFilters)
        {
            if (mf.sharedMesh) {
                maxSubMeshes = Mathf.Max(maxSubMeshes, mf.sharedMesh.subMeshCount);
            }
        }
        // create separate lists for each submesh index found
        List<CombineInstance>[] combines = new List<CombineInstance>[maxSubMeshes];
        for (int j = 0; j < maxSubMeshes; j++)
        {
            combines[j] = new List<CombineInstance>();
        }

        for (int i=0; i < meshFilters.Length; i++)
        {
            if (!meshFilters*.sharedMesh) continue;*

// iterate submeshes, and put them into the corresponding combine lists
for (int j = 0; j < meshFilters*.sharedMesh.subMeshCount; j++)*
{
CombineInstance ci = new CombineInstance
{
mesh = meshFilters*.sharedMesh,*
subMeshIndex = j,
transform = meshFilters*.transform.localToWorldMatrix*
};

combines[j].Add(ci);
}

// disable GO (considering root included)
if (meshFilters != rootMf) meshFilters*.gameObject.SetActive(false);*
}

// first combine all the meshes of the ‘sub’ combines lists into combineFinal list
foreach (List subCombine in combines)
{
mesh = new Mesh();
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.CombineMeshes(subCombine.ToArray(), true, true);
//store combined mesh for further combining
CombineInstance ci = new CombineInstance { mesh = mesh };
combineFinal.Add(ci);
}

// and finally combine all joined submeshes into large single mesh
mesh = new Mesh();
mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32;
mesh.name = “Combined-Subs” + maxSubMeshes.ToString();
// now each mesh becomes a submesh in given order
mesh.CombineMeshes(combineFinal.ToArray(), false, false);

// restore the parent GO-s pos+rot
root.transform.position = position;
root.transform.rotation = rotation;

// assign the mesh to the root’s filter if any
if (rootMf) rootMf.sharedMesh = mesh;

return mesh;
}