using UnityEngine; using System.Collections; using System.Linq; using System.Collections.Generic; namespace Utility.Meshes { public static class Subdivion { static List vertices; static List normals; static List colors; static List uv; static List uv1; static List uv2; static List> allIndices; static Dictionary newVectices; static void InitArrays(Mesh mesh) { vertices = new List(mesh.vertices); normals = new List(mesh.normals); colors = new List(mesh.colors); uv = new List(mesh.uv); uv1 = new List(mesh.uv2); uv2 = new List(mesh.uv2); allIndices = new List>(); } static void CleanUp() { vertices = null; normals = null; colors = null; uv = null; uv1 = null; uv2 = null; allIndices = null; } #region Subdivide4 (2x2) static int GetNewVertex4(int i1, int i2) { int newIndex = vertices.Count; uint t1 = ((uint)i1 << 16) | (uint)i2; uint t2 = ((uint)i2 << 16) | (uint)i1; if (newVectices.ContainsKey(t2)) return newVectices[t2]; if (newVectices.ContainsKey(t1)) return newVectices[t1]; newVectices.Add(t1, newIndex); vertices.Add((vertices[i1] + vertices[i2]) * 0.5f); if (normals.Count > 0) normals.Add((normals[i1] + normals[i2]).normalized); if (colors.Count > 0) colors.Add((colors[i1] + colors[i2]) * 0.5f); if (uv.Count > 0) uv.Add((uv[i1] + uv[i2]) * 0.5f); if (uv1.Count > 0) uv1.Add((uv1[i1] + uv1[i2]) * 0.5f); if (uv2.Count > 0) uv2.Add((uv2[i1] + uv2[i2]) * 0.5f); return newIndex; } /// /// Devides each triangles into 4. A quad(2 tris) will be splitted into 2x2 quads( 8 tris ) /// /// public static void Subdivide4(Mesh mesh) { newVectices = new Dictionary(); InitArrays(mesh); int[] triangles = mesh.triangles; Debug.Log(mesh.subMeshCount); for (int j = 0; j < mesh.subMeshCount; j++) { var subMesh = mesh.GetSubMesh(j); allIndices.Add(new List()); List subIndices = allIndices[j]; for (int i = subMesh.indexStart; i < subMesh.indexStart + subMesh.indexCount; i += 3) { int i1 = triangles[i + 0]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; int a = GetNewVertex4(i1, i2); int b = GetNewVertex4(i2, i3); int c = GetNewVertex4(i3, i1); subIndices.Add(i1); subIndices.Add(a); subIndices.Add(c); subIndices.Add(i2); subIndices.Add(b); subIndices.Add(a); subIndices.Add(i3); subIndices.Add(c); subIndices.Add(b); subIndices.Add(a); subIndices.Add(b); subIndices.Add(c); // center triangle } } mesh.vertices = vertices.ToArray(); if (normals.Count > 0) mesh.normals = normals.ToArray(); if (colors.Count > 0) mesh.colors = colors.ToArray(); if (uv.Count > 0) mesh.uv = uv.ToArray(); if (uv1.Count > 0) mesh.uv2 = uv1.ToArray(); if (uv2.Count > 0) mesh.uv2 = uv2.ToArray(); mesh.triangles = allIndices.SelectMany(x => x).ToArray(); mesh.subMeshCount = allIndices.Count; for (int i = 0; i < allIndices.Count; i++) mesh.SetTriangles(allIndices[i], i); CleanUp(); } #endregion Subdivide4 (2x2) #region Subdivide9 (3x3) static int GetNewVertex9(int i1, int i2, int i3) { int newIndex = vertices.Count; // center points don't go into the edge list if (i3 == i1 || i3 == i2) { uint t1 = ((uint)i1 << 16) | (uint)i2; if (newVectices.ContainsKey(t1)) return newVectices[t1]; newVectices.Add(t1, newIndex); } // calculate new vertex vertices.Add((vertices[i1] + vertices[i2] + vertices[i3]) / 3.0f); if (normals.Count > 0) normals.Add((normals[i1] + normals[i2] + normals[i3]).normalized); if (colors.Count > 0) colors.Add((colors[i1] + colors[i2] + colors[i3]) / 3.0f); if (uv.Count > 0) uv.Add((uv[i1] + uv[i2] + uv[i3]) / 3.0f); if (uv1.Count > 0) uv1.Add((uv1[i1] + uv1[i2] + uv1[i3]) / 3.0f); if (uv2.Count > 0) uv2.Add((uv2[i1] + uv2[i2] + uv2[i3]) / 3.0f); return newIndex; } /// /// Devides each triangles into 9. A quad(2 tris) will be splitted into 3x3 quads( 18 tris ) /// /// public static void Subdivide9(Mesh mesh) { newVectices = new Dictionary(); InitArrays(mesh); int[] triangles = mesh.triangles; Debug.Log(mesh.subMeshCount); for (int j = 0; j < mesh.subMeshCount; j++) { var subMesh = mesh.GetSubMesh(j); allIndices.Add(new List()); List subIndices = allIndices[j]; for (int i = subMesh.indexStart; i < subMesh.indexStart + subMesh.indexCount; i += 3) { int i1 = triangles[i + 0]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; int a1 = GetNewVertex9(i1, i2, i1); int a2 = GetNewVertex9(i2, i1, i2); int b1 = GetNewVertex9(i2, i3, i2); int b2 = GetNewVertex9(i3, i2, i3); int c1 = GetNewVertex9(i3, i1, i3); int c2 = GetNewVertex9(i1, i3, i1); int d = GetNewVertex9(i1, i2, i3); subIndices.Add(i1); subIndices.Add(a1); subIndices.Add(c2); subIndices.Add(i2); subIndices.Add(b1); subIndices.Add(a2); subIndices.Add(i3); subIndices.Add(c1); subIndices.Add(b2); subIndices.Add(d); subIndices.Add(a1); subIndices.Add(a2); subIndices.Add(d); subIndices.Add(b1); subIndices.Add(b2); subIndices.Add(d); subIndices.Add(c1); subIndices.Add(c2); subIndices.Add(d); subIndices.Add(c2); subIndices.Add(a1); subIndices.Add(d); subIndices.Add(a2); subIndices.Add(b1); subIndices.Add(d); subIndices.Add(b2); subIndices.Add(c1); } } mesh.vertices = vertices.ToArray(); if (normals.Count > 0) mesh.normals = normals.ToArray(); if (colors.Count > 0) mesh.colors = colors.ToArray(); if (uv.Count > 0) mesh.uv = uv.ToArray(); if (uv1.Count > 0) mesh.uv2 = uv1.ToArray(); if (uv2.Count > 0) mesh.uv2 = uv2.ToArray(); mesh.triangles = allIndices.SelectMany(x => x).ToArray(); mesh.subMeshCount = allIndices.Count; for (int i = 0; i < allIndices.Count; i++) mesh.SetTriangles(allIndices[i], i); CleanUp(); } #endregion Subdivide9 (3x3) #region Subdivide /// /// This functions subdivides the mesh based on the level parameter /// Note that only the 4 and 9 subdivides are supported so only those divides /// are possible. [2,3,4,6,8,9,12,16,18,24,27,32,36,48,64, ...] /// The function tried to approximate the desired level /// /// /// Should be a number made up of (2^x * 3^y) /// [2,3,4,6,8,9,12,16,18,24,27,32,36,48,64, ...] /// public static void Subdivide(Mesh mesh, int level) { if (level < 2) return; while (level > 1) { // remove prime factor 3 while (level % 3 == 0) { Subdivide9(mesh); level /= 3; } // remove prime factor 2 while (level % 2 == 0) { Subdivide4(mesh); level /= 2; } // try to approximate. All other primes are increased by one // so they can be processed if (level > 3) level++; } } #endregion Subdivide public static Mesh DuplicateMesh(Mesh mesh) { Mesh newMesh = (Mesh)UnityEngine.Object.Instantiate(mesh); newMesh.uv = mesh.uv; return newMesh; } } }