319 lines
9.3 KiB
C++
319 lines
9.3 KiB
C++
// Copyright (c) 2012-2020 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 intesect
|
|
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 intesect
|
|
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 redudant 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 (itereate fron 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
|