| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | #ifndef OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED | ||
| 5 | #define OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED | ||
| 6 | |||
| 7 | #include <openvdb/Grid.h> | ||
| 8 | #include <openvdb/Exceptions.h> | ||
| 9 | #include <openvdb/math/Math.h> | ||
| 10 | #include <openvdb/math/Transform.h> | ||
| 11 | #include <openvdb/tools/VolumeToMesh.h> | ||
| 12 | #include <openvdb/tools/MeshToVolume.h> | ||
| 13 | #include <openvdb/util/NullInterrupter.h> | ||
| 14 | #include <openvdb/util/Util.h> | ||
| 15 | #include <openvdb/openvdb.h> | ||
| 16 | #include <tbb/blocked_range.h> | ||
| 17 | #include <tbb/parallel_for.h> | ||
| 18 | #include <type_traits> | ||
| 19 | |||
| 20 | |||
| 21 | namespace openvdb { | ||
| 22 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 23 | namespace OPENVDB_VERSION_NAME { | ||
| 24 | namespace tools { | ||
| 25 | |||
| 26 | |||
| 27 | /// @brief Return a new grid of type @c GridType that contains a narrow-band level set | ||
| 28 | /// representation of an isosurface of a given grid. | ||
| 29 | /// | ||
| 30 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
| 31 | /// closed isosurfaces at the given @a isovalue | ||
| 32 | /// @param isovalue the isovalue that defines the implicit surface (defaults to zero, | ||
| 33 | /// which is typical if the input grid is already a level set or a SDF). | ||
| 34 | /// @param halfWidth half the width of the narrow band, in voxel units | ||
| 35 | /// (defaults to 3 voxels, which is required for some level set operations) | ||
| 36 | /// @param xform optional transform for the output grid | ||
| 37 | /// (if not provided, the transform of the input @a grid will be matched) | ||
| 38 | /// | ||
| 39 | /// @throw TypeError if @a grid is not scalar or not floating-point | ||
| 40 | /// | ||
| 41 | /// @note If the input grid contains overlapping isosurfaces, interior edges will be lost. | ||
| 42 | template<class GridType> | ||
| 43 | typename GridType::Ptr | ||
| 44 | levelSetRebuild(const GridType& grid, float isovalue = 0, | ||
| 45 | float halfWidth = float(LEVEL_SET_HALF_WIDTH), const math::Transform* xform = nullptr); | ||
| 46 | |||
| 47 | |||
| 48 | /// @brief Return a new grid of type @c GridType that contains a narrow-band level set | ||
| 49 | /// representation of an isosurface of a given grid. | ||
| 50 | /// | ||
| 51 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
| 52 | /// closed isosurfaces at the given @a isovalue | ||
| 53 | /// @param isovalue the isovalue that defines the implicit surface | ||
| 54 | /// @param exBandWidth the exterior narrow-band width in voxel units | ||
| 55 | /// @param inBandWidth the interior narrow-band width in voxel units | ||
| 56 | /// @param xform optional transform for the output grid | ||
| 57 | /// (if not provided, the transform of the input @a grid will be matched) | ||
| 58 | /// | ||
| 59 | /// @throw TypeError if @a grid is not scalar or not floating-point | ||
| 60 | /// | ||
| 61 | /// @note If the input grid contains overlapping isosurfaces, interior edges will be lost. | ||
| 62 | template<class GridType> | ||
| 63 | typename GridType::Ptr | ||
| 64 | levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth, | ||
| 65 | const math::Transform* xform = nullptr); | ||
| 66 | |||
| 67 | |||
| 68 | /// @brief Return a new grid of type @c GridType that contains a narrow-band level set | ||
| 69 | /// representation of an isosurface of a given grid. | ||
| 70 | /// | ||
| 71 | /// @param grid a scalar, floating-point grid with one or more disjoint, | ||
| 72 | /// closed isosurfaces at the given @a isovalue | ||
| 73 | /// @param isovalue the isovalue that defines the implicit surface | ||
| 74 | /// @param exBandWidth the exterior narrow-band width in voxel units | ||
| 75 | /// @param inBandWidth the interior narrow-band width in voxel units | ||
| 76 | /// @param xform optional transform for the output grid | ||
| 77 | /// (if not provided, the transform of the input @a grid will be matched) | ||
| 78 | /// @param interrupter optional interrupter object | ||
| 79 | /// | ||
| 80 | /// @throw TypeError if @a grid is not scalar or not floating-point | ||
| 81 | /// | ||
| 82 | /// @note If the input grid contains overlapping isosurfaces, interior edges will be lost. | ||
| 83 | template<class GridType, typename InterruptT> | ||
| 84 | typename GridType::Ptr | ||
| 85 | levelSetRebuild(const GridType& grid, float isovalue, float exBandWidth, float inBandWidth, | ||
| 86 | const math::Transform* xform = nullptr, InterruptT* interrupter = nullptr); | ||
| 87 | |||
| 88 | |||
| 89 | //////////////////////////////////////// | ||
| 90 | |||
| 91 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 92 | |||
| 93 | // Internal utility objects and implementation details | ||
| 94 | |||
| 95 | namespace internal { | ||
| 96 | |||
| 97 | class PointListTransform | ||
| 98 | { | ||
| 99 | public: | ||
| 100 | PointListTransform(const PointList& pointsIn, std::vector<Vec3s>& pointsOut, | ||
| 101 | const math::Transform& xform) | ||
| 102 | 5 | : mPointsIn(pointsIn) | |
| 103 | , mPointsOut(&pointsOut) | ||
| 104 | 5 | , mXform(xform) | |
| 105 | { | ||
| 106 | } | ||
| 107 | |||
| 108 | 5 | void runParallel() | |
| 109 | { | ||
| 110 | 5 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this); | |
| 111 | 5 | } | |
| 112 | |||
| 113 | void runSerial() | ||
| 114 | { | ||
| 115 | (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size())); | ||
| 116 | } | ||
| 117 | |||
| 118 | 512 | inline void operator()(const tbb::blocked_range<size_t>& range) const | |
| 119 | { | ||
| 120 |
2/2✓ Branch 0 taken 1664 times.
✓ Branch 1 taken 512 times.
|
2176 | for (size_t n = range.begin(); n < range.end(); ++n) { |
| 121 | 1664 | (*mPointsOut)[n] = Vec3s(mXform.worldToIndex(mPointsIn[n])); | |
| 122 | } | ||
| 123 | 512 | } | |
| 124 | |||
| 125 | private: | ||
| 126 | const PointList& mPointsIn; | ||
| 127 | std::vector<Vec3s> * const mPointsOut; | ||
| 128 | const math::Transform& mXform; | ||
| 129 | }; | ||
| 130 | |||
| 131 | |||
| 132 | class PrimCpy | ||
| 133 | { | ||
| 134 | public: | ||
| 135 | PrimCpy(const PolygonPoolList& primsIn, const std::vector<size_t>& indexList, | ||
| 136 | std::vector<Vec4I>& primsOut) | ||
| 137 | 5 | : mPrimsIn(primsIn) | |
| 138 | , mIndexList(indexList) | ||
| 139 | 5 | , mPrimsOut(&primsOut) | |
| 140 | { | ||
| 141 | } | ||
| 142 | |||
| 143 | 5 | void runParallel() | |
| 144 | { | ||
| 145 | 5 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mIndexList.size()), *this); | |
| 146 | 5 | } | |
| 147 | |||
| 148 | void runSerial() | ||
| 149 | { | ||
| 150 | (*this)(tbb::blocked_range<size_t>(0, mIndexList.size())); | ||
| 151 | } | ||
| 152 | |||
| 153 | 32 | inline void operator()(const tbb::blocked_range<size_t>& range) const | |
| 154 | { | ||
| 155 | openvdb::Vec4I quad; | ||
| 156 | 32 | quad[3] = openvdb::util::INVALID_IDX; | |
| 157 | 32 | std::vector<Vec4I>& primsOut = *mPrimsOut; | |
| 158 | |||
| 159 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
|
64 | for (size_t n = range.begin(); n < range.end(); ++n) { |
| 160 | 32 | size_t index = mIndexList[n]; | |
| 161 | 32 | PolygonPool& polygons = mPrimsIn[n]; | |
| 162 | |||
| 163 | // Copy quads | ||
| 164 |
2/2✓ Branch 0 taken 1656 times.
✓ Branch 1 taken 32 times.
|
1688 | for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) { |
| 165 | 1656 | primsOut[index++] = polygons.quad(i); | |
| 166 | } | ||
| 167 | 32 | polygons.clearQuads(); | |
| 168 | |||
| 169 | // Copy triangles (adaptive mesh) | ||
| 170 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) { |
| 171 | const openvdb::Vec3I& triangle = polygons.triangle(i); | ||
| 172 | ✗ | quad[0] = triangle[0]; | |
| 173 | ✗ | quad[1] = triangle[1]; | |
| 174 | ✗ | quad[2] = triangle[2]; | |
| 175 | ✗ | primsOut[index++] = quad; | |
| 176 | } | ||
| 177 | |||
| 178 | 32 | polygons.clearTriangles(); | |
| 179 | } | ||
| 180 | 32 | } | |
| 181 | |||
| 182 | private: | ||
| 183 | const PolygonPoolList& mPrimsIn; | ||
| 184 | const std::vector<size_t>& mIndexList; | ||
| 185 | std::vector<Vec4I> * const mPrimsOut; | ||
| 186 | }; | ||
| 187 | |||
| 188 | } // namespace internal | ||
| 189 | |||
| 190 | /// @endcond | ||
| 191 | |||
| 192 | //////////////////////////////////////// | ||
| 193 | |||
| 194 | |||
| 195 | //{ | ||
| 196 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 197 | |||
| 198 | /// The normal entry points for level set rebuild are the levelSetRebuild() functions. | ||
| 199 | /// doLevelSetRebuild() is mainly for internal use, but when the isovalue and half band | ||
| 200 | /// widths are given in ValueType units (for example, if they are queried from | ||
| 201 | /// a grid), it might be more convenient to call this function directly. | ||
| 202 | /// | ||
| 203 | /// @internal This overload is enabled only for grids with a scalar, floating-point ValueType. | ||
| 204 | template<class GridType, typename InterruptT> | ||
| 205 | inline typename std::enable_if< | ||
| 206 | std::is_floating_point<typename GridType::ValueType>::value, typename GridType::Ptr>::type | ||
| 207 | 10 | doLevelSetRebuild(const GridType& grid, typename GridType::ValueType iso, | |
| 208 | typename GridType::ValueType exWidth, typename GridType::ValueType inWidth, | ||
| 209 | const math::Transform* xform, InterruptT* interrupter) | ||
| 210 | { | ||
| 211 | const float | ||
| 212 | ✗ | isovalue = float(iso), | |
| 213 | ✗ | exBandWidth = float(exWidth), | |
| 214 | ✗ | inBandWidth = float(inWidth); | |
| 215 | |||
| 216 | 20 | tools::VolumeToMesh mesher(isovalue); | |
| 217 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | mesher(grid); |
| 218 | |||
| 219 |
2/6✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
10 | math::Transform::Ptr transform = (xform != nullptr) ? xform->copy() : grid.transform().copy(); |
| 220 | |||
| 221 |
2/6✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
10 | std::vector<Vec3s> points(mesher.pointListSize()); |
| 222 | |||
| 223 | { // Copy and transform (required for MeshToVolume) points to grid space. | ||
| 224 | internal::PointListTransform ptnXForm(mesher.pointList(), points, *transform); | ||
| 225 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | ptnXForm.runParallel(); |
| 226 | mesher.pointList().reset(nullptr); | ||
| 227 | } | ||
| 228 | |||
| 229 | std::vector<Vec4I> primitives; | ||
| 230 | |||
| 231 | { // Copy primitives. | ||
| 232 | PolygonPoolList& polygonPoolList = mesher.polygonPoolList(); | ||
| 233 | |||
| 234 | size_t numPrimitives = 0; | ||
| 235 |
1/4✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
10 | std::vector<size_t> indexlist(mesher.polygonPoolListSize()); |
| 236 | |||
| 237 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 5 times.
|
74 | for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) { |
| 238 | const openvdb::tools::PolygonPool& polygons = polygonPoolList[n]; | ||
| 239 | 64 | indexlist[n] = numPrimitives; | |
| 240 | 64 | numPrimitives += polygons.numQuads(); | |
| 241 | 64 | numPrimitives += polygons.numTriangles(); | |
| 242 | } | ||
| 243 | |||
| 244 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | primitives.resize(numPrimitives); |
| 245 | internal::PrimCpy primCpy(polygonPoolList, indexlist, primitives); | ||
| 246 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | primCpy.runParallel(); |
| 247 | } | ||
| 248 | |||
| 249 | QuadAndTriangleDataAdapter<Vec3s, Vec4I> mesh(points, primitives); | ||
| 250 | |||
| 251 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
10 | if (interrupter) { |
| 252 | return meshToVolume<GridType>(*interrupter, mesh, *transform, exBandWidth, inBandWidth, | ||
| 253 | ✗ | DISABLE_RENORMALIZATION, nullptr); | |
| 254 | } | ||
| 255 | |||
| 256 | return meshToVolume<GridType>(mesh, *transform, exBandWidth, inBandWidth, | ||
| 257 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | DISABLE_RENORMALIZATION, nullptr); |
| 258 | } | ||
| 259 | |||
| 260 | |||
| 261 | /// @internal This overload is enabled only for grids that do not have a scalar, | ||
| 262 | /// floating-point ValueType. | ||
| 263 | template<class GridType, typename InterruptT> | ||
| 264 | inline typename std::enable_if< | ||
| 265 | !std::is_floating_point<typename GridType::ValueType>::value, typename GridType::Ptr>::type | ||
| 266 | ✗ | doLevelSetRebuild(const GridType&, typename GridType::ValueType /*isovalue*/, | |
| 267 | typename GridType::ValueType /*exWidth*/, typename GridType::ValueType /*inWidth*/, | ||
| 268 | const math::Transform*, InterruptT*) | ||
| 269 | { | ||
| 270 | ✗ | OPENVDB_THROW(TypeError, | |
| 271 | "level set rebuild is supported only for scalar, floating-point grids"); | ||
| 272 | } | ||
| 273 | |||
| 274 | /// @endcond | ||
| 275 | //} | ||
| 276 | |||
| 277 | |||
| 278 | //////////////////////////////////////// | ||
| 279 | |||
| 280 | |||
| 281 | template<class GridType, typename InterruptT> | ||
| 282 | typename GridType::Ptr | ||
| 283 | 10 | levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth, | |
| 284 | const math::Transform* xform, InterruptT* interrupter) | ||
| 285 | { | ||
| 286 | using ValueT = typename GridType::ValueType; | ||
| 287 | ValueT | ||
| 288 | 10 | isovalue(zeroVal<ValueT>() + ValueT(iso)), | |
| 289 | 10 | exBandWidth(zeroVal<ValueT>() + ValueT(exWidth)), | |
| 290 | 10 | inBandWidth(zeroVal<ValueT>() + ValueT(inWidth)); | |
| 291 | |||
| 292 | 10 | return doLevelSetRebuild(grid, isovalue, exBandWidth, inBandWidth, xform, interrupter); | |
| 293 | } | ||
| 294 | |||
| 295 | |||
| 296 | template<class GridType> | ||
| 297 | typename GridType::Ptr | ||
| 298 | ✗ | levelSetRebuild(const GridType& grid, float iso, float exWidth, float inWidth, | |
| 299 | const math::Transform* xform) | ||
| 300 | { | ||
| 301 | using ValueT = typename GridType::ValueType; | ||
| 302 | ValueT | ||
| 303 | ✗ | isovalue(zeroVal<ValueT>() + ValueT(iso)), | |
| 304 | ✗ | exBandWidth(zeroVal<ValueT>() + ValueT(exWidth)), | |
| 305 | ✗ | inBandWidth(zeroVal<ValueT>() + ValueT(inWidth)); | |
| 306 | |||
| 307 | return doLevelSetRebuild<GridType, util::NullInterrupter>( | ||
| 308 | ✗ | grid, isovalue, exBandWidth, inBandWidth, xform, nullptr); | |
| 309 | } | ||
| 310 | |||
| 311 | |||
| 312 | template<class GridType> | ||
| 313 | typename GridType::Ptr | ||
| 314 | ✗ | levelSetRebuild(const GridType& grid, float iso, float halfVal, const math::Transform* xform) | |
| 315 | { | ||
| 316 | using ValueT = typename GridType::ValueType; | ||
| 317 | ValueT | ||
| 318 | ✗ | isovalue(zeroVal<ValueT>() + ValueT(iso)), | |
| 319 | ✗ | halfWidth(zeroVal<ValueT>() + ValueT(halfVal)); | |
| 320 | |||
| 321 | return doLevelSetRebuild<GridType, util::NullInterrupter>( | ||
| 322 | ✗ | grid, isovalue, halfWidth, halfWidth, xform, nullptr); | |
| 323 | } | ||
| 324 | |||
| 325 | |||
| 326 | //////////////////////////////////////// | ||
| 327 | |||
| 328 | |||
| 329 | // Explicit Template Instantiation | ||
| 330 | |||
| 331 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 332 | |||
| 333 | #ifdef OPENVDB_INSTANTIATE_LEVELSETREBUILD | ||
| 334 | #include <openvdb/util/ExplicitInstantiation.h> | ||
| 335 | #endif | ||
| 336 | |||
| 337 | #define _FUNCTION(TreeT) \ | ||
| 338 | Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, const math::Transform*) | ||
| 339 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 340 | #undef _FUNCTION | ||
| 341 | |||
| 342 | #define _FUNCTION(TreeT) \ | ||
| 343 | Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, float, const math::Transform*) | ||
| 344 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 345 | #undef _FUNCTION | ||
| 346 | |||
| 347 | #define _FUNCTION(TreeT) \ | ||
| 348 | Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, float, const math::Transform*, \ | ||
| 349 | util::NullInterrupter*) | ||
| 350 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 351 | #undef _FUNCTION | ||
| 352 | |||
| 353 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 354 | |||
| 355 | |||
| 356 | } // namespace tools | ||
| 357 | } // namespace OPENVDB_VERSION_NAME | ||
| 358 | } // namespace openvdb | ||
| 359 | |||
| 360 | #endif // OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED | ||
| 361 |