public List <DTPolygon> Subtract(DTPolygon subject, DTPolygon clippingPolygon)
        {
            if (!DTUtility.BoundsCheck(subject.Contour, clippingPolygon.Contour))
            {
                // There is no overlap at all, so output a copy of the subject polygon
                return(new List <DTPolygon>()
                {
                    new DTPolygon(new List <Vector2>(subject.Contour))
                });
            }

            clipper.Clear();

            // Add subject polygon paths
            clipper.AddPath(subject.Contour.ToIntPointList(), PolyType.ptSubject, true);
            foreach (var hole in subject.Holes)
            {
                clipper.AddPath(hole.ToIntPointList(), PolyType.ptSubject, true);
            }

            // Add clipping polygon paths
            clipper.AddPath(clippingPolygon.Contour.ToIntPointList(), PolyType.ptClip, true);
            foreach (var hole in clippingPolygon.Holes)
            {
                clipper.AddPath(hole.ToIntPointList(), PolyType.ptClip, true);
            }

            // Execute subtraction and store result in a PolyTree so that we can easily identify holes
            PolyTree clipperOutput = new PolyTree();

            clipper.Execute(ClipType.ctDifference, clipperOutput, PolyFillType.pftEvenOdd, PolyFillType.pftNonZero);

            // Convert Polytree into list of DTPolygons
            return(clipperOutput.ToDTPolygons());
        }
        // Returns true if the subject polygon was modified at all
        private bool SubtractInternal(List <Vector2> subject, List <Vector2> clippingPolygon, out List <DTPolygon> outputPolygons)
        {
            if (!DTUtility.BoundsCheck(subject, clippingPolygon))
            {
                // There is no overlap at all, so output a copy of the subject polygon
                outputPolygons = new List <DTPolygon>()
                {
                    new DTPolygon(new List <Vector2>(subject))
                };
                return(false);
            }

            polyP = subject;
            polyQ = clippingPolygon;

            pIndex = 0;
            qIndex = 0;

            // The first entry intersection point of the first output polgon. If we return to this point, break the loop
            bool firstIntersectionFound  = false;
            int? firstIntersectionPIndex = null;
            int? firstIntersectionQIndex = null;

            // List of disjoint output polygons after subtraction
            outputPolygons = new List <DTPolygon>();

            // Working output polygon vertices
            polygonBegin = null;
            List <Vector2> pVertices = new List <Vector2>();
            List <Vector2> qVertices = new List <Vector2>();

            for (int i = 0; i <= 2 * (polyP.Count + polyQ.Count); ++i)
            {
                if (polygonBegin.HasValue)
                {
                    Vector2?exitIntersection = ExitIntersection();
                    if (exitIntersection.HasValue)
                    {
                        // Exit intersection point for output polygon: construct output polygon
                        DTPolygon poly = new DTPolygon();
                        poly.Contour.Add(polygonBegin.Value);
                        poly.Contour.AddRange(pVertices);
                        poly.Contour.Add(exitIntersection.Value);
                        poly.Contour.AddRange(qVertices.AsEnumerable().Reverse());

                        // Simplify polygon
                        poly = poly.Simplify();
                        if (poly != null)
                        {
                            outputPolygons.Add(poly);
                        }

                        // Clear working polygon vertices
                        polygonBegin = null;
                        pVertices.Clear();
                        qVertices.Clear();
                    }
                }
                else
                {
                    Vector2?entranceIntersection = EntranceIntersection();
                    if (entranceIntersection.HasValue)
                    {
                        // Loop exit condition: revisiting first intersection
                        if (firstIntersectionFound &&
                            ModP(pIndex) == ModP(firstIntersectionPIndex.Value) &&
                            ModQ(qIndex) == ModQ(firstIntersectionQIndex.Value))
                        {
                            break;
                        }

                        // Entry intersection point for output polygon
                        polygonBegin = entranceIntersection.Value;
                        pVertices.Clear();
                        qVertices.Clear();

                        // Keep track of this point if it is the entry intersection for the 1st output polygon
                        if (!firstIntersectionFound)
                        {
                            firstIntersectionFound  = true;
                            firstIntersectionPIndex = pIndex;
                            firstIntersectionQIndex = qIndex;
                        }
                    }
                }

                void advanceP()
                {
                    if (polygonBegin.HasValue)
                    {
                        pVertices.Add(P);
                    }
                    ++pIndex;
                }

                void advanceQ()
                {
                    if (polygonBegin.HasValue)
                    {
                        qVertices.Add(Q);
                    }
                    ++qIndex;
                }

                float pSide = (P - QPrev).Cross(QEdge);
                float qSide = (Q - PPrev).Cross(PEdge);
                float cross = QEdge.Cross(PEdge);

                if (cross <= 0)
                {
                    // QEdge heading inward

                    if (qSide < 0)
                    {
                        // Q inside P's half-plane, heading away from PEdge
                        advanceP();
                    }
                    else
                    {
                        // Q outside P's half-plane or on P's line, heading toward PEdge
                        advanceQ();
                    }
                }
                else
                {
                    // QEdge heading outward

                    if (pSide < 0)
                    {
                        // P inside Q's half-plane, heading away from QEdge
                        advanceQ();
                    }
                    else
                    {
                        // P outside Q's half-plane or on Q's line, heading toward QEdge
                        advanceP();
                    }
                }
            }

            // There were no intersections, so either one poly is entirely contained within the other, or there is no overlap at all
            if (outputPolygons.Count == 0)
            {
                if (polyP.Inside(polyQ))
                {
                    // P is entirely within Q, so do nothing. The entire polygon has been subtracted
                    return(true);
                }
                else if (polyQ.Inside(polyP))
                {
                    // Q is entirely within P, so output a copy of P, with Q (reversed) set as a hole
                    outputPolygons.Add(new DTPolygon(
                                           new List <Vector2>(polyP),
                                           new List <List <Vector2> >()
                    {
                        polyQ.AsEnumerable().Reverse().ToList()
                    }));
                    return(true);
                }
                else
                {
                    if (polyP.Simplify() == polyQ.Simplify())
                    {
                        // The polygons are equal, so do nothing. The entire polygon has been subtracted
                        return(true);
                    }
                    else
                    {
                        // There is no overlap at all, so output a copy of P
                        outputPolygons.Add(new DTPolygon(new List <Vector2>(polyP)));
                        return(false);
                    }
                }
            }

            return(true);
        }
        public void ExecuteExplosions(IEnumerable <Explosion> explosions, IEnumerable <DestructibleObject> dtObjects, IPolygonSubtractor subtractor)
        {
            ORourkeSubtractor oRourkeSub = (ORourkeSubtractor)subtractor;

            if (oRourkeSub == null)
            {
                throw new NotSupportedException("This explosion executor only supports ORourkeSubtractor");
            }

            TriangleNetTriangulator.Instance.callCount = 0;

            // Store destructible objects in a new list, since we may add or remove some during processing
            List <DestructibleObject> dtObjectList = dtObjects.ToList();
            // Add new destructible objects to this list instead of objectList until finished processing the current explosion
            List <DestructibleObject> pendingAdditions = new List <DestructibleObject>();

            // Process all objects for all explosions
            foreach (var exp in explosions)
            {
                for (int i = 0; i < dtObjectList.Count; i++)
                {
                    // Remove this object from the list if it has been destroyed
                    if (dtObjectList[i] == null)
                    {
                        dtObjectList.RemoveAt(i--);
                        continue;
                    }
                    // Cast this DO to an advanced DO
                    var dtObj = (DO_Advanced_Triangle_Clip_Collide)dtObjectList[i];
                    if (dtObj == null)
                    {
                        throw new NotSupportedException("This explosion executor only supports DO_Advanced_Triangle_Clip_Collide");
                    }

                    // Do basic AABB-circle check to see whether we can skip processing this destructible object with this explosion
                    int bc = DTUtility.BoundsCheck(dtObj, exp);
                    if (bc == -1)
                    {
                        // Object is not affected by explosion
                        continue;
                    }
                    else if (bc == 1)
                    {
                        // Object is completely removed by explosion
                        dtObjectList.RemoveAt(i--);
                        UnityEngine.Object.Destroy(dtObj.gameObject);
                        continue;
                    }

                    // Leave the polygroup in local coordinates and transform the explosion instead to the DO's space.
                    // Note that this is stored in the subject DO and will be referenced by all PolygroupModifiers,
                    // so changes to the original DO will affect all PolygroupModifiers. This shouldn't cause any
                    // problems since we only need the indices anyway.
                    PolygroupModifier inputPolygroup       = dtObj.GetPolygroup();
                    List <Vector2>    transformedExplosion = dtObj.InverseTransformPoints(exp.DTPolygon.Contour);

                    // Subtract explosion polygon from destructible object polygon group
                    DTProfilerMarkers.SubtractPolygroup.Begin();
                    List <PolygroupModifier> result = oRourkeSub.AdvancedSubtractPolygroup(inputPolygroup, transformedExplosion);
                    DTProfilerMarkers.SubtractPolygroup.End();

                    int count = result.Count();
                    if (count == 0)
                    {
                        // If no output polygons, remove the current destrucible object
                        dtObjectList.RemoveAt(i--);
                        UnityEngine.Object.Destroy(dtObj.gameObject);
                        continue;
                    }
                    else
                    {
                        // Otherwise apply the output polygons (fragments) to GameObjects (new or reused)
                        for (int j = 0; j < result.Count; j++)
                        {
                            if (j < result.Count - 1)
                            {
                                // Duplicate the GameObject that was clipped by the explosion, so that we maintain properties such as velocity and also maintain the same collider + mesh
                                GameObject go     = UnityEngine.Object.Instantiate(dtObj.gameObject, dtObj.transform.parent);
                                var        newObj = go.GetComponent <DO_Advanced_Triangle_Clip_Collide>();
                                newObj.SetPolygroup(new PolygroupModifier(new DTConvexPolygroup(inputPolygroup.originalPolygroup), inputPolygroup.keptIndices, null));

                                // Apply the new clipped polygon list
                                newObj.ApplyPolygroupModifier(result[j]);

                                // Add it to the objectList, but not until after finished processing this explosion
                                pendingAdditions.Add(newObj);
                                continue;
                            }
                            else
                            {
                                // Reuse the existing GameObject by applying the new clipped polygon to it
                                dtObj.ApplyPolygroupModifier(result[j]);
                                continue;
                            }
                        }
                    }
                }
                // Add pendingAdditions elements to objectList so that they are included when processing the next explosion in explosions
                dtObjectList.AddRange(pendingAdditions);
                pendingAdditions.Clear();
            }

            Debug.Log("# Objects:" + dtObjectList.Count);
            Debug.Log("# Polygons:" + dtObjectList.Sum(obj => obj.GetTransformedPolygonList().Count));
            Debug.Log("# Triangulation Calls:" + TriangleNetTriangulator.Instance.callCount);
        }
