Beispiel #1
0
    public bool ClickShortcutCheck(EventModifiers em, pb_Object pb, pb_Face quad)
    {
        // if(em == (EventModifiers.Control | EventModifiers.Shift | EventModifiers.Alt))
        // {
        //  Continue(pb, quad);
        //  pb_Editor_Utility.ShowNotification("Continue UV");
        //  return true;
        // }

        if (em == (EventModifiers.Control | EventModifiers.Shift))
        {
            ApplyMaterial(pb, quad, queuedMat);
            RepaintSceneViews();
            pb_Editor_Utility.ShowNotification("Quick Apply Material");
            return(true);
        }

        if (em == (EventModifiers.Control))
        {
            pb.SetFaceUV(quad, new pb_UV(uv_gui));
            RepaintSceneViews();
            pb_Editor_Utility.ShowNotification("Copy UV Settings");
            return(true);
        }

        return(false);
    }
Beispiel #2
0
    private void Update()
    {
        float   num      = Time.time * speed;
        Vector3 position = new Vector3(Mathf.PerlinNoise(num, num) * travel, 2f, Mathf.PerlinNoise(num + 1f, num + 1f) * travel);

        base.transform.position = position;
        if (target == null)
        {
            Debug.LogWarning("Missing the ProBuilder Mesh target!");
            return;
        }
        Vector3 a = target.transform.InverseTransformPoint(base.transform.position);

        if (nearest != null)
        {
            target.SetFaceColor(nearest, Color.white);
        }
        int   num2 = target.faces.Length;
        float num3 = float.PositiveInfinity;

        nearest = target.faces[0];
        for (int i = 0; i < num2; i++)
        {
            float num4 = Vector3.Distance(a, FaceCenter(target, target.faces[i]));
            if (num4 < num3)
            {
                num3    = num4;
                nearest = target.faces[i];
            }
        }
        target.SetFaceColor(nearest, Color.blue);
        target.RefreshColors();
    }
Beispiel #3
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();
        }
Beispiel #4
0
    public bool FaceCheck(Vector3 pos)
    {
        Ray ray = Camera.main.ScreenPointToRay (pos);
        RaycastHit hit;

        if( Physics.Raycast(ray.origin, ray.direction, out hit))
        {
            pb_Object hitpb = hit.transform.gameObject.GetComponent<pb_Object>();

            if(hitpb == null)
                return false;

            Mesh m = hitpb.msh;

            int[] tri = new int[3] {
                m.triangles[hit.triangleIndex * 3 + 0],
                m.triangles[hit.triangleIndex * 3 + 1],
                m.triangles[hit.triangleIndex * 3 + 2]
            };

            pb = hitpb;
            quad = hitpb.QuadWithTriangle(tri);
            return true;
        }
        return false;
    }
Beispiel #5
0
    public static bool HiddenFace(pb_Object pb, pb_Face q, float dist)
    {
        // Grab the face normal
        Vector3 dir = pbUtil.PlaneNormal(pb.VerticesInWorldSpace(q));

        // And also the center of the face
        Vector3 orig = pb.QuadCenter(q);

        // Case a ray from the center of the face out in the normal direction.
        // If an object is hit, return true (that this face is hidden), otherwise
        // return false.  This is pretty simplistic and doesn't account for a lot
        // of "gotchas", but it ought to serve as a fairly decent jumping off point
        // for NoDrawing a dense level.
        RaycastHit hit;
        if(Physics.Raycast(orig, dir, out hit, dist)) {
            // We've hit something.  Now check to see if it is a ProBuilder object,
            // and if so, make sure it's a visblocking brush.
            pb_Entity ent = hit.transform.GetComponent<pb_Entity>();
            if(ent != null)
            {
                if(ent.entityType == ProBuilder.EntityType.Brush || ent.entityType == ProBuilder.EntityType.Occluder)
                    return true;		// it's a brush, blocks vision, return true
                else
                    return false;		// not a vis blocking brush
            }
        }

        // It ain't a ProBuilder object of the entity type Brush or Occluder (world brush)
        return false;
    }
Beispiel #6
0
    /**
     *	\brief Call this to ensure that the mesh is unique.  Basically performs a DeepCopy and assigns back to self.
     */
    public void MakeUnique()
    {
        pb_Face[] q = new pb_Face[_faces.Length];

        for (int i = 0; i < q.Length; i++)
        {
            q[i] = new pb_Face(_faces[i]);
        }

        pb_IntArray[] sv = new pb_IntArray[_sharedIndices.Length];
        System.Array.Copy(_sharedIndices, sv, sv.Length);

        SetSharedIndices(sv);
        SetFaces(q);

        Vector3[] v = new Vector3[vertexCount];
        System.Array.Copy(_vertices, v, vertexCount);
        SetVertices(v);

        if (_uv != null && _uv.Length == vertexCount)
        {
            Vector2[] u = new Vector2[vertexCount];
            System.Array.Copy(_uv, u, vertexCount);
            SetUV(u);
        }

        msh = pbUtil.DeepCopyMesh(msh);

        ToMesh();
        Refresh();
    }
Beispiel #7
0
    bool FaceRaycast(Vector2 mouse, out pb_Object pb, out pb_Face face)
    {
        var        ray = Camera.main.ScreenPointToRay(mouse);
        RaycastHit rayHit;

        if (Physics.Raycast(ray.origin, ray.direction, out rayHit))
        {
            pb = rayHit.transform.gameObject.GetComponent <pb_Object>();

            if (pb == null)
            {
                face = null;
                return(false);
            }

            Mesh m = pb.GetComponent <MeshFilter>().sharedMesh;

            int[] tri = new int[3] {
                m.triangles[rayHit.triangleIndex * 3 + 0],
                m.triangles[rayHit.triangleIndex * 3 + 1],
                m.triangles[rayHit.triangleIndex * 3 + 2]
            };

            return(pb.FaceWithTriangle(tri, out face));
        }

        pb   = null;
        face = null;

        return(false);
    }
Beispiel #8
0
    private void GeometryWithPoints(Vector3[] v)
    {
        // Wrap in faces
        pb_Face[] f = new pb_Face[v.Length / 4];

        for (int i = 0; i < v.Length; i += 4)
        {
            f[i / 4] = new pb_Face(new int[6]
            {
                i + 0, i + 1, i + 2,
                i + 1, i + 3, i + 2
            },
                                   pb_Constant.DefaultMaterial,
                                   new pb_UV(),
                                   0,
                                   -1,
                                   -1,
                                   false);
        }

        SetVertices(v);
        SetUV(new Vector2[v.Length]);
        SetColors(pbUtil.FilledArray <Color>(Color.white, v.Length));

        SetFaces(f);
        SetSharedIndices(pb_IntArrayUtility.ExtractSharedIndices(v));

        ToMesh();
        Refresh();
    }
		/**
		 * \brief Returns all connected faces.
		 */
		public static List<pb_Face> GetConnectedFaces(pb_Object pb, pb_Face[] selFaces)
		{
			int len = selFaces.Length;

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

			pb_IntArray[] sharedIndices = pb.sharedIndices;
				
			pb_Edge[][] sharedEdges = new pb_Edge[len][];
			for(int i = 0; i < len; i++)
				sharedEdges[i] = pb_Edge.GetUniversalEdges(selFaces[i].edges, sharedIndices);

			for(int i = 0; i < pb.faces.Length; i++)
			{
				pb_Edge[] faceEdges = pb_Edge.GetUniversalEdges(pb.faces[i].edges, sharedIndices);
				
				for(int j = 0; j < len; j++)
				{
					int ind = faceEdges.ContainsMatch(sharedEdges[j]);
					if(ind > -1)
						faces.Add(pb.faces[i]);
				}
			}

			return faces;
		}
Beispiel #10
0
 /**
  * Returns all faces that share an edge with originFace.  If calling multiple times, use the variation that
  * accepts a dictionary lookup to  save to the cost of generating it each call.
  */
 public static List <pb_Face> GetNeighborFaces(pb_Object pb, pb_Face originFace)
 {
     return(GetNeighborFaces(pb, pb.sharedIndices.ToDictionary(), new List <pb_Face>()
     {
         originFace
     }, originFace));
 }
Beispiel #11
0
    public bool FaceCheck(Vector3 pos)
    {
        Ray        ray = Camera.main.ScreenPointToRay(pos);
        RaycastHit hit;

        if (Physics.Raycast(ray.origin, ray.direction, out hit))
        {
            pb_Object hitpb = hit.transform.gameObject.GetComponent <pb_Object>();

            if (hitpb == null)
            {
                return(false);
            }

            Mesh m = hitpb.msh;

            int[] tri = new int[3] {
                m.triangles[hit.triangleIndex * 3 + 0],
                m.triangles[hit.triangleIndex * 3 + 1],
                m.triangles[hit.triangleIndex * 3 + 2]
            };

            pb   = hitpb;
            quad = hitpb.QuadWithTriangle(tri);
            return(true);
        }
        return(false);
    }
        /**
         *	Returns the average of each vertex position in a face.
         *	In local space.
         */
        private Vector3 FaceCenter(pb_Object pb, pb_Face face)
        {
            Vector3[] vertices = pb.vertices;

            Vector3 average = Vector3.zero;

            // face holds triangle data.  distinctIndices is a
            // cached collection of the distinct indices that
            // make up the triangles. Ex:
            // tris = {0, 1, 2, 2, 3, 0}
            // distinct indices = {0, 1, 2, 3}
            foreach (int index in face.distinctIndices)
            {
                average.x += vertices[index].x;
                average.y += vertices[index].y;
                average.z += vertices[index].z;
            }

            float len = (float)face.distinctIndices.Length;

            average.x /= len;
            average.y /= len;
            average.z /= len;

            return(average);
        }
