/// <summary> /// Split brush polygons by a supplied plane, outputing the polygons on either side and capping the two halves on the plane /// </summary> /// <returns><c>true</c>, if splitting actually took place, <c>false</c> otherwise.</returns> /// <param name="polygons">Source polygons.</param> /// <param name="splitPlane">Split plane.</param> /// <param name="excludeNewPolygons">If set to <c>true</c> the cap polygons will be marked as excludeFromBuild.</param> /// <param name="polygonsFront">Generated polygons in front of the plane.</param> /// <param name="polygonsBack">Generated polygons behind the plane.</param> public static bool SplitPolygonsByPlane(List <Polygon> polygons, // Source polygons that will be split Plane splitPlane, bool excludeNewPolygons, // Whether new polygons should be marked as excludeFromBuild out List <Polygon> polygonsFront, out List <Polygon> polygonsBack) { polygonsFront = new List <Polygon>(); polygonsBack = new List <Polygon>(); // First of all make sure splitting actually needs to occur (we'll get bad issues if // we try splitting geometry when we don't need to) if (!GeometryHelper.PolygonsIntersectPlane(polygons, splitPlane)) { return(false); } // For copying the material and color from the nearest polygons to the clipping to the generated polygons Material newMaterial = polygons[0].Material; Color newColor = polygons[0].Vertices[0].Color; // These are the vertices that will be used in the new caps List <Vertex> newVertices = new List <Vertex>(); for (int polygonIndex = 0; polygonIndex < polygons.Count; polygonIndex++) { Polygon.PolygonPlaneRelation planeRelation = Polygon.TestPolygonAgainstPlane(polygons[polygonIndex], splitPlane); // Polygon has been found to span both sides of the plane, attempt to split into two pieces if (planeRelation == Polygon.PolygonPlaneRelation.Spanning) { Polygon frontPolygon; Polygon backPolygon; Vertex newVertex1; Vertex newVertex2; // Attempt to split the polygon if (Polygon.SplitPolygon(polygons[polygonIndex], out frontPolygon, out backPolygon, out newVertex1, out newVertex2, splitPlane)) { // If the split algorithm was successful (produced two valid polygons) then add each polygon to // their respective points and track the intersection points polygonsFront.Add(frontPolygon); polygonsBack.Add(backPolygon); newVertices.Add(newVertex1); newVertices.Add(newVertex2); newMaterial = polygons[polygonIndex].Material; newColor = polygons[0].Vertices[0].Color; } else { // Two valid polygons weren't generated, so use the valid one if (frontPolygon != null) { planeRelation = Polygon.PolygonPlaneRelation.InFront; } else if (backPolygon != null) { planeRelation = Polygon.PolygonPlaneRelation.Behind; } else { Debug.LogError("Polygon splitting has resulted in two zero area polygons. This is unhandled."); // Polygon.PolygonPlaneRelation secondplaneRelation = Polygon.TestPolygonAgainstPlane(polygons[polygonIndex], splitPlane); } } } // If the polygon is on one side of the plane or the other if (planeRelation != Polygon.PolygonPlaneRelation.Spanning) { // Make sure any points that are coplanar on non-straddling polygons are still used in polygon // construction for (int vertexIndex = 0; vertexIndex < polygons[polygonIndex].Vertices.Length; vertexIndex++) { if (Polygon.ComparePointToPlane(polygons[polygonIndex].Vertices[vertexIndex].Position, splitPlane) == Polygon.PointPlaneRelation.On) { newVertices.Add(polygons[polygonIndex].Vertices[vertexIndex]); } } if (planeRelation == Polygon.PolygonPlaneRelation.Behind) { polygonsBack.Add(polygons[polygonIndex]); } else { polygonsFront.Add(polygons[polygonIndex]); } } } // If any splits occured or coplanar vertices are found. (For example if you're splitting a sphere at the // equator then no polygons will be split but there will be a bunch of coplanar vertices!) if (newVertices.Count > 0) { // HACK: This code is awful, because we end up with lots of duplicate vertices List <Vector3> positions = newVertices.Select(item => item.Position).ToList(); Polygon newPolygon = PolygonFactory.ConstructPolygon(positions, true); // Assuming it was possible to create a polygon if (newPolygon != null) { if (!MathHelper.PlaneEqualsLooser(newPolygon.Plane, splitPlane)) { // Polygons are sometimes constructed facing the wrong way, possibly due to a winding order // mismatch. If the two normals are opposite, flip the new polygon if (Vector3.Dot(newPolygon.Plane.normal, splitPlane.normal) < -0.9f) { newPolygon.Flip(); } } newPolygon.ExcludeFromFinal = excludeNewPolygons; // Copy the material and color from the nearest polygons to the new polygon newPolygon.Material = newMaterial; newPolygon.SetColor(newColor); polygonsFront.Add(newPolygon); newPolygon = newPolygon.DeepCopy(); newPolygon.Flip(); newPolygon.ExcludeFromFinal = excludeNewPolygons; // Copy the material and color from the nearest polygons to the new polygon newPolygon.Material = newMaterial; newPolygon.SetColor(newColor); if (newPolygon.Plane.normal == Vector3.zero) { Debug.LogError("Invalid Normal! Shouldn't be zero. This is unexpected since extraneous positions should have been removed!"); // Polygon fooNewPolygon = PolygonFactory.ConstructPolygon(positions, true); } polygonsBack.Add(newPolygon); } return(true); } else { // It wasn't possible to create the polygon, for example the constructed polygon was too small // This could happen if you attempt to clip the tip off a long but thin brush, the plane-polyhedron test // would say they intersect but in reality the resulting polygon would be near zero area return(false); } }