internal static void Flood(ProBuilderMesh pb, WingedEdge wing, Vector3 wingNrm, float maxAngle, HashSet <Face> selection) { WingedEdge next = wing; do { WingedEdge opp = next.opposite; if (opp != null && !selection.Contains(opp.face)) { if (maxAngle > 0f) { Vector3 oppNormal = Math.Normal(pb, opp.face); if (Vector3.Angle(wingNrm, oppNormal) < maxAngle) { if (selection.Add(opp.face)) { Flood(pb, opp, oppNormal, maxAngle, selection); } } } else { if (selection.Add(opp.face)) { Flood(pb, opp, wingNrm, maxAngle, selection); } } } next = next.next; }while (next != wing); }
/// <summary> /// Attempts to find edges along an Edge loop. /// /// http://wiki.blender.org/index.php/Doc:2.4/Manual/Modeling/Meshes/Selecting/Edges says: /// First check to see if the selected element connects to only 3 other edges. /// If the edge in question has already been added to the list, the selection ends. /// Of the 3 edges that connect to the current edge, the ones that share a face with the current edge are eliminated /// and the remaining edge is added to the list and is made the current edge. /// </summary> /// <param name="mesh"></param> /// <param name="edges"></param> /// <param name="loop"></param> /// <returns></returns> internal static bool GetEdgeLoop(ProBuilderMesh mesh, IEnumerable <Edge> edges, out Edge[] loop) { List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh); IEnumerable <EdgeLookup> m_edgeLookup = EdgeLookup.GetEdgeLookup(edges, mesh.sharedVertexLookup); HashSet <EdgeLookup> sources = new HashSet <EdgeLookup>(m_edgeLookup); HashSet <EdgeLookup> used = new HashSet <EdgeLookup>(); for (int i = 0; i < wings.Count; i++) { if (used.Contains(wings[i].edge) || !sources.Contains(wings[i].edge)) { continue; } bool completeLoop = GetEdgeLoopInternal(wings[i], wings[i].edge.common.b, used); // loop didn't close if (!completeLoop) { GetEdgeLoopInternal(wings[i], wings[i].edge.common.a, used); } } loop = used.Select(x => x.local).ToArray(); return(true); }
void ExtrudeEdge() { // fetch a random perimeter edge connected to the last face extruded List <WingedEdge> wings = WingedEdge.GetWingedEdges(m_Mesh); IEnumerable <WingedEdge> sourceWings = wings.Where(x => x.face == m_LastExtrudedFace); List <Edge> nonManifoldEdges = sourceWings.Where(x => x.opposite == null).Select(y => y.edge.local).ToList(); int rand = (int)Random.Range(0, nonManifoldEdges.Count); Edge sourceEdge = nonManifoldEdges[rand]; // get the direction this edge should extrude in var edgeCenter = Math.Average(m_Mesh.positions, new[] { sourceEdge.a, sourceEdge.b }); var faceCenter = Math.Average(m_Mesh.positions, m_LastExtrudedFace.distinctIndexes); Vector3 dir = (edgeCenter - faceCenter).normalized; // this will be populated with the extruded edge Edge[] extrudedEdges; // perform extrusion extrudedEdges = m_Mesh.Extrude(new Edge[] { sourceEdge }, 0f, false, true); // get the last extruded face m_LastExtrudedFace = m_Mesh.faces.Last(); // translate the vertices m_Mesh.TranslateVertices(extrudedEdges, dir * distance); // rebuild mesh with new geometry added by extrude m_Mesh.ToMesh(); // rebuild mesh normals, textures, collisions, etc m_Mesh.Refresh(); }
static WingedEdge EdgeRingNext(WingedEdge edge) { if (edge == null) { return(null); } WingedEdge next = edge.next, prev = edge.previous; int i = 0; while (next != prev && next != edge) { next = next.next; if (next == prev) { return(null); } prev = prev.previous; i++; } if (i % 2 == 0 || next == edge) { next = null; } return(next); }
static ConnectFaceRebuildData InsertVertices(Face face, List <WingedEdge> edges, List <Vertex> vertices) { List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face); List <Vertex> n_vertices = new List <Vertex>(); List <int> newVertexIndexes = new List <int>(); HashSet <Edge> affected = new HashSet <Edge>(edges.Select(x => x.edge.local)); for (int i = 0; i < perimeter.Count; i++) { n_vertices.Add(vertices[perimeter[i].a]); if (affected.Contains(perimeter[i])) { newVertexIndexes.Add(n_vertices.Count); n_vertices.Add(Vertex.Mix(vertices[perimeter[i].a], vertices[perimeter[i].b], .5f)); } } FaceRebuildData res = AppendElements.FaceWithVertices(n_vertices, false); res.face.textureGroup = face.textureGroup; res.face.uv = new AutoUnwrapSettings(face.uv); res.face.smoothingGroup = face.smoothingGroup; res.face.manualUV = face.manualUV; res.face.submeshIndex = face.submeshIndex; return(new ConnectFaceRebuildData(res, newVertexIndexes)); }
static void SlideEdge(IList <Vertex> vertices, WingedEdge we, float amount) { we.face.manualUV = true; we.face.textureGroup = -1; Edge slide_x = GetLeadingEdge(we, we.edge.common.a); Edge slide_y = GetLeadingEdge(we, we.edge.common.b); if (!slide_x.IsValid() || !slide_y.IsValid()) { return; } Vertex x = (vertices[slide_x.a] - vertices[slide_x.b]); x.Normalize(); Vertex y = (vertices[slide_y.a] - vertices[slide_y.b]); y.Normalize(); // need the pb_Vertex value to be modified, not reassigned in this array (which += does) vertices[we.edge.local.a].Add(x * amount); vertices[we.edge.local.b].Add(y * amount); }
static void GetEdgeLoopInternalIterative(WingedEdge start, Edge edge, HashSet <EdgeLookup> used) { int indA = edge.a; int indB = edge.b; WingedEdge cur = start; if (!used.Contains(cur.edge)) { used.Add(cur.edge); } List <WingedEdge> spokesA = GetSpokes(cur, indA, true).DistinctBy(x => x.edge.common).ToList(); List <WingedEdge> spokesB = GetSpokes(cur, indB, true).DistinctBy(x => x.edge.common).ToList(); if (spokesA.Count == 4) { cur = spokesA[2]; if (!used.Contains(cur.edge)) { used.Add(cur.edge); } } if (spokesB.Count == 4) { cur = spokesB[2]; if (!used.Contains(cur.edge)) { used.Add(cur.edge); } } }
/// <summary> /// 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. /// </summary> /// <param name="mesh"></param> /// <param name="f1"></param> /// <param name="f2"></param> /// <param name="channel"></param> /// <returns></returns> public static bool AutoStitch(ProBuilderMesh mesh, Face f1, Face f2, int channel) { var wings = WingedEdge.GetWingedEdges(mesh, new [] { f1, f2 }); var sharedEdge = wings.FirstOrDefault(x => x.face == f1 && x.opposite != null && x.opposite.face == f2); if (sharedEdge == null) { return(false); } if (f1.manualUV) { f2.manualUV = true; } f1.textureGroup = -1; f2.textureGroup = -1; Projection.PlanarProject(mesh, f2); if (AlignEdges(mesh, f2, sharedEdge.edge.local, sharedEdge.opposite.edge.local, channel)) { if (!f2.manualUV) { UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(mesh, new [] { f2 }); } return(true); } return(false); }
/// <summary> /// Insert a new vertex at the center of a face and connect the center of all edges to it. /// </summary> /// <param name="face"></param> /// <param name="edges"></param> /// <param name="vertices"></param> /// <returns></returns> static List <ConnectFaceRebuildData> ConnectEdgesInFace( Face face, List <WingedEdge> edges, List <Vertex> vertices) { List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face); int splitCount = edges.Count; Vertex centroid = Vertex.Average(vertices, face.distinctIndexesInternal); List <List <Vertex> > n_vertices = ArrayUtility.Fill <List <Vertex> >(x => { return(new List <Vertex>()); }, splitCount); List <List <int> > n_indexes = ArrayUtility.Fill <List <int> >(x => { return(new List <int>()); }, splitCount); HashSet <Edge> edgesToSplit = new HashSet <Edge>(edges.Select(x => x.edge.local)); int index = 0; // creates two new polygon perimeter lines by stepping the current face perimeter and inserting new vertices where edges match for (int i = 0; i < perimeter.Count; i++) { n_vertices[index % splitCount].Add(vertices[perimeter[i].a]); if (edgesToSplit.Contains(perimeter[i])) { Vertex mix = Vertex.Mix(vertices[perimeter[i].a], vertices[perimeter[i].b], .5f); // split current poly line n_indexes[index].Add(n_vertices[index].Count); n_vertices[index].Add(mix); // add the centroid vertex n_indexes[index].Add(n_vertices[index].Count); n_vertices[index].Add(centroid); // advance the poly line index index = (index + 1) % splitCount; // then add the edge center vertex and move on n_vertices[index].Add(mix); } } List <ConnectFaceRebuildData> faces = new List <ConnectFaceRebuildData>(); for (int i = 0; i < n_vertices.Count; i++) { FaceRebuildData f = AppendElements.FaceWithVertices(n_vertices[i], false); if (f == null) { faces.Clear(); return(null); } faces.Add(new ConnectFaceRebuildData(f, n_indexes[i])); } return(faces); }
/// <summary> /// Get a face loop or ring from a set of winged edges. /// </summary> /// <param name="wings"></param> /// <param name="face"></param> /// <param name="ring"></param> /// <returns></returns> static HashSet <Face> GetFaceLoop(List <WingedEdge> wings, Face face, bool ring) { HashSet <Face> loop = new HashSet <Face>(); if (face == null) { return(loop); } WingedEdge start = wings.FirstOrDefault(x => x.face == face); if (start == null) { return(loop); } if (ring) { start = start.next ?? start.previous; } for (int i = 0; i < 2; i++) { WingedEdge cur = start; if (i == 1) { if (start.opposite != null && start.opposite.face != null) { cur = start.opposite; } else { break; } } do { if (!loop.Add(cur.face)) { break; } if (cur.Count() != 4) { break; } // count == 4 assures us next.next is valid, but opposite can still be null cur = cur.next.next.opposite; }while (cur != null && cur.face != null); } return(loop); }
public static List <List <Edge> > FindHoles(ProBuilderMesh mesh, HashSet <int> common) { List <List <Edge> > holes = new List <List <Edge> >(); List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh); foreach (List <WingedEdge> hole in FindHoles(wings, common)) { holes.Add(hole.Select(x => x.edge.local).ToList()); } return(holes); }
static List <FaceRebuildData> GetBridgeFaces( IList <Vertex> vertices, WingedEdge left, WingedEdge right, Dictionary <int, List <SimpleTuple <FaceRebuildData, List <int> > > > holes) { List <FaceRebuildData> faces = new List <FaceRebuildData>(); FaceRebuildData rf = new FaceRebuildData(); EdgeLookup a = left.edge; EdgeLookup b = right.edge; rf.vertices = new List <Vertex>() { vertices[a.local.a], vertices[a.local.b], vertices[a.common.a == b.common.a ? b.local.a : b.local.b], vertices[a.common.a == b.common.a ? b.local.b : b.local.a] }; Vector3 an = Math.Normal(vertices, left.face.indexesInternal); Vector3 bn = Math.Normal(rf.vertices, k_BridgeIndexesTri); int[] triangles = new int[] { 2, 1, 0, 2, 3, 1 }; if (Vector3.Dot(an, bn) < 0f) { System.Array.Reverse(triangles); } rf.face = new Face( triangles, left.face.submeshIndex, AutoUnwrapSettings.tile, -1, -1, -1, false); faces.Add(rf); holes.AddOrAppend(a.common.a, new SimpleTuple <FaceRebuildData, List <int> >(rf, new List <int>() { 0, 2 })); holes.AddOrAppend(a.common.b, new SimpleTuple <FaceRebuildData, List <int> >(rf, new List <int>() { 1, 3 })); return(faces); }
/// <summary> /// Find any holes touching one of the passed vertex indexes. /// </summary> /// <param name="mesh"></param> /// <param name="indexes"></param> /// <returns></returns> internal static List <List <Edge> > FindHoles(ProBuilderMesh mesh, IEnumerable <int> indexes) { HashSet <int> common = mesh.GetSharedVertexHandles(indexes); List <List <Edge> > holes = new List <List <Edge> >(); List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh); foreach (List <WingedEdge> hole in FindHoles(wings, common)) { holes.Add(hole.Select(x => x.edge.local).ToList()); } return(holes); }
/// <summary> /// Accepts a face and set of edges to split on. /// </summary> /// <param name="face"></param> /// <param name="a"></param> /// <param name="b"></param> /// <param name="vertices"></param> /// <returns></returns> static List <ConnectFaceRebuildData> ConnectEdgesInFace( Face face, WingedEdge a, WingedEdge b, List <Vertex> vertices) { List <Edge> perimeter = WingedEdge.SortEdgesByAdjacency(face); List <Vertex>[] n_vertices = new List <Vertex>[2] { new List <Vertex>(), new List <Vertex>() }; List <int>[] n_indexes = new List <int>[2] { new List <int>(), new List <int>() }; int index = 0; // creates two new polygon perimeter lines by stepping the current face perimeter and inserting new vertices where edges match for (int i = 0; i < perimeter.Count; i++) { n_vertices[index % 2].Add(vertices[perimeter[i].a]); if (perimeter[i].Equals(a.edge.local) || perimeter[i].Equals(b.edge.local)) { Vertex mix = Vertex.Mix(vertices[perimeter[i].a], vertices[perimeter[i].b], .5f); n_indexes[index % 2].Add(n_vertices[index % 2].Count); n_vertices[index % 2].Add(mix); index++; n_indexes[index % 2].Add(n_vertices[index % 2].Count); n_vertices[index % 2].Add(mix); } } List <ConnectFaceRebuildData> faces = new List <ConnectFaceRebuildData>(); for (int i = 0; i < n_vertices.Length; i++) { FaceRebuildData f = AppendElements.FaceWithVertices(n_vertices[i], false); faces.Add(new ConnectFaceRebuildData(f, n_indexes[i])); } return(faces); }
/// <summary> /// Return all edges connected to @wing with @sharedIndex as the pivot point. The first entry in the list is always the queried wing. /// </summary> /// <param name="wing"></param> /// <param name="sharedIndex"></param> /// <param name="allowHoles"></param> /// <returns></returns> internal static List <WingedEdge> GetSpokes(WingedEdge wing, int sharedIndex, bool allowHoles = false) { List <WingedEdge> spokes = new List <WingedEdge>(); WingedEdge cur = wing; bool opp = false; do { // https://fogbugz.unity3d.com/f/cases/1241105/ if (spokes.Contains(cur)) { return(spokes); } spokes.Add(cur); cur = NextSpoke(cur, sharedIndex, opp); opp = !opp; // we've looped around as far as it's gon' go if (cur != null && cur.edge.common.Equals(wing.edge.common)) { return(spokes); } }while (cur != null); if (!allowHoles) { return(null); } // if the first loop didn't come back, that means there was a hole in the geo // do the loop again using the opposite wing cur = wing.opposite; opp = false; List <WingedEdge> fragment = new List <WingedEdge>(); // if mesh is non-manifold this situation could arise while (cur != null && !cur.edge.common.Equals(wing.edge.common)) { fragment.Add(cur); cur = NextSpoke(cur, sharedIndex, opp); opp = !opp; } fragment.Reverse(); spokes.AddRange(fragment); return(spokes); }
public void GenerateMesh(WingedEdge w) { gameObject.AddComponent <MeshFilter>(); gameObject.AddComponent <MeshRenderer>(); List <int> triangles = new List <int>(); List <Vector3> vertices = new List <Vector3>(); Mesh mesh = new Mesh(); GetComponent <MeshFilter>().mesh = mesh; int aux = 0; for (int i = 0; i < w.Vertices.Count; i++) { for (int j = 0; j < w.Vertices[i].Edges.Count; j++) { if (w.Vertices[i].Edges[j].RightFace != null && w.Vertices[i].Edges[j].LeftFace != null) { Vector3 a = w.Vertices[i].Position; Vector3 b = w.Vertices[i].Edges[j].LeftFace.FaceCircumcenter; Vector3 c = w.Vertices[i].Edges[j].RightFace.FaceCircumcenter; b = PointInLine(a, b); c = PointInLine(a, c); if (!IsClockwise(a, b, c)) { Vector3 aux2 = c; c = b; b = aux2; } vertices.Add(a); vertices.Add(b); vertices.Add(c); triangles.Add(aux + 0); triangles.Add(aux + 1); triangles.Add(aux + 2); aux += 3; } } } mesh.vertices = vertices.ToArray(); // y - y1 y2 - y1 mesh.triangles = triangles.ToArray(); // x - x1 = x2 - x1 }
/// <summary> /// Grow faces to include any face touching the perimeter edges. /// </summary> /// <param name="mesh">The source mesh.</param> /// <param name="faces">The faces to grow out from.</param> /// <param name="maxAngleDiff">If provided, adjacent faces must have a normal that is within maxAngleDiff (in degrees) difference of the perimeter face.</param> /// <returns>The original faces selection, plus any new faces added as a result the grow operation.</returns> public static HashSet <Face> GrowSelection(ProBuilderMesh mesh, IEnumerable <Face> faces, float maxAngleDiff = -1f) { List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh, true); HashSet <Face> source = new HashSet <Face>(faces); HashSet <Face> neighboring = new HashSet <Face>(); Vector3 srcNormal = Vector3.zero; bool checkAngle = maxAngleDiff > 0f; for (int i = 0; i < wings.Count; i++) { if (!source.Contains(wings[i].face)) { continue; } if (checkAngle) { srcNormal = Math.Normal(mesh, wings[i].face); } using (var it = new WingedEdgeEnumerator(wings[i])) { while (it.MoveNext()) { var w = it.Current; if (w.opposite != null && !source.Contains(w.opposite.face)) { if (checkAngle) { Vector3 oppNormal = Math.Normal(mesh, w.opposite.face); if (Vector3.Angle(srcNormal, oppNormal) < maxAngleDiff) { neighboring.Add(w.opposite.face); } } else { neighboring.Add(w.opposite.face); } } } } } return(neighboring); }
/// <summary> /// Iterates through face edges and builds a list using the opposite edge, iteratively. /// </summary> /// <param name="pb">The probuilder mesh</param> /// <param name="edges">The edges already selected</param> /// <returns>The new selected edges</returns> internal static IEnumerable <Edge> GetEdgeRingIterative(ProBuilderMesh pb, IEnumerable <Edge> edges) { List <WingedEdge> wings = WingedEdge.GetWingedEdges(pb); List <EdgeLookup> edgeLookup = EdgeLookup.GetEdgeLookup(edges, pb.sharedVertexLookup).ToList(); edgeLookup = edgeLookup.Distinct().ToList(); Dictionary <Edge, WingedEdge> wings_dic = new Dictionary <Edge, WingedEdge>(); for (int i = 0; i < wings.Count; i++) { if (!wings_dic.ContainsKey(wings[i].edge.common)) { wings_dic.Add(wings[i].edge.common, wings[i]); } } HashSet <EdgeLookup> used = new HashSet <EdgeLookup>(); for (int i = 0, c = edgeLookup.Count; i < c; i++) { WingedEdge we; if (!wings_dic.TryGetValue(edgeLookup[i].common, out we)) { continue; } WingedEdge cur = we; if (!used.Contains(cur.edge)) { used.Add(cur.edge); } var next = EdgeRingNext(cur); if (next != null && next.opposite != null && !used.Contains(next.edge)) { used.Add(next.edge); } var prev = EdgeRingNext(cur.opposite); if (prev != null && prev.opposite != null && !used.Contains(prev.edge)) { used.Add(prev.edge); } } return(used.Select(x => x.local)); }
/// <summary> /// Ensure that all adjacent face normals are pointing in a uniform direction. This function supports multiple islands of connected faces, but it may not unify each island the same way. /// </summary> /// <param name="mesh">The mesh that the faces belong to.</param> /// <param name="faces">The faces to make uniform.</param> /// <returns>The state of the action.</returns> public static ActionResult ConformNormals(this ProBuilderMesh mesh, IEnumerable <Face> faces) { List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh, faces); HashSet <Face> used = new HashSet <Face>(); int count = 0; // this loop adds support for multiple islands of grouped selections for (int i = 0; i < wings.Count; i++) { if (used.Contains(wings[i].face)) { continue; } Dictionary <Face, bool> flags = new Dictionary <Face, bool>(); GetWindingFlags(wings[i], true, flags); int flip = 0; foreach (var kvp in flags) { flip += kvp.Value ? 1 : -1; } bool direction = flip > 0; foreach (var kvp in flags) { if (direction != kvp.Value) { count++; kvp.Key.Reverse(); } } used.UnionWith(flags.Keys); } if (count > 0) { return(new ActionResult(ActionResult.Status.Success, count > 1 ? string.Format("Flipped {0} faces", count) : "Flipped 1 face")); } else { return(new ActionResult(ActionResult.Status.NoChange, "Faces Uniform")); } }
/// <summary> /// Recursively add all faces touching any of the selected faces. /// </summary> /// <param name="mesh">The source mesh.</param> /// <param name="faces">The starting faces.</param> /// <param name="maxAngleDiff">Faces must have a normal that is within maxAngleDiff (in degrees) difference of the perimeter face to be added to the collection.</param> /// <returns>A collection of faces that are connected by shared edges to the original faces.</returns> public static HashSet <Face> FloodSelection(ProBuilderMesh mesh, IList <Face> faces, float maxAngleDiff) { List <WingedEdge> wings = WingedEdge.GetWingedEdges(mesh, true); HashSet <Face> source = new HashSet <Face>(faces); HashSet <Face> flood = new HashSet <Face>(); for (int i = 0; i < wings.Count; i++) { if (!flood.Contains(wings[i].face) && source.Contains(wings[i].face)) { flood.Add(wings[i].face); Flood(mesh, wings[i], maxAngleDiff > 0f ? Math.Normal(mesh, wings[i].face) : Vector3_Zero, maxAngleDiff, flood); } } return(flood); }
static WingedEdge NextSpoke(WingedEdge wing, int pivot, bool opp) { if (opp) { return(wing.opposite); } if (wing.next.edge.common.Contains(pivot)) { return(wing.next); } if (wing.previous.edge.common.Contains(pivot)) { return(wing.previous); } return(null); }
/** * Get a weighted value for the quality of a quad composed of two triangles. 0 is terrible, 1 is perfect. * normalThreshold will discard any quads where the dot product of their normals is less than the threshold. * @todo Abstract the quad detection to a separate class so it can be applied to pb_Objects. */ static float GetQuadScore(this ProBuilderMesh mesh, WingedEdge left, WingedEdge right, float normalThreshold = .9f) { Vertex[] vertices = mesh.GetVertices(); int[] quad = WingedEdge.MakeQuad(left, right); if (quad == null) { return(0f); } // first check normals Vector3 leftNormal = Math.Normal(vertices[quad[0]].position, vertices[quad[1]].position, vertices[quad[2]].position); Vector3 rightNormal = Math.Normal(vertices[quad[2]].position, vertices[quad[3]].position, vertices[quad[0]].position); float score = Vector3.Dot(leftNormal, rightNormal); if (score < normalThreshold) { return(0f); } // next is right-angle-ness check Vector3 a = (vertices[quad[1]].position - vertices[quad[0]].position); Vector3 b = (vertices[quad[2]].position - vertices[quad[1]].position); Vector3 c = (vertices[quad[3]].position - vertices[quad[2]].position); Vector3 d = (vertices[quad[0]].position - vertices[quad[3]].position); a.Normalize(); b.Normalize(); c.Normalize(); d.Normalize(); float da = Mathf.Abs(Vector3.Dot(a, b)); float db = Mathf.Abs(Vector3.Dot(b, c)); float dc = Mathf.Abs(Vector3.Dot(c, d)); float dd = Mathf.Abs(Vector3.Dot(d, a)); score += 1f - ((da + db + dc + dd) * .25f); // and how close to parallel the opposite sides area score += Mathf.Abs(Vector3.Dot(a, c)) * .5f; score += Mathf.Abs(Vector3.Dot(b, d)) * .5f; // the three tests each contribute 1 return(score * .33f); }
public void GenerateMesh(WingedEdge w) { List <Vector2> vertices2D = new List <Vector2>(); for (int i = 0; i < w.Vertices.Count; i++) { for (int j = 0; j < w.Vertices[i].Edges.Count; j++) { if (w.Vertices[i].Edges[j].RightFace != null && w.Vertices[i].Edges[j].LeftFace != null) { float x = w.Vertices[i].Edges[j].LeftFace.FaceCircumcenter.x; float y = w.Vertices[i].Edges[j].LeftFace.FaceCircumcenter.z; Vector2 b = new Vector2(x, y); b = PointInLine(w.Vertices[i].Position, b); if (!vertices2D.Contains(b)) { vertices2D.Add(b); } float x2 = w.Vertices[i].Edges[j].RightFace.FaceCircumcenter.x; float y2 = w.Vertices[i].Edges[j].RightFace.FaceCircumcenter.z; Vector2 c = new Vector2(x2, y2); c = PointInLine(w.Vertices[i].Position, c); if (!vertices2D.Contains(c)) { vertices2D.Add(c); } } } if (vertices2D.Count >= 3) { GrahamScan(vertices2D); } Triangulate(vertices2D.ToArray()); vertices2D.Clear(); } // Use the triangulator to get indices for creating triangles }
static WingedEdge FindNextEdgeInHole(WingedEdge wing, int common) { WingedEdge next = wing.GetAdjacentEdgeWithCommonIndex(common); int counter = 0; while (next != null && next != wing && counter++ < k_MaxHoleIterations) { if (next.opposite == null) { return(next); } next = next.opposite.GetAdjacentEdgeWithCommonIndex(common); } return(null); }
public static bool?ConformOppositeNormal(WingedEdge source) { if (source == null || source.opposite == null) { return(false); } Edge cea = GetCommonEdgeInWindingOrder(source); Edge ceb = GetCommonEdgeInWindingOrder(source.opposite); if (cea.a == ceb.a) { source.opposite.face.Reverse(); return(true); } return(null); //no change }
public void CheckEdge(WingedEdge w) { bool aux = false; foreach (EdgeWE e in w.Edges) { if (e.LeftFace != null && e.RightFace != null) { List <VertexWE> v1 = w.GetFaceVertices(e.RightFace); List <VertexWE> v2 = w.GetFaceVertices(e.LeftFace); VertexWE RightOppositeVertex = null; VertexWE LeftOppositeVertex = null; for (int i = 0; i < v1.Count; i++) { if (!Equals(v1[i], e.Vertex1) && !Equals(v1[i], e.Vertex2)) { RightOppositeVertex = v1[i]; } if (!Equals(v2[i], e.Vertex1) && !Equals(v2[i], e.Vertex2)) { LeftOppositeVertex = v2[i]; } } Vector3 Rightcc = CalculateCircumscribedCircumference(v1[0], v1[1], v1[2]); Vector3 Leftcc = CalculateCircumscribedCircumference(v2[0], v2[1], v2[2]); double RightccRadius = CalculateRadius(Rightcc, v1[0], v1[1], v1[2]); double LeftccRadius = CalculateRadius(Leftcc, v2[0], v2[1], v2[2]); if (InsideCC(Rightcc, RightccRadius, LeftOppositeVertex) || InsideCC(Leftcc, LeftccRadius, RightOppositeVertex)) { List <FaceWE> newfaces = w.FlipEdge(e); aux = true; break; } } } if (aux) { CheckEdge(w); } }
/// <summary> /// Ensure the opposite face to source matches the winding order. /// </summary> /// <param name="source"></param> /// <returns></returns> internal static ActionResult ConformOppositeNormal(WingedEdge source) { if (source == null || source.opposite == null) { return(new ActionResult(ActionResult.Status.Failure, "Source edge does not share an edge with another face.")); } Edge cea = GetCommonEdgeInWindingOrder(source); Edge ceb = GetCommonEdgeInWindingOrder(source.opposite); if (cea.a == ceb.a) { source.opposite.face.Reverse(); return(new ActionResult(ActionResult.Status.Success, "Reversed target face winding order.")); } return(new ActionResult(ActionResult.Status.NoChange, "Faces already unified.")); }
static IEnumerable <List <Face> > GetFaceSelectionGroups(ProBuilderMesh mesh) { var wings = WingedEdge.GetWingedEdges(mesh, mesh.selectedFacesInternal, true); var filter = new HashSet <Face>(); var groups = new List <List <Face> >(); foreach (var wing in wings) { var group = new List <Face>() { }; CollectAdjacentFaces(wing, filter, group); if (group.Count > 0) { groups.Add(group); } } return(groups); }
static Edge GetLeadingEdge(WingedEdge wing, int common) { if (wing.previous.edge.common.a == common) { return(new Edge(wing.previous.edge.local.b, wing.previous.edge.local.a)); } else if (wing.previous.edge.common.b == common) { return(new Edge(wing.previous.edge.local.a, wing.previous.edge.local.b)); } else if (wing.next.edge.common.a == common) { return(new Edge(wing.next.edge.local.b, wing.next.edge.local.a)); } else if (wing.next.edge.common.b == common) { return(new Edge(wing.next.edge.local.a, wing.next.edge.local.b)); } return(Edge.Empty); }
static void GetWindingFlags(WingedEdge edge, bool flag, Dictionary <Face, bool> flags) { flags.Add(edge.face, flag); WingedEdge next = edge; do { WingedEdge opp = next.opposite; if (opp != null && !flags.ContainsKey(opp.face)) { Edge cea = GetCommonEdgeInWindingOrder(next); Edge ceb = GetCommonEdgeInWindingOrder(opp); GetWindingFlags(opp, cea.a == ceb.a ? !flag : flag, flags); } next = next.next; }while (next != edge); }