Beispiel #13
0
    private void GeometryWithPoints(Vector3[] v)
    {
        // Wrap in faces
        pb_Face[] f = new pb_Face[v.Length / 4];

        for (int i = 0; i < v.Length; i += 4)
        {
            f[i / 4] = new pb_Face(new int[6]
            {
                i + 0, i + 1, i + 2,
                i + 1, i + 3, i + 2
            },
                                   pb_Constant.DefaultMaterial,
                                   new pb_UV(),
                                   0,
                                   -1,
                                   -1,
                                   (Color32)Color.white);
        }

        SetVertices(v);
        SetFaces(f);
        SetSharedIndices(ExtractSharedIndices());

        /* Set probuilder stuff type */
        _shape = Shape.Cube;

        ToMesh();
        Refresh();
    }
Beispiel #14
0
        /**
         * Provided two faces, this method will attempt to project @f2 and align its size, rotation, and position
         * to match the shared edge on f1.  Returns true on success, false otherwise.
         */
        public static bool AutoStitch(pb_Object pb, pb_Face f1, pb_Face f2)
        {
            // Cache shared indices (we gon' use 'em a lot)
            Dictionary <int, int> sharedIndices = pb.sharedIndices.ToDictionary();

            for (int i = 0; i < f1.edges.Length; i++)
            {
                // find a matching edge
                int ind = f2.edges.IndexOf(f1.edges[i], sharedIndices);
                if (ind > -1)
                {
                    // First, project the second face
                    pbUVOps.ProjectFacesAuto(pb, new pb_Face[] { f2 });

                    // Use the first first projected as the starting point
                    // and match the vertices
                    f1.manualUV = true;
                    f2.manualUV = true;

                    f1.textureGroup = -1;
                    f2.textureGroup = -1;

                    AlignEdges(pb, f1, f2, f1.edges[i], f2.edges[ind]);
                    return(true);
                }
            }

            // no matching edge found
            return(false);
        }
Beispiel #15
0
        /**
         * Returns all faces that share an edge with originFace
         */
        public static List <pb_Face> GetConnectedFaces(pb_Object pb, pb_Face originFace)
        {
            List <pb_Face> faces = new List <pb_Face>();

            pb_IntArray[] sharedIndices = pb.sharedIndices;

            pb_Edge[] sharedEdges = pb_Edge.GetUniversalEdges(originFace.edges, sharedIndices);

            for (int i = 0; i < pb.faces.Length; i++)
            {
                if (pb.faces[i] == originFace)
                {
                    continue;
                }

                pb_Edge[] faceEdges = pb_Edge.GetUniversalEdges(pb.faces[i].edges, sharedIndices);

                int ind = faceEdges.ContainsMatch(sharedEdges);
                if (ind > -1)
                {
                    faces.Add(pb.faces[i]);
                }
            }

            return(faces);
        }
    public static bool HiddenFace(pb_Object pb, pb_Face q, float dist)
    {
        // Grab the face normal
        Vector3 dir = pb_Math.PlaneNormal(pb.VerticesInWorldSpace(q));

        // If casting from the center of the plane hits, chekc the rest of the points for collisions
        Vector3 orig = pb.FaceCenter(q);

        bool      hidden = true;
        Transform hitObj = RaycastFaceCheck(orig, dir, dist, null);

        if (hitObj != null)
        {
            Vector3[] v = pb.VerticesInWorldSpace(q.indices);
            for (int i = 0; i < v.Length; i++)
            {
                if (null == RaycastFaceCheck(v[i], dir, dist, hitObj))
                {
                    hidden = false;
                    break;
                }
            }
        }
        else
        {
            hidden = false;
        }

        return(hidden);
    }
Beispiel #17
0
        void RefreshSelectedFacePreview()
        {
            pb_Face face = new pb_Face(currentSelection.face);                  // Copy the currently selected face

            face.ShiftIndicesToZero();                                          // Shift the selected face indices to zero

            // Copy the currently selected vertices in world space.
            // World space so that we don't have to apply transforms
            // to match the current selection.
            Vector3[] verts = currentSelection.pb.VerticesInWorldSpace(currentSelection.face.distinctIndices);

            // Now go through and move the verts we just grabbed out about .1m from the original face.
            Vector3 normal = pb_Math.Normal(verts);

            for (int i = 0; i < verts.Length; i++)
            {
                verts[i] += normal.normalized * .01f;
            }

            if (preview)
            {
                Destroy(preview.gameObject);
            }

            preview = pb_Object.CreateInstanceWithVerticesFaces(verts, new pb_Face[1] {
                face
            });
            preview.SetFaceMaterial(preview.faces, previewMaterial);
            preview.ToMesh();
            preview.Refresh();
        }
    public static bool HiddenFace(pb_Object pb, pb_Face q, float dist)
    {
        // Grab the face normal
        Vector3 dir = pb_Math.Normal(pb.VerticesInWorldSpace(q.indices));

        // If casting from the center of the plane hits, chekc the rest of the points for collisions
        Vector3 orig = pb.transform.TransformPoint(pb_Math.Average(pb.GetVertices(q)));

        bool hidden = true;
        Transform hitObj = RaycastFaceCheck(orig, dir, dist, null);
        if(hitObj != null)
        {
            Vector3[] v = pb.VerticesInWorldSpace(q.indices);
            for(int i = 0; i < v.Length; i++)
            {
                if(null == RaycastFaceCheck(v[i], dir, dist, hitObj))
                {
                    hidden = false;
                    break;
                }
            }
        }
        else
            hidden = false;

        return hidden;
    }
/**
	 *	\brief
	 *	param sharedIndex An optional array that sets the new pb_Face indices to use the _sharedIndices array.
	 *	\returns The newly appended pb_Face.
	 */
	public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, pb_Face face)
	{
		int[] shared = new int[v.Length];
		for(int i = 0; i < v.Length; i++)
			shared[i] = -1;
		return pb.AppendFace(v, face, shared);
	}
Beispiel #20
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 static bool SubdivideFace(this pb_Object pb, pb_Face[] faces, out pb_Face[] splitFaces)
	{
		List<EdgeConnection> split = new List<EdgeConnection>();
		foreach(pb_Face face in pb.SelectedFaces)
			split.Add(new EdgeConnection(face, new List<pb_Edge>(face.edges)));

		return pb.ConnectEdges(split, out splitFaces);
	}
	/**
	 * \brief Flips the winding order for the entire mesh. 
	 */
	// public static void ReverseWindingOrder(this pb_Object pb)
	// {
	// 	for(int i = 0; i < pb.faces.Length; i++)
	// 		pb.faces[i].ReverseIndices();
	
	// 	pb.ToMesh();
	// 	pb.Refresh();
	// }	

	/**
	 *	\brief Reverse the winding order for each passed #pb_Face.
	 *	@param faces The faces to apply normal flippin' to.
	 *	\returns Nothing.  No soup for you.
	 *	\sa SelectedFaces pb_Face
	 */
	public static void ReverseWindingOrder(this pb_Object pb, pb_Face[] faces)
	{
		for(int i = 0; i < faces.Length; i++)
			faces[i].ReverseIndices();

		pb.ToMesh();
		pb.Refresh();
	}	
Beispiel #23
0
        /**
         *	\brief Given an array of "donors", this method returns a merged #pb_Object.
         */
        public static bool CombineObjects(pb_Object[] pbs, out pb_Object combined)
        {
            combined = null;

            if (pbs.Length < 1)
            {
                return(false);
            }

            List <Vector3>     v = new List <Vector3>();
            List <pb_Face>     f = new List <pb_Face>();
            List <pb_IntArray> s = new List <pb_IntArray>();

            foreach (pb_Object pb in pbs)
            {
                int vertexCount = v.Count;

                // Vertices
                {
                    v.AddRange(pb.VerticesInWorldSpace());
                }

                // Faces
                {
                    pb_Face[] faces = new pb_Face[pb.faces.Length];
                    for (int i = 0; i < faces.Length; i++)
                    {
                        faces[i] = new pb_Face(pb.faces[i]);
                        faces[i].ShiftIndices(vertexCount);
                        faces[i].RebuildCaches();
                    }

                    f.AddRange(faces);
                }

                // Shared Indices
                {
                    pb_IntArray[] si = pb.GetSharedIndices();
                    for (int i = 0; i < si.Length; i++)
                    {
                        for (int n = 0; n < si[i].Length; n++)
                        {
                            si[i][n] += vertexCount;
                        }
                    }

                    s.AddRange(si);
                }
            }

            combined = pb_Object.CreateInstanceWithVerticesFacesSharedIndices(v.ToArray(), f.ToArray(), s.ToArray());

            combined.CenterPivot(new int[1] {
                0
            });

            return(true);
        }
Beispiel #24
0
/**
 *	\brief
 *	param sharedIndex An optional array that sets the new pb_Face indices to use the _sharedIndices array.
 *	\returns The newly appended pb_Face.
 */
        public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, pb_Face face)
        {
            int[] shared = new int[v.Length];
            for (int i = 0; i < v.Length; i++)
            {
                shared[i] = -1;
            }
            return(pb.AppendFace(v, face, shared));
        }
Beispiel #25
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());
        }
