Sunday, August 28, 2016

Multi-Material Mesh Merge Snippet

For this video

public void AdvancedMerge()
 {
  // All our children (and us)
  MeshFilter[] filters = GetComponentsInChildren (false);

  // All the meshes in our children (just a big list)
  List materials = new List();
  MeshRenderer[] renderers = GetComponentsInChildren (false); // <-- you can optimize this
  foreach (MeshRenderer renderer in renderers)
  {
   if (renderer.transform == transform)
    continue;
   Material[] localMats = renderer.sharedMaterials;
   foreach (Material localMat in localMats)
    if (!materials.Contains (localMat))
     materials.Add (localMat);
  }

  // Each material will have a mesh for it.
  List submeshes = new List();
  foreach (Material material in materials)
  {
   // Make a combiner for each (sub)mesh that is mapped to the right material.
   List combiners = new List ();
   foreach (MeshFilter filter in filters)
   {
    if (filter.transform == transform) continue;
    // The filter doesn't know what materials are involved, get the renderer.
    MeshRenderer renderer = filter.GetComponent ();  // <-- (Easy optimization is possible here, give it a try!)
    if (renderer == null)
    {
     Debug.LogError (filter.name + " has no MeshRenderer");
     continue;
    }

    // Let's see if their materials are the one we want right now.
    Material[] localMaterials = renderer.sharedMaterials;
    for (int materialIndex = 0; materialIndex < localMaterials.Length; materialIndex++)
    {
     if (localMaterials [materialIndex] != material)
      continue;
     // This submesh is the material we're looking for right now.
     CombineInstance ci = new CombineInstance();
     ci.mesh = filter.sharedMesh;
     ci.subMeshIndex = materialIndex;
     ci.transform = Matrix4x4.identity;
     combiners.Add (ci);
    }
   }
   // Flatten into a single mesh.
   Mesh mesh = new Mesh ();
   mesh.CombineMeshes (combiners.ToArray(), true);
   submeshes.Add (mesh);
  }

  // The final mesh: combine all the material-specific meshes as independent submeshes.
  List finalCombiners = new List ();
  foreach (Mesh mesh in submeshes)
  {
   CombineInstance ci = new CombineInstance ();
   ci.mesh = mesh;
   ci.subMeshIndex = 0;
   ci.transform = Matrix4x4.identity;
   finalCombiners.Add (ci);
  }
  Mesh finalMesh = new Mesh();
  finalMesh.CombineMeshes (finalCombiners.ToArray(), false);
  myMeshFilter.sharedMesh = finalMesh;
  Debug.Log ("Final mesh has " + submeshes.Count + " materials.");
 }

2 comments:

Himanshu singh chauhan said...

First of all...your tutorial was just awesome ... ur way of explaining is really cool.

Everything is working fine....except the the vert count...u see when ever there are n different meshes (without any submesh) the vert count is the sum of vert count of individual meshes(which it should be)..... but whenever there is a mesh with multiple submeshes the vert count of each submesh is taken as the vert count of the entire parent mesh(this is causing me a problem)... For example if there are 3 meshes A(with 2 submesh, m total verts) , B(0 submesh, n total verts) , C(0 submesh , k total verts)...so ideally the vert count of final mesh should be = m + n + k...but i am getting = 2m + n + k.... ;( (m verts for each submesh of A )

Sorry for such long comments.... i'll really appreciate if u look into this...asap.

Craig Perko said...

It sounds like the verts are being included in both submeshes. I don't know whether that's a bug in the way you created the mesh or in the combiner function, I've never noticed it either way.

If you can't solve the problem any other way, you can always create a mesh that is just the one submesh you want, then delete all the verts that aren't mentioned by the tris. This will require you to understand how the verts and tris work, though.