Files
FlaxEngine/Source/Engine/CSG/CSGMesh.cpp
2023-01-10 15:29:37 +01:00

319 lines
9.3 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "CSGMesh.h"
#if COMPILE_WITH_CSG_BUILDER
#include "Engine/Core/Collections/Array.h"
using namespace CSG;
bool CSG::Mesh::HasMode(Mode mode) const
{
for (int32 i = 0; i < _brushesMeta.Count(); i++)
{
if (_brushesMeta[i].Mode == mode)
return true;
}
return false;
}
void CSG::Mesh::PerformOperation(Mesh* other)
{
ASSERT(other->_brushesMeta.Count() > 0);
// Check if has more than one submeshes
if (other->_brushesMeta.Count() > 1)
{
// Just add all subbrushes
Add(other);
}
else
{
auto mode = other->_brushesMeta[0].Mode;
// Switch between mesh operation mode
switch (mode)
{
case Mode::Additive:
{
// Check if both meshes do not intersect
if (AABB::IsOutside(_bounds, other->GetBounds())) // TODO: test every sub bounds not whole _bounds
{
// Add vertices to the mesh without any additional calculations
Add(other);
}
else
{
// Remove common part from other mesh and then combine them
//intersect(other, Remove, Keep);
Add(other);
// TODO: ogarnac to bo to jest zle xD
}
}
break;
case Mode::Subtractive:
{
// Check if both meshes do not intersect
if (AABB::IsOutside(_bounds, other->GetBounds())) // TODO: test every sub bounds not whole _bounds
{
// Do nothing
}
else
{
// Perform intersection operations
intersect(other, Remove, Keep);
other->intersect(this, Flip, Remove);
// Merge meshes
Add(other);
}
}
break;
}
}
}
void CSG::Mesh::Add(const Mesh* other)
{
ASSERT(this != other && other);
// Cache data
int32 baseIndexVertices = _vertices.Count();
int32 baseIndexSurfaces = _surfaces.Count();
int32 baseIndexEdges = _edges.Count();
int32 baseIndexPolygons = _polygons.Count();
auto oVertices = other->GetVertices();
auto oSurfaces = other->GetSurfaces();
auto oEdges = other->GetEdges();
auto oPolygons = other->GetPolygons();
auto oMeta = &other->_brushesMeta;
// Clone vertices
for (int32 i = 0; i < oVertices->Count(); i++)
{
_vertices.Add(oVertices->At(i));
}
// Clone surfaces
for (int32 i = 0; i < oSurfaces->Count(); i++)
{
_surfaces.Add(oSurfaces->At(i));
}
// Clone edges
for (int32 i = 0; i < oEdges->Count(); i++)
{
HalfEdge edge = oEdges->At(i);
edge.PolygonIndex += baseIndexPolygons;
edge.TwinIndex += baseIndexEdges;
edge.NextIndex += baseIndexEdges;
edge.VertexIndex += baseIndexVertices;
_edges.Add(edge);
}
// Clone polygons
for (int32 i = 0; i < oPolygons->Count(); i++)
{
Polygon polygon = oPolygons->At(i);
polygon.SurfaceIndex += baseIndexSurfaces;
polygon.FirstEdgeIndex += baseIndexEdges;
_polygons.Add(polygon);
}
// Clone meta
for (int32 i = 0; i < oMeta->Count(); i++)
{
BrushMeta meta = oMeta->At(i);
meta.StartSurfaceIndex += baseIndexSurfaces;
_brushesMeta.Add(meta);
}
// Increase bounds
_bounds.Add(other->GetBounds());
}
void CSG::Mesh::intersect(const Mesh* other, PolygonOperation insideOp, PolygonOperation outsideOp)
{
// insideOp - operation for polygons being inside the other brush
// outsideOp - operation for polygons being outside the other brush
// Prevent from redundant action
if (insideOp == Keep && outsideOp == Keep)
return;
// Check every sub brush from other mesh
int32 count = other->_brushesMeta.Count();
for (int32 subMeshIndex = 0; subMeshIndex < count; subMeshIndex++)
{
auto& brushMeta = other->_brushesMeta[subMeshIndex];
// Check if can intersect with that sub mesh data
if (!AABB::IsOutside(brushMeta.Bounds, _bounds))
{
PolygonOperation iOp = insideOp;
PolygonOperation oOp = outsideOp;
if (brushMeta.Mode == Mode::Subtractive)
{
iOp = Remove;
oOp = Keep;
}
intersectSubMesh(other, subMeshIndex, iOp, oOp);
}
}
// Update bounds
updateBounds();
}
void CSG::Mesh::intersectSubMesh(const Mesh* other, int32 subMeshIndex, PolygonOperation insideOp, PolygonOperation outsideOp)
{
// Cache data
auto oSurfaces = other->GetSurfaces();
auto& brushMeta = other->_brushesMeta[subMeshIndex];
auto oBounds = brushMeta.Bounds;
int32 startBrushSurface = brushMeta.StartSurfaceIndex;
int32 endBrushSurface = startBrushSurface + brushMeta.SurfacesCount;
// Check every polygon (iterate from end since we can ass new polygons and we don't want to process them)
for (int32 i = _polygons.Count() - 1; i >= 0; i--)
{
auto& polygon = _polygons[i];
if (!polygon.Visible || polygon.FirstEdgeIndex == INVALID_INDEX)
continue;
AABB polygonBounds = polygon.Bounds;
auto finalResult = CompletelyInside;
// A quick check if the polygon lies outside the planes we're cutting our polygons with
if (!AABB::IsOutside(oBounds, polygonBounds))
{
PolygonSplitResult intermediateResult;
Polygon* outsidePolygon;
for (int32 otherIndex = startBrushSurface; otherIndex < endBrushSurface; otherIndex++)
{
auto& cuttingSurface = oSurfaces->At(otherIndex);
auto side = cuttingSurface.OnSide(polygonBounds);
if (side == PlaneIntersectionType::Front)
{
finalResult = CompletelyOutside;
continue;
}
if (side == PlaneIntersectionType::Back)
{
continue;
}
intermediateResult = polygonSplit(cuttingSurface, i, &outsidePolygon);
if (intermediateResult == CompletelyOutside)
{
finalResult = CompletelyOutside;
break;
}
if (intermediateResult == Split)
{
resolvePolygon(*outsidePolygon, outsideOp);
}
else if (intermediateResult != CompletelyInside)
{
finalResult = intermediateResult;
}
}
}
else
{
finalResult = CompletelyOutside;
}
switch (finalResult)
{
case CompletelyInside:
{
resolvePolygon(_polygons[i], insideOp);
break;
}
case CompletelyOutside:
{
resolvePolygon(_polygons[i], outsideOp);
break;
}
// The polygon can only be visible if it's part of the last brush that shares it's surface area,
// otherwise we'd get overlapping polygons if two brushes overlap.
// When the (final) polygon is aligned with one of the cutting planes, we know it lies on the surface of
// the CSG node we're cutting the polygons with. We also know that this node is not the node this polygon belongs to
// because we've done that check earlier on. So we flag this polygon as being invisible.
case PlaneAligned:
// TODO: check this case
//polygon.Visible = false;
//aligned.Add(inputPolygon);
break;
case PlaneOppositeAligned:
// TODO: check this case
//polygon.Visible = false;
//revAligned.Add(inputPolygon);
break;
case Split:
break;
default:
break;
}
}
}
void CSG::Mesh::updateBounds()
{
_bounds.Clear();
int32 triangleIndices[3];
for (int32 i = 0; i < _polygons.Count(); i++)
{
auto& polygon = _polygons[i];
if (!polygon.Visible || polygon.FirstEdgeIndex == INVALID_INDEX)
continue;
HalfEdge iterator = _edges[polygon.FirstEdgeIndex];
triangleIndices[0] = iterator.VertexIndex;
iterator = _edges[iterator.NextIndex];
triangleIndices[1] = iterator.VertexIndex;
const HalfEdge& polygonFirst = _edges[polygon.FirstEdgeIndex];
while (iterator != polygonFirst)
{
iterator = _edges.At(iterator.NextIndex);
triangleIndices[2] = iterator.VertexIndex;
// Validate triangle
if (triangleIndices[0] != triangleIndices[1] && triangleIndices[0] != triangleIndices[2] && triangleIndices[1] != triangleIndices[2])
{
for (int32 q = 0; q < 3; q++)
{
Vector3 pos = _vertices[triangleIndices[q]];
_bounds.Add(pos);
}
}
triangleIndices[1] = triangleIndices[2];
}
}
}
void CSG::Mesh::doPolygonsOperation(bool isInverted, bool visibility)
{
for (int32 i = 0; i < _polygons.Count(); i++)
{
if (_polygons[i].Inverted == isInverted)
_polygons[i].Visible = visibility;
}
}
#endif