Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

CombineMeshes example flawed?

Discussion in 'Scripting' started by jcarpay, Oct 30, 2009.

  1. jcarpay

    jcarpay

    Joined:
    Aug 15, 2008
    Posts:
    561
  2. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Er...yeah, if by "flawed" you mean "can't possibly work and won't even compile".... Here's some actual working code:

    Code (csharp):
    1. // This script should be put on an empty GameObject
    2. // Objects to be combined should be children of the empty GameObject
    3. @script RequireComponent(MeshFilter)
    4. @script RequireComponent(MeshRenderer)
    5.  
    6. function Start () {
    7.     for (var child in transform)
    8.         child.position += transform.position;
    9.     transform.position = Vector3.zero;
    10.     transform.rotation = Quaternion.identity;
    11.    
    12.     var meshFilters = GetComponentsInChildren(MeshFilter);
    13.     var combine : CombineInstance[] = new CombineInstance[meshFilters.Length-1];
    14.     var index = 0;
    15.     for (var i = 0; i < meshFilters.Length; i++)
    16.     {
    17.         if (meshFilters[i].sharedMesh == null) continue;
    18.         combine[index].mesh = meshFilters[i].sharedMesh;
    19.         combine[index++].transform = meshFilters[i].transform.localToWorldMatrix;
    20.         meshFilters[i].renderer.enabled = false;
    21.     }
    22.     GetComponent(MeshFilter).mesh = new Mesh();
    23.     GetComponent(MeshFilter).mesh.CombineMeshes (combine);
    24.     renderer.material = meshFilters[1].renderer.sharedMaterial;
    25. }
    (It's stuff like this which makes me wonder why in the world GetComponentsInChildren doesn't do what it says, and return components in the children only.... Seems like that trips me up almost every time.)

    --Eric
     
    Herman-Tulleken likes this.
  3. jcarpay

    jcarpay

    Joined:
    Aug 15, 2008
    Posts:
    561
    Thanks a bunch!
     
  4. Mixality_KrankyBoy

    Mixality_KrankyBoy

    Joined:
    Mar 27, 2009
    Posts:
    739
    This code simply will not work for me. The game objects being combined simple disappear. I will keep experimenting but I have removed all other scripts on the base empty object. Any ideas?
     
  5. Mixality_KrankyBoy

    Mixality_KrankyBoy

    Joined:
    Mar 27, 2009
    Posts:
    739
    It appears the code you added to compensate for the empty game object NOT being at 0,0,0 did not work.
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    I tested it several times and it works fine.

    --Eric
     
  7. Ramen Sama

    Ramen Sama

    Joined:
    Mar 28, 2009
    Posts:
    561
    Works fine!

    Was trying out the documentation provided by unity and was getting frustrated.. Found this thread with actual working code!
     
  8. Kragh

    Kragh

    Joined:
    Jan 22, 2008
    Posts:
    661
    It's probably because your combined version of your meshes exeeds the limit of 65 k. vertices. I know I have had that at some point.
     
  9. Mixality_KrankyBoy

    Mixality_KrankyBoy

    Joined:
    Mar 27, 2009
    Posts:
    739
    That's likely it - I gave up on it as it never worked for me in most cases.
     
  10. kingdruid

    kingdruid

    Joined:
    Jun 14, 2008
    Posts:
    64
    Doesn't look like this works with a submesh.

    Any idea's?

    I tried adding the following in the for loop:
    Code (csharp):
    1.  
    2.       combine[index].mesh.subMeshCount = meshFilters[i].mesh.subMeshCount;
    3.       combine[index].mesh.SetTriangles(meshFilters[i].mesh.GetTriangles(0),0);
    4.       combine[index].mesh.SetTriangles(meshFilters[i].mesh.GetTriangles(1),1);
    and the following at the end:
    Code (csharp):
    1.  
    2.    GetComponent(MeshFilter).mesh.subMeshCount = 2;
    My mesh only has 2 submesh.
     
  11. duke

    duke

    Joined:
    Jan 10, 2007
    Posts:
    763
    I've got a similar problem where i'm generating meshes via code. Making a single one works, but running it through a loop where each loop adds to a CombineInstance, which is added to a single mesh at the end of the method, returns an empty mesh..

    The final/returned mesh has the correct amount of verts+tris.
     
  12. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    Can you post the code you are using, Duke?
     
  13. silencer

    silencer

    Joined:
    May 16, 2009
    Posts:
    80
    I'm getting the same behavior. Is this a GPU issue, or something else I'm missing?
     
  14. silencer

    silencer

    Joined:
    May 16, 2009
    Posts:
    80
    I think I solved it. If you apply this to existing meshes (i.e - they were in the scene before you applied the script to the parent object), it won't work and your meshes will disappear. You will have to rebuild the hierarchy of meshes again, unfortunately.
     
  15. ant123

    ant123

    Guest

    Joined:
    May 31, 2011
    Posts:
    13
    A clear explanation of this would be:

    A/Place this code on a parent object with meshes already in it
    B/Place in object where clones will appear
    C/place above cloning script
    D/Place below cloning script
    E/Place in seperate parent object to cloning script's gameobject
    F/Place limit of 65k vertices for every combinemesh script.
    G/Parent requires Mesh Filter
    H/Clone script requires Mesh Filter

    It failed for me and i have all those questions that i cant find and answer for, any help is welcome.
     
    Last edited: Aug 12, 2011
  16. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    I realize this is an old thread, but.... is there any way to preserve the lightmapping on the meshes that are combined? Some trick I'm missing? I've verified the UV's are copied over correctly, however, the lightmaps render with the incorrect mapping.

    I'm sure it's because the empty parent object doesn't have the proper lightmap index set up and the atlas tiling and offset information.. and if the lightmap scale had been changed on any of the child objects?

    I can manually fiddle with the atlas tiling and offset for the parent and get one of the objects to look right..

    Does this mean I have to manually remap all the lightmap UV's to get this to work and if so, how come CombineMeshes doesn't automatically do this?
     
  17. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    For anyone that happens along this thread... I figured out the questions I asked above.

    You do have to remap the lightmap uvs yourself if you're hoping to combine already lightmapped objects.

    First create the new, empty game object with the lightmapIndex set properly ( if you're using multiple lightmaps, you'll have to combine objects using only the same lightmap index ), set the lightmapTilingOffset to Vector4( 1.0f, 1.0f, 0.0f, 0.0f ). And then for each mesh you are combining, modify the mesh.uv2 elements to adjust for the fact that there is no longer a gameobject they are tied to that provides the proper lightmapTilingOffset to the renderer. That code will be something like this:

    Code (csharp):
    1.  
    2. // remap the lightmap uvs before combining
    3. Vector2[] uvs = mesh.uv2;
    4. Vector4 lightmapTilingOffset = meshFilter.renderer.lightmapTilingOffset;
    5.                        
    6. // remap the uvs to adjust for the parent objects lightmap tiling and offset values
    7. Vector2 uvscale = new Vector2( lightmapTilingOffset.x, lightmapTilingOffset.y );
    8. Vector2 uvoffset = new Vector2( lightmapTilingOffset.z, lightmapTilingOffset.w );
    9. for ( int j = 0; j < uvs.Length; j++ ) {
    10.       uvs[j] = uvoffset + new Vector2( uvscale.x * uvs[j].x, uvscale.y * uvs[j].y );
    11. }            
    12. mesh.uv2 = uvs;
    13.  
    14.  
    Additionally, if you're using negative scale on any of the objects to flip them in the scene ( evident by them drawing inside out in your scene now ), then you'll want to flip the winding order of the verts in all of the triangles for that individual mesh.

    It would be nice if in a future version of the engine, CombineMeshes did these steps for us.

    -andrew
     
  18. podperson

    podperson

    Joined:
    Jun 6, 2006
    Posts:
    1,371
    Thanks MrScary ... this helps but the gotcha I found was I need to run a light mapping pass on the scene before the combine or the offsets will be useless.
     
  19. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    Right.. what are you trying to do? In my case, I was taking a lot of small meshes and combining them into bigger ones to reduce object counts. The lightmaps have to be baked first. It's possible that you could check the "lock atlas" toggle in the light mapping window and rebake afterwards, but I wouldn't hold your breath.
     
  20. HazeTI

    HazeTI

    Joined:
    Jan 12, 2012
    Posts:
    83
    Another nugget for anyone else who comes across this. We were having the same problem as MrScary, but our situation was slightly different. In our case we were taking a prefab that was made from several meshes and using CombineMeshes to create a prefab with a single mesh.

    In our instance, the meshes which had their .uv2 set all had their lightmaps come out wrong, the meshes which did not have any uv2 (i.e. set to null) were correct when a light pass was done. So I had to make sure there were no uv2 values set after combining the meshes.
     
  21. Vengent

    Vengent

    Joined:
    Jan 15, 2012
    Posts:
    23
    Old thread, but ran into this same problem and it doesn't seem to resolve according to thread.

    I used the updated code instead of the sample, and get the same error.

    'sharedMesh' is not a member of a 'UnityEngine.Component'.

    It's on an empty game object that has a bunch of children (cubes).

    My root problem is using flattened cubes (read this was better than planes?) to build a ground of tiles, when I used a grid projector it drastically increased the draw calls (from 74 to 2017!), trying to find a way to optimize that.

    Note - all tiles use the same texture for now.
     
  22. MrScary

    MrScary

    Joined:
    Dec 8, 2007
    Posts:
    94
    Well i'm just responding to the flattened cubes vs a plane. I suppose that's possible depending on how many subdivisions the plane has... That said, you shouldn't use either of those for a bunch of square ground tiles :)

    Just generate a bunch of quads. It will run super fast. Look at the Mesh samples for how to build your own meshes.
     
  23. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    The original code wasn't written for #pragma strict...here's a small revision:

    Code (csharp):
    1. // This script should be put on an empty GameObject
    2. // Objects to be combined should be children of the empty GameObject
    3. #pragma strict
    4. #pragma downcast
    5.  
    6. @script RequireComponent(MeshFilter)
    7. @script RequireComponent(MeshRenderer)
    8.  
    9. function Start () {
    10.     for (var child : Transform in transform)
    11.         child.position += transform.position;
    12.     transform.position = Vector3.zero;
    13.     transform.rotation = Quaternion.identity;
    14.  
    15.     var meshFilters = GetComponentsInChildren.<MeshFilter>();
    16.     var combine : CombineInstance[] = new CombineInstance[meshFilters.Length-1];
    17.     var index = 0;
    18.  
    19.     for (var i = 0; i < meshFilters.Length; i++)
    20.     {
    21.         if (meshFilters[i].sharedMesh == null) continue;
    22.         combine[index].mesh = meshFilters[i].sharedMesh;
    23.         combine[index++].transform = meshFilters[i].transform.localToWorldMatrix;
    24.         meshFilters[i].renderer.enabled = false;
    25.     }
    26.  
    27.     GetComponent(MeshFilter).mesh = new Mesh();
    28.     GetComponent(MeshFilter).mesh.CombineMeshes (combine);
    29.     renderer.material = meshFilters[1].renderer.sharedMaterial;
    30. }
    --Eric
     
    mtdrume and Lylek like this.
  24. Vengent

    Vengent

    Joined:
    Jan 15, 2012
    Posts:
    23
    Hmm, I genned up a basic quad in maya and imported in, applied it to my colliders on the tile prefabs, the tris went down, but not the draw calls.

    Hmm, on further inspection, this hosed all my scale stuff, guess I didn't do something in maya right.

    Edit: Thanks for the updated script - It had some weirdness with textures disappearing. I ended up importing the scripts package and trying that included CS one, it worked like a champ, and brought draw calls down (with grid projector) to like 20 (from 2k+).

    Just when I felt like I was getting a handle on unity in general, boom, meshes!
     
    Last edited: May 25, 2012
  25. Tuhljin

    Tuhljin

    Joined:
    Jun 23, 2012
    Posts:
    12
    I know this is an old thread but I ran across Eric's script above and had a problem. It works fine if it's called only once, but if I instantiate more children for the parent and call it again, I get an error: "IndexOutOfRangeException: Array index is out of range." This points to the line:
    Code (csharp):
    1.  
    2. combine[index].mesh = meshFilters[i].sharedMesh
    3.  
    Some testing shows that it's having problems with combine[index] (and not meshFilters[ i ]).

    I've spent hours searching for and trying various solutions but nothing really panned out. There's probably a simple fix I'm overlooking.

    Now, before you answer, there might be a different solution altogether for what I need, so I'll give you that: What it really comes down to is that I need to be able to add or remove children from the parent GameObject and have the mesh be updated to reflect the changes. What's the best way to do this?
     
  26. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    You'd need a quite different approach in that case. Offhand I'd think of storing the starting index and length for the vertices/triangles/etc. for each object that ends up being part of the combined mesh, so you'd have a way of referencing the different objects within the mesh. You'd have to redo the triangle indices when adding/removing parts of the mesh, or else just zero out the vertices when removing parts.

    --Eric
     
  27. Tuhljin

    Tuhljin

    Joined:
    Jun 23, 2012
    Posts:
    12
    Thanks for the reply. I may look into that for some of the objects. However, much of the time, all the child objects are just translucent cubes or rectangular boxes. (It's not exactly like this, but imagine the "dig here" indicator in Dungeon Keeper 2 except with more possible depth.) Maybe in those cases, I should just be editing the shape of a single object instead of creating a bunch of them and combining their meshes. Is there a good tutorial somewhere on how to do this? Everyone seems to just point to Unity's Procedural example project, which I've downloaded and checked out, but I'm not sure how to start with that. It seems to do fancier stuff than what I need and doesn't give a simple launching point for figuring it out.
     
    Last edited: Sep 2, 2012
  28. augasur

    augasur

    Joined:
    Mar 4, 2012
    Posts:
    133
    Sorry for reviving very old thread, but how to use it with more than one material on the objects?
     
  29. junglemason

    junglemason

    Joined:
    Dec 30, 2010
    Posts:
    69
    Using either Eric's script, or the example that's in the Unity docs now, it turns everything perfectly inside out. I guess it's reversing the winding order on everything? Just a guess. How do I fix that? In my original meshes, I am not doing any negative scale to flip anything around.

    The one strange thing I noticed is that when I set localPosition on something when the parent has been rotated, it is ignoring the parent's rotation. Something higher up might be going wrong with my meshes...
     
  30. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    I'm quite sure it doesn't turn anything inside out. Tried it again now, and yep, it's fine.

    --Eric
     
  31. junglemason

    junglemason

    Joined:
    Dec 30, 2010
    Posts:
    69
    I'm confident it works for you and others. I'm just trying to see if anyone has advice for why it's not working for me, maybe if they've encountered the same thing on their end and fixed it. Tossing it out there...
     
  32. junglemason

    junglemason

    Joined:
    Dec 30, 2010
    Posts:
    69
    I've changed which primitives I'm using for the meshes to start with, and that has changed the results of this script. Now it only turns part of the meshes inside out. That's progress. I'll have to keep playing. Again, not saying this script is wrong, I'm sure there is something wrong with the primitive meshes I am starting with - I just don't know what...
     
  33. diekeure

    diekeure

    Joined:
    Jan 25, 2013
    Posts:
    221
    ZTORION and Krishx007 like this.
  34. GlennKo

    GlennKo

    Joined:
    Oct 27, 2016
    Posts:
    4
    Also, I think modifying the line to :

    Code (csharp):
    1. combine[i].transform = transform.worldToLocalMatrix * meshFilters[i].transform.localToWorldMatrix;
    will help if you're parenting the holder-to-combine object somewhere within the hierachy which is "non-standard".


    Also sometimes, need to use 'mesh' instead of 'sharedMesh'?
     
  35. oLDo

    oLDo

    Joined:
    Mar 14, 2017
    Posts:
    55
    Hi.

    It's possible to use CombineMeshes on meshes created just in memory, without adding them as GameObjects to scene?

    Code (csharp):
    1. List<Mesh> meshes = new List<Mesh>();
    2. //some iterations adding to this list
    3.  
    4. List<CombineInstance> ci = new List<CombineInstance>();
    5. foreach (var m in meshes)
    6.   ci.Add(new CombineInstance() { mesh = m });
    7.  
    8. var g = new GameObject("combined");
    9. g.AddComponent<MeshFilter>().mesh = new Mesh();
    10. g.GetComponent<MeshFilter>().mesh.CombineMeshes(ci.ToArray());
    11. g.AddComponent<MeshRenderer>().sharedMaterial = material;
    12.  

    It's not working. Do I'm right or is impossible?
     
  36. oLDo

    oLDo

    Joined:
    Mar 14, 2017
    Posts:
    55
    Okay, I found the issue. While I'm defining CombineInstance without matrix, then I need call CombineMeshes(arr, true, false); The third argument "useMatrices" must be false. And it's working also with meshes array only without GameObjects.
     
    lGillot likes this.
  37. LumoKvin

    LumoKvin

    Joined:
    Sep 28, 2019
    Posts:
    195
  38. HeyBishop

    HeyBishop

    Joined:
    Jun 22, 2017
    Posts:
    238
    I'm in the same boat.

    I modified it slightly, following what @MattMirrorfish did in his video here:



    Code (CSharp):
    1.  private void CombineMeshesOfChildren(GameObject parentObject)
    2.     {
    3.         Vector3 position = parentObject.transform.position;
    4.         parentObject.transform.position = Vector3.zero;
    5.  
    6.         MeshFilter[] meshFilters = parentObject.GetComponentsInChildren<MeshFilter>();
    7.         CombineInstance[] combine = new CombineInstance[meshFilters.Length];
    8.  
    9.         int i = 0;
    10.  
    11.         while (i < meshFilters.Length)
    12.         {
    13.             combine[i].mesh = meshFilters[i].sharedMesh;
    14.             combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
    15.             meshFilters[i].gameObject.SetActive(false);
    16.  
    17.             i++;
    18.         }
    19.         parentObject.transform.GetComponent<MeshFilter>().mesh = new Mesh();
    20.         parentObject.transform.GetComponent<MeshFilter>().mesh.CombineMeshes(combine, true, true);
    21.         parentObject.transform.gameObject.SetActive(true);
    22.  
    23.         parentObject.transform.position = position;
    24.  
    25.  
    26.     }
     
  39. feanie

    feanie

    Joined:
    May 1, 2016
    Posts:
    1
    Thank you HeyBishop, the code solved it perfectly out of the box!
     
    IndieFist and HeyBishop like this.
  40. IndieFist

    IndieFist

    Joined:
    Jul 18, 2013
    Posts:
    524
    Thanks for sharing, also i have been see your video.
    But when i try to get this working continue dissappears all my mesh.
    Should i create an empty gameobject with this method, and all children the one that be combined?
    I tested with books, plants, or walls, but simple dissappears all.
     
  41. ekyah411

    ekyah411

    Joined:
    Aug 12, 2019
    Posts:
    6
    My problem was that the when importing the model, Read/Write was not enabled, and that caused the combined mesh to disappear. Have you tried that?
     
    pavan3k and IndieFist like this.
  42. IndieFist

    IndieFist

    Joined:
    Jul 18, 2013
    Posts:
    524
    Thank you, let me try it and test it.