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));
        }
Example #6
0
        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);
        }
Example #9
0
        /// <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);
        }
Example #11
0
        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);
        }
Example #12
0
        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);
        }
Example #14
0
        /// <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);
        }
Example #16
0
    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));
        }
Example #19
0
        /// <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);
        }
Example #23
0
    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
    }
Example #24
0
        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
        }
Example #26
0
    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);
        }
    }
Example #27
0
        /// <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);
        }
Example #29
0
        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);
        }
Example #30
0
        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);
        }