Program Listing for File HalfEdgeFace.cpp¶
↰ Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/physics/HalfEdgeFace.cpp
)
#include <physics/halfedgeface.hpp>
#include <physics/halfedgeedge.hpp>
#include <rendering/debugrendering.hpp>
namespace legion::physics
{
HalfEdgeFace::HalfEdgeFace(HalfEdgeEdge* newStartEdge, math::vec3 newNormal) : startEdge{ newStartEdge }, normal{ newNormal }
{
/*log::debug("HalfEdgeFace::HalfEdgeFace");*/
static int faceCount = 0;
DEBUG_color =math::color( math::linearRand(0.25f, 0.7f), math::linearRand(0.25f, 0.7f), math::linearRand(0.25f, 0.7f));
math::vec3 faceCenter{ 0.0f };
int edgeCount = 0;
auto calculateFaceCentroid = [&faceCenter, &edgeCount](HalfEdgeEdge* edge)
{
math::vec3 pos = edge->edgePosition;
faceCenter += pos;
edgeCount++;
};
forEachEdge(calculateFaceCentroid);
centroid = faceCenter / static_cast<float>(edgeCount);
int currentEdgeId = 0;
auto initializeEdgeToFaceFunc = [this, ¤tEdgeId, edgeCount](HalfEdgeEdge* edge)
{
edge->face = this;
int nextID = currentEdgeId + 1 < edgeCount ? currentEdgeId + 1 : 0;
EdgeLabel label
(std::make_pair(faceCount, currentEdgeId), std::make_pair(faceCount, nextID));
edge->label = std::move(label);
currentEdgeId++;
};
forEachEdge(initializeEdgeToFaceFunc);
faceCount++;
}
void HalfEdgeFace::deleteEdges()
{
HalfEdgeEdge* current = startEdge->nextEdge;
do
{
if (current->prevEdge->pairingEdge && current->prevEdge->pairingEdge->pairingEdge == current) current->prevEdge->pairingEdge->pairingEdge = nullptr;
delete current->prevEdge;
current = current->nextEdge;
} while (current != startEdge && current != nullptr);
startEdge = nullptr;
}
void HalfEdgeFace::setFaceForAllEdges()
{
HalfEdgeEdge* current = startEdge;
do
{
current->face = this;
current = current->nextEdge;
} while (current != startEdge);
}
void HalfEdgeFace::forEachEdge(legion::core::delegate< void(HalfEdgeEdge*)> functionToExecute)
{
HalfEdgeEdge* initialEdge = startEdge;
HalfEdgeEdge* currentEdge = startEdge;
if (!currentEdge) return;
//the HalfEdgeEdge* 'startEdge' creates a ring buffer.
//This means that initialEdge will eventually go back to "startEdge", ending the loop.
do
{
HalfEdgeEdge* edgeToExecuteOn = currentEdge;
currentEdge = currentEdge->nextEdge;
functionToExecute(edgeToExecuteOn);
} while (initialEdge != currentEdge && currentEdge->nextEdge != nullptr);
}
void HalfEdgeFace::inverse()
{
normal = -normal; // Inverse the normal
}
bool HalfEdgeFace::testConvexity(const HalfEdgeFace& other) const
{
if (other == *this)
{
log::warn("Testing face with itself for convexity: returning true");
return true;
}
math::vec3 difference = startEdge->edgePosition - other.centroid;
float scaledAngle = math::dot(difference, normal);
// if the scaledAngle is smaller or equal to 0, it is not convex
if (scaledAngle <= 0)
{
return false;
}
return true;
}
bool HalfEdgeFace::makeNormalsConvexWithFace(HalfEdgeFace& other)
{
if (other == *this)
{
log::warn("Make normals for face convex with itself: returning false");
return false;
}
face_angle_relation relation= other.getAngleRelation(*this);
if (relation == face_angle_relation::concave)
{
inverse();
return true;
}
if (relation == face_angle_relation::coplanar)
{
log::warn("COPLANAR");
this->normal = other.normal;
return true;
}
//math::vec3 difference = startEdge->edgePosition - other.centroid;
//float scaledAngle = math::dot(difference, normal);
//if (scaledAngle < -math::epsilon<float>())
//{
// inverse();
// return true;
//}
//else if (scaledAngle < math::epsilon<float>())
//{
// this->normal = other.normal;
// log::warn("COPLANAR");
// return true;
//}
return false;
}
bool HalfEdgeFace::makeNormalsConvexWithNeighbors(HalfEdgeFace& other)
{
HalfEdgeEdge* start = this->startEdge;
HalfEdgeEdge* end = nullptr;
//do
// {
// log::debug(":makeNormalsConvexWithNeighbors 1 ");
// if (start == other.startEdge->pairingEdge)
// {
// end = start;
// break;
// }
// start = start->nextEdge;
// }
// while (start != this->startEdge);
end = HalfEdgeFace::findMiddleEdge(*this, other);
if (end == nullptr)
{
//log::debug("end == nullptr ");
return false;
}
start = end;
// end = this->startEdge;
do
{
//log::debug(":makeNormalsConvexWithNeighbors 2 ");
HalfEdgeFace* face = start->pairingEdge->face;
face_angle_relation relation = face->getAngleRelation(*this);
if (relation == face_angle_relation::concave)
{
inverse();
//log::debug("inversing normals");
return true;
}
else if (relation == face_angle_relation::convex)
{
return false;
}
//if (relation == face_angle_relation::coplanar)
//{
// // log::debug("COPLANAR");
// this->normal = other.normal;
// return true;
//}
start = start->nextEdge;
} while (start != end);
//log::debug("mission failed");
return false;
}
HalfEdgeFace::face_angle_relation HalfEdgeFace::getAngleRelation(const HalfEdgeFace& other)
{
if (other == *this)
{
log::warn("Calculating face angle relation between the same face, returning coplanar");
return face_angle_relation::coplanar;
}
//log::debug("IN FUNCTION face i {} face j {}", math::to_string(normal), math::to_string(other.normal));
float distToPlane = math::pointToPlane(centroid, other.centroid, other.normal);
//float distToPlane2 = math::pointToPlane(other.centroid, centroid, normal);
/*log::debug("centroid {} other.centroid {} ", centroid, other.centroid);
log::debug("distToPlane {} ", distToPlane);
log::debug("distToPlane2 {} ", distToPlane2);
log::debug("angle {} ", math::angleToPlane(centroid, other.centroid, other.normal));
log::debug("angle2 {} ", math::angleToPlane(other.centroid, centroid, normal));*/
//distToPlane2 = distToPlane;
//log::debug("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
static float error = math::sqrt(math::epsilon<float>()) ;
// if the distance to the face is negative, is it under the other face, therefore convex
if (distToPlane <= -error)
{
return face_angle_relation::convex;
}
else if (distToPlane >= error)
{
return face_angle_relation::concave;
}
else return face_angle_relation::coplanar;
}
bool HalfEdgeFace::testConvexity(const HalfEdgeFace& first, const HalfEdgeFace& second)
{
if (first == second)
{
log::warn("Testing face with itself for convexity: returning true");
return true;
}
return first.testConvexity(second) && second.testConvexity(first);
}
bool HalfEdgeFace::makeNormalsConvexWithFace(HalfEdgeFace& first, HalfEdgeFace& second)
{
if (first == second)
{
log::warn("Make normals for face convex with itself: returning false");
return false;
}
bool inversedNormal = first.makeNormalsConvexWithFace(second);
inversedNormal |= second.makeNormalsConvexWithFace(first);
return inversedNormal;
}
void HalfEdgeFace::mergeCoplanarNeighbors(std::vector<HalfEdgeFace*> & removed)
{
HalfEdgeEdge* current = startEdge;
if (this->normal != math::vec3(0, 0, 0))
{
return;
}
do
{
HalfEdgeFace* neigbor = current->pairingEdge->face;
if (neigbor->normal != math::vec3(0, 0, 0))
{
current = current->nextEdge;
continue;
}
face_angle_relation relation = this->getAngleRelation(*neigbor);
if (relation == face_angle_relation::coplanar)
{
//log::debug("relation == face_angle_relation::coplanar in :mergeCoplanarNeighbors ");
HalfEdgeEdge* middle = HalfEdgeFace::findMiddleEdge(*this, *neigbor);
if (middle)
{
this->mergeFaces(*middle);
removed.push_back(neigbor);
neigbor->normal = math::vec3();
}
}
// this->mergeFaces
current = current->nextEdge;
}
while (current != startEdge);
return;
}
HalfEdgeEdge* HalfEdgeFace::findMiddleEdge(const HalfEdgeFace& first, const HalfEdgeFace& second)
{
HalfEdgeEdge* firstCurrent = first.startEdge;
do
{
HalfEdgeEdge* secondCurrent = second.startEdge;
do
{
if (firstCurrent->pairingEdge == secondCurrent)
{
return firstCurrent;
}
if (firstCurrent == secondCurrent->pairingEdge)
{
return firstCurrent;
}
secondCurrent = secondCurrent->nextEdge;
} while (secondCurrent != second.startEdge);
firstCurrent = firstCurrent->nextEdge;
} while (firstCurrent != first.startEdge);
return nullptr;
}
HalfEdgeFace* HalfEdgeFace::mergeFaces(HalfEdgeEdge& middleEdge)
{
if (middleEdge.face->startEdge == &middleEdge)
{
middleEdge.face->startEdge = middleEdge.face->startEdge->prevEdge;
}
// Set correct faces
HalfEdgeEdge* end = middleEdge.pairingEdge;
HalfEdgeEdge* current = middleEdge.pairingEdge;
do
{
current->face = middleEdge.face;
current = current->nextEdge;
} while (current != end);
// Link edges
middleEdge.prevEdge->nextEdge = middleEdge.pairingEdge->nextEdge;
middleEdge.nextEdge->prevEdge = middleEdge.pairingEdge->prevEdge;
middleEdge.pairingEdge->prevEdge->nextEdge = middleEdge.nextEdge;
middleEdge.pairingEdge->nextEdge->prevEdge = middleEdge.prevEdge;
HalfEdgeFace* face = middleEdge.face;
// Recalculate centroid
{
math::vec3 faceCenter{ 0.0f };
int edgeCount = 0;
auto calculateFaceCentroid = [&faceCenter, &edgeCount](HalfEdgeEdge* edge)
{
math::vec3 pos = edge->edgePosition;
faceCenter += pos;
edgeCount++;
};
face->forEachEdge(calculateFaceCentroid);
face->centroid = faceCenter / static_cast<float>(edgeCount);
}
delete middleEdge.pairingEdge;
delete& middleEdge;
return face;
}
void HalfEdgeFace::DEBUG_DrawFace(const math::mat4& transform,const math::color& debugColor,float time)
{
auto drawFunc = [&transform,debugColor,time](HalfEdgeEdge* edge)
{
edge->DEBUG_drawEdge(transform, debugColor, time);
};
forEachEdge(drawFunc);
}
HalfEdgeFace::~HalfEdgeFace()
{
auto deleteFunc = [](HalfEdgeEdge* edge)
{
if (edge->pairingEdge && edge->pairingEdge && edge->pairingEdge->pairingEdge == edge)
{
edge->pairingEdge->pairingEdge = nullptr;
}
delete edge;
};
forEachEdge(deleteFunc);
}
}