Exemplo n.º 1
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);
        }
Exemplo n.º 2
0
        // 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);
        }
Exemplo n.º 3
0
        /**
         *	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);
        }
Exemplo n.º 4
0
        /**
         *	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);
        }