void MeshSimplifier::simplify()

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();
}