public static void ExtrudePolygonOld(Polygon sourcePolygon, out Polygon[] outputPolygons, out Quaternion rotation) { float extrusionDistance = 1; Polygon newPolygon = sourcePolygon.DeepCopy(); newPolygon.UniqueIndex = -1; newPolygon.Flip(); Vector3 normal = sourcePolygon.Plane.normal; Polygon oppositePolygon = sourcePolygon.DeepCopy(); oppositePolygon.UniqueIndex = -1; Vertex[] vertices = oppositePolygon.Vertices; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position += normal; } oppositePolygon.SetVertices(vertices); Polygon[] brushSides = new Polygon[sourcePolygon.Vertices.Length]; for (int i = 0; i < newPolygon.Vertices.Length; i++) { Vertex vertex1 = newPolygon.Vertices[i].DeepCopy(); Vertex vertex2 = newPolygon.Vertices[(i + 1) % newPolygon.Vertices.Length].DeepCopy(); Vector2 uvDelta = vertex2.UV - vertex1.UV; float sourceDistance = Vector3.Distance(vertex1.Position, vertex2.Position); Vector2 rotatedUVDelta = uvDelta.Rotate(90) * (extrusionDistance / sourceDistance); Vertex vertex3 = vertex1.DeepCopy(); vertex3.Position += normal * extrusionDistance; vertex3.UV += rotatedUVDelta; Vertex vertex4 = vertex2.DeepCopy(); vertex4.Position += normal * extrusionDistance; vertex4.UV += rotatedUVDelta; Vertex[] newVertices = new Vertex[] { vertex1, vertex2, vertex4, vertex3 }; brushSides[i] = new Polygon(newVertices, sourcePolygon.Material, false, false); brushSides[i].Flip(); brushSides[i].ResetVertexNormals(); } List <Polygon> polygons = new List <Polygon>(); polygons.Add(newPolygon); polygons.Add(oppositePolygon); polygons.AddRange(brushSides); outputPolygons = polygons.ToArray(); rotation = Quaternion.identity; }
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); } }
internal List <Polygon> ProvideSubtractChunks(List <BrushChunk> subtractChunks) { List <Polygon> damagedPolygons = new List <Polygon>(); List <Polygon> addedPolygons = new List <Polygon>(); // If this chunk knows about any split planes // if(splitPlanes.Count > 0) { // For each of the subtract chunks int subtractChunkCount = subtractChunks.Count; for (int chunkIndex = 0; chunkIndex < subtractChunkCount; chunkIndex++) { int polygonCount = subtractChunks[chunkIndex].Polygons.Count; // For each of the polygons within those subtract chunks for (int j = 0; j < polygonCount; j++) { Polygon polygon = subtractChunks[chunkIndex].Polygons[j]; // Disregard any polygons that can't be displayed as final geometry if (polygon.ExcludeFromFinal) { continue; } Plane polygonPlane = polygon.CachedPlaneTest; // Determine if any of the split planes this chunk has been split against match any of those // subtraction polygons bool anyFound = false; int polygonsCount = polygons.Count; Plane splitPlane; for (int i = 0; i < polygonsCount; i++) { if (!polygons[i].ExcludeFromFinal) { continue; } splitPlane = polygons[i].CachedPlaneTest; if (MathHelper.PlaneEqualsLooserWithFlip(polygonPlane, splitPlane)) { anyFound = true; break; } } bool added = false; if (anyFound) { // TODO: Is GetCenterPoint expensive? Vector3 target = polygon.GetCenterPoint(); // If this brush chunk contains the subtraction polygon // TODO: This is a heftly call a lot of the time, so see about optimising if (GeometryHelper.PolyhedronContainsPointEpsilon3(this.polygons, target)) { added = true; // Duplicate the subtraction polygon polygon = polygon.DeepCopy(); // Flip it, so that it can form outer geometry polygon.Flip(); // // for (int i = 0; i < this.polygons.Count; i++) { if (!this.polygons[i].ExcludeFromFinal && MathHelper.PlaneEqualsLooser(this.polygons[i].Plane, polygon.Plane)) { if (!addedPolygons.Contains(this.polygons[i])) { //Debug.LogWarning("Removing duplicate from chunk " + this.uniqueID + " subtraction chunk " + subtractChunks[chunkIndex].UniqueID); polygons[i].ExcludeFromFinal = true; } } } // Add it to this brush chunk polygons.Add(polygon); addedPolygons.Add(polygon); } } if (!added) { damagedPolygons.Add(polygon); } } } } return(damagedPolygons); }
private void CreateBrush(List <Vector3> positions) { Polygon sourcePolygon = PolygonFactory.ConstructPolygon(positions, true); // Early out if it wasn't possible to create the polygon if (sourcePolygon == null) { return; } if (activePolygon != null) { for (int i = 0; i < sourcePolygon.Vertices.Length; i++) { Vector2 newUV = GeometryHelper.GetUVForPosition(activePolygon, sourcePolygon.Vertices[i].Position); sourcePolygon.Vertices[i].UV = newUV; } } Vector3 planeNormal = GetActivePlane().normal; // Debug.Log(Vector3.Dot(sourcePolygon.Plane.normal, planeNormal)); // Flip the polygon if the winding order is wrong if (Vector3.Dot(sourcePolygon.Plane.normal, planeNormal) < 0) { sourcePolygon.Flip(); // Need to flip the UVs across the U (X) direction for (int i = 0; i < sourcePolygon.Vertices.Length; i++) { Vector2 uv = sourcePolygon.Vertices[i].UV; uv.x = 1 - uv.x; sourcePolygon.Vertices[i].UV = uv; } } float extrusionDistance = 1; Vector3 positionOffset = Vector3.zero; if (selectingHeight) { extrusionDistance = prismHeight; } else { if (activePolygon != null && activeBrush != null) { extrusionDistance = activeBrush.CalculateExtentsInAxis(planeNormal); } else { Brush lastSelectedBrush = csgModel.LastSelectedBrush; if (lastSelectedBrush != null) { Bounds lastSelectedBrushBounds = lastSelectedBrush.GetBoundsTransformed(); for (int i = 0; i < 3; i++) { if (!planeNormal[i].EqualsWithEpsilon(0)) { if (lastSelectedBrushBounds.size[i] != 0) { extrusionDistance = lastSelectedBrushBounds.size[i]; if (planeNormal[i] > 0) { positionOffset[i] = lastSelectedBrushBounds.center[i] - lastSelectedBrushBounds.extents[i]; } else { positionOffset[i] = lastSelectedBrushBounds.center[i] + lastSelectedBrushBounds.extents[i]; } } } } } } // Subtractions should go through if (csgMode == CSGMode.Subtract) { sourcePolygon.Flip(); } } Quaternion rotation; Polygon[] polygons; SurfaceUtility.ExtrudePolygon(sourcePolygon, extrusionDistance, out polygons, out rotation); GameObject newObject = csgModel.CreateCustomBrush(polygons); PrimitiveBrush newBrush = newObject.GetComponent <PrimitiveBrush>(); newObject.transform.rotation = rotation; newObject.transform.position += positionOffset; if (activePolygon != null && activePolygon.Material != csgModel.GetDefaultMaterial()) { for (int i = 0; i < polygons.Length; i++) { polygons[i].Material = activePolygon.Material; } } // Finally give the new brush the other set of polygons newBrush.SetPolygons(polygons, true); newBrush.Mode = csgMode; newBrush.ResetPivot(); // Use this brush as the basis for drawing the next brush csgModel.SetLastSelectedBrush(newBrush); Undo.RegisterCreatedObjectUndo(newObject, "Draw Brush"); }
public static void ExtrudePolygon(Polygon sourcePolygon, out Polygon[] outputPolygons, out Quaternion rotation) { float extrusionDistance = 1; Polygon basePolygon = sourcePolygon.DeepCopy(); basePolygon.UniqueIndex = -1; rotation = Quaternion.LookRotation(basePolygon.Plane.normal); Quaternion cancellingRotation = Quaternion.Inverse(rotation); Vertex[] vertices = basePolygon.Vertices; // Vector3 offsetPosition = vertices[0].Position; for (int i = 0; i < vertices.Length; i++) { // vertices[i].Position -= offsetPosition; vertices[i].Position = cancellingRotation * vertices[i].Position; vertices[i].Normal = cancellingRotation * vertices[i].Normal; } // Vector3 newOffsetPosition = vertices[0].Position; // Vector3 delta = newOffsetPosition - offsetPosition; // for (int i = 0; i < vertices.Length; i++) // { // vertices[i].Position += delta; // } basePolygon.SetVertices(vertices); Vector3 normal = basePolygon.Plane.normal; Polygon oppositePolygon = basePolygon.DeepCopy(); oppositePolygon.UniqueIndex = -1; basePolygon.Flip(); vertices = oppositePolygon.Vertices; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position += normal; } oppositePolygon.SetVertices(vertices); Polygon[] brushSides = new Polygon[sourcePolygon.Vertices.Length]; for (int i = 0; i < basePolygon.Vertices.Length; i++) { Vertex vertex1 = basePolygon.Vertices[i].DeepCopy(); Vertex vertex2 = basePolygon.Vertices[(i + 1) % basePolygon.Vertices.Length].DeepCopy(); Vector2 uvDelta = vertex2.UV - vertex1.UV; float sourceDistance = Vector3.Distance(vertex1.Position, vertex2.Position); Vector2 rotatedUVDelta = uvDelta.Rotate(90) * (extrusionDistance / sourceDistance); Vertex vertex3 = vertex1.DeepCopy(); vertex3.Position += normal * extrusionDistance; vertex3.UV += rotatedUVDelta; Vertex vertex4 = vertex2.DeepCopy(); vertex4.Position += normal * extrusionDistance; vertex4.UV += rotatedUVDelta; Vertex[] newVertices = new Vertex[] { vertex1, vertex2, vertex4, vertex3 }; brushSides[i] = new Polygon(newVertices, sourcePolygon.Material, false, false); brushSides[i].Flip(); brushSides[i].ResetVertexNormals(); } List <Polygon> polygons = new List <Polygon>(); polygons.Add(basePolygon); polygons.Add(oppositePolygon); polygons.AddRange(brushSides); outputPolygons = polygons.ToArray(); }
/// <summary> /// Creates a brush by extruding a supplied polygon by a specified extrusion distance. /// </summary> /// <param name="sourcePolygon">Source polygon, typically transformed into world space.</param> /// <param name="extrusionDistance">Extrusion distance, this is the height (or depth) of the created geometry perpendicular to the source polygon.</param> /// <param name="outputPolygons">Output brush polygons.</param> /// <param name="rotation">The rotation to be supplied to the new brush transform.</param> public static void ExtrudePolygon(Polygon sourcePolygon, float extrusionDistance, out Polygon[] outputPolygons, out Quaternion rotation) { bool flipped = false; if (extrusionDistance < 0) { sourcePolygon.Flip(); extrusionDistance = -extrusionDistance; flipped = true; } // Create base polygon Polygon basePolygon = sourcePolygon.DeepCopy(); basePolygon.UniqueIndex = -1; rotation = Quaternion.LookRotation(basePolygon.Plane.normal); Quaternion cancellingRotation = Quaternion.Inverse(rotation); Vertex[] vertices = basePolygon.Vertices; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = cancellingRotation * vertices[i].Position; vertices[i].Normal = cancellingRotation * vertices[i].Normal; } basePolygon.SetVertices(vertices); // Create the opposite polygon by duplicating the base polygon, offsetting and flipping Vector3 normal = basePolygon.Plane.normal; Polygon oppositePolygon = basePolygon.DeepCopy(); oppositePolygon.UniqueIndex = -1; basePolygon.Flip(); vertices = oppositePolygon.Vertices; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position += normal * extrusionDistance; // vertices[i].UV.x *= -1; // Flip UVs } oppositePolygon.SetVertices(vertices); // Now create each of the brush side polygons Polygon[] brushSides = new Polygon[sourcePolygon.Vertices.Length]; for (int i = 0; i < basePolygon.Vertices.Length; i++) { Vertex vertex1 = basePolygon.Vertices[i].DeepCopy(); Vertex vertex2 = basePolygon.Vertices[(i + 1) % basePolygon.Vertices.Length].DeepCopy(); // Create new UVs for the sides, otherwise we'll get distortion float sourceDistance = Vector3.Distance(vertex1.Position, vertex2.Position); float uvDistance = Vector2.Distance(vertex1.UV, vertex2.UV); float uvScale = sourceDistance / uvDistance; vertex1.UV = Vector2.zero; if (flipped) { vertex2.UV = new Vector2(-sourceDistance / uvScale, 0); } else { vertex2.UV = new Vector2(sourceDistance / uvScale, 0); } Vector2 uvDelta = vertex2.UV - vertex1.UV; Vector2 rotatedUVDelta = uvDelta.Rotate(90) * (extrusionDistance / sourceDistance); Vertex vertex3 = vertex1.DeepCopy(); vertex3.Position += normal * extrusionDistance; vertex3.UV += rotatedUVDelta; Vertex vertex4 = vertex2.DeepCopy(); vertex4.Position += normal * extrusionDistance; vertex4.UV += rotatedUVDelta; Vertex[] newVertices = new Vertex[] { vertex1, vertex2, vertex4, vertex3 }; brushSides[i] = new Polygon(newVertices, sourcePolygon.Material, false, false); brushSides[i].Flip(); brushSides[i].ResetVertexNormals(); } List <Polygon> polygons = new List <Polygon>(); polygons.Add(basePolygon); polygons.Add(oppositePolygon); polygons.AddRange(brushSides); outputPolygons = polygons.ToArray(); }