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