internal static bool PolyhedronIntersectsLine(Polygon[] polygons, Vector3 lineStart, Vector3 lineEnd) { // Use two time values to represent the reduced line segment, at the start 0 represents lineStart and 1 // represents lineEnd float timeStart = 0; float timeEnd = 1f; Vector3 lineDelta = lineEnd - lineStart; for (int i = 0; i < polygons.Length; i++) { Plane plane = polygons[i].Plane; // Find the relation between the line and this polygon // Essentially we test the direction the line is moving in against the polygon's normal to see whether // the line is enterring or exiting through the polygon. float dot = Vector3.Dot(plane.normal, lineDelta); // Find the intersection time between the plane and the position // Note this essentially substitutes the line segment equation into the plane equation then makes t the // subject // Not sure why we have to flip time intersection's sign, probably to do with the pecularity of the way // Unity's Plane works float timeIntersection = -(Vector3.Dot(plane.normal, lineStart) + plane.distance) / dot; if (dot < 0) // Directions are opposing, must be enterring through the polygon { if (timeIntersection > timeStart) { timeStart = timeIntersection; } } else if (dot > 0) // Directions are similar, must be exiting through the polygon { if (timeIntersection < timeEnd) { timeEnd = timeIntersection; } } else // (dot == 0) directions are perpendicular, line is tangential to the polygon { // Rather than just ignoring the perpendicular case, we instead test either of the points on the line // against the plane, if it's outside the plane then the line is outside the polyhedron! if (Polygon.ComparePointToPlane(lineStart, polygons[i].Plane) == Polygon.PointPlaneRelation.Behind) { return(false); } } // No intersection has occurred if (timeEnd - timeStart <= 0) { return(false); } } return(true); }
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 (!PolygonsIntersectPlane(polygons, splitPlane)) { return(false); } Material newMaterial = polygons[0].Material; // 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; } 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; newPolygon.Material = newMaterial; polygonsFront.Add(newPolygon); newPolygon = newPolygon.DeepCopy(); newPolygon.Flip(); newPolygon.ExcludeFromFinal = excludeNewPolygons; newPolygon.Material = newMaterial; 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); } }