示例#1
0
int main(int argc, char *argv[])
{
	
	//
	// command line parsing.
	//
	const char* inFile = parseStringParam("-in", argc, argv);
	if (inFile == nullptr) printHelpExit();

	const char* outFile = parseStringParam("-out", argc, argv);
	if (outFile == nullptr) printHelpExit();

	unsigned int outFacesN;
	if (!parseIntParam("-outfaces", argc, argv, outFacesN)) printHelpExit();

	unsigned int upsampleN;
	if (!parseIntParam("-upsample", argc, argv, upsampleN)) printHelpExit();
	
	// original mesh vertices and indices. This is the original mesh, which has a hole.
	MatrixXd originalV;
	MatrixXi originalF;

	if (!igl::readOFF(inFile, originalV, originalF)) {
		printHelpExit();
	}

	VectorXi originalLoop; // indices of the boundary of the hole. 
	igl::boundary_loop(originalF, originalLoop);

	if (originalLoop.size() == 0) {
		printf("Mesh has no hole!");
		printHelpExit();
	}

	// upsample the original mesh. this makes fusing the original mesh with the patch much easier.
	igl::upsample(Eigen::MatrixXd(originalV), Eigen::MatrixXi(originalF), originalV, originalF, upsampleN);

	// compute boundary center.
	VectorXd bcenter(3);
	{
		VectorXi R = originalLoop;
		VectorXi C(3); C << 0, 1, 2;
		MatrixXd B;
		MatrixXd A = originalV;
		igl::slice(A, R, C, B);
		bcenter = (1.0f / originalLoop.size()) * B.colwise().sum();
	}

	// a flat patch that fills the hole.
	MatrixXd patchV = MatrixXd(originalLoop.size() + 1, 3); // patch will have an extra vertex for the center vertex.
	MatrixXi patchF = MatrixXi(originalLoop.size(), 3);

	{
		VectorXi R = originalLoop;
		VectorXi C(3); C << 0, 1, 2;
		MatrixXd A = originalV;
		MatrixXd temp1;
		igl::slice(A, R, C, temp1);

		MatrixXd temp2(1, 3);
		temp2 << bcenter(0), bcenter(1), bcenter(2);

		// patch vertices will be the boundary vertices, plus a center vertex. concat these together.
		igl::cat(1, temp1, temp2, patchV);

		// create triangles that connect boundary vertices and the center vertex.
		for (int i = 0; i < originalLoop.size(); ++i) {
			patchF(i, 2) = (int)originalLoop.size();
			patchF(i, 1) = i;
			patchF(i, 0) = (1 + i) % originalLoop.size();
		}

		// also upsample patch. patch and original mesh will have the same upsampling level now
		// making it trivial to fuse them together.
		igl::upsample(Eigen::MatrixXd(patchV), Eigen::MatrixXi(patchF), patchV, patchF, upsampleN);
	}

	// the final mesh, where the patch and original mesh has been fused together.
	std::vector<std::vector<double>> fusedV;
	std::vector<std::vector<int>> fusedF;

	int index = 0; // vertex index counter for the fused mesh.

				   // first, we add the upsampled patch to the fused mesh.
	{
		for (; index < patchV.rows(); ++index) {
			fusedV.push_back({ patchV(index, 0), patchV(index, 1), patchV(index, 2) });
		}

		int findex = 0;
		for (; findex < patchF.rows(); ++findex) {
			fusedF.push_back({ patchF(findex, 0), patchF(findex, 1), patchF(findex, 2) });
		}
	}

	// now, we fuse the patch and the original mesh together.
	{
		// remaps indices from the original mesh to the fused mesh.
		std::map<int, int> originalToFusedMap;

		for (int itri = 0; itri < originalF.rows(); ++itri) {

			int triIndices[3];
			for (int iv = 0; iv < 3; ++iv) {

				int triIndex = originalF(itri, iv);

				int ret;

				if (originalToFusedMap.count(triIndex) == 0) {
					int foundMatch = -1;

					// the vertices at the boundary are the same, for both the two meshes(patch and original mesh).
					// we ensure that these vertices are not duplicated.
					// this is also how we ensure that the two meshes are fused together.
					for (int jj = 0; jj < patchV.rows(); ++jj) {
						VectorXd u(3); u << fusedV[jj][0], fusedV[jj][1], fusedV[jj][2];
						VectorXd v(3); v << originalV(triIndex, 0), originalV(triIndex, 1), originalV(triIndex, 2);

						if ((u - v).norm() < 0.00001) {
							foundMatch = jj;
							break;
						}
					}

					if (foundMatch != -1) {
						originalToFusedMap[triIndex] = foundMatch;
						ret = foundMatch;
					}
					else {
						fusedV.push_back({ originalV(triIndex, 0), originalV(triIndex, 1), originalV(triIndex, 2) });
						originalToFusedMap[triIndex] = index;
						ret = index;
						index++;
					}
				}
				else {
					ret = originalToFusedMap[triIndex];
				}

				triIndices[iv] = ret;
			}

			fusedF.push_back({
				triIndices[0],
				triIndices[1],
				triIndices[2] });


		}

	}

	MatrixXd fairedV(fusedV.size(), 3);
	MatrixXi fairedF(fusedF.size(), 3);
	// now we shall do surface fairing on the mesh, to ensure
	// that the patch conforms to the surrounding curvature.
	{

		for (int vindex = 0; vindex < fusedV.size(); ++vindex) {
			auto r = fusedV[vindex];

			fairedV(vindex, 0) = r[0];
			fairedV(vindex, 1) = r[1];
			fairedV(vindex, 2) = r[2];
		}

		for (int findex = 0; findex < fusedF.size(); ++findex) {
			auto r = fusedF[findex];

			fairedF(findex, 0) = r[0];
			fairedF(findex, 1) = r[1];
			fairedF(findex, 2) = r[2];
		}

		VectorXi b(fairedV.rows() - patchV.rows());
		MatrixXd bc(fairedV.rows() - patchV.rows(), 3);
		// setup the boundary conditions. This is simply the vertex positions of the vertices not part of the patch.
		for (int i = (int)patchV.rows(); i < (int)fairedV.rows(); ++i) {
			int jj = i - (int)patchV.rows();

			b(jj) = i;

			bc(jj, 0) = fairedV(i, 0);
			bc(jj, 1) = fairedV(i, 1);
			bc(jj, 2) = fairedV(i, 2);
		}

		MatrixXd Z;
		int k = 2;
		// surface fairing simply means that we solve the equation
		// Delta^2 f = 0
		// with appropriate boundary conditions.
		// this function igl::harmonic from libigl takes care of that.

		// note that this is pretty inefficient thought.
		// the only boundary conditions necessary are the 2-ring of vertices around the patch.
		// the rest of the mesh vertices need not be specified.
		// we specify the rest of the mesh for simplicity of code, but it is not strictly necessary,
		// and pretty inefficient, since we have to solve a LARGE matrix equation as a result of this.
		igl::harmonic(fairedV, fairedF, b, bc, k, Z);
		fairedV = Z;
	}

	// finally, we do a decimation step.
	MatrixXd finalV(fusedV.size(), 3);
	MatrixXi finalF(fusedF.size(), 3);
	VectorXi temp0; VectorXi temp1;
	igl::decimate(fairedV, fairedF, outFacesN, finalV, finalF, temp0, temp1);
	
	igl::writeOFF(outFile, finalV, finalF);
}