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:
PointListTransform(const PointList & pointsIn,std::vector<Vec3s> & pointsOut,const math::Transform & xform)100     PointListTransform(const PointList& pointsIn, std::vector<Vec3s>& pointsOut,
101         const math::Transform& xform)
102         : mPointsIn(pointsIn)
103         , mPointsOut(&pointsOut)
104         , mXform(xform)
105     {
106     }
107 
runParallel()108     void runParallel()
109     {
110         tbb::parallel_for(tbb::blocked_range<size_t>(0, mPointsOut->size()), *this);
111     }
112 
runSerial()113     void runSerial()
114     {
115         (*this)(tbb::blocked_range<size_t>(0, mPointsOut->size()));
116     }
117 
operator()118     inline void operator()(const tbb::blocked_range<size_t>& range) const
119     {
120         for (size_t n = range.begin(); n < range.end(); ++n) {
121             (*mPointsOut)[n] = Vec3s(mXform.worldToIndex(mPointsIn[n]));
122         }
123     }
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:
PrimCpy(const PolygonPoolList & primsIn,const std::vector<size_t> & indexList,std::vector<Vec4I> & primsOut)135     PrimCpy(const PolygonPoolList& primsIn, const std::vector<size_t>& indexList,
136         std::vector<Vec4I>& primsOut)
137         : mPrimsIn(primsIn)
138         , mIndexList(indexList)
139         , mPrimsOut(&primsOut)
140     {
141     }
142 
runParallel()143     void runParallel()
144     {
145         tbb::parallel_for(tbb::blocked_range<size_t>(0, mIndexList.size()), *this);
146     }
147 
runSerial()148     void runSerial()
149     {
150         (*this)(tbb::blocked_range<size_t>(0, mIndexList.size()));
151     }
152 
operator()153     inline void operator()(const tbb::blocked_range<size_t>& range) const
154     {
155         openvdb::Vec4I quad;
156         quad[3] = openvdb::util::INVALID_IDX;
157         std::vector<Vec4I>& primsOut = *mPrimsOut;
158 
159         for (size_t n = range.begin(); n < range.end(); ++n) {
160             size_t index = mIndexList[n];
161             PolygonPool& polygons = mPrimsIn[n];
162 
163             // Copy quads
164             for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
165                 primsOut[index++] = polygons.quad(i);
166             }
167             polygons.clearQuads();
168 
169             // Copy triangles (adaptive mesh)
170             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             polygons.clearTriangles();
179         }
180     }
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
doLevelSetRebuild(const GridType & grid,typename GridType::ValueType iso,typename GridType::ValueType exWidth,typename GridType::ValueType inWidth,const math::Transform * xform,InterruptT * interrupter)207 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     tools::VolumeToMesh mesher(isovalue);
217     mesher(grid);
218 
219     math::Transform::Ptr transform = (xform != nullptr) ? xform->copy() : grid.transform().copy();
220 
221     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         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         std::vector<size_t> indexlist(mesher.polygonPoolListSize());
236 
237         for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
238             const openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
239             indexlist[n] = numPrimitives;
240             numPrimitives += polygons.numQuads();
241             numPrimitives += polygons.numTriangles();
242         }
243 
244         primitives.resize(numPrimitives);
245         internal::PrimCpy primCpy(polygonPoolList, indexlist, primitives);
246         primCpy.runParallel();
247     }
248 
249     QuadAndTriangleDataAdapter<Vec3s, Vec4I> mesh(points, primitives);
250 
251     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         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
doLevelSetRebuild(const GridType &,typename GridType::ValueType,typename GridType::ValueType,typename GridType::ValueType,const math::Transform *,InterruptT *)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
levelSetRebuild(const GridType & grid,float iso,float exWidth,float inWidth,const math::Transform * xform,InterruptT * interrupter)283 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         isovalue(zeroVal<ValueT>() + ValueT(iso)),
289         exBandWidth(zeroVal<ValueT>() + ValueT(exWidth)),
290         inBandWidth(zeroVal<ValueT>() + ValueT(inWidth));
291 
292     return doLevelSetRebuild(grid, isovalue, exBandWidth, inBandWidth, xform, interrupter);
293 }
294 
295 
296 template<class GridType>
297 typename GridType::Ptr
levelSetRebuild(const GridType & grid,float iso,float exWidth,float inWidth,const math::Transform * xform)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
levelSetRebuild(const GridType & grid,float iso,float halfVal,const math::Transform * xform)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