Example #1
0
        /**
         * Generate a mesh from the provided hull made of triangles
         */
        private static Mesh CreateFrom(List <Triangle> hull)
        {
            int count = hull.Count;

            if (count <= 0)
            {
                return(null);
            }

            Mesh newMesh = new Mesh();

            Vector3[] newVertices = new Vector3[count * 3];
            Vector2[] newUvs      = new Vector2[count * 3];
            int[]     newIndices  = new int[count * 3];

            int addedCount = 0;

            // fill our mesh arrays
            for (int i = 0; i < count; i++)
            {
                Triangle newTri = hull[i];

                int i0 = addedCount + 0;
                int i1 = addedCount + 1;
                int i2 = addedCount + 2;

                newVertices[i0] = newTri.positionA;
                newVertices[i1] = newTri.positionB;
                newVertices[i2] = newTri.positionC;

                newUvs[i0] = newTri.uvA;
                newUvs[i1] = newTri.uvB;
                newUvs[i2] = newTri.uvC;

                // note -> this case could be optimized by having the order
                // returned properly from the intersector
                // -> note -> this is no longer required since triangles are added
                // as clockwise from the intersector

                /*if (newTri.IsCW()) {
                 *      newIndices[i0] = i0;
                 *      newIndices[i1] = i1;
                 *      newIndices[i2] = i2;
                 * }
                 * else {
                 *      newIndices[i0] = i0;
                 *      newIndices[i1] = i2;
                 *      newIndices[i2] = i1;
                 * }*/
                newIndices[i0] = i0;
                newIndices[i1] = i1;
                newIndices[i2] = i2;

                addedCount += 3;
            }

            // fill the mesh structure
            newMesh.vertices  = newVertices;
            newMesh.uv        = newUvs;
            newMesh.triangles = newIndices;

            // consider computing this array externally instead
            newMesh.RecalculateNormals();

            return(newMesh);
        }
Example #2
0
        /**
         * Slice the gameobject mesh (if any) using the Plane, which will generate
         * a maximum of 2 other Meshes.
         * This function will recalculate new UV coordinates to ensure textures are applied
         * properly.
         * Returns null if no intersection has been found or the GameObject does not contain
         * a valid mesh to cut.
         */
        public static SlicedHull Slice(Mesh sharedMesh, Plane pl, bool genCrossSection = true)
        {
            if (sharedMesh == null)
            {
                return(null);
            }

            Vector3[] ve      = sharedMesh.vertices;
            Vector2[] uv      = sharedMesh.uv;
            int[]     indices = sharedMesh.triangles;

            int indicesCount = indices.Length;

            // we reuse this object for all intersection tests
            IntersectionResult result = new IntersectionResult();

            // all our buffers, as Triangles
            List <Triangle> upperHull = new List <Triangle>();
            List <Triangle> lowerHull = new List <Triangle>();
            List <Vector3>  crossHull = new List <Vector3>();

            // loop through all the mesh vertices, generating upper and lower hulls
            // and all intersection points
            for (int index = 0; index < indicesCount; index += 3)
            {
                int i0 = indices[index + 0];
                int i1 = indices[index + 1];
                int i2 = indices[index + 2];

                Triangle newTri = new Triangle(ve[i0], ve[i1], ve[i2], uv[i0], uv[i1], uv[i2]);

                // slice this particular triangle with the provided
                // plane
                if (newTri.Split(pl, result))
                {
                    int upperHullCount = result.upperHullCount;
                    int lowerHullCount = result.lowerHullCount;
                    int interHullCount = result.intersectionPointCount;

                    for (int i = 0; i < upperHullCount; i++)
                    {
                        upperHull.Add(result.upperHull[i]);
                    }

                    for (int i = 0; i < lowerHullCount; i++)
                    {
                        lowerHull.Add(result.lowerHull[i]);
                    }

                    for (int i = 0; i < interHullCount; i++)
                    {
                        crossHull.Add(result.intersectionPoints[i]);
                    }
                }
                else
                {
                    SideOfPlane side = pl.SideOf(ve[i0]);

                    if (side == SideOfPlane.UP || side == SideOfPlane.ON)
                    {
                        upperHull.Add(newTri);
                    }
                    else
                    {
                        lowerHull.Add(newTri);
                    }
                }
            }

            // start creating our hulls
            Mesh finalUpperHull = CreateFrom(upperHull);
            Mesh finalLowerHull = CreateFrom(lowerHull);

            // we need to generate the cross section if set
            // NOTE -> This uses a MonotoneChain algorithm which will only work
            // on cross sections which are Convex
            if (genCrossSection)
            {
                Mesh[] crossSections = CreateFrom(crossHull, pl.normal);

                if (crossSections != null)
                {
                    return(new SlicedHull(finalUpperHull, finalLowerHull, crossSections[0], crossSections[1]));
                }
            }

            return(new SlicedHull(finalUpperHull, finalLowerHull));
        }
