Example #1
0
        /**
         * Get all edges that are on the perimeter of this face group selection.
         */
        public static IEnumerable <pb_Edge> GetPerimeterEdges(pb_Object pb, Dictionary <int, int> sharedIndicesLookup, IEnumerable <pb_Face> faces)
        {
            List <pb_Edge> faceEdges = faces.SelectMany(x => x.edges).ToList();                 /// actual edges
            int            edgeCount = faceEdges.Count;

            // translate all face edges to universal edges
            Dictionary <pb_Edge, List <pb_Edge> > dup = new Dictionary <pb_Edge, List <pb_Edge> >();
            List <pb_Edge> list;

            for (int i = 0; i < edgeCount; i++)
            {
                pb_Edge uni = new pb_Edge(sharedIndicesLookup[faceEdges[i].x], sharedIndicesLookup[faceEdges[i].y]);

                if (dup.TryGetValue(uni, out list))
                {
                    list.Add(faceEdges[i]);
                }
                else
                {
                    dup.Add(uni, new List <pb_Edge>()
                    {
                        faceEdges[i]
                    });
                }
            }

            return(dup.Where(x => x.Value.Count < 2).Select(x => x.Value[0]));
        }
Example #2
0
        public static List <pb_Face> GetNeighborFaces(pb_Object pb, pb_Edge edge, Dictionary <int, int> lookup)
        {
            List <pb_Face> faces = new List <pb_Face>();

            pb_Edge uni = new pb_Edge(lookup[edge.x], lookup[edge.y]);
            pb_Edge e   = new pb_Edge(0, 0);

            for (int i = 0; i < pb.faces.Length; i++)
            {
                pb_Edge[] edges = pb.faces[i].edges;
                for (int n = 0; n < edges.Length; n++)
                {
                    e.x = edges[n].x;
                    e.y = edges[n].y;

                    if ((uni.x == lookup[e.x] && uni.y == lookup[e.y]) ||
                        (uni.x == lookup[e.y] && uni.y == lookup[e.x]))
                    {
                        faces.Add(pb.faces[i]);
                        break;
                    }
                }
            }
            return(faces);
        }
		/**
		 * \brief Returns all connected faces keeping adjacent faces ordered to correspond to the order they were passed.
		 */
		public static List<pb_Face>[][] GetConnectedFacesJagged(pb_Object pb, pb_Face[] selFaces)
		{
			int len = selFaces.Length;

			List<pb_Face>[][] faces = new List<pb_Face>[len][];
			for(int j = 0; j < len; j++)
			{
				faces[j] = new List<pb_Face>[selFaces[j].edges.Length];
				for(int i = 0; i < selFaces[j].edges.Length; i++)
					faces[j][i] = 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[j][ind].Add(pb.faces[i]);
				}
			}

			return faces;
		}
Example #4
0
        /**
         * Returns an array of faces where each face has at least one non-shared edge.
         */
        public static IEnumerable <pb_Face> GetPerimeterFaces(pb_Object pb, IEnumerable <pb_Face> faces)
        {
            Dictionary <int, int> lookup = pb.sharedIndices.ToDictionary();
            Dictionary <pb_Edge, List <pb_Face> > sharedEdges = new Dictionary <pb_Edge, List <pb_Face> >();

            /**
             * To be considered a perimeter face, at least one edge must not share
             * any boundary with another face.
             */

            foreach (pb_Face face in faces)
            {
                foreach (pb_Edge e in face.edges)
                {
                    pb_Edge edge = new pb_Edge(lookup[e.x], lookup[e.y]);

                    if (sharedEdges.ContainsKey(edge))
                    {
                        sharedEdges[edge].Add(face);
                    }
                    else
                    {
                        sharedEdges.Add(edge, new List <pb_Face>()
                        {
                            face
                        });
                    }
                }
            }

            return(sharedEdges.Where(x => x.Value.Count < 2).Select(x => x.Value[0]).Distinct());
        }
Example #5
0
        /**
         * \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);
        }
Example #6
0
        /**
         * Returns a unique array of Edges connected to the passed vertex indices.
         */
        public static pb_Edge[] GetConnectedEdges(pb_Object pb, int[] indices)
        {
            Dictionary <int, int> lookup = pb.sharedIndices.ToDictionary();

            List <pb_Edge> connectedEdges = new List <pb_Edge>();

            HashSet <int> shared = new HashSet <int>();

            for (int i = 0; i < indices.Length; i++)
            {
                shared.Add(lookup[indices[i]]);
            }

            pb_Edge[]         edges = pb_Edge.AllEdges(pb.faces);
            HashSet <pb_Edge> used  = new HashSet <pb_Edge>();

            pb_Edge uni = new pb_Edge(0, 0);

            for (int i = 0; i < edges.Length; i++)
            {
                pb_Edge key = new pb_Edge(lookup[edges[i].x], lookup[edges[i].y]);

                if (shared.Contains(key.x) || shared.Contains(key.y) && !used.Contains(uni))
                {
                    connectedEdges.Add(edges[i]);
                    used.Add(key);
                }
            }

            return(connectedEdges.ToArray());
        }
Example #7
0
        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();
        }
Example #8
0
        /**
         * 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));
        }
Example #9
0
 /**
  * Get vertices at x,y index with edge.
  */
 public Vector3[] GetEdgeVertices(pb_Edge edge)
 {
     return(new Vector3[]
     {
         _vertices[edge.x],
         _vertices[edge.y]
     });
 }
Example #10
0
        /**
         * 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());
        }
Example #11
0
 public pb_Edge[] GetEdges()
 {
     pb_Edge[] edges = new pb_Edge[indices.Length];
     for (int i = 0; i < indices.Length; i += 3)
     {
         edges[i + 0] = new pb_Edge(indices[i + 0], indices[i + 1]);
         edges[i + 1] = new pb_Edge(indices[i + 1], indices[i + 2]);
         edges[i + 2] = new pb_Edge(indices[i + 2], indices[i + 0]);
     }
     return(edges);
 }
		/**
		 *	Returns all faces connected to the passed edge.
		 */
		public static List<pb_Face> GetConnectedFaces(pb_Object pb, pb_Edge edge)
		{
			List<pb_Face> faces = new List<pb_Face>();
			pb_IntArray[] sharedIndices = pb.sharedIndices;

			foreach(pb_Face f in pb.faces)
			{
				if(f.edges.IndexOf(edge, sharedIndices) > -1)
					faces.Add(f);
			}
			return faces;
		}
Example #13
0
        /**
         * Returns the opposite edge on the neighboring face (if possible - if the edge does not connect to an additional face opposite_face will be null).
         */
        public static bool GetOppositeEdge(pb_Object pb, pb_Face face, pb_Edge edge, Dictionary <int, int> lookup, out pb_Face opposite_face, out pb_Edge opposite_edge)
        {
            opposite_face = null;
            opposite_edge = null;

            if (face.edges.Length != 4)
            {
                return(false);
            }

            // Construct a list of all edges starting at vertex edge.y and going around the face.  Then grab the middle edge.
            pb_Edge[] ordered_edges = new pb_Edge[face.edges.Length];
            ordered_edges[0] = edge;

            for (int i = 1; i < face.edges.Length; i++)
            {
                foreach (pb_Edge e in face.edges)
                {
                    if (e.x == ordered_edges[i - 1].y)
                    {
                        ordered_edges[i] = e;
                        break;
                    }
                }
            }
            pb_Edge opEdgeLocal = ordered_edges[face.edges.Length / 2];

            List <pb_Face> connectedFaces = pbMeshUtils.GetNeighborFaces(pb, opEdgeLocal, lookup);

            connectedFaces.Remove(face);

            if (connectedFaces.Count < 1)
            {
                opposite_edge = opEdgeLocal;                    // sometimes ya still want this edge (planes, for example)
                return(true);
            }

            opposite_face = connectedFaces[0];

            for (int i = 0; i < opposite_face.edges.Length; i++)
            {
                if (opposite_face.edges[i].Equals(opEdgeLocal, lookup))
                {
                    opposite_edge = opposite_face.edges[i];
                    break;
                }
            }

            return(true);
        }
Example #14
0
        /**
         *	Returns all faces connected to the passed edge.
         */
        public static List <pb_Face> GetConnectedFaces(pb_Object pb, pb_Edge edge)
        {
            List <pb_Face> faces = new List <pb_Face>();

            pb_IntArray[] sharedIndices = pb.sharedIndices;

            foreach (pb_Face f in pb.faces)
            {
                if (f.edges.IndexOf(edge, sharedIndices) > -1)
                {
                    faces.Add(f);
                }
            }
            return(faces);
        }