Beispiel #26
0
        /**
         *	Given a face and a point, this will add a vertex to the pb_Object and retriangulate the face.
         */
        public static bool AppendVerticesToFace(this pb_Object pb, pb_Face face, List <Vector3> points, out pb_Face newFace)
        {
            if (!face.isValid())
            {
                newFace = face;
                return(false);
            }

            // First order of business - project face to 2d
            int[]     distinctIndices = face.distinctIndices;
            Vector3[] verts           = pb.GetVertices(distinctIndices);

            // Get the face normal before modifying the vertex array
            Vector3 nrm      = pb_Math.Normal(pb.GetVertices(face.indices));
            Vector3 projAxis = pb_Math.GetProjectionAxis(nrm).ToVector3();

            // Add the new point
            Vector3[] t_verts = new Vector3[verts.Length + points.Count];
            System.Array.Copy(verts, 0, t_verts, 0, verts.Length);
            System.Array.Copy(points.ToArray(), 0, t_verts, verts.Length, points.Count);

            verts = t_verts;

            // Project
            List <Vector2> plane = new List <Vector2>(pb_Math.VerticesTo2DPoints(verts, projAxis));

            // Save the sharedIndices index for each distinct vertex
            pb_IntArray[] sharedIndices = pb.sharedIndices;
            int[]         sharedIndex   = new int[distinctIndices.Length + points.Count];
            for (int i = 0; i < distinctIndices.Length; i++)
            {
                sharedIndex[i] = sharedIndices.IndexOf(distinctIndices[i]);
            }

            for (int i = distinctIndices.Length; i < distinctIndices.Length + points.Count; i++)
            {
                sharedIndex[i] = -1;            // add the new vertex to it's own sharedIndex
            }
            // Triangulate the face with the new point appended
            int[] tris = Delauney.Triangulate(plane).ToIntArray();

            // Check to make sure the triangulated face is facing the same direction, and flip if not
            Vector3 del = Vector3.Cross(verts[tris[2]] - verts[tris[0]], verts[tris[1]] - verts[tris[0]]).normalized;

            if (Vector3.Dot(nrm, del) > 0)
            {
                System.Array.Reverse(tris);
            }

            // Compose new face
            newFace = pb.AppendFace(verts, new pb_Face(tris, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, -1, face.color), sharedIndex);

            // And delete the old
            pb.DeleteFace(face);

            return(true);
        }
Beispiel #27
0
    /**
     *	Removes face from SelectedFaces array, and updates the SelectedTriangles and SelectedEdges arrays to match.
     */
    public void RemoveFromFaceSelection(pb_Face face)
    {
        int indx = System.Array.IndexOf(this.faces, face);

        if (indx > -1)
        {
            SetSelectedFaces(m_selectedFaces.RemoveAt(indx));
        }
    }
Beispiel #28
0
 /**
  * Build a starting point (in this case, a quad)
  */
 void Start()
 {
     pb = pb_ShapeGenerator.PlaneGenerator(1, 1, 0, 0, ProBuilder.Core.Axis.Up);
     foreach (var f in pb.faces)
     {
         f.material = pb_Material.DefaultMaterial;
     }
     lastExtrudedFace = pb.faces[0];
 }
Beispiel #29
0
 public Vector2[] GetFaceUV(pb_Face q)
 {
     int[]     dv  = q.distinctIndices;
     Vector2[] uvs = new Vector2[dv.Length];
     for (int i = 0; i < uvs.Length; i++)
     {
         uvs[i] = msh.uv[dv[i]];
     }
     return(uvs);
 }
Beispiel #30
0
    /**
     *	\brief Gets all vertices in local space from face.
     *	@param _face The #pb_Face to draw vertices from.
     *	\returns A Vector3[] array containing all vertices contained within a #pb_Face.
     */
    public Vector3[] GetVertices(pb_Face face)
    {
        Vector3[] v = new Vector3[face.indices.Length];
        for (int i = 0; i < face.indices.Length; i++)
        {
            v[i] = vertices[face.indices[i]];
        }

        return(v);
    }
	static void Triangulate(pb_Object pb)
	{
		Vector3[] 	v = pb.vertices;
		Vector2[] 	u = pb.msh.uv;

		int triangleCount = pb.msh.triangles.Length;

		if(triangleCount == v.Length)
		{
			Debug.LogWarning("We can't pull over any further!\npb_Object: " + pb.name + " is already triangulated.");
		}

		int vertexCount = triangleCount;
		int faceCount = vertexCount / 3;

		Vector3[]	tri_vertices = new Vector3[vertexCount];
		Vector2[]	tri_uvs = new Vector2[vertexCount];
		pb_Face[]	tri_faces = new pb_Face[faceCount];

		int n = 0, f = 0;
		foreach(pb_Face face in pb.faces)
		{
			int[] indices = face.indices;

			for(int i = 0; i < indices.Length; i+=3)
			{
				tri_vertices[n+0] = v[indices[i+0]];
				tri_vertices[n+1] = v[indices[i+1]];
				tri_vertices[n+2] = v[indices[i+2]];

				tri_uvs[n+0] = u[indices[i+0]];
				tri_uvs[n+1] = u[indices[i+1]];
				tri_uvs[n+2] = u[indices[i+2]];
	
				tri_faces[f++] = new pb_Face( new int[] { n+0, n+1, n+2 },
											face.material,
											face.uv,
											face.smoothingGroup,
											face.textureGroup,		// textureGroup -> force to manual uv mode
											face.elementGroup,
											face.manualUV,			// manualUV
											face.color
										);	
				n += 3;
			}

		}

		pb.SetVertices(tri_vertices);
		pb.SetUV(tri_uvs);
		pb.SetFaces(tri_faces);

		pb.SetSharedIndices( pb_IntArrayUtility.ExtractSharedIndices(tri_vertices) );
		pb.SetSharedIndicesUV( new pb_IntArray[0] );
	}
Beispiel #32
0
	/**
	 * Append a group of new faces to the pb_Object.  Significantly faster than calling AppendFace multiple times.
	 */
	public static pb_Face[] AppendFaces(this pb_Object pb, Vector3[][] new_Vertices, Color[][] new_Colors, Vector2[][] new_uvs, pb_Face[] new_Faces, int[][] new_SharedIndices)
	{
		List<Vector3> _verts = new List<Vector3>(pb.vertices);
		List<Color> _colors = new List<Color>(pb.colors);
		List<Vector2> _uv = new List<Vector2>(pb.uv);

		List<pb_Face> _faces = new List<pb_Face>(pb.faces);
		pb_IntArray[] sharedIndices = pb.sharedIndices;

		int vc = pb.vertexCount;

		for(int i = 0; i < new_Faces.Length; i++)
		{
			_verts.AddRange(new_Vertices[i]);
			_colors.AddRange(new_Colors[i]);
			_uv.AddRange(new_uvs[i]);

			new_Faces[i].ShiftIndicesToZero();
			new_Faces[i].ShiftIndices(vc);
			new_Faces[i].RebuildCaches();
			_faces.Add(new_Faces[i]);

			if(new_SharedIndices != null && new_Vertices[i].Length != new_SharedIndices[i].Length)
			{
				Debug.LogError("Append Face failed because sharedIndex array does not match new vertex array.");
				return null;
			}

			if(new_SharedIndices != null)
			{
				for(int j = 0; j < new_SharedIndices[i].Length; j++)
				{
					pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, new_SharedIndices[i][j], j+vc);
				}
			}
			else
			{
				for(int j = 0; j < new_Vertices[i].Length; j++)
				{
					pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, -1, j+vc);
				}
			}

			vc = _verts.Count;
		}

		pb.SetSharedIndices(sharedIndices);

		pb.SetVertices(_verts.ToArray());
		pb.SetColors(_colors.ToArray());
		pb.SetUV(_uv.ToArray());
		pb.SetFaces(_faces.ToArray());

		return new_Faces;
	}
Beispiel #33
0
    static void Triangulate(pb_Object pb)
    {
        Vector3[] v = pb.vertices;
        Vector2[] u = pb.msh.uv;

        int triangleCount = pb.msh.triangles.Length;

        if (triangleCount == v.Length)
        {
            Debug.LogWarning("We can't pull over any further!\npb_Object: " + pb.name + " is already triangulated.");
        }

        int vertexCount = triangleCount;
        int faceCount   = vertexCount / 3;

        Vector3[] tri_vertices = new Vector3[vertexCount];
        Vector2[] tri_uvs      = new Vector2[vertexCount];
        pb_Face[] tri_faces    = new pb_Face[faceCount];

        int n = 0, f = 0;

        foreach (pb_Face face in pb.faces)
        {
            int[] indices = face.indices;

            for (int i = 0; i < indices.Length; i += 3)
            {
                tri_vertices[n + 0] = v[indices[i + 0]];
                tri_vertices[n + 1] = v[indices[i + 1]];
                tri_vertices[n + 2] = v[indices[i + 2]];

                tri_uvs[n + 0] = u[indices[i + 0]];
                tri_uvs[n + 1] = u[indices[i + 1]];
                tri_uvs[n + 2] = u[indices[i + 2]];

                tri_faces[f++] = new pb_Face(new int[] { n + 0, n + 1, n + 2 },
                                             face.material,
                                             face.uv,
                                             face.smoothingGroup,
                                             face.textureGroup,                                                         // textureGroup -> force to manual uv mode
                                             face.elementGroup,
                                             face.manualUV,                                                             // manualUV
                                             face.color
                                             );
                n += 3;
            }
        }

        pb.SetVertices(tri_vertices);
        pb.SetUV(tri_uvs);
        pb.SetFaces(tri_faces);

        pb.SetSharedIndices(pb_IntArrayUtility.ExtractSharedIndices(tri_vertices));
        pb.SetSharedIndicesUV(new pb_IntArray[0]);
    }
