in source/render/MeshSimplifier.cpp [453:559]
void MeshSimplifier::simplify(
const int numFacesOut,
const float strictness,
const bool removeBoundaryEdges) {
// Compute Q matrices and errors for all vertexes
LOG(INFO) << "Computing initial costs...";
computeInitialQuadrics();
const int numFacesIn = faces.size();
int numFacesDeleted = 0;
int numFacesDeletedPrev = 0;
double threshold = 0;
int countNumFacesSame = 0;
int iteration = 0;
while (int(faces.size()) > numFacesOut) {
removeDeletedFaces();
if (iteration == 0) {
LOG(INFO) << "Assigning faces and vertexes...";
}
assignFaceVertexes();
if (iteration == 0) {
LOG(INFO) << "Identifying boundaries...";
identifyBoundaries();
}
if (iteration == 0 || numFacesDeletedPrev != numFacesDeleted) {
threshold = getThreshold(strictness);
countNumFacesSame = 0;
} else {
// Scale threshold up to avoid getting stuck
threshold *= 2 * ++countNumFacesSame;
if (std::isinf(threshold)) {
// Sometimes there are no qualifying faces left, so the threshold would
// increase without limit
break;
}
}
numFacesDeletedPrev = numFacesDeleted;
if (iteration % 1 == 0) {
LOG(INFO) << folly::sformat(
"Iter: {}, faces: {}, threshold: {}", iteration, faces.size(), threshold);
}
for (auto& face : faces) {
if (face.isDeleted || face.isTouched) {
continue;
}
// Select all valid vertex pairs
for (int i = 0; i < NUM_VERTEXES_FACE; ++i) {
// Ignore if error (cost) is higher than threshold
if (face.cost[i] > threshold) {
continue;
}
int vIdx0 = face.vertexesIdx[i];
int vIdx1 = face.vertexesIdx[(i + 1) % NUM_VERTEXES_FACE];
// Ignore non-boundary edges with one boundary vertex
if (vertexes[vIdx0].isBoundary != vertexes[vIdx1].isBoundary) {
continue;
}
// Optionally ignore boundary edges entirely
if (!removeBoundaryEdges && (vertexes[vIdx0].isBoundary || vertexes[vIdx1].isBoundary)) {
continue;
}
// Compute optimal target point
Eigen::Vector3d pTarget;
computeError(vertexes[vIdx0], vertexes[vIdx1], pTarget);
// Prevent mesh inversion
if (haveNormalsFlipped(pTarget, vIdx0, vIdx1) ||
haveNormalsFlipped(pTarget, vIdx1, vIdx0)) {
continue;
}
// Mark faces for deletion. These are the faces common to both vertexes
const std::vector<int> commonFacesIdxs = commonFaces(vIdx0, vIdx1);
for (int faceIdx : commonFacesIdxs) {
faces[faceIdx].isDeleted = true;
}
numFacesDeleted += commonFacesIdxs.size();
// Update costs of all valid pairs involving new vertex
updateCosts(vIdx0, vIdx1, pTarget);
// Nothing else to do on the remaining vertexes of current face
break;
}
// Check remaining faces after processing every face
if (numFacesIn - numFacesDeleted <= numFacesOut) {
break;
}
}
++iteration;
}
// Assign final values to vertexes and faces
LOG(INFO) << "Creating final mesh...";
createFinalMesh();
}