int main(int argc, char * argv[]) { string mesh_path; string samples_path; string out_path; int curr_opt = 0; for (int i = 1; i < argc; ++i) { string arg = argv[i]; if (!beginsWith(arg, "-")) { switch (curr_opt) { case 0: mesh_path = arg; break; case 1: samples_path = arg; break; case 2: out_path = arg; break; } curr_opt++; if (curr_opt > 3) break; } else { if (beginsWith(arg, "--max-nbrs=")) { if (sscanf(arg.c_str(), "--max-nbrs=%d", &max_nbrs) != 1 || max_nbrs <= 0) { THEA_ERROR << "Invalid --max-nbrs parameter"; return -1; } } else if (beginsWith(arg, "--min-samples=")) { if (sscanf(arg.c_str(), "--min-samples=%ld", &min_samples) != 1 || min_samples <= 0) { THEA_ERROR << "Invalid --min-samples parameter"; return -1; } } else if (arg == "-n" || arg == "--normals") { consistent_normals = true; } else if (arg == "-r" || arg == "--reachability") { reachability = true; } else { THEA_ERROR << "Unknown parameter: " << arg; return -1; } } } if (curr_opt != 3) { THEA_CONSOLE << ""; THEA_CONSOLE << "Usage: " << argv[0] << " [OPTIONS] <mesh|dense-points> <points> <graph-outfile>"; THEA_CONSOLE << ""; THEA_CONSOLE << "Options:"; THEA_CONSOLE << " --max-nbrs=N Maximum degree of proximity graph"; THEA_CONSOLE << " --min-samples=N Minimum number of original plus generated samples"; THEA_CONSOLE << " --normals | -n Run extra tests assuming consistently oriented mesh normals"; THEA_CONSOLE << " --reachability | -r Reachability test for adjacency (requires -n)"; THEA_CONSOLE << ""; return -1; } if (reachability && !consistent_normals) { THEA_ERROR << "Reachability test requires consistent normals"; return -1; } //=========================================================================================================================== // Load points //=========================================================================================================================== TheaArray<Vector3> sample_positions; TheaArray<Vector3> sample_normals; if (loadSamples(samples_path, sample_positions, sample_normals)) return -1; bool has_normals = (!sample_positions.empty() && sample_normals.size() == sample_positions.size()); THEA_CONSOLE << "Loaded " << sample_positions.size() << " sample(s) from " << samples_path; //=========================================================================================================================== // Load mesh or dense samples //=========================================================================================================================== TheaArray<Vector3> dense_positions; TheaArray<Vector3> dense_normals; bool dense_has_normals = false; KDTree kdtree; if (mesh_path != "-") { // First try to load the file as a set of points int dense_load_status = loadSamples(mesh_path, dense_positions, dense_normals); dense_has_normals = (!dense_positions.empty() && dense_normals.size() == dense_positions.size()); if (dense_load_status != 0 && dense_load_status != UNSUPPORTED_FORMAT) { return -1; } else if (dense_load_status == 0) { THEA_CONSOLE << dense_positions.size() << " extra samples added from: " << mesh_path; } else // Now try to load the file as a mesh, if the above failed { MG mg; try { mg.load(mesh_path); } THEA_STANDARD_CATCH_BLOCKS(return -1;, ERROR, "Could not load mesh %s", mesh_path.c_str()) THEA_CONSOLE << "Loaded mesh from " << mesh_path; // Make sure the mesh is properly scaled AxisAlignedBox3 mesh_bounds = mg.getBounds(); AxisAlignedBox3 samples_bounds; for (array_size_t i = 0; i < sample_positions.size(); ++i) samples_bounds.merge(sample_positions[i]); Real scale_error = (mesh_bounds.getLow() - samples_bounds.getLow()).length() + (mesh_bounds.getHigh() - samples_bounds.getHigh()).length(); if (scale_error > 0.01 * mesh_bounds.getExtent().length()) { // Rescale the mesh Real scale = (samples_bounds.getExtent() / mesh_bounds.getExtent()).max(); // samples give a smaller bound than the // true bound, so take the axis in which // the approximation is best AffineTransform3 tr = AffineTransform3::translation(samples_bounds.getCenter()) * AffineTransform3::scaling(scale) * AffineTransform3::translation(-mesh_bounds.getCenter()); MeshTransformer func(tr); mg.forEachMeshUntil(&func); mg.updateBounds(); THEA_CONSOLE << "Matched scale of source mesh and original samples"; } kdtree.add(mg); kdtree.init(); // If the samples have no normals, compute them if (consistent_normals && !has_normals) { sample_normals.resize(sample_positions.size()); for (array_size_t i = 0; i < sample_positions.size(); ++i) { long elem = kdtree.closestElement<MetricL2>(sample_positions[i]); if (elem < 0) { THEA_ERROR << "Could not find nearest neighbor of sample " << i << " on mesh"; return false; } sample_normals[i] = kdtree.getElements()[(array_size_t)elem].getNormal(); } has_normals = true; THEA_CONSOLE << "Computed sample normals"; } // Augment the number of samples if necessary if ((long)sample_positions.size() < min_samples) { dense_positions.clear(); dense_normals.clear(); MeshSampler<Mesh> sampler(mg); sampler.sampleEvenlyByArea((long)(min_samples - sample_positions.size()), dense_positions, (consistent_normals ? &dense_normals : NULL)); if (!dense_positions.empty()) THEA_CONSOLE << dense_positions.size() << " extra samples added to original set, for density"; dense_has_normals = (!dense_positions.empty() && dense_normals.size() == dense_positions.size()); } }