Example #15
0
        internal static List <pb_Face>[][] GetNeighborFacesJagged(pb_Object pb, pb_Edge[][] selEdges)
        {
            int len = selEdges.Length;

            List <pb_Face>[][] faces = new List <pb_Face> [len][];
            for (int j = 0; j < len; j++)
            {
                faces[j] = new List <pb_Face> [selEdges[j].Length];
                for (int i = 0; i < selEdges[j].Length; i++)
                {
                    faces[j][i] = 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(selEdges[i], sharedIndices).Distinct().ToArray();
            }

            for (int i = 0; i < pb.faces.Length; i++)
            {
                pb_Edge[] faceEdges = pb_Edge.GetUniversalEdges(pb.faces[i].edges, sharedIndices).Distinct().ToArray();

                for (int j = 0; j < len; j++)
                {
                    int ind = -1;
                    for (int t = 0; t < sharedEdges[j].Length; t++)
                    {
                        if (faceEdges.Contains(sharedEdges[j][t]))
                        {
                            ind = t;
                            break;
                        }
                    }

                    if (ind > -1)
                    {
                        faces[j][ind].Add(pb.faces[i]);
                    }
                }
            }

            return(faces);
        }
Example #16
0
        /**
         * The SelectedEdges array contains Edges made up of indices that aren't guaranteed to be 'valid' - that is, they
         * may not belong to the same face.  This method extracts an edge and face combo from the face independent edge
         * selection.
         * @param faces - Corresponding face to edge list
         * @param edges - An edge composed of indices that belong to a same face (matching face in faces List).
         * @returns True if at least one valid edge is found, false if not.
         */
        public static bool ValidFaceAndEdgeWithEdge(pb_Object pb, pb_Edge faceIndependentEdge, Dictionary <int, int> sharedIndices, out List <pb_Face> faces, out List <pb_Edge> edges)
        {
            faces = new List <pb_Face>();
            edges = new List <pb_Edge>();

            foreach (pb_Face f in pb.faces)
            {
                int ind = f.edges.IndexOf(faceIndependentEdge, sharedIndices);

                if (ind > -1)
                {
                    faces.Add(f);
                    edges.Add(f.edges[ind]);
                }
            }

            return(faces.Count > 0);
        }
Example #17
0
        /**
         * \brief Returns faces that share an edge with any of @c selFcaes.
         */
        public static pb_Face[] GetNeighborFaces(pb_Object pb, Dictionary <int, int> sharedIndicesLookup, pb_Face[] selFaces)
        {
            List <pb_Face> perimeterFaces = new List <pb_Face>();

            pb_Edge[] perimeterEdges = pbMeshUtils.GetPerimeterEdges(pb, sharedIndicesLookup, selFaces).ToArray();
            pb_Edge[] universalEdges = new pb_Edge[perimeterEdges.Length];

            for (int i = 0; i < perimeterEdges.Length; i++)
            {
                universalEdges[i] = new pb_Edge(sharedIndicesLookup[perimeterEdges[i].x],
                                                sharedIndicesLookup[perimeterEdges[i].y]);
            }

            pb_Edge edge_u = new pb_Edge(-1, -1);

            HashSet <pb_Face> skip = new HashSet <pb_Face>(selFaces);

            foreach (pb_Face face in pb.faces)
            {
                if (skip.Contains(face))
                {
                    skip.Remove(face);
                    continue;
                }

                foreach (pb_Edge edge in face.edges)
                {
                    edge_u.x = sharedIndicesLookup[edge.x];
                    edge_u.y = sharedIndicesLookup[edge.y];

                    if (universalEdges.Contains(edge_u))
                    {
                        perimeterFaces.Add(face);
                        break;
                    }
                }
            }

            return(perimeterFaces.ToArray());
        }
Example #18
0
		/**
		 * 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;
		}
Example #19
0
        /**
         * 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);
        }
    private void ExtrudeEdge()
    {
        pb_Face sourceFace = lastExtrudedFace;
        List <pb_WingedEdge>        wingedEdges = pb_WingedEdge.GetWingedEdges(pb);
        IEnumerable <pb_WingedEdge> source      = wingedEdges.Where((pb_WingedEdge x) => x.face == sourceFace);
        List <pb_Edge> list = (from x in source
                               where x.opposite == null
                               select x into y
                               select y.edge.local).ToList();
        int     index   = Random.Range(0, list.Count);
        pb_Edge pb_Edge = list[index];
        Vector3 a       = (pb.vertices[pb_Edge.x] + pb.vertices[pb_Edge.y]) * 0.5f - sourceFace.distinctIndices.Average((int x) => pb.vertices[x]);

        a.Normalize();
        pb.Extrude(new pb_Edge[1]
        {
            pb_Edge
        }, 0f, extrudeAsGroup: false, enableManifoldExtrude: true, out pb_Edge[] extrudedEdges);
        lastExtrudedFace = pb.faces.Last();
        pb.SetSelectedEdges(extrudedEdges);
        pb.TranslateVertices(pb.SelectedTriangles, a * distance);
        pb.ToMesh();
        pb.Refresh();
    }
Example #21
0
		/**
		 * Checks if mouse is over an edge, and if so, returns true setting @edge.
		 */
		public static bool EdgeRaycast(Camera cam, Vector2 mousePosition, pb_Object mesh, pb_Edge[] edges, Vector3[] verticesInWorldSpace, out pb_Edge edge)
		{
			Vector3 v0, v1;
			float bestDistance = Mathf.Infinity;
			float distance = 0f;
			edge = null;

			GameObject go = ObjectRaycast(cam, mousePosition, (GameObject[]) Resources.FindObjectsOfTypeAll(typeof(GameObject)));

			if( go == null || go != mesh.gameObject)
			{
				int width = Screen.width;
				int height = Screen.height;

				for(int i = 0; i < edges.Length; i++)
				{
					v0 = verticesInWorldSpace[edges[i].x];
					v1 = verticesInWorldSpace[edges[i].y];
					
					distance = pb_HandleUtility.DistancePoint2DToLine(cam, mousePosition, v0, v1);

					if ( distance < bestDistance && distance < MAX_EDGE_SELECT_DISTANCE )// && !PointIsOccluded(mesh, (v0+v1)*.5f) )
					{
						Vector3 vs0 = cam.WorldToScreenPoint(v0);
						
						// really simple frustum check (will fail on edges that have vertices outside the frustum but is visible)
						if( vs0.z <= 0 || vs0.x < 0 || vs0.y < 0 || vs0.x > width || vs0.y > height )
							continue;

						Vector3 vs1 = cam.WorldToScreenPoint(v1);

						if( vs1.z <= 0 || vs1.x < 0 || vs1.y < 0 || vs1.x > width || vs1.y > height )
							continue;


						bestDistance = distance;
						edge = edges[i];
					}
				}
			}
			else
			{
				// Test culling
				List<pb_RaycastHit> hits;
				Ray ray = cam.ScreenPointToRay(mousePosition);// HandleUtility.GUIPointToWorldRay(mousePosition);

				if( FaceRaycast(ray, mesh, out hits, Mathf.Infinity, Culling.FrontBack) )
				{
					// Sort from nearest hit to farthest
					hits.Sort( (x, y) => x.Distance.CompareTo(y.Distance) );
					
					// Find the nearest edge in the hit faces
					Vector3[] v = mesh.vertices;

					for(int i = 0; i < hits.Count; i++)
					{
						if( pb_HandleUtility.PointIsOccluded(cam, mesh, mesh.transform.TransformPoint(hits[i].Point)) )
							continue;

						foreach(pb_Edge e in mesh.faces[hits[i].FaceIndex].GetEdges())
						{
							float d = pb_Math.DistancePointLineSegment(hits[i].Point, v[e.x], v[e.y]);

							if(d < bestDistance)
							{
								bestDistance = d;
								edge = e;
							}
						}

						if( Vector3.Dot(ray.direction, mesh.transform.TransformDirection(hits[i].Normal)) < 0f )
							break;
					}

					if(edge != null && pb_HandleUtility.DistancePoint2DToLine(cam, mousePosition, mesh.transform.TransformPoint(v[edge.x]), mesh.transform.TransformPoint(v[edge.y])) > MAX_EDGE_SELECT_DISTANCE)
					{
						edge = null;
					}
					// else
					// {
					// 	edge.x = mesh.ToUserIndex(edge.x);
					// 	edge.y = mesh.ToUserIndex(edge.y);
					// }
				}
			}

			return edge != null;
		}
Example #22
0
		/**
		 * 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 void SetSelectedEdges(pb_Edge[] edges)
	{
		this.m_selectedFaces = new pb_Face[0];
		this.m_SelectedEdges = edges;
		this.m_selectedTriangles = m_SelectedEdges.ToIntArray();				
		// this.m_selectedTriangles = sharedIndices.UniqueIndicesWithValues(m_SelectedEdges.ToIntArray());				
	}
Example #24
0
	/**
	 *	Edge extrusion override
	 */
	public static bool Extrude(this pb_Object pb, pb_Edge[] edges, float extrudeDistance, bool extrudeAsGroup, bool enableManifoldExtrude, out pb_Edge[] extrudedEdges)
	{
		pb_IntArray[] sharedIndices = pb.sharedIndices;
		Dictionary<int, int> lookup = sharedIndices.ToDictionary();

		List<pb_Edge> validEdges = new List<pb_Edge>();
		List<pb_Face> edgeFaces = new List<pb_Face>();
		
		foreach(pb_Edge e in edges)
		{
			int faceCount = 0;
			pb_Face fa = null;
			foreach(pb_Face f in pb.faces)
			{
				if(f.edges.IndexOf(e, lookup) > -1)
				{
					fa = f;
					if(++faceCount > 1)
						break;
				}

			}

			if(enableManifoldExtrude || faceCount < 2)
			{
				validEdges.Add(e);
				edgeFaces.Add(fa);
			}
		}

		if(validEdges.Count < 1)
		{
			extrudedEdges = null;
			return false;
		}

		Vector3[] localVerts = pb.vertices;
		Vector3[] oNormals = pb.msh.normals;

		int[] allEdgeIndices = new int[validEdges.Count * 2];
		int c = 0;
		for(int i = 0; i < validEdges.Count; i++)
		{
			allEdgeIndices[c++] = validEdges[i].x;
			allEdgeIndices[c++] = validEdges[i].y;
		}

		List<pb_Edge> extrudedIndices = new List<pb_Edge>();
		List<pb_Edge> newEdges = new List<pb_Edge>();		// used to set the editor selection to the newly created edges

		/// build out new faces around validEdges

		for(int i = 0; i < validEdges.Count; i++)
		{
			pb_Edge edge = validEdges[i];
			pb_Face face = edgeFaces[i];

			// Averages the normals using only vertices that are on the edge
			Vector3 xnorm = extrudeAsGroup ? Norm( sharedIndices[lookup[edge.x]], allEdgeIndices, oNormals ) : pb_Math.Normal(pb, face);
			Vector3 ynorm = extrudeAsGroup ? Norm( sharedIndices[lookup[edge.y]], allEdgeIndices, oNormals ) : pb_Math.Normal(pb, face);

			int x_sharedIndex = lookup[edge.x];
			int y_sharedIndex = lookup[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 Color[4] 
				{
					pb.colors[ edge.x ],
					pb.colors[ edge.y ],
					pb.colors[ edge.x ],
					pb.colors[ edge.y ]
				},
				new Vector2[4],
				new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, face.material, new pb_UV(), 0, -1, -1, false ),
				new int[4] { x_sharedIndex, y_sharedIndex, -1, -1 });

			newEdges.Add(new pb_Edge(newFace.indices[3], newFace.indices[4]));

			extrudedIndices.Add(new pb_Edge(x_sharedIndex, newFace.indices[3]));
			extrudedIndices.Add(new pb_Edge(y_sharedIndex, newFace.indices[4]));
		}

		sharedIndices = pb.sharedIndices;

		// merge extruded vertex indices with each other
		if(extrudeAsGroup)
		{
			for(int i = 0; i < extrudedIndices.Count; i++)
			{
				int val = extrudedIndices[i].x;
				for(int n = 0; n < extrudedIndices.Count; n++)
				{
					if(n == i)
						continue;

					if(extrudedIndices[n].x == val)
					{
						pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, extrudedIndices[n].y, extrudedIndices[i].y);
						break;
					}
				}
			}
		}

		pb.SetSharedIndices(sharedIndices);
		pb.RebuildFaceCaches();
		
		extrudedEdges = newEdges.ToArray();
		return true;
	}
Example #25
0
	/**
	 * If in Edge mode, finds the nearest Edge to the mouse
	 */
	private void UpdateMouse(Vector3 mousePosition)
	{
		if(selection.Length < 1) return;

		switch(selectionMode)
		{
			// default:
			case SelectMode.Edge:

				GameObject go = HandleUtility.PickGameObject(mousePosition, false);

				pb_Edge bestEdge = null;
				pb_Object bestObj = go == null ? null : go.GetComponent<pb_Object>();

				if(bestObj != null && !selection.Contains(bestObj))
				{
					bestObj = null;
					bestEdge = null;
					goto SkipMouseCheck;
				}

				/**
				 * If mouse isn't over a pb object, it still may be near enough to an edge.
				 */
				if(bestObj == null)
				{
					// TODO
					float bestDistance = MAX_EDGE_SELECT_DISTANCE;				

					try 
					{
						for(int i = 0; i < m_universalEdges.Length; i++)
						{
							pb_Edge[] edges = m_universalEdges[i];
							
							for(int j = 0; j < edges.Length; j++)
							{
								int x = selection[i].sharedIndices[edges[j].x][0];
								int y = selection[i].sharedIndices[edges[j].y][0];

								Vector3 world_vert_x = m_verticesInWorldSpace[i][x];
								Vector3 world_vert_y = m_verticesInWorldSpace[i][y];

								float d = HandleUtility.DistanceToLine(world_vert_x, world_vert_y);
								
								if(d < bestDistance)
								{
									bestObj = selection[i];
									bestEdge = new pb_Edge(x, y);
									bestDistance = d;
								}
							}			
						}
					} catch {}
				}
				else
				{
					// Test culling
					List<pb_RaycastHit> hits;
					Ray ray = HandleUtility.GUIPointToWorldRay(mousePosition);

					if(pb_Handle_Utility.MeshRaycast(ray, bestObj, out hits, Mathf.Infinity, Culling.FrontBack))
					{
						Camera cam = SceneView.lastActiveSceneView.camera;

						// Sort from nearest hit to farthest
						hits.Sort( (x, y) => x.Distance.CompareTo(y.Distance) );
						
						// Find the nearest edge in the hit faces

						float bestDistance = Mathf.Infinity;
						Vector3[] v = bestObj.vertices;

						for(int i = 0; i < hits.Count; i++)
						{
							if( pb_HandleUtility.PointIsOccluded(cam, bestObj, bestObj.transform.TransformPoint(hits[i].Point)) )
								continue;

							foreach(pb_Edge edge in bestObj.faces[hits[i].FaceIndex].edges)
							{
								float d = HandleUtility.DistancePointLine(hits[i].Point, v[edge.x], v[edge.y]);

								if(d < bestDistance)
								{
									bestDistance = d;
									bestEdge = edge;
								}
							}

							if( Vector3.Dot(ray.direction, bestObj.transform.TransformDirection(hits[i].Normal)) < 0f )
								break;
						}

						if(bestEdge != null && HandleUtility.DistanceToLine(bestObj.transform.TransformPoint(v[bestEdge.x]), bestObj.transform.TransformPoint(v[bestEdge.y])) > MAX_EDGE_SELECT_DISTANCE)
							bestEdge = null;
					}
				}	

				SkipMouseCheck:

				if(bestEdge != nearestEdge || bestObj != nearestEdgeObject)
				{
					nearestEdge = bestEdge;
					nearestEdgeObject = bestObj;

					SceneView.RepaintAll();
				}
				break;
		}
	}
		// public static pb_Edge[] GetEdgeLoop(pb_Object pb, pb_Edge[] edges)
		// {
		// 	List<pb_Edge> ring = new List<pb_Edge>();

		// 	pb_Edge[] orig_uni_edges = pb_Edge.GetUniversalEdges(edges, pb.sharedIndices);
		// 	pb_Edge[] all_uni_edges = pb_Edge.GetUniversalEdges(pb_Edge.AllEdges(pb.faces), pb.sharedIndices);

		// 	Bugger.Log(orig_uni_edges.ToFormattedString("\n"));
		// 	Bugger.Log(all_uni_edges.ToFormattedString("\n"));

		// 	foreach(pb_Edge e in orig_uni_edges)
		// 	{
		// 		ring.Add(e);
		// 		int lasty = e.y;
		// 		bool foundNeighbor = true;

		// 		int n = 0;
		// 		while(foundNeighbor && n < 32)
		// 		{
		// 			foundNeighbor = false;
					
		// 			foreach(pb_Edge ne in all_uni_edges)	
		// 			{
						
		// 				n++;
		// 				if(ne.Equals(e)) continue;

		// 				if(ne.x == lasty)
		// 				{
		// 					Bugger.Log(e + " = " + ne);
							
		// 					lasty = ne.y;
		// 					ring.Add(ne);
		// 					foundNeighbor = true;
		// 					break;
		// 				}
		// 			}
		// 		}
		// 	}
		
		// 	pb_Edge[] tri_ring = ring.Distinct().ToArray();

		// 	for(int i = 0; i < tri_ring.Length; i++)
		// 	{
		// 		tri_ring[i].x = pb.sharedIndices[tri_ring[i].x][0];
		// 		tri_ring[i].y = pb.sharedIndices[tri_ring[i].y][0];
		// 	}

		// 	return tri_ring;
		// }

		/**
		 * Returns the opposite edge on the neighboring face (if possible - if the edge does not connect to an additional face opposite_face will be null).
		 */
		public static bool GetOppositeEdge(pb_Object pb, pb_Face face, pb_Edge edge, out pb_Face opposite_face, out pb_Edge opposite_edge)
		{
			opposite_face = null;
			opposite_edge = null;
 
			// Construct a list of all edges starting at vertex edge.y and going around the face.  Then grab the middle edge.
			pb_Edge[] ordered_edges = new pb_Edge[face.edges.Length];
			ordered_edges[0] = edge;

			for(int i = 1; i < face.edges.Length; i++)
			{
				foreach(pb_Edge e in face.edges)
				{
					if(e.x == ordered_edges[i-1].y)
					{
						ordered_edges[i] = e;
						break;
					}
				}
			}

			pb_Edge opEdgeLocal = ordered_edges[face.edges.Length/2];

			List<pb_Face> connectedFaces = pbMeshUtils.GetConnectedFaces(pb, opEdgeLocal);
			connectedFaces.Remove(face);

			if(connectedFaces.Count < 1)
			{
				opposite_edge = opEdgeLocal;	// sometimes ya still want this edge (planes, for example)
				return true;
			}

			opposite_face = connectedFaces[0];
			
			for(int i = 0; i < opposite_face.edges.Length; i++)
			{
				if(opposite_face.edges[i].Equals(opEdgeLocal, pb.sharedIndices))
				{
					opposite_edge = opposite_face.edges[i];
					break;
				}
			}

			return true;
		}
		/**
		 * 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();
		}
	/**
	 *	
	 */
	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;
	}
		public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b) { return pb.Bridge(a, b, true); }
