#include "hrbf_resampler.h" // Sampling and welding #include "../TopoBlenderLib/SimilarSampling.cpp" #include "../NURBS/weld.h" // HRBF #include "hrbf/hrbf_phi_funcs.h" #include "hrbf/hrbf_core.h" typedef HRBF_fit > HRBF; // Marching cubes #include "mc/MarchingCubes.h" #include "NanoKdTree.h" void hrbf_resampler::initParameters(RichParameterSet *pars) { // Sampling parameters pars->addParam(new RichInt("numSamples", 10, "Number of samples")); // Marching Cubes parameters pars->addParam(new RichFloat("cellScale", 0.1f, "Cell scale", "Cell scale in % of maximum bounding extent")); } GRIDCELL evalCell(Vector3 p, Scalar cellSize, HRBF & fit){ GRIDCELL cell; Vector3 deltaX(cellSize,0,0), deltaY(0,cellSize,0), deltaZ(0,0,cellSize); cell.p[0] = p; cell.val[0] = fit.eval(cell.p[0]); cell.p[1] = p + deltaX; cell.val[1] = fit.eval(cell.p[1]); cell.p[2] = p + deltaX + deltaY; cell.val[2] = fit.eval(cell.p[2]); cell.p[3] = p + deltaY; cell.val[3] = fit.eval(cell.p[3]); p += deltaZ; cell.p[4] = p; cell.val[4] = fit.eval(cell.p[4]); cell.p[5] = p + deltaX; cell.val[5] = fit.eval(cell.p[5]); cell.p[6] = p + deltaX + deltaY; cell.val[6] = fit.eval(cell.p[6]); cell.p[7] = p + deltaY; cell.val[7] = fit.eval(cell.p[7]); return cell; } void hrbf_resampler::applyFilter(RichParameterSet *pars) { // Sample mesh QVector m_samples, m_normals; m_samples = SimilarSampler::All(mesh(), pars->getInt("numSamples"), m_normals); // Similar sampling std::vector points = m_samples.toStdVector(), normals = m_normals.toStdVector(); // convert to std::vector foreach(Vector3 p, m_samples) drawArea()->drawPoint(p); // Compute grid parameters mesh()->updateBoundingBox(); Vector3 ext = mesh()->bbox().diagonal(); double cellSize = ext.norm() * pars->getFloat("cellScale"); int countX = qMax(1.0, ext.x() / cellSize), countY = qMax(1.0, ext.y() / cellSize), countZ = qMax(1.0, ext.z() / cellSize); Vector3 startCorner = mesh()->bbox().min(); // Construct HRBF fit of samples HRBF fit; fit.hermite_fit(points, normals); /// Extract iso-surface via Marching Cubes // Compute active grid cells by looking at samples std::vector vertices; std::vector tris; int voffset = 0; NanoKdTree kdtree; for(int x = -2; x <= countX + 1; x++){ for(int y = -2; y <= countY + 1; y++){ for(int z = -2; z <= countZ + 1; z++){ GRIDCELL cell = evalCell(startCorner + (Vector3(x, y, z) * cellSize), cellSize, fit); TriMeshChunk chunk; if( Polygonise(cell, chunk) ) { foreach(Vector3 p, chunk.vertices) { vertices.push_back(p); kdtree.addPoint(p); } foreach(TriMeshFace f, chunk.faces) tris.push_back( Vector3i(f[2] + voffset, f[1] + voffset, f[0] + voffset) ); voffset += chunk.vertices.size(); } } } } mesh()->isVisible = false; mesh()->clear(); // Vertices should be unique double threshold = 1e-10; kdtree.build(); std::vector groups; for(int i = 0; i < (int)vertices.size(); i++){ Vector3 p = vertices[i]; KDResults matches; kdtree.ball_search(p, threshold, matches); double diffs = 0; foreach(KDResultPair r, matches) diffs += r.second; if( matches.size() > 1 && diffs > 0){ groups.push_back(matches); } } // Snap matches to first element foreach(KDResults matches, groups){ Vector3 snapPoint = vertices[ matches.front().first ]; for(int i = 1; i < (int)matches.size(); i++){ vertices[matches[i].first] = snapPoint; } } std::vector xrefs; weld(vertices, xrefs, std::hash_Vector3d(), std::equal_to()); // Add re-sampled mesh foreach(Vector3 p, vertices) mesh()->add_vertex(p); foreach(Vector3i f, tris){ int v1 = xrefs[f[0]]; int v2 = xrefs[f[1]]; int v3 = xrefs[f[2]]; if(v1 != v2 && v2 != v3 && v1 != v3) mesh()->add_triangle(Vertex(v1),Vertex(v2),Vertex(v3)); } mesh()->updateBoundingBox(); mesh()->update_face_normals(); mesh()->update_vertex_normals(); mesh()->isVisible = true; } Q_EXPORT_PLUGIN(hrbf_resampler)