Beispiel #4
0
        public void ExecuteExplosions(IEnumerable <Explosion> explosions, IEnumerable <DestructibleObject> dtObjects, IPolygonSubtractor subtractor)
        {
            // Store destructible objects in a new list, since we may add or remove some during processing
            List <DestructibleObject> dtObjectList = dtObjects.ToList();
            // Add new destructible objects to this list instead of objectList until finished processing the current explosion
            List <DestructibleObject> pendingAdditions = new List <DestructibleObject>();

            // Process all objects for all explosions
            foreach (var exp in explosions)
            {
                List <List <DTPolygon> > relevantObjectPolygons = new List <List <DTPolygon> >();
                List <int> relevantObjectIndices = new List <int>();
                for (int i = 0; i < dtObjectList.Count; ++i)
                {
                    var dtObj = dtObjectList[i];

                    // Do basic AABB-circle check to see whether we can skip processing this destructible object with this explosion
                    int bc = DTUtility.BoundsCheck(dtObj, exp);
                    if (bc == -1)
                    {
                        // Object is not affected by explosion
                        continue;
                    }
                    else if (bc == 1)
                    {
                        // Object is completely removed by explosion
                        dtObjectList[i] = null;
                        UnityEngine.Object.Destroy(dtObj.gameObject);
                        continue;
                    }
                    else
                    {
                        // Add object to the input list for the subtractor
                        relevantObjectPolygons.Add(dtObj.GetTransformedPolygonList());
                        relevantObjectIndices.Add(i);
                        continue;
                    }
                }

                var result = subtractor.SubtractBulk(relevantObjectPolygons, new DTPolygon[] { exp.DTPolygon });

                // Iterate results corresponding to each input polygon group
                for (int i = 0; i < result.Count; i++)
                {
                    // Add new destructible objects for any output polygons that could not be matched with an input polygon
                    if (i >= relevantObjectPolygons.Count)
                    {
                        GameObject         go        = new GameObject();
                        DestructibleObject newObj    = go.AddComponent <DestructibleObject>();
                        List <DTPolygon>   polygroup = result[i][0];
                        newObj.ApplyTransformedPolygonList(polygroup);
                        pendingAdditions.Add(newObj);

                        continue;
                    }

                    // We know that these output polygons correspond to one or more pieces of this existing destructible object
                    DestructibleObject dtObj = dtObjectList[relevantObjectIndices[i]];

                    if (result[i].Count == 0)
                    {
                        // If no output polygons, remove the current destrucible object
                        dtObjectList[relevantObjectIndices[i]] = null;
                        UnityEngine.Object.Destroy(dtObj.gameObject);
                        continue;
                    }
                    else
                    {
                        // Otherwise apply the output polygons (fragments) to GameObjects (new or reused)
                        foreach (List <DTPolygon> polygroup in result[i])
                        {
                            if (polygroup != result[i].Last())
                            {
                                // Duplicate the GameObject that was clipped by the explosion, so that we maintain properties such as velocity
                                GameObject         go     = UnityEngine.Object.Instantiate(dtObj.gameObject, dtObj.transform.parent);
                                DestructibleObject newObj = go.GetComponent <DestructibleObject>();

                                // Apply the new clipped polygon
                                newObj.ApplyTransformedPolygonList(polygroup);

                                // Add it to the objectList, but not until after finished processing this explosion
                                pendingAdditions.Add(newObj);
                                continue;
                            }
                            else
                            {
                                // Reuse the existing GameObject by applying the new clipped polygon to it
                                dtObj.ApplyTransformedPolygonList(polygroup);
                                continue;
                            }
                        }
                    }
                }

                // Delete any entries that were set to null
                for (int i = 0; i < dtObjectList.Count; ++i)
                {
                    if (dtObjectList[i] == null)
                    {
                        dtObjectList.RemoveAt(i--);
                    }
                }

                // Add pendingAdditions elements to objectList so that they are included when processing the next explosion in explosions
                dtObjectList.AddRange(pendingAdditions);
                pendingAdditions.Clear();
            }
        }
        public void ExecuteExplosions(IEnumerable <Explosion> explosions, IEnumerable <DestructibleObject> dtObjects, IPolygonSubtractor subtractor)
        {
            TriangleNetTriangulator.Instance.callCount = 0;

            // Store destructible objects in a new list, since we may add or remove some during processing
            List <DestructibleObject> dtObjectList = dtObjects.ToList();
            // Add new destructible objects to this list instead of objectList until finished processing the current explosion
            List <DestructibleObject> pendingAdditions = new List <DestructibleObject>();

            // Process all objects for all explosions
            foreach (var exp in explosions)
            {
                for (int i = 0; i < dtObjectList.Count; i++)
                {
                    DestructibleObject dtObj = dtObjectList[i];

                    // Do basic AABB-circle check to see whether we can skip processing this destructible object with this explosion
                    int bc = DTUtility.BoundsCheck(dtObj, exp);
                    if (bc == -1)
                    {
                        // Object is not affected by explosion
                        continue;
                    }
                    else if (bc == 1)
                    {
                        // Object is completely removed by explosion
                        dtObjectList.RemoveAt(i--);
                        UnityEngine.Object.Destroy(dtObj.gameObject);
                        continue;
                    }

                    List <DTPolygon> inputPolygroup = dtObj.GetTransformedPolygonList();

                    // Subtract explosion polygon from destructible object polygon group
                    DTProfilerMarkers.SubtractPolygroup.Begin();
                    List <List <DTPolygon> > result = subtractor.SubtractPolygroup(inputPolygroup, new List <DTPolygon>()
                    {
                        exp.DTPolygon
                    });
                    DTProfilerMarkers.SubtractPolygroup.End();

                    int count = result.Count();
                    if (count == 0)
                    {
                        // If no output polygons, remove the current destrucible object
                        dtObjectList.RemoveAt(i--);
                        UnityEngine.Object.Destroy(dtObj.gameObject);
                        continue;
                    }
                    else
                    {
                        // Otherwise apply the output polygons (fragments) to GameObjects (new or reused)
                        foreach (List <DTPolygon> polygroup in result)
                        {
                            if (polygroup != result.Last())
                            {
                                // Duplicate the GameObject that was clipped by the explosion, so that we maintain properties such as velocity
                                GameObject         go     = UnityEngine.Object.Instantiate(dtObj.gameObject, dtObj.transform.parent);
                                DestructibleObject newObj = go.GetComponent <DestructibleObject>();

                                // Apply the new clipped polygon list
                                newObj.ApplyTransformedPolygonList(polygroup);

                                // Add it to the objectList, but not until after finished processing this explosion
                                pendingAdditions.Add(newObj);
                                continue;
                            }
                            else
                            {
                                // Reuse the existing GameObject by applying the new clipped polygon to it
                                dtObj.ApplyTransformedPolygonList(polygroup);
                                continue;
                            }
                        }
                    }
                }
                // Add pendingAdditions elements to objectList so that they are included when processing the next explosion in explosions
                dtObjectList.AddRange(pendingAdditions);
                pendingAdditions.Clear();
            }

            Debug.Log("# Objects:" + dtObjectList.Count);
            Debug.Log("# Polygons:" + dtObjectList.Sum(obj => obj.GetTransformedPolygonList().Count));
            Debug.Log("# Triangulation Calls:" + TriangleNetTriangulator.Instance.callCount);
        }