Example #30
0
        private static bool ConnectEdges(this pb_Object pb, List <pb_EdgeConnection> pb_edgeConnectionsUnfiltered, out pb_Face[] faces)
        {
            List <pb_EdgeConnection> pb_edgeConnections = new List <pb_EdgeConnection>();

            foreach (pb_EdgeConnection ec in pb_edgeConnectionsUnfiltered)
            {
                if (ec.isValid)
                {
                    pb_edgeConnections.Add(ec);
                }
            }

            int len = pb_edgeConnections.Count;

            if (len < 1)
            {
                faces = null;
                return(false);
            }

            Vector3[] vertices = pb.vertices;
            Color[]   colors   = pb.colors;

            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 <Color[]>   all_splitColors        = new List <Color[]>();
            List <Vector2[]> all_splitUVs           = new List <Vector2[]>();
            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 pb_EdgeConnection, it's easy to maintain the relationship.
            DanglingVertex?[][] danglingVertices = new DanglingVertex?[len][];

            // profiler.BeginSample("foreach(edge connection)");
            int i = 0;

            foreach (pb_EdgeConnection fc in pb_edgeConnections)
            {
                pb_Face[]   splitFaces         = null;
                Vector3[][] splitVertices      = null;
                Color[][]   splitColors        = null;
                Vector2[][] splitUVs           = 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;

                    Color cola = (colors[fc.edges[0].x] + colors[fc.edges[0].y]) / 2f;
                    Color colb = (colors[fc.edges[1].x] + colors[fc.edges[1].y]) / 2f;

                    danglingVertices[i] = new DanglingVertex?[2] {
                        new DanglingVertex(edgeACen, cola), new DanglingVertex(edgeBCen, colb)
                    };

                    success[i] = SplitFace_Internal(
                        new SplitSelection(pb, fc.face, edgeACen, edgeBCen, cola, colb, false, false, new int[] { fc.edges[0].x, fc.edges[0].y }, new int[] { fc.edges[1].x, fc.edges[1].y }),
                        out splitFaces,
                        out splitVertices,
                        out splitColors,
                        out splitUVs,
                        out splitSharedIndices);

                    if (success[i])
                    {
                        successfullySplitFaces.Add(fc.face);
                    }
                }
                else
                {
                    DanglingVertex?[] appendedVertices = null;

                    success[i] = SubdivideFace_Internal(pb, fc,
                                                        out appendedVertices,
                                                        out splitFaces,
                                                        out splitVertices,
                                                        out splitColors,
                                                        out splitUVs,
                                                        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_splitColors.Add(splitColors[j]);
                        all_splitUVs.Add(splitUVs[j]);
                        all_splitSharedIndices.Add(splitSharedIndices[j]);
                    }
                }

                i++;
            }
            // profiler.EndSample();


            // profiler.BeginSample("Retrianguate");

            /**
             *	Figure out which faces need to be re-triangulated
             */
            pb_Edge[][] tedges = new pb_Edge[pb_edgeConnections.Count][];
            int         n      = 0;

            for (i = 0; i < pb_edgeConnections.Count; i++)
            {
                tedges[n++] = pb_edgeConnections[i].edges.ToArray();
            }

            List <pb_Face>[][] allConnects = pbMeshUtils.GetNeighborFacesJagged(pb, tedges);


            Dictionary <pb_Face, List <DanglingVertex> > addVertex = new Dictionary <pb_Face, List <DanglingVertex> >();
            List <pb_Face> temp = new List <pb_Face>();

            for (int j = 0; j < pb_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 < pb_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((DanglingVertex)danglingVertices[j][i]);
                            }
                            else
                            {
                                temp.Add(face);
                                addVertex.Add(face, new List <DanglingVertex>(1)
                                {
                                    (DanglingVertex)danglingVertices[j][i]
                                });
                            }
                        }
                    }
                }
            }
            // profiler.EndSample();

            // profiler.BeginSample("Append vertices to faces");
            pb_Face[] appendedFaces = pb.AppendFaces(all_splitVertices.ToArray(), all_splitColors.ToArray(), all_splitUVs.ToArray(), all_splitFaces.ToArray(), all_splitSharedIndices.ToArray());

            List <pb_Face> triangulatedFaces = new List <pb_Face>();

            foreach (KeyValuePair <pb_Face, List <DanglingVertex> > add in addVertex)
            {
                pb_Face newFace;

                if (pb.AppendVerticesToFace(add.Key, add.Value.Select(x => x.position).ToArray(), add.Value.Select(x => x.color).ToArray(), out newFace))
                {
                    triangulatedFaces.Add(newFace);
                }
                else
                {
                    Debug.LogError("Mesh re-triangulation failed.");            //  Specifically, AppendVerticesToFace(" + add.Key + " : " + add.Value.ToFormattedString(", "));
                }
            }
            // profiler.EndSample();

            // profiler.BeginSample("rebuild mesh");

            // 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);

            // safe to assume that we probably didn't delete anything :/
            int[] welds;

            // profiler.BeginSample("weld vertices");
            pb.WeldVertices(allModifiedTris, Mathf.Epsilon, out welds);

            // profiler.EndSample();
            // pb.SetSharedIndices( pb_IntArrayUtility.ExtractSharedIndices(pb.vertices) );

            // Now that we're done screwing with geo, delete all the old faces (that were successfully split)
            // profiler.BeginSample("delete faces");
            pb.DeleteFaces(successfullySplitFaces.ToArray());
            faces = appendedFaces;
            // profiler.EndSample();
            // profiler.EndSample();
            // profiler.EndSample();
            // Debug.Log(profiler.ToString());

            return(true);
        }
Example #31
0
	/**
	 * Get vertices at x,y index with edge.
	 */
	public Vector3[] GetVertices(pb_Edge edge)
	{
		return new Vector3[]
		{
			_vertices[edge.x],
			_vertices[edge.y]
		};
	}
Example #32
0
 public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b)
 {
     return(pb.Bridge(a, b, true));
 }
Example #33
0
        /**
         *	Edge extrusion override
         */
        public static pb_Edge[] Extrude(this pb_Object pb, pb_Edge[] edges, float extrudeDistance, bool enforcePerimiterEdgesOnly)
        {
            pb_IntArray[] sharedIndices = pb.sharedIndices;

            List <pb_Edge> validEdges = new List <pb_Edge>();
            List <pb_Face> edgeFaces  = new List <pb_Face>();

            foreach (pb_Edge e in edges)
            {
                int     faceCount = 0;
                pb_Face fa        = null;
                foreach (pb_Face f in pb.faces)
                {
                    if (f.edges.IndexOf(e, sharedIndices) > -1)
                    {
                        fa = f;
                        if (++faceCount > 1)
                        {
                            break;
                        }
                    }
                }

                if (!enforcePerimiterEdgesOnly || faceCount < 2)
                {
                    validEdges.Add(e);
                    edgeFaces.Add(fa);
                }
            }

            if (validEdges.Count < 1)
            {
                return(null);
            }

            Vector3[] localVerts = pb.vertices;
            Vector3[] oNormals   = pb.msh.normals;

            int[] allEdgeIndices = new int[validEdges.Count * 2];
            int   c = 0;        // har har har

            for (int i = 0; i < validEdges.Count; i++)
            {
                allEdgeIndices[c++] = validEdges[i].x;
                allEdgeIndices[c++] = validEdges[i].y;
            }

            List <pb_Edge> extrudedIndices = new List <pb_Edge>();
            List <pb_Edge> newEdges        = new List <pb_Edge>();      // used to set the editor selection to the newly created edges

            /// build out new faces around validEdges

            for (int i = 0; i < validEdges.Count; i++)
            {
                pb_Edge edge = validEdges[i];
                pb_Face face = edgeFaces[i];

                // Averages the normals using only vertices that are on the edge
                Vector3 xnorm = Norm(edge.x, sharedIndices, allEdgeIndices, oNormals);
                Vector3 ynorm = Norm(edge.y, sharedIndices, allEdgeIndices, oNormals);

                int x_sharedIndex = sharedIndices.IndexOf(edge.x);
                int y_sharedIndex = sharedIndices.IndexOf(edge.y);

                pb_Face newFace = pb.AppendFace(
                    new Vector3[4]
                {
                    localVerts [edge.x],
                    localVerts [edge.y],
                    localVerts [edge.x] + xnorm.normalized * extrudeDistance,
                    localVerts [edge.y] + ynorm.normalized * extrudeDistance
                },
                    new pb_Face(new int[6] {
                    2, 1, 0, 2, 3, 1
                }, face.material, new pb_UV(), 0, -1, -1, face.colors[0]),
                    new int[4] {
                    x_sharedIndex, y_sharedIndex, -1, -1
                });

                newEdges.Add(new pb_Edge(newFace.indices[3], newFace.indices[4]));

                extrudedIndices.Add(new pb_Edge(x_sharedIndex, newFace.indices[3]));
                extrudedIndices.Add(new pb_Edge(y_sharedIndex, newFace.indices[4]));
            }

            sharedIndices = pb.sharedIndices;

            // merge extruded vertex indices with each other
            for (int i = 0; i < extrudedIndices.Count; i++)
            {
                int val = extrudedIndices[i].x;
                for (int n = 0; n < extrudedIndices.Count; n++)
                {
                    if (n == i)
                    {
                        continue;
                    }

                    if (extrudedIndices[n].x == val)
                    {
                        pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, extrudedIndices[n].y, extrudedIndices[i].y);
                        break;
                    }
                }
            }

            pb.SetSharedIndices(sharedIndices);
            pb.RebuildFaceCaches();

            return(newEdges.ToArray());
        }
