Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug with mesh combining.

Discussion in 'Scripting' started by Ziron999, Apr 25, 2020.

  1. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    I can't get multiple materials to work and subindex says it's invalid when i forcefully make it the same??
    see screenshot.
    What am i missing?
     

    Attached Files:

  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Could be just about anything from my point of view, given the lack of properly formatted relevant code in your post.

    Also, pretty sure submesh indices are zero-based. Is that your problem?
     
    Last edited: Apr 25, 2020
    Bunny83 and Yoreki like this.
  3. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    I've been getting errors when trying to combine meshes with multiple materials. I've been reading it's due to the sub meshes...not sure what else you really need to know...

    If i just don't define one and combine the materials for the mesh renderers say:
    This renderer has more materials then the Mesh has submeshes(but it does not, thus the screenshot) Multiple materials will be applied to the same submesh, which costs performance. Consider using multiple shader passes.

    Also, it looks messed up unlike the other combined meshes.
     
    Last edited: Apr 26, 2020
  4. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    here...
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class MeshCombiner : MonoBehaviour
    6. {
    7.     //public Material[] Materials;
    8.  
    9.     void Start()
    10.     {
    11.         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
    12.         MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>();
    13.         CombineInstance[] combine = new CombineInstance[meshFilters.Length];
    14.         Material[] SharedMats = meshRenderers[1].sharedMaterials;
    15.         //Material[] Mats = meshRenderers[1].materials;
    16.  
    17.         int i = 0;
    18.  
    19.         while (i < meshFilters.Length)
    20.         {
    21.             combine[i].mesh = meshFilters[i].sharedMesh;
    22.             combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
    23.             meshFilters[i].gameObject.SetActive(false);
    24.  
    25.             i++;
    26.         }
    27.         GameObject go = new GameObject(gameObject.name);
    28.         go.AddComponent<MeshFilter>();
    29.         go.AddComponent<MeshRenderer>();
    30.    
    31.         var goRenderer = go.GetComponent<MeshRenderer>();
    32.  
    33.         go.SetActive(false);
    34.         go.transform.GetComponent<MeshFilter>().mesh = new Mesh();
    35.         go.transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine);
    36.         go.SetActive(true);
    37.  
    38.         //goRenderer.materials = Mats;
    39.         goRenderer.sharedMaterials = SharedMats;
    40.  
    41.         Destroy(gameObject);
    42.     }
    43. }
    44.  
    now try to use a model with multiple materials and watch what happens to your positioning/texturing.
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Aha! Now we're getting somewhere! The code speaks! And the documentation speaks louder.

    If you refer to the online documentation:

    https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html

    You will notice the second optional argument to the .CombineMeshes() function is called mergeSubMeshes. This optional argument has a default value of true. This means if you do NOT provide this argument, the function is invoked as if you had provided true.

    If you read a few more paragraphs you will see how the behavior of this function changes when that argument is true, as it is (by default) in your code above.

    Perhaps the behavior will be more to your liking if you supply false for that optional argument? I say perhaps because I have never used this API at all personally.
     
  6. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    Here is a before and after result no matter which way i do it this is the best i can get...it's like the transparency for the leaves screws up for some reason.
    I've checked all material settings and they are exactly the same

    I have tried applying false but then it just disappears even when trying to make it a separate GameObject for some reason.

    New Code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class MeshCombiner : MonoBehaviour
    6. {
    7.     void Start()
    8.     {
    9.         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
    10.         MeshRenderer[] meshRenderers = GetComponentsInChildren<MeshRenderer>();
    11.         CombineInstance[] combine = new CombineInstance[meshFilters.Length];
    12.         //Material[] SharedMats = meshRenderers[1].sharedMaterials;
    13.         Material MainMaterial = meshRenderers[1].materials[0];
    14.         Material[] SubMaterial = new Material[meshRenderers[1].materials.Length-1];
    15.         List<CombineInstance> combine2 = new List<CombineInstance>();
    16.  
    17.         for (int i = 0; i < meshFilters.Length; i++)
    18.         {
    19.             Mesh mShared = meshFilters[i].sharedMesh;
    20.  
    21.             combine[i].mesh = mShared;
    22.             combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
    23.  
    24.             if (mShared.subMeshCount > 1)
    25.             {
    26.                 // combine submeshes
    27.                 for (int j = 0; j < mShared.subMeshCount; j++)
    28.                 {
    29.                     if (j < mShared.subMeshCount - 1)
    30.                         SubMaterial[j] = meshRenderers[1].materials[j+1];
    31.                     CombineInstance ci = new CombineInstance();
    32.  
    33.                     ci.mesh = mShared;
    34.                     ci.subMeshIndex = j;
    35.                     ci.transform = meshFilters[i].transform.localToWorldMatrix;
    36.  
    37.                     combine2.Add(ci);
    38.                 }
    39.             }
    40.  
    41.             meshFilters[i].gameObject.SetActive(false);
    42.         }
    43.         GameObject go = new GameObject(gameObject.name);
    44.         go.AddComponent<MeshFilter>();
    45.         go.AddComponent<MeshRenderer>();
    46.      
    47.         var goFilter = go.GetComponent<MeshFilter>();
    48.         var goRenderer = go.GetComponent<MeshRenderer>();
    49.  
    50.         go.SetActive(false);
    51.         goFilter.mesh = new Mesh();
    52.         goFilter.mesh.CombineMeshes(combine);
    53.         go.SetActive(true);
    54.  
    55.         //goRenderer.materials = Mats;
    56.         goRenderer.material = MainMaterial;
    57.  
    58.         if (SubMaterial.Length >= 1)
    59.         {
    60.             GameObject go2 = new GameObject(gameObject.name + "Sub");
    61.             go2.AddComponent<MeshFilter>();
    62.             go2.AddComponent<MeshRenderer>();
    63.  
    64.             var goFilter2 = go2.GetComponent<MeshFilter>();
    65.             var goRenderer2 = go2.GetComponent<MeshRenderer>();
    66.  
    67.             go2.SetActive(false);
    68.             goFilter2.mesh = new Mesh();
    69.             goFilter2.mesh.CombineMeshes(combine2.ToArray());
    70.             go2.SetActive(true);
    71.  
    72.             //goRenderer.materials = Mats;
    73.             goRenderer2.material = SubMaterial[0];
    74.         }
    75.  
    76.         Destroy(gameObject);
    77.     }
    78. }
    79.  
     

    Attached Files:

  7. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    AHA
    I finally fixed it and it's because submesh ZERO has been going on no matter what so i changed the part of the code that involves the 2nd gameobject and now it works!
    Code (CSharp):
    1.             if (mShared.subMeshCount > 1)
    2.             {
    3.                 // combine submeshes
    4.                 for (int j = 1; j < mShared.subMeshCount; j++)
    5.                 {
    6.                     SubMaterial[j-1] = meshRenderers[1].sharedMaterials[j];
    7.                     CombineInstance ci = new CombineInstance();
    8.  
    9.                     ci.mesh = mShared;
    10.                     ci.subMeshIndex = j;
    11.                     ci.transform = meshFilters[i].transform.localToWorldMatrix;
    12.  
    13.                     combine2.Add(ci);
    14.                 }
    15.             }
     
  8. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    Amazing like 200 objects are just 2 objects now!!!

    That took a lot of understanding but I figured it out. thx anyway and sorry for not being clear enough.

    I still want to make this more dynamic but at least it works with 2 materials for now and that will take care of like 99% of the game. Don't really have a reason to go further now lol.
     
    Kurt-Dekker likes this.
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Ha, well, look at what I found: I finally tried out that API myself and actually got it to work, but as you saw, it was NOT easy.

    First, there were unfortunately a LOT of issues with Unity's sample code. I will be flagging them to Unity in a followup.

    I started from this reference:

    https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html

    You can compare the above original source code above to my code below, which works as advertised.

    Code (csharp):
    1. // code originally from this Unity3D website:
    2. // https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html
    3. //
    4. // Improved by Kurt Dekker @kurtdekker
    5. //
    6. // Usage:
    7. // - make a blank object
    8. // - put this script on it
    9. // - parent all other mesh GameObjects below this one
    10. // - run
    11. // - voila!
    12.  
    13. using UnityEngine;
    14. using System.Collections;
    15. using System.Collections.Generic;
    16.  
    17. [RequireComponent(typeof(MeshFilter))]
    18. [RequireComponent(typeof(MeshRenderer))]
    19. public class ExampleCombinerImproved : MonoBehaviour
    20. {
    21.     void Start()
    22.     {
    23.         MeshFilter[] meshFilters = GetComponentsInChildren<MeshFilter>();
    24.         List<CombineInstance> combines = new List<CombineInstance>();
    25.  
    26.         // KurtFixed: handle materials... I mean, they're kind of important!
    27.         List<Material> materials = new List<Material>();
    28.  
    29.         for (int i = 0; i < meshFilters.Length; i++)
    30.         {
    31.             // KurtFixed: we gotta ignore ourselves or our count would be off!
    32.             if (meshFilters[i] == GetComponent<MeshFilter>())
    33.             {
    34.                 continue;
    35.             }
    36.  
    37.             // KurtFixed: tally up the materials, since each mesh could have multiple
    38.             var mr = meshFilters[i].GetComponent<MeshRenderer>();
    39.             for (int j = 0; j < mr.materials.Length; j++)
    40.             {
    41.                 var combine = new CombineInstance();
    42.  
    43.                 combine.mesh = meshFilters[i].sharedMesh;
    44.                 combine.subMeshIndex = j;
    45.  
    46.                 combine.transform = meshFilters[i].transform.localToWorldMatrix;
    47.                 meshFilters[i].gameObject.SetActive(false);
    48.  
    49.                 combines.Add(combine);
    50.  
    51.                 materials.Add( mr.materials[j]);
    52.             }
    53.         }
    54.         transform.GetComponent<MeshFilter>().mesh = new Mesh();
    55.         transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combines.ToArray(), false);
    56.         transform.gameObject.SetActive(true);
    57.  
    58.         // KurtFixed: inject the original materials
    59.         gameObject.GetComponent<MeshRenderer>().materials = materials.ToArray();
    60.     }
    61. }
    I also included a sample scene setup with primitives that get combined when you play. Since none of the primitives have two materials, I made an FBX with two materials, an icosphere, and put it all together, and it all works and combines.
     

    Attached Files:

  10. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    It's actually very buggy. Haven't used your package yet but just used my working code in a bigger map and this happens now. upload_2020-4-26_13-57-46.png
     
  11. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    used your package and everything just straight disappears and i have nothing. doesn't work at all? lol
    unity seriously needs to redesign this.
     
  12. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    It seems like there is a bug when the objects are very far apart from one another. If i break it up and have them a little closer together the bug does not happen. Meaning i need to still have like 10 or so GO's instead of thousands broken up into sections.
    I also need to have each parent's children have the same exact materials in the same exact array locations for it to work.
     
    Last edited: Apr 26, 2020
  13. Ziron999

    Ziron999

    Joined:
    Jan 22, 2014
    Posts:
    278
    //RULES:
    //1: Can not have to many vertices when combining.
    //2: Materials in the children must all be the same
    //3: The materials ALSO must be in the EXACT same part in the array
    //4: More then 2 materials not supported currently.
     
    kukewilly likes this.
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,711
    Before you waste too much more time on this, are you aware that Unity does dynamic and static batching already? Any additional benefit you get by all this custom hackery has to be "netted out" against the baseline.
     
    Bunny83 likes this.
  15. DarkGate

    DarkGate

    Joined:
    Jan 26, 2016
    Posts:
    33
    I think that this type of system is great for creating dynamic objects at runtime. It will significantly reduce your batch size and help boost your FPS. Otherwise, stick to dynamic and static batching depending on your platform.
     
    Bunny83 likes this.
  16. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    Note that you can use the StaticBatchingUtility at runtime if you dynamically create objects. Note that once you called Combine it can not be "undone". So the only true solution is to destroy the root object and start from scratch. I've used this around 8 years ago when we were working on a track mania like track building system for mobile. The performance was horrible when you had 100+ segments but using the batching utility once loaded worked well.
     
    Kurt-Dekker likes this.