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(); }