Example #34
0
        /**
         * move the UVs to where the edges passed meet
         */
        static bool AlignEdges(pb_Object pb, pb_Face f1, pb_Face f2, pb_Edge edge1, pb_Edge edge2)
        {
            Vector2[]     uvs             = pb.uv;
            pb_IntArray[] sharedIndices   = pb.sharedIndices;
            pb_IntArray[] sharedIndicesUV = pb.sharedIndicesUV;

            /**
             * Match each edge vertex to the other
             */
            int[] matchX = new int[2] {
                edge1.x, -1
            };
            int[] matchY = new int[2] {
                edge1.y, -1
            };

            int siIndex = sharedIndices.IndexOf(edge1.x);

            if (siIndex < 0)
            {
                return(false);
            }

            if (sharedIndices[siIndex].array.Contains(edge2.x))
            {
                matchX[1] = edge2.x;
                matchY[1] = edge2.y;
            }
            else
            {
                matchX[1] = edge2.y;
                matchY[1] = edge2.x;
            }

            // scale face 2 to match the edge size of f1
            float dist_e1 = Vector2.Distance(uvs[edge1.x], uvs[edge1.y]);
            float dist_e2 = Vector2.Distance(uvs[edge2.x], uvs[edge2.y]);

            float scale = dist_e1 / dist_e2;

            // doesn't matter what point we scale around because we'll move it in the next step anyways
            foreach (int i in f2.distinctIndices)
            {
                uvs[i] = uvs[i].ScaleAroundPoint(Vector2.zero, Vector2.one * scale);
            }

            /**
             * Figure out where the center of each edge is so that we can move the f2 edge to match f1's origin
             */
            Vector2 f1_center = (uvs[edge1.x] + uvs[edge1.y]) / 2f;
            Vector2 f2_center = (uvs[edge2.x] + uvs[edge2.y]) / 2f;

            Vector2 diff = f1_center - f2_center;

            /**
             * Move f2 face to where it's matching edge center is on top of f1's center
             */
            foreach (int i in f2.distinctIndices)
            {
                uvs[i] += diff;
            }

            /**
             * Now that the edge's centers are matching, rotate f2 to match f1's angle
             */
            Vector2 angle1 = uvs[matchY[0]] - uvs[matchX[0]];
            Vector2 angle2 = uvs[matchY[1]] - uvs[matchX[1]];

            float angle = Vector2.Angle(angle1, angle2);

            if (Vector3.Cross(angle1, angle2).z < 0)
            {
                angle = 360f - angle;
            }

            foreach (int i in f2.distinctIndices)
            {
                uvs[i] = pb_Math.RotateAroundPoint(uvs[i], f1_center, angle);
            }

            float error = Mathf.Abs(Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]])) + Mathf.Abs(Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]));

            // now check that the matched UVs are on top of one another if the error allowance is greater than some small value
            if (error > .02)
            {
                // first try rotating 180 degrees
                foreach (int i in f2.distinctIndices)
                {
                    uvs[i] = pb_Math.RotateAroundPoint(uvs[i], f1_center, 180f);
                }

                float e2 = Mathf.Abs(Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]])) + Mathf.Abs(Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]));
                if (e2 < error)
                {
                    error = e2;
                }
                else
                {
                    // flip 'em back around
                    foreach (int i in f2.distinctIndices)
                    {
                        uvs[i] = pb_Math.RotateAroundPoint(uvs[i], f1_center, 180f);
                    }
                }
            }

            // If successfully aligned, merge the sharedIndicesUV
            pbUVOps.SplitUVs(pb, f2.distinctIndices);

            pb_IntArrayUtility.MergeSharedIndices(ref sharedIndicesUV, matchX);
            pb_IntArrayUtility.MergeSharedIndices(ref sharedIndicesUV, matchY);

            pb_IntArray.RemoveEmptyOrNull(ref sharedIndicesUV);

            pb.SetSharedIndicesUV(sharedIndicesUV);

            // @todo Update Element Groups here?

            pb.SetUV(uvs);

            return(true);
        }