Example #3
0
        /**
         * O(n log n) Convex Hull Algorithm.
         * Accepts a list of vertices as Vector3 and triangulates them according to a projection
         * plane defined as planeNormal. Algorithm will output vertices, indices and UV coordinates
         * as arrays
         */
        public static bool MonotoneChain(List <Vector3> vertices, Vector3 normal, out List <Triangle> tri, TextureRegion texRegion)
        {
            int count = vertices.Count;

            // we cannot triangulate less than 3 points. Use minimum of 3 points
            if (count < 3)
            {
                tri = null;
                return(false);
            }

            // first, we map from 3D points into a 2D plane represented by the provided normal
            Vector3 u = Vector3.Normalize(Vector3.Cross(normal, Vector3.up));

            if (Vector3.zero == u)
            {
                u = Vector3.Normalize(Vector3.Cross(normal, Vector3.forward));
            }
            Vector3 v = Vector3.Cross(u, normal);

            // generate an array of mapped values
            Mapped2D[] mapped = new Mapped2D[count];

            // these values will be used to generate new UV coordinates later on
            float maxDivX = float.MinValue;
            float maxDivY = float.MinValue;
            float minDivX = float.MaxValue;
            float minDivY = float.MaxValue;

            // map the 3D vertices into the 2D mapped values
            for (int i = 0; i < count; i++)
            {
                Vector3 vertToAdd = vertices[i];

                Mapped2D newMappedValue = new Mapped2D(vertToAdd, u, v);
                Vector2  mapVal         = newMappedValue.mappedValue;

                // grab our maximal values so we can map UV's in a proper range
                maxDivX = Mathf.Max(maxDivX, mapVal.x);
                maxDivY = Mathf.Max(maxDivY, mapVal.y);
                minDivX = Mathf.Min(minDivX, mapVal.x);
                minDivY = Mathf.Min(minDivY, mapVal.y);

                mapped[i] = newMappedValue;
            }

            // sort our newly generated array values
            Array.Sort <Mapped2D>(mapped, (a, b) => {
                Vector2 x = a.mappedValue;
                Vector2 p = b.mappedValue;

                return((x.x < p.x || (x.x == p.x && x.y < p.y)) ? -1 : 1);
            });

            // our final hull mappings will end up in herew
            Mapped2D[] hulls = new Mapped2D[count + 1];

            int k = 0;

            // build the lower hull of the chain
            for (int i = 0; i < count; i++)
            {
                while (k >= 2)
                {
                    Vector2 mA = hulls[k - 2].mappedValue;
                    Vector2 mB = hulls[k - 1].mappedValue;
                    Vector2 mC = mapped[i].mappedValue;

                    if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f)
                    {
                        break;
                    }

                    k--;
                }

                hulls[k++] = mapped[i];
            }

            // build the upper hull of the chain
            for (int i = count - 2, t = k + 1; i >= 0; i--)
            {
                while (k >= t)
                {
                    Vector2 mA = hulls[k - 2].mappedValue;
                    Vector2 mB = hulls[k - 1].mappedValue;
                    Vector2 mC = mapped[i].mappedValue;

                    if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f)
                    {
                        break;
                    }

                    k--;
                }

                hulls[k++] = mapped[i];
            }

            // finally we can build our mesh, generate all the variables
            // and fill them up
            int vertCount = k - 1;
            int triCount  = (vertCount - 2) * 3;

            // this should not happen, but here just in case
            if (vertCount < 3)
            {
                tri = null;
                return(false);
            }

            // ensure List does not dynamically grow, performing copy ops each time!
            tri = new List <Triangle>(triCount / 3);

            float width  = maxDivX - minDivX;
            float height = maxDivY - minDivY;

            int indexCount = 1;

            // generate both the vertices and uv's in this loop
            for (int i = 0; i < triCount; i += 3)
            {
                // the Vertices in our triangle
                Mapped2D posA = hulls[0];
                Mapped2D posB = hulls[indexCount];
                Mapped2D posC = hulls[indexCount + 1];

                // generate UV Maps
                Vector2 uvA = posA.mappedValue;
                Vector2 uvB = posB.mappedValue;
                Vector2 uvC = posC.mappedValue;

                uvA.x = (uvA.x - minDivX) / width;
                uvA.y = (uvA.y - minDivY) / height;

                uvB.x = (uvB.x - minDivX) / width;
                uvB.y = (uvB.y - minDivY) / height;

                uvC.x = (uvC.x - minDivX) / width;
                uvC.y = (uvC.y - minDivY) / height;

                Triangle newTriangle = new Triangle(posA.originalValue, posB.originalValue, posC.originalValue);

                // ensure our UV coordinates are mapped into the requested TextureRegion
                newTriangle.SetUV(texRegion.Map(uvA), texRegion.Map(uvB), texRegion.Map(uvC));

                // the normals is the same for all vertices since the final mesh is completly flat
                newTriangle.SetNormal(normal, normal, normal);
                newTriangle.ComputeTangents();

                tri.Add(newTriangle);

                indexCount++;
            }

            return(true);
        }