using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Text; using System; namespace Dreamteck { public class MeshUtility { private static Vector3[] tan1 = new Vector3[0]; private static Vector3[] tan2 = new Vector3[0]; public static int[] GeneratePlaneTriangles(int x, int z, bool flip, int startTriangleIndex = 0, int startVertex = 0) { int nbFaces = x * (z - 1); int[] triangles = new int[nbFaces * 6]; GeneratePlaneTriangles(ref triangles, x, z, flip); return triangles; } public static int[] GeneratePlaneTriangles(ref int[] triangles, int x, int z, bool flip, int startTriangleIndex = 0, int startVertex = 0, bool reallocateArray = false) { int nbFaces = x * (z - 1); if (reallocateArray && triangles.Length != nbFaces * 6) { if (startTriangleIndex > 0) { int[] newTris = new int[startTriangleIndex + nbFaces * 6]; for (int i = 0; i < startTriangleIndex; i++) newTris[i] = triangles[i]; triangles = newTris; } else triangles = new int[nbFaces * 6]; } int g = x + 1; int t = startTriangleIndex; // for (int face = 0; face < nbFaces + z - 2; face++) // { // if ((float)(face + 1) % (float)g == 0f && face != 0) face++; // if (flip) // { // triangles[t++] = face + x + 1 + startVertex; // triangles[t++] = face + 1 + startVertex; // triangles[t++] = face + startVertex; // triangles[t++] = face + x + 1 + startVertex; // triangles[t++] = face + x + 2 + startVertex; // triangles[t++] = face + 1 + startVertex; // } // else // { // triangles[t++] = face + startVertex; // triangles[t++] = face + 1 + startVertex; // triangles[t++] = face + x + 1 + startVertex; // triangles[t++] = face + 1 + startVertex; // triangles[t++] = face + x + 2 + startVertex; // triangles[t++] = face + x + 1 + startVertex; // } // } int index = 0; for (int row = 0; row < z - 1; row++) { for (int col = 0; col < x; col++) { int i = row * (x + 1) + col; if (flip) { triangles[index++] = i + x + 1; triangles[index++] = i + 1; triangles[index++] = i; triangles[index++] = i + x + 1; triangles[index++] = i + x + 2; triangles[index++] = i + 1; } else { triangles[index++] = i + startVertex; triangles[index++] = i + 1 + startVertex; triangles[index++] = i + x + 1 + startVertex; triangles[index++] = i + 1 + startVertex; triangles[index++] = i + x + 2 + startVertex; triangles[index++] = i + x + 1 + startVertex; } } } return triangles; } public static void CalculateTangents(TS_Mesh mesh) { int vertexCount = mesh.vertexCount; int triangleCount = mesh.triangles.Length / 3; // 优化1:数组重用与按需分配 if (tan1.Length < vertexCount) { tan1 = new Vector3[vertexCount]; tan2 = new Vector3[vertexCount]; } else { // 重用数组但需要清零 Array.Clear(tan1, 0, vertexCount); Array.Clear(tan2, 0, vertexCount); } // 优化2:避免重复访问属性 Vector3[] vertices = mesh.vertices; Vector2[] uvs = mesh.uv; Vector3[] normals = mesh.normals; int[] triangles = mesh.triangles; // 优化3:预先检查数组长度 if (uvs.Length < vertexCount || normals.Length < vertexCount) { Debug.LogError("UV or Normal array size mismatch"); return; } // 优化4:缓存变量减少重复计算 Vector3 edge1 = Vector3.zero; Vector3 edge2 = Vector3.zero; Vector2 uvEdge1 = Vector2.zero; Vector2 uvEdge2 = Vector2.zero; // 优化5:循环展开减少分支预测失败 for (int i = 0; i < triangleCount; i++) { int triIndex = i * 3; int i1 = triangles[triIndex]; int i2 = triangles[triIndex + 1]; int i3 = triangles[triIndex + 2]; // 计算边向量 edge1.x = vertices[i2].x - vertices[i1].x; edge1.y = vertices[i2].y - vertices[i1].y; edge1.z = vertices[i2].z - vertices[i1].z; edge2.x = vertices[i3].x - vertices[i1].x; edge2.y = vertices[i3].y - vertices[i1].y; edge2.z = vertices[i3].z - vertices[i1].z; // 计算UV差 uvEdge1.x = uvs[i2].x - uvs[i1].x; uvEdge1.y = uvs[i2].y - uvs[i1].y; uvEdge2.x = uvs[i3].x - uvs[i1].x; uvEdge2.y = uvs[i3].y - uvs[i1].y; // 优化6:避免除零检查的分支预测 float div = uvEdge1.x * uvEdge2.y - uvEdge2.x * uvEdge1.y; float r = Mathf.Abs(div) < Mathf.Epsilon ? 0f : 1f / div; // 计算切线向量 Vector3 sdir = new Vector3( (uvEdge2.y * edge1.x - uvEdge1.y * edge2.x) * r, (uvEdge2.y * edge1.y - uvEdge1.y * edge2.y) * r, (uvEdge2.y * edge1.z - uvEdge1.y * edge2.z) * r ); Vector3 tdir = new Vector3( (uvEdge1.x * edge2.x - uvEdge2.x * edge1.x) * r, (uvEdge1.x * edge2.y - uvEdge2.x * edge1.y) * r, (uvEdge1.x * edge2.z - uvEdge2.x * edge1.z) * r ); // 累加到顶点 tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } // 优化7:确保切线数组已分配 if (mesh.tangents == null || mesh.tangents.Length != vertexCount) { mesh.tangents = new Vector4[vertexCount]; } // 优化8:手动Gram-Schmidt正交化 for (int i = 0; i < vertexCount; i++) { Vector3 n = normals[i]; Vector3 t = tan1[i]; // 手动正交化 (比Vector3.OrthoNormalize更高效) t = (t - n * Vector3.Dot(n, t)).normalized; // 计算副法线方向 Vector3 b = Vector3.Cross(n, t); float w = (Vector3.Dot(b, tan2[i]) < 0.0f) ? -1.0f : 1.0f; mesh.tangents[i] = new Vector4(t.x, t.y, t.z, w); } } public static void MakeDoublesided(Mesh input) { Vector3[] vertices = input.vertices; Vector3[] normals = input.normals; Vector2[] uvs = input.uv; Color[] colors = input.colors; int[] triangles = input.triangles; List submeshes = new List(); for (int i = 0; i < input.subMeshCount; i++) submeshes.Add(input.GetTriangles(i)); Vector3[] newVertices = new Vector3[vertices.Length * 2]; Vector3[] newNormals = new Vector3[normals.Length * 2]; Vector2[] newUvs = new Vector2[uvs.Length * 2]; Color[] newColors = new Color[colors.Length * 2]; int[] newTris = new int[triangles.Length * 2]; List newSubmeshes = new List(); for (int i = 0; i < submeshes.Count; i++) { newSubmeshes.Add(new int[submeshes[i].Length * 2]); submeshes[i].CopyTo(newSubmeshes[i], 0); } for (int i = 0; i < vertices.Length; i++) { newVertices[i] = vertices[i]; newNormals[i] = normals[i]; newUvs[i] = uvs[i]; if (colors.Length > i) newColors[i] = colors[i]; newVertices[i + vertices.Length] = vertices[i]; newNormals[i + vertices.Length] = -normals[i]; newUvs[i + vertices.Length] = uvs[i]; if (colors.Length > i) newColors[i + vertices.Length] = colors[i]; } for (int i = 0; i < triangles.Length; i += 3) { int index1 = triangles[i]; int index2 = triangles[i + 1]; int index3 = triangles[i + 2]; newTris[i] = index1; newTris[i + 1] = index2; newTris[i + 2] = index3; newTris[i + triangles.Length] = index3 + vertices.Length; newTris[i + triangles.Length + 1] = index2 + vertices.Length; newTris[i + triangles.Length + 2] = index1 + vertices.Length; } for (int i = 0; i < submeshes.Count; i++) { for (int n = 0; n < submeshes[i].Length; n += 3) { int index1 = submeshes[i][n]; int index2 = submeshes[i][n + 1]; int index3 = submeshes[i][n + 2]; newSubmeshes[i][n] = index1; newSubmeshes[i][n + 1] = index2; newSubmeshes[i][n + 2] = index3; newSubmeshes[i][n + submeshes[i].Length] = index3 + vertices.Length; newSubmeshes[i][n + submeshes[i].Length + 1] = index2 + vertices.Length; newSubmeshes[i][n + submeshes[i].Length + 2] = index1 + vertices.Length; } } input.vertices = newVertices; input.normals = newNormals; input.uv = newUvs; input.colors = newColors; input.triangles = newTris; for (int i = 0; i < newSubmeshes.Count; i++) input.SetTriangles(newSubmeshes[i], i); } public static void MakeDoublesided(TS_Mesh input) { Vector3[] vertices = input.vertices; Vector3[] normals = input.normals; Vector2[] uvs = input.uv; Color[] colors = input.colors; int[] triangles = input.triangles; List submeshes = input.subMeshes; Vector3[] newVertices = new Vector3[vertices.Length * 2]; Vector3[] newNormals = new Vector3[normals.Length * 2]; Vector2[] newUvs = new Vector2[uvs.Length * 2]; Color[] newColors = new Color[colors.Length * 2]; int[] newTris = new int[triangles.Length * 2]; List newSubmeshes = new List(); for (int i = 0; i < submeshes.Count; i++) { newSubmeshes.Add(new int[submeshes[i].Length * 2]); submeshes[i].CopyTo(newSubmeshes[i], 0); } for (int i = 0; i < vertices.Length; i++) { newVertices[i] = vertices[i]; newNormals[i] = normals[i]; newUvs[i] = uvs[i]; if (colors.Length > i) newColors[i] = colors[i]; newVertices[i + vertices.Length] = vertices[i]; newNormals[i + vertices.Length] = -normals[i]; newUvs[i + vertices.Length] = uvs[i]; if (colors.Length > i) newColors[i + vertices.Length] = colors[i]; } for (int i = 0; i < triangles.Length; i += 3) { int index1 = triangles[i]; int index2 = triangles[i + 1]; int index3 = triangles[i + 2]; newTris[i] = index1; newTris[i + 1] = index2; newTris[i + 2] = index3; newTris[i + triangles.Length] = index3 + vertices.Length; newTris[i + triangles.Length + 1] = index2 + vertices.Length; newTris[i + triangles.Length + 2] = index1 + vertices.Length; } for (int i = 0; i < submeshes.Count; i++) { for (int n = 0; n < submeshes[i].Length; n += 3) { int index1 = submeshes[i][n]; int index2 = submeshes[i][n + 1]; int index3 = submeshes[i][n + 2]; newSubmeshes[i][n] = index1; newSubmeshes[i][n + 1] = index2; newSubmeshes[i][n + 2] = index3; newSubmeshes[i][n + submeshes[i].Length] = index3 + vertices.Length; newSubmeshes[i][n + submeshes[i].Length + 1] = index2 + vertices.Length; newSubmeshes[i][n + submeshes[i].Length + 2] = index1 + vertices.Length; } } input.vertices = newVertices; input.normals = newNormals; input.uv = newUvs; input.colors = newColors; input.triangles = newTris; input.subMeshes = newSubmeshes; } public static void MakeDoublesidedHalf(TS_Mesh input) { int vertexHalf = input.vertices.Length / 2; int trisHalf = input.triangles.Length / 2; for (int i = 0; i < vertexHalf; i++) { input.vertices[i + vertexHalf] = input.vertices[i]; if (input.normals.Length > i) input.normals[i + vertexHalf] = -input.normals[i]; if (input.tangents.Length > i) input.tangents[i + vertexHalf] = input.tangents[i]; if (input.uv.Length > i) input.uv[i + vertexHalf] = input.uv[i]; if (input.uv2.Length > i) input.uv2[i + vertexHalf] = input.uv2[i]; if (input.uv3.Length > i) input.uv3[i + vertexHalf] = input.uv3[i]; if (input.uv4.Length > i) input.uv4[i + vertexHalf] = input.uv4[i]; if (input.colors.Length > i) input.colors[i + vertexHalf] = input.colors[i]; } for (int i = 0; i < trisHalf; i += 3) { input.triangles[i + trisHalf + 2] = input.triangles[i] + vertexHalf; input.triangles[i + trisHalf + 1] = input.triangles[i + 1] + vertexHalf; input.triangles[i + trisHalf] = input.triangles[i + 2] + vertexHalf; } for (int i = 0; i < input.subMeshes.Count; i++) { trisHalf = input.subMeshes[i].Length / 2; for (int n = 0; n < trisHalf; n += 3) { input.subMeshes[i][n + trisHalf + 2] = input.subMeshes[i][n] + vertexHalf; input.subMeshes[i][n + trisHalf + 1] = input.subMeshes[i][n + 1] + vertexHalf; input.subMeshes[i][n + trisHalf] = input.subMeshes[i][n + 2] + vertexHalf; } } } public static void TransformMesh(TS_Mesh input, Matrix4x4 matrix) { if (input.vertices == null || input.normals == null) return; for (int i = 0; i < input.vertices.Length; i++) { input.vertices[i] = matrix.MultiplyPoint3x4(input.vertices[i]); input.normals[i] = matrix.MultiplyVector(input.normals[i]); } } public static void TransformMesh(Mesh input, Matrix4x4 matrix) { Vector3[] vertices = input.vertices; Vector3[] normals = input.vertices; if (input.vertices == null || input.normals == null) return; for (int i = 0; i < input.vertices.Length; i++) { vertices[i] = matrix.MultiplyPoint3x4(vertices[i]); normals[i] = matrix.MultiplyVector(normals[i]); } input.vertices = vertices; input.normals = normals; } public static void TransformVertices(Vector3[] vertices, Matrix4x4 matrix) { for (int i = 0; i < vertices.Length; i++) { vertices[i] = matrix.MultiplyPoint3x4(vertices[i]); } } public static void TransformNormals(Vector3[] normals, Matrix4x4 matrix) { for (int i = 0; i < normals.Length; i++) { normals[i] = matrix.MultiplyVector(normals[i]); } } public static string ToOBJString(Mesh mesh, Material[] materials) { int numVertices = 0; if (mesh == null) { return "####Error####"; } StringBuilder sb = new StringBuilder(); sb.Append("g " + mesh.name + "\n"); foreach (Vector3 v in mesh.vertices) { numVertices++; sb.Append(string.Format("v {0} {1} {2}\n", -v.x, v.y, v.z)); } sb.Append("\n"); foreach (Vector3 n in mesh.normals) { sb.Append(string.Format("vn {0} {1} {2}\n", -n.x, n.y, n.z)); } sb.Append("\n"); foreach (Vector3 v in mesh.uv) { sb.Append(string.Format("vt {0} {1}\n", v.x, v.y)); } sb.Append("\n"); foreach (Vector2 v in mesh.uv2) { sb.Append(string.Format("vt2 {0} {1}\n", v.x, v.y)); } sb.Append("\n"); foreach (Vector2 v in mesh.uv3) { sb.Append(string.Format("vt2 {0} {1}\n", v.x, v.y)); } sb.Append("\n"); foreach (Color c in mesh.colors) { sb.Append(string.Format("vc {0} {1} {2} {3}\n", c.r, c.g, c.b, c.a)); } for (int material = 0; material < mesh.subMeshCount; material++) { sb.Append("\n"); sb.Append("usemtl ").Append(materials[material].name).Append("\n"); sb.Append("usemap ").Append(materials[material].name).Append("\n"); int[] triangles = mesh.GetTriangles(material); for (int i = 0; i < triangles.Length; i += 3) { sb.Append(string.Format("f {2}/{2}/{2} {1}/{1}/{1} {0}/{0}/{0}\n", triangles[i] + 1, triangles[i + 1] + 1, triangles[i + 2] + 1)); } } return sb.ToString().Replace(',', '.'); } public static Mesh Copy(Mesh input) { Mesh copy = new Mesh(); copy.name = input.name; copy.vertices = input.vertices; copy.normals = input.normals; copy.colors = input.colors; copy.uv = input.uv; copy.uv2 = input.uv2; copy.uv3 = input.uv3; copy.uv4 = input.uv4; copy.tangents = input.tangents; copy.boneWeights = input.boneWeights; copy.bindposes = input.bindposes; copy.triangles = input.triangles; copy.subMeshCount = input.subMeshCount; for (int i = 0; i < input.subMeshCount; i++) { copy.SetTriangles(input.GetTriangles(i), i); } return copy; } public static void Triangulate(Vector2[] points, ref int[] output) { List indices = new List(); int pointsLength = points.Length; if (pointsLength < 3) { output = new int[0]; return; } int[] V = new int[pointsLength]; if (Area(points, pointsLength) > 0) { for (int v = 0; v < pointsLength; v++) V[v] = v; } else { for (int v = 0; v < pointsLength; v++) V[v] = (pointsLength - 1) - v; } int nv = pointsLength; int count = 2 * nv; for (int m = 0, v = nv - 1; nv > 2;) { if ((count--) <= 0) { if (output.Length != indices.Count) output = new int[indices.Count]; indices.CopyTo(output, 0); return; } int u = v; if (nv <= u) u = 0; v = u + 1; if (nv <= v) v = 0; int w = v + 1; if (nv <= w) w = 0; if (Snip(points, u, v, w, nv, V)) { int a, b, c, s, t; a = V[u]; b = V[v]; c = V[w]; indices.Add(c); indices.Add(b); indices.Add(a); m++; for (s = v, t = v + 1; t < nv; s++, t++) V[s] = V[t]; nv--; count = 2 * nv; } } indices.Reverse(); if (output.Length != indices.Count) output = new int[indices.Count]; indices.CopyTo(output, 0); } public static void FlipTriangles(ref int[] triangles) { for (int i = 0; i < triangles.Length; i += 3) { int temp = triangles[i]; triangles[i] = triangles[i + 2]; triangles[i + 2] = temp; } } public static void FlipFaces(TS_Mesh input) { for (int i = 0; i < input.subMeshes.Count; i++) { int[] array = input.subMeshes[i]; FlipTriangles(ref array); } FlipTriangles(ref input.triangles); for (int i = 0; i < input.normals.Length; i++) { input.normals[i] *= -1f; } } public static void BreakMesh(Mesh input, bool keepNormals = true) { Vector3[] newVertices = new Vector3[input.triangles.Length]; Vector3[] newNormals = new Vector3[newVertices.Length]; Vector2[] newUVs = new Vector2[newVertices.Length]; Vector4[] newTangents = new Vector4[newVertices.Length]; Color[] newColors = new Color[newVertices.Length]; BoneWeight[] newBoneWeights = new BoneWeight[newVertices.Length]; Vector3[] oldVertices = input.vertices; Vector2[] oldUvs = input.uv; Vector3[] oldNormals = input.normals; Vector4[] oldTangents = input.tangents; Color[] oldColors = input.colors; BoneWeight[] oldBoneWeights = input.boneWeights; if (oldColors.Length != oldVertices.Length) { oldColors = new Color[oldVertices.Length]; for (int i = 0; i < oldColors.Length; i++) oldColors[i] = Color.white; } List submeshList = new List(); int submeshes = input.subMeshCount; int vertIndex = 0; for (int i = 0; i < submeshes; i++) { int[] submesh = input.GetTriangles(i); for (int n = 0; n < submesh.Length; n += 3) { newVertices[vertIndex] = oldVertices[submesh[n]]; newVertices[vertIndex + 1] = oldVertices[submesh[n + 1]]; newVertices[vertIndex + 2] = oldVertices[submesh[n + 2]]; if (oldNormals.Length > submesh[n + 2]) { if (!keepNormals) { newNormals[vertIndex] = newNormals[vertIndex + 1] = newNormals[vertIndex + 2] = (oldNormals[submesh[n]] + oldNormals[submesh[n + 1]] + oldNormals[submesh[n + 2]]).normalized; } else { newNormals[vertIndex] = oldNormals[submesh[n]]; newNormals[vertIndex + 1] = oldNormals[submesh[n + 1]]; newNormals[vertIndex + 2] = oldNormals[submesh[n + 2]]; } } if (oldColors.Length > submesh[n + 2]) newColors[vertIndex] = newColors[vertIndex + 1] = newColors[vertIndex + 2] = (oldColors[submesh[n]] + oldColors[submesh[n + 1]] + oldColors[submesh[n + 2]]) / 3f; if (oldUvs.Length > submesh[n + 2]) { newUVs[vertIndex] = oldUvs[submesh[n]]; newUVs[vertIndex + 1] = oldUvs[submesh[n + 1]]; newUVs[vertIndex + 2] = oldUvs[submesh[n + 2]]; } if (oldTangents.Length > submesh[n + 2]) { newTangents[vertIndex] = oldTangents[submesh[n]]; newTangents[vertIndex + 1] = oldTangents[submesh[n + 1]]; newTangents[vertIndex + 2] = oldTangents[submesh[n + 2]]; } if (oldBoneWeights.Length > submesh[n + 2]) { newBoneWeights[vertIndex] = oldBoneWeights[submesh[n]]; newBoneWeights[vertIndex + 1] = oldBoneWeights[submesh[n + 1]]; newBoneWeights[vertIndex + 2] = oldBoneWeights[submesh[n + 2]]; } submesh[n] = vertIndex; submesh[n + 1] = vertIndex + 1; submesh[n + 2] = vertIndex + 2; vertIndex += 3; } submeshList.Add(submesh); } input.vertices = newVertices; input.normals = newNormals; input.colors = newColors; input.uv = newUVs; input.tangents = newTangents; input.subMeshCount = submeshList.Count; input.boneWeights = newBoneWeights; for (int i = 0; i < submeshList.Count; i++) { input.SetTriangles(submeshList[i], i); } } private static float Area(Vector2[] points, int maxCount) { float A = 0.0f; for (int p = maxCount - 1, q = 0; q < maxCount; p = q++) { Vector2 pval = points[p]; Vector2 qval = points[q]; A += pval.x * qval.y - qval.x * pval.y; } return (A * 0.5f); } private static bool Snip(Vector2[] points, int u, int v, int w, int n, int[] V) { int p; Vector2 A = points[V[u]]; Vector2 B = points[V[v]]; Vector2 C = points[V[w]]; if (Mathf.Epsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x)))) return false; for (p = 0; p < n; p++) { if ((p == u) || (p == v) || (p == w)) continue; Vector2 P = points[V[p]]; if (InsideTriangle(A, B, C, P)) return false; } return true; } private static bool InsideTriangle(Vector2 A, Vector2 B, Vector2 C, Vector2 P) { float ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; float cCROSSap, bCROSScp, aCROSSbp; ax = C.x - B.x; ay = C.y - B.y; bx = A.x - C.x; by = A.y - C.y; cx = B.x - A.x; cy = B.y - A.y; apx = P.x - A.x; apy = P.y - A.y; bpx = P.x - B.x; bpy = P.y - B.y; cpx = P.x - C.x; cpy = P.y - C.y; aCROSSbp = ax * bpy - ay * bpx; cCROSSap = cx * apy - cy * apx; bCROSScp = bx * cpy - by * cpx; return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); } } }