static bool TrianglesToQuads( triangleByUV triangles, // input structure -> triangle primitives quadByUV quads, // output structure -> quad primitives int totalTriangles, // how many input triangles we have ref int totalQuads, // how many output quads were generated string at // element we're processing... ) { // pair the triangles into quads by max edge connections if (triangles == null || // no triangle list triangles.Count <= 0 || // OR no primitives quads == null) // OR no output list { return(true); } // calculate progess amount string title = "Triangle Primitives->Quads " + at; float increment = 1.0f / (totalTriangles - 1); bool wasCanceled = false; int atTriangle = 0; // sorted by "triangle A connectes to triangle B" triangleByQuad connections = new triangleByQuad(); quads.Clear(); foreach (Vector2 UV in triangles.Keys) // by UV color { foreach (Vector3 N in triangles[UV].Keys) // by normal direction { foreach (Vector2 r in triangles[UV][N].Keys) // by row in normal direction, axis plane { foreach (Triangle A in triangles[UV][N][r]) // triangle A { // go through all listed triangles as A/B atTriangle++; // up the current progress if (atTriangle % (totalTriangles / progressUpdates) == 0 && EditorUtility.DisplayCancelableProgressBar (title, "Searching...", atTriangle * increment)) { wasCanceled = true; goto Cancel; } if (!connections.ContainsValue(A)) // not yet listed in quads { foreach (Triangle B in triangles[UV][N][r]) // check all triangles against A { if (A != B && // not self-comparing !connections.ContainsValue(B) && // AND not yet listed in quads A.maxEdgeVertices.TrueForAll // AND the same two vertices are in both edge lists (o => B.maxEdgeVertices.Contains(o))) { // this is the connection A->B, forming a single quad connections[A] = B; // figure out the row of the quad float qr = 0; if (Mathf.Abs(N.x) > 0.0f) { qr = A.A.x; // X+,-: YZ plane } else if (Mathf.Abs(N.y) > 0.0f) { qr = A.A.y; // Y+,-: XZ plane } else if (Mathf.Abs(N.z) > 0.0f) { qr = A.A.z; // Z+,-: XY plane } // create missing acceleration structure levels if (!quads.ContainsKey(UV)) { quads[UV] = new quadByNormal(); } if (!quads[UV].ContainsKey(N)) { quads[UV][N] = new quadByRow(); } if (!quads[UV][N].ContainsKey(qr)) { quads[UV][N][qr] = new quadByVertex(); } // now add the quad by "UV->Normal->Row" key quads[UV][N][qr].Add( new HashSet <Vector3> (new Vector3[] { A.A, A.B, A.C, B.A, B.B, B.C }) ); break; } } } } } } } // export the amount of generated quad connections totalQuads = connections.Count; Cancel: // clear progress bar, signal "wasn't canceled" EditorUtility.ClearProgressBar(); return(wasCanceled); }
static void Rebuild() { if (Selection.activeGameObject == null) { Debug.Log(title + ": Nothing selected."); return; } List <MeshFilter> all = new List <MeshFilter>(); foreach (Transform t in Selection.transforms) { all.AddRange(t.gameObject.GetComponentsInChildren <MeshFilter>()); } if (all == null || // no MeshFilter(s) all.Count <= 0) // OR no entries { Debug.Log(title + ": No mesh(s) on selection."); return; } int processing = 0; foreach (MeshFilter mf in all) { if (mf != null && // instance needs to be valid mf.sharedMesh != null) // AND have a shared mesh { // convert every mesh we find below string path = AssetDatabase.GetAssetPath(mf.sharedMesh); if (path.ToLower().IndexOf("assets/") < 0) { Debug.LogWarning(title + ": Conversion of mesh ['" + path + "'] not supported."); continue; } processing++; // current item we're processing bool cancel = false; // did we cancel an operation? string at = "(" + processing + " of " + all.Count + ")"; // save start timestamp double startAt = EditorApplication.timeSinceStartup; // sorted by "UV->normal direction->(row, horizontal axis)"->[Triangle list] triangleByUV triangles = new triangleByUV(); // sorted by "UV->normal direction->row"->[List of "Quad vertices list"] quadByUV quads = new quadByUV(); // convert the established mesh to triangle primitives float trianglesBefore = (mf.sharedMesh.triangles.Length / 3.0f); int totalTriangles = 0; cancel = MeshToTriangles(mf, triangles, ref totalTriangles, at); if (triangles.Count <= 0) { Debug.LogError(title + ": Conversion of mesh to Triangle primitives failed."); continue; } if (cancel) { continue; } // search for quad connections and fill up the quad list int totalQuads = 0; cancel = TrianglesToQuads(triangles, quads, totalTriangles, ref totalQuads, at); if (quads.Count <= 0) { Debug.LogError(title + ": Conversion of Triangles->Quads failed."); continue; } if (cancel) { continue; } // optimize the mesh structure by grouping quads cancel = GroupQuads(quads, totalQuads, at); if (cancel) { continue; } // spit out the newly generated mesh string outputAs = QuadsToMesh(mf, quads, title); // print out total time in seconds double totalTime = EditorApplication.timeSinceStartup - startAt; float trianglesAfter = (mf.sharedMesh.triangles.Length / 3.0f); float fRemaining = (trianglesAfter / trianglesBefore) * 100.0f; float fReduction = 100.0f - fRemaining; Debug.Log(string.Format( "{0}: Done. Processing Time: {1:0.00}s. [Triangles:{2}->{3}, remaining:{4:0.00}%, reduction:{5:0.00}%]", outputAs, totalTime, trianglesBefore, trianglesAfter, fRemaining, fReduction )); } } }
const int progressUpdates = 4; // how often to update the progress bar static bool MeshToTriangles( MeshFilter mf, // input mesh source triangleByUV triangles, // output structure -> triangles ref int totalTriangles, // amount of triangles we're processing string at // element we're processing... ) { // convert an established mesh into a complex list of Triangle primitives if (mf == null || // no MeshFilter mf.sharedMesh == null || // OR no shared mesh triangles == null) // OR no output list { return(true); } // get the triangles from the established mesh int[] f = mf.sharedMesh.triangles; Vector3[] v = mf.sharedMesh.vertices; Vector3[] n = mf.sharedMesh.normals; Vector2[] uv = mf.sharedMesh.uv; triangles.Clear(); string title = "Mesh->Triangle Primitives " + at; float increment = 1.0f / (f.Length - 1); bool wasCanceled = false; totalTriangles = f.Length / 3; for (int i = 0; i < f.Length; i += 3) { // create triangle primitives from all faces if (i % (f.Length / progressUpdates) == 0 && EditorUtility.DisplayCancelableProgressBar (title, "Converting...", i * increment)) { wasCanceled = true; break; } int A = f[i + 0]; int B = f[i + 1]; int C = f[i + 2]; Vector3 UV = uv[A]; Vector3 N = n [A].normalized; // winding cannot be trusted. we need to calculate a lot on the go Triangle t = new Triangle(v[A], v[B], v[C]); Vector2 r = Vector2.zero; if (Mathf.Abs(N.x) > 0.0f) { r = new Vector2(t.A.x, t.A.z); // X+,-: YZ plane, search Z dir } else if (Mathf.Abs(N.y) > 0.0f) { r = new Vector2(t.A.y, t.A.x); // Y+,-: XZ plane, search X dir } else if (Mathf.Abs(N.z) > 0.0f) { r = new Vector2(t.A.z, t.A.x); // Z+,-: XY plane, search X dir } // create missing acceleration structure levels if (!triangles.ContainsKey(UV)) { triangles[UV] = new triangleByNormal(); } if (!triangles[UV].ContainsKey(N)) { triangles[UV][N] = new triangleByRow(); } if (!triangles[UV][N].ContainsKey(r)) { triangles[UV][N][r] = new triangleByList(); } // now finally add triangle by "UV->Normal->Row" key triangles[UV][N][r].Add(t); } // clear progress bar, signal "wasn't canceled" EditorUtility.ClearProgressBar(); return(wasCanceled); }