Beispiel #34
0
        /**
         * Merge all faces into a sigle face.
         */
        public static pb_Face MergeFaces(this pb_Object pb, pb_Face[] faces)
        {
            List <int> collectedIndices = new List <int>(faces[0].indices);

            for (int i = 1; i < faces.Length; i++)
            {
                collectedIndices.AddRange(faces[i].indices);
            }

            pb_Face mergedFace = new pb_Face(collectedIndices.ToArray(),
                                             faces[0].material,
                                             faces[0].uv,
                                             faces[0].smoothingGroup,
                                             faces[0].textureGroup,
                                             faces[0].elementGroup,
                                             faces[0].manualUV);

            pb_Face[] rebuiltFaces = new pb_Face[pb.faces.Length - faces.Length + 1];

            int n = 0;

            foreach (pb_Face f in pb.faces)
            {
                if (System.Array.IndexOf(faces, f) < 0)
                {
                    rebuiltFaces[n++] = f;
                }
            }

            rebuiltFaces[n] = mergedFace;

            pb.SetFaces(rebuiltFaces);

            // merge vertices that are on top of one another now that they share a face
            Dictionary <int, int> shared = new Dictionary <int, int>();

            for (int i = 0; i < mergedFace.indices.Length; i++)
            {
                int sharedIndex = pb.sharedIndices.IndexOf(mergedFace.indices[i]);

                if (shared.ContainsKey(sharedIndex))
                {
                    mergedFace.indices[i] = shared[sharedIndex];
                }
                else
                {
                    shared.Add(sharedIndex, mergedFace.indices[i]);
                }
            }

            pb.RemoveUnusedVertices();

            return(mergedFace);
        }
Beispiel #35
0
            public int indexB;          // face index - cannot be a sharedIndex

            /**
             *	Constructor
             */
            public SplitSelection(pb_Object pb, pb_Face face, Vector3 pointA, Vector3 pointB, bool aIsVertex, bool bIsVertex, int indexA, int indexB)
            {
                this.pb        = pb;
                this.face      = face;
                this.pointA    = pointA;
                this.pointB    = pointB;
                this.aIsVertex = aIsVertex;
                this.bIsVertex = bIsVertex;
                this.indexA    = indexA;
                this.indexB    = indexB;
            }
Beispiel #36
0
        /**
         * Removes faces from a pb_Object.  Overrides available for pb_Face[] and int[] faceIndices.  handles
         * all the sharedIndices moving stuff for you.
         */
        public static void DeleteFaces(this pb_Object pb, int[] faceIndices)
        {
            pb_Face[] faces = new pb_Face[faceIndices.Length];

            for (int i = 0; i < faces.Length; i++)
            {
                faces[i] = pb.faces[faceIndices[i]];
            }

            int[] distInd = pb_Face.AllTrianglesDistinct(faces);

            Vector3[] verts = pb.vertices.RemoveAt(distInd);
            Color[]   cols  = pb.colors.RemoveAt(distInd);
            Vector2[] uvs   = pb.uv.RemoveAt(distInd);

            pb_Face[] nFaces = pb.faces.RemoveAt(faceIndices);

            // shift all other face indices down to account for moved vertex positions
            for (int i = 0; i < nFaces.Length; i++)
            {
                int[] tris = nFaces[i].indices;
                for (int n = 0; n < tris.Length; n++)
                {
                    int sub = 0;
                    for (int d = 0; d < distInd.Length; d++)
                    {
                        if (tris[n] > distInd[d])
                        {
                            sub++;
                        }
                    }
                    tris[n] -= sub;
                }
                nFaces[i].SetIndices(tris);
            }

            // shift all other face indices in the shared index array down to account for moved vertex positions
            pb_IntArray[] si    = pb.sharedIndices;
            pb_IntArray[] si_uv = pb.sharedIndicesUV;

            pb_IntArrayUtility.RemoveValuesAndShift(ref si, distInd);
            pb_IntArrayUtility.RemoveValuesAndShift(ref si_uv, distInd);

            pb.SetSharedIndices(si);
            pb.SetSharedIndicesUV(si_uv);

            pb.SetVertices(verts);
            pb.SetColors(cols);
            pb.SetUV(uvs);

            pb.SetFaces(nFaces);
            pb.RebuildFaceCaches();
        }
        void Update()
        {
            float time = Time.time * speed;

            Vector3 position = new Vector3(
                Mathf.PerlinNoise(time, time) * travel,
                2,
                Mathf.PerlinNoise(time + 1f, time + 1f) * travel
                );

            transform.position = position;

            if (target == null)
            {
                Debug.LogWarning("Missing the ProBuilder Mesh target!");
                return;
            }

            // instead of testing distance by converting each face's center to world space,
            // convert the world space of this object to the pb-Object local transform.
            Vector3 pbRelativePosition = target.transform.InverseTransformPoint(transform.position);

            // reset the last colored face to white
            if (nearest != null)
            {
                target.SetFaceColor(nearest, Color.white);
            }

            // iterate each face in the pb_Object looking for the one nearest
            // to this object.
            int   faceCount        = target.faces.Length;
            float smallestDistance = Mathf.Infinity;

            nearest = target.faces[0];

            for (int i = 0; i < faceCount; i++)
            {
                float distance = Vector3.Distance(pbRelativePosition, FaceCenter(target, target.faces[i]));

                if (distance < smallestDistance)
                {
                    smallestDistance = distance;
                    nearest          = target.faces[i];
                }
            }

            // Set a single face's vertex colors.  If you're updating more than one face, consider using
            // the pb_Object.SetColors(Color[] colors); function instead.
            target.SetFaceColor(nearest, Color.blue);

            // Apply the stored vertex color array to the Unity mesh.
            target.Refresh(RefreshMask.Colors);
        }
Beispiel #38
0
    public void SetFaceColor(pb_Face face, Color color)
    {
        if (_colors == null)
        {
            _colors = pbUtil.FilledArray <Color>(Color.white, vertexCount);
        }

        foreach (int i in face.distinctIndices)
        {
            _colors[i] = color;
        }
    }
	/**
	 * Append a group of new faces to the pb_Object.  Significantly faster than calling AppendFace multiple times.
	 */
	public static pb_Face[] AppendFaces(this pb_Object pb, Vector3[][] new_Vertices, pb_Face[] new_Faces, int[][] new_SharedIndices)
	{
		List<Vector3> _verts = new List<Vector3>(pb.vertices);
		List<pb_Face> _faces = new List<pb_Face>(pb.faces);
		pb_IntArray[] sharedIndices = pb.sharedIndices;

		int vc = pb.vertexCount;

		// Dictionary<int, int> grp = new Dictionary<int, int>();	// this allows append face to add new vertices to a new shared index group
		// 														// if the sharedIndex is negative and less than -1, it will create new gorup
		// 														// that other sharedIndex members can then append themselves to.
		for(int i = 0; i < new_Faces.Length; i++)
		{
			_verts.AddRange(new_Vertices[i]);
			new_Faces[i].ShiftIndicesToZero();
			new_Faces[i].ShiftIndices(vc);
			_faces.Add(new_Faces[i]);

			if(new_SharedIndices != null && new_Vertices[i].Length != new_SharedIndices[i].Length)
			{
				Debug.LogError("Append Face failed because sharedIndex array does not match new vertex array.");
				return null;
			}

			if(new_SharedIndices != null)
				for(int j = 0; j < new_SharedIndices[i].Length; j++)
				{
					// TODO - FIX ME
					// if(new_SharedIndices[i][j] < -1)
					// {
					// 	if(grp.ContainsKey(new_SharedIndices[i][j]))
					// 		AddValueAtIndex(grp[new_SharedIndices[i][j]], j+vc);
					// 	else
					// 		grp.Add(new_SharedIndices[i][j], AddValueAtIndex(new_SharedIndices[i][j], j+vc));
					// }
					// else
						pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, new_SharedIndices[i][j], j+vc);
				}
			else
				for(int j = 0; j < new_Vertices[i].Length; j++)
				{
					pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, -1, j+vc);
				}
			vc = _verts.Count;
		}

		pb.SetSharedIndices(sharedIndices);
		pb.SetVertices(_verts.ToArray());
		pb.SetFaces(_faces.ToArray());
		pb.ToMesh();

		return new_Faces;
	}
Beispiel #40
0
    /**
     * \brief Gets vertex normals for the selected face.
     * @param face
     * \returns Vector3[] containing all normals for a face.
     */
    public Vector3[] GetNormals(pb_Face face)
    {
        // muhahaha
        Vector3[] normals = msh.normals;
        Vector3[] v       = new Vector3[face.indices.Length];
        for (int i = 0; i < face.indices.Length; i++)
        {
            v[i] = normals[face.indices[i]];
        }

        return(v);
    }
		public static Vector3 Normal(pb_Object pb, pb_Face face)
		{
			Vector3 p0 = pb.vertices[face.indices[0]];
			Vector3 p1 = pb.vertices[face.indices[1]];
			Vector3 p2 = pb.vertices[face.indices[2]];

			Vector3 cross = Vector3.Cross(p1 - p0, p2 - p0);
			if (cross.magnitude < Mathf.Epsilon)
				return new Vector3(0f, 0f, 0f); // bad triangle
			else
			{
				return cross.normalized;
			}
		}
 public pb_SerializableFace(pb_Face face)
 {
     this.indices			= face.indices;
     this.distinctIndices	= face.distinctIndices;
     this.edges				= face.edges;
     this.smoothingGroup		= face.smoothingGroup;
     this.uv					= face.uv;
     this.material			= face.material;
     this.manualUV  			= false;
     pb_UpgradeKitUtils.TryGetField(face, "manualUV", ref this.manualUV);
     this.elementGroup		= -1;
     pb_UpgradeKitUtils.TryGetField(face, "elementGroup", ref this.elementGroup);
     this.textureGroup		= -1;
     pb_UpgradeKitUtils.TryGetField(face, "textureGroup", ref this.textureGroup);
 }
	/**
	 *	\brief Duplicates and returns the passed pb_Object.
	 *	@param pb The pb_Object to duplicate.
	 *	\returns A unique copy of the passed pb_Object.
	 */
	public static pb_Object InitWithObject(pb_Object pb)
	{
		Vector3[] v = new Vector3[pb.vertexCount];
		System.Array.Copy(pb.vertices, v, pb.vertexCount);

		pb_Face[] f = new pb_Face[pb.faces.Length];
		
		for(int i = 0; i < f.Length; i++)
			f[i] = new pb_Face(pb.faces[i]);

		pb_Object p = CreateInstanceWithVerticesFacesSharedIndices(v, f, pb.GetSharedIndices());

		p.gameObject.name = pb.gameObject.name + "-clone";

		return p;
	}
