/// Builds an array of edges that connect to only one triangle. /// In other words, the outline of the mesh public static Edge[] BuildManifoldEdges(GOMesh mesh) { // Build a edge list for all unique edges in the mesh Edge[] edges = BuildEdges(mesh.vertices.Length, mesh.triangles); // We only want edges that connect to a single triangle ArrayList culledEdges = new ArrayList(); foreach (Edge edge in edges) { if (edge.faceIndex[0] == edge.faceIndex[1]) { culledEdges.Add(edge); } } return(culledEdges.ToArray(typeof(Edge)) as Edge[]); }
public GOMesh(GOMesh premesh) { vertices = premesh.vertices; triangles = premesh.triangles; uv = premesh.uv; }
public static void ExtrudeMesh(GOMesh srcMesh, GOMesh extrudedMesh, Matrix4x4[] extrusion, Edge[] edges, bool invertFaces) { int extrudedVertexCount = edges.Length * 2 * extrusion.Length; int triIndicesPerStep = edges.Length * 6; int extrudedTriIndexCount = triIndicesPerStep * (extrusion.Length - 1); Vector3[] inputVertices = srcMesh.vertices; Vector2[] inputUV = srcMesh.uv; int[] inputTriangles = srcMesh.triangles; Vector3[] vertices = new Vector3[extrudedVertexCount + srcMesh.vertices.Length * 2]; Vector2[] uvs = new Vector2[vertices.Length]; int[] triangles = new int[extrudedTriIndexCount + inputTriangles.Length * 2]; // Build extruded vertices int v = 0; for (int i = 0; i < extrusion.Length; i++) { Matrix4x4 matrix = extrusion[i]; float vcoord = (float)i / (extrusion.Length - 1); float factor = (extrusion.Length - 1) - ((float)i / (extrusion.Length - 1)) * srcMesh.sliceHeight; //foreach (Edge e in edges) for (int k = 0; k < edges.Length; k++) { Edge e = edges[k]; vertices[v + 0] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[0]]); vertices[v + 1] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[1]]); switch (srcMesh.uvMappingStyle) { case GOUVMappingStyle.TopAndSidesRepeated: Matrix4x4 mn; if (i == extrusion.Length - 1) { mn = extrusion[0]; } else { mn = extrusion[i + 1]; } Vector3 p1 = mn.MultiplyPoint(inputVertices[e.vertexIndex[0]]); Vector3 p2 = mn.MultiplyPoint(inputVertices[e.vertexIndex[1]]); uvs[v + 0] = new Vector2(p1.x, -p1.y) / 100; uvs[v + 1] = new Vector2(p2.x, -p2.y) / 100; break; case GOUVMappingStyle.TopRepeatedSidesStretched: uvs[v + 0] = inputUV[e.vertexIndex[0]]; uvs[v + 1] = inputUV[e.vertexIndex[1]]; break; case GOUVMappingStyle.TopFitSidesFit: case GOUVMappingStyle.TopRepeatedSidesFit: case GOUVMappingStyle.TopCenteredSidesFit: uvs[v + 0] = new Vector2(1, extrusion.Length - 1 - i); uvs[v + 1] = new Vector2(0, extrusion.Length - 1 - i); break; case GOUVMappingStyle.TopFitSidesRatio: float hdist = Vector3.Distance(vertices[v + 0], vertices[v + 1]); float vdist = srcMesh.sliceHeight; int ratio = Mathf.CeilToInt(hdist / vdist); uvs[v + 0] = new Vector2(1 * ratio, extrusion.Length - 1 - i); uvs[v + 1] = new Vector2(0, extrusion.Length - 1 - i); break; case GOUVMappingStyle.TopFitSidesSliced: hdist = Vector3.Distance(vertices[v + 0], vertices[v + 1]); vdist = srcMesh.sliceHeight; ratio = Mathf.CeilToInt(hdist / vdist); float textureFactor = 1 / 3f; int f = (extrusion.Length - 1 - i); float offset = 0f; if (srcMesh.Y > srcMesh.sliceHeight) { offset = textureFactor; } if (extrusion.Length == 2) { uvs[v + 0] = new Vector2(1 * ratio, textureFactor * f + offset); uvs[v + 1] = new Vector2(0, textureFactor * f + offset); break; } float x = f % 2 == 0 ? textureFactor : 1 - textureFactor; // (f/2f) - 0.25f; if (i == 0) { x = extrusion.Length % 2 == 0 ? 1 : textureFactor; } else if (f == 0) { x = offset; } uvs[v + 0] = new Vector2(1 * ratio, x); uvs[v + 1] = new Vector2(0, x); break; default: break; } v += 2; } } // Build cap vertices // * The bottom mesh we scale along it's negative extrusion direction. This way extruding a half sphere results in a capsule. for (int c = 0; c < 2; c++) { Matrix4x4 matrix = extrusion[c == 0 ? 0 : extrusion.Length - 1]; int firstCapVertex = c == 0 ? extrudedVertexCount : extrudedVertexCount + inputVertices.Length; for (int i = 0; i < inputVertices.Length; i++) { vertices[firstCapVertex + i] = matrix.MultiplyPoint(inputVertices[i]); uvs[firstCapVertex + i] = inputUV[i]; } } // Build extruded triangles for (int i = 0; i < extrusion.Length - 1; i++) { int baseVertexIndex = (edges.Length * 2) * i; int nextVertexIndex = (edges.Length * 2) * (i + 1); for (int e = 0; e < edges.Length; e++) { int triIndex = i * triIndicesPerStep + e * 6; triangles[triIndex + 0] = baseVertexIndex + e * 2; triangles[triIndex + 1] = nextVertexIndex + e * 2; triangles[triIndex + 2] = baseVertexIndex + e * 2 + 1; triangles[triIndex + 3] = nextVertexIndex + e * 2; triangles[triIndex + 4] = nextVertexIndex + e * 2 + 1; triangles[triIndex + 5] = baseVertexIndex + e * 2 + 1; } } // build cap triangles int triCount = inputTriangles.Length / 3; // Top if (!srcMesh.separateTop) { int firstCapVertex = extrudedVertexCount; int firstCapTriIndex = extrudedTriIndexCount; for (int i = 0; i < triCount; i++) { triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex; } } else { int firstCapVertex = extrudedVertexCount; int firstCapTriIndex = extrudedTriIndexCount; extrudedMesh.topTriangles = new int[triangles.Length]; for (int i = 0; i < triCount; i++) { extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex; extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex; } } // Bottom if (!srcMesh.separateBottom) { int firstCapVertex = extrudedVertexCount + inputVertices.Length; int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length; for (int i = 0; i < triCount; i++) { triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex; } } else { int firstCapVertex = extrudedVertexCount + inputVertices.Length; int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length; extrudedMesh.bottomTriangles = new int[triangles.Length]; for (int i = 0; i < triCount; i++) { extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex; extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex; } } if (invertFaces) { for (int i = 0; i < triangles.Length / 3; i++) { int temp = triangles[i * 3 + 0]; triangles[i * 3 + 0] = triangles[i * 3 + 1]; triangles[i * 3 + 1] = temp; } } extrudedMesh.vertices = vertices; extrudedMesh.uv = uvs; extrudedMesh.triangles = triangles; }
public static void ExtrudeMesh(GOMesh srcMesh, GOMesh extrudedMesh, Matrix4x4[] extrusion, bool invertFaces) { Edge[] edges = BuildManifoldEdges(srcMesh); ExtrudeMesh(srcMesh, extrudedMesh, extrusion, edges, invertFaces); }
/// <summary> /// Recalculate the normals of a mesh based on an angle threshold. This takes /// into account distinct vertices that have the same position. /// </summary> /// <param name="mesh"></param> /// <param name="angle"> /// The smoothing angle. Note that triangles that already share /// the same vertex will be smooth regardless of the angle! /// </param> public static void RecalculateNormals(this GOMesh mesh, float angle) { var cosineThreshold = Mathf.Cos(angle * Mathf.Deg2Rad); var vertices = mesh.vertices; var normals = new Vector3[vertices.Length]; var triangles = mesh.triangles; int submeshCount = 0; // Holds the normal of each triangle in each sub mesh. var triNormals = new Vector3[submeshCount][]; var dictionary = new Dictionary <VertexKey, List <VertexEntry> >(vertices.Length); for (var subMeshIndex = 0; subMeshIndex < submeshCount; ++subMeshIndex) { triNormals[subMeshIndex] = new Vector3[triangles.Length / 3]; for (var i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; // Calculate the normal of the triangle Vector3 p1 = vertices[i2] - vertices[i1]; Vector3 p2 = vertices[i3] - vertices[i1]; Vector3 normal = Vector3.Cross(p1, p2).normalized; int triIndex = i / 3; triNormals[subMeshIndex][triIndex] = normal; List <VertexEntry> entry; VertexKey key; if (!dictionary.TryGetValue(key = new VertexKey(vertices[i1]), out entry)) { entry = new List <VertexEntry>(4); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i1)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i2]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i2)); if (!dictionary.TryGetValue(key = new VertexKey(vertices[i3]), out entry)) { entry = new List <VertexEntry>(); dictionary.Add(key, entry); } entry.Add(new VertexEntry(subMeshIndex, triIndex, i3)); } } // Each entry in the dictionary represents a unique vertex position. foreach (var vertList in dictionary.Values) { for (var i = 0; i < vertList.Count; ++i) { var sum = new Vector3(); var lhsEntry = vertList[i]; for (var j = 0; j < vertList.Count; ++j) { var rhsEntry = vertList[j]; if (lhsEntry.VertexIndex == rhsEntry.VertexIndex) { sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } else { // The dot product is the cosine of the angle between the two triangles. // A larger cosine means a smaller angle. var dot = Vector3.Dot( triNormals[lhsEntry.MeshIndex][lhsEntry.TriangleIndex], triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]); if (dot >= cosineThreshold) { sum += triNormals[rhsEntry.MeshIndex][rhsEntry.TriangleIndex]; } } } normals[lhsEntry.VertexIndex] = sum.normalized; } } mesh.normals = normals; }
public static void ExtrudeMesh(GOMesh srcMesh, GOMesh extrudedMesh, Matrix4x4[] extrusion, Edge[] edges, bool invertFaces) { int extrudedVertexCount = edges.Length * 2 * extrusion.Length; int triIndicesPerStep = edges.Length * 6; int extrudedTriIndexCount = triIndicesPerStep * (extrusion.Length - 1); Vector3[] inputVertices = srcMesh.vertices; Vector2[] inputUV = srcMesh.uv; int[] inputTriangles = srcMesh.triangles; Vector3[] vertices = new Vector3[extrudedVertexCount + srcMesh.vertices.Length * 2]; Vector2[] uvs = new Vector2[vertices.Length]; int[] triangles = new int[extrudedTriIndexCount + inputTriangles.Length * 2]; // Build extruded vertices int v = 0; for (int i = 0; i < extrusion.Length; i++) { Matrix4x4 matrix = extrusion[i]; float vcoord = (float)i / (extrusion.Length - 1); foreach (Edge e in edges) { vertices[v + 0] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[0]]); vertices[v + 1] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[1]]); uvs[v + 0] = new Vector2(inputUV[e.vertexIndex[0]].x, vcoord); uvs[v + 1] = new Vector2(inputUV[e.vertexIndex[1]].x, vcoord); v += 2; } } // Build cap vertices // * The bottom mesh we scale along it's negative extrusion direction. This way extruding a half sphere results in a capsule. for (int c = 0; c < 2; c++) { Matrix4x4 matrix = extrusion[c == 0 ? 0 : extrusion.Length - 1]; int firstCapVertex = c == 0 ? extrudedVertexCount : extrudedVertexCount + inputVertices.Length; for (int i = 0; i < inputVertices.Length; i++) { vertices[firstCapVertex + i] = matrix.MultiplyPoint(inputVertices[i]); uvs[firstCapVertex + i] = inputUV[i]; } } // Build extruded triangles for (int i = 0; i < extrusion.Length - 1; i++) { int baseVertexIndex = (edges.Length * 2) * i; int nextVertexIndex = (edges.Length * 2) * (i + 1); for (int e = 0; e < edges.Length; e++) { int triIndex = i * triIndicesPerStep + e * 6; triangles[triIndex + 0] = baseVertexIndex + e * 2; triangles[triIndex + 1] = nextVertexIndex + e * 2; triangles[triIndex + 2] = baseVertexIndex + e * 2 + 1; triangles[triIndex + 3] = nextVertexIndex + e * 2; triangles[triIndex + 4] = nextVertexIndex + e * 2 + 1; triangles[triIndex + 5] = baseVertexIndex + e * 2 + 1; } } // build cap triangles int triCount = inputTriangles.Length / 3; // Top if (!srcMesh.separateTop) { int firstCapVertex = extrudedVertexCount; int firstCapTriIndex = extrudedTriIndexCount; for (int i = 0; i < triCount; i++) { triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex; } } else { int firstCapVertex = extrudedVertexCount; int firstCapTriIndex = extrudedTriIndexCount; extrudedMesh.topTriangles = new int[triangles.Length]; for (int i = 0; i < triCount; i++) { extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex; extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; extrudedMesh.topTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex; } } // Bottom if (!srcMesh.separateBottom) { int firstCapVertex = extrudedVertexCount + inputVertices.Length; int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length; for (int i = 0; i < triCount; i++) { triangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; triangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex; } } else { int firstCapVertex = extrudedVertexCount + inputVertices.Length; int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length; extrudedMesh.bottomTriangles = new int[triangles.Length]; for (int i = 0; i < triCount; i++) { extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex; extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex; extrudedMesh.bottomTriangles[i * 3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex; } } if (invertFaces) { for (int i = 0; i < triangles.Length / 3; i++) { int temp = triangles[i * 3 + 0]; triangles[i * 3 + 0] = triangles[i * 3 + 1]; triangles[i * 3 + 1] = temp; } } extrudedMesh.vertices = vertices; extrudedMesh.uv = uvs; extrudedMesh.triangles = triangles; }