Example #35
0
        /**
         * Extrudes passed faces on their normal axis using extrudeDistance.
         */
        public static bool Extrude(this pb_Object pb, pb_Face[] faces, float extrudeDistance, bool extrudeAsGroup, out pb_Face[] appendedFaces)
        {
            appendedFaces = null;

            if (faces == null || faces.Length < 1)
            {
                return(false);
            }

            pb_IntArray[]         sharedIndices = pb.GetSharedIndices();
            Dictionary <int, int> lookup        = sharedIndices.ToDictionary();

            Vector3[] localVerts = pb.vertices;

            pb_Edge[][] perimeterEdges = extrudeAsGroup ? new pb_Edge[1][] { pbMeshUtils.GetPerimeterEdges(pb, lookup, faces).ToArray() } : faces.Select(x => x.edges).ToArray();

            if (perimeterEdges == null || perimeterEdges.Length < 1 || (extrudeAsGroup && perimeterEdges[0].Length < 3))
            {
                Debug.LogWarning("No perimeter edges found.  Try deselecting and reselecting this object and trying again.");
                return(false);
            }

            pb_Face[][] edgeFaces      = new pb_Face[perimeterEdges.Length][];  // can't assume faces and perimiter edges will be 1:1 - so calculate perimeters then extract face information
            int[][]     allEdgeIndices = new int[perimeterEdges.Length][];
            int         c = 0;

            for (int i = 0; i < perimeterEdges.Length; i++)
            {
                c = 0;
                allEdgeIndices[i] = new int[perimeterEdges[i].Length * 2];
                edgeFaces[i]      = new pb_Face[perimeterEdges[i].Length];

                for (int n = 0; n < perimeterEdges[i].Length; n++)
                {
                    // gets the faces associated with each perimeter edge
                    foreach (pb_Face face in faces)
                    {
                        if (face.edges.Contains(perimeterEdges[i][n]))
                        {
                            edgeFaces[i][n] = face;
                            break;
                        }
                    }

                    allEdgeIndices[i][c++] = perimeterEdges[i][n].x;
                    allEdgeIndices[i][c++] = perimeterEdges[i][n].y;
                }
            }

            List <pb_Edge>[] extrudedIndices = new List <pb_Edge> [perimeterEdges.Length];
            Vector3[]        normals         = pb.msh.normals;

            List <Vector3[]> append_vertices = new List <Vector3[]>();
            List <Color[]>   append_color    = new List <Color[]>();
            List <Vector2[]> append_uv       = new List <Vector2[]>();
            List <pb_Face>   append_face     = new List <pb_Face>();
            List <int[]>     append_shared   = new List <int[]>();

            /// build out new faces around edges

            for (int i = 0; i < perimeterEdges.Length; i++)
            {
                extrudedIndices[i] = new List <pb_Edge>();

                for (int n = 0; n < perimeterEdges[i].Length; n++)
                {
                    pb_Edge edge = perimeterEdges[i][n];
                    pb_Face face = edgeFaces[i][n];

                    // Averages the normals using only vertices that are on the edge
                    Vector3 xnorm = Vector3.zero;
                    Vector3 ynorm = Vector3.zero;

                    // don't bother getting vertex normals if not auto-extruding
                    if (Mathf.Abs(extrudeDistance) > Mathf.Epsilon)
                    {
                        if (!extrudeAsGroup)
                        {
                            xnorm = pb_Math.Normal(localVerts[face.indices[0]], localVerts[face.indices[1]], localVerts[face.indices[2]]);
                            ynorm = xnorm;
                        }
                        else
                        {
                            xnorm = Norm(sharedIndices[lookup[edge.x]], allEdgeIndices[i], normals);
                            ynorm = Norm(sharedIndices[lookup[edge.y]], allEdgeIndices[i], normals);
                        }
                    }

                    int x_sharedIndex = lookup[edge.x];
                    int y_sharedIndex = lookup[edge.y];

                    // this could be condensed to a single call with an array of new faces
                    append_vertices.Add(new Vector3[]
                    {
                        localVerts [edge.x],
                        localVerts [edge.y],
                        localVerts [edge.x] + xnorm.normalized * extrudeDistance,
                        localVerts [edge.y] + ynorm.normalized * extrudeDistance
                    });

                    append_color.Add(new Color[]
                    {
                        pb.colors[edge.x],
                        pb.colors[edge.y],
                        pb.colors[edge.x],
                        pb.colors[edge.y]
                    });

                    append_uv.Add(new Vector2[4]);

                    append_face.Add(new pb_Face(
                                        new int[6] {
                        0, 1, 2, 1, 3, 2
                    },                                                                          // indices
                                        face.material,                                          // material
                                        new pb_UV(face.uv),                                     // UV material
                                        face.smoothingGroup,                                    // smoothing group
                                        -1,                                                     // texture group
                                        -1,                                                     // uv element group
                                        false)                                                  // manualUV flag
                                    );

                    append_shared.Add(new int[4]
                    {
                        x_sharedIndex,
                        y_sharedIndex,
                        -1,
                        -1
                    });

                    extrudedIndices[i].Add(new pb_Edge(x_sharedIndex, -1));
                    extrudedIndices[i].Add(new pb_Edge(y_sharedIndex, -1));
                }
            }

            appendedFaces = pb.AppendFaces(append_vertices.ToArray(), append_color.ToArray(), append_uv.ToArray(), append_face.ToArray(), append_shared.ToArray());

            // x = shared index, y = triangle (only known once faces are appended to pb_Object)
            for (int i = 0, f = 0; i < extrudedIndices.Length; i++)
            {
                for (int n = 0; n < extrudedIndices[i].Count; n += 2)
                {
                    extrudedIndices[i][n + 0].y = appendedFaces[f].indices[2];
                    extrudedIndices[i][n + 1].y = appendedFaces[f++].indices[4];
                }
            }

            pb_IntArray[]         si    = pb.sharedIndices; // leave the sharedIndices copy alone since we need the un-altered version later
            Dictionary <int, int> welds = si.ToDictionary();

            // Weld side-wall top vertices together, both grouped and non-grouped need this.
            for (int f = 0; f < extrudedIndices.Length; f++)
            {
                for (int i = 0; i < extrudedIndices[f].Count - 1; i++)
                {
                    int val = extrudedIndices[f][i].x;
                    for (int n = i + 1; n < extrudedIndices[f].Count; n++)
                    {
                        if (extrudedIndices[f][n].x == val)
                        {
                            welds[extrudedIndices[f][i].y] = welds[extrudedIndices[f][n].y];
                            break;
                        }
                    }
                }
            }

            localVerts = pb.vertices;

            // Remove smoothing and texture group flags
            foreach (pb_Face f in faces)
            {
                f.SetSmoothingGroup(-1);
                f.textureGroup = -1;
            }

            if (extrudeAsGroup)
            {
                foreach (pb_Face f in faces)
                {
                    int[] distinctIndices = f.distinctIndices;

                    // Merge in-group face seams
                    foreach (int ind in distinctIndices)
                    {
                        int oldIndex = si.IndexOf(ind);

                        for (int n = 0; n < allEdgeIndices.Length; n++)
                        {
                            for (int i = 0; i < extrudedIndices[n].Count; i++)
                            {
                                if (oldIndex == extrudedIndices[n][i].x)
                                {
                                    welds[ind] = welds[extrudedIndices[n][i].y];
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            else

            /**
             * If extruding as separate faces, weld each face to the tops of the bridging faces
             */
            {
                // Dictionary<int, int> hold = si.ToDictionary();

                for (int i = 0; i < edgeFaces.Length; i++)
                {
                    foreach (int n in pb_Face.AllTrianglesDistinct(edgeFaces[i]))
                    {
                        int old_si_index = lookup[n];
                        int match        = extrudedIndices[i].FindIndex(x => x.x == old_si_index);

                        if (match < 0)
                        {
                            continue;
                        }

                        int match_tri_index = extrudedIndices[i][match].y;

                        if (welds.ContainsKey(match_tri_index))
                        {
                            welds[n] = welds[match_tri_index];
                        }
                    }
                }
            }

            si = welds.ToSharedIndices();


            pb.SplitUVs(pb_Face.AllTriangles(faces));

            /**
             * Move the inside faces to the top of the extrusion
             *
             * This is a separate loop cause the one above this must completely merge all sharedindices prior to
             * checking the normal averages
             *
             */
            Vector3 norm = Vector3.zero;

            int[] allIndices = pb_Face.AllTrianglesDistinct(faces);
            foreach (pb_Face f in faces)
            {
                if (!extrudeAsGroup)
                {
                    norm = pb_Math.Normal(localVerts[f.indices[0]], localVerts[f.indices[1]], localVerts[f.indices[2]]);
                }

                foreach (int ind in f.distinctIndices)
                {
                    if (extrudeAsGroup)
                    {
                        norm = Norm(sharedIndices[lookup[ind]], allIndices, normals);
                    }

                    localVerts[ind] += norm.normalized * extrudeDistance;
                }
            }

            // Test the winding of the first pulled face, and reverse if it's ccw
            if (pb.GetWindingOrder(faces[0]) == WindingOrder.CounterClockwise)
            {
                foreach (pb_Face face in appendedFaces)
                {
                    face.ReverseIndices();
                }
            }

            pb.SetSharedIndices(si);
            pb.SetVertices(localVerts);


            return(true);
        }
Example #36
0
		/**
		 * Returns the indices of perimeter vertices in selection.
		 */
		public static int[] GetPerimeterVertices(pb_Object pb, int[] indices, pb_Edge[] universal_edges_all)
		{
			int len = indices.Length;
			pb_IntArray[] sharedIndices = pb.sharedIndices;
			int[] universal = new int[len];

			for(int i = 0; i < len; i++)
				universal[i] = sharedIndices.IndexOf(indices[i]);

			int[] connections = new int[indices.Length];
			for(int i = 0; i < indices.Length - 1; i++)
			{
				for(int n = i+1; n < indices.Length; n++)
				{
					if(universal_edges_all.Contains(universal[i], universal[n]))
					{
						connections[i]++;
						connections[n]++;
					}
				}
			}

			int min = pb_Math.Min(connections);
			List<int> perimeter = new List<int>();
			for(int i = 0; i < len; i++)
			{
				if(connections[i] <= min)
					perimeter.Add(i);
			}

			return perimeter.Count < len ? perimeter.ToArray() : new int[] {};
		}
Example #37
0
 /**
  *	Returns all faces connected to the passed edge.
  */
 public static List <pb_Face> GetNeighborFaces(pb_Object pb, pb_Edge edge)
 {
     return(GetNeighborFaces(pb, edge, pb.sharedIndices.ToDictionary()));
 }
Example #38
0
	private void OnSelectionChange()
	{
		nearestEdge = null;
		nearestEdgeObject = null;

		UpdateSelection(false);

		HideSelectedWireframe();
	}
		/**
		 * The SelectedEdges array contains Edges made up of indices that aren't guaranteed to be 'valid' - that is, they
		 * may not belong to the same face.  This method extracts an edge and face combo from the face independent edge
		 * selection.
		 * @param faces - Corresponding face to edge list
		 * @param edges - An edge composed of indices that belong to a same face (matching face in faces List).
		 * @returns True if at least one valid edge is found, false if not.
		 */
		public static bool ValidFaceAndEdgeWithEdge(pb_Object pb, pb_Edge faceIndependentEdge, out List<pb_Face> faces, out List<pb_Edge> edges)
		{
			faces = new List<pb_Face>();
			edges = new List<pb_Edge>();

			pb_IntArray[] sharedIndices = pb.sharedIndices;
			
			foreach(pb_Face f in pb.faces)
			{
				int ind = f.edges.IndexOf(faceIndependentEdge, sharedIndices);
				if(ind > -1)
				{
					faces.Add(f);
					edges.Add(f.edges[ind]);
				}
			}

			return faces.Count > 0;
		}
Example #40
0
	private bool EdgeClickCheck(out pb_Object pb)
	{
		if(!shiftKey && !ctrlKey)
		{
			// don't call ClearFaceSelection b/c that also removes
			// nearestEdge info
			foreach(pb_Object p in selection)
				p.ClearSelection();
		}

		if(nearestEdgeObject != null)
		{
			pb = nearestEdgeObject;
	
			if(nearestEdge != null && nearestEdge.IsValid())
			{
				pb_Edge edge;
				
				if( pb_Edge.ValidateEdge(pb, nearestEdge, out edge) )
					nearestEdge = edge;

				int ind = pb.SelectedEdges.IndexOf(nearestEdge, pb.sharedIndices.ToDictionary());
				
				pbUndo.RecordSelection(pb, "Change Edge Selection");
				
				if( ind > -1 )
					pb.SetSelectedEdges(pb.SelectedEdges.RemoveAt(ind));
				else
					pb.SetSelectedEdges(pb.SelectedEdges.Add(nearestEdge));

				return true;
			}

			return false;
		}
		else
		{
			if(!shiftKey && !ctrlKey)
				ClearFaceSelection();

			pb = null;

			return false;
		}
	}
Example #41
0
	private static bool ConnectEdges(this pb_Object pb, List<pb_EdgeConnection> pb_edgeConnectionsUnfiltered, out pb_Face[] faces)
	{
		List<pb_EdgeConnection> pb_edgeConnections = new List<pb_EdgeConnection>();
		foreach(pb_EdgeConnection ec in pb_edgeConnectionsUnfiltered)
			if(ec.isValid)
				pb_edgeConnections.Add(ec);

		int len = pb_edgeConnections.Count;

		if(len < 1)
		{
			faces = null;
			return false;
		}

		Vector3[] vertices = pb.vertices;
		Color[] colors = pb.colors;

		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<Color[]> all_splitColors 		= new List<Color[]>();
		List<Vector2[]> all_splitUVs	 	= new List<Vector2[]>();
		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 pb_EdgeConnection, it's easy to maintain the relationship.
		DanglingVertex?[][] danglingVertices = new DanglingVertex?[len][];	

		// profiler.BeginSample("foreach(edge connection)");
		int i = 0;
		foreach(pb_EdgeConnection fc in pb_edgeConnections)
		{	
			pb_Face[] splitFaces 		= null;
			Vector3[][] splitVertices 	= null;
			Color[][] splitColors  		= null;
			Vector2[][] splitUVs 		= 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;

				Color cola = (colors[fc.edges[0].x] + colors[fc.edges[0].y]) / 2f;
				Color colb = (colors[fc.edges[1].x] + colors[fc.edges[1].y]) / 2f;

				danglingVertices[i] = new DanglingVertex?[2] { new DanglingVertex(edgeACen, cola), new DanglingVertex(edgeBCen, colb) };

				success[i] = SplitFace_Internal(
					new SplitSelection(pb, fc.face, edgeACen, edgeBCen, cola, colb, false, false, new int[]{fc.edges[0].x, fc.edges[0].y}, new int[]{fc.edges[1].x, fc.edges[1].y}),
					out splitFaces,
					out splitVertices, 
					out splitColors,
					out splitUVs,
					out splitSharedIndices);
				
				if(success[i])
					successfullySplitFaces.Add(fc.face);
			}
			else
			{
				DanglingVertex?[] appendedVertices = null;

				success[i] = SubdivideFace_Internal(pb, fc,
					out appendedVertices,
					out splitFaces,
					out splitVertices,
					out splitColors,
					out splitUVs,
					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_splitColors.Add(splitColors[j]);
					all_splitUVs.Add(splitUVs[j]);
					all_splitSharedIndices.Add(splitSharedIndices[j]);
				}
			}

			i++;
		}
		// profiler.EndSample();


		// profiler.BeginSample("Retrianguate");
		/**
		 *	Figure out which faces need to be re-triangulated
		 */
		pb_Edge[][] tedges = new pb_Edge[pb_edgeConnections.Count][];
		int n = 0;
		for(i = 0; i < pb_edgeConnections.Count; i++)
			tedges[n++] = pb_edgeConnections[i].edges.ToArray();

		List<pb_Face>[][] allConnects = pbMeshUtils.GetNeighborFacesJagged(pb, tedges);		


		Dictionary<pb_Face, List<DanglingVertex>> addVertex = new Dictionary<pb_Face, List<DanglingVertex>>();
		List<pb_Face> temp = new List<pb_Face>();
		for(int j = 0; j < pb_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 < pb_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( (DanglingVertex)danglingVertices[j][i] );
						else
						{
							temp.Add(face);
							addVertex.Add(face, new List<DanglingVertex>(1) { (DanglingVertex)danglingVertices[j][i] });
						}
					}
				}
			}
		}
		// profiler.EndSample();

		// profiler.BeginSample("Append vertices to faces");
		pb_Face[] appendedFaces = pb.AppendFaces(all_splitVertices.ToArray(), all_splitColors.ToArray(), all_splitUVs.ToArray(), all_splitFaces.ToArray(), all_splitSharedIndices.ToArray());
		
		List<pb_Face> triangulatedFaces = new List<pb_Face>();
		foreach(KeyValuePair<pb_Face, List<DanglingVertex>> add in addVertex)
		{
			pb_Face newFace;

			if( pb.AppendVerticesToFace(add.Key, add.Value.Select(x => x.position).ToArray(), add.Value.Select(x => x.color).ToArray(), out newFace) )
				triangulatedFaces.Add(newFace);
			else
				Debug.LogError("Mesh re-triangulation failed.");//  Specifically, AppendVerticesToFace(" + add.Key + " : " + add.Value.ToFormattedString(", "));
		}
		// profiler.EndSample();

		// profiler.BeginSample("rebuild mesh");

		// 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);
		
		// safe to assume that we probably didn't delete anything :/
		int[] welds;

		// profiler.BeginSample("weld vertices");
		pb.WeldVertices(allModifiedTris, Mathf.Epsilon, out welds);

		// profiler.EndSample();
		// pb.SetSharedIndices( pb_IntArrayUtility.ExtractSharedIndices(pb.vertices) );

		// Now that we're done screwing with geo, delete all the old faces (that were successfully split)		
		// profiler.BeginSample("delete faces");
		pb.DeleteFaces( successfullySplitFaces.ToArray() );
		faces = appendedFaces;
		// profiler.EndSample();
		// profiler.EndSample();
		// profiler.EndSample();
		// Debug.Log(profiler.ToString());

		return true;
	}
Example #42
0
		public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b, bool enforcePerimiterEdgesOnly)
		{
			pb_IntArray[] sharedIndices = pb.GetSharedIndices();
			Dictionary<int, int> lookup = sharedIndices.ToDictionary();

			// Check to see if a face already exists
			if(enforcePerimiterEdgesOnly)
			{
				if( pbMeshUtils.GetNeighborFaces(pb, a).Count > 1 || pbMeshUtils.GetNeighborFaces(pb, b).Count > 1 )
				{
					return false;
				}
			}

			foreach(pb_Face face in pb.faces)
			{
				if(face.edges.IndexOf(a, lookup) >= 0 && face.edges.IndexOf(b, lookup) >= 0)
				{
					Debug.LogWarning("Face already exists between these two edges!");
					return false;
				}
			}
		
			Vector3[] verts = pb.vertices;
			Vector3[] v;
			Color[] c;
			int[] s;
			pb_UV uvs = new pb_UV();
			Material mat = pb_Constant.DefaultMaterial;

			// Get material and UV stuff from the first edge face 
			foreach(pb_Face face in pb.faces)
			{
				if(face.edges.Contains(a))	
				{
					uvs = new pb_UV(face.uv);
					mat = face.material;
					break;
				}
			}

			// Bridge will form a triangle
			if( a.Contains(b.x, sharedIndices) || a.Contains(b.y, sharedIndices) )
			{
				v = new Vector3[3];
				c = new Color[3];
				s = new int[3];

				bool axbx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.x) > -1;
				bool axby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.y) > -1;
				
				bool aybx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.x) > -1;
				bool ayby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.y) > -1;
				
				if(axbx)
				{	
					v[0] = verts[a.x];
					c[0] = pb.colors[a.x];
					s[0] = sharedIndices.IndexOf(a.x);
					v[1] = verts[a.y];
					c[1] = pb.colors[a.y];
					s[1] = sharedIndices.IndexOf(a.y);
					v[2] = verts[b.y];
					c[2] = pb.colors[b.y];
					s[2] = sharedIndices.IndexOf(b.y);
				}
				else
				if(axby)
				{
					v[0] = verts[a.x];
					c[0] = pb.colors[a.x];
					s[0] = sharedIndices.IndexOf(a.x);
					v[1] = verts[a.y];
					c[1] = pb.colors[a.y];
					s[1] = sharedIndices.IndexOf(a.y);
					v[2] = verts[b.x];
					c[2] = pb.colors[b.x];
					s[2] = sharedIndices.IndexOf(b.x);
				}
				else
				if(aybx)
				{
					v[0] = verts[a.y];
					c[0] = pb.colors[a.y];
					s[0] = sharedIndices.IndexOf(a.y);
					v[1] = verts[a.x];
					c[1] = pb.colors[a.x];
					s[1] = sharedIndices.IndexOf(a.x);
					v[2] = verts[b.y];
					c[2] = pb.colors[b.y];
					s[2] = sharedIndices.IndexOf(b.y);
				}
				else
				if(ayby)
				{
					v[0] = verts[a.y];
					c[0] = pb.colors[a.y];
					s[0] = sharedIndices.IndexOf(a.y);
					v[1] = verts[a.x];
					c[1] = pb.colors[a.x];
					s[1] = sharedIndices.IndexOf(a.x);
					v[2] = verts[b.x];
					c[2] = pb.colors[b.x];
					s[2] = sharedIndices.IndexOf(b.x);
				}

				pb.AppendFace(
					v,
					c,
					new Vector2[v.Length],
					new pb_Face( axbx || axby ? new int[3] {2, 1, 0} : new int[3] {0, 1, 2}, mat, uvs, 0, -1, -1, false ),
					s);

				return true;
			}

			// Else, bridge will form a quad

			v = new Vector3[4];
			c = new Color[4];
			s = new int[4]; // shared indices index to add to

			v[0] = verts[a.x];
			c[0] = pb.colors[a.x];
			s[0] = sharedIndices.IndexOf(a.x);
			v[1] = verts[a.y];
			c[1] = pb.colors[a.y];
			s[1] = sharedIndices.IndexOf(a.y);

			Vector3 nrm = Vector3.Cross( verts[b.x]-verts[a.x], verts[a.y]-verts[a.x] ).normalized;
			Vector2[] planed = pb_Math.PlanarProject( new Vector3[4] {verts[a.x], verts[a.y], verts[b.x], verts[b.y] }, nrm );

			Vector2 ipoint = Vector2.zero;
			bool interescts = pb_Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);

			if(!interescts)
			{
				v[2] = verts[b.x];
				c[2] = pb.colors[b.x];
				s[2] = sharedIndices.IndexOf(b.x);
				v[3] = verts[b.y];
				c[3] = pb.colors[b.y];
				s[3] = sharedIndices.IndexOf(b.y);
			}
			else
			{
				v[2] = verts[b.y];
				c[2] = pb.colors[b.y];
				s[2] = sharedIndices.IndexOf(b.y);
				v[3] = verts[b.x];
				c[3] = pb.colors[b.x];
				s[3] = sharedIndices.IndexOf(b.x);
			}

			pb.AppendFace(
				v,
				c,
				new Vector2[v.Length],
				new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, mat, uvs, 0, -1, -1, false ),
				s);

			return true;
		}