Beispiel #44
0
		/**
		 * Attempt to figure out the winding order the passed face.  Note that 
		 * this may return WindingOrder.Unknown.
		 */
		public static WindingOrder GetWindingOrder(this pb_Object pb, pb_Face face)
		{
			Vector2[] p = pb_Math.PlanarProject(pb.GetVertices( face.edges.AllTriangles() ), pb_Math.Normal(pb, face));

			float sum = 0f;

			// http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
			for(int i = 0; i < p.Length; i++)
			{
				Vector2 a = p[i];
				Vector2 b = i < p.Length - 1 ? p[i+1] : p[0];

				sum += ( (b.x-a.x) * (b.y+a.y) );
			}

			return sum == 0f ? WindingOrder.Unknown : (sum >= 0f ? WindingOrder.Clockwise : WindingOrder.CounterClockwise);
		}
	/**
	 * Deep copy constructor.
	 */
	public pb_Face(pb_Face face)
	{
		_indices = new int[face.indices.Length];
		System.Array.Copy(face.indices, _indices, face.indices.Length);
		
		_uv = new pb_UV(face.uv);

		_mat = face.material;

		_smoothingGroup = face.smoothingGroup;
		textureGroup = face.textureGroup;
		elementGroup = face.elementGroup;

		_colors = new Color32[face.colors.Length];
		System.Array.Copy(face.colors, _colors, colors.Length);
		
		manualUV = face.manualUV;

		RebuildCaches();
	}
Beispiel #46
0
	/**
	 * Append a new face to the pb_Object using sharedIndex array to set the face indices to sharedIndex groups.
	 */
	public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, Color[] c, Vector2[] u, pb_Face face, int[] sharedIndex)
	{
		int vertexCount = pb.vertexCount;

		Vector3[] _verts = new Vector3[vertexCount + v.Length];
		Color[] _colors = new Color[vertexCount + c.Length];
		Vector2[] _uvs = new Vector2[pb.uv.Length + u.Length];

		List<pb_Face> _faces = new List<pb_Face>(pb.faces);
		pb_IntArray[] sharedIndices = pb.sharedIndices;

		// copy new vertices
		System.Array.Copy(pb.vertices, 0, _verts, 0, vertexCount);
		System.Array.Copy(v, 0, _verts, vertexCount, v.Length);

		// copy new colors
		System.Array.Copy(pb.colors, 0, _colors, 0, vertexCount);
		System.Array.Copy(c, 0, _colors, vertexCount, c.Length);

		// copy new uvs
		System.Array.Copy(pb.uv, 0, _uvs, 0, pb.uv.Length);
		System.Array.Copy(u, 0, _uvs, pb.uv.Length, u.Length);

		face.ShiftIndicesToZero();
		face.ShiftIndices(vertexCount);
		face.RebuildCaches();

		_faces.Add(face);

		for(int i = 0; i < sharedIndex.Length; i++)
			pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, sharedIndex[i], i+vertexCount);

		pb.SetVertices( _verts );
		pb.SetColors( _colors );
		pb.SetUV( _uvs );
		
		pb.SetSharedIndices(sharedIndices);
		pb.SetFaces(_faces.ToArray());

		return face;
	}
Beispiel #47
0
	/**
	 *	\brief Duplicates and returns the passed pb_Object.
	 *	@param pb The pb_Object to duplicate.
	 *	\returns A unique copy of the passed pb_Object.
	 */
	public static pb_Object InitWithObject(pb_Object pb)
	{
		Vector3[] v = new Vector3[pb.vertexCount];
		System.Array.Copy(pb.vertices, v, pb.vertexCount);
		
		Vector2[] u = new Vector2[pb.vertexCount];
		System.Array.Copy(pb.uv, u, pb.vertexCount);

		Color[] c = new Color[pb.vertexCount];
		System.Array.Copy(pb.colors, c, pb.vertexCount);

		pb_Face[] f = new pb_Face[pb.faces.Length];
		
		for(int i = 0; i < f.Length; i++)
			f[i] = new pb_Face(pb.faces[i]);

		pb_Object p = CreateInstanceWithElements(v, u, c, f, pb.GetSharedIndices(), pb.GetSharedIndicesUV());

		p.gameObject.name = pb.gameObject.name + "-clone";

		return p;
	}
	public static pb_Object ProBuilderize(Transform t)
	{
		Mesh m = t.GetComponent<MeshFilter>().sharedMesh;

		pb_Face[] faces = new pb_Face[m.triangles.Length/3];
		int f = 0;
		for(int n = 0; n < m.subMeshCount; n++)
		{
			for(int i = 0; i < m.triangles.Length; i+=3)
			{
				faces[f] = new pb_Face(
					new int[3] {
						m.triangles[i+0],
						m.triangles[i+1],
						m.triangles[i+2]
						},
					t.GetComponent<MeshRenderer>().sharedMaterials[n],
					new pb_UV(),
					0,
					Color.white
				);
				f++;
			}
		}

		t.gameObject.SetActive(false);
		pb_Object pb = ProBuilder.CreateObjectWithVerticesFaces(m.vertices, faces);
		pb.SetName("FrankenMesh");
		pb_Editor_Utility.SetEntityType(ProBuilder.EntityType.Detail, pb.gameObject);
		
		GameObject go = pb.gameObject;

		go.transform.position = t.position;
		go.transform.localRotation = t.localRotation;
		go.transform.localScale = t.localScale;
		pb.FreezeScaleTransform();
		return pb;
	}
	/**
	 * Append a new face to the pb_Object.
	 */
	public static pb_Face AppendFace(this pb_Object pb, Vector3[] v, pb_Face face, int[] sharedIndex)
	{
		List<Vector3> _verts = new List<Vector3>(pb.vertices);
		List<pb_Face> _faces = new List<pb_Face>(pb.faces);
		pb_IntArray[] sharedIndices = pb.sharedIndices;
		int vertexCount = pb.vertexCount;

		_verts.AddRange(v);
		face.ShiftIndicesToZero();
		face.ShiftIndices(vertexCount);
		face.RebuildCaches();
		_faces.Add(face);

		// Dictionary<int, int> grp = new Dictionary<int, int>();	// this allows append face to add new vertices to a new shared index group
		// 														// if the sharedIndex is negative and less than -1, it will create new gorup
		// 														// that other sharedIndex members can then append themselves to.
		for(int i = 0; i < sharedIndex.Length; i++)
		{
			// if(sharedIndex[i] < -1)
			// {
			// 	if(grp.ContainsKey(sharedIndex[i]))
			// 		AddIndexToSharedIndexArray(grp[sharedIndex[i]], i+vertexCount);
			// 	else
			// 		grp.Add(sharedIndex[i], AddIndexToSharedIndexArray(sharedIndex[i], i+vertexCount));
			// }
			// else
				pb_IntArrayUtility.AddValueAtIndex(ref sharedIndices, sharedIndex[i], i+vertexCount);
		}

		pb.SetSharedIndices(sharedIndices);
		pb.SetVertices(_verts.ToArray() );
		pb.SetFaces(_faces.ToArray());

		pb.ToMesh();

		return face;
	}
