|
| 1 | +// Copyright Contributors to the OpenVDB Project |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | + |
| 4 | +// the following files are from OpenVDB |
| 5 | +#include <openvdb/tools/Morphology.h> |
| 6 | +#include <openvdb/util/CpuTimer.h> |
| 7 | +#include <openvdb/tools/MeshToVolume.h> |
| 8 | + |
| 9 | +// the following files are from NanoVDB |
| 10 | +#include <nanovdb/NanoVDB.h> |
| 11 | +#include <nanovdb/cuda/DeviceBuffer.h> |
| 12 | +#include <nanovdb/tools/CreateNanoGrid.h> |
| 13 | + |
| 14 | +#include <thrust/universal_vector.h> |
| 15 | + |
| 16 | +template<typename BuildT> |
| 17 | +void mainMeshToGrid( |
| 18 | + const nanovdb::Vec3f *devicePoints, |
| 19 | + const int pointCount, |
| 20 | + const nanovdb::Vec3i *deviceTriangles, |
| 21 | + const int triangleCount, |
| 22 | + const nanovdb::Map map); |
| 23 | + |
| 24 | +void readOBJ(const std::string& filename, |
| 25 | + std::vector<openvdb::Vec3s>& points, |
| 26 | + std::vector<openvdb::Vec3I>& triangles, |
| 27 | + std::vector<openvdb::Vec4I>& quads) |
| 28 | +{ |
| 29 | + std::ifstream file(filename); |
| 30 | + if (!file.is_open()) { |
| 31 | + OPENVDB_THROW(openvdb::IoError, "Failed to open OBJ file: " + filename); |
| 32 | + } |
| 33 | + |
| 34 | + std::string line; |
| 35 | + int lineNumber = 0; |
| 36 | + |
| 37 | + while (std::getline(file, line)) { |
| 38 | + lineNumber++; |
| 39 | + std::istringstream iss(line); |
| 40 | + std::string type; |
| 41 | + iss >> type; |
| 42 | + |
| 43 | + if (type == "v") { |
| 44 | + float x, y, z; |
| 45 | + iss >> x >> y >> z; |
| 46 | + points.push_back(openvdb::Vec3s(x, y, z)); |
| 47 | + } else if (type == "f") { |
| 48 | + std::vector<int> faceIndices; |
| 49 | + std::string vertexData; |
| 50 | + |
| 51 | + while (iss >> vertexData) { |
| 52 | + // Isolate the vertex index (everything before the first slash) |
| 53 | + size_t slashPos = vertexData.find('/'); |
| 54 | + std::string indexStr = vertexData.substr(0, slashPos); |
| 55 | + |
| 56 | + if (indexStr.empty()) continue; |
| 57 | + |
| 58 | + int raw_idx = std::stoi(indexStr); |
| 59 | + int actual_idx = 0; |
| 60 | + |
| 61 | + // Handle negative indices: relative to the number of points parsed so far |
| 62 | + if (raw_idx < 0) { |
| 63 | + actual_idx = points.size() + raw_idx; |
| 64 | + } else { |
| 65 | + // Standard positive indices: OBJ is 1-based, convert to 0-based for C++ |
| 66 | + actual_idx = raw_idx - 1; |
| 67 | + } |
| 68 | + |
| 69 | + // Strict bounds checking to prevent segfaults |
| 70 | + if (actual_idx < 0 || actual_idx >= points.size()) { |
| 71 | + OPENVDB_THROW(openvdb::ValueError, |
| 72 | + "OBJ parse error on line " + std::to_string(lineNumber) + |
| 73 | + ": Face index out of bounds (Raw: " + std::to_string(raw_idx) + |
| 74 | + ", Computed: " + std::to_string(actual_idx) + ", Total Points: " + |
| 75 | + std::to_string(points.size()) + ")"); |
| 76 | + } |
| 77 | + |
| 78 | + faceIndices.push_back(actual_idx); |
| 79 | + } |
| 80 | + |
| 81 | + // Add to the appropriate OpenVDB list |
| 82 | + if (faceIndices.size() == 3) { |
| 83 | + triangles.push_back(openvdb::Vec3I(faceIndices[0], faceIndices[1], faceIndices[2])); |
| 84 | + } else if (faceIndices.size() == 4) { |
| 85 | + quads.push_back(openvdb::Vec4I(faceIndices[0], faceIndices[1], faceIndices[2], faceIndices[3])); |
| 86 | + } else if (faceIndices.size() > 4) { |
| 87 | + std::cerr << "Warning on line " << lineNumber << ": Skipping face with " |
| 88 | + << faceIndices.size() << " vertices. Triangulate your mesh!" << std::endl; |
| 89 | + } |
| 90 | + } |
| 91 | + } |
| 92 | +} |
| 93 | + |
| 94 | +/// @brief This example depends on OpenVDB, NanoVDB, and CUDA |
| 95 | +int main(int argc, char *argv[]) |
| 96 | +{ |
| 97 | + using GridT = openvdb::FloatGrid; |
| 98 | + using BuildT = nanovdb::ValueOnIndex; |
| 99 | + |
| 100 | + // Select the type of dilation here. The NN_EDGE case supports leaf dilation too (currently) |
| 101 | + // openvdb::tools::NearestNeighbors nnType = openvdb::tools::NN_FACE_EDGE_VERTEX; |
| 102 | + openvdb::tools::NearestNeighbors nnType = openvdb::tools::NN_FACE; |
| 103 | + |
| 104 | + openvdb::util::CpuTimer cpuTimer; |
| 105 | + |
| 106 | + try { |
| 107 | + |
| 108 | + if (argc<2) OPENVDB_THROW(openvdb::ValueError, "usage: "+std::string(argv[0])+" input.obj [output.vdb]\n"); |
| 109 | + std::string inputFile = argv[1]; |
| 110 | + std::string outputFile = "output.vdb"; |
| 111 | + if (argc > 2) |
| 112 | + outputFile = argv[2]; |
| 113 | + float voxelSize = 0.001f; |
| 114 | + if (argc > 3) |
| 115 | + voxelSize = atof(argv[3]); |
| 116 | + |
| 117 | + std::vector<openvdb::Vec3s> openvdb_points; |
| 118 | + std::vector<openvdb::Vec3I> openvdb_triangles; |
| 119 | + std::vector<openvdb::Vec4I> quads; |
| 120 | + |
| 121 | + // Read the OBJ file |
| 122 | + std::cout << "Reading " << inputFile << "..." << std::endl; |
| 123 | + readOBJ(inputFile, openvdb_points, openvdb_triangles, quads); |
| 124 | + std::cout << "Loaded " << openvdb_points.size() << " vertices, " |
| 125 | + << openvdb_triangles.size() << " openvdb_triangles, and " |
| 126 | + << quads.size() << " quads." << std::endl; |
| 127 | + |
| 128 | + // Initialize OpenVDB |
| 129 | + openvdb::initialize(); |
| 130 | + |
| 131 | + // Setup Grid Transform (Voxel Size) |
| 132 | + openvdb::math::Transform::Ptr transform = |
| 133 | + openvdb::math::Transform::createLinearTransform(voxelSize); |
| 134 | + |
| 135 | + // Convert Mesh to Level Set (SDF) |
| 136 | + // halfband specifies the half-width of the narrow band in voxel units |
| 137 | + float halfband = 3.0f; |
| 138 | + cpuTimer.start("Converting mesh to OpenVDB level set"); |
| 139 | + openvdb::FloatGrid::Ptr grid = openvdb::tools::meshToLevelSet<openvdb::FloatGrid>( |
| 140 | + *transform, openvdb_points, openvdb_triangles, quads, halfband); |
| 141 | + cpuTimer.stop(); |
| 142 | + |
| 143 | + |
| 144 | + // Write the Grid to a VDB File |
| 145 | + grid->setName("LevelSet"); |
| 146 | + grid->print(std::cout, 2); |
| 147 | + std::cout << "Writing to " << outputFile << "..." << std::endl; |
| 148 | + openvdb::GridPtrVec grids; |
| 149 | + grids.push_back(grid); |
| 150 | + openvdb::io::File file(outputFile); |
| 151 | + file.write(grids); |
| 152 | + file.close(); |
| 153 | + |
| 154 | + // Cast the raw pointers from the std::vector data |
| 155 | + const auto* nano_pts_data = reinterpret_cast<const nanovdb::Vec3f*>(openvdb_points.data()); |
| 156 | + const auto* nano_tris_data = reinterpret_cast<const nanovdb::Vec3i*>(openvdb_triangles.data()); |
| 157 | + |
| 158 | + // Initialize the thrust vectors using the casted pointer ranges |
| 159 | + thrust::universal_vector<nanovdb::Vec3f> nanovdb_points(nano_pts_data, nano_pts_data + openvdb_points.size()); |
| 160 | + thrust::universal_vector<nanovdb::Vec3i> nanovdb_triangles(nano_tris_data, nano_tris_data + openvdb_triangles.size()); |
| 161 | + |
| 162 | + // Convert OpenVDB transform to nanovdb::Map |
| 163 | + |
| 164 | + const auto openvdb_mat4 = transform->baseMap()->getAffineMap()->getMat4(); |
| 165 | + nanovdb::Map map; |
| 166 | + map.set(openvdb_mat4, openvdb_mat4.inverse()); |
| 167 | + |
| 168 | + mainMeshToGrid<BuildT>( |
| 169 | + nanovdb_points.data().get(), |
| 170 | + nanovdb_points.size(), |
| 171 | + nanovdb_triangles.data().get(), |
| 172 | + nanovdb_triangles.size(), |
| 173 | + map); |
| 174 | + |
| 175 | + return 0; |
| 176 | + } |
| 177 | + catch (const std::exception& e) { |
| 178 | + std::cerr << "An exception occurred: \"" << e.what() << "\"" << std::endl; |
| 179 | + } |
| 180 | + return 0; |
| 181 | +} |
0 commit comments