Example #43
0
	/**
	 *	Clears all `selected` caches associated with each pb_Object in the current selection.  The means triangles, faces, and edges.
	 */
	public void ClearFaceSelection()
	{
		foreach(pb_Object pb in selection) {
			pb.ClearSelection();
		}

		nearestEdge = null;
		nearestEdgeObject = null;
	}
Example #44
0
	/**
	 * Attempts to insert new edges connecting the center of all passed edges.
	 */
	public static bool ConnectEdges(this pb_Object pb, pb_Edge[] edges, out pb_Edge[] newEdges)
	{
		// pb_Profiler profiler = new pb_Profiler();

		// profiler.BeginSample("Con nectEdges");

		int len = edges.Length;
		List<pb_EdgeConnection> splits = new List<pb_EdgeConnection>();
		Dictionary<int, int> lookup = pb.sharedIndices.ToDictionary();

		// profiler.BeginSample("Split Edges");
		for(int i = 0; i < len; i++)
		{
			List<pb_Face> neighbors = pbMeshUtils.GetNeighborFaces(pb, edges[i]);

			foreach(pb_Face face in neighbors)
			{
				if(!splits.Contains((pb_EdgeConnection)face))
				{
					List<pb_Edge> faceEdges = new List<pb_Edge>();
					foreach(pb_Edge e in edges)
					{
						int localEdgeIndex = face.edges.IndexOf(e, lookup);
						if(localEdgeIndex > -1)
							faceEdges.Add(face.edges[localEdgeIndex]);
					}

					if(faceEdges.Count > 1)	
						splits.Add(new pb_EdgeConnection(face, faceEdges));
				}
			}
		}
		// profiler.EndSample();

		Vector3[] vertices = pb.GetVertices( pb_EdgeConnection.AllTriangles(splits).Distinct().ToArray() );


		pb_Face[] faces;
 		bool success = ConnectEdges(pb, splits, out faces);

		// profiler.BeginSample("Find New Edges");
		if(success)
		{
			/**
			 * Get the newly created Edges so that we can return them.
			 */
			List<pb_Edge> nedges = new List<pb_Edge>();

			for(int i = 0; i < faces.Length; i++)
			{
				for(int n = 0; n < faces[i].edges.Length; n++)
				{
					if( pb_Math.ContainsApprox(vertices, pb.vertices[faces[i].edges[n].x], .001f) ||
						pb_Math.ContainsApprox(vertices, pb.vertices[faces[i].edges[n].y], .001f) )
						continue;
					else
						nedges.Add( faces[i].edges[n] );
				}
			}
			newEdges = nedges.ToArray();
		}
		else
		{
			newEdges = null;
		}
		// profiler.EndSample();

		// profiler.EndSample();
		// Debug.Log(profiler.ToString());

		return success;
	}
Example #45
0
        /**
         * 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.
         */
        public static bool GetEdgeLoop(pb_Object pb, pb_Edge[] edges, out pb_Edge[] loop)
        {
            pb_IntArray[]         sharedIndices = pb.sharedIndices;
            Dictionary <int, int> lookup        = sharedIndices.ToDictionary();

            List <pb_Edge> loopEdges           = new List <pb_Edge>();
            int            largestPossibleLoop = pb.vertexCount;

            int  c    = 0;
            bool useY = true;

            foreach (pb_Edge edge in edges)
            {
                if (loopEdges.IndexOf(edge, lookup) > -1)
                {
                    continue;
                }

                // First go in the Y direction.  If that doesn't loop, then go in the X direction
                int     cycles   = 0;
                bool    nextEdge = false;
                pb_Edge curEdge  = edge;

                do
                {
                    if (cycles != 1)
                    {
                        loopEdges.Add(curEdge);
                    }
                    else
                    {
                        cycles++;
                    }

                    // get the index of this triangle in the sharedIndices array
                    // int[] si = sharedIndices[sharedIndices.IndexOf( useY ? curEdge.y : curEdge.x )].array;
                    int[] si = sharedIndices[lookup[useY ? curEdge.y : curEdge.x]].array;

                    // Get all faces connected to this vertex
                    pb_Face[] faces = pbMeshUtils.GetNeighborFaces(pb, si).ToArray();

                    pb_Face[] edgeAdjacent           = System.Array.FindAll(faces, x => x.edges.IndexOf(curEdge, lookup) > -1);
                    pb_Edge[] invalidEdges_universal = pb_Edge.GetUniversalEdges(pb_Edge.AllEdges(edgeAdjacent), sharedIndices).Distinct().ToArray();

                    // these are faces that do NOT border the current edge
                    pb_Face[] nextFaces = System.Array.FindAll(faces, x => x.edges.IndexOf(curEdge, lookup) < 0);

                    if (nextFaces.Length != 2)
                    {
                        if (cycles < 1)
                        {
                            curEdge  = edge;
                            nextEdge = true;
                            useY     = false;
                            cycles++;
                            continue;
                        }
                        else
                        {
                            nextEdge = false;
                            break;
                        }
                    }

                    nextEdge = false;
                    bool superBreak = false;
                    for (int i = 0; i < nextFaces.Length; i++)
                    {
                        foreach (pb_Edge e in nextFaces[i].edges)
                        {
                            if (invalidEdges_universal.Contains(pb_Edge.GetUniversalEdge(e, sharedIndices)))
                            {
                                continue;
                            }

                            int xindex = System.Array.IndexOf(si, e.x);
                            int yindex = System.Array.IndexOf(si, e.y);

                            if (xindex > -1 || yindex > -1)
                            {
                                if (e.Equals(edge, lookup))
                                {
                                    // we've completed the loop.  exit.
                                    superBreak = true;
                                }
                                else
                                {
                                    useY       = xindex > -1;
                                    curEdge    = e;
                                    superBreak = true;
                                    nextEdge   = true;
                                }

                                break;
                            }
                        }

                        if (superBreak)
                        {
                            break;
                        }
                    }

                    if (!nextEdge && cycles < 1)
                    {
                        curEdge  = edge;
                        nextEdge = true;
                        useY     = false;
                        cycles++;
                    }

                    // This is a little arbitrary...
                    if (c++ > largestPossibleLoop)
                    {
                        Debug.LogError("Caught in a loop while searching for a loop! Oh the irony...\n" + loopEdges.Count);
                        nextEdge = false;
                    }
                }while(nextEdge);
            }

            loop = loopEdges.Distinct().ToArray();

            return(loopEdges.Count > 0);
        }
Example #46
0
        public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b, bool enforcePerimiterEdgesOnly)
        {
            pb_IntArray[]         sharedIndices = pb.GetSharedIndices();
            Dictionary <int, int> lookup        = sharedIndices.ToDictionary();

            // Check to see if a face already exists
            if (enforcePerimiterEdgesOnly)
            {
                if (pbMeshUtils.GetNeighborFaces(pb, a).Count > 1 || pbMeshUtils.GetNeighborFaces(pb, b).Count > 1)
                {
                    return(false);
                }
            }

            foreach (pb_Face face in pb.faces)
            {
                if (face.edges.IndexOf(a, lookup) >= 0 && face.edges.IndexOf(b, lookup) >= 0)
                {
                    Debug.LogWarning("Face already exists between these two edges!");
                    return(false);
                }
            }

            Vector3[] verts = pb.vertices;
            Vector3[] v;
            Color[]   c;
            int[]     s;
            pb_UV     uvs = new pb_UV();
            Material  mat = pb_Constant.DefaultMaterial;

            // Get material and UV stuff from the first edge face
            foreach (pb_Face face in pb.faces)
            {
                if (face.edges.Contains(a))
                {
                    uvs = new pb_UV(face.uv);
                    mat = face.material;
                    break;
                }
            }

            // Bridge will form a triangle
            if (a.Contains(b.x, sharedIndices) || a.Contains(b.y, sharedIndices))
            {
                v = new Vector3[3];
                c = new Color[3];
                s = new int[3];

                bool axbx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.x) > -1;
                bool axby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.y) > -1;

                bool aybx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.x) > -1;
                bool ayby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.y) > -1;

                if (axbx)
                {
                    v[0] = verts[a.x];
                    c[0] = pb.colors[a.x];
                    s[0] = sharedIndices.IndexOf(a.x);
                    v[1] = verts[a.y];
                    c[1] = pb.colors[a.y];
                    s[1] = sharedIndices.IndexOf(a.y);
                    v[2] = verts[b.y];
                    c[2] = pb.colors[b.y];
                    s[2] = sharedIndices.IndexOf(b.y);
                }
                else
                if (axby)
                {
                    v[0] = verts[a.x];
                    c[0] = pb.colors[a.x];
                    s[0] = sharedIndices.IndexOf(a.x);
                    v[1] = verts[a.y];
                    c[1] = pb.colors[a.y];
                    s[1] = sharedIndices.IndexOf(a.y);
                    v[2] = verts[b.x];
                    c[2] = pb.colors[b.x];
                    s[2] = sharedIndices.IndexOf(b.x);
                }
                else
                if (aybx)
                {
                    v[0] = verts[a.y];
                    c[0] = pb.colors[a.y];
                    s[0] = sharedIndices.IndexOf(a.y);
                    v[1] = verts[a.x];
                    c[1] = pb.colors[a.x];
                    s[1] = sharedIndices.IndexOf(a.x);
                    v[2] = verts[b.y];
                    c[2] = pb.colors[b.y];
                    s[2] = sharedIndices.IndexOf(b.y);
                }
                else
                if (ayby)
                {
                    v[0] = verts[a.y];
                    c[0] = pb.colors[a.y];
                    s[0] = sharedIndices.IndexOf(a.y);
                    v[1] = verts[a.x];
                    c[1] = pb.colors[a.x];
                    s[1] = sharedIndices.IndexOf(a.x);
                    v[2] = verts[b.x];
                    c[2] = pb.colors[b.x];
                    s[2] = sharedIndices.IndexOf(b.x);
                }

                pb.AppendFace(
                    v,
                    c,
                    new Vector2[v.Length],
                    new pb_Face(axbx || axby ? new int[3] {
                    2, 1, 0
                } : new int[3] {
                    0, 1, 2
                }, mat, uvs, 0, -1, -1, false),
                    s);

                return(true);
            }

            // Else, bridge will form a quad

            v = new Vector3[4];
            c = new Color[4];
            s = new int[4];             // shared indices index to add to

            v[0] = verts[a.x];
            c[0] = pb.colors[a.x];
            s[0] = sharedIndices.IndexOf(a.x);
            v[1] = verts[a.y];
            c[1] = pb.colors[a.y];
            s[1] = sharedIndices.IndexOf(a.y);

            Vector3 nrm = Vector3.Cross(verts[b.x] - verts[a.x], verts[a.y] - verts[a.x]).normalized;

            Vector2[] planed = pb_Math.PlanarProject(new Vector3[4] {
                verts[a.x], verts[a.y], verts[b.x], verts[b.y]
            }, nrm);

            Vector2 ipoint     = Vector2.zero;
            bool    interescts = pb_Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);

            if (!interescts)
            {
                v[2] = verts[b.x];
                c[2] = pb.colors[b.x];
                s[2] = sharedIndices.IndexOf(b.x);
                v[3] = verts[b.y];
                c[3] = pb.colors[b.y];
                s[3] = sharedIndices.IndexOf(b.y);
            }
            else
            {
                v[2] = verts[b.y];
                c[2] = pb.colors[b.y];
                s[2] = sharedIndices.IndexOf(b.y);
                v[3] = verts[b.x];
                c[3] = pb.colors[b.x];
                s[3] = sharedIndices.IndexOf(b.x);
            }

            pb.AppendFace(
                v,
                c,
                new Vector2[v.Length],
                new pb_Face(new int[6] {
                2, 1, 0, 2, 3, 1
            }, mat, uvs, 0, -1, -1, false),
                s);

            return(true);
        }
