// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "CSGMesh.h" #if COMPILE_WITH_CSG_BUILDER using namespace CSG; void CSG::Mesh::resolvePolygon(Polygon& polygon, PolygonOperation op) { switch (op) { case Keep: break; case Remove: { polygon.Visible = false; break; } case Flip: { polygon.Inverted = true; break; } default: break; } } void CSG::Mesh::edgeSplit(int32 edgeIndex, const Vector3& vertex) { // Splits a half edge /* original: thisEdge *<====================== ---------------------->* twin split: newEdge thisEdge *<=========*<=========== --------->*----------->* thisTwin newTwin */ HalfEdge& thisEdge = _edges[edgeIndex]; auto thisTwinIndex = thisEdge.TwinIndex; auto& thisTwin = _edges[thisTwinIndex]; auto thisEdgeIndex = thisTwin.TwinIndex; HalfEdge newEdge; auto newEdgeIndex = _edges.Count(); HalfEdge newTwin; auto newTwinIndex = newEdgeIndex + 1; auto vertexIndex = _vertices.Count(); newEdge.PolygonIndex = thisEdge.PolygonIndex; newTwin.PolygonIndex = thisTwin.PolygonIndex; newEdge.VertexIndex = thisEdge.VertexIndex; thisEdge.VertexIndex = vertexIndex; newTwin.VertexIndex = thisTwin.VertexIndex; thisTwin.VertexIndex = vertexIndex; newEdge.NextIndex = thisEdge.NextIndex; thisEdge.NextIndex = newEdgeIndex; newTwin.NextIndex = thisTwin.NextIndex; thisTwin.NextIndex = newTwinIndex; newEdge.TwinIndex = thisTwinIndex; thisTwin.TwinIndex = newEdgeIndex; thisEdge.TwinIndex = newTwinIndex; newTwin.TwinIndex = thisEdgeIndex; _edges.Add(newEdge); _edges.Add(newTwin); _vertices.Add(vertex); } PolygonSplitResult CSG::Mesh::polygonSplit(const Surface& cuttingPlane, int32 inputPolygonIndex, Polygon** outputPolygon) { // Splits a polygon into two pieces, or categorizes it as outside, inside or aligned // Note: This method is not optimized! Code is simplified for clarity! // for example: Plane.Distance / Plane.OnSide should be inlined manually and shouldn't use enums, but floating point values directly! Polygon& inputPolygon = _polygons[inputPolygonIndex]; int32 prev = inputPolygon.FirstEdgeIndex; int32 current = _edges[prev].NextIndex; int32 next = _edges[current].NextIndex; int32 last = next; int32 enterEdge = INVALID_INDEX; int32 exitEdge = INVALID_INDEX; auto prevVertex = _vertices[_edges[prev].VertexIndex]; auto prevDistance = cuttingPlane.Distance(prevVertex); // distance to previous vertex auto prevSide = Surface::OnSide(prevDistance); // side of plane of previous vertex auto currentVertex = _vertices[_edges[current].VertexIndex]; auto currentDistance = cuttingPlane.Distance(currentVertex); // distance to current vertex auto currentSide = Surface::OnSide(currentDistance); // side of plane of current vertex do { auto nextVertex = _vertices[_edges[next].VertexIndex]; auto nextDistance = cuttingPlane.Distance(nextVertex); // distance to next vertex auto nextSide = Surface::OnSide(nextDistance); // side of plane of next vertex if (prevSide != currentSide) // check if edge crossed the plane ... { if (currentSide != PlaneIntersectionType::Intersecting) // prev:inside/outside - current:inside/outside - next:?? { if (prevSide != PlaneIntersectionType::Intersecting) // prev:inside/outside - current:outside - next:?? { // Calculate intersection of edge with plane split the edge into two, inserting the new vertex auto newVertex = Surface::Intersection(prevVertex, currentVertex, prevDistance, currentDistance); edgeSplit(current, newVertex); if (prevSide == PlaneIntersectionType::Back) // prev:inside - current:outside - next:?? { // edge01 exits: // // outside // 1 // * // ......./........ intersect // / // 0 // inside exitEdge = current; } else if (prevSide == PlaneIntersectionType::Front) // prev:outside - current:inside - next:?? { // edge01 enters: // // outside // 0 // \ // .......\........ intersect // * // 1 // inside enterEdge = current; } prev = _edges[prev].NextIndex; prevSide = PlaneIntersectionType::Intersecting; if (exitEdge != INVALID_INDEX && enterEdge != INVALID_INDEX) break; current = _edges[prev].NextIndex; currentVertex = _vertices[_edges[current].VertexIndex]; next = _edges[current].NextIndex; nextVertex = _vertices[_edges[next].VertexIndex]; } } else // prev:?? - current:intersects - next:?? { if (prevSide == PlaneIntersectionType::Intersecting || // prev:intersects - current:intersects - next:?? nextSide == PlaneIntersectionType::Intersecting || // prev:?? - current:intersects - next:intersects prevSide == nextSide) // prev:inside/outde - current:intersects - next:inside/outde { if (prevSide == PlaneIntersectionType::Back || // prev:inside - current:intersects - next:intersects/inside nextSide == PlaneIntersectionType::Back) // prev:intersects/inside - current:intersects - next:inside { // outside // 0 1 // --------*....... intersect // \ // 2 // inside // // outside // 1 2 // ........*------- intersect // / // 0 // inside // // outside // 1 //........*....... intersect // / \ // 0 2 // inside // prevSide = PlaneIntersectionType::Back; enterEdge = exitEdge = INVALID_INDEX; break; } else if (prevSide == PlaneIntersectionType::Front || // prev:outside - current:intersects - next:intersects/outside nextSide == PlaneIntersectionType::Front) // prev:intersects/outside - current:intersects - next:outside { // outside // 2 // / //..------*....... intersect // 0 1 // inside // // outside // 0 // \ //........*------- intersect // 1 2 // inside // // outside // 0 2 // \ / //........*....... intersect // 1 // inside // prevSide = PlaneIntersectionType::Front; enterEdge = exitEdge = INVALID_INDEX; break; } } else // prev:inside/outside - current:intersects - next:inside/outside { if (prevSide == PlaneIntersectionType::Back) // prev:inside - current:intersects - next:outside { // find exit edge: // // outside // 2 // 1 / // ........*....... intersect // / // 0 // inside exitEdge = current; if (enterEdge != INVALID_INDEX) break; } else // prev:outside - current:intersects - next:inside { // find enter edge: // // outside // 0 // \ 1 // ........*....... intersect // \ // 2 // inside enterEdge = current; if (exitEdge != INVALID_INDEX) break; } } } } prev = current; current = next; next = _edges[next].NextIndex; prevDistance = currentDistance; currentDistance = nextDistance; prevSide = currentSide; currentSide = nextSide; prevVertex = currentVertex; currentVertex = nextVertex; } while (next != last); // We should never have only one edge crossing the plane ASSERT((enterEdge == INVALID_INDEX) == (exitEdge == INVALID_INDEX)); // Check if we have an edge that exits and an edge that enters the plane and split the polygon into two if we do if (enterEdge != INVALID_INDEX && exitEdge != INVALID_INDEX) { // enter . // . // =====>*-----> // . // // outside. inside // . // <-----*<===== // . // . exit Polygon outsidePolygon; HalfEdge outsideEdge, insideEdge; auto outsidePolygonIndex = _polygons.Count(); auto outsideEdgeIndex = _edges.Count(); auto insideEdgeIndex = outsideEdgeIndex + 1; outsideEdge.TwinIndex = insideEdgeIndex; insideEdge.TwinIndex = outsideEdgeIndex; insideEdge.PolygonIndex = inputPolygonIndex; outsideEdge.PolygonIndex = outsidePolygonIndex; HalfEdge* exitEdgeP = &_edges[exitEdge]; HalfEdge* enterEdgeP = &_edges[enterEdge]; outsideEdge.VertexIndex = exitEdgeP->VertexIndex; insideEdge.VertexIndex = enterEdgeP->VertexIndex; outsideEdge.NextIndex = exitEdgeP->NextIndex; insideEdge.NextIndex = enterEdgeP->NextIndex; exitEdgeP->NextIndex = insideEdgeIndex; enterEdgeP->NextIndex = outsideEdgeIndex; outsidePolygon.FirstEdgeIndex = outsideEdgeIndex; inputPolygon.FirstEdgeIndex = insideEdgeIndex; outsidePolygon.Visible = inputPolygon.Visible; outsidePolygon.Inverted = inputPolygon.Inverted; outsidePolygon.SurfaceIndex = inputPolygon.SurfaceIndex; _edges.Add(outsideEdge); _edges.Add(insideEdge); // calculate the bounds of the polygons outsidePolygon.Bounds.Clear(); auto first = _edges[outsidePolygon.FirstEdgeIndex]; auto iterator = first; do { outsidePolygon.Bounds.Add(_vertices[iterator.VertexIndex]); iterator.PolygonIndex = outsidePolygonIndex; iterator = _edges[iterator.NextIndex]; } while (iterator != first); inputPolygon.Bounds.Clear(); first = _edges[inputPolygon.FirstEdgeIndex]; iterator = first; do { inputPolygon.Bounds.Add(_vertices[iterator.VertexIndex]); iterator = _edges[iterator.NextIndex]; } while (iterator != first); _polygons.Add(outsidePolygon); *outputPolygon = &_polygons[outsidePolygonIndex]; return Split; } switch (prevSide) { case PlaneIntersectionType::Back: return CompletelyInside; case PlaneIntersectionType::Front: return CompletelyOutside; default: { auto polygonPlane = _surfaces[inputPolygon.SurfaceIndex]; auto result = Vector3::Dot(polygonPlane.Normal, cuttingPlane.Normal); return result > 0 ? PlaneAligned : PlaneOppositeAligned; } } } #endif