OpenVDB: openvdb/tools/LevelSetRebuild.h Source File

OpenVDB 12.1.0

Loading...
Searching...
No Matches
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
22#include <openvdb/tools/VolumeToMesh.h>
23#include <openvdb/tools/MeshToVolume.h>
24#include <openvdb/tools/Interpolation.h>
25#endif
26#include <openvdb/util/NullInterrupter.h>
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
38namespace openvdb {
39 OPENVDB_USE_VERSION_NAMESPACE
40namespace OPENVDB_VERSION_NAME {
41namespace 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.
59template<class GridType>
60typename GridType::Ptr
61levelSetRebuild(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.
79template<class GridType>
80typename GridType::Ptr
81levelSetRebuild(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.
100template<class GridType, typename InterruptT>
101typename GridType::Ptr
102levelSetRebuild(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
112namespace internal {
113
114class PointListTransform
115{
116public:
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
142private:
143 const PointList& mPointsIn;
144 std::vector<Vec3s> * const mPointsOut;
145 const math::Transform& mXform;
146};
147
148
149class PrimCpy
150{
151public:
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
199private:
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.
221template<class GridType, typename InterruptT>
222inline typename std::enable_if<
223 std::is_floating_point<typename GridType::ValueType>::value, typename GridType::Ptr>::type
224doLevelSetRebuild(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.
303template<class GridType, typename InterruptT>
304inline typename std::enable_if<
305 !std::is_floating_point<typename GridType::ValueType>::value, typename GridType::Ptr>::type
306doLevelSetRebuild(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
321template<class GridType, typename InterruptT>
322typename 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
336template<class GridType>
337typename 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
352template<class GridType>
353typename GridType::Ptr
354levelSetRebuild(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
374#include <openvdb/util/ExplicitInstantiation.h>
375#endif
376
377#define _FUNCTION(TreeT) \
378 Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, const math::Transform*)
379 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
380#undef _FUNCTION
381
382#define _FUNCTION(TreeT) \
383 Grid<TreeT>::Ptr levelSetRebuild(const Grid<TreeT>&, float, float, float, const math::Transform*)
384 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
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*)
390 OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION)
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
ExplicitInstantiation.h
Grid.h
Interpolation.h
OPENVDB_USE_ORACLE_IN_REBUILD
#define OPENVDB_USE_ORACLE_IN_REBUILD
Definition LevelSetRebuild.h:15
Math.h
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
MeshToVolume.h
Convert polygonal meshes that consist of quads and/or triangles into signed or unsigned distance fiel...
NullInterrupter.h
Transform.h
VolumeToMesh.h
Extract polygonal surfaces from scalar volumes.
openvdb::v12_1::math::Transform
Definition Transform.h:40
openvdb::v12_1::tools::levelSetRebuild
GridType::Ptr levelSetRebuild(const GridType &grid, float isovalue=0, float halfWidth=float(LEVEL_SET_HALF_WIDTH), const math::Transform *xform=nullptr)
Return a new grid of type GridType that contains a narrow-band level set representation of an isosurf...
Definition LevelSetRebuild.h:354
openvdb::v12_1::Vec4I
math::Vec4< Index32 > Vec4I
Definition Types.h:88
openvdb::v12_1::zeroVal
constexpr T zeroVal()
Return the value of type T that corresponds to zero.
Definition Math.h:70
openvdb::v12_1::Vec3I
math::Vec3< Index32 > Vec3I
Definition Types.h:73
openvdb
Definition Exceptions.h:13
Exceptions.h
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
Util.h
openvdb.h
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218
OPENVDB_REAL_TREE_INSTANTIATE
#define OPENVDB_REAL_TREE_INSTANTIATE(Function)
Definition version.h.in:162