Example #47
0
		/**
		 * 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.
		 */	
		public static bool GetEdgeLoop(pb_Object pb, pb_Edge[] edges, out pb_Edge[] loop)
		{
			pb_IntArray[] sharedIndices = pb.sharedIndices;
			Dictionary<int, int> lookup = sharedIndices.ToDictionary();

			List<pb_Edge> loopEdges = new List<pb_Edge>();
			int largestPossibleLoop = pb.vertexCount;

			int c = 0;
			bool useY = true;

			foreach(pb_Edge edge in edges)
			{
				if(	loopEdges.IndexOf(edge, lookup) > -1 )
					continue;

				// First go in the Y direction.  If that doesn't loop, then go in the X direction
				int cycles = 0;
				bool nextEdge = false;
				pb_Edge curEdge = edge;

				do
				{
					if(cycles != 1)
						loopEdges.Add(curEdge);
					else
						cycles++;

					// get the index of this triangle in the sharedIndices array
					// int[] si = sharedIndices[sharedIndices.IndexOf( useY ? curEdge.y : curEdge.x )].array;
					int[] si = sharedIndices[lookup[useY ? curEdge.y : curEdge.x]].array;

					// Get all faces connected to this vertex
					pb_Face[] faces = pbMeshUtils.GetNeighborFaces(pb, si).ToArray();

					pb_Face[] edgeAdjacent = System.Array.FindAll(faces, x => x.edges.IndexOf(curEdge, lookup) > -1);
					pb_Edge[] invalidEdges_universal = pb_Edge.GetUniversalEdges( pb_Edge.AllEdges(edgeAdjacent), sharedIndices ).Distinct().ToArray();

					// these are faces that do NOT border the current edge
					pb_Face[] nextFaces = System.Array.FindAll(faces, x => x.edges.IndexOf(curEdge, lookup) < 0);

					if(nextFaces.Length != 2)
					{
						if(cycles < 1)
						{
							curEdge = edge;
							nextEdge = true;
							useY = false;
							cycles++;
							continue;
						}
						else
						{
							nextEdge = false;
							break;
						}
					}

					nextEdge = false;
					bool superBreak = false;
					for(int i = 0; i < nextFaces.Length; i++)
					{
						foreach(pb_Edge e in nextFaces[i].edges)
						{
							if( invalidEdges_universal.Contains(pb_Edge.GetUniversalEdge(e, sharedIndices)) )
								continue;

							int xindex = System.Array.IndexOf(si, e.x);
							int yindex = System.Array.IndexOf(si, e.y);

							if(  xindex > -1 || yindex > -1 )
							{
								if( e.Equals(edge, lookup) )
								{
									// we've completed the loop.  exit.
									superBreak = true;
								}
								else
								{
									useY = xindex > -1;
									curEdge = e;
									superBreak = true;
									nextEdge = true;
								}
								
								break;
							}
						}

						if(superBreak) break;
					}
					
					if(!nextEdge && cycles < 1)
					{
						curEdge = edge;
						nextEdge = true;
						useY = false;
						cycles++;
					}

					// This is a little arbitrary...
					if(c++ > largestPossibleLoop)
					{
						Debug.LogError("Caught in a loop while searching for a loop! Oh the irony...\n" + loopEdges.Count);
						nextEdge = false;
					}
				}
				while(nextEdge);

			}

			loop = loopEdges.Distinct().ToArray();

			return loopEdges.Count > 0;
		}
Example #48
0
		private static bool ConnectEdges(pb_Object pb, pb_Edge[] edgesToConnect)
		{	
			int len = edgesToConnect.Length;
			List<EdgeConnection> splits = new List<EdgeConnection>();

			for(int i = 0; i < len; i++)
			{
				foreach(pb_Face face in pbMeshUtils.GetConnectedFaces(pb, edgesToConnect[i]))
				{
					if(!splits.Contains((EdgeConnection)face))
					{
						List<pb_Edge> faceEdges = new List<pb_Edge>();
						foreach(pb_Edge e in edgesToConnect)
						{
							int localEdgeIndex = face.edges.IndexOf(e, pb.sharedIndices);
							if(localEdgeIndex > -1)
								faceEdges.Add(face.edges[localEdgeIndex]);
						}
	
						if(faceEdges.Count > 1)	
							splits.Add(new EdgeConnection(face, faceEdges));
					}
				}
			}

			pb_Face[] faces;
			if(pb.ConnectEdges(splits, out faces))
			{
				pb.SetSelectedFaces(faces);
				pb.GenerateUV2(true);
				pb.Refresh();
				return true;
			}
			return false;
		}
Example #49
0
        public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b, bool enforcePerimiterEdgesOnly)
        {
            pb_IntArray[] sharedIndices = pb.GetSharedIndices();

            // Check to see if a face already exists
            if (enforcePerimiterEdgesOnly)
            {
                if (pbMeshUtils.GetConnectedFaces(pb, a).Count > 1 || pbMeshUtils.GetConnectedFaces(pb, b).Count > 1)
                {
                    Debug.LogWarning("Both edges are not on perimeter!  You may turn off this Bridging restriction in Preferences/ProBuilder/Bridge Perimiter Edges Only");
                    return(false);
                }
            }
            else
            {
                foreach (pb_Face face in pb.faces)
                {
                    if (face.edges.IndexOf(a, sharedIndices) >= 0 && face.edges.IndexOf(b, sharedIndices) >= 0)
                    {
                        Debug.LogWarning("Face already exists between these two edges!");
                        return(false);
                    }
                }
            }

            Vector3[] verts = pb.vertices;
            Vector3[] v;
            int[]     s;
            pb_UV     uvs   = new pb_UV();
            Color32   color = (Color32)Color.white;
            Material  mat   = pb_Constant.DefaultMaterial;

            // Get material and UV stuff from the first edge face
            foreach (pb_Face face in pb.faces)
            {
                if (face.edges.Contains(a))
                {
                    uvs   = new pb_UV(face.uv);
                    mat   = face.material;
                    color = face.colors[0];
                    break;
                }
            }

            // Bridge will form a triangle
            if (a.Contains(b.x, sharedIndices) || a.Contains(b.y, sharedIndices))
            {
                v = new Vector3[3];
                s = new int[3];

                bool axbx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.x) > -1;
                bool axby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.y) > -1;

                bool aybx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.x) > -1;
                bool ayby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.y) > -1;

                if (axbx)
                {
                    v[0] = verts[a.x];
                    s[0] = sharedIndices.IndexOf(a.x);
                    v[1] = verts[a.y];
                    s[1] = sharedIndices.IndexOf(a.y);
                    v[2] = verts[b.y];
                    s[2] = sharedIndices.IndexOf(b.y);
                }
                else
                if (axby)
                {
                    v[0] = verts[a.x];
                    s[0] = sharedIndices.IndexOf(a.x);
                    v[1] = verts[a.y];
                    s[1] = sharedIndices.IndexOf(a.y);
                    v[2] = verts[b.x];
                    s[2] = sharedIndices.IndexOf(b.x);
                }
                else
                if (aybx)
                {
                    v[0] = verts[a.y];
                    s[0] = sharedIndices.IndexOf(a.y);
                    v[1] = verts[a.x];
                    s[1] = sharedIndices.IndexOf(a.x);
                    v[2] = verts[b.y];
                    s[2] = sharedIndices.IndexOf(b.y);
                }
                else
                if (ayby)
                {
                    v[0] = verts[a.y];
                    s[0] = sharedIndices.IndexOf(a.y);
                    v[1] = verts[a.x];
                    s[1] = sharedIndices.IndexOf(a.x);
                    v[2] = verts[b.x];
                    s[2] = sharedIndices.IndexOf(b.x);
                }

                pb.AppendFace(
                    v,
                    new pb_Face(axbx || axby ? new int[3] {
                    2, 1, 0
                } : new int[3] {
                    0, 1, 2
                }, mat, uvs, 0, -1, -1, color),
                    s);

                pb.RebuildFaceCaches();
                pb.Refresh();

                return(true);
            }

            // Else, bridge will form a quad

            v = new Vector3[4];
            s = new int[4];             // shared indices index to add to

            v[0] = verts[a.x];
            s[0] = sharedIndices.IndexOf(a.x);
            v[1] = verts[a.y];
            s[1] = sharedIndices.IndexOf(a.y);

            Vector3 nrm = Vector3.Cross(verts[b.x] - verts[a.x], verts[a.y] - verts[a.x]).normalized;

            Vector2[] planed = pb_Math.VerticesTo2DPoints(new Vector3[4] {
                verts[a.x], verts[a.y], verts[b.x], verts[b.y]
            }, nrm);

            Vector2 ipoint     = Vector2.zero;
            bool    interescts = pb_Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);

            if (!interescts)
            {
                v[2] = verts[b.x];
                s[2] = sharedIndices.IndexOf(b.x);
                v[3] = verts[b.y];
                s[3] = sharedIndices.IndexOf(b.y);
            }
            else
            {
                v[2] = verts[b.y];
                s[2] = sharedIndices.IndexOf(b.y);
                v[3] = verts[b.x];
                s[3] = sharedIndices.IndexOf(b.x);
            }

            pb.AppendFace(
                v,
                new pb_Face(new int[6] {
                2, 1, 0, 2, 3, 1
            }, mat, uvs, 0, -1, -1, color),
                s);

            pb.RebuildFaceCaches();

            return(true);
        }
	public pb_Edge[] GetEdges()
	{
		pb_Edge[] edges = new pb_Edge[indices.Length];
		for(int i = 0; i < indices.Length; i+=3) {
			edges[i+0] = new pb_Edge(indices[i+0],indices[i+1]);
			edges[i+1] = new pb_Edge(indices[i+1],indices[i+2]);
			edges[i+2] = new pb_Edge(indices[i+2],indices[i+0]);
		}
		return edges;
	}
 public pb_SerializableEdge(pb_Edge v)
 {
     this.x = v.x;
     this.y = v.y;
 }
Example #52
0
        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();
        }
Example #53
0
	public void SetSelectedEdges(pb_Edge[] edges)
	{
		this.m_selectedFaces = new int[0];

		int len = edges.Length;
		this.m_SelectedEdges = new pb_Edge[len];
		for(int i = 0; i < len; i++)
			this.m_SelectedEdges[i] = new pb_Edge(edges[i]);

		this.m_selectedTriangles = m_SelectedEdges.AllTriangles();				
	}
