/** * \brief Collapses all passed indices to a single shared index. * */ public static bool MergeVertices(this pb_Object pb, int[] indices) { Vector3[] verts = pb.vertices; Vector3 cen = Vector3.zero; foreach (int i in indices) { cen += verts[i]; } cen /= (float)indices.Length; pb_IntArray[] sharedIndices = pb.sharedIndices; int newIndex = pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, indices); pb.SetSharedIndices(sharedIndices); int firstTriInSharedIndexArr = pb.sharedIndices[newIndex][0]; pb.SetSharedVertexPosition(firstTriInSharedIndexArr, cen); int[] mergedSharedIndex = pb.GetSharedIndices()[newIndex].array; int[] removedIndices = pb.RemoveDegenerateTriangles(); // get a non-deleted index to work with int ind = -1; for (int i = 0; i < mergedSharedIndex.Length; i++) { if (!removedIndices.Contains(mergedSharedIndex[i])) { ind = mergedSharedIndex[i]; } } int t = ind; for (int i = 0; i < removedIndices.Length; i++) { if (ind > removedIndices[i]) { t--; } } pb.ClearSelection(); if (t > -1) { pb.SetSelectedTriangles(new int[1] { t }); } return(true); }
/** * \brief Collapses all passed indices to a single shared index. Retains vertex normals. * */ public static bool MergeVertices(this pb_Object pb, int[] indices, out int collapsedIndex) { Vector3[] verts = pb.vertices; Vector3 cen = Vector3.zero; foreach (int i in indices) { cen += verts[i]; } cen /= (float)indices.Length; pb_IntArray[] sharedIndices = pb.sharedIndices; int newIndex = pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, indices); pb.SetSharedIndices(sharedIndices); pb.SetSharedVertexPosition(newIndex, cen); int[] mergedSharedIndex = pb.GetSharedIndices()[newIndex].array; int[] removedIndices; pb.RemoveDegenerateTriangles(out removedIndices); // get a non-deleted index to work with int ind = -1; for (int i = 0; i < mergedSharedIndex.Length; i++) { if (!removedIndices.Contains(mergedSharedIndex[i])) { ind = mergedSharedIndex[i]; } } int t = ind; for (int i = 0; i < removedIndices.Length; i++) { if (ind > removedIndices[i]) { t--; } } if (t > -1) { collapsedIndex = t; return(true); } else { collapsedIndex = -1; return(false); } }
public pb_SerializableObject(pb_Object pb) { this.vertices = pb.vertices; // Make sure the mesh is valid, and in sync with current pb_Object if(pb.msh == null || pb.msh.vertexCount != pb.vertexCount) { pb_UpgradeKitUtils.RebuildMesh(pb); } this.uv = pb.msh != null ? pb.msh.uv : null; if(pb.msh != null && pb.msh.colors != null && pb.msh.colors.Length == pb.vertexCount) { this.color = pb.msh.colors; } else { this.color = new Color[pb.vertexCount]; for(int i = 0; i < this.color.Length; i++) this.color[i] = Color.white; } this.faces = pb.faces; this.sharedIndices = (int[][])pb.GetSharedIndices().ToArray(); PropertyInfo prop_uv = pb.GetType().GetProperty("sharedIndicesUV", BindingFlags.Instance | BindingFlags.Public); if(prop_uv != null) { var val = prop_uv.GetValue(pb, null); if(val != null) { pb_IntArray[] sharedUvs = (pb_IntArray[])val; this.sharedIndicesUV = (int[][])sharedUvs.ToArray(); } else { this.sharedIndicesUV = new int[0][]; } } else { this.sharedIndicesUV = new int[0][]; } PropertyInfo prop_userCollisions = pb.GetType().GetProperty("userCollisions", BindingFlags.Instance | BindingFlags.Public); userCollisions = prop_userCollisions == null ? false : (bool) prop_userCollisions.GetValue(pb, null); }
/** * \brief Duplicates and returns the passed pb_Object. * @param pb The pb_Object to duplicate. * \returns A unique copy of the passed pb_Object. */ public static pb_Object InitWithObject(pb_Object pb) { Vector3[] v = new Vector3[pb.vertexCount]; System.Array.Copy(pb.vertices, v, pb.vertexCount); pb_Face[] f = new pb_Face[pb.faces.Length]; for(int i = 0; i < f.Length; i++) f[i] = new pb_Face(pb.faces[i]); pb_Object p = CreateInstanceWithVerticesFacesSharedIndices(v, f, pb.GetSharedIndices()); p.gameObject.name = pb.gameObject.name + "-clone"; return p; }
/** * \brief Duplicates and returns the passed pb_Object. * @param pb The pb_Object to duplicate. * \returns A unique copy of the passed pb_Object. */ public static pb_Object InitWithObject(pb_Object pb) { Vector3[] v = new Vector3[pb.vertexCount]; System.Array.Copy(pb.vertices, v, pb.vertexCount); pb_Face[] f = new pb_Face[pb.faces.Length]; for (int i = 0; i < f.Length; i++) { f[i] = new pb_Face(pb.faces[i]); } pb_Object p = CreateInstanceWithVerticesFacesSharedIndices(v, f, pb.GetSharedIndices()); p.gameObject.name = pb.gameObject.name + "-clone"; return(p); }
/** * \brief Duplicates and returns the passed pb_Object. * @param pb The pb_Object to duplicate. * \returns A unique copy of the passed pb_Object. */ public static pb_Object InitWithObject(pb_Object pb) { Vector3[] v = new Vector3[pb.vertexCount]; System.Array.Copy(pb.vertices, v, pb.vertexCount); Vector2[] u = new Vector2[pb.vertexCount]; System.Array.Copy(pb.uv, u, pb.vertexCount); Color[] c = new Color[pb.vertexCount]; System.Array.Copy(pb.colors, c, pb.vertexCount); pb_Face[] f = new pb_Face[pb.faces.Length]; for(int i = 0; i < f.Length; i++) f[i] = new pb_Face(pb.faces[i]); pb_Object p = CreateInstanceWithElements(v, u, c, f, pb.GetSharedIndices(), pb.GetSharedIndicesUV()); p.gameObject.name = pb.gameObject.name + "-clone"; return p; }
/** * \brief Duplicates and returns the passed pb_Object. * @param pb The pb_Object to duplicate. * \returns A unique copy of the passed pb_Object. */ public static pb_Object InitWithObject(pb_Object pb) { Vector3[] v = new Vector3[pb.vertexCount]; System.Array.Copy(pb.vertices, v, pb.vertexCount); Vector2[] u = new Vector2[pb.vertexCount]; System.Array.Copy(pb.uv, u, pb.vertexCount); Color[] c = new Color[pb.vertexCount]; System.Array.Copy(pb.colors, c, pb.vertexCount); pb_Face[] f = new pb_Face[pb.faces.Length]; for (int i = 0; i < f.Length; i++) { f[i] = new pb_Face(pb.faces[i]); } pb_Object p = CreateInstanceWithElements(v, u, c, f, pb.GetSharedIndices(), pb.GetSharedIndicesUV()); p.gameObject.name = pb.gameObject.name + "-clone"; return(p); }
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); }
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); }
/** * Extrudes passed faces on their normal axis using extrudeDistance. */ public static bool Extrude(this pb_Object pb, pb_Face[] faces, float extrudeDistance, bool extrudeAsGroup, out pb_Face[] appendedFaces) { appendedFaces = null; if (faces == null || faces.Length < 1) { return(false); } pb_IntArray[] sharedIndices = pb.GetSharedIndices(); Dictionary <int, int> lookup = sharedIndices.ToDictionary(); Vector3[] localVerts = pb.vertices; pb_Edge[][] perimeterEdges = extrudeAsGroup ? new pb_Edge[1][] { pbMeshUtils.GetPerimeterEdges(pb, lookup, faces).ToArray() } : faces.Select(x => x.edges).ToArray(); if (perimeterEdges == null || perimeterEdges.Length < 1 || (extrudeAsGroup && perimeterEdges[0].Length < 3)) { Debug.LogWarning("No perimeter edges found. Try deselecting and reselecting this object and trying again."); return(false); } pb_Face[][] edgeFaces = new pb_Face[perimeterEdges.Length][]; // can't assume faces and perimiter edges will be 1:1 - so calculate perimeters then extract face information int[][] allEdgeIndices = new int[perimeterEdges.Length][]; int c = 0; for (int i = 0; i < perimeterEdges.Length; i++) { c = 0; allEdgeIndices[i] = new int[perimeterEdges[i].Length * 2]; edgeFaces[i] = new pb_Face[perimeterEdges[i].Length]; for (int n = 0; n < perimeterEdges[i].Length; n++) { // gets the faces associated with each perimeter edge foreach (pb_Face face in faces) { if (face.edges.Contains(perimeterEdges[i][n])) { edgeFaces[i][n] = face; break; } } allEdgeIndices[i][c++] = perimeterEdges[i][n].x; allEdgeIndices[i][c++] = perimeterEdges[i][n].y; } } List <pb_Edge>[] extrudedIndices = new List <pb_Edge> [perimeterEdges.Length]; Vector3[] normals = pb.msh.normals; List <Vector3[]> append_vertices = new List <Vector3[]>(); List <Color[]> append_color = new List <Color[]>(); List <Vector2[]> append_uv = new List <Vector2[]>(); List <pb_Face> append_face = new List <pb_Face>(); List <int[]> append_shared = new List <int[]>(); /// build out new faces around edges for (int i = 0; i < perimeterEdges.Length; i++) { extrudedIndices[i] = new List <pb_Edge>(); for (int n = 0; n < perimeterEdges[i].Length; n++) { pb_Edge edge = perimeterEdges[i][n]; pb_Face face = edgeFaces[i][n]; // 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 (Mathf.Abs(extrudeDistance) > Mathf.Epsilon) { if (!extrudeAsGroup) { xnorm = pb_Math.Normal(localVerts[face.indices[0]], localVerts[face.indices[1]], localVerts[face.indices[2]]); ynorm = xnorm; } else { xnorm = Norm(sharedIndices[lookup[edge.x]], allEdgeIndices[i], normals); ynorm = Norm(sharedIndices[lookup[edge.y]], allEdgeIndices[i], normals); } } int x_sharedIndex = lookup[edge.x]; int y_sharedIndex = lookup[edge.y]; // this could be condensed to a single call with an array of new faces append_vertices.Add(new Vector3[] { localVerts [edge.x], localVerts [edge.y], localVerts [edge.x] + xnorm.normalized * extrudeDistance, localVerts [edge.y] + ynorm.normalized * extrudeDistance }); append_color.Add(new Color[] { pb.colors[edge.x], pb.colors[edge.y], pb.colors[edge.x], pb.colors[edge.y] }); append_uv.Add(new Vector2[4]); append_face.Add(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 false) // manualUV flag ); append_shared.Add(new int[4] { x_sharedIndex, y_sharedIndex, -1, -1 }); extrudedIndices[i].Add(new pb_Edge(x_sharedIndex, -1)); extrudedIndices[i].Add(new pb_Edge(y_sharedIndex, -1)); } } appendedFaces = pb.AppendFaces(append_vertices.ToArray(), append_color.ToArray(), append_uv.ToArray(), append_face.ToArray(), append_shared.ToArray()); // x = shared index, y = triangle (only known once faces are appended to pb_Object) for (int i = 0, f = 0; i < extrudedIndices.Length; i++) { for (int n = 0; n < extrudedIndices[i].Count; n += 2) { extrudedIndices[i][n + 0].y = appendedFaces[f].indices[2]; extrudedIndices[i][n + 1].y = appendedFaces[f++].indices[4]; } } pb_IntArray[] si = pb.sharedIndices; // leave the sharedIndices copy alone since we need the un-altered version later Dictionary <int, int> welds = si.ToDictionary(); // Weld side-wall top vertices together, both grouped and non-grouped need this. for (int f = 0; f < extrudedIndices.Length; f++) { for (int i = 0; i < extrudedIndices[f].Count - 1; i++) { int val = extrudedIndices[f][i].x; for (int n = i + 1; n < extrudedIndices[f].Count; n++) { if (extrudedIndices[f][n].x == val) { welds[extrudedIndices[f][i].y] = welds[extrudedIndices[f][n].y]; break; } } } } localVerts = pb.vertices; // Remove smoothing and texture group flags foreach (pb_Face f in faces) { f.SetSmoothingGroup(-1); f.textureGroup = -1; } if (extrudeAsGroup) { foreach (pb_Face f in faces) { int[] distinctIndices = f.distinctIndices; // Merge in-group face seams foreach (int ind in distinctIndices) { int oldIndex = si.IndexOf(ind); for (int n = 0; n < allEdgeIndices.Length; n++) { for (int i = 0; i < extrudedIndices[n].Count; i++) { if (oldIndex == extrudedIndices[n][i].x) { welds[ind] = welds[extrudedIndices[n][i].y]; break; } } } } } } else /** * If extruding as separate faces, weld each face to the tops of the bridging faces */ { // Dictionary<int, int> hold = si.ToDictionary(); for (int i = 0; i < edgeFaces.Length; i++) { foreach (int n in pb_Face.AllTrianglesDistinct(edgeFaces[i])) { int old_si_index = lookup[n]; int match = extrudedIndices[i].FindIndex(x => x.x == old_si_index); if (match < 0) { continue; } int match_tri_index = extrudedIndices[i][match].y; if (welds.ContainsKey(match_tri_index)) { welds[n] = welds[match_tri_index]; } } } } si = welds.ToSharedIndices(); pb.SplitUVs(pb_Face.AllTriangles(faces)); /** * Move the inside faces to the top of the extrusion * * This is a separate loop cause the one above this must completely merge all sharedindices prior to * checking the normal averages * */ Vector3 norm = Vector3.zero; int[] allIndices = pb_Face.AllTrianglesDistinct(faces); foreach (pb_Face f in faces) { if (!extrudeAsGroup) { norm = pb_Math.Normal(localVerts[f.indices[0]], localVerts[f.indices[1]], localVerts[f.indices[2]]); } foreach (int ind in f.distinctIndices) { if (extrudeAsGroup) { norm = Norm(sharedIndices[lookup[ind]], allIndices, normals); } localVerts[ind] += norm.normalized * extrudeDistance; } } // Test the winding of the first pulled face, and reverse if it's ccw if (pb.GetWindingOrder(faces[0]) == WindingOrder.CounterClockwise) { foreach (pb_Face face in appendedFaces) { face.ReverseIndices(); } } pb.SetSharedIndices(si); pb.SetVertices(localVerts); return(true); }