Advertisement
marcb152

Mesh Combiner for Unity

Jun 6th, 2020
294
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 15.27 KB | None | 0 0
  1. //This script is free to use, but the hexagon combine type doesn't work yet
  2. //This script will not work without the editor script
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5.  
  6. public class MeshMerge : MonoBehaviour
  7. {
  8.     [SerializeField]
  9.     private Material material;
  10.     [SerializeField]
  11.     private CombineType combineType = CombineType.Simple;
  12.     [SerializeField]
  13.     private SubMeshesType subMeshesType = SubMeshesType.MergeSubmeshesTogether;
  14.     [SerializeField]
  15.     [Tooltip("This bool allows you to combine meshes up to 4 billions of vertices instead of the default 65 536 vertices, but it isn't supported on all the platforms")]
  16.     private bool use32BitsBuffer;
  17.     [SerializeField]
  18.     private Mesh mesh;
  19.     [SerializeField]
  20.     private Gradient colors;
  21.     [SerializeField]
  22.     private int range;
  23.     [SerializeField]
  24.     private Vector3 gridOffset;
  25.     [SerializeField]
  26.     private Vector2 gridMaxSize;
  27.     [SerializeField]
  28.     private List<CellData> groupedItems = new List<CellData>();
  29.  
  30.     [SerializeField]
  31.     private Texture2D atlasTexture;
  32.     [SerializeField]
  33.     private string path;
  34.     [SerializeField]
  35.     private Material newMat;
  36.  
  37.     public List<GameObject> items = new List<GameObject>();
  38.  
  39.     public void CombineMeshes()
  40.     {
  41.         Quaternion oldRot = transform.rotation;
  42.         Vector3 oldPos = transform.position;
  43.         Vector3 oldScale = transform.localScale;
  44.  
  45.         List<MeshFilter> filters = new List<MeshFilter>();
  46.         List<CombineInstance> combiners = new List<CombineInstance>();
  47.  
  48.         SetGroupedList();
  49.         GroupItems();
  50.         foreach (CellData data in groupedItems)
  51.         {
  52.             uint totalVertexCount = 0;
  53.             Mesh finalMesh;
  54.             if (use32BitsBuffer) finalMesh = new Mesh{ indexFormat = UnityEngine.Rendering.IndexFormat.UInt32 };
  55.             else finalMesh = new Mesh();
  56.             filters.Clear();
  57.             combiners.Clear();
  58.  
  59.             foreach (GameObject mf in data.cellItems)
  60.                 filters.Add(mf.GetComponent<MeshFilter>());
  61.             print(name + " is combining " + filters.Count + " meshes ! ID : " + data.ID);
  62.  
  63.             for (int a = 0; a < filters.Count; a++)
  64.             {
  65.                 totalVertexCount += (uint)filters[a].sharedMesh.vertexCount;
  66.                 if (!use32BitsBuffer && totalVertexCount >= 65536)
  67.                 {
  68.                     Debug.LogError("Can't combine meshes with more than 65 536 vertices ! (" + totalVertexCount + ")", gameObject);
  69.                     return;
  70.                 }
  71.                 if (use32BitsBuffer && totalVertexCount >= 4000000000)
  72.                 {
  73.                     Debug.LogError("Can't combine meshes with more than 4 000 000 000 vertices ! (" + totalVertexCount + ")", gameObject);
  74.                     return;
  75.                 }
  76.                 if (filters[a].transform == transform)
  77.                     continue;
  78.                 if (subMeshesType == SubMeshesType.UseOnlyTheFirst)
  79.                 {
  80.                     CombineInstance subComb = new CombineInstance();
  81.                     subComb.subMeshIndex = 0;
  82.                     subComb.mesh = filters[a].sharedMesh;
  83.                     subComb.transform = filters[a].transform.localToWorldMatrix;
  84.                     combiners.Add(subComb);
  85.                 }
  86.                 else
  87.                 {
  88.                     for (int b = 0; b < filters[a].sharedMesh.subMeshCount; b++)
  89.                     {
  90.                         CombineInstance subComb = new CombineInstance();
  91.                         subComb.subMeshIndex = b;
  92.                         subComb.mesh = filters[a].sharedMesh;
  93.                         subComb.transform = filters[a].transform.localToWorldMatrix;
  94.                         combiners.Add(subComb);
  95.                     }
  96.                 }
  97.             }
  98.             finalMesh.CombineMeshes(combiners.ToArray(), subMeshesType == SubMeshesType.MergeSubmeshesTogether || subMeshesType == SubMeshesType.UseOnlyTheFirst ? true : false);
  99.             GameObject truc = new GameObject("Cell_" + data.ID);
  100.             truc.AddComponent<MeshFilter>().sharedMesh = finalMesh;
  101.             truc.AddComponent<MeshRenderer>().material = material;
  102.             truc.transform.position = oldPos;
  103.             truc.transform.rotation = oldRot;
  104.             truc.transform.localScale = oldScale;
  105.             foreach (GameObject item in data.cellItems)
  106.                 item.SetActive(false);
  107.         }
  108.     }
  109.     public void Undo()
  110.     {
  111.         for (int a = 0; a < items.Count; a++)
  112.             items[a].SetActive(true);
  113.     }
  114.     void OnDrawGizmosSelected()
  115.     {
  116.         if (combineType != CombineType.Simple && range > 5)
  117.         {
  118.             bool color = false;
  119.             for (int i = 0; i < gridMaxSize.x; i += range)
  120.             {
  121.                 for (int o = 0; o < gridMaxSize.y; o += range)
  122.                 {
  123.                     Gizmos.color = colors.Evaluate((color = !color) ? 0 : 1);
  124.                     if (combineType == CombineType.Cube)
  125.                         Gizmos.DrawWireCube(Vector3.zero + gridOffset + new Vector3(i, 0, o), new Vector3(range, range / 5, range));
  126.                     if (combineType == CombineType.Hex)
  127.                     {
  128.                         //Even
  129.                         if ((o / range) % 2 == 0)
  130.                             Gizmos.DrawMesh(mesh, gridOffset + new Vector3(i + gridOffset.z, 0, o * 0.866f + 0.77f), Quaternion.Euler(-90, 0, 0), Vector3.one * range * 0.962f);
  131.                         //Odd
  132.                         else
  133.                             Gizmos.DrawMesh(mesh, gridOffset + new Vector3(i, 0, o * 0.866f + 0.77f), Quaternion.Euler(-90, 0, 0), Vector3.one * range * 0.962f);
  134.                     }
  135.                 }
  136.             }
  137.         }
  138.     }
  139.     public void CalculatePerfectCellSize()
  140.     {
  141.         SetGroupedList();
  142.         GroupItems();
  143.         uint totalVertexCount = 0;
  144.         foreach (GameObject go in groupedItems[0].cellItems)
  145.             totalVertexCount += (uint)go.GetComponent<MeshFilter>().sharedMesh.vertexCount;
  146.         if (!use32BitsBuffer)
  147.         {
  148.             float bestSize = Mathf.RoundToInt(64000f / totalVertexCount * range) - 1;
  149.             if (totalVertexCount >= 65536)
  150.                 Debug.Log("The first cell has more than 65 536 vertices (" + totalVertexCount + "), please lower the size to something like : " + bestSize, gameObject);
  151.             if (totalVertexCount < 65536)
  152.                 Debug.Log("The first cell has less than 65 536 vertices (" + totalVertexCount + "), the best size will probably be : " + bestSize, gameObject);
  153.         }
  154.         else
  155.         {
  156.             float bestSize = Mathf.RoundToInt(4000000000f / totalVertexCount * range) - 1;
  157.             if (totalVertexCount >= 4000000000)
  158.                 Debug.Log("The first cell has more than 4 000 000 000 vertices (" + totalVertexCount + "), please lower the size to something like : " + bestSize, gameObject);
  159.             if (totalVertexCount < 4000000000)
  160.                 Debug.Log("The first cell has less than 4 000 000 000 vertices (" + totalVertexCount + "), the best size will probably be : " + bestSize, gameObject);
  161.         }
  162.     }
  163.     public void SetGroupedList()
  164.     {
  165.         if (combineType != CombineType.Simple)
  166.         {
  167.             groupedItems.Clear();
  168.             int cellsNbr = 0;
  169.             for (int i = 0; i < gridMaxSize.x; i += range)
  170.             {
  171.                 for (int o = 0; o < gridMaxSize.y; o += range)
  172.                 {
  173.                     groupedItems.Add(new CellData(
  174.                         cellsNbr,
  175.                         new Vector2(i, o)
  176.                         ));
  177.                     cellsNbr++;
  178.                 }
  179.             }
  180.         }
  181.         else
  182.         {
  183.             groupedItems.Clear();
  184.             groupedItems.Add(new CellData(
  185.                         0,
  186.                         new Vector2(0, 0)
  187.                         ));
  188.         }
  189.     }
  190.     public void GroupItems()
  191.     {
  192.         for (int i = 0; i < groupedItems.Count; i++)
  193.         {
  194.             foreach (GameObject item in items)
  195.             {
  196.                 if (combineType == CombineType.Cube && item.transform.position.x <= groupedItems[i].cellOrigin.x + range && item.transform.position.x >= groupedItems[i].cellOrigin.x && item.transform.position.z <= groupedItems[i].cellOrigin.y + range && item.transform.position.z >= groupedItems[i].cellOrigin.y)
  197.                     groupedItems[i].cellItems.Add(item);
  198.                 else if (combineType == CombineType.Simple)
  199.                     groupedItems[i].cellItems.Add(item);
  200.             }
  201.         }
  202.     }
  203.     public void AtlasUVS()
  204.     {
  205.         //if (subMeshesType == SubMeshesType.)
  206.         List<MeshFilter> meshFilters = new List<MeshFilter>();
  207.         for (int a = 0; a < items.Count; a++)
  208.         {
  209.             if (items[a].GetComponent<MeshFilter>())
  210.                 meshFilters.Add(items[a].GetComponent<MeshFilter>());
  211.             else
  212.                 Debug.LogError("Item " + a + " in the list doesn't have any MeshFilter on it. Remove it or add to it a MeshFilter");
  213.         }
  214.  
  215.         List<Texture2D> textures = new List<Texture2D>();
  216.         for (int a = 0; a < items.Count; a++)
  217.         {
  218.             if (items[a].GetComponent<MeshRenderer>() && items[a].GetComponent<MeshRenderer>().sharedMaterial)
  219.                 textures.Add(DuplicateTextureWithMat(items[a].GetComponent<MeshRenderer>().sharedMaterial));
  220.             else
  221.                 Debug.LogError("Item " + a + " in the list doesn't have any MeshRenderer on it. Remove it or add to it a MeshRenderer");
  222.         }
  223.  
  224.         Rect[] uvRectChangesFromTextureAtlas = atlasTexture.PackTextures(textures.ToArray(), 0, 8192);
  225.  
  226.         for (var i = 0; i < uvRectChangesFromTextureAtlas.Length; i++)
  227.         {
  228.             Quaternion oldRot = items[i].transform.rotation;
  229.             Vector3 oldPos = items[i].transform.position;
  230.             Vector3 oldScale = items[i].transform.localScale;
  231.  
  232.             Rect rect = uvRectChangesFromTextureAtlas[i];
  233.  
  234.             Mesh atlasedMesh = CopyMesh(meshFilters[i].sharedMesh);
  235.             Vector2[] remappedUVs = atlasedMesh.uv;
  236.  
  237.             for (var j = 0; j < remappedUVs.Length; j++)
  238.             {
  239.                 var uv = remappedUVs[j];
  240.  
  241.                 uv.x = rect.x + (uv.x * rect.width);
  242.                 uv.y = rect.y + (uv.y * rect.height);
  243.  
  244.                 remappedUVs[j] = uv;
  245.             }
  246.  
  247.             atlasedMesh.uv = remappedUVs;
  248.  
  249.             GameObject truc = new GameObject(items[i].name + "_Atlased");
  250.             truc.transform.rotation = oldRot;
  251.             truc.transform.position = oldPos;
  252.             truc.transform.localScale = oldScale;
  253.             items[i].SetActive(false);
  254.             truc.AddComponent<MeshFilter>().sharedMesh = atlasedMesh;
  255.             newMat.mainTexture = atlasTexture;
  256.             truc.AddComponent<MeshRenderer>().material = newMat;
  257.         }
  258.         if (!string.IsNullOrEmpty(path))
  259.             System.IO.File.WriteAllBytes(path + "/" + atlasTexture.name + ".png", atlasTexture.EncodeToPNG());
  260.     }
  261.     private Mesh CopyMesh (Mesh mesh)
  262.     {
  263.         return Instantiate(mesh);
  264.     }
  265.     public Texture2D DuplicateTextureWithMat(Material mat)
  266.     {
  267.         Texture2D source = new Texture2D(1, 1);
  268.         if (mat.mainTexture)
  269.             source = (Texture2D)mat.mainTexture;
  270.         else
  271.             source.SetPixel(0, 0, mat.color);
  272.         RenderTexture renderTex = RenderTexture.GetTemporary(
  273.                     source.width,
  274.                     source.height,
  275.                     0,
  276.                     RenderTextureFormat.Default,
  277.                     RenderTextureReadWrite.Linear);
  278.  
  279.         Graphics.Blit(source, renderTex);
  280.         RenderTexture previous = RenderTexture.active;
  281.         RenderTexture.active = renderTex;
  282.         Texture2D readableText = new Texture2D(source.width, source.height);
  283.         readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
  284.         readableText.Apply();
  285.         RenderTexture.active = previous;
  286.         RenderTexture.ReleaseTemporary(renderTex);
  287.         return readableText;
  288.     }
  289.     public void AtlasAndCombine()
  290.     {
  291.         Quaternion oldRot = transform.rotation;
  292.         Vector3 oldPos = transform.position;
  293.         Vector3 oldScale = transform.localScale;
  294.  
  295.         List<MeshFilter> filters = new List<MeshFilter>();
  296.         List<CombineInstance> combiners = new List<CombineInstance>();
  297.  
  298.         int totalVertexCount = 0;
  299.         Mesh finalMesh = new Mesh();
  300.  
  301.         List<Texture2D> textures = new List<Texture2D>();
  302.  
  303.         foreach (GameObject mf in items)
  304.         {
  305.             filters.Add(mf.GetComponent<MeshFilter>());
  306.             for (int b = 0; b < mf.GetComponent<MeshFilter>().sharedMesh.subMeshCount; b++)
  307.                 textures.Add(DuplicateTextureWithMat(mf.GetComponent<MeshRenderer>().sharedMaterials[b]));
  308.         }
  309.         Rect[] uvRectChangesFromTextureAtlas = atlasTexture.PackTextures(textures.ToArray(), 0, 8192);
  310.         print(name + " is combining " + filters.Count + " meshes !");
  311.  
  312.         for (int a = 0; a < filters.Count; a++)
  313.         {
  314.             totalVertexCount += filters[a].sharedMesh.vertexCount;
  315.             if (totalVertexCount >= 65536)
  316.             {
  317.                 Debug.LogError("Can't combine meshes with more than 65 536 vertices ! (" + totalVertexCount + ")", gameObject);
  318.                 return;
  319.             }
  320.             if (filters[a].transform == transform)
  321.                 continue;
  322.             for (int b = 0; b < filters[a].sharedMesh.subMeshCount; b++)
  323.             {
  324.                 Rect rect = uvRectChangesFromTextureAtlas[a + b];
  325.                 Vector2[] remappedUVs = CopyMesh(filters[a].sharedMesh).uv;
  326.                 HashSet<int> tris = new HashSet<int>(filters[a].sharedMesh.GetTriangles(b));
  327.  
  328.                 for (var j = 0; j < remappedUVs.Length; j++)
  329.                 {
  330.                     var uv = remappedUVs[j];
  331.  
  332.                     uv.x = rect.x + (uv.x * rect.width);
  333.                     uv.y = rect.y + (uv.y * rect.height);
  334.  
  335.                     remappedUVs[j] = uv;
  336.                 }
  337.                 CombineInstance subComb = new CombineInstance();
  338.                 subComb.subMeshIndex = b;
  339.                 subComb.mesh = CopyMesh(filters[a].sharedMesh);
  340.                 subComb.mesh.uv = remappedUVs;
  341.                 subComb.transform = filters[a].transform.localToWorldMatrix;
  342.                 combiners.Add(subComb);
  343.             }
  344.         }
  345.         finalMesh.CombineMeshes(combiners.ToArray());
  346.         GameObject truc = new GameObject("Cell_Atlased");
  347.         truc.AddComponent<MeshFilter>().sharedMesh = finalMesh;
  348.         newMat.mainTexture = atlasTexture;
  349.         truc.AddComponent<MeshRenderer>().material = newMat;
  350.         truc.transform.position = oldPos;
  351.         truc.transform.rotation = oldRot;
  352.         truc.transform.localScale = oldScale;
  353.         if (!string.IsNullOrEmpty(path))
  354.             System.IO.File.WriteAllBytes(path + "/" + atlasTexture.name + ".png", atlasTexture.EncodeToPNG());
  355.         foreach (GameObject item in items)
  356.             item.SetActive(false);
  357.     }
  358. }
  359. public enum CombineType
  360. {
  361.     Cube = 0,
  362.     Hex = 1,
  363.     Simple = 2
  364. }
  365. public enum SubMeshesType
  366. {
  367.     MergeSubmeshesTogether = 0,
  368.     DontMergeThemTogether = 1,
  369.     UseOnlyTheFirst = 2
  370. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement