using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using System.Linq; public class MeshMerger : MonoBehaviour { [SerializeField] private Transform[] _mergeObjects; [SerializeField] private Transform _meshPivot; [Header("Save properties")] [SerializeField] private string _objectName = "MergedObject"; #if(UNITY_EDITOR) [SerializeField] private bool _saveMesh; [SerializeField] private string _savePath = "Assets/SavedMeshes"; private MeshSaver _meshSaver = new MeshSaver(); #endif public void Merge() { Transform[] _meshesTransforms = GetTargetTranforms(_mergeObjects).Distinct().ToArray(); Mesh[] meshes = new Mesh[_meshesTransforms.Length]; meshes = _meshesTransforms.Select(transform => transform.GetComponent().sharedMesh).ToArray(); Vector3[] vertices = new Vector3[0]; int[] triangles = new int[0]; Vector2[] uv = new Vector2[0]; Vector4[] tangents = new Vector4[0]; Vector3[] normals = new Vector3[0]; List subMeshesInfo = new List(); for (int meshIndex = 0; meshIndex < meshes.Length; meshIndex++) { Mesh currentMesh = meshes[meshIndex]; Transform currentTransform = _meshesTransforms[meshIndex]; Material[] meshMaterials = _meshesTransforms[meshIndex].GetComponent().sharedMaterials; Vector3 meshLocalPosition = currentTransform.position - _meshPivot.position; //Vector3 globalScale = Vector3.Scale(currentTransform.localScale, // currentTransform.parent == null ? Vector3.one : GetGlobalScale(currentTransform.parent)); Vector3 globalScale = GetGlobalScale(currentTransform); int subMeshCount = currentMesh.subMeshCount; SubMeshDescriptor[] subMeshDescriptors = new SubMeshDescriptor[subMeshCount]; for (int i = 0; i < subMeshCount; i++) subMeshDescriptors[i] = currentMesh.GetSubMesh(i); for (int i = 0; i < meshMaterials.Length; i++) { List addedVertices = currentMesh.vertices .ToList() .GetRange(subMeshDescriptors[i].firstVertex, subMeshDescriptors[i].vertexCount) .Select(verticle => RotateAndScaleVerticle(verticle, currentTransform.rotation, globalScale, Vector3.zero) + meshLocalPosition) .ToList(); List addedUV = currentMesh.uv .ToList() .GetRange(subMeshDescriptors[i].firstVertex, subMeshDescriptors[i].vertexCount); List addedTangents = currentMesh.tangents .ToList() .GetRange(subMeshDescriptors[i].firstVertex, subMeshDescriptors[i].vertexCount) .Select(tangent => { Vector4 result = RotateAndScaleVerticle(tangent, currentTransform.rotation, Vector3.one, Vector3.zero); result.w = tangent.w; return result; }) .ToList(); List addedNormals = currentMesh.normals .ToList() .GetRange(subMeshDescriptors[i].firstVertex, subMeshDescriptors[i].vertexCount) .Select(normal => RotateAndScaleVerticle(normal, currentTransform.rotation, Vector3.one, Vector3.zero)) .ToList(); List addedTriangleIndexes = currentMesh.triangles .ToList() .GetRange(subMeshDescriptors[i].indexStart, subMeshDescriptors[i].indexCount) .Select(index => index - subMeshDescriptors[i].firstVertex) .ToList(); int subMeshInfoIndex = subMeshesInfo .FindIndex(subMeshInfo => subMeshInfo.VerticesMaterial == meshMaterials[i]); if (subMeshInfoIndex == -1) { subMeshesInfo.Add(new SubMeshInfo { Vertices = addedVertices, UV = addedUV, Tangents = addedTangents, Normals = addedNormals, TriangleIndexes = addedTriangleIndexes, VerticesMaterial = meshMaterials[i] }); } else { subMeshesInfo[subMeshInfoIndex].TriangleIndexes .AddRange(addedTriangleIndexes .Select(index => index + subMeshesInfo[subMeshInfoIndex].Vertices.Count)); subMeshesInfo[subMeshInfoIndex].Vertices.AddRange(addedVertices); subMeshesInfo[subMeshInfoIndex].UV.AddRange(addedUV); subMeshesInfo[subMeshInfoIndex].Tangents.AddRange(addedTangents); subMeshesInfo[subMeshInfoIndex].Normals.AddRange(addedNormals); } } } Material[] materials = new Material[subMeshesInfo.Count]; SubMeshDescriptor[] finalSubMeshDescriptors = new SubMeshDescriptor[subMeshesInfo.Count]; for (int i = 0; i < subMeshesInfo.Count; i++) { materials[i] = subMeshesInfo[i].VerticesMaterial; int startVerticleIndex = vertices.Length; int startTrianglesIndex = triangles.Length; triangles = triangles .Concat(subMeshesInfo[i].TriangleIndexes .Select(index => index + vertices.Length)) .ToArray(); vertices = vertices .Concat(subMeshesInfo[i].Vertices) .ToArray(); uv = uv .Concat(subMeshesInfo[i].UV) .ToArray(); tangents = tangents .Concat(subMeshesInfo[i].Tangents) .ToArray(); normals = normals .Concat(subMeshesInfo[i].Normals) .ToArray(); finalSubMeshDescriptors[i] = new SubMeshDescriptor { firstVertex = startVerticleIndex, indexStart = startTrianglesIndex, vertexCount = subMeshesInfo[i].Vertices.Count, indexCount = subMeshesInfo[i].TriangleIndexes.Count, }; } Mesh resultMesh = CreateMesh(vertices, triangles, uv, tangents, normals, finalSubMeshDescriptors); GameObject house = CreateGameObject(resultMesh, materials); #if(UNITY_EDITOR) if (_saveMesh) _meshSaver.SaveMesh(resultMesh, _savePath); #endif } private Vector3 RotateAndScaleVerticle(Vector3 verticle, Quaternion rotation, Vector3 scale, Vector3 pivot) { verticle = Vector3.Scale(verticle, scale); Vector3 pos = rotation * (verticle - pivot) + pivot; return pos; } private Vector3 Abs(Vector3 vector) { return new Vector3(Mathf.Abs(vector.x), Mathf.Abs(vector.y), Mathf.Abs(vector.z)); } //private Vector3 GetGlobalScale(Transform transform) //{ // Vector3 scale = transform.rotation * transform.localScale; // if (transform.parent == null) // return Abs(scale); // else return Vector3.Scale(Abs(scale), GetGlobalScale(transform.parent)); //} private Vector3 GetGlobalScale(Transform transform) { if (transform.parent == null) return transform.localScale; else return Vector3.Scale(transform.localScale, GetGlobalScale(transform.parent)); } private Transform[] GetTargetTranforms(Transform[] transforms) { List result = new List(); for (int i = 0; i < transforms.Length; i++) { if (transforms[i].TryGetComponent(out MeshFilter meshFilter)) result.Add(transforms[i]); Transform[] children = new Transform[transforms[i].childCount]; for (int t = 0; t < transforms[i].childCount; t++) children[t] = transforms[i].GetChild(t); result.AddRange(GetTargetTranforms(children)); } return result.ToArray(); } private Mesh CreateMesh(Vector3[] vertices, int[] triangles, Vector2[] uv, Vector4[] tangents, Vector3[] normals, SubMeshDescriptor[] subMeshDescriptors) { Mesh resultMesh = new Mesh(); resultMesh.vertices = vertices; resultMesh.triangles = triangles; resultMesh.uv = uv; resultMesh.tangents = tangents; resultMesh.normals = normals; resultMesh.name = $"{_objectName}Mesh"; resultMesh.subMeshCount = subMeshDescriptors.Length; for (int i = 0; i < subMeshDescriptors.Length; i++) resultMesh.SetSubMesh(i, subMeshDescriptors[i]); resultMesh.RecalculateBounds(); return resultMesh; } private GameObject CreateGameObject(Mesh mesh, Material[] materials) { GameObject house = new GameObject(_objectName); house.AddComponent().sharedMesh = mesh; house.AddComponent().sharedMaterials = materials; return house; } private struct SubMeshInfo { public List Vertices; public List UV; public List Tangents; public List Normals; public List TriangleIndexes; public Material VerticesMaterial; } }