public bool ClickShortcutCheck(EventModifiers em, pb_Object pb, pb_Face quad) { // if(em == (EventModifiers.Control | EventModifiers.Shift | EventModifiers.Alt)) // { // Continue(pb, quad); // pb_Editor_Utility.ShowNotification("Continue UV"); // return true; // } if (em == (EventModifiers.Control | EventModifiers.Shift)) { ApplyMaterial(pb, quad, queuedMat); RepaintSceneViews(); pb_Editor_Utility.ShowNotification("Quick Apply Material"); return(true); } if (em == (EventModifiers.Control)) { pb.SetFaceUV(quad, new pb_UV(uv_gui)); RepaintSceneViews(); pb_Editor_Utility.ShowNotification("Copy UV Settings"); return(true); } return(false); }
private void Update() { float num = Time.time * speed; Vector3 position = new Vector3(Mathf.PerlinNoise(num, num) * travel, 2f, Mathf.PerlinNoise(num + 1f, num + 1f) * travel); base.transform.position = position; if (target == null) { Debug.LogWarning("Missing the ProBuilder Mesh target!"); return; } Vector3 a = target.transform.InverseTransformPoint(base.transform.position); if (nearest != null) { target.SetFaceColor(nearest, Color.white); } int num2 = target.faces.Length; float num3 = float.PositiveInfinity; nearest = target.faces[0]; for (int i = 0; i < num2; i++) { float num4 = Vector3.Distance(a, FaceCenter(target, target.faces[i])); if (num4 < num3) { num3 = num4; nearest = target.faces[i]; } } target.SetFaceColor(nearest, Color.blue); target.RefreshColors(); }
void ExtrudeEdge() { pb_Face sourceFace = lastExtrudedFace; // fetch a random perimeter edge connected to the last face extruded List <pb_WingedEdge> wings = pb_WingedEdge.GetWingedEdges(pb); IEnumerable <pb_WingedEdge> sourceWings = wings.Where(x => x.face == sourceFace); List <pb_Edge> nonManifoldEdges = sourceWings.Where(x => x.opposite == null).Select(y => y.edge.local).ToList(); int rand = (int)Random.Range(0, nonManifoldEdges.Count); pb_Edge sourceEdge = nonManifoldEdges[rand]; // get the direction this edge should extrude in Vector3 dir = ((pb.vertices[sourceEdge.x] + pb.vertices[sourceEdge.y]) * .5f) - sourceFace.distinctIndices.Average(x => pb.vertices[x]); dir.Normalize(); // this will be populated with the extruded edge pb_Edge[] extrudedEdges; // perform extrusion pb.Extrude(new pb_Edge[] { sourceEdge }, 0f, false, true, out extrudedEdges); // get the last extruded face lastExtrudedFace = pb.faces.Last(); // translate the vertices- pb.TranslateVertices(extrudedEdges[0].ToArray(), dir * distance); // rebuild mesh with new geometry added by extrude pb.ToMesh(); // rebuild mesh normals, textures, collisions, etc pb.Refresh(); }
public bool FaceCheck(Vector3 pos) { Ray ray = Camera.main.ScreenPointToRay (pos); RaycastHit hit; if( Physics.Raycast(ray.origin, ray.direction, out hit)) { pb_Object hitpb = hit.transform.gameObject.GetComponent<pb_Object>(); if(hitpb == null) return false; Mesh m = hitpb.msh; int[] tri = new int[3] { m.triangles[hit.triangleIndex * 3 + 0], m.triangles[hit.triangleIndex * 3 + 1], m.triangles[hit.triangleIndex * 3 + 2] }; pb = hitpb; quad = hitpb.QuadWithTriangle(tri); return true; } return false; }
public static bool HiddenFace(pb_Object pb, pb_Face q, float dist) { // Grab the face normal Vector3 dir = pbUtil.PlaneNormal(pb.VerticesInWorldSpace(q)); // And also the center of the face Vector3 orig = pb.QuadCenter(q); // Case a ray from the center of the face out in the normal direction. // If an object is hit, return true (that this face is hidden), otherwise // return false. This is pretty simplistic and doesn't account for a lot // of "gotchas", but it ought to serve as a fairly decent jumping off point // for NoDrawing a dense level. RaycastHit hit; if(Physics.Raycast(orig, dir, out hit, dist)) { // We've hit something. Now check to see if it is a ProBuilder object, // and if so, make sure it's a visblocking brush. pb_Entity ent = hit.transform.GetComponent<pb_Entity>(); if(ent != null) { if(ent.entityType == ProBuilder.EntityType.Brush || ent.entityType == ProBuilder.EntityType.Occluder) return true; // it's a brush, blocks vision, return true else return false; // not a vis blocking brush } } // It ain't a ProBuilder object of the entity type Brush or Occluder (world brush) return false; }
/** * \brief Call this to ensure that the mesh is unique. Basically performs a DeepCopy and assigns back to self. */ public void MakeUnique() { pb_Face[] q = new pb_Face[_faces.Length]; for (int i = 0; i < q.Length; i++) { q[i] = new pb_Face(_faces[i]); } pb_IntArray[] sv = new pb_IntArray[_sharedIndices.Length]; System.Array.Copy(_sharedIndices, sv, sv.Length); SetSharedIndices(sv); SetFaces(q); Vector3[] v = new Vector3[vertexCount]; System.Array.Copy(_vertices, v, vertexCount); SetVertices(v); if (_uv != null && _uv.Length == vertexCount) { Vector2[] u = new Vector2[vertexCount]; System.Array.Copy(_uv, u, vertexCount); SetUV(u); } msh = pbUtil.DeepCopyMesh(msh); ToMesh(); Refresh(); }
bool FaceRaycast(Vector2 mouse, out pb_Object pb, out pb_Face face) { var ray = Camera.main.ScreenPointToRay(mouse); RaycastHit rayHit; if (Physics.Raycast(ray.origin, ray.direction, out rayHit)) { pb = rayHit.transform.gameObject.GetComponent <pb_Object>(); if (pb == null) { face = null; return(false); } Mesh m = pb.GetComponent <MeshFilter>().sharedMesh; int[] tri = new int[3] { m.triangles[rayHit.triangleIndex * 3 + 0], m.triangles[rayHit.triangleIndex * 3 + 1], m.triangles[rayHit.triangleIndex * 3 + 2] }; return(pb.FaceWithTriangle(tri, out face)); } pb = null; face = null; return(false); }
private void GeometryWithPoints(Vector3[] v) { // Wrap in faces pb_Face[] f = new pb_Face[v.Length / 4]; for (int i = 0; i < v.Length; i += 4) { f[i / 4] = new pb_Face(new int[6] { i + 0, i + 1, i + 2, i + 1, i + 3, i + 2 }, pb_Constant.DefaultMaterial, new pb_UV(), 0, -1, -1, false); } SetVertices(v); SetUV(new Vector2[v.Length]); SetColors(pbUtil.FilledArray <Color>(Color.white, v.Length)); SetFaces(f); SetSharedIndices(pb_IntArrayUtility.ExtractSharedIndices(v)); ToMesh(); Refresh(); }
/** * \brief Returns all connected faces. */ public static List<pb_Face> GetConnectedFaces(pb_Object pb, pb_Face[] selFaces) { int len = selFaces.Length; List<pb_Face> faces = new List<pb_Face>(); pb_IntArray[] sharedIndices = pb.sharedIndices; pb_Edge[][] sharedEdges = new pb_Edge[len][]; for(int i = 0; i < len; i++) sharedEdges[i] = pb_Edge.GetUniversalEdges(selFaces[i].edges, sharedIndices); for(int i = 0; i < pb.faces.Length; i++) { pb_Edge[] faceEdges = pb_Edge.GetUniversalEdges(pb.faces[i].edges, sharedIndices); for(int j = 0; j < len; j++) { int ind = faceEdges.ContainsMatch(sharedEdges[j]); if(ind > -1) faces.Add(pb.faces[i]); } } return faces; }
/** * Returns all faces that share an edge with originFace. If calling multiple times, use the variation that * accepts a dictionary lookup to save to the cost of generating it each call. */ public static List <pb_Face> GetNeighborFaces(pb_Object pb, pb_Face originFace) { return(GetNeighborFaces(pb, pb.sharedIndices.ToDictionary(), new List <pb_Face>() { originFace }, originFace)); }
public bool FaceCheck(Vector3 pos) { Ray ray = Camera.main.ScreenPointToRay(pos); RaycastHit hit; if (Physics.Raycast(ray.origin, ray.direction, out hit)) { pb_Object hitpb = hit.transform.gameObject.GetComponent <pb_Object>(); if (hitpb == null) { return(false); } Mesh m = hitpb.msh; int[] tri = new int[3] { m.triangles[hit.triangleIndex * 3 + 0], m.triangles[hit.triangleIndex * 3 + 1], m.triangles[hit.triangleIndex * 3 + 2] }; pb = hitpb; quad = hitpb.QuadWithTriangle(tri); return(true); } return(false); }
/** * Returns the average of each vertex position in a face. * In local space. */ private Vector3 FaceCenter(pb_Object pb, pb_Face face) { Vector3[] vertices = pb.vertices; Vector3 average = Vector3.zero; // face holds triangle data. distinctIndices is a // cached collection of the distinct indices that // make up the triangles. Ex: // tris = {0, 1, 2, 2, 3, 0} // distinct indices = {0, 1, 2, 3} foreach (int index in face.distinctIndices) { average.x += vertices[index].x; average.y += vertices[index].y; average.z += vertices[index].z; } float len = (float)face.distinctIndices.Length; average.x /= len; average.y /= len; average.z /= len; return(average); }
private void GeometryWithPoints(Vector3[] v) { // Wrap in faces pb_Face[] f = new pb_Face[v.Length / 4]; for (int i = 0; i < v.Length; i += 4) { f[i / 4] = new pb_Face(new int[6] { i + 0, i + 1, i + 2, i + 1, i + 3, i + 2 }, pb_Constant.DefaultMaterial, new pb_UV(), 0, -1, -1, (Color32)Color.white); } SetVertices(v); SetFaces(f); SetSharedIndices(ExtractSharedIndices()); /* Set probuilder stuff type */ _shape = Shape.Cube; ToMesh(); Refresh(); }
/** * Provided two faces, this method will attempt to project @f2 and align its size, rotation, and position * to match the shared edge on f1. Returns true on success, false otherwise. */ public static bool AutoStitch(pb_Object pb, pb_Face f1, pb_Face f2) { // Cache shared indices (we gon' use 'em a lot) Dictionary <int, int> sharedIndices = pb.sharedIndices.ToDictionary(); for (int i = 0; i < f1.edges.Length; i++) { // find a matching edge int ind = f2.edges.IndexOf(f1.edges[i], sharedIndices); if (ind > -1) { // First, project the second face pbUVOps.ProjectFacesAuto(pb, new pb_Face[] { f2 }); // Use the first first projected as the starting point // and match the vertices f1.manualUV = true; f2.manualUV = true; f1.textureGroup = -1; f2.textureGroup = -1; AlignEdges(pb, f1, f2, f1.edges[i], f2.edges[ind]); return(true); } } // no matching edge found return(false); }
/** * Returns all faces that share an edge with originFace */ public static List <pb_Face> GetConnectedFaces(pb_Object pb, pb_Face originFace) { List <pb_Face> faces = new List <pb_Face>(); pb_IntArray[] sharedIndices = pb.sharedIndices; pb_Edge[] sharedEdges = pb_Edge.GetUniversalEdges(originFace.edges, sharedIndices); for (int i = 0; i < pb.faces.Length; i++) { if (pb.faces[i] == originFace) { continue; } pb_Edge[] faceEdges = pb_Edge.GetUniversalEdges(pb.faces[i].edges, sharedIndices); int ind = faceEdges.ContainsMatch(sharedEdges); if (ind > -1) { faces.Add(pb.faces[i]); } } return(faces); }
public static bool HiddenFace(pb_Object pb, pb_Face q, float dist) { // Grab the face normal Vector3 dir = pb_Math.PlaneNormal(pb.VerticesInWorldSpace(q)); // If casting from the center of the plane hits, chekc the rest of the points for collisions Vector3 orig = pb.FaceCenter(q); bool hidden = true; Transform hitObj = RaycastFaceCheck(orig, dir, dist, null); if (hitObj != null) { Vector3[] v = pb.VerticesInWorldSpace(q.indices); for (int i = 0; i < v.Length; i++) { if (null == RaycastFaceCheck(v[i], dir, dist, hitObj)) { hidden = false; break; } } } else { hidden = false; } return(hidden); }
void RefreshSelectedFacePreview() { pb_Face face = new pb_Face(currentSelection.face); // Copy the currently selected face face.ShiftIndicesToZero(); // Shift the selected face indices to zero // Copy the currently selected vertices in world space. // World space so that we don't have to apply transforms // to match the current selection. Vector3[] verts = currentSelection.pb.VerticesInWorldSpace(currentSelection.face.distinctIndices); // Now go through and move the verts we just grabbed out about .1m from the original face. Vector3 normal = pb_Math.Normal(verts); for (int i = 0; i < verts.Length; i++) { verts[i] += normal.normalized * .01f; } if (preview) { Destroy(preview.gameObject); } preview = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[1] { face }); preview.SetFaceMaterial(preview.faces, previewMaterial); preview.ToMesh(); preview.Refresh(); }
public static bool HiddenFace(pb_Object pb, pb_Face q, float dist) { // Grab the face normal Vector3 dir = pb_Math.Normal(pb.VerticesInWorldSpace(q.indices)); // If casting from the center of the plane hits, chekc the rest of the points for collisions Vector3 orig = pb.transform.TransformPoint(pb_Math.Average(pb.GetVertices(q))); bool hidden = true; Transform hitObj = RaycastFaceCheck(orig, dir, dist, null); if(hitObj != null) { Vector3[] v = pb.VerticesInWorldSpace(q.indices); for(int i = 0; i < v.Length; i++) { if(null == RaycastFaceCheck(v[i], dir, dist, hitObj)) { hidden = false; break; } } } else hidden = false; return hidden; }
/** * \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); }
/** * Iterates through face edges and builds a list using the opposite edge. * @todo Lots of slow stuff in here */ public static pb_Edge[] GetEdgeRing(pb_Object pb, pb_Edge[] edges) { List <pb_Edge> usedEdges = new List <pb_Edge>(); Dictionary <int, int> lookup = pb.sharedIndices.ToDictionary(); foreach (pb_Edge e in edges) { List <pb_Face> origFace; List <pb_Edge> origEdge; // ValidFaceAndEdgeWithEdge will return false if < 1 face and edge combo is found. if (!ValidFaceAndEdgeWithEdge(pb, e, lookup, out origFace, out origEdge)) { continue; } // Only add the initial edge once usedEdges.Add(origEdge[0]); pb_Face opFace; pb_Edge opEdge; bool superBreak = false; for (int i = 0; i < origFace.Count; i++) { pb_Face curFace = origFace[i]; pb_Edge curEdge = origEdge[i]; while (GetOppositeEdge(pb, curFace, curEdge, lookup, out opFace, out opEdge)) { curFace = opFace; curEdge = opEdge; usedEdges.Add(curEdge); if (curFace == null) { break; } if (curFace == origFace[i]) { superBreak = true; break; } } if (superBreak) { break; } } } pb_Edge[] dist = pb_Edge.GetUniversalEdges(usedEdges.ToArray(), lookup); return(pb_Edge.GetLocalEdges_Fast(dist.Distinct().ToArray(), pb.sharedIndices)); }
public static bool SubdivideFace(this pb_Object pb, pb_Face[] faces, out pb_Face[] splitFaces) { List<EdgeConnection> split = new List<EdgeConnection>(); foreach(pb_Face face in pb.SelectedFaces) split.Add(new EdgeConnection(face, new List<pb_Edge>(face.edges))); return pb.ConnectEdges(split, out splitFaces); }
/** * \brief Flips the winding order for the entire mesh. */ // public static void ReverseWindingOrder(this pb_Object pb) // { // for(int i = 0; i < pb.faces.Length; i++) // pb.faces[i].ReverseIndices(); // pb.ToMesh(); // pb.Refresh(); // } /** * \brief Reverse the winding order for each passed #pb_Face. * @param faces The faces to apply normal flippin' to. * \returns Nothing. No soup for you. * \sa SelectedFaces pb_Face */ public static void ReverseWindingOrder(this pb_Object pb, pb_Face[] faces) { for(int i = 0; i < faces.Length; i++) faces[i].ReverseIndices(); pb.ToMesh(); pb.Refresh(); }
/** * \brief Given an array of "donors", this method returns a merged #pb_Object. */ public static bool CombineObjects(pb_Object[] pbs, out pb_Object combined) { combined = null; if (pbs.Length < 1) { return(false); } List <Vector3> v = new List <Vector3>(); List <pb_Face> f = new List <pb_Face>(); List <pb_IntArray> s = new List <pb_IntArray>(); foreach (pb_Object pb in pbs) { int vertexCount = v.Count; // Vertices { v.AddRange(pb.VerticesInWorldSpace()); } // Faces { pb_Face[] faces = new pb_Face[pb.faces.Length]; for (int i = 0; i < faces.Length; i++) { faces[i] = new pb_Face(pb.faces[i]); faces[i].ShiftIndices(vertexCount); faces[i].RebuildCaches(); } f.AddRange(faces); } // Shared Indices { pb_IntArray[] si = pb.GetSharedIndices(); for (int i = 0; i < si.Length; i++) { for (int n = 0; n < si[i].Length; n++) { si[i][n] += vertexCount; } } s.AddRange(si); } } combined = pb_Object.CreateInstanceWithVerticesFacesSharedIndices(v.ToArray(), f.ToArray(), s.ToArray()); combined.CenterPivot(new int[1] { 0 }); return(true); }
/** * \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)); }
/** * Iterates through face edges and builds a list using the opposite edge. */ public static pb_Edge[] GetEdgeRing(pb_Object pb, pb_Edge[] edges) { List <pb_Edge> usedEdges = new List <pb_Edge>(); foreach (pb_Edge e in edges) { List <pb_Face> origFace; List <pb_Edge> origEdge; if (!ValidFaceAndEdgeWithEdge(pb, e, out origFace, out origEdge)) { continue; } // ValidFaceAndEdgeWithEdge will return false if < 1 face and edge combo is found. // Only add the initial edge once usedEdges.Add(origEdge[0]); pb_Face opFace; pb_Edge opEdge; bool superBreak = false; for (int i = 0; i < origFace.Count; i++) { pb_Face curFace = origFace[i]; pb_Edge curEdge = origEdge[i]; while (GetOppositeEdge(pb, curFace, curEdge, out opFace, out opEdge)) { curFace = opFace; curEdge = opEdge; usedEdges.Add(curEdge); if (curFace == null) { break; } if (curFace == origFace[i]) { superBreak = true; break; } } if (superBreak) { break; } } } return(usedEdges.Distinct().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, 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); }
/** * Removes face from SelectedFaces array, and updates the SelectedTriangles and SelectedEdges arrays to match. */ public void RemoveFromFaceSelection(pb_Face face) { int indx = System.Array.IndexOf(this.faces, face); if (indx > -1) { SetSelectedFaces(m_selectedFaces.RemoveAt(indx)); } }
/** * Build a starting point (in this case, a quad) */ void Start() { pb = pb_ShapeGenerator.PlaneGenerator(1, 1, 0, 0, ProBuilder.Core.Axis.Up); foreach (var f in pb.faces) { f.material = pb_Material.DefaultMaterial; } lastExtrudedFace = pb.faces[0]; }
public Vector2[] GetFaceUV(pb_Face q) { int[] dv = q.distinctIndices; Vector2[] uvs = new Vector2[dv.Length]; for (int i = 0; i < uvs.Length; i++) { uvs[i] = msh.uv[dv[i]]; } return(uvs); }
/** * \brief Gets all vertices in local space from face. * @param _face The #pb_Face to draw vertices from. * \returns A Vector3[] array containing all vertices contained within a #pb_Face. */ public Vector3[] GetVertices(pb_Face face) { Vector3[] v = new Vector3[face.indices.Length]; for (int i = 0; i < face.indices.Length; i++) { v[i] = vertices[face.indices[i]]; } return(v); }
static void Triangulate(pb_Object pb) { Vector3[] v = pb.vertices; Vector2[] u = pb.msh.uv; int triangleCount = pb.msh.triangles.Length; if(triangleCount == v.Length) { Debug.LogWarning("We can't pull over any further!\npb_Object: " + pb.name + " is already triangulated."); } int vertexCount = triangleCount; int faceCount = vertexCount / 3; Vector3[] tri_vertices = new Vector3[vertexCount]; Vector2[] tri_uvs = new Vector2[vertexCount]; pb_Face[] tri_faces = new pb_Face[faceCount]; int n = 0, f = 0; foreach(pb_Face face in pb.faces) { int[] indices = face.indices; for(int i = 0; i < indices.Length; i+=3) { tri_vertices[n+0] = v[indices[i+0]]; tri_vertices[n+1] = v[indices[i+1]]; tri_vertices[n+2] = v[indices[i+2]]; tri_uvs[n+0] = u[indices[i+0]]; tri_uvs[n+1] = u[indices[i+1]]; tri_uvs[n+2] = u[indices[i+2]]; tri_faces[f++] = new pb_Face( new int[] { n+0, n+1, n+2 }, face.material, face.uv, face.smoothingGroup, face.textureGroup, // textureGroup -> force to manual uv mode face.elementGroup, face.manualUV, // manualUV face.color ); n += 3; } } pb.SetVertices(tri_vertices); pb.SetUV(tri_uvs); pb.SetFaces(tri_faces); pb.SetSharedIndices( pb_IntArrayUtility.ExtractSharedIndices(tri_vertices) ); pb.SetSharedIndicesUV( new pb_IntArray[0] ); }
/** * Append a group of new faces to the pb_Object. Significantly faster than calling AppendFace multiple times. */ public static pb_Face[] AppendFaces(this pb_Object pb, Vector3[][] new_Vertices, Color[][] new_Colors, Vector2[][] new_uvs, pb_Face[] new_Faces, int[][] new_SharedIndices) { List<Vector3> _verts = new List<Vector3>(pb.vertices); List<Color> _colors = new List<Color>(pb.colors); List<Vector2> _uv = new List<Vector2>(pb.uv); List<pb_Face> _faces = new List<pb_Face>(pb.faces); pb_IntArray[] sharedIndices = pb.sharedIndices; int vc = pb.vertexCount; for(int i = 0; i < new_Faces.Length; i++) { _verts.AddRange(new_Vertices[i]); _colors.AddRange(new_Colors[i]); _uv.AddRange(new_uvs[i]); new_Faces[i].ShiftIndicesToZero(); new_Faces[i].ShiftIndices(vc); new_Faces[i].RebuildCaches(); _faces.Add(new_Faces[i]); if(new_SharedIndices != null && new_Vertices[i].Length != new_SharedIndices[i].Length) { Debug.LogError("Append Face failed because sharedIndex array does not match new vertex array."); return null; } if(new_SharedIndices != null) { for(int j = 0; j < new_SharedIndices[i].Length; j++) { pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, new_SharedIndices[i][j], j+vc); } } else { for(int j = 0; j < new_Vertices[i].Length; j++) { pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, -1, j+vc); } } vc = _verts.Count; } pb.SetSharedIndices(sharedIndices); pb.SetVertices(_verts.ToArray()); pb.SetColors(_colors.ToArray()); pb.SetUV(_uv.ToArray()); pb.SetFaces(_faces.ToArray()); return new_Faces; }
static void Triangulate(pb_Object pb) { Vector3[] v = pb.vertices; Vector2[] u = pb.msh.uv; int triangleCount = pb.msh.triangles.Length; if (triangleCount == v.Length) { Debug.LogWarning("We can't pull over any further!\npb_Object: " + pb.name + " is already triangulated."); } int vertexCount = triangleCount; int faceCount = vertexCount / 3; Vector3[] tri_vertices = new Vector3[vertexCount]; Vector2[] tri_uvs = new Vector2[vertexCount]; pb_Face[] tri_faces = new pb_Face[faceCount]; int n = 0, f = 0; foreach (pb_Face face in pb.faces) { int[] indices = face.indices; for (int i = 0; i < indices.Length; i += 3) { tri_vertices[n + 0] = v[indices[i + 0]]; tri_vertices[n + 1] = v[indices[i + 1]]; tri_vertices[n + 2] = v[indices[i + 2]]; tri_uvs[n + 0] = u[indices[i + 0]]; tri_uvs[n + 1] = u[indices[i + 1]]; tri_uvs[n + 2] = u[indices[i + 2]]; tri_faces[f++] = new pb_Face(new int[] { n + 0, n + 1, n + 2 }, face.material, face.uv, face.smoothingGroup, face.textureGroup, // textureGroup -> force to manual uv mode face.elementGroup, face.manualUV, // manualUV face.color ); n += 3; } } pb.SetVertices(tri_vertices); pb.SetUV(tri_uvs); pb.SetFaces(tri_faces); pb.SetSharedIndices(pb_IntArrayUtility.ExtractSharedIndices(tri_vertices)); pb.SetSharedIndicesUV(new pb_IntArray[0]); }
/** * Merge all faces into a sigle face. */ public static pb_Face MergeFaces(this pb_Object pb, pb_Face[] faces) { List <int> collectedIndices = new List <int>(faces[0].indices); for (int i = 1; i < faces.Length; i++) { collectedIndices.AddRange(faces[i].indices); } pb_Face mergedFace = new pb_Face(collectedIndices.ToArray(), faces[0].material, faces[0].uv, faces[0].smoothingGroup, faces[0].textureGroup, faces[0].elementGroup, faces[0].manualUV); pb_Face[] rebuiltFaces = new pb_Face[pb.faces.Length - faces.Length + 1]; int n = 0; foreach (pb_Face f in pb.faces) { if (System.Array.IndexOf(faces, f) < 0) { rebuiltFaces[n++] = f; } } rebuiltFaces[n] = mergedFace; pb.SetFaces(rebuiltFaces); // merge vertices that are on top of one another now that they share a face Dictionary <int, int> shared = new Dictionary <int, int>(); for (int i = 0; i < mergedFace.indices.Length; i++) { int sharedIndex = pb.sharedIndices.IndexOf(mergedFace.indices[i]); if (shared.ContainsKey(sharedIndex)) { mergedFace.indices[i] = shared[sharedIndex]; } else { shared.Add(sharedIndex, mergedFace.indices[i]); } } pb.RemoveUnusedVertices(); return(mergedFace); }
public int indexB; // face index - cannot be a sharedIndex /** * Constructor */ public SplitSelection(pb_Object pb, pb_Face face, Vector3 pointA, Vector3 pointB, bool aIsVertex, bool bIsVertex, int indexA, int indexB) { this.pb = pb; this.face = face; this.pointA = pointA; this.pointB = pointB; this.aIsVertex = aIsVertex; this.bIsVertex = bIsVertex; this.indexA = indexA; this.indexB = indexB; }
/** * 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(); }
void Update() { float time = Time.time * speed; Vector3 position = new Vector3( Mathf.PerlinNoise(time, time) * travel, 2, Mathf.PerlinNoise(time + 1f, time + 1f) * travel ); transform.position = position; if (target == null) { Debug.LogWarning("Missing the ProBuilder Mesh target!"); return; } // instead of testing distance by converting each face's center to world space, // convert the world space of this object to the pb-Object local transform. Vector3 pbRelativePosition = target.transform.InverseTransformPoint(transform.position); // reset the last colored face to white if (nearest != null) { target.SetFaceColor(nearest, Color.white); } // iterate each face in the pb_Object looking for the one nearest // to this object. int faceCount = target.faces.Length; float smallestDistance = Mathf.Infinity; nearest = target.faces[0]; for (int i = 0; i < faceCount; i++) { float distance = Vector3.Distance(pbRelativePosition, FaceCenter(target, target.faces[i])); if (distance < smallestDistance) { smallestDistance = distance; nearest = target.faces[i]; } } // Set a single face's vertex colors. If you're updating more than one face, consider using // the pb_Object.SetColors(Color[] colors); function instead. target.SetFaceColor(nearest, Color.blue); // Apply the stored vertex color array to the Unity mesh. target.Refresh(RefreshMask.Colors); }
public void SetFaceColor(pb_Face face, Color color) { if (_colors == null) { _colors = pbUtil.FilledArray <Color>(Color.white, vertexCount); } foreach (int i in face.distinctIndices) { _colors[i] = color; } }
/** * Append a group of new faces to the pb_Object. Significantly faster than calling AppendFace multiple times. */ public static pb_Face[] AppendFaces(this pb_Object pb, Vector3[][] new_Vertices, pb_Face[] new_Faces, int[][] new_SharedIndices) { List<Vector3> _verts = new List<Vector3>(pb.vertices); List<pb_Face> _faces = new List<pb_Face>(pb.faces); pb_IntArray[] sharedIndices = pb.sharedIndices; int vc = pb.vertexCount; // Dictionary<int, int> grp = new Dictionary<int, int>(); // this allows append face to add new vertices to a new shared index group // // if the sharedIndex is negative and less than -1, it will create new gorup // // that other sharedIndex members can then append themselves to. for(int i = 0; i < new_Faces.Length; i++) { _verts.AddRange(new_Vertices[i]); new_Faces[i].ShiftIndicesToZero(); new_Faces[i].ShiftIndices(vc); _faces.Add(new_Faces[i]); if(new_SharedIndices != null && new_Vertices[i].Length != new_SharedIndices[i].Length) { Debug.LogError("Append Face failed because sharedIndex array does not match new vertex array."); return null; } if(new_SharedIndices != null) for(int j = 0; j < new_SharedIndices[i].Length; j++) { // TODO - FIX ME // if(new_SharedIndices[i][j] < -1) // { // if(grp.ContainsKey(new_SharedIndices[i][j])) // AddValueAtIndex(grp[new_SharedIndices[i][j]], j+vc); // else // grp.Add(new_SharedIndices[i][j], AddValueAtIndex(new_SharedIndices[i][j], j+vc)); // } // else pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, new_SharedIndices[i][j], j+vc); } else for(int j = 0; j < new_Vertices[i].Length; j++) { pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, -1, j+vc); } vc = _verts.Count; } pb.SetSharedIndices(sharedIndices); pb.SetVertices(_verts.ToArray()); pb.SetFaces(_faces.ToArray()); pb.ToMesh(); return new_Faces; }
/** * \brief Gets vertex normals for the selected face. * @param face * \returns Vector3[] containing all normals for a face. */ public Vector3[] GetNormals(pb_Face face) { // muhahaha Vector3[] normals = msh.normals; Vector3[] v = new Vector3[face.indices.Length]; for (int i = 0; i < face.indices.Length; i++) { v[i] = normals[face.indices[i]]; } return(v); }
public static Vector3 Normal(pb_Object pb, pb_Face face) { Vector3 p0 = pb.vertices[face.indices[0]]; Vector3 p1 = pb.vertices[face.indices[1]]; Vector3 p2 = pb.vertices[face.indices[2]]; Vector3 cross = Vector3.Cross(p1 - p0, p2 - p0); if (cross.magnitude < Mathf.Epsilon) return new Vector3(0f, 0f, 0f); // bad triangle else { return cross.normalized; } }
public pb_SerializableFace(pb_Face face) { this.indices = face.indices; this.distinctIndices = face.distinctIndices; this.edges = face.edges; this.smoothingGroup = face.smoothingGroup; this.uv = face.uv; this.material = face.material; this.manualUV = false; pb_UpgradeKitUtils.TryGetField(face, "manualUV", ref this.manualUV); this.elementGroup = -1; pb_UpgradeKitUtils.TryGetField(face, "elementGroup", ref this.elementGroup); this.textureGroup = -1; pb_UpgradeKitUtils.TryGetField(face, "textureGroup", ref this.textureGroup); }
/** * \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; }
/** * Attempt to figure out the winding order the passed face. Note that * this may return WindingOrder.Unknown. */ public static WindingOrder GetWindingOrder(this pb_Object pb, pb_Face face) { Vector2[] p = pb_Math.PlanarProject(pb.GetVertices( face.edges.AllTriangles() ), pb_Math.Normal(pb, face)); float sum = 0f; // http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order for(int i = 0; i < p.Length; i++) { Vector2 a = p[i]; Vector2 b = i < p.Length - 1 ? p[i+1] : p[0]; sum += ( (b.x-a.x) * (b.y+a.y) ); } return sum == 0f ? WindingOrder.Unknown : (sum >= 0f ? WindingOrder.Clockwise : WindingOrder.CounterClockwise); }
/** * Deep copy constructor. */ public pb_Face(pb_Face face) { _indices = new int[face.indices.Length]; System.Array.Copy(face.indices, _indices, face.indices.Length); _uv = new pb_UV(face.uv); _mat = face.material; _smoothingGroup = face.smoothingGroup; textureGroup = face.textureGroup; elementGroup = face.elementGroup; _colors = new Color32[face.colors.Length]; System.Array.Copy(face.colors, _colors, colors.Length); manualUV = face.manualUV; RebuildCaches(); }
/** * Append a new face to the pb_Object using sharedIndex array to set the face indices to sharedIndex groups. */ public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, Color[] c, Vector2[] u, pb_Face face, int[] sharedIndex) { int vertexCount = pb.vertexCount; Vector3[] _verts = new Vector3[vertexCount + v.Length]; Color[] _colors = new Color[vertexCount + c.Length]; Vector2[] _uvs = new Vector2[pb.uv.Length + u.Length]; List<pb_Face> _faces = new List<pb_Face>(pb.faces); pb_IntArray[] sharedIndices = pb.sharedIndices; // copy new vertices System.Array.Copy(pb.vertices, 0, _verts, 0, vertexCount); System.Array.Copy(v, 0, _verts, vertexCount, v.Length); // copy new colors System.Array.Copy(pb.colors, 0, _colors, 0, vertexCount); System.Array.Copy(c, 0, _colors, vertexCount, c.Length); // copy new uvs System.Array.Copy(pb.uv, 0, _uvs, 0, pb.uv.Length); System.Array.Copy(u, 0, _uvs, pb.uv.Length, u.Length); face.ShiftIndicesToZero(); face.ShiftIndices(vertexCount); face.RebuildCaches(); _faces.Add(face); for(int i = 0; i < sharedIndex.Length; i++) pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, sharedIndex[i], i+vertexCount); pb.SetVertices( _verts ); pb.SetColors( _colors ); pb.SetUV( _uvs ); pb.SetSharedIndices(sharedIndices); pb.SetFaces(_faces.ToArray()); return face; }
/** * \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 pb_Object ProBuilderize(Transform t) { Mesh m = t.GetComponent<MeshFilter>().sharedMesh; pb_Face[] faces = new pb_Face[m.triangles.Length/3]; int f = 0; for(int n = 0; n < m.subMeshCount; n++) { for(int i = 0; i < m.triangles.Length; i+=3) { faces[f] = new pb_Face( new int[3] { m.triangles[i+0], m.triangles[i+1], m.triangles[i+2] }, t.GetComponent<MeshRenderer>().sharedMaterials[n], new pb_UV(), 0, Color.white ); f++; } } t.gameObject.SetActive(false); pb_Object pb = ProBuilder.CreateObjectWithVerticesFaces(m.vertices, faces); pb.SetName("FrankenMesh"); pb_Editor_Utility.SetEntityType(ProBuilder.EntityType.Detail, pb.gameObject); GameObject go = pb.gameObject; go.transform.position = t.position; go.transform.localRotation = t.localRotation; go.transform.localScale = t.localScale; pb.FreezeScaleTransform(); return pb; }
/** * Append a new face to the pb_Object. */ public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, pb_Face face, int[] sharedIndex) { List<Vector3> _verts = new List<Vector3>(pb.vertices); List<pb_Face> _faces = new List<pb_Face>(pb.faces); pb_IntArray[] sharedIndices = pb.sharedIndices; int vertexCount = pb.vertexCount; _verts.AddRange(v); face.ShiftIndicesToZero(); face.ShiftIndices(vertexCount); face.RebuildCaches(); _faces.Add(face); // Dictionary<int, int> grp = new Dictionary<int, int>(); // this allows append face to add new vertices to a new shared index group // // if the sharedIndex is negative and less than -1, it will create new gorup // // that other sharedIndex members can then append themselves to. for(int i = 0; i < sharedIndex.Length; i++) { // if(sharedIndex[i] < -1) // { // if(grp.ContainsKey(sharedIndex[i])) // AddIndexToSharedIndexArray(grp[sharedIndex[i]], i+vertexCount); // else // grp.Add(sharedIndex[i], AddIndexToSharedIndexArray(sharedIndex[i], i+vertexCount)); // } // else pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, sharedIndex[i], i+vertexCount); } pb.SetSharedIndices(sharedIndices); pb.SetVertices(_verts.ToArray() ); pb.SetFaces(_faces.ToArray()); pb.ToMesh(); return face; }
/** * Returns all faces that share an edge with originFace. */ public static List<pb_Face> GetNeighborFaces(pb_Object pb, Dictionary<int, int> lookup, IEnumerable<pb_Face> mask, pb_Face originFace) { List<pb_Face> faces = new List<pb_Face>(); HashSet<pb_Edge> sharedEdges = new HashSet<pb_Edge>(); for(int i = 0; i < originFace.edges.Length; i++) { sharedEdges.Add(new pb_Edge(lookup[originFace.edges[i].x], lookup[originFace.edges[i].y])); } pb_Edge edge_s = new pb_Edge(-1,-1); for(int i = 0; i < pb.faces.Length; i++) { foreach(pb_Edge edge in pb.faces[i].edges) { edge_s.x = lookup[edge.x]; edge_s.y = lookup[edge.y]; bool contains = sharedEdges.Contains(edge_s); if( contains ) { if(mask.Contains(pb.faces[i])) { continue; } faces.Add(pb.faces[i]); break; } } } return faces; }
void ExtrudeEdge() { pb_Face sourceFace = lastExtrudedFace; // fetch a random perimeter edge connected to the last face extruded List<pb_WingedEdge> wings = pb_WingedEdge.GetWingedEdges(pb); IEnumerable<pb_WingedEdge> sourceWings = wings.Where(x => x.face == sourceFace); List<pb_Edge> nonManifoldEdges = sourceWings.Where(x => x.opposite == null).Select(y => y.edge.local).ToList(); int rand = (int) Random.Range(0, nonManifoldEdges.Count); pb_Edge sourceEdge = nonManifoldEdges[rand]; // get the direction this edge should extrude in Vector3 dir = ((pb.vertices[sourceEdge.x] + pb.vertices[sourceEdge.y]) * .5f) - sourceFace.distinctIndices.Average(x => pb.vertices[x]); dir.Normalize(); // this will be populated with the extruded edge pb_Edge[] extrudedEdges; // perform extrusion pb.Extrude(new pb_Edge[] { sourceEdge }, 0f, false, true, out extrudedEdges); // get the last extruded face lastExtrudedFace = pb.faces.Last(); // not strictly necessary, but makes it easier to handle element selection pb.SetSelectedEdges( extrudedEdges ); // translate the vertices pb.TranslateVertices(pb.SelectedTriangles, dir * distance); // rebuild mesh with new geometry added by extrude pb.ToMesh(); // rebuild mesh normals, textures, collisions, etc pb.Refresh(); }
/** * \brief Given an array of "donors", this method returns a merged #pb_Object. */ public static bool CombineObjects(pb_Object[] pbs, out pb_Object combined) { combined = null; if(pbs.Length < 1) return false; List<Vector3> v = new List<Vector3>(); List<pb_Face> f = new List<pb_Face>(); List<pb_IntArray> s = new List<pb_IntArray>(); foreach(pb_Object pb in pbs) { int vertexCount = v.Count; // Vertices { v.AddRange(pb.VerticesInWorldSpace()); } // Faces { pb_Face[] faces = new pb_Face[pb.faces.Length]; for(int i = 0; i < faces.Length; i++) { faces[i] = new pb_Face(pb.faces[i]); faces[i].ShiftIndices(vertexCount); faces[i].RebuildCaches(); } f.AddRange(faces); } // Shared Indices { pb_IntArray[] si = pb.GetSharedIndices(); for(int i = 0; i < si.Length; i++) { for(int n = 0; n < si[i].Length; n++) si[i][n] += vertexCount; } s.AddRange(si); } } combined = pb_Object.CreateInstanceWithVerticesFacesSharedIndices(v.ToArray(), f.ToArray(), s.ToArray()); combined.CenterPivot(new int[1]{0}); return true; }
/** * Removes the vertex associations so that this face may be moved independently of the main object. */ public static void DetachFace(this pb_Object pb, pb_Face face) { pb_IntArray[] sharedIndices = pb.sharedIndices; pb_IntArrayUtility.RemoveValues(ref sharedIndices, face.indices); // Add these vertices back into the sharedIndices array under it's own entry for(int i = 0; i < face.distinctIndices.Length; i++) { int[] arr = new int[1] { face.distinctIndices[i] }; sharedIndices = pbUtil.Add(sharedIndices, new pb_IntArray(arr)); } pb.SetSharedIndices(sharedIndices); }
/** * Inserts a split from each selected vertex to the center of the face */ private static bool PokeFace_Internal(pb_Object pb, pb_Face face, int[] indices_nonFaceSpecific, out pb_Face[] splitFaces, out Vector3[][] splitVertices, out int[][] splitSharedIndices) { splitFaces = null; splitVertices = null; splitSharedIndices = null; pb_IntArray[] sharedIndices = pb.sharedIndices; ///** Sort index array such that it only uses indices local to the passed face int[] indices = new int[indices_nonFaceSpecific.Length]; int[] dist_ind_si = new int[face.distinctIndices.Length]; // figure out sharedIndices index of distinct Indices for(int i = 0; i < face.distinctIndices.Length; i++) dist_ind_si[i] = sharedIndices.IndexOf(face.distinctIndices[i]); // now do the same for non-face specific indices, assigning matching groups for(int i = 0; i < indices.Length; i++) { int ind = System.Array.IndexOf(dist_ind_si, sharedIndices.IndexOf(indices_nonFaceSpecific[i])); if(ind < 0) return false; indices[i] = face.distinctIndices[ind]; } ///** Sort index array such that it only uses indices local to the passed face Vector3 cen3d = pb_Math.Average(pb.GetVertices(face)); Vector3[] verts = pb.GetVertices(face.distinctIndices); Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices)); Vector2[] plane = pb_Math.VerticesTo2DPoints(verts, nrm); Vector2[] indPlane = pb_Math.VerticesTo2DPoints(pb.GetVertices(indices), nrm); Vector2 cen2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { cen3d }, nrm)[0]; // Get the directions from which to segment this face Vector2[] dividers = new Vector2[indices.Length]; for(int i = 0; i < indices.Length; i++) dividers[i] = (indPlane[i] - cen2d).normalized; List<Vector2>[] quadrants2d = new List<Vector2>[indices.Length]; List<Vector3>[] quadrants3d = new List<Vector3>[indices.Length]; List<int>[] sharedIndex = new List<int>[indices.Length]; for(int i = 0; i < quadrants2d.Length; i++) { quadrants2d[i] = new List<Vector2>(1) { cen2d }; quadrants3d[i] = new List<Vector3>(1) { cen3d }; sharedIndex[i] = new List<int>(1) { -2 }; // any negative value less than -1 will be treated as a new group } for(int i = 0; i < face.distinctIndices.Length; i++) { // if this index is a divider, it needs to belong to the leftmost and // rightmost quadrant int indexInPokeVerts = System.Array.IndexOf(indices, face.distinctIndices[i]); int ignore = -1; if( indexInPokeVerts > -1) { // Add vert to this quadrant quadrants2d[indexInPokeVerts].Add(plane[i]); quadrants3d[indexInPokeVerts].Add(verts[i]); sharedIndex[indexInPokeVerts].Add(pb.sharedIndices.IndexOf(face.distinctIndices[i])); // And also the one closest counter clockwise ignore = indexInPokeVerts; } Vector2 dir = (plane[i]-cen2d).normalized; // plane corresponds to distinctIndices float largestClockwiseDistance = 0f; int quad = -1; for(int j = 0; j < dividers.Length; j++) { if(j == ignore) continue; // this is a dividing vertex - ignore float dist = Vector2.Angle(dividers[j], dir); if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f ) dist = 360f - dist; if(dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(plane[i]); quadrants3d[quad].Add(verts[i]); sharedIndex[quad].Add(pb.sharedIndices.IndexOf(face.distinctIndices[i])); } int len = quadrants2d.Length; // Triangulate int[][] tris = new int[len][]; for(int i = 0; i < len; i++) { tris[i] = Delauney.Triangulate(quadrants2d[i]).ToIntArray(); if(tris[i].Length < 3) return false; // todo - check that face normal is correct } splitFaces = new pb_Face[len]; splitVertices = new Vector3[len][]; splitSharedIndices = new int[len][]; for(int i = 0; i < len; i++) { // triangles, material, pb_UV, smoothing group, shared index splitFaces[i] = new pb_Face(tris[i], face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, -1, face.color); splitVertices[i] = quadrants3d[i].ToArray(); splitSharedIndices[i] = sharedIndex[i].ToArray(); } return true; }
/** * */ public static bool ConnectEdges(this pb_Object pb, List<EdgeConnection> edgeConnectionsUnfiltered, out pb_Face[] faces) { // first, remove any junk connections. faces with less than two edges confuse this method. List<EdgeConnection> edgeConnections = new List<EdgeConnection>(); foreach(EdgeConnection ec in edgeConnectionsUnfiltered) if(ec.isValid) edgeConnections.Add(ec); int len = edgeConnections.Count; if(len < 1) { Debug.LogWarning("No valid split paths found. This is most likely because you are attempting to split edges that do belong to the same face, or do not have more than one edge selected. This is not currently supported, sorry!"); faces = null; return false; } Vector3[] vertices = pb.vertices; List<pb_Face> successfullySplitFaces = new List<pb_Face>(); List<pb_Face> all_splitFaces = new List<pb_Face>(); List<Vector3[]> all_splitVertices = new List<Vector3[]>(); List<int[]> all_splitSharedIndices = new List<int[]>(); bool[] success = new bool[len]; // use a nullable type because in order for the adjacent face triangulation // code to work, it needs to know what dangling vert belongs to which edge, // if we out a vector3[] with each index corresponding to the passed edges // in EdgeConnection, it's easy to maintain the relationship. Vector3?[][] danglingVertices = new Vector3?[len][]; int i = 0; foreach(EdgeConnection fc in edgeConnections) { pb_Face[] splitFaces = null; Vector3[][] splitVertices = null; int[][] splitSharedIndices = null; if( fc.edges.Count < 3 ) { Vector3 edgeACen = (vertices[fc.edges[0].x] + vertices[fc.edges[0].y]) / 2f; Vector3 edgeBCen = (vertices[fc.edges[1].x] + vertices[fc.edges[1].y]) / 2f; danglingVertices[i] = new Vector3?[2] { edgeACen, edgeBCen }; success[i] = SplitFace_Internal(new SplitSelection(pb, fc.face, edgeACen, edgeBCen, false, false, -1, -1), out splitFaces, out splitVertices, out splitSharedIndices); if(success[i]) successfullySplitFaces.Add(fc.face); } else { Vector3?[] appendedVertices = null; success[i] = SubdivideFace_Internal(pb, fc, out appendedVertices, out splitFaces, out splitVertices, out splitSharedIndices); if(success[i]) successfullySplitFaces.Add(fc.face); danglingVertices[i] = appendedVertices; } if(success[i]) { int texGroup = fc.face.textureGroup < 0 ? pb.UnusedTextureGroup(i+1) : fc.face.textureGroup; for(int j = 0; j < splitFaces.Length; j++) { splitFaces[j].textureGroup = texGroup; all_splitFaces.Add(splitFaces[j]); all_splitVertices.Add(splitVertices[j]); all_splitSharedIndices.Add(splitSharedIndices[j]); } } i++; } /** * Figure out which faces need to be re-triangulated */ pb_Edge[][] tedges = new pb_Edge[edgeConnections.Count][]; int n = 0; for(i = 0; i < edgeConnections.Count; i++) tedges[n++] = edgeConnections[i].edges.ToArray(); List<pb_Face>[][] allConnects = pbMeshUtils.GetConnectedFacesJagged(pb, tedges); Dictionary<pb_Face, List<Vector3>> addVertex = new Dictionary<pb_Face, List<Vector3>>(); List<pb_Face> temp = new List<pb_Face>(); for(int j = 0; j < edgeConnections.Count; j++) { if(!success[j]) continue; // check that this edge has a buddy that it welded it's new vertex to, and if not, // create one for(i = 0; i < edgeConnections[j].edges.Count; i++) { if(danglingVertices[j][i] == null) continue; List<pb_Face> connected = allConnects[j][i]; foreach(pb_Face face in connected) { int ind = successfullySplitFaces.IndexOf(face); if(ind < 0) { if(addVertex.ContainsKey(face)) addVertex[face].Add((Vector3)danglingVertices[j][i]); else { temp.Add(face); addVertex.Add(face, new List<Vector3>(1) { (Vector3)danglingVertices[j][i] }); } } } } } pb_Face[] appendedFaces = pb.AppendFaces(all_splitVertices.ToArray(), all_splitFaces.ToArray(), all_splitSharedIndices.ToArray()); List<pb_Face> triangulatedFaces = new List<pb_Face>(); foreach(KeyValuePair<pb_Face, List<Vector3>> add in addVertex) { pb_Face newFace; if( pb.AppendVerticesToFace(add.Key, add.Value, out newFace) ) triangulatedFaces.Add(newFace); else Debug.LogError("Mesh re-triangulation failed. Specifically, AppendVerticesToFace(" + add.Key + " : " + add.Value.ToFormattedString(", ")); } // Re-triangulate any faces left with dangling verts at edges // Weld verts, including those added in re-triangu int[] splitFaceTris = pb_Face.AllTriangles(appendedFaces); int[] triangulatedFaceTris = pb_Face.AllTriangles(triangulatedFaces); int[] allModifiedTris = new int[splitFaceTris.Length + triangulatedFaceTris.Length]; System.Array.Copy(splitFaceTris, 0, allModifiedTris, 0, splitFaceTris.Length); System.Array.Copy(triangulatedFaceTris, 0, allModifiedTris, splitFaceTris.Length, triangulatedFaceTris.Length); pb.WeldVertices(allModifiedTris, Mathf.Epsilon); // Now that we're done screwing with geo, delete all the old faces (that were successfully split) pb.DeleteFaces( successfullySplitFaces.ToArray() ); faces = appendedFaces; return true; }
/** * This method assumes that the split selection edges share a common face and have already been sanity checked. Will return * the variables necessary to compose a new face from the split, or null if the split is invalid. */ private static bool SplitFace_Internal(SplitSelection splitSelection, out pb_Face[] splitFaces, out Vector3[][] splitVertices, out int[][] splitSharedIndices) { splitFaces = null; splitVertices = null; splitSharedIndices = null; pb_Object pb = splitSelection.pb; // we'll be using this a lot pb_Face face = splitSelection.face; // likewise int[] indices = face.distinctIndices; pb_IntArray[] sharedIndices = pb.sharedIndices; int[] sharedIndex = new int[indices.Length]; for(int i = 0; i < indices.Length; i++) sharedIndex[i] = sharedIndices.IndexOf(indices[i]); // First order of business is to translate the face to 2D plane. Vector3[] verts = pb.GetVertices(face.distinctIndices); Vector3 projAxis = pb_Math.GetProjectionAxis( pb_Math.Normal(pb.GetVertices(face.indices))).ToVector3(); Vector2[] plane = pb_Math.VerticesTo2DPoints(verts, projAxis); // Split points Vector3 splitPointA_3d = splitSelection.pointA; Vector3 splitPointB_3d = splitSelection.pointB; Vector2 splitPointA_2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { splitPointA_3d }, projAxis )[0]; Vector2 splitPointB_2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { splitPointB_3d }, projAxis )[0]; List<Vector3> v_polyA = new List<Vector3>(); // point in object space List<Vector2> v_polyA_2d = new List<Vector2>(); // point in 2d space - used to triangulate List<Vector3> v_polyB = new List<Vector3>(); // point in object space List<Vector2> v_polyB_2d = new List<Vector2>(); // point in 2d space - used to triangulate List<int> i_polyA = new List<int>(); // sharedIndices array index List<int> i_polyB = new List<int>(); // sharedIndices array index List<int> nedgeA = new List<int>(); List<int> nedgeB = new List<int>(); // Sort points into two separate polygons for(int i = 0; i < indices.Length; i++) { // is this point (a) a vertex to split or (b) on the negative or positive side of this split line if( (splitSelection.aIsVertex && splitSelection.indexA == indices[i]) || (splitSelection.bIsVertex && splitSelection.indexB == indices[i]) ) { v_polyA.Add( verts[i] ); v_polyB.Add( verts[i] ); v_polyA_2d.Add( plane[i] ); v_polyB_2d.Add( plane[i] ); i_polyA.Add( sharedIndex[i] ); i_polyB.Add( sharedIndex[i] ); } else { // split points across the division line Vector2 perp = pb_Math.Perpendicular(splitPointB_2d, splitPointA_2d); Vector2 origin = (splitPointA_2d + splitPointB_2d) / 2f; if( Vector2.Dot(perp, plane[i]-origin) > 0 ) { v_polyA.Add(verts[i]); v_polyA_2d.Add(plane[i]); i_polyA.Add(sharedIndex[i]); } else { v_polyB.Add(verts[i]); v_polyB_2d.Add(plane[i]); i_polyB.Add(sharedIndex[i]); } } } if(!splitSelection.aIsVertex) { v_polyA.Add( splitPointA_3d ); v_polyA_2d.Add( splitPointA_2d ); v_polyB.Add( splitPointA_3d ); v_polyB_2d.Add( splitPointA_2d ); i_polyA.Add(-1); i_polyB.Add(-1); // neg 1 because it's a new vertex point nedgeA.Add(v_polyA.Count); nedgeB.Add(v_polyB.Count); } if(!splitSelection.bIsVertex) { v_polyA.Add( splitPointB_3d ); v_polyA_2d.Add( splitPointB_2d ); v_polyB.Add( splitPointB_3d ); v_polyB_2d.Add( splitPointB_2d ); i_polyA.Add(-1); i_polyB.Add(-1); // neg 1 because it's a new vertex point nedgeA.Add(v_polyA.Count); nedgeB.Add(v_polyB.Count); } if(v_polyA_2d.Count < 3 || v_polyB_2d.Count < 3) { splitFaces = null; splitVertices = null; splitSharedIndices = null; return false; } // triangulate new polygons int[] t_polyA = Delauney.Triangulate(v_polyA_2d).ToIntArray(); int[] t_polyB = Delauney.Triangulate(v_polyB_2d).ToIntArray(); if(t_polyA.Length < 3 || t_polyB.Length < 3) return false; // figure out the face normals for the new faces and check to make sure they match the original face Vector2[] pln = pb_Math.VerticesTo2DPoints( pb.GetVertices(face.indices), projAxis ); Vector3 nrm = Vector3.Cross( pln[2] - pln[0], pln[1] - pln[0]); Vector3 nrmA = Vector3.Cross( v_polyA_2d[ t_polyA[2] ]-v_polyA_2d[ t_polyA[0] ], v_polyA_2d[ t_polyA[1] ]-v_polyA_2d[ t_polyA[0] ] ); Vector3 nrmB = Vector3.Cross( v_polyB_2d[ t_polyB[2] ]-v_polyB_2d[ t_polyB[0] ], v_polyB_2d[ t_polyB[1] ]-v_polyB_2d[ t_polyB[0] ] ); if(Vector3.Dot(nrm, nrmA) < 0) System.Array.Reverse(t_polyA); if(Vector3.Dot(nrm, nrmB) < 0) System.Array.Reverse(t_polyB); // triangles, material, pb_UV, smoothing group, shared index pb_Face faceA = new pb_Face( t_polyA, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.color); pb_Face faceB = new pb_Face( t_polyB, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.color); splitFaces = new pb_Face[2] { faceA, faceB }; splitVertices = new Vector3[2][] { v_polyA.ToArray(), v_polyB.ToArray() }; splitSharedIndices = new int[2][] { i_polyA.ToArray(), i_polyB.ToArray() }; return true; }
public static void Extrude(this pb_Object pb, pb_Face[] faces) { pb.Extrude(faces, EXTRUDE_DISTANCE); }
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(); }
// todo - there's a lot of duplicate code between this and poke face. /** * Inserts a vertex at the center of each edge, then connects the new vertices to another new * vertex placed at the center of the face. */ // internal method - so it's allow to be messy, right? private static bool SubdivideFace_Internal(pb_Object pb, EdgeConnection edgeConnection, out Vector3?[] appendedVertices, out pb_Face[] splitFaces, out Vector3[][] splitVertices, out int[][] splitSharedIndices) { splitFaces = null; splitVertices = null; splitSharedIndices = null; appendedVertices = new Vector3?[edgeConnection.edges.Count]; // cache all the things pb_Face face = edgeConnection.face; pb_IntArray[] sharedIndices = pb.sharedIndices; Vector3[] vertices = pb.vertices; List<Vector3> edgeCenters3d = new List<Vector3>();//pb.GetVertices(edgeConnection.face)); // filter duplicate edges int u = 0; List<int> usedEdgeIndices = new List<int>(); foreach(pb_Edge edge in edgeConnection.edges) { int ind = face.edges.IndexOf(edge, sharedIndices); if(!usedEdgeIndices.Contains(ind)) { Vector3 cen = (vertices[edge.x] + vertices[edge.y]) / 2f; edgeCenters3d.Add(cen); usedEdgeIndices.Add(ind); appendedVertices[u] = cen; } else appendedVertices[u] = null; u++; } // now we have all the vertices of the old face, plus the new edge center vertices Vector3[] verts3d = pb.GetVertices(face.distinctIndices); Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices)); Vector2[] verts2d = pb_Math.VerticesTo2DPoints(verts3d, nrm); Vector2[] edgeCenters2d = pb_Math.VerticesTo2DPoints(edgeCenters3d.ToArray(), nrm); Vector3 cen3d = pb_Math.Average(verts3d); Vector2 cen2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { cen3d }, nrm)[0]; // Get the directions from which to segment this face Vector2[] dividers = new Vector2[edgeCenters2d.Length]; for(int i = 0; i < edgeCenters2d.Length; i++) dividers[i] = (edgeCenters2d[i] - cen2d).normalized; List<Vector2>[] quadrants2d = new List<Vector2>[edgeCenters2d.Length]; List<Vector3>[] quadrants3d = new List<Vector3>[edgeCenters2d.Length]; List<int>[] sharedIndex = new List<int>[edgeCenters2d.Length]; for(int i = 0; i < quadrants2d.Length; i++) { quadrants2d[i] = new List<Vector2>(1) { cen2d }; quadrants3d[i] = new List<Vector3>(1) { cen3d }; sharedIndex[i] = new List<int>(1) { -2 }; // any negative value less than -1 will be treated as a new group } // add the divisors for(int i = 0; i < edgeCenters2d.Length; i++) { quadrants2d[i].Add(edgeCenters2d[i]); quadrants3d[i].Add(edgeCenters3d[i]); sharedIndex[i].Add(-1); // -(i+2) to group new vertices in AppendFace // and add closest in the counterclockwise direction Vector2 dir = (edgeCenters2d[i]-cen2d).normalized; float largestClockwiseDistance = 0f; int quad = -1; for(int j = 0; j < dividers.Length; j++) { if(j == i) continue; // this is a dividing vertex - ignore float dist = Vector2.Angle(dividers[j], dir); if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f ) dist = 360f - dist; if(dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(edgeCenters2d[i]); quadrants3d[quad].Add(edgeCenters3d[i]); sharedIndex[quad].Add(-1); } // distribute the existing vertices for(int i = 0; i < face.distinctIndices.Length; i++) { Vector2 dir = (verts2d[i]-cen2d).normalized; // plane corresponds to distinctIndices float largestClockwiseDistance = 0f; int quad = -1; for(int j = 0; j < dividers.Length; j++) { float dist = Vector2.Angle(dividers[j], dir); if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f ) dist = 360f - dist; if(dist > largestClockwiseDistance) { largestClockwiseDistance = dist; quad = j; } } quadrants2d[quad].Add(verts2d[i]); quadrants3d[quad].Add(verts3d[i]); sharedIndex[quad].Add(pb.sharedIndices.IndexOf(face.distinctIndices[i])); } int len = quadrants2d.Length; // Triangulate int[][] tris = new int[len][]; for(int i = 0; i < len; i++) { if(quadrants2d[i].Count < 3) { Debug.LogError("Insufficient points to triangulate - bailing on subdivide operation. This is probably due to a concave face, or maybe the compiler just doesn't like you today. 50/50 odds really."); return false; } tris[i] = Delauney.Triangulate(quadrants2d[i]).ToIntArray(); Vector3[] nrm_check = new Vector3[3] { quadrants3d[i][tris[i][0]], quadrants3d[i][tris[i][1]], quadrants3d[i][tris[i][2]] }; if( Vector3.Dot(nrm, pb_Math.Normal(nrm_check)) < 0 ) System.Array.Reverse(tris[i]); } splitFaces = new pb_Face[len]; splitVertices = new Vector3[len][]; splitSharedIndices = new int[len][]; for(int i = 0; i < len; i++) { // triangles, material, pb_UV, smoothing group, shared index splitFaces[i] = new pb_Face(tris[i], face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.color); splitVertices[i] = quadrants3d[i].ToArray(); splitSharedIndices[i] = sharedIndex[i].ToArray(); } return true; }