Example #1
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());
        }
		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 #3
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);
        }
Example #4
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 #5
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 #6
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 #7
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();
		}