|
5 | 5 | /// |
6 | 6 | /// @brief Host driver for the connected-components example (NanoVDB / CUDA only, no OpenVDB). |
7 | 7 | /// |
8 | | -/// Reads a triangle mesh from a Wavefront .obj file, builds the index<->world transform, |
9 | | -/// and hands the mesh to the CUDA side, which rasterizes it into a ValueOnIndex narrow-band |
10 | | -/// grid, discards the surface/barrier shell, and runs connected-components labeling |
| 8 | +/// Reads one or more Wavefront .obj meshes (concatenated into a single vertex/triangle list), |
| 9 | +/// builds the index<->world transform, and hands the mesh to the CUDA side, which rasterizes |
| 10 | +/// it into a ValueOnIndex narrow-band grid, optionally discards the surface/barrier shell |
| 11 | +/// (--discard-surface-voxels), and runs connected-components labeling |
11 | 12 | /// (nanovdb::tools::cuda::ConnectedComponents) on the result. The device side also runs a |
12 | 13 | /// CPU union-find oracle that independently verifies the GPU labeling. |
13 | 14 | /// |
|
27 | 28 |
|
28 | 29 | // ---- Host/device seam (implemented in connected_components_cuda_kernels.cu) ----------------------- |
29 | 30 | // |
30 | | -// Rasterize the mesh -> ValueOnIndex narrow band, discard the barrier shell (unsigned distance |
31 | | -// within sqrt(3)/2 voxels of the surface), and run connected-components labeling. Prints topology |
32 | | -// diagnostics, the component count, and a CPU-oracle PASS/FAIL, and returns the number of |
33 | | -// connected components. |
| 31 | +// Rasterize the mesh -> ValueOnIndex narrow band and run connected-components labeling. When |
| 32 | +// @a discardSurfaceVoxels is true, the surface/barrier shell (unsigned distance within sqrt(3)/2 |
| 33 | +// voxels of the surface) is pruned first, splitting each closed surface's band into disjoint |
| 34 | +// inner/outer shells; otherwise labeling runs on the full narrow band (one component per closed |
| 35 | +// surface). Prints topology diagnostics, the component count, and a CPU-oracle PASS/FAIL, and |
| 36 | +// returns the number of connected components. |
34 | 37 | uint64_t connectedComponentsFromMesh(const std::vector<nanovdb::Vec3f>& points, |
35 | 38 | const std::vector<nanovdb::Vec3i>& triangles, |
36 | 39 | const nanovdb::Map& map, |
37 | | - float bandWidth); |
| 40 | + float bandWidth, |
| 41 | + bool discardSurfaceVoxels); |
38 | 42 |
|
39 | 43 | /// @brief Minimal Wavefront .obj reader (vertices + faces) using NanoVDB types. |
40 | 44 | /// |
@@ -86,29 +90,59 @@ static void readOBJ(const std::string& filename, |
86 | 90 | int main(int argc, char* argv[]) |
87 | 91 | { |
88 | 92 | try { |
89 | | - if (argc < 2) |
90 | | - throw std::runtime_error("usage: " + std::string(argv[0]) + |
91 | | - " <input.obj> [voxelSize] [bandWidth]"); |
| 93 | + // Parse args: any non-option token is an input .obj; options set the transform and the |
| 94 | + // barrier-discard switch. |
| 95 | + std::vector<std::string> objFiles; |
| 96 | + float voxelSize = 0.01f; |
| 97 | + float bandWidth = 3.0f; |
| 98 | + bool discardSurfaceVoxels = false; |
92 | 99 |
|
93 | | - const std::string inputFile = argv[1]; |
94 | | - const float voxelSize = (argc > 2) ? std::stof(argv[2]) : 0.01f; |
95 | | - const float bandWidth = (argc > 3) ? std::stof(argv[3]) : 3.0f; |
| 100 | + auto nextValue = [&](int& i, const char* opt) -> const char* { |
| 101 | + if (i + 1 >= argc) throw std::runtime_error(std::string("missing value for ") + opt); |
| 102 | + return argv[++i]; |
| 103 | + }; |
| 104 | + for (int i = 1; i < argc; ++i) { |
| 105 | + const std::string a = argv[i]; |
| 106 | + if (a == "--discard-surface-voxels") discardSurfaceVoxels = true; |
| 107 | + else if (a == "--voxel-size") voxelSize = std::stof(nextValue(i, "--voxel-size")); |
| 108 | + else if (a == "--band-width") bandWidth = std::stof(nextValue(i, "--band-width")); |
| 109 | + else if (a.rfind("--", 0) == 0) throw std::runtime_error("unknown option: " + a); |
| 110 | + else objFiles.push_back(a); |
| 111 | + } |
| 112 | + if (objFiles.empty()) |
| 113 | + throw std::runtime_error( |
| 114 | + "usage: " + std::string(argv[0]) + |
| 115 | + " <input.obj> [more.obj ...] [--voxel-size S] [--band-width W] [--discard-surface-voxels]"); |
96 | 116 |
|
| 117 | + // Read and merge all input meshes into one vertex/triangle list. Merging simply concatenates |
| 118 | + // the meshes in their own coordinates (no repositioning), offsetting each mesh's triangle |
| 119 | + // indices past the vertices already added. |
97 | 120 | std::vector<nanovdb::Vec3f> points; |
98 | 121 | std::vector<nanovdb::Vec3i> triangles; |
99 | | - std::cout << "Reading " << inputFile << "...\n"; |
100 | | - readOBJ(inputFile, points, triangles); |
101 | | - std::cout << "Loaded " << points.size() << " vertices, " |
102 | | - << triangles.size() << " triangles.\n"; |
| 122 | + for (const std::string& file : objFiles) { |
| 123 | + std::vector<nanovdb::Vec3f> filePoints; |
| 124 | + std::vector<nanovdb::Vec3i> fileTriangles; |
| 125 | + std::cout << "Reading " << file << "...\n"; |
| 126 | + readOBJ(file, filePoints, fileTriangles); |
| 127 | + const int offset = int(points.size()); |
| 128 | + points.insert(points.end(), filePoints.begin(), filePoints.end()); |
| 129 | + for (const nanovdb::Vec3i& t : fileTriangles) |
| 130 | + triangles.emplace_back(t[0] + offset, t[1] + offset, t[2] + offset); |
| 131 | + } |
| 132 | + std::cout << "Loaded " << points.size() << " vertices, " << triangles.size() |
| 133 | + << " triangles from " << objFiles.size() << " mesh(es).\n"; |
103 | 134 | if (points.empty() || triangles.empty()) |
104 | 135 | throw std::runtime_error("mesh has no triangles"); |
105 | 136 |
|
106 | 137 | // Index<->world transform: uniform voxel size, no translation. |
107 | 138 | nanovdb::Map map; |
108 | 139 | map.set(double(voxelSize), nanovdb::Vec3d(0.0), 1.0); |
109 | 140 |
|
| 141 | + std::cout << "Surface voxels: " |
| 142 | + << (discardSurfaceVoxels ? "discarded (barrier shell pruned)" |
| 143 | + : "kept (full narrow band)") << "\n"; |
110 | 144 | const uint64_t numComponents = |
111 | | - connectedComponentsFromMesh(points, triangles, map, bandWidth); |
| 145 | + connectedComponentsFromMesh(points, triangles, map, bandWidth, discardSurfaceVoxels); |
112 | 146 | std::cout << "Connected components: " << numComponents << "\n"; |
113 | 147 |
|
114 | 148 | return 0; |
|
0 commit comments