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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

CombineMesh with submeshes

Discussion in 'Scripting' started by Tempest, Jan 16, 2010.

  1. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    Unfortunately, the script in the documentation comes with this comment:
    So, I've looked after the multiple materials, but my problem is how do I combine meshes that have submeshes. I have two meshes which have identical submesh indexes and the renderers use the same materials. I want to combine these two meshes into one mesh, but keep the submeshes.

    Thanks.
     
  2. andeeeee

    andeeeee

    Joined:
    Jul 19, 2005
    Posts:
    8,768
    I don't think there is any direct way in the API to combine pairs of submeshes with the same indices. You'll probably have to combine each of the pairs separately.
     
  3. kingdruid

    kingdruid

    Joined:
    Jun 14, 2008
    Posts:
    64
    Does anyone have a script to do this? I've tried a few, but most of them don't work with submeshes
     
    WikiMalik likes this.
  4. WikiMalik

    WikiMalik

    Joined:
    Nov 13, 2014
    Posts:
    16
    Had any luck? Cause that's where I'm standing now :/
     
  5. Programmer_RM

    Programmer_RM

    Joined:
    Nov 6, 2015
    Posts:
    6
    I've got something that works, but the resulting mesh gets the total vertexcount times the amount of submeshes. So for example:
    A cylinder is made of 3 submeshes: A disk (v=25, t=30), another disk (v=25, t=30) and the body (v=50, t=40).
    I want to combine 2 cylinders into one mesh (let's call it duo-cylinder), while keeping the mesh structure (2 disks, 2 disks , 2 bodies) of 3 submeshes.
    I start by combining paired submeshes into 3 meshes (with mergsubmeshes=true), so I'll get:
    duo-disk (v=200, t=60), another duo-disk (v=200, t=60), duo-cylinder body (v=200, t=80).
    Then I combine the 3 meshes (with mergsubmeshes=false) into the duo-cylinder. The original cylinder (v=100, t=100) had 100 vertices and 100 triangles and I want the duo-cylinder to have 200 vertices and 200 triangles. Instead what I actually get is one with 600 vertices and 200 triangles. Removing these tripled vertices is possible, but I've not yet succeeded. My first attempt created a for-loop that had to execute 25.000.000 times. Of course the mesh had 8 times the amount of vertices as in this example, but it should be possible within O(vertexcount) steps instead of my O(vertexcount^2).
     
  6. Programmer_RM

    Programmer_RM

    Joined:
    Nov 6, 2015
    Posts:
    6
    This is part of the code I mentioned:
    Code (CSharp):
    1. CombineInstance[] ci = getCombineInstances(go); //get all meshes to combine
    2.                 CombineInstance[] comb = new CombineInstance[0]; //all instances in this array will become a submesh in the final mesh
    3.                 int extracted = 0;
    4.                 for(int i = 0;  extracted < ci.Length; i++)
    5.                 {
    6.                     CombineInstance[] extractedsubmesh = extract(ci,i); //get a sublist of all instances with the same submeshindex
    7.                     extracted += extractedsubmesh.Length; //keep track how many instances are extracted
    8.                     Mesh subMesh = new Mesh();
    9.                     subMesh.Clear(); //probably don't need this
    10.                     subMesh.CombineMeshes(extractedsubmesh,true,true); //turn the submeshes into one mesh
    11.                     CombineInstance[] subInstance = new CombineInstance[1];
    12.                     subInstance[0].mesh = subMesh; //use the newly created mesh later as a submesh of the complete mesh
    13.                     subInstance[0].subMeshIndex = 0; //submesh shouldn't have other submeshes at this point
    14.                     comb = merge(comb, subInstance); //add a new combineInstance to comb
    15.                 }
    16.                 combined.CombineMeshes(comb, false, false);
    These are the functions used in above code:
    Code (CSharp):
    1. public static CombineInstance[] extract(CombineInstance[] ci, int subMeshindex)
    2.     {
    3.         List<CombineInstance> subMeshes = new List<CombineInstance>();
    4.         for (int i = 0; i < ci.Length; i++)
    5.         {
    6.             if (ci[i].subMeshIndex == subMeshindex)
    7.             {
    8.                 subMeshes.Add(ci[i]);
    9.             }
    10.         }
    11.         return subMeshes.ToArray();
    12.     }
    13.  
    14.     public static CombineInstance[] getCombineInstances(GameObject go)
    15.     {
    16.         if (go != null)
    17.         {
    18.             MeshFilter mf = go.GetComponent<MeshFilter>();
    19.             Mesh mesh = new Mesh();
    20.             mesh.Clear();
    21.             if (mf != null)
    22.             {
    23.                 mesh = mf.mesh;
    24.             }
    25.             CombineInstance[] ci = new CombineInstance[mesh.subMeshCount];
    26.             for(int i = 0; i < mesh.subMeshCount; i++)
    27.             {
    28.                 ci[i].mesh = mesh;
    29.                 ci[i].subMeshIndex = i;
    30.                 ci[i].transform = go.transform.localToWorldMatrix;
    31.             }    
    32.              
    33.             for (int i = 0; i < go.transform.childCount; i++)
    34.             {
    35.                 ci = merge(ci,getCombineInstances(go.transform.GetChild(i).gameObject));
    36.             }
    37.             return ci;
    38.         }
    39.         return new CombineInstance[0];
    40.     }
    41.  
    42.     public static CombineInstance[] merge(CombineInstance[] ci1, CombineInstance[] ci2)
    43.     {
    44.         CombineInstance[] ci = new CombineInstance[ci1.Length + ci2.Length];
    45.         for (int i = 0; i < ci1.Length; i++)
    46.         {
    47.             ci[i] = ci1[i];
    48.         }
    49.         for (int i = 0; i < ci2.Length; i++)
    50.         {
    51.             ci[i + ci1.Length] = ci2[i];
    52.         }
    53.  
    54.         return ci;
    55.     }
     
  7. Programmer_RM

    Programmer_RM

    Joined:
    Nov 6, 2015
    Posts:
    6
    I wrote this static class over the weekend and am quite happy with the results so far. I got the functionality I wanted without the vertex overhead:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public static class MeshCombiner{
    6.  
    7.     //takes an array of meshes and combines them into one mesh
    8.     //submeshes within the same mesh will never merge,
    9.     //but submeshes from different meshes with the same submesh-index can be merged by setting merge to true
    10.     static public Mesh combinedMeshes(Mesh[] meshes, bool merge)
    11.     {
    12.         if (merge)
    13.         {
    14.             return mergedMeshes(meshes);
    15.         }
    16.         else
    17.         {
    18.             return concatenatedMeshes(meshes);
    19.         }
    20.     }
    21.  
    22.     //takes an array of meshes and combines them into one mesh
    23.     //the given array transforms will be used to transform the first transforms.Length meshes
    24.     //submeshes within the same mesh will never merge,
    25.     //but submeshes from different meshes with the same submesh-index can be merged by setting merge to true
    26.     static public Mesh combinedMeshes(Mesh[] meshes, Matrix4x4[] transforms, bool merge)
    27.     {
    28.         Mesh[] transmeshes = new Mesh[meshes.Length];
    29.         for (int i = 0; i < meshes.Length && i < transforms.Length; i++)
    30.         {
    31.             transmeshes[i] = transformedMesh(meshes[i], transforms[i]);
    32.         }
    33.         return combinedMeshes(transmeshes, merge);
    34.     }
    35.  
    36.     //returns a mesh with all meshes combined, while keeping all submeshes seperate
    37.     //the order of the meshes is determined by the order in which they are put in the array
    38.     static public Mesh concatenatedMeshes(Mesh[] meshes)
    39.     {
    40.         Mesh merged = new Mesh();
    41.         CombineInstance[] ci = new CombineInstance[meshes.Length];
    42.         int submeshcount = 0;
    43.         for (int i = 0; i < meshes.Length; i++)
    44.         {
    45.             submeshcount = Mathf.Max(meshes[i].subMeshCount, submeshcount);
    46.             ci[i].mesh = meshes[i];
    47.             ci[i].subMeshIndex = 0;
    48.         }
    49.         merged.CombineMeshes(ci, true, false); //let's assume that the vertices are in order
    50.         merged.triangles = null;
    51.         merged.subMeshCount = 0;
    52.         int offset = 0;
    53.         for(int i = 0; i < meshes.Length; i++)
    54.         {
    55.             for (int j = 0; j < meshes[i].subMeshCount; j++)
    56.             {
    57.                 int[] t = meshes[i].GetTriangles(j);
    58.                 for (int k = 0; k < t.Length; k++)
    59.                 {
    60.                     t[k] += offset; //make sure that every triangle points to the right points
    61.                 }
    62.                 merged.subMeshCount++;
    63.                 merged.SetTriangles(t, merged.subMeshCount - 1);
    64.             }
    65.             offset += meshes[i].vertexCount; //assuming that the vertices are in order, this should tell where they start for each mesh
    66.         }
    67.         return merged;
    68.     }
    69.  
    70.     //returns a mesh with all meshes combined by merging submeshes with the same submesh index
    71.     //the index of all submeshes will remain the same
    72.     static public Mesh mergedMeshes(Mesh[] meshes)
    73.     {
    74.         Mesh merged = new Mesh();
    75.         CombineInstance[] ci = new CombineInstance[meshes.Length];
    76.         int submeshcount = 0;
    77.         for (int i = 0; i < meshes.Length; i++)
    78.         {
    79.             submeshcount = Mathf.Max(meshes[i].subMeshCount, submeshcount);
    80.             ci[i].mesh = meshes[i];
    81.             ci[i].subMeshIndex = 0;
    82.         }
    83.         merged.CombineMeshes(ci, true, false); //let's assume that the vertices are in order
    84.         merged.subMeshCount = submeshcount;
    85.         for (int i = 1; i < submeshcount; i++) //submesh index 0 is already merged
    86.         {
    87.             List<int> submesh = new List<int>();
    88.             int offset = 0;
    89.             for (int j = 0; j < meshes.Length; j++)
    90.             {
    91.                 if (i < meshes[j].subMeshCount)
    92.                 {
    93.                     int[] t = meshes[j].GetTriangles(i);
    94.                     for (int k = 0; k < t.Length; k++)
    95.                     {
    96.                         t[k] += offset; //make sure that every triangle points to the righ points
    97.                     }
    98.                     submesh.AddRange(t);
    99.                 }
    100.                 offset += meshes[j].vertexCount; //assuming that the vertices are in order, this should tell where they start for each mesh
    101.             }
    102.             merged.SetTriangles(submesh, i);
    103.         }
    104.         return merged;
    105.     }
    106.  
    107.     //returns the same mesh with all submeshes merged into one submesh
    108.     static public Mesh mergedMesh(Mesh mesh)
    109.     {
    110.         Mesh merged = Object.Instantiate<Mesh>(mesh);
    111.         int[] t = merged.triangles;
    112.         merged.triangles = null;
    113.         merged.subMeshCount = 1;
    114.         merged.triangles = t;
    115.         return merged;
    116.     }
    117.  
    118.     //returns the same mesh with all vertex data transformed
    119.     static public Mesh transformedMesh(Mesh mesh, Matrix4x4 M)
    120.     {
    121.         CombineInstance[] ci = new CombineInstance[1];
    122.         ci[0].mesh = mesh;
    123.         ci[0].subMeshIndex = 0;
    124.         ci[0].transform = M;
    125.  
    126.         Mesh transformed = new Mesh();
    127.         transformed.CombineMeshes(ci, false, true);
    128.         transformed.subMeshCount = mesh.subMeshCount;
    129.         for (int i = 1; i < mesh.subMeshCount; i++)
    130.         {
    131.             transformed.SetTriangles(mesh.GetTriangles(i),i);
    132.         }
    133.         return transformed;
    134.     }
    135.  
    136. }
    137.  
     
    RHShanks likes this.
  8. Programmer_RM

    Programmer_RM

    Joined:
    Nov 6, 2015
    Posts:
    6
    Class Summary: CombineMeshes is a static class that can be used to combine Meshes instead of CombineInstances, without multiplying the vertexCount! The class contains 6 functions of which one is an overload. Note that mergedMeshes and mergedMesh are completely different functions. The function transformedMesh would probably more suitable in some other class, but since it's used by some of the other functions, I included it.

    Function Summary:

    public static Mesh transformedMesh(Mesh mesh, Matrix4x4 M): returns a copy of the given mesh, but with all vertex data transformed using M

    public static Mesh mergedMesh(Mesh mesh): returns a copy of the given mesh, but with all subMeshes merged into one

    public static Mesh mergedMeshes(Mesh[] meshes): returns a mesh for which all meshes are combined by merging their submeshes based on their submesh-index

    public static Mesh concatenatedMeshes(Mesh[] meshes): returns a mesh for which all meshes are combined by concatenating them

    public static Mesh combinedMeshes(Mesh[] meshes, bool merge): returns a mesh for which all meshes are first transformed and then combined by either concatenatedMeshes (merge = false) or mergedMeshes (merge = true)

    public static Mesh combinedMeshes(Mesh[] meshes, Matrix4x4[] transforms, bool merge): returns a mesh for which all meshes are first transformed and then combined by either concatenatedMeshes (merge = false) or mergedMeshes (merge = true)
     
    andywatts and RHShanks like this.