/** * \brief * param sharedIndex An optional array that sets the new pb_Face indices to use the _sharedIndices array. * \returns The newly appended pb_Face. */ public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, pb_Face face) { int[] shared = new int[v.Length]; for(int i = 0; i < v.Length; i++) shared[i] = -1; return pb.AppendFace(v, face, shared); }
public static void Extrude(this pb_Object pb, pb_Face[] faces, float extrudeDistance) { if(faces == null || faces.Length < 1) return; pb_IntArray[] sharedIndices = pb.GetSharedIndices(); Vector3[] localVerts = pb.vertices; Vector3[] oNormals = pb.msh.normals; pb_Edge[] perimeterEdges = pb.GetPerimeterEdges(faces); if(perimeterEdges == null || perimeterEdges.Length < 3) { Debug.LogWarning("No perimeter edges found. Try deselecting and reselecting this object and trying again."); return; } pb_Face[] edgeFaces = new pb_Face[perimeterEdges.Length]; // can't assume faces and perimiter edges will be 1:1 - so calculate perimeters then extrace face information int[] allEdgeIndices = new int[perimeterEdges.Length * 2]; int c = 0; for(int i = 0; i < perimeterEdges.Length; i++) { // wtf does this do edgeFaces[i] = faces[0]; foreach(pb_Face face in faces) if(face.edges.Contains(perimeterEdges[i])) edgeFaces[i] = face; allEdgeIndices[c++] = perimeterEdges[i].x; allEdgeIndices[c++] = perimeterEdges[i].y; } List<pb_Edge> extrudedIndices = new List<pb_Edge>(); /// build out new faces around edges for(int i = 0; i < perimeterEdges.Length; i++) { pb_Edge edge = perimeterEdges[i]; pb_Face face = edgeFaces[i]; // Averages the normals using only vertices that are on the edge Vector3 xnorm = Vector3.zero; Vector3 ynorm = Vector3.zero; // don't bother getting vertex normals if not auto-extruding if(extrudeDistance > Mathf.Epsilon) { xnorm = Norm( edge.x, sharedIndices, allEdgeIndices, oNormals ); ynorm = Norm( edge.y, sharedIndices, allEdgeIndices, oNormals ); } int x_sharedIndex = sharedIndices.IndexOf(edge.x); int y_sharedIndex = sharedIndices.IndexOf(edge.y); pb_Face newFace = pb.AppendFace( new Vector3[4] { localVerts [ edge.x ], localVerts [ edge.y ], localVerts [ edge.x ] + xnorm.normalized * extrudeDistance, localVerts [ edge.y ] + ynorm.normalized * extrudeDistance }, new pb_Face( new int[6] {0, 1, 2, 1, 3, 2 }, // indices face.material, // material new pb_UV(face.uv), // UV material face.smoothingGroup, // smoothing group -1, // texture group -1, // uv element group face.colors[0] ), // colors new int[4] { x_sharedIndex, y_sharedIndex, -1, -1 }); extrudedIndices.Add(new pb_Edge(x_sharedIndex, newFace.indices[2])); extrudedIndices.Add(new pb_Edge(y_sharedIndex, newFace.indices[4])); } // merge extruded vertex indices with each other pb_IntArray[] si = pb.sharedIndices; // leave the sharedIndices copy alone since we need the un-altered version later for(int i = 0; i < extrudedIndices.Count; i++) { int val = extrudedIndices[i].x; for(int n = 0; n < extrudedIndices.Count; n++) { if(n == i) continue; if(extrudedIndices[n].x == val) { pb_IntArrayUtility.MergeSharedIndices(ref si, extrudedIndices[n].y, extrudedIndices[i].y); break; } } } // Move extruded faces to top localVerts = pb.vertices; Dictionary<int, int> remappedTexGroups = new Dictionary<int, int>(); foreach(pb_Face f in faces) { // Remap texture groups if(f.textureGroup > 0) { if(remappedTexGroups.ContainsKey(f.textureGroup)) { f.textureGroup = remappedTexGroups[f.textureGroup]; } else { int newTexGroup = pb.UnusedTextureGroup(); remappedTexGroups.Add(f.textureGroup, newTexGroup); f.textureGroup = newTexGroup; } } int[] distinctIndices = f.distinctIndices; foreach(int ind in distinctIndices) { int oldIndex = si.IndexOf(ind); for(int i = 0; i < extrudedIndices.Count; i++) { if(oldIndex == extrudedIndices[i].x) { pb_IntArrayUtility.MergeSharedIndices(ref si, extrudedIndices[i].y, ind); break; } } } } // this is a separate loop cause the one above this must completely merge all sharedindices prior to // checking the normal averages foreach(pb_Face f in faces) { foreach(int ind in f.distinctIndices) { Vector3 norm = Norm( ind, si, allEdgeIndices, oNormals ); localVerts[ind] += norm.normalized * extrudeDistance; } } pb.SetSharedIndices(si); pb.SetVertices(localVerts); pb.RebuildFaceCaches(); }
public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b, bool enforcePerimiterEdgesOnly) { pb_IntArray[] sharedIndices = pb.GetSharedIndices(); // Check to see if a face already exists if(enforcePerimiterEdgesOnly) { if( pbMeshUtils.GetConnectedFaces(pb, a).Count > 1 || pbMeshUtils.GetConnectedFaces(pb, b).Count > 1 ) { Debug.LogWarning("Both edges are not on perimeter! You may turn off this Bridging restriction in Preferences/ProBuilder/Bridge Perimiter Edges Only"); return false; } } else { foreach(pb_Face face in pb.faces) { if(face.edges.IndexOf(a, sharedIndices) >= 0 && face.edges.IndexOf(b, sharedIndices) >= 0) { Debug.LogWarning("Face already exists between these two edges!"); return false; } } } Vector3[] verts = pb.vertices; Vector3[] v; int[] s; pb_UV uvs = new pb_UV(); Color32 color = (Color32)Color.white; Material mat = pb_Constant.DefaultMaterial; // Get material and UV stuff from the first edge face foreach(pb_Face face in pb.faces) { if(face.edges.Contains(a)) { uvs = new pb_UV(face.uv); mat = face.material; color = face.colors[0]; break; } } // Bridge will form a triangle if( a.Contains(b.x, sharedIndices) || a.Contains(b.y, sharedIndices) ) { v = new Vector3[3]; s = new int[3]; bool axbx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.x) > -1; bool axby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.y) > -1; bool aybx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.x) > -1; bool ayby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.y) > -1; if(axbx) { v[0] = verts[a.x]; s[0] = sharedIndices.IndexOf(a.x); v[1] = verts[a.y]; s[1] = sharedIndices.IndexOf(a.y); v[2] = verts[b.y]; s[2] = sharedIndices.IndexOf(b.y); } else if(axby) { v[0] = verts[a.x]; s[0] = sharedIndices.IndexOf(a.x); v[1] = verts[a.y]; s[1] = sharedIndices.IndexOf(a.y); v[2] = verts[b.x]; s[2] = sharedIndices.IndexOf(b.x); } else if(aybx) { v[0] = verts[a.y]; s[0] = sharedIndices.IndexOf(a.y); v[1] = verts[a.x]; s[1] = sharedIndices.IndexOf(a.x); v[2] = verts[b.y]; s[2] = sharedIndices.IndexOf(b.y); } else if(ayby) { v[0] = verts[a.y]; s[0] = sharedIndices.IndexOf(a.y); v[1] = verts[a.x]; s[1] = sharedIndices.IndexOf(a.x); v[2] = verts[b.x]; s[2] = sharedIndices.IndexOf(b.x); } pb.AppendFace( v, new pb_Face( axbx || axby ? new int[3] {2, 1, 0} : new int[3] {0, 1, 2}, mat, uvs, 0, -1, -1, color ), s); pb.RebuildFaceCaches(); pb.Refresh(); return true; } // Else, bridge will form a quad v = new Vector3[4]; s = new int[4]; // shared indices index to add to v[0] = verts[a.x]; s[0] = sharedIndices.IndexOf(a.x); v[1] = verts[a.y]; s[1] = sharedIndices.IndexOf(a.y); Vector3 nrm = Vector3.Cross( verts[b.x]-verts[a.x], verts[a.y]-verts[a.x] ).normalized; Vector2[] planed = pb_Math.VerticesTo2DPoints( new Vector3[4] {verts[a.x], verts[a.y], verts[b.x], verts[b.y] }, nrm ); Vector2 ipoint = Vector2.zero; bool interescts = pb_Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint); if(!interescts) { v[2] = verts[b.x]; s[2] = sharedIndices.IndexOf(b.x); v[3] = verts[b.y]; s[3] = sharedIndices.IndexOf(b.y); } else { v[2] = verts[b.y]; s[2] = sharedIndices.IndexOf(b.y); v[3] = verts[b.x]; s[3] = sharedIndices.IndexOf(b.x); } pb.AppendFace( v, new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, mat, uvs, 0, -1, -1, color ), s); pb.RebuildFaceCaches(); return true; }
/** * Edge extrusion override */ public static pb_Edge[] Extrude(this pb_Object pb, pb_Edge[] edges, float extrudeDistance, bool enforcePerimiterEdgesOnly) { pb_IntArray[] sharedIndices = pb.sharedIndices; List<pb_Edge> validEdges = new List<pb_Edge>(); List<pb_Face> edgeFaces = new List<pb_Face>(); foreach(pb_Edge e in edges) { int faceCount = 0; pb_Face fa = null; foreach(pb_Face f in pb.faces) { if(f.edges.IndexOf(e, sharedIndices) > -1) { fa = f; if(++faceCount > 1) break; } } if(!enforcePerimiterEdgesOnly || faceCount < 2) { validEdges.Add(e); edgeFaces.Add(fa); } } if(validEdges.Count < 1) return null; Vector3[] localVerts = pb.vertices; Vector3[] oNormals = pb.msh.normals; int[] allEdgeIndices = new int[validEdges.Count * 2]; int c = 0; // har har har for(int i = 0; i < validEdges.Count; i++) { allEdgeIndices[c++] = validEdges[i].x; allEdgeIndices[c++] = validEdges[i].y; } List<pb_Edge> extrudedIndices = new List<pb_Edge>(); List<pb_Edge> newEdges = new List<pb_Edge>(); // used to set the editor selection to the newly created edges /// build out new faces around validEdges for(int i = 0; i < validEdges.Count; i++) { pb_Edge edge = validEdges[i]; pb_Face face = edgeFaces[i]; // Averages the normals using only vertices that are on the edge Vector3 xnorm = Norm( edge.x, sharedIndices, allEdgeIndices, oNormals ); Vector3 ynorm = Norm( edge.y, sharedIndices, allEdgeIndices, oNormals ); int x_sharedIndex = sharedIndices.IndexOf(edge.x); int y_sharedIndex = sharedIndices.IndexOf(edge.y); pb_Face newFace = pb.AppendFace( new Vector3[4] { localVerts [ edge.x ], localVerts [ edge.y ], localVerts [ edge.x ] + xnorm.normalized * extrudeDistance, localVerts [ edge.y ] + ynorm.normalized * extrudeDistance }, new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, face.material, new pb_UV(), 0, -1, -1, face.colors[0] ), new int[4] { x_sharedIndex, y_sharedIndex, -1, -1 }); newEdges.Add(new pb_Edge(newFace.indices[3], newFace.indices[4])); extrudedIndices.Add(new pb_Edge(x_sharedIndex, newFace.indices[3])); extrudedIndices.Add(new pb_Edge(y_sharedIndex, newFace.indices[4])); } sharedIndices = pb.sharedIndices; // merge extruded vertex indices with each other for(int i = 0; i < extrudedIndices.Count; i++) { int val = extrudedIndices[i].x; for(int n = 0; n < extrudedIndices.Count; n++) { if(n == i) continue; if(extrudedIndices[n].x == val) { pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, extrudedIndices[n].y, extrudedIndices[i].y); break; } } } pb.SetSharedIndices(sharedIndices); pb.RebuildFaceCaches(); return newEdges.ToArray(); }
/** * Given a face and a point, this will add a vertex to the pb_Object and retriangulate the face. */ public static bool AppendVerticesToFace(this pb_Object pb, pb_Face face, Vector3[] points, Color[] addColors, out pb_Face newFace) { if(!face.IsValid()) { newFace = face; return false; } // First order of business - project face to 2d int[] distinctIndices = face.distinctIndices; Vector3[] verts = pb.GetVertices(distinctIndices); Color[] cols = pbUtil.ValuesWithIndices(pb.colors, distinctIndices); Vector2[] uvs = new Vector2[distinctIndices.Length+points.Length]; System.Array.Copy(pb.GetUVs(distinctIndices), 0, uvs, 0, distinctIndices.Length); // Add the new point Vector3[] t_verts = new Vector3[verts.Length + points.Length]; System.Array.Copy(verts, 0, t_verts, 0, verts.Length); System.Array.Copy(points, 0, t_verts, verts.Length, points.Length); verts = t_verts; // Add the new color Color[] t_col = new Color[cols.Length + addColors.Length]; System.Array.Copy(cols, 0, t_col, 0, cols.Length); System.Array.Copy(addColors, 0, t_col, cols.Length, addColors.Length); cols = t_col; // Get the face normal before modifying the vertex array Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices)); Vector3 projAxis = pb_Math.ProjectionAxisToVector( pb_Math.VectorToProjectionAxis(nrm) ); // Project List<Vector2> plane = new List<Vector2>(pb_Math.PlanarProject(verts, projAxis)); // Save the sharedIndices index for each distinct vertex pb_IntArray[] sharedIndices = pb.sharedIndices; int[] sharedIndex = new int[distinctIndices.Length+points.Length]; for(int i = 0; i < distinctIndices.Length; i++) sharedIndex[i] = sharedIndices.IndexOf(distinctIndices[i]); for(int i = distinctIndices.Length; i < distinctIndices.Length+points.Length; i++) sharedIndex[i] = -1; // add the new vertex to it's own sharedIndex // Triangulate the face with the new point appended int[] tris = Delaunay.Triangulate(plane).ToIntArray(); // Check to make sure the triangulated face is facing the same direction, and flip if not Vector3 del = Vector3.Cross( verts[tris[2]] - verts[tris[0]], verts[tris[1]]-verts[tris[0]]).normalized; if(Vector3.Dot(nrm, del) > 0) System.Array.Reverse(tris); // Build the new face pb_Face triangulated_face = new pb_Face(tris, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, -1, face.manualUV); /** * Attempt to figure out where the new UV point(s) should go (if auto uv'ed face) */ if(triangulated_face.manualUV) { for(int n = distinctIndices.Length; n < uvs.Length; n++) { // these are the two vertices that are split by the new vertex int[] adjacent_vertex = System.Array.FindAll(triangulated_face.edges, x => x.Contains(n)).Select(x => x.x != n ? x.x : x.y).ToArray(); if(adjacent_vertex.Length == 2) { uvs[n] = (uvs[adjacent_vertex[0]] + uvs[adjacent_vertex[1]])/2f; } else { Debug.LogWarning("Failed to find appropriate UV coordinate for new vertex point. Setting face to AutoUV."); triangulated_face.manualUV = false; } } } // Compose new face newFace = pb.AppendFace(verts, cols, uvs, triangulated_face, sharedIndex); // And delete the old pb.DeleteFace(face); return true; }
/** * Given a face and a point, this will add a vertex to the pb_Object and retriangulate the face. */ public static bool AppendVerticesToFace(this pb_Object pb, pb_Face face, List<Vector3> points, out pb_Face newFace) { if(!face.isValid()) { newFace = face; return false; } // First order of business - project face to 2d int[] distinctIndices = face.distinctIndices; Vector3[] verts = pb.GetVertices(distinctIndices); // Get the face normal before modifying the vertex array Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices)); Vector3 projAxis = pb_Math.GetProjectionAxis(nrm).ToVector3(); // Add the new point Vector3[] t_verts = new Vector3[verts.Length + points.Count]; System.Array.Copy(verts, 0, t_verts, 0, verts.Length); System.Array.Copy(points.ToArray(), 0, t_verts, verts.Length, points.Count); verts = t_verts; // Project List<Vector2> plane = new List<Vector2>(pb_Math.VerticesTo2DPoints(verts, projAxis)); // Save the sharedIndices index for each distinct vertex pb_IntArray[] sharedIndices = pb.sharedIndices; int[] sharedIndex = new int[distinctIndices.Length+points.Count]; for(int i = 0; i < distinctIndices.Length; i++) sharedIndex[i] = sharedIndices.IndexOf(distinctIndices[i]); for(int i = distinctIndices.Length; i < distinctIndices.Length+points.Count; i++) sharedIndex[i] = -1; // add the new vertex to it's own sharedIndex // Triangulate the face with the new point appended int[] tris = Delauney.Triangulate(plane).ToIntArray(); // Check to make sure the triangulated face is facing the same direction, and flip if not Vector3 del = Vector3.Cross( verts[tris[2]] - verts[tris[0]], verts[tris[1]]-verts[tris[0]]).normalized; if(Vector3.Dot(nrm, del) > 0) System.Array.Reverse(tris); // Compose new face newFace = pb.AppendFace(verts, new pb_Face(tris, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, -1, face.color), sharedIndex); // And delete the old pb.DeleteFace(face); return true; }
public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b, bool enforcePerimiterEdgesOnly) { pb_IntArray[] sharedIndices = pb.GetSharedIndices(); Dictionary<int, int> lookup = sharedIndices.ToDictionary(); // Check to see if a face already exists if(enforcePerimiterEdgesOnly) { if( pbMeshUtils.GetNeighborFaces(pb, a).Count > 1 || pbMeshUtils.GetNeighborFaces(pb, b).Count > 1 ) { return false; } } foreach(pb_Face face in pb.faces) { if(face.edges.IndexOf(a, lookup) >= 0 && face.edges.IndexOf(b, lookup) >= 0) { Debug.LogWarning("Face already exists between these two edges!"); return false; } } Vector3[] verts = pb.vertices; Vector3[] v; Color[] c; int[] s; pb_UV uvs = new pb_UV(); Material mat = pb_Constant.DefaultMaterial; // Get material and UV stuff from the first edge face foreach(pb_Face face in pb.faces) { if(face.edges.Contains(a)) { uvs = new pb_UV(face.uv); mat = face.material; break; } } // Bridge will form a triangle if( a.Contains(b.x, sharedIndices) || a.Contains(b.y, sharedIndices) ) { v = new Vector3[3]; c = new Color[3]; s = new int[3]; bool axbx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.x) > -1; bool axby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.y) > -1; bool aybx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.x) > -1; bool ayby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.y) > -1; if(axbx) { v[0] = verts[a.x]; c[0] = pb.colors[a.x]; s[0] = sharedIndices.IndexOf(a.x); v[1] = verts[a.y]; c[1] = pb.colors[a.y]; s[1] = sharedIndices.IndexOf(a.y); v[2] = verts[b.y]; c[2] = pb.colors[b.y]; s[2] = sharedIndices.IndexOf(b.y); } else if(axby) { v[0] = verts[a.x]; c[0] = pb.colors[a.x]; s[0] = sharedIndices.IndexOf(a.x); v[1] = verts[a.y]; c[1] = pb.colors[a.y]; s[1] = sharedIndices.IndexOf(a.y); v[2] = verts[b.x]; c[2] = pb.colors[b.x]; s[2] = sharedIndices.IndexOf(b.x); } else if(aybx) { v[0] = verts[a.y]; c[0] = pb.colors[a.y]; s[0] = sharedIndices.IndexOf(a.y); v[1] = verts[a.x]; c[1] = pb.colors[a.x]; s[1] = sharedIndices.IndexOf(a.x); v[2] = verts[b.y]; c[2] = pb.colors[b.y]; s[2] = sharedIndices.IndexOf(b.y); } else if(ayby) { v[0] = verts[a.y]; c[0] = pb.colors[a.y]; s[0] = sharedIndices.IndexOf(a.y); v[1] = verts[a.x]; c[1] = pb.colors[a.x]; s[1] = sharedIndices.IndexOf(a.x); v[2] = verts[b.x]; c[2] = pb.colors[b.x]; s[2] = sharedIndices.IndexOf(b.x); } pb.AppendFace( v, c, new Vector2[v.Length], new pb_Face( axbx || axby ? new int[3] {2, 1, 0} : new int[3] {0, 1, 2}, mat, uvs, 0, -1, -1, false ), s); return true; } // Else, bridge will form a quad v = new Vector3[4]; c = new Color[4]; s = new int[4]; // shared indices index to add to v[0] = verts[a.x]; c[0] = pb.colors[a.x]; s[0] = sharedIndices.IndexOf(a.x); v[1] = verts[a.y]; c[1] = pb.colors[a.y]; s[1] = sharedIndices.IndexOf(a.y); Vector3 nrm = Vector3.Cross( verts[b.x]-verts[a.x], verts[a.y]-verts[a.x] ).normalized; Vector2[] planed = pb_Math.PlanarProject( new Vector3[4] {verts[a.x], verts[a.y], verts[b.x], verts[b.y] }, nrm ); Vector2 ipoint = Vector2.zero; bool interescts = pb_Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint); if(!interescts) { v[2] = verts[b.x]; c[2] = pb.colors[b.x]; s[2] = sharedIndices.IndexOf(b.x); v[3] = verts[b.y]; c[3] = pb.colors[b.y]; s[3] = sharedIndices.IndexOf(b.y); } else { v[2] = verts[b.y]; c[2] = pb.colors[b.y]; s[2] = sharedIndices.IndexOf(b.y); v[3] = verts[b.x]; c[3] = pb.colors[b.x]; s[3] = sharedIndices.IndexOf(b.x); } pb.AppendFace( v, c, new Vector2[v.Length], new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, mat, uvs, 0, -1, -1, false ), s); return true; }
/** * Edge extrusion override */ public static bool Extrude(this pb_Object pb, pb_Edge[] edges, float extrudeDistance, bool extrudeAsGroup, bool enableManifoldExtrude, out pb_Edge[] extrudedEdges) { pb_IntArray[] sharedIndices = pb.sharedIndices; Dictionary<int, int> lookup = sharedIndices.ToDictionary(); List<pb_Edge> validEdges = new List<pb_Edge>(); List<pb_Face> edgeFaces = new List<pb_Face>(); foreach(pb_Edge e in edges) { int faceCount = 0; pb_Face fa = null; foreach(pb_Face f in pb.faces) { if(f.edges.IndexOf(e, lookup) > -1) { fa = f; if(++faceCount > 1) break; } } if(enableManifoldExtrude || faceCount < 2) { validEdges.Add(e); edgeFaces.Add(fa); } } if(validEdges.Count < 1) { extrudedEdges = null; return false; } Vector3[] localVerts = pb.vertices; Vector3[] oNormals = pb.msh.normals; int[] allEdgeIndices = new int[validEdges.Count * 2]; int c = 0; for(int i = 0; i < validEdges.Count; i++) { allEdgeIndices[c++] = validEdges[i].x; allEdgeIndices[c++] = validEdges[i].y; } List<pb_Edge> extrudedIndices = new List<pb_Edge>(); List<pb_Edge> newEdges = new List<pb_Edge>(); // used to set the editor selection to the newly created edges /// build out new faces around validEdges for(int i = 0; i < validEdges.Count; i++) { pb_Edge edge = validEdges[i]; pb_Face face = edgeFaces[i]; // Averages the normals using only vertices that are on the edge Vector3 xnorm = extrudeAsGroup ? Norm( sharedIndices[lookup[edge.x]], allEdgeIndices, oNormals ) : pb_Math.Normal(pb, face); Vector3 ynorm = extrudeAsGroup ? Norm( sharedIndices[lookup[edge.y]], allEdgeIndices, oNormals ) : pb_Math.Normal(pb, face); int x_sharedIndex = lookup[edge.x]; int y_sharedIndex = lookup[edge.y]; pb_Face newFace = pb.AppendFace( new Vector3[4] { localVerts [ edge.x ], localVerts [ edge.y ], localVerts [ edge.x ] + xnorm.normalized * extrudeDistance, localVerts [ edge.y ] + ynorm.normalized * extrudeDistance }, new Color[4] { pb.colors[ edge.x ], pb.colors[ edge.y ], pb.colors[ edge.x ], pb.colors[ edge.y ] }, new Vector2[4], new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, face.material, new pb_UV(), 0, -1, -1, false ), new int[4] { x_sharedIndex, y_sharedIndex, -1, -1 }); newEdges.Add(new pb_Edge(newFace.indices[3], newFace.indices[4])); extrudedIndices.Add(new pb_Edge(x_sharedIndex, newFace.indices[3])); extrudedIndices.Add(new pb_Edge(y_sharedIndex, newFace.indices[4])); } sharedIndices = pb.sharedIndices; // merge extruded vertex indices with each other if(extrudeAsGroup) { for(int i = 0; i < extrudedIndices.Count; i++) { int val = extrudedIndices[i].x; for(int n = 0; n < extrudedIndices.Count; n++) { if(n == i) continue; if(extrudedIndices[n].x == val) { pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, extrudedIndices[n].y, extrudedIndices[i].y); break; } } } } pb.SetSharedIndices(sharedIndices); pb.RebuildFaceCaches(); extrudedEdges = newEdges.ToArray(); return true; }