Beispiel #50
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;
		}
    void ExtrudeEdge()
    {
        pb_Face sourceFace = lastExtrudedFace;

        // fetch a random perimeter edge connected to the last face extruded
        List<pb_WingedEdge> wings = pb_WingedEdge.GetWingedEdges(pb);
        IEnumerable<pb_WingedEdge> sourceWings = wings.Where(x => x.face == sourceFace);
        List<pb_Edge> nonManifoldEdges = sourceWings.Where(x => x.opposite == null).Select(y => y.edge.local).ToList();
        int rand = (int) Random.Range(0, nonManifoldEdges.Count);
        pb_Edge sourceEdge = nonManifoldEdges[rand];

        // get the direction this edge should extrude in
        Vector3 dir = ((pb.vertices[sourceEdge.x] + pb.vertices[sourceEdge.y]) * .5f) - sourceFace.distinctIndices.Average(x => pb.vertices[x]);
        dir.Normalize();

        // this will be populated with the extruded edge
        pb_Edge[] extrudedEdges;

        // perform extrusion
        pb.Extrude(new pb_Edge[] { sourceEdge }, 0f, false, true, out extrudedEdges);

        // get the last extruded face
        lastExtrudedFace = pb.faces.Last();

        // not strictly necessary, but makes it easier to handle element selection
        pb.SetSelectedEdges( extrudedEdges );

        // translate the vertices
        pb.TranslateVertices(pb.SelectedTriangles, dir * distance);

        // rebuild mesh with new geometry added by extrude
        pb.ToMesh();

        // rebuild mesh normals, textures, collisions, etc
        pb.Refresh();
    }
	/**
	 *	\brief Given an array of "donors", this method returns a merged #pb_Object.
	 */
	 public static bool CombineObjects(pb_Object[] pbs, out pb_Object combined)
	 {
	 	combined = null;

	 	if(pbs.Length < 1) return false;

	 	List<Vector3> v = new List<Vector3>();
	 	List<pb_Face> f = new List<pb_Face>();
	 	List<pb_IntArray> s = new List<pb_IntArray>();

	 	foreach(pb_Object pb in pbs)
	 	{
	 		int vertexCount = v.Count;

	 		// Vertices
	 		{
		 		v.AddRange(pb.VerticesInWorldSpace());
			}

			// Faces
		 	{
		 		pb_Face[] faces = new pb_Face[pb.faces.Length];
		 		for(int i = 0; i < faces.Length; i++)
		 		{
		 			faces[i] = new pb_Face(pb.faces[i]);
		 			faces[i].ShiftIndices(vertexCount);
		 			faces[i].RebuildCaches();
		 		}

		 		f.AddRange(faces);
	 		}

	 		// Shared Indices
	 		{
		 		pb_IntArray[] si = pb.GetSharedIndices();
		 		for(int i = 0; i < si.Length; i++)
		 		{
		 			for(int n = 0; n < si[i].Length; n++)
		 				si[i][n] += vertexCount;
		 		}

		 		s.AddRange(si);
		 	}
	 	}

	 	combined = pb_Object.CreateInstanceWithVerticesFacesSharedIndices(v.ToArray(), f.ToArray(), s.ToArray());
	 	
	 	combined.CenterPivot(new int[1]{0});

	 	return true;
	 }
	/**
	 * Removes the vertex associations so that this face may be moved independently of the main object.
	 */
	public static void DetachFace(this pb_Object pb, pb_Face face)
	{
		pb_IntArray[] sharedIndices = pb.sharedIndices;
		pb_IntArrayUtility.RemoveValues(ref sharedIndices, face.indices);

		// Add these vertices back into the sharedIndices array under it's own entry
		for(int i = 0; i < face.distinctIndices.Length; i++)
		{			
			int[] arr = new int[1] { face.distinctIndices[i] };
			sharedIndices = pbUtil.Add(sharedIndices, new pb_IntArray(arr));
		}

		pb.SetSharedIndices(sharedIndices);
	}
	/**
	 *	Inserts a split from each selected vertex to the center of the face
	 */
	private static bool PokeFace_Internal(pb_Object pb, pb_Face face, int[] indices_nonFaceSpecific,
		out pb_Face[] splitFaces,
		out Vector3[][] splitVertices,
		out int[][] splitSharedIndices)
	{
		splitFaces = null;
		splitVertices = null;
		splitSharedIndices = null;

		pb_IntArray[] sharedIndices = pb.sharedIndices;

		///** Sort index array such that it only uses indices local to the passed face
		int[] indices = new int[indices_nonFaceSpecific.Length];
		int[] dist_ind_si = new int[face.distinctIndices.Length];

		// figure out sharedIndices index of distinct Indices
		for(int i = 0; i < face.distinctIndices.Length; i++)
			dist_ind_si[i] = sharedIndices.IndexOf(face.distinctIndices[i]);

		// now do the same for non-face specific indices, assigning matching groups
		for(int i = 0; i < indices.Length; i++)
		{
			int ind = System.Array.IndexOf(dist_ind_si, sharedIndices.IndexOf(indices_nonFaceSpecific[i]));
			if(ind < 0) return false;

			indices[i] = face.distinctIndices[ind];
		}
		///** Sort index array such that it only uses indices local to the passed face

		Vector3 cen3d = pb_Math.Average(pb.GetVertices(face));
		
		Vector3[] verts 	= pb.GetVertices(face.distinctIndices);
		Vector3 nrm 		= pb_Math.Normal(pb.GetVertices(face.indices));
		Vector2[] plane 	= pb_Math.VerticesTo2DPoints(verts, nrm);
		Vector2[] indPlane 	= pb_Math.VerticesTo2DPoints(pb.GetVertices(indices), nrm);
		Vector2 cen2d 		= pb_Math.VerticesTo2DPoints( new Vector3[1] { cen3d }, nrm)[0];

		// Get the directions from which to segment this face
		Vector2[] dividers = new Vector2[indices.Length];
		for(int i = 0; i < indices.Length; i++)
			dividers[i] = (indPlane[i] - cen2d).normalized;

		List<Vector2>[] quadrants2d = new List<Vector2>[indices.Length];
		List<Vector3>[] quadrants3d = new List<Vector3>[indices.Length];
		List<int>[]		sharedIndex = new List<int>[indices.Length];

		for(int i = 0; i < quadrants2d.Length; i++)
		{
			quadrants2d[i] = new List<Vector2>(1) { cen2d };
			quadrants3d[i] = new List<Vector3>(1) { cen3d };
			sharedIndex[i] = new List<int>(1) { -2 };		// any negative value less than -1 will be treated as a new group
		}

		for(int i = 0; i < face.distinctIndices.Length; i++)
		{
			// if this index is a divider, it needs to belong to the leftmost and 
			// rightmost quadrant
			int indexInPokeVerts = System.Array.IndexOf(indices, face.distinctIndices[i]);
			int ignore = -1;
			if( indexInPokeVerts > -1)
			{	
				// Add vert to this quadrant
				quadrants2d[indexInPokeVerts].Add(plane[i]);
				quadrants3d[indexInPokeVerts].Add(verts[i]);
				sharedIndex[indexInPokeVerts].Add(pb.sharedIndices.IndexOf(face.distinctIndices[i]));

				// And also the one closest counter clockwise
				ignore = indexInPokeVerts;
			}

			Vector2 dir = (plane[i]-cen2d).normalized;	// plane corresponds to distinctIndices
			float largestClockwiseDistance = 0f;
			int quad = -1;
			for(int j = 0; j < dividers.Length; j++)
			{
				if(j == ignore) continue;	// this is a dividing vertex - ignore

				float dist = Vector2.Angle(dividers[j], dir);
				if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f )
					dist = 360f - dist;

				if(dist > largestClockwiseDistance)
				{
					largestClockwiseDistance = dist;
					quad = j;
				}
			}

			quadrants2d[quad].Add(plane[i]);
			quadrants3d[quad].Add(verts[i]);
			sharedIndex[quad].Add(pb.sharedIndices.IndexOf(face.distinctIndices[i]));
		}

		int len = quadrants2d.Length;

		// Triangulate
		int[][] tris = new int[len][];
		for(int i = 0; i < len; i++)
		{
			tris[i] = Delauney.Triangulate(quadrants2d[i]).ToIntArray();
			
			if(tris[i].Length < 3)
				return false;

			// todo - check that face normal is correct
		}

		splitFaces 		= new pb_Face[len];
		splitVertices 	= new Vector3[len][];
		splitSharedIndices 	= new int[len][];

		for(int i = 0; i < len; i++)
		{
			// triangles, material, pb_UV, smoothing group, shared index
			splitFaces[i] = new pb_Face(tris[i], face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, -1, face.color);
			splitVertices[i] = quadrants3d[i].ToArray();
			splitSharedIndices[i] = sharedIndex[i].ToArray();
		}

		return true;
	}
	/**
	 *	
	 */
	public static bool ConnectEdges(this pb_Object pb, List<EdgeConnection> edgeConnectionsUnfiltered, out pb_Face[] faces)
	{
		// first, remove any junk connections.  faces with less than two edges confuse this method.
		List<EdgeConnection> edgeConnections = new List<EdgeConnection>();
		foreach(EdgeConnection ec in edgeConnectionsUnfiltered)
			if(ec.isValid)
				edgeConnections.Add(ec);

		int len = edgeConnections.Count;

		if(len < 1)
		{
			Debug.LogWarning("No valid split paths found.  This is most likely because you are attempting to split edges that do belong to the same face, or do not have more than one edge selected.  This is not currently supported, sorry!");
			faces = null;
			return false;
		}


		Vector3[] vertices = pb.vertices;

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

		List<pb_Face> all_splitFaces = new List<pb_Face>();
		List<Vector3[]> all_splitVertices = new List<Vector3[]>();
		List<int[]> all_splitSharedIndices = new List<int[]>();
		bool[] success = new bool[len];

		// use a nullable type because in order for the adjacent face triangulation
		// code to work, it needs to know what dangling vert belongs to which edge, 
		// if we out a vector3[] with each index corresponding to the passed edges
		// in EdgeConnection, it's easy to maintain the relationship.
		Vector3?[][] danglingVertices = new Vector3?[len][];	

		int i = 0;
		foreach(EdgeConnection fc in edgeConnections)
		{	
			pb_Face[] splitFaces = null;
			Vector3[][] splitVertices = null;
			int[][] splitSharedIndices = null;
	
			if( fc.edges.Count < 3 )
			{
				Vector3 edgeACen = (vertices[fc.edges[0].x] + vertices[fc.edges[0].y]) / 2f; 
				Vector3 edgeBCen = (vertices[fc.edges[1].x] + vertices[fc.edges[1].y]) / 2f;
				danglingVertices[i] = new Vector3?[2] { edgeACen, edgeBCen };
				success[i] = SplitFace_Internal(new SplitSelection(pb, fc.face, edgeACen, edgeBCen, false, false, -1, -1),
					out splitFaces,
					out splitVertices, 
					out splitSharedIndices);

				if(success[i])
					successfullySplitFaces.Add(fc.face);
			}
			else
			{
				Vector3?[] appendedVertices = null;
				success[i] = SubdivideFace_Internal(pb, fc,
					out appendedVertices,
					out splitFaces,
					out splitVertices,
					out splitSharedIndices);
	
				if(success[i])
					successfullySplitFaces.Add(fc.face);
	
				danglingVertices[i] = appendedVertices;
			}

			if(success[i])
			{
				int texGroup = fc.face.textureGroup < 0 ? pb.UnusedTextureGroup(i+1) : fc.face.textureGroup;
				
				for(int j = 0; j < splitFaces.Length; j++)
				{
					splitFaces[j].textureGroup = texGroup;
					all_splitFaces.Add(splitFaces[j]);
					all_splitVertices.Add(splitVertices[j]);
					all_splitSharedIndices.Add(splitSharedIndices[j]);
				}
			}

			i++;
		}

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

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

		Dictionary<pb_Face, List<Vector3>> addVertex = new Dictionary<pb_Face, List<Vector3>>();
		List<pb_Face> temp = new List<pb_Face>();
		for(int j = 0; j < edgeConnections.Count; j++)
		{
			if(!success[j]) continue;

			// check that this edge has a buddy that it welded it's new vertex to, and if not,
			// create one
			for(i = 0; i < edgeConnections[j].edges.Count; i++)
			{
				if(danglingVertices[j][i] == null) 
					continue;

				List<pb_Face> connected = allConnects[j][i];

				foreach(pb_Face face in connected)
				{
					int ind = successfullySplitFaces.IndexOf(face);

					if(ind < 0)
					{
						if(addVertex.ContainsKey(face))
							addVertex[face].Add((Vector3)danglingVertices[j][i]);
						else
						{
							temp.Add(face);
							addVertex.Add(face, new List<Vector3>(1) { (Vector3)danglingVertices[j][i] });
						}
					}
				}
			}
		}
		
		pb_Face[] appendedFaces = pb.AppendFaces(all_splitVertices.ToArray(), all_splitFaces.ToArray(), all_splitSharedIndices.ToArray());
		
		List<pb_Face> triangulatedFaces = new List<pb_Face>();
		foreach(KeyValuePair<pb_Face, List<Vector3>> add in addVertex)
		{
			pb_Face newFace;
			if( pb.AppendVerticesToFace(add.Key, add.Value, out newFace) )
				triangulatedFaces.Add(newFace);
			else
				Debug.LogError("Mesh re-triangulation failed.  Specifically, AppendVerticesToFace(" + add.Key + " : " + add.Value.ToFormattedString(", "));
		}

		// Re-triangulate any faces left with dangling verts at edges
		// Weld verts, including those added in re-triangu
		int[] splitFaceTris = pb_Face.AllTriangles(appendedFaces);
		int[] triangulatedFaceTris = pb_Face.AllTriangles(triangulatedFaces);
		int[] allModifiedTris = new int[splitFaceTris.Length + triangulatedFaceTris.Length];
		System.Array.Copy(splitFaceTris, 0, allModifiedTris, 0, splitFaceTris.Length);
		System.Array.Copy(triangulatedFaceTris, 0, allModifiedTris, splitFaceTris.Length, triangulatedFaceTris.Length);
		
		pb.WeldVertices(allModifiedTris, Mathf.Epsilon);
		
		// Now that we're done screwing with geo, delete all the old faces (that were successfully split)		
		pb.DeleteFaces( successfullySplitFaces.ToArray() );
		faces = appendedFaces;
		return true;
	}
	/**
	 *	This method assumes that the split selection edges share a common face and have already been sanity checked.  Will return 
	 *	the variables necessary to compose a new face from the split, or null if the split is invalid.
	 */
	private static bool SplitFace_Internal(SplitSelection splitSelection,
		out pb_Face[] splitFaces,
		out Vector3[][] splitVertices,
		out int[][] splitSharedIndices) 
	{
		splitFaces = null;
		splitVertices = null;
		splitSharedIndices = null;

		pb_Object pb = splitSelection.pb;	// we'll be using this a lot
		pb_Face face = splitSelection.face;	// likewise

		int[] indices = face.distinctIndices;
		pb_IntArray[] sharedIndices = pb.sharedIndices;
		int[] sharedIndex = new int[indices.Length];
		for(int i = 0; i < indices.Length; i++)
			sharedIndex[i] = sharedIndices.IndexOf(indices[i]);

		// First order of business is to translate the face to 2D plane.
		Vector3[] verts = pb.GetVertices(face.distinctIndices);

		Vector3 projAxis = pb_Math.GetProjectionAxis( pb_Math.Normal(pb.GetVertices(face.indices))).ToVector3();
		Vector2[] plane = pb_Math.VerticesTo2DPoints(verts, projAxis);

		// Split points
 		Vector3 splitPointA_3d = splitSelection.pointA;
 		Vector3 splitPointB_3d = splitSelection.pointB;

		Vector2 splitPointA_2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { splitPointA_3d }, projAxis )[0];
		Vector2 splitPointB_2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { splitPointB_3d }, projAxis )[0];

		List<Vector3> v_polyA = new List<Vector3>();	// point in object space
		List<Vector2> v_polyA_2d = new List<Vector2>();	// point in 2d space - used to triangulate
		List<Vector3> v_polyB = new List<Vector3>();	// point in object space
		List<Vector2> v_polyB_2d = new List<Vector2>();	// point in 2d space - used to triangulate
		List<int> i_polyA = new List<int>();			// sharedIndices array index
		List<int> i_polyB = new List<int>();			// sharedIndices array index

		List<int> nedgeA = new List<int>();
		List<int> nedgeB = new List<int>();

		// Sort points into two separate polygons
		for(int i = 0; i < indices.Length; i++)
		{
			// is this point (a) a vertex to split or (b) on the negative or positive side of this split line
			if( (splitSelection.aIsVertex && splitSelection.indexA == indices[i]) ||  (splitSelection.bIsVertex && splitSelection.indexB == indices[i]) )
			{
				v_polyA.Add( verts[i] );
				v_polyB.Add( verts[i] );

				v_polyA_2d.Add( plane[i] );
				v_polyB_2d.Add( plane[i] );

				i_polyA.Add( sharedIndex[i] );
				i_polyB.Add( sharedIndex[i] );
			}
			else
			{
				// split points across the division line
				Vector2 perp = pb_Math.Perpendicular(splitPointB_2d, splitPointA_2d);
				Vector2 origin = (splitPointA_2d + splitPointB_2d) / 2f;
				
				if( Vector2.Dot(perp, plane[i]-origin) > 0 )
				{
					v_polyA.Add(verts[i]);
					v_polyA_2d.Add(plane[i]);
					i_polyA.Add(sharedIndex[i]);
				}
				else
				{
					v_polyB.Add(verts[i]);
					v_polyB_2d.Add(plane[i]);
					i_polyB.Add(sharedIndex[i]);
				}
			}
		}

		if(!splitSelection.aIsVertex)
		{
			v_polyA.Add( splitPointA_3d );
			v_polyA_2d.Add( splitPointA_2d );
			v_polyB.Add( splitPointA_3d );
			v_polyB_2d.Add( splitPointA_2d );
			i_polyA.Add(-1);
			i_polyB.Add(-1);	//	neg 1 because it's a new vertex point

			nedgeA.Add(v_polyA.Count);
			nedgeB.Add(v_polyB.Count);
		}

		if(!splitSelection.bIsVertex)
		{
			v_polyA.Add( splitPointB_3d );
			v_polyA_2d.Add( splitPointB_2d );
			v_polyB.Add( splitPointB_3d );
			v_polyB_2d.Add( splitPointB_2d );
			i_polyA.Add(-1);
			i_polyB.Add(-1);	//	neg 1 because it's a new vertex point
		
			nedgeA.Add(v_polyA.Count);
			nedgeB.Add(v_polyB.Count);
		}

		if(v_polyA_2d.Count < 3 || v_polyB_2d.Count < 3)
		{
			splitFaces = null;
			splitVertices = null;
			splitSharedIndices = null;
			return false;
		}

		// triangulate new polygons
		int[] t_polyA = Delauney.Triangulate(v_polyA_2d).ToIntArray();
		int[] t_polyB = Delauney.Triangulate(v_polyB_2d).ToIntArray();

		if(t_polyA.Length < 3 || t_polyB.Length < 3)
			return false;

		// figure out the face normals for the new faces and check to make sure they match the original face
		Vector2[] pln = pb_Math.VerticesTo2DPoints( pb.GetVertices(face.indices), projAxis );
		Vector3 nrm = Vector3.Cross( pln[2] - pln[0], pln[1] - pln[0]);
		Vector3 nrmA = Vector3.Cross( v_polyA_2d[ t_polyA[2] ]-v_polyA_2d[ t_polyA[0] ], v_polyA_2d[ t_polyA[1] ]-v_polyA_2d[ t_polyA[0] ] );
		Vector3 nrmB = Vector3.Cross( v_polyB_2d[ t_polyB[2] ]-v_polyB_2d[ t_polyB[0] ], v_polyB_2d[ t_polyB[1] ]-v_polyB_2d[ t_polyB[0] ] );

		if(Vector3.Dot(nrm, nrmA) < 0) System.Array.Reverse(t_polyA);
		if(Vector3.Dot(nrm, nrmB) < 0) System.Array.Reverse(t_polyB);

		// triangles, material, pb_UV, smoothing group, shared index
		pb_Face faceA = new pb_Face( t_polyA, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.color);
		pb_Face faceB = new pb_Face( t_polyB, face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.color);

		splitFaces = new pb_Face[2] { faceA, faceB };
		splitVertices = new Vector3[2][] { v_polyA.ToArray(), v_polyB.ToArray() };
		splitSharedIndices = new int[2][] { i_polyA.ToArray(), i_polyB.ToArray() };

		return true;
	}
		public int indexB;	// face index - cannot be a sharedIndex

		/**
		 *	Constructor
		 */
		public SplitSelection(pb_Object pb, pb_Face face, Vector3 pointA, Vector3 pointB, bool aIsVertex, bool bIsVertex, int indexA, int indexB)
		{
			this.pb = pb;
			this.face = face;
			this.pointA = pointA;
			this.pointB = pointB;
			this.aIsVertex = aIsVertex;
			this.bIsVertex = bIsVertex;
			this.indexA = indexA;
			this.indexB = indexB;
		}
	public static void Extrude(this pb_Object pb, pb_Face[] faces)
	{
		pb.Extrude(faces, EXTRUDE_DISTANCE);
	}
	public static void Extrude(this pb_Object pb, pb_Face[] faces, float extrudeDistance)
	{
		if(faces == null || faces.Length < 1)
			return;

		pb_IntArray[] sharedIndices = pb.GetSharedIndices();

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

		pb_Edge[] perimeterEdges = pb.GetPerimeterEdges(faces);

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

		pb_Face[] edgeFaces = new pb_Face[perimeterEdges.Length];	// can't assume faces and perimiter edges will be 1:1 - so calculate perimeters then extrace face information
		int[] allEdgeIndices = new int[perimeterEdges.Length * 2];
		int c = 0;
		for(int i = 0; i < perimeterEdges.Length; i++)
		{
			// wtf does this do
			edgeFaces[i] = faces[0];
			foreach(pb_Face face in faces)
				if(face.edges.Contains(perimeterEdges[i]))
					edgeFaces[i] = face;

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

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

		/// build out new faces around edges
		for(int i = 0; i < perimeterEdges.Length; i++)
		{
			pb_Edge edge = perimeterEdges[i];
			pb_Face face = edgeFaces[i];

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

			// don't bother getting vertex normals if not auto-extruding
			if(extrudeDistance > Mathf.Epsilon)
			{
				xnorm = Norm( edge.x, sharedIndices, allEdgeIndices, oNormals );
				ynorm = Norm( edge.y, sharedIndices, allEdgeIndices, oNormals );
			}

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

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

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

		// merge extruded vertex indices with each other
		pb_IntArray[] si = pb.sharedIndices;	// leave the sharedIndices copy alone since we need the un-altered version later
		for(int i = 0; i < extrudedIndices.Count; i++)
		{
			int val = extrudedIndices[i].x;
			for(int n = 0; n < extrudedIndices.Count; n++)
			{
				if(n == i)
					continue;

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

		// Move extruded faces to top
		localVerts = pb.vertices;
		Dictionary<int, int> remappedTexGroups = new Dictionary<int, int>();
		foreach(pb_Face f in faces)
		{
			// Remap texture groups
			if(f.textureGroup > 0)
			{
				if(remappedTexGroups.ContainsKey(f.textureGroup))
				{
					f.textureGroup = remappedTexGroups[f.textureGroup];
				}
				else
				{
					int newTexGroup = pb.UnusedTextureGroup();
					remappedTexGroups.Add(f.textureGroup, newTexGroup);
					f.textureGroup = newTexGroup;
				}
			}

			int[] distinctIndices = f.distinctIndices;

			foreach(int ind in distinctIndices)
			{
				int oldIndex = si.IndexOf(ind);
				for(int i = 0; i < extrudedIndices.Count; i++)
				{
					if(oldIndex == extrudedIndices[i].x)
					{
						pb_IntArrayUtility.MergeSharedIndices(ref si, extrudedIndices[i].y, ind);
						break;
					}
				}
			}
		}

		// this is a separate loop cause the one above this must completely merge all sharedindices prior to 
		// checking the normal averages
		foreach(pb_Face f in faces)
		{
			foreach(int ind in f.distinctIndices)
			{
				Vector3 norm = Norm( ind, si, allEdgeIndices, oNormals );
				localVerts[ind] += norm.normalized * extrudeDistance;
			}
		}

		pb.SetSharedIndices(si);
		pb.SetVertices(localVerts);
		pb.RebuildFaceCaches();
	}
	// todo - there's a lot of duplicate code between this and poke face.
	/**
	 *	Inserts a vertex at the center of each edge, then connects the new vertices to another new
	 *	vertex placed at the center of the face.
	 */
	// internal method - so it's allow to be messy, right?
	private static bool SubdivideFace_Internal(pb_Object pb, EdgeConnection edgeConnection, 
		out Vector3?[] appendedVertices,	
		out pb_Face[] splitFaces,
		out Vector3[][] splitVertices,
		out int[][] splitSharedIndices)
	{
		splitFaces = null;
		splitVertices = null;
		splitSharedIndices = null;
		appendedVertices = new Vector3?[edgeConnection.edges.Count];

		// cache all the things
		pb_Face face = edgeConnection.face;
		pb_IntArray[] sharedIndices = pb.sharedIndices;
		Vector3[] vertices = pb.vertices;

		List<Vector3> edgeCenters3d = new List<Vector3>();//pb.GetVertices(edgeConnection.face));
		
		// filter duplicate edges
		int u = 0;
		List<int> usedEdgeIndices = new List<int>();
		foreach(pb_Edge edge in edgeConnection.edges)
		{
			int ind = face.edges.IndexOf(edge, sharedIndices);
			if(!usedEdgeIndices.Contains(ind))
			{
				Vector3 cen = (vertices[edge.x] + vertices[edge.y]) / 2f;
				edgeCenters3d.Add(cen);
				usedEdgeIndices.Add(ind);
				appendedVertices[u] = cen;
			}
			else
				appendedVertices[u] = null;

			u++;
		}

		// now we have all the vertices of the old face, plus the new edge center vertices

		Vector3[] verts3d = pb.GetVertices(face.distinctIndices);
		Vector3 nrm = pb_Math.Normal(pb.GetVertices(face.indices));

		Vector2[] verts2d = pb_Math.VerticesTo2DPoints(verts3d, nrm);
		Vector2[] edgeCenters2d = pb_Math.VerticesTo2DPoints(edgeCenters3d.ToArray(), nrm);
		
		Vector3 cen3d = pb_Math.Average(verts3d);
		Vector2 cen2d = pb_Math.VerticesTo2DPoints( new Vector3[1] { cen3d }, nrm)[0];

		// Get the directions from which to segment this face
		Vector2[] dividers = new Vector2[edgeCenters2d.Length];
		for(int i = 0; i < edgeCenters2d.Length; i++)
			dividers[i] = (edgeCenters2d[i] - cen2d).normalized;

		List<Vector2>[] quadrants2d = new List<Vector2>[edgeCenters2d.Length];
		List<Vector3>[] quadrants3d = new List<Vector3>[edgeCenters2d.Length];
		List<int>[]		sharedIndex = new List<int>[edgeCenters2d.Length];

		for(int i = 0; i < quadrants2d.Length; i++)
		{
			quadrants2d[i] = new List<Vector2>(1) { cen2d };
			quadrants3d[i] = new List<Vector3>(1) { cen3d };
			sharedIndex[i] = new List<int>(1) { -2 };		// any negative value less than -1 will be treated as a new group
		}

		// add the divisors
		for(int i = 0; i < edgeCenters2d.Length; i++)
		{
			quadrants2d[i].Add(edgeCenters2d[i]);
			quadrants3d[i].Add(edgeCenters3d[i]);
			sharedIndex[i].Add(-1);	// -(i+2) to group new vertices in AppendFace

			// and add closest in the counterclockwise direction
			Vector2 dir = (edgeCenters2d[i]-cen2d).normalized;
			float largestClockwiseDistance = 0f;
			int quad = -1;
			for(int j = 0; j < dividers.Length; j++)
			{
				if(j == i) continue;	// this is a dividing vertex - ignore

				float dist = Vector2.Angle(dividers[j], dir);
				if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f )
					dist = 360f - dist;

				if(dist > largestClockwiseDistance)
				{
					largestClockwiseDistance = dist;
					quad = j;
				}
			}

			quadrants2d[quad].Add(edgeCenters2d[i]);
			quadrants3d[quad].Add(edgeCenters3d[i]);
			sharedIndex[quad].Add(-1);
		}

		// distribute the existing vertices
		for(int i = 0; i < face.distinctIndices.Length; i++)
		{
			Vector2 dir = (verts2d[i]-cen2d).normalized;	// plane corresponds to distinctIndices
			float largestClockwiseDistance = 0f;
			int quad = -1;
			for(int j = 0; j < dividers.Length; j++)
			{
				float dist = Vector2.Angle(dividers[j], dir);
				if( Vector2.Dot(pb_Math.Perpendicular(dividers[j]), dir) < 0f )
					dist = 360f - dist;

				if(dist > largestClockwiseDistance)
				{
					largestClockwiseDistance = dist;
					quad = j;
				}
			}

			quadrants2d[quad].Add(verts2d[i]);
			quadrants3d[quad].Add(verts3d[i]);
			sharedIndex[quad].Add(pb.sharedIndices.IndexOf(face.distinctIndices[i]));
		}

		int len = quadrants2d.Length;

		// Triangulate
		int[][] tris = new int[len][];
		for(int i = 0; i < len; i++)
		{
			if(quadrants2d[i].Count < 3)
			{
				Debug.LogError("Insufficient points to triangulate - bailing on subdivide operation.  This is probably due to a concave face, or maybe the compiler just doesn't like you today.  50/50 odds really.");
				return false;
			}
		
			tris[i] = Delauney.Triangulate(quadrants2d[i]).ToIntArray();
			
			Vector3[] nrm_check = new Vector3[3]
			{
				quadrants3d[i][tris[i][0]],
				quadrants3d[i][tris[i][1]],
				quadrants3d[i][tris[i][2]]
			};

			if( Vector3.Dot(nrm, pb_Math.Normal(nrm_check)) < 0 )
				System.Array.Reverse(tris[i]);
		}

		splitFaces 		= new pb_Face[len];
		splitVertices 	= new Vector3[len][];
		splitSharedIndices 	= new int[len][];

		for(int i = 0; i < len; i++)
		{
			// triangles, material, pb_UV, smoothing group, shared index
			splitFaces[i] = new pb_Face(tris[i], face.material, new pb_UV(face.uv), face.smoothingGroup, face.textureGroup, face.elementGroup, face.color);
			splitVertices[i] = quadrants3d[i].ToArray();
			splitSharedIndices[i] = sharedIndex[i].ToArray();
		}

		return true;
	}