/** * Removes faces from a pb_Object. Overrides available for pb_Face[] and int[] faceIndices. handles * all the sharedIndices moving stuff for you. */ public static void DeleteFaces(this pb_Object pb, int[] faceIndices) { pb_Face[] faces = new pb_Face[faceIndices.Length]; for (int i = 0; i < faces.Length; i++) { faces[i] = pb.faces[faceIndices[i]]; } int[] distInd = pb_Face.AllTrianglesDistinct(faces); Vector3[] verts = pb.vertices.RemoveAt(distInd); Color[] cols = pb.colors.RemoveAt(distInd); Vector2[] uvs = pb.uv.RemoveAt(distInd); pb_Face[] nFaces = pb.faces.RemoveAt(faceIndices); // shift all other face indices down to account for moved vertex positions for (int i = 0; i < nFaces.Length; i++) { int[] tris = nFaces[i].indices; for (int n = 0; n < tris.Length; n++) { int sub = 0; for (int d = 0; d < distInd.Length; d++) { if (tris[n] > distInd[d]) { sub++; } } tris[n] -= sub; } nFaces[i].SetIndices(tris); } // shift all other face indices in the shared index array down to account for moved vertex positions pb_IntArray[] si = pb.sharedIndices; pb_IntArray[] si_uv = pb.sharedIndicesUV; pb_IntArrayUtility.RemoveValuesAndShift(ref si, distInd); pb_IntArrayUtility.RemoveValuesAndShift(ref si_uv, distInd); pb.SetSharedIndices(si); pb.SetSharedIndicesUV(si_uv); pb.SetVertices(verts); pb.SetColors(cols); pb.SetUV(uvs); pb.SetFaces(nFaces); pb.RebuildFaceCaches(); }
/** * Removes faces from a pb_Object. Overrides available for pb_Face[] and int[] faceIndices. handles * all the sharedIndices moving stuff for you. */ public static void DeleteFaces(this pb_Object pb, pb_Face[] faces) { int[] f_ind = new int[faces.Length]; // test for triangle array equality, not reference equality for (int i = 0; i < faces.Length; i++) { f_ind[i] = System.Array.IndexOf(pb.faces, faces[i]); } List <int> indices_to_remove = new List <int>(pb_Face.AllTrianglesDistinct(faces)); indices_to_remove.Sort(); Vector3[] verts = pb.vertices.SortedRemoveAt(indices_to_remove); Color[] cols = pb.colors.SortedRemoveAt(indices_to_remove); Vector2[] uvs = pb.uv.SortedRemoveAt(indices_to_remove); pb_Face[] nFaces = pb.faces.RemoveAt(f_ind); // shift all other face indices down to account for moved vertex positions for (int i = 0; i < nFaces.Length; i++) { int[] tris = nFaces[i].indices; for (int n = 0; n < tris.Length; n++) { int index = pbUtil.NearestIndexPriorToValue(indices_to_remove, tris[n]); // add 1 because index is zero based tris[n] -= index + 1; } nFaces[i].SetIndices(tris); } // shift all other face indices in the shared index array down to account for moved vertex positions pb_IntArray[] si = pb.sharedIndices; pb_IntArray[] si_uv = pb.sharedIndicesUV; pb_IntArrayUtility.RemoveValuesAndShift(ref si, indices_to_remove); pb_IntArrayUtility.RemoveValuesAndShift(ref si_uv, indices_to_remove); pb.SetSharedIndices(si); pb.SetSharedIndicesUV(si_uv); pb.SetVertices(verts); pb.SetColors(cols); pb.SetUV(uvs); pb.SetFaces(nFaces); pb.RebuildFaceCaches(); }
/** * Deletes the vertices from the passed index array. Handles rebuilding the sharedIndices array. */ public static void DeleteVerticesWithIndices(this pb_Object pb, int[] distInd) { Vector3[] verts = pb.vertices; Color[] cols = pb.colors; Vector2[] uvs = pb.uv; verts = verts.RemoveAt(distInd); cols = cols.RemoveAt(distInd); uvs = uvs.RemoveAt(distInd); pb_Face[] nFaces = pb.faces; // shift all other face indices down to account for moved vertex positions for (int i = 0; i < nFaces.Length; i++) { int[] tris = nFaces[i].indices; for (int n = 0; n < tris.Length; n++) { int sub = 0; for (int d = 0; d < distInd.Length; d++) { if (tris[n] > distInd[d]) { sub++; } } tris[n] -= sub; } nFaces[i].SetIndices(tris); } // shift all other face indices in the shared index array down to account for moved vertex positions pb_IntArray[] si = pb.sharedIndices; pb_IntArrayUtility.RemoveValuesAndShift(ref si, distInd); pb.SetSharedIndices(si); pb.SetVertices(verts); pb.SetColors(cols); pb.SetUV(uvs); pb.SetFaces(nFaces); pb.RebuildFaceCaches(); pb.ToMesh(); }
/** * Removes faces from a pb_Object. Overrides available for pb_Face[] and int[] faceIndices. handles * all the sharedIndices moving stuff for you. */ public static void DeleteFaces(this pb_Object pb, pb_Face[] faces) { int[] f_ind = new int[faces.Length]; for (int i = 0; i < faces.Length; i++) { f_ind[i] = System.Array.IndexOf(pb.faces, faces[i]); } int[] distInd = pb_Face.AllTrianglesDistinct(faces); Vector3[] verts = pb.vertices.RemoveAt(distInd); pb_Face[] nFaces = pb.faces.RemoveAt(f_ind); // shift all other face indices down to account for moved vertex positions for (int i = 0; i < nFaces.Length; i++) { int[] tris = nFaces[i].indices; for (int n = 0; n < tris.Length; n++) { int sub = 0; for (int d = 0; d < distInd.Length; d++) { if (tris[n] > distInd[d]) { sub++; } } tris[n] -= sub; } nFaces[i].SetIndices(tris); } // shift all other face indices in the shared index array down to account for moved vertex positions pb_IntArray[] si = pb.sharedIndices; pb_IntArrayUtility.RemoveValuesAndShift(ref si, distInd); pb.SetSharedIndices(si); pb.SetVertices(verts); pb.SetFaces(nFaces); pb.RebuildFaceCaches(); pb.ToMesh(); }
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()); }
/** * \brief Returns a pb_Object prism with the passed size. * @param size Size to apply to generated object. * \returns New #pb_Object. */ public static pb_Object PrismGenerator(Vector3 size) { Vector3[] template = new Vector3[6] { Vector3.Scale(new Vector3(-.5f, 0f, -.5f), size), Vector3.Scale(new Vector3(.5f, 0f, -.5f), size), Vector3.Scale(new Vector3(0f, .5f, -.5f), size), Vector3.Scale(new Vector3(-.5f, 0f, .5f), size), Vector3.Scale(new Vector3(0.5f, 0f, .5f), size), Vector3.Scale(new Vector3(0f, .5f, .5f), size) }; Vector3[] v = new Vector3[18] { template[0], // 0 front template[1], // 1 template[2], // 2 template[1], // 3 right side template[4], // 4 template[2], // 5 template[5], // 6 template[4], // 7 back side template[3], // 8 template[5], // 9 template[3], // 10 left side template[0], // 11 template[5], // 12 template[2], // 13 template[0], // 14 // bottom template[1], // 15 template[3], // 16 template[4] // 17 }; pb_Face[] f = new pb_Face[5] { new pb_Face(new int[3] { 2, 1, 0 }), // x new pb_Face(new int[6] { 5, 4, 3, 5, 6, 4 }), // x new pb_Face(new int[3] { 9, 8, 7 }), new pb_Face(new int[6] { 12, 11, 10, 12, 13, 11 }), new pb_Face(new int[6] { 14, 15, 16, 15, 17, 16 }) }; pb_Object pb = ProBuilder.CreateObjectWithVerticesFaces(v, f); pb.RebuildFaceCaches(); pb.SetName("Prism"); return(pb); }