Example #54
0
	/**
	 * Invert the current selection.
	 */
	public static void MenuInvertSelection(pb_Object[] selection)
	{
		pbUndo.RecordSelection(selection, "Invert Selection");

		switch( editor != null ? editor.selectionMode : (SelectMode)0 )
		{
			case SelectMode.Vertex:
				foreach(pb_Object pb in selection)
				{
					pb_IntArray[] sharedIndices = pb.sharedIndices;
					List<int> selSharedIndices = new List<int>();

					foreach(int i in pb.SelectedTriangles)
						selSharedIndices.Add( sharedIndices.IndexOf(i) );				

					List<int> inverse = new List<int>();

					for(int i = 0; i < sharedIndices.Length; i++)
					{
						if(!selSharedIndices.Contains(i))
							inverse.Add(sharedIndices[i][0]);
					}

					pb.SetSelectedTriangles(inverse.ToArray());
				}
				break;

			case SelectMode.Face:
				foreach(pb_Object pb in selection)
				{
					List<pb_Face> inverse = new List<pb_Face>();

					for(int i = 0; i < pb.faces.Length; i++)
						if( System.Array.IndexOf(pb.SelectedFaceIndices, i) < 0 )
							inverse.Add(pb.faces[i]);
					
					pb.SetSelectedFaces(inverse.ToArray());	
				}
				break;

			case SelectMode.Edge:
				
				if(!editor) break;

				for(int i = 0; i < selection.Length; i++)
				{
					pb_Edge[] universal_selected_edges = pb_Edge.GetUniversalEdges(selection[i].SelectedEdges, selection[i].sharedIndices).Distinct().ToArray();
					pb_Edge[] inverse_universal = System.Array.FindAll(editor.SelectedUniversalEdges[i], x => !universal_selected_edges.Contains(x));
					pb_Edge[] inverse = new pb_Edge[inverse_universal.Length];
					
					for(int n = 0; n < inverse_universal.Length; n++)
						inverse[n] = new pb_Edge( selection[i].sharedIndices[inverse_universal[n].x][0], selection[i].sharedIndices[inverse_universal[n].y][0] );

					selection[i].SetSelectedEdges(inverse);
				}
				break;
		}

		if(editor)
			editor.UpdateSelection();
		
		pb_Editor_Utility.ShowNotification("Invert Selection");

		SceneView.RepaintAll();
	}
	/**
	 *	Edge extrusion override
	 */
	public static pb_Edge[] Extrude(this pb_Object pb, pb_Edge[] edges, float extrudeDistance, bool enforcePerimiterEdgesOnly)
	{
		pb_IntArray[] sharedIndices = pb.sharedIndices;

		List<pb_Edge> validEdges = new List<pb_Edge>();
		List<pb_Face> edgeFaces = new List<pb_Face>();
		
		foreach(pb_Edge e in edges)
		{
			int faceCount = 0;
			pb_Face fa = null;
			foreach(pb_Face f in pb.faces)
			{
				if(f.edges.IndexOf(e, sharedIndices) > -1)
				{
					fa = f;
					if(++faceCount > 1)
						break;
				}

			}

			if(!enforcePerimiterEdgesOnly || faceCount < 2)
			{
				validEdges.Add(e);
				edgeFaces.Add(fa);
			}
		}

		if(validEdges.Count < 1)
			return null;

		Vector3[] localVerts = pb.vertices;
		Vector3[] oNormals = pb.msh.normals;

		int[] allEdgeIndices = new int[validEdges.Count * 2];
		int c = 0;	// har har har
		for(int i = 0; i < validEdges.Count; i++)
		{
			allEdgeIndices[c++] = validEdges[i].x;
			allEdgeIndices[c++] = validEdges[i].y;
		}

		List<pb_Edge> extrudedIndices = new List<pb_Edge>();
		List<pb_Edge> newEdges = new List<pb_Edge>();		// used to set the editor selection to the newly created edges

		/// build out new faces around validEdges

		for(int i = 0; i < validEdges.Count; i++)
		{
			pb_Edge edge = validEdges[i];
			pb_Face face = edgeFaces[i];

			// Averages the normals using only vertices that are on the edge
			Vector3 xnorm = Norm( edge.x, sharedIndices, allEdgeIndices, oNormals );
			Vector3 ynorm = Norm( edge.y, sharedIndices, allEdgeIndices, oNormals );

			int x_sharedIndex = sharedIndices.IndexOf(edge.x);
			int y_sharedIndex = sharedIndices.IndexOf(edge.y);

			pb_Face newFace = pb.AppendFace(
				new Vector3[4]
				{
					localVerts [ edge.x ],
					localVerts [ edge.y ],
					localVerts [ edge.x ] + xnorm.normalized * extrudeDistance,
					localVerts [ edge.y ] + ynorm.normalized * extrudeDistance
				},
				new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, face.material, new pb_UV(), 0, -1, -1, face.colors[0] ),
				new int[4] { x_sharedIndex, y_sharedIndex, -1, -1 });

			newEdges.Add(new pb_Edge(newFace.indices[3], newFace.indices[4]));

			extrudedIndices.Add(new pb_Edge(x_sharedIndex, newFace.indices[3]));
			extrudedIndices.Add(new pb_Edge(y_sharedIndex, newFace.indices[4]));
		}

		sharedIndices = pb.sharedIndices;

		// merge extruded vertex indices with each other
		for(int i = 0; i < extrudedIndices.Count; i++)
		{
			int val = extrudedIndices[i].x;
			for(int n = 0; n < extrudedIndices.Count; n++)
			{
				if(n == i)
					continue;

				if(extrudedIndices[n].x == val)
				{
					pb_IntArrayUtility.MergeSharedIndices(ref sharedIndices, extrudedIndices[n].y, extrudedIndices[i].y);
					break;
				}
			}
		}

		pb.SetSharedIndices(sharedIndices);
		pb.RebuildFaceCaches();
		
		return newEdges.ToArray();
	}
Example #56
0
	/**
	 * move the UVs to where the edges passed meet
	 */
	static bool AlignEdges(pb_Object pb, pb_Face f1, pb_Face f2, pb_Edge edge1, pb_Edge edge2)
	{
		Vector2[] uvs = pb.uv;
		pb_IntArray[] sharedIndices = pb.sharedIndices;
		pb_IntArray[] sharedIndicesUV = pb.sharedIndicesUV;

		/**
		 * Match each edge vertex to the other
		 */
		int[] matchX = new int[2] { edge1.x, -1 };
		int[] matchY = new int[2] { edge1.y, -1 };

		int siIndex = sharedIndices.IndexOf(edge1.x);
		if(siIndex < 0) 
			return false;

		if(sharedIndices[siIndex].array.Contains(edge2.x))
		{
			matchX[1] = edge2.x;
			matchY[1] = edge2.y;
		}
		else
		{
			matchX[1] = edge2.y;
			matchY[1] = edge2.x;
		}

		// scale face 2 to match the edge size of f1
		float dist_e1 = Vector2.Distance(uvs[edge1.x], uvs[edge1.y]);
		float dist_e2 = Vector2.Distance(uvs[edge2.x], uvs[edge2.y]);
		
		float scale = dist_e1/dist_e2;
		
		// doesn't matter what point we scale around because we'll move it in the next step anyways
		foreach(int i in f2.distinctIndices)
			uvs[i] = uvs[i].ScaleAroundPoint(Vector2.zero, Vector2.one * scale);

		/**
		 * Figure out where the center of each edge is so that we can move the f2 edge to match f1's origin 
		 */
		Vector2 f1_center = (uvs[edge1.x] + uvs[edge1.y]) / 2f;
		Vector2 f2_center = (uvs[edge2.x] + uvs[edge2.y]) / 2f;

		Vector2 diff = f1_center - f2_center;

		/**
		 * Move f2 face to where it's matching edge center is on top of f1's center
		 */
		foreach(int i in f2.distinctIndices)
			uvs[i] += diff;

		/**
		 * Now that the edge's centers are matching, rotate f2 to match f1's angle
		 */
		Vector2 angle1 = uvs[matchY[0]] - uvs[matchX[0]];
		Vector2 angle2 = uvs[matchY[1]] - uvs[matchX[1]];

		float angle = Vector2.Angle(angle1, angle2);
		if(Vector3.Cross(angle1, angle2).z < 0)
			angle = 360f - angle;
	
		foreach(int i in f2.distinctIndices)
			uvs[i] = pb_Math.RotateAroundPoint(uvs[i], f1_center, angle);

		float error = Mathf.Abs( Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]]) ) + Mathf.Abs( Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]) );

		// now check that the matched UVs are on top of one another if the error allowance is greater than some small value
		if(error > .02)
		{
			// first try rotating 180 degrees
			foreach(int i in f2.distinctIndices)
				uvs[i] = pb_Math.RotateAroundPoint(uvs[i], f1_center, 180f);

			float e2 = Mathf.Abs( Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]]) ) + Mathf.Abs( Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]) );
			if(e2 < error)
				error = e2;
			else
			{
				// flip 'em back around
				foreach(int i in f2.distinctIndices)
					uvs[i] = pb_Math.RotateAroundPoint(uvs[i], f1_center, 180f);
			}
		}

		// If successfully aligned, merge the sharedIndicesUV
		pbUVOps.SplitUVs(pb, f2.distinctIndices);

		pb_IntArrayUtility.MergeSharedIndices(ref sharedIndicesUV, matchX);
		pb_IntArrayUtility.MergeSharedIndices(ref sharedIndicesUV, matchY);

		pb_IntArray.RemoveEmptyOrNull(ref sharedIndicesUV);

		pb.SetSharedIndicesUV(sharedIndicesUV);

		// @todo Update Element Groups here?

		pb.SetUV(uvs);

		return true;
	}
		public static bool Bridge(this pb_Object pb, pb_Edge a, pb_Edge b, bool enforcePerimiterEdgesOnly)
		{
			pb_IntArray[] sharedIndices = pb.GetSharedIndices();

			// Check to see if a face already exists
			if(enforcePerimiterEdgesOnly)
			{
				if( pbMeshUtils.GetConnectedFaces(pb, a).Count > 1 || pbMeshUtils.GetConnectedFaces(pb, b).Count > 1 )
				{
					Debug.LogWarning("Both edges are not on perimeter!  You may turn off this Bridging restriction in Preferences/ProBuilder/Bridge Perimiter Edges Only");
					return false;
				}
			}
			else
			{
				foreach(pb_Face face in pb.faces)
				{
					if(face.edges.IndexOf(a, sharedIndices) >= 0 && face.edges.IndexOf(b, sharedIndices) >= 0)
					{
						Debug.LogWarning("Face already exists between these two edges!");
						return false;
					}
				}
			}

			Vector3[] verts = pb.vertices;
			Vector3[] v;
			int[] s;
			pb_UV uvs = new pb_UV();
			Color32 color = (Color32)Color.white;
			Material mat = pb_Constant.DefaultMaterial;

			// Get material and UV stuff from the first edge face 
			foreach(pb_Face face in pb.faces)
			{
				if(face.edges.Contains(a))	
				{
					uvs = new pb_UV(face.uv);
					mat = face.material;
					color = face.colors[0];
					break;
				}
			}

			// Bridge will form a triangle
			if( a.Contains(b.x, sharedIndices) || a.Contains(b.y, sharedIndices) )
			{
				v = new Vector3[3];
				s = new int[3];

				bool axbx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.x) > -1;
				bool axby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.x)], b.y) > -1;
				
				bool aybx = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.x) > -1;
				bool ayby = System.Array.IndexOf(sharedIndices[sharedIndices.IndexOf(a.y)], b.y) > -1;
				
				if(axbx)
				{	
					v[0] = verts[a.x];
					s[0] = sharedIndices.IndexOf(a.x);
					v[1] = verts[a.y];
					s[1] = sharedIndices.IndexOf(a.y);
					v[2] = verts[b.y];
					s[2] = sharedIndices.IndexOf(b.y);
				}
				else
				if(axby)
				{
					v[0] = verts[a.x];
					s[0] = sharedIndices.IndexOf(a.x);
					v[1] = verts[a.y];
					s[1] = sharedIndices.IndexOf(a.y);
					v[2] = verts[b.x];
					s[2] = sharedIndices.IndexOf(b.x);
				}
				else
				if(aybx)
				{
					v[0] = verts[a.y];
					s[0] = sharedIndices.IndexOf(a.y);
					v[1] = verts[a.x];
					s[1] = sharedIndices.IndexOf(a.x);
					v[2] = verts[b.y];
					s[2] = sharedIndices.IndexOf(b.y);
				}
				else
				if(ayby)
				{
					v[0] = verts[a.y];
					s[0] = sharedIndices.IndexOf(a.y);
					v[1] = verts[a.x];
					s[1] = sharedIndices.IndexOf(a.x);
					v[2] = verts[b.x];
					s[2] = sharedIndices.IndexOf(b.x);
				}

				pb.AppendFace(
					v,
					new pb_Face( axbx || axby ? new int[3] {2, 1, 0} : new int[3] {0, 1, 2}, mat, uvs, 0, -1, -1, color ),
					s);

				pb.RebuildFaceCaches();
				pb.Refresh();

				return true;
			}

			// Else, bridge will form a quad

			v = new Vector3[4];
			s = new int[4]; // shared indices index to add to

			v[0] = verts[a.x];
			s[0] = sharedIndices.IndexOf(a.x);
			v[1] = verts[a.y];
			s[1] = sharedIndices.IndexOf(a.y);

			Vector3 nrm = Vector3.Cross( verts[b.x]-verts[a.x], verts[a.y]-verts[a.x] ).normalized;
			Vector2[] planed = pb_Math.VerticesTo2DPoints( new Vector3[4] {verts[a.x], verts[a.y], verts[b.x], verts[b.y] }, nrm );

			Vector2 ipoint = Vector2.zero;
			bool interescts = pb_Math.GetLineSegmentIntersect(planed[0], planed[2], planed[1], planed[3], ref ipoint);

			if(!interescts)
			{
				v[2] = verts[b.x];
				s[2] = sharedIndices.IndexOf(b.x);
				v[3] = verts[b.y];
				s[3] = sharedIndices.IndexOf(b.y);
			}
			else
			{
				v[2] = verts[b.y];
				s[2] = sharedIndices.IndexOf(b.y);
				v[3] = verts[b.x];
				s[3] = sharedIndices.IndexOf(b.x);
			}

			pb.AppendFace(
				v,
				new pb_Face( new int[6] {2, 1, 0, 2, 3, 1 }, mat, uvs, 0, -1, -1, color ),
				s);

			pb.RebuildFaceCaches();

			return true;
		}
Example #58
0
        /**
         *
         */
        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);
        }