OpenVDB 14.0.0
Loading...
Searching...
No Matches
VolumeToMesh.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: Apache-2.0
3
4/// @file VolumeToMesh.h
5///
6/// @brief Extract polygonal surfaces from scalar volumes.
7///
8/// @author Mihai Alden
9
10#ifndef OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED
11#define OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED
12
13#include <openvdb/Platform.h>
14#include <openvdb/math/Operators.h> // for ISGradient
16#include <openvdb/util/Util.h> // for INVALID_IDX
17#include <openvdb/util/Assert.h>
18#include <openvdb/openvdb.h>
19
20#include <tbb/blocked_range.h>
21#include <tbb/parallel_for.h>
22#include <tbb/parallel_reduce.h>
23#include <tbb/task_arena.h>
24
25#include <cmath> // for std::isfinite()
26#include <cstring> // for std::memset
27#include <map>
28#include <memory>
29#include <set>
30#include <type_traits>
31#include <vector>
32
33
34namespace openvdb {
36namespace OPENVDB_VERSION_NAME {
37namespace tools {
38
39
40////////////////////////////////////////
41
42
43// Wrapper functions for the VolumeToMesh converter
44
45
46/// @brief Uniformly mesh any scalar grid that has a continuous isosurface.
47///
48/// @param grid a scalar grid to mesh
49/// @param points output list of world space points
50/// @param quads output quad index list
51/// @param isovalue determines which isosurface to mesh
52///
53/// @throw TypeError if @a grid does not have a scalar value type
54template<typename GridType>
55void
57 const GridType& grid,
58 std::vector<Vec3s>& points,
59 std::vector<Vec4I>& quads,
60 double isovalue = 0.0);
61
62
63/// @brief Adaptively mesh any scalar grid that has a continuous isosurface.
64/// @details When converting to polygons, the adaptivity threshold determines
65/// how closely the isosurface is matched by the resulting mesh. Higher
66/// thresholds will allow more variation in polygon size, using fewer
67/// polygons to express the surface. Triangles will only be created for
68/// areas of the mesh which hit the adaptivity threshold and can't be
69/// represented as quads.
70/// @note Do not use this method just to get a triangle mesh - use the above
71/// method and post process the quad index list.
72///
73/// @param grid a scalar grid to mesh
74/// @param points output list of world space points
75/// @param triangles output triangle index list
76/// @param quads output quad index list
77/// @param isovalue determines which isosurface to mesh
78/// @param adaptivity surface adaptivity threshold [0 to 1]
79/// @param relaxDisorientedTriangles toggle relaxing disoriented triangles during
80/// adaptive meshing.
81///
82/// @throw TypeError if @a grid does not have a scalar value type
83template<typename GridType>
84void
86 const GridType& grid,
87 std::vector<Vec3s>& points,
88 std::vector<Vec3I>& triangles,
89 std::vector<Vec4I>& quads,
90 double isovalue = 0.0,
91 double adaptivity = 0.0,
92 bool relaxDisorientedTriangles = true);
93
94
95////////////////////////////////////////
96
97
98/// @brief Polygon flags, used for reference based meshing.
100
101
102/// @brief Collection of quads and triangles
104{
105public:
106
107 inline PolygonPool();
108 inline PolygonPool(const size_t numQuads, const size_t numTriangles);
109
110 inline void copy(const PolygonPool& rhs);
111
112 inline void resetQuads(size_t size);
113 inline void clearQuads();
114
115 inline void resetTriangles(size_t size);
116 inline void clearTriangles();
117
118
119 // polygon accessor methods
120
121 const size_t& numQuads() const { return mNumQuads; }
122
123 openvdb::Vec4I& quad(size_t n) { return mQuads[n]; }
124 const openvdb::Vec4I& quad(size_t n) const { return mQuads[n]; }
125
126
127 const size_t& numTriangles() const { return mNumTriangles; }
128
129 openvdb::Vec3I& triangle(size_t n) { return mTriangles[n]; }
130 const openvdb::Vec3I& triangle(size_t n) const { return mTriangles[n]; }
131
132
133 // polygon flags accessor methods
134
135 char& quadFlags(size_t n) { return mQuadFlags[n]; }
136 const char& quadFlags(size_t n) const { return mQuadFlags[n]; }
137
138 char& triangleFlags(size_t n) { return mTriangleFlags[n]; }
139 const char& triangleFlags(size_t n) const { return mTriangleFlags[n]; }
140
141
142 // reduce the polygon containers, n has to
143 // be smaller than the current container size.
144
145 inline bool trimQuads(const size_t n, bool reallocate = false);
146 inline bool trimTriangles(const size_t n, bool reallocate = false);
147
148 OPENVDB_DEPRECATED_MESSAGE("Use trimTriangles() instead.")
149 inline bool trimTrinagles(const size_t n, bool reallocate = false)
150 {
151 return this->trimTriangles(n, reallocate);
152 }
153
154private:
155 // disallow copy by assignment
156 void operator=(const PolygonPool&) {}
157
158 size_t mNumQuads, mNumTriangles;
159 std::unique_ptr<openvdb::Vec4I[]> mQuads;
160 std::unique_ptr<openvdb::Vec3I[]> mTriangles;
161 std::unique_ptr<char[]> mQuadFlags, mTriangleFlags;
162};
163
164
165/// @{
166/// @brief Point and primitive list types.
167using PointList = std::unique_ptr<openvdb::Vec3s[]>;
168using PolygonPoolList = std::unique_ptr<PolygonPool[]>;
169/// @}
170
171
172////////////////////////////////////////
173
174
175/// @brief Mesh any scalar grid that has a continuous isosurface.
177{
178
179 /// @param isovalue Determines which isosurface to mesh.
180 /// @param adaptivity Adaptivity threshold [0 to 1]
181 /// @param relaxDisorientedTriangles Toggle relaxing disoriented triangles during
182 /// adaptive meshing.
183 VolumeToMesh(double isovalue = 0, double adaptivity = 0, bool relaxDisorientedTriangles = true);
184
185 //////////
186
187 /// @{
188 // Mesh data accessors
189
190 size_t pointListSize() const { return mPointListSize; }
191 PointList& pointList() { return mPoints; }
192 const PointList& pointList() const { return mPoints; }
193
194 size_t polygonPoolListSize() const { return mPolygonPoolListSize; }
195 PolygonPoolList& polygonPoolList() { return mPolygons; }
196 const PolygonPoolList& polygonPoolList() const { return mPolygons; }
197
198 std::vector<uint8_t>& pointFlags() { return mPointFlags; }
199 const std::vector<uint8_t>& pointFlags() const { return mPointFlags; }
200 /// @}
201
202
203 //////////
204
205
206 /// @brief Main call
207 /// @note Call with scalar typed grid.
208 template<typename InputGridType>
209 void operator()(const InputGridType&);
210
211
212 //////////
213
214
215 /// @brief When surfacing fractured SDF fragments, the original unfractured
216 /// SDF grid can be used to eliminate seam lines and tag polygons that are
217 /// coincident with the reference surface with the @c POLYFLAG_EXTERIOR
218 /// flag and polygons that are in proximity to the seam lines with the
219 /// @c POLYFLAG_FRACTURE_SEAM flag. (The performance cost for using this
220 /// reference based scheme compared to the regular meshing scheme is
221 /// approximately 15% for the first fragment and neglect-able for
222 /// subsequent fragments.)
223 ///
224 /// @note Attributes from the original asset such as uv coordinates, normals etc.
225 /// are typically transferred to polygons that are marked with the
226 /// @c POLYFLAG_EXTERIOR flag. Polygons that are not marked with this flag
227 /// are interior to reference surface and might need projected UV coordinates
228 /// or a different material. Polygons marked as @c POLYFLAG_FRACTURE_SEAM can
229 /// be used to drive secondary elements such as debris and dust in a FX pipeline.
230 ///
231 /// @param grid reference surface grid of @c GridT type.
232 /// @param secAdaptivity Secondary adaptivity threshold [0 to 1]. Used in regions
233 /// that do not exist in the reference grid. (Parts of the
234 /// fragment surface that are not coincident with the
235 /// reference surface.)
236 void setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity = 0);
237
238
239 /// @param mask A boolean grid whose active topology defines the region to mesh.
240 /// @param invertMask Toggle to mesh the complement of the mask.
241 /// @note The mask's tree configuration has to match @c GridT's tree configuration.
242 void setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask = false);
243
244 /// @param grid A scalar grid used as a spatial multiplier for the adaptivity threshold.
245 /// @note The grid's tree configuration has to match @c GridT's tree configuration.
246 void setSpatialAdaptivity(const GridBase::ConstPtr& grid);
247
248
249 /// @param tree A boolean tree whose active topology defines the adaptivity mask.
250 /// @note The tree configuration has to match @c GridT's tree configuration.
251 void setAdaptivityMask(const TreeBase::ConstPtr& tree);
252
253private:
254 // Disallow copying
256 VolumeToMesh& operator=(const VolumeToMesh&);
257
258
259 PointList mPoints;
260 PolygonPoolList mPolygons;
261
262 size_t mPointListSize, mSeamPointListSize, mPolygonPoolListSize;
263 double mIsovalue, mPrimAdaptivity, mSecAdaptivity;
264
265 GridBase::ConstPtr mRefGrid, mSurfaceMaskGrid, mAdaptivityGrid;
266 TreeBase::ConstPtr mAdaptivityMaskTree;
267
268 TreeBase::Ptr mRefSignTree, mRefIdxTree;
269
270 bool mInvertSurfaceMask, mRelaxDisorientedTriangles;
271
272 std::unique_ptr<uint32_t[]> mQuantizedSeamPoints;
273 std::vector<uint8_t> mPointFlags;
274}; // struct VolumeToMesh
275
276
277////////////////////////////////////////
278
279
280/// @brief Given a set of tangent elements, @c points with corresponding @c normals,
281/// this method returns the intersection point of all tangent elements.
282///
283/// @note Used to extract surfaces with sharp edges and corners from volume data,
284/// see the following paper for details: "Feature Sensitive Surface
285/// Extraction from Volume Data, Kobbelt et al. 2001".
287 const std::vector<Vec3d>& points,
288 const std::vector<Vec3d>& normals)
289{
290 using Mat3d = math::Mat3d;
291
292 Vec3d avgPos(0.0);
293
294 if (points.empty()) return avgPos;
295
296 for (size_t n = 0, N = points.size(); n < N; ++n) {
297 avgPos += points[n];
298 }
299
300 avgPos /= double(points.size());
301
302 // Unique components of the 3x3 A^TA matrix, where A is
303 // the matrix of normals.
304 double m00=0,m01=0,m02=0,
305 m11=0,m12=0,
306 m22=0;
307
308 // The rhs vector, A^Tb, where b = n dot p
309 Vec3d rhs(0.0);
310
311 for (size_t n = 0, N = points.size(); n < N; ++n) {
312
313 const Vec3d& n_ref = normals[n];
314
315 // A^TA
316 m00 += n_ref[0] * n_ref[0]; // diagonal
317 m11 += n_ref[1] * n_ref[1];
318 m22 += n_ref[2] * n_ref[2];
319
320 m01 += n_ref[0] * n_ref[1]; // Upper-tri
321 m02 += n_ref[0] * n_ref[2];
322 m12 += n_ref[1] * n_ref[2];
323
324 // A^Tb (centered around the origin)
325 rhs += n_ref * n_ref.dot(points[n] - avgPos);
326 }
327
328 Mat3d A(m00,m01,m02,
329 m01,m11,m12,
330 m02,m12,m22);
331
332 /*
333 // Inverse
334 const double det = A.det();
335 if (det > 0.01) {
336 Mat3d A_inv = A.adjoint();
337 A_inv *= (1.0 / det);
338
339 return avgPos + A_inv * rhs;
340 }
341 */
342
343 // Compute the pseudo inverse
344
345 math::Mat3d eigenVectors;
346 Vec3d eigenValues;
347
348 diagonalizeSymmetricMatrix(A, eigenVectors, eigenValues, 300);
349
351
352
353 double tolerance = std::max(std::abs(eigenValues[0]), std::abs(eigenValues[1]));
354 tolerance = std::max(tolerance, std::abs(eigenValues[2]));
355 tolerance *= 0.01;
356
357 int clamped = 0;
358 for (int i = 0; i < 3; ++i ) {
359 if (std::abs(eigenValues[i]) < tolerance) {
360 D[i][i] = 0.0;
361 ++clamped;
362 } else {
363 D[i][i] = 1.0 / eigenValues[i];
364 }
365 }
366
367 // Assemble the pseudo inverse and calc. the intersection point
368 if (clamped < 3) {
369 Mat3d pseudoInv = eigenVectors * D * eigenVectors.transpose();
370 return avgPos + pseudoInv * rhs;
371 }
372
373 return avgPos;
374}
375
376
377////////////////////////////////////////////////////////////////////////////////
378////////////////////////////////////////////////////////////////////////////////
379
380
381// Internal utility objects and implementation details
382
383/// @cond OPENVDB_DOCS_INTERNAL
384
385namespace volume_to_mesh_internal {
386
387template<typename ValueType>
388struct FillArray
389{
390 FillArray(ValueType* array, const ValueType& v) : mArray(array), mValue(v) { }
391
392 void operator()(const tbb::blocked_range<size_t>& range) const {
393 const ValueType v = mValue;
394 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
395 mArray[n] = v;
396 }
397 }
398
399 ValueType * const mArray;
400 const ValueType mValue;
401};
402
403
404template<typename ValueType>
405inline void
406fillArray(ValueType* array, const ValueType& val, const size_t length)
407{
408 const auto grainSize = std::max<size_t>(
409 length / tbb::this_task_arena::max_concurrency(), 1024);
410 const tbb::blocked_range<size_t> range(0, length, grainSize);
411 tbb::parallel_for(range, FillArray<ValueType>(array, val), tbb::simple_partitioner());
412}
413
414
415/// @brief Bit-flags used to classify cells.
416enum { SIGNS = 0xFF, EDGES = 0xE00, INSIDE = 0x100,
417 XEDGE = 0x200, YEDGE = 0x400, ZEDGE = 0x800, SEAM = 0x1000};
418
419
420/// @brief Used to quickly determine if a given cell is adaptable.
421const bool sAdaptable[256] = {
422 1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1,
423 1,0,1,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,0,1,
424 1,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
425 1,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,1,
426 1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,0,1,
427 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,1,0,0,0,1,
428 1,0,0,0,1,0,1,0,1,1,0,0,1,1,1,1,1,1,0,0,1,0,0,0,1,1,0,0,1,1,0,1,
429 1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1};
430
431
432/// @brief Contains the ambiguous face index for certain cell configuration.
433const unsigned char sAmbiguousFace[256] = {
434 0,0,0,0,0,5,0,0,0,0,5,0,0,0,0,0,0,0,1,0,0,5,1,0,4,0,0,0,4,0,0,0,
435 0,1,0,0,2,0,0,0,0,1,5,0,2,0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0,
436 0,0,2,2,0,5,0,0,3,3,0,0,0,0,0,0,6,6,0,0,6,0,0,0,0,0,0,0,0,0,0,0,
437 0,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
438 0,4,0,4,3,0,3,0,0,0,5,0,0,0,0,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0,
439 6,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
440 0,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
441 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
442
443
444/// @brief Lookup table for different cell sign configurations. The first entry specifies
445/// the total number of points that need to be generated inside a cell and the
446/// remaining 12 entries indicate different edge groups.
447const unsigned char sEdgeGroupTable[256][13] = {
448 {0,0,0,0,0,0,0,0,0,0,0,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0},
449 {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,0,1,1,0,0,0,0,0,0,0,1,0},{1,1,1,1,1,0,0,0,0,1,0,1,0},
450 {1,1,0,1,0,0,0,0,0,0,1,1,0},{1,0,0,1,1,0,0,0,0,1,1,1,0},{1,0,0,1,1,0,0,0,0,0,0,0,1},
451 {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,1,1,1,1,0,0,0,0,0,1,0,1},{1,0,1,1,0,0,0,0,0,1,1,0,1},
452 {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1},
453 {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,0,0,0,0,1,0,0,1,1,0,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0},
454 {1,1,1,0,0,1,0,0,1,1,1,0,0},{1,0,1,0,1,1,0,0,1,0,1,0,0},{2,0,1,1,0,2,0,0,2,2,0,1,0},
455 {1,1,1,1,1,1,0,0,1,0,0,1,0},{1,1,0,1,0,1,0,0,1,1,1,1,0},{1,0,0,1,1,1,0,0,1,0,1,1,0},
456 {1,0,0,1,1,1,0,0,1,1,0,0,1},{1,1,0,1,0,1,0,0,1,0,0,0,1},{2,2,1,1,2,1,0,0,1,2,1,0,1},
457 {1,0,1,1,0,1,0,0,1,0,1,0,1},{1,0,1,0,1,1,0,0,1,1,0,1,1},{1,1,1,0,0,1,0,0,1,0,0,1,1},
458 {2,1,0,0,1,2,0,0,2,1,2,2,2},{1,0,0,0,0,1,0,0,1,0,1,1,1},{1,0,0,0,0,1,1,0,0,0,1,0,0},
459 {1,1,0,0,1,1,1,0,0,1,1,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0},{1,0,1,0,1,1,1,0,0,1,0,0,0},
460 {1,0,1,1,0,1,1,0,0,0,1,1,0},{2,2,2,1,1,1,1,0,0,1,2,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0},
461 {1,0,0,1,1,1,1,0,0,1,0,1,0},{2,0,0,2,2,1,1,0,0,0,1,0,2},{1,1,0,1,0,1,1,0,0,1,1,0,1},
462 {1,1,1,1,1,1,1,0,0,0,0,0,1},{1,0,1,1,0,1,1,0,0,1,0,0,1},{1,0,1,0,1,1,1,0,0,0,1,1,1},
463 {2,1,1,0,0,2,2,0,0,2,1,2,2},{1,1,0,0,1,1,1,0,0,0,0,1,1},{1,0,0,0,0,1,1,0,0,1,0,1,1},
464 {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0},
465 {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,0,1,1,0,0,1,0,1,1,1,1,0},{2,1,1,2,2,0,2,0,2,0,1,2,0},
466 {1,1,0,1,0,0,1,0,1,1,0,1,0},{1,0,0,1,1,0,1,0,1,0,0,1,0},{1,0,0,1,1,0,1,0,1,1,1,0,1},
467 {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,1,2,2,1,0,2,0,2,1,0,0,2},{1,0,1,1,0,0,1,0,1,0,0,0,1},
468 {2,0,2,0,2,0,1,0,1,2,2,1,1},{2,2,2,0,0,0,1,0,1,0,2,1,1},{2,2,0,0,2,0,1,0,1,2,0,1,1},
469 {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,0,0,0,0,0,1,1,0,0,0,1,0},{2,1,0,0,1,0,2,2,0,1,0,2,0},
470 {1,1,1,0,0,0,1,1,0,0,1,1,0},{1,0,1,0,1,0,1,1,0,1,1,1,0},{1,0,1,1,0,0,1,1,0,0,0,0,0},
471 {1,1,1,1,1,0,1,1,0,1,0,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0},{1,0,0,1,1,0,1,1,0,1,1,0,0},
472 {1,0,0,1,1,0,1,1,0,0,0,1,1},{1,1,0,1,0,0,1,1,0,1,0,1,1},{2,1,2,2,1,0,1,1,0,0,1,2,1},
473 {2,0,1,1,0,0,2,2,0,2,2,1,2},{1,0,1,0,1,0,1,1,0,0,0,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1},
474 {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,0,0,0,0,0,1,1,0,1,1,0,1},{1,0,0,0,0,1,1,1,1,1,0,1,0},
475 {1,1,0,0,1,1,1,1,1,0,0,1,0},{2,1,1,0,0,2,2,1,1,1,2,1,0},{2,0,2,0,2,1,1,2,2,0,1,2,0},
476 {1,0,1,1,0,1,1,1,1,1,0,0,0},{2,2,2,1,1,2,2,1,1,0,0,0,0},{2,2,0,2,0,1,1,2,2,2,1,0,0},
477 {2,0,0,1,1,2,2,1,1,0,2,0,0},{2,0,0,1,1,1,1,2,2,1,0,1,2},{2,2,0,2,0,2,2,1,1,0,0,2,1},
478 {4,3,2,2,3,4,4,1,1,3,4,2,1},{3,0,2,2,0,1,1,3,3,0,1,2,3},{2,0,2,0,2,2,2,1,1,2,0,0,1},
479 {2,1,1,0,0,1,1,2,2,0,0,0,2},{3,1,0,0,1,2,2,3,3,1,2,0,3},{2,0,0,0,0,1,1,2,2,0,1,0,2},
480 {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,1,0,0,1,1,0,1,0,1,1,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0},
481 {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,0,1,1,0,1,0,1,0,0,1,0,0},{2,1,1,2,2,2,0,2,0,2,1,0,0},
482 {1,1,0,1,0,1,0,1,0,0,0,0,0},{1,0,0,1,1,1,0,1,0,1,0,0,0},{1,0,0,1,1,1,0,1,0,0,1,1,1},
483 {2,2,0,2,0,1,0,1,0,1,2,2,1},{2,2,1,1,2,2,0,2,0,0,0,1,2},{2,0,2,2,0,1,0,1,0,1,0,2,1},
484 {1,0,1,0,1,1,0,1,0,0,1,0,1},{2,2,2,0,0,1,0,1,0,1,2,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1},
485 {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,0,0,0,0,0,0,1,1,1,1,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0},
486 {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,0,1,0,1,0,0,1,1,0,0,1,0},{1,0,1,1,0,0,0,1,1,1,1,0,0},
487 {2,2,2,1,1,0,0,1,1,0,2,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0},{1,0,0,1,1,0,0,1,1,0,0,0,0},
488 {2,0,0,2,2,0,0,1,1,2,2,2,1},{2,1,0,1,0,0,0,2,2,0,1,1,2},{3,2,1,1,2,0,0,3,3,2,0,1,3},
489 {2,0,1,1,0,0,0,2,2,0,0,1,2},{2,0,1,0,1,0,0,2,2,1,1,0,2},{2,1,1,0,0,0,0,2,2,0,1,0,2},
490 {2,1,0,0,1,0,0,2,2,1,0,0,2},{1,0,0,0,0,0,0,1,1,0,0,0,1},{1,0,0,0,0,0,0,1,1,0,0,0,1},
491 {1,1,0,0,1,0,0,1,1,1,0,0,1},{2,1,1,0,0,0,0,2,2,0,1,0,2},{1,0,1,0,1,0,0,1,1,1,1,0,1},
492 {1,0,1,1,0,0,0,1,1,0,0,1,1},{2,1,1,2,2,0,0,1,1,1,0,1,2},{1,1,0,1,0,0,0,1,1,0,1,1,1},
493 {2,0,0,1,1,0,0,2,2,2,2,2,1},{1,0,0,1,1,0,0,1,1,0,0,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0},
494 {1,1,1,1,1,0,0,1,1,0,1,0,0},{1,0,1,1,0,0,0,1,1,1,1,0,0},{1,0,1,0,1,0,0,1,1,0,0,1,0},
495 {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0},{1,0,0,0,0,0,0,1,1,1,1,1,0},
496 {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1},{1,1,1,0,0,1,0,1,0,1,1,0,1},
497 {1,0,1,0,1,1,0,1,0,0,1,0,1},{1,0,1,1,0,1,0,1,0,1,0,1,1},{2,2,2,1,1,2,0,2,0,0,0,2,1},
498 {2,1,0,1,0,2,0,2,0,1,2,2,1},{2,0,0,2,2,1,0,1,0,0,1,1,2},{1,0,0,1,1,1,0,1,0,1,0,0,0},
499 {1,1,0,1,0,1,0,1,0,0,0,0,0},{2,1,2,2,1,2,0,2,0,1,2,0,0},{1,0,1,1,0,1,0,1,0,0,1,0,0},
500 {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0},{2,2,0,0,2,1,0,1,0,2,1,1,0},
501 {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,0,0,0,0,1,1,1,1,0,1,0,1},{2,1,0,0,1,2,1,1,2,2,1,0,1},
502 {1,1,1,0,0,1,1,1,1,0,0,0,1},{2,0,2,0,2,1,2,2,1,1,0,0,2},{2,0,1,1,0,1,2,2,1,0,1,2,1},
503 {4,1,1,3,3,2,4,4,2,2,1,4,3},{2,2,0,2,0,2,1,1,2,0,0,1,2},{3,0,0,1,1,2,3,3,2,2,0,3,1},
504 {1,0,0,1,1,1,1,1,1,0,1,0,0},{2,2,0,2,0,1,2,2,1,1,2,0,0},{2,2,1,1,2,2,1,1,2,0,0,0,0},
505 {2,0,1,1,0,2,1,1,2,2,0,0,0},{2,0,2,0,2,2,1,1,2,0,2,1,0},{3,1,1,0,0,3,2,2,3,3,1,2,0},
506 {2,1,0,0,1,1,2,2,1,0,0,2,0},{2,0,0,0,0,2,1,1,2,2,0,1,0},{1,0,0,0,0,0,1,1,0,1,1,0,1},
507 {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1},{1,0,1,0,1,0,1,1,0,0,0,0,1},
508 {2,0,2,2,0,0,1,1,0,2,2,1,2},{3,1,1,2,2,0,3,3,0,0,1,3,2},{2,1,0,1,0,0,2,2,0,1,0,2,1},
509 {2,0,0,1,1,0,2,2,0,0,0,2,1},{1,0,0,1,1,0,1,1,0,1,1,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0},
510 {2,2,1,1,2,0,1,1,0,2,0,0,0},{1,0,1,1,0,0,1,1,0,0,0,0,0},{2,0,1,0,1,0,2,2,0,1,1,2,0},
511 {2,1,1,0,0,0,2,2,0,0,1,2,0},{2,1,0,0,1,0,2,2,0,1,0,2,0},{1,0,0,0,0,0,1,1,0,0,0,1,0},
512 {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,1,0,0,1,0,1,0,1,1,0,1,1},{1,1,1,0,0,0,1,0,1,0,1,1,1},
513 {2,0,2,0,2,0,1,0,1,1,1,2,2},{1,0,1,1,0,0,1,0,1,0,0,0,1},{2,2,2,1,1,0,2,0,2,2,0,0,1},
514 {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,0,0,2,2,0,1,0,1,1,1,0,2},{1,0,0,1,1,0,1,0,1,0,0,1,0},
515 {1,1,0,1,0,0,1,0,1,1,0,1,0},{2,2,1,1,2,0,2,0,2,0,2,1,0},{2,0,2,2,0,0,1,0,1,1,1,2,0},
516 {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0},
517 {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,0,0,0,0,1,1,0,0,1,0,1,1},{1,1,0,0,1,1,1,0,0,0,0,1,1},
518 {2,2,2,0,0,1,1,0,0,2,1,2,2},{2,0,1,0,1,2,2,0,0,0,2,1,1},{1,0,1,1,0,1,1,0,0,1,0,0,1},
519 {2,1,1,2,2,1,1,0,0,0,0,0,2},{2,1,0,1,0,2,2,0,0,1,2,0,1},{2,0,0,2,2,1,1,0,0,0,1,0,2},
520 {1,0,0,1,1,1,1,0,0,1,0,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0},{3,1,2,2,1,3,3,0,0,1,3,2,0},
521 {2,0,1,1,0,2,2,0,0,0,2,1,0},{1,0,1,0,1,1,1,0,0,1,0,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0},
522 {2,2,0,0,2,1,1,0,0,2,1,0,0},{1,0,0,0,0,1,1,0,0,0,1,0,0},{1,0,0,0,0,1,0,0,1,0,1,1,1},
523 {2,2,0,0,2,1,0,0,1,1,2,2,2},{1,1,1,0,0,1,0,0,1,0,0,1,1},{2,0,1,0,1,2,0,0,2,2,0,1,1},
524 {1,0,1,1,0,1,0,0,1,0,1,0,1},{3,1,1,3,3,2,0,0,2,2,1,0,3},{1,1,0,1,0,1,0,0,1,0,0,0,1},
525 {2,0,0,2,2,1,0,0,1,1,0,0,2},{1,0,0,1,1,1,0,0,1,0,1,1,0},{2,1,0,1,0,2,0,0,2,2,1,1,0},
526 {2,1,2,2,1,1,0,0,1,0,0,2,0},{2,0,1,1,0,2,0,0,2,2,0,1,0},{1,0,1,0,1,1,0,0,1,0,1,0,0},
527 {2,1,1,0,0,2,0,0,2,2,1,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0},{1,0,0,0,0,1,0,0,1,1,0,0,0},
528 {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1},
529 {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,0,1,1,0,0,0,0,0,1,1,0,1},{2,1,1,2,2,0,0,0,0,0,1,0,2},
530 {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,0,0,1,1,0,0,0,0,0,0,0,1},{1,0,0,1,1,0,0,0,0,1,1,1,0},
531 {1,1,0,1,0,0,0,0,0,0,1,1,0},{2,1,2,2,1,0,0,0,0,1,0,2,0},{1,0,1,1,0,0,0,0,0,0,0,1,0},
532 {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0},
533 {0,0,0,0,0,0,0,0,0,0,0,0,0}};
534
535////////////////////////////////////////
536
537inline bool
538isPlanarQuad(
539 const Vec3d& p0, const Vec3d& p1,
540 const Vec3d& p2, const Vec3d& p3,
541 const double epsilon = 0.001)
542{
543 // compute representative plane
544 Vec3d normal = (p2-p0).cross(p1-p3);
545 normal.normalize();
546 const Vec3d centroid = (p0 + p1 + p2 + p3);
547 const double d = centroid.dot(normal) * 0.25;
548
549
550 // test vertice distance to plane
551 double absDist = std::abs(p0.dot(normal) - d);
552 if (absDist > epsilon) return false;
553
554 absDist = std::abs(p1.dot(normal) - d);
555 if (absDist > epsilon) return false;
556
557 absDist = std::abs(p2.dot(normal) - d);
558 if (absDist > epsilon) return false;
559
560 absDist = std::abs(p3.dot(normal) - d);
561 if (absDist > epsilon) return false;
562
563 return true;
564}
565
566
567////////////////////////////////////////
568
569
570/// @{
571/// @brief Utility methods for point quantization.
572
573enum {
574 MASK_FIRST_10_BITS = 0x000003FF,
575 MASK_DIRTY_BIT = 0x80000000,
576 MASK_INVALID_BIT = 0x40000000
577};
578
579inline uint32_t
580packPoint(const Vec3d& v)
581{
582 uint32_t data = 0;
583
584 // values are expected to be in the [0.0 to 1.0] range.
585 OPENVDB_ASSERT(!(v.x() > 1.0) && !(v.y() > 1.0) && !(v.z() > 1.0));
586 OPENVDB_ASSERT(!(v.x() < 0.0) && !(v.y() < 0.0) && !(v.z() < 0.0));
587
588 data |= (uint32_t(v.x() * 1023.0) & MASK_FIRST_10_BITS) << 20;
589 data |= (uint32_t(v.y() * 1023.0) & MASK_FIRST_10_BITS) << 10;
590 data |= (uint32_t(v.z() * 1023.0) & MASK_FIRST_10_BITS);
591
592 return data;
593}
594
595inline Vec3d
596unpackPoint(uint32_t data)
597{
598 Vec3d v;
599 v.z() = double(data & MASK_FIRST_10_BITS) * 0.0009775171;
600 data = data >> 10;
601 v.y() = double(data & MASK_FIRST_10_BITS) * 0.0009775171;
602 data = data >> 10;
603 v.x() = double(data & MASK_FIRST_10_BITS) * 0.0009775171;
604
605 return v;
606}
607
608/// @}
609
610////////////////////////////////////////
611
612template<typename T>
613inline bool isBoolValue() { return false; }
614
615template<>
616inline bool isBoolValue<bool>() { return true; }
617
618template<typename T>
619inline bool isInsideValue(T value, T isovalue) { return value < isovalue; }
620
621template<>
622inline bool isInsideValue<bool>(bool value, bool /*isovalue*/) { return value; }
623
624
625/// @brief Minor wrapper around the Leaf API. This was originally added to avoid
626// atomic access with delayed loading, however delayed loading is no longer supported.
627template <typename LeafT,
628 bool IsBool = std::is_same<typename LeafT::ValueType, bool>::value>
629struct LeafBufferAccessor
630{
631 using T = typename LeafT::ValueType;
632 LeafBufferAccessor(const LeafT& leaf) : mData(leaf.buffer().data()) {}
633 inline T get(const Index idx) const { return mData[idx]; }
634 const T* const mData;
635};
636
637template <typename LeafT>
638struct LeafBufferAccessor<LeafT, true>
639{
640 using T = bool;
641 LeafBufferAccessor(const LeafT& leaf) : mLeaf(leaf) {}
642 inline T get(const Index idx) const { return mLeaf.getValue(idx); }
643 const LeafT& mLeaf;
644};
645
646
647/// @brief Whether a coordinate does not lie at the positive edge of a leaf node.
648template <typename LeafT>
649bool isInternalLeafCoord(const Coord& ijk)
650{
651 return
652 ijk[0] < int(LeafT::DIM - 1) &&
653 ijk[1] < int(LeafT::DIM - 1) &&
654 ijk[2] < int(LeafT::DIM - 1);
655}
656
657
658/// @brief Extracts the eight corner values for a cell starting at the given @ijk coordinate.
659template<typename AccessorT, typename ValueT>
660inline void
661getCellVertexValues(const AccessorT& accessor,
662 Coord ijk,
663 std::array<ValueT, 8>& values)
664{
665 values[0] = ValueT(accessor.getValue(ijk)); // i, j, k
666 ++ijk[0];
667 values[1] = ValueT(accessor.getValue(ijk)); // i+1, j, k
668 ++ijk[2];
669 values[2] = ValueT(accessor.getValue(ijk)); // i+1, j, k+1
670 --ijk[0];
671 values[3] = ValueT(accessor.getValue(ijk)); // i, j, k+1
672 --ijk[2]; ++ijk[1];
673 values[4] = ValueT(accessor.getValue(ijk)); // i, j+1, k
674 ++ijk[0];
675 values[5] = ValueT(accessor.getValue(ijk)); // i+1, j+1, k
676 ++ijk[2];
677 values[6] = ValueT(accessor.getValue(ijk)); // i+1, j+1, k+1
678 --ijk[0];
679 values[7] = ValueT(accessor.getValue(ijk)); // i, j+1, k+1
680}
681
682
683/// @brief Extracts the eight corner values for a cell starting at the given @ijk coordinate.
684template<typename LeafT, typename ValueT>
685inline void
686getCellVertexValues(const LeafT& leaf,
687 const Index offset,
688 std::array<ValueT, 8>& values)
689{
690 const LeafBufferAccessor<LeafT> acc(leaf);
691
692 values[0] = ValueT(acc.get(offset)); // i, j, k
693 values[3] = ValueT(acc.get(offset + 1)); // i, j, k+1
694 values[4] = ValueT(acc.get(offset + LeafT::DIM)); // i, j+1, k
695 values[7] = ValueT(acc.get(offset + LeafT::DIM + 1)); // i, j+1, k+1
696 values[1] = ValueT(acc.get(offset + (LeafT::DIM * LeafT::DIM))); // i+1, j, k
697 values[2] = ValueT(acc.get(offset + (LeafT::DIM * LeafT::DIM) + 1)); // i+1, j, k+1
698 values[5] = ValueT(acc.get(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM)); // i+1, j+1, k
699 values[6] = ValueT(acc.get(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1)); // i+1, j+1, k+1
700}
701
702
703template<typename ValueType>
704inline uint8_t
705computeSignFlags(const std::array<ValueType, 8>& values, const ValueType iso)
706{
707 unsigned signs = 0;
708 signs |= isInsideValue(values[0], iso) ? 1u : 0u;
709 signs |= isInsideValue(values[1], iso) ? 2u : 0u;
710 signs |= isInsideValue(values[2], iso) ? 4u : 0u;
711 signs |= isInsideValue(values[3], iso) ? 8u : 0u;
712 signs |= isInsideValue(values[4], iso) ? 16u : 0u;
713 signs |= isInsideValue(values[5], iso) ? 32u : 0u;
714 signs |= isInsideValue(values[6], iso) ? 64u : 0u;
715 signs |= isInsideValue(values[7], iso) ? 128u : 0u;
716 return uint8_t(signs);
717}
718
719
720/// @brief General method that computes the cell-sign configuration at the given
721/// @c ijk coordinate.
722template<typename AccessorT>
723inline uint8_t
724evalCellSigns(const AccessorT& accessor, const Coord& ijk, typename AccessorT::ValueType iso)
725{
726 unsigned signs = 0;
727 Coord coord = ijk; // i, j, k
728 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 1u;
729 coord[0] += 1; // i+1, j, k
730 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 2u;
731 coord[2] += 1; // i+1, j, k+1
732 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 4u;
733 coord[0] = ijk[0]; // i, j, k+1
734 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 8u;
735 coord[1] += 1; coord[2] = ijk[2]; // i, j+1, k
736 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 16u;
737 coord[0] += 1; // i+1, j+1, k
738 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 32u;
739 coord[2] += 1; // i+1, j+1, k+1
740 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 64u;
741 coord[0] = ijk[0]; // i, j+1, k+1
742 if (isInsideValue(accessor.getValue(coord), iso)) signs |= 128u;
743 return uint8_t(signs);
744}
745
746
747/// @brief Leaf node optimized method that computes the cell-sign configuration
748/// at the given local @c offset
749template<typename LeafT>
750inline uint8_t
751evalCellSigns(const LeafT& leaf, const Index offset, typename LeafT::ValueType iso)
752{
753 const LeafBufferAccessor<LeafT> acc(leaf);
754
755 unsigned signs = 0;
756 if (isInsideValue(acc.get(offset), iso)) signs |= 1u; // i, j, k
757 if (isInsideValue(acc.get(offset + (LeafT::DIM * LeafT::DIM)), iso)) signs |= 2u; // i+1, j, k
758 if (isInsideValue(acc.get(offset + (LeafT::DIM * LeafT::DIM) + 1), iso)) signs |= 4u; // i+1, j, k+1
759 if (isInsideValue(acc.get(offset + 1), iso)) signs |= 8u; // i, j, k+1
760 if (isInsideValue(acc.get(offset + LeafT::DIM), iso)) signs |= 16u; // i, j+1, k
761 if (isInsideValue(acc.get(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM), iso)) signs |= 32u; // i+1, j+1, k
762 if (isInsideValue(acc.get(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1), iso)) signs |= 64u; // i+1, j+1, k+1
763 if (isInsideValue(acc.get(offset + LeafT::DIM + 1), iso)) signs |= 128u; // i, j+1, k+1
764 return uint8_t(signs);
765}
766
767
768/// @brief Used to correct topological ambiguities related to two adjacent cells
769/// that share an ambiguous face.
770template<class AccessorT>
771inline void
772correctCellSigns(uint8_t& signs,
773 const uint8_t face,
774 const AccessorT& acc,
775 Coord ijk,
776 const typename AccessorT::ValueType iso)
777{
778 switch (int(face)) {
779 case 1:
780 ijk[2] -= 1;
781 if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 3) signs = uint8_t(~signs);
782 break;
783 case 2:
784 ijk[0] += 1;
785 if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 4) signs = uint8_t(~signs);
786 break;
787 case 3:
788 ijk[2] += 1;
789 if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 1) signs = uint8_t(~signs);
790 break;
791 case 4:
792 ijk[0] -= 1;
793 if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 2) signs = uint8_t(~signs);
794 break;
795 case 5:
796 ijk[1] -= 1;
797 if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 6) signs = uint8_t(~signs);
798 break;
799 case 6:
800 ijk[1] += 1;
801 if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 5) signs = uint8_t(~signs);
802 break;
803 default:
804 break;
805 }
806}
807
808
809template<class AccessorT>
810inline bool
811isNonManifold(const AccessorT& accessor, const Coord& ijk,
812 typename AccessorT::ValueType isovalue, const int dim)
813{
814 const int hDim = dim >> 1;
815 bool m, p[8]; // Corner signs
816
817 Coord coord = ijk; // i, j, k
818 p[0] = isInsideValue(accessor.getValue(coord), isovalue);
819 coord[0] += dim; // i+dim, j, k
820 p[1] = isInsideValue(accessor.getValue(coord), isovalue);
821 coord[2] += dim; // i+dim, j, k+dim
822 p[2] = isInsideValue(accessor.getValue(coord), isovalue);
823 coord[0] = ijk[0]; // i, j, k+dim
824 p[3] = isInsideValue(accessor.getValue(coord), isovalue);
825 coord[1] += dim; coord[2] = ijk[2]; // i, j+dim, k
826 p[4] = isInsideValue(accessor.getValue(coord), isovalue);
827 coord[0] += dim; // i+dim, j+dim, k
828 p[5] = isInsideValue(accessor.getValue(coord), isovalue);
829 coord[2] += dim; // i+dim, j+dim, k+dim
830 p[6] = isInsideValue(accessor.getValue(coord), isovalue);
831 coord[0] = ijk[0]; // i, j+dim, k+dim
832 p[7] = isInsideValue(accessor.getValue(coord), isovalue);
833
834 // Check if the corner sign configuration is ambiguous
835 unsigned signs = 0;
836 if (p[0]) signs |= 1u;
837 if (p[1]) signs |= 2u;
838 if (p[2]) signs |= 4u;
839 if (p[3]) signs |= 8u;
840 if (p[4]) signs |= 16u;
841 if (p[5]) signs |= 32u;
842 if (p[6]) signs |= 64u;
843 if (p[7]) signs |= 128u;
844 if (!sAdaptable[signs]) return true;
845
846 // Manifold check
847
848 // Evaluate edges
849 const int i = ijk[0], ip = ijk[0] + hDim, ipp = ijk[0] + dim;
850 const int j = ijk[1], jp = ijk[1] + hDim, jpp = ijk[1] + dim;
851 const int k = ijk[2], kp = ijk[2] + hDim, kpp = ijk[2] + dim;
852
853 // edge 1
854 coord.reset(ip, j, k);
855 m = isInsideValue(accessor.getValue(coord), isovalue);
856 if (p[0] != m && p[1] != m) return true;
857
858 // edge 2
859 coord.reset(ipp, j, kp);
860 m = isInsideValue(accessor.getValue(coord), isovalue);
861 if (p[1] != m && p[2] != m) return true;
862
863 // edge 3
864 coord.reset(ip, j, kpp);
865 m = isInsideValue(accessor.getValue(coord), isovalue);
866 if (p[2] != m && p[3] != m) return true;
867
868 // edge 4
869 coord.reset(i, j, kp);
870 m = isInsideValue(accessor.getValue(coord), isovalue);
871 if (p[0] != m && p[3] != m) return true;
872
873 // edge 5
874 coord.reset(ip, jpp, k);
875 m = isInsideValue(accessor.getValue(coord), isovalue);
876 if (p[4] != m && p[5] != m) return true;
877
878 // edge 6
879 coord.reset(ipp, jpp, kp);
880 m = isInsideValue(accessor.getValue(coord), isovalue);
881 if (p[5] != m && p[6] != m) return true;
882
883 // edge 7
884 coord.reset(ip, jpp, kpp);
885 m = isInsideValue(accessor.getValue(coord), isovalue);
886 if (p[6] != m && p[7] != m) return true;
887
888 // edge 8
889 coord.reset(i, jpp, kp);
890 m = isInsideValue(accessor.getValue(coord), isovalue);
891 if (p[7] != m && p[4] != m) return true;
892
893 // edge 9
894 coord.reset(i, jp, k);
895 m = isInsideValue(accessor.getValue(coord), isovalue);
896 if (p[0] != m && p[4] != m) return true;
897
898 // edge 10
899 coord.reset(ipp, jp, k);
900 m = isInsideValue(accessor.getValue(coord), isovalue);
901 if (p[1] != m && p[5] != m) return true;
902
903 // edge 11
904 coord.reset(ipp, jp, kpp);
905 m = isInsideValue(accessor.getValue(coord), isovalue);
906 if (p[2] != m && p[6] != m) return true;
907
908
909 // edge 12
910 coord.reset(i, jp, kpp);
911 m = isInsideValue(accessor.getValue(coord), isovalue);
912 if (p[3] != m && p[7] != m) return true;
913
914
915 // Evaluate faces
916
917 // face 1
918 coord.reset(ip, jp, k);
919 m = isInsideValue(accessor.getValue(coord), isovalue);
920 if (p[0] != m && p[1] != m && p[4] != m && p[5] != m) return true;
921
922 // face 2
923 coord.reset(ipp, jp, kp);
924 m = isInsideValue(accessor.getValue(coord), isovalue);
925 if (p[1] != m && p[2] != m && p[5] != m && p[6] != m) return true;
926
927 // face 3
928 coord.reset(ip, jp, kpp);
929 m = isInsideValue(accessor.getValue(coord), isovalue);
930 if (p[2] != m && p[3] != m && p[6] != m && p[7] != m) return true;
931
932 // face 4
933 coord.reset(i, jp, kp);
934 m = isInsideValue(accessor.getValue(coord), isovalue);
935 if (p[0] != m && p[3] != m && p[4] != m && p[7] != m) return true;
936
937 // face 5
938 coord.reset(ip, j, kp);
939 m = isInsideValue(accessor.getValue(coord), isovalue);
940 if (p[0] != m && p[1] != m && p[2] != m && p[3] != m) return true;
941
942 // face 6
943 coord.reset(ip, jpp, kp);
944 m = isInsideValue(accessor.getValue(coord), isovalue);
945 if (p[4] != m && p[5] != m && p[6] != m && p[7] != m) return true;
946
947 // test cube center
948 coord.reset(ip, jp, kp);
949 m = isInsideValue(accessor.getValue(coord), isovalue);
950 if (p[0] != m && p[1] != m && p[2] != m && p[3] != m &&
951 p[4] != m && p[5] != m && p[6] != m && p[7] != m) return true;
952
953 return false;
954}
955
956
957////////////////////////////////////////
958
959
960template <class LeafType>
961inline void
962mergeVoxels(LeafType& leaf, const Coord& start, const int dim, const int regionId)
963{
964 Coord ijk;
965 const Coord end = start.offsetBy(dim);
966
967 for (ijk[0] = start[0]; ijk[0] < end[0]; ++ijk[0]) {
968 for (ijk[1] = start[1]; ijk[1] < end[1]; ++ijk[1]) {
969 for (ijk[2] = start[2]; ijk[2] < end[2]; ++ijk[2]) {
970 leaf.setValueOnly(ijk, regionId);
971 }
972 }
973 }
974}
975
976
977// Note that we must use ValueType::value_type or else Visual C++ gets confused
978// thinking that it is a constructor.
979template <class LeafType>
980inline bool
981isMergeable(const LeafType& leaf,
982 const Coord& start,
983 const int dim,
984 typename LeafType::ValueType::value_type adaptivity)
985{
986 if (adaptivity < 1e-6) return false;
987
988 using VecT = typename LeafType::ValueType;
989 Coord ijk;
990 const Coord end = start.offsetBy(dim);
991
992 std::vector<VecT> norms;
993 for (ijk[0] = start[0]; ijk[0] < end[0]; ++ijk[0]) {
994 for (ijk[1] = start[1]; ijk[1] < end[1]; ++ijk[1]) {
995 for (ijk[2] = start[2]; ijk[2] < end[2]; ++ijk[2]) {
996 if (!leaf.isValueOn(ijk)) continue;
997 norms.push_back(leaf.getValue(ijk));
998 }
999 }
1000 }
1001
1002 const size_t N = norms.size();
1003 for (size_t ni = 0; ni < N; ++ni) {
1004 VecT n_i = norms[ni];
1005 for (size_t nj = 0; nj < N; ++nj) {
1006 VecT n_j = norms[nj];
1007 if ((1.0 - n_i.dot(n_j)) > adaptivity) return false;
1008 }
1009 }
1010 return true;
1011}
1012
1013
1014////////////////////////////////////////
1015
1016
1017/// linear interpolation.
1018inline double evalZeroCrossing(double v0, double v1, double iso) { return (iso - v0) / (v1 - v0); }
1019
1020
1021/// @brief Computes the average cell point for a given edge group.
1022inline Vec3d
1023computePoint(const std::array<double, 8>& values,
1024 const unsigned char signs,
1025 const unsigned char edgeGroup,
1026 const double iso)
1027{
1028 Vec3d avg(0.0, 0.0, 0.0);
1029 int samples = 0;
1030
1031 if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1
1032 avg[0] += evalZeroCrossing(values[0], values[1], iso);
1033 ++samples;
1034 }
1035
1036 if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2
1037 avg[0] += 1.0;
1038 avg[2] += evalZeroCrossing(values[1], values[2], iso);
1039 ++samples;
1040 }
1041
1042 if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2
1043 avg[0] += evalZeroCrossing(values[3], values[2], iso);
1044 avg[2] += 1.0;
1045 ++samples;
1046 }
1047
1048 if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3
1049 avg[2] += evalZeroCrossing(values[0], values[3], iso);
1050 ++samples;
1051 }
1052
1053 if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5
1054 avg[0] += evalZeroCrossing(values[4], values[5], iso);
1055 avg[1] += 1.0;
1056 ++samples;
1057 }
1058
1059 if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6
1060 avg[0] += 1.0;
1061 avg[1] += 1.0;
1062 avg[2] += evalZeroCrossing(values[5], values[6], iso);
1063 ++samples;
1064 }
1065
1066 if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6
1067 avg[0] += evalZeroCrossing(values[7], values[6], iso);
1068 avg[1] += 1.0;
1069 avg[2] += 1.0;
1070 ++samples;
1071 }
1072
1073 if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7
1074 avg[1] += 1.0;
1075 avg[2] += evalZeroCrossing(values[4], values[7], iso);
1076 ++samples;
1077 }
1078
1079 if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4
1080 avg[1] += evalZeroCrossing(values[0], values[4], iso);
1081 ++samples;
1082 }
1083
1084 if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5
1085 avg[0] += 1.0;
1086 avg[1] += evalZeroCrossing(values[1], values[5], iso);
1087 ++samples;
1088 }
1089
1090 if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6
1091 avg[0] += 1.0;
1092 avg[1] += evalZeroCrossing(values[2], values[6], iso);
1093 avg[2] += 1.0;
1094 ++samples;
1095 }
1096
1097 if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7
1098 avg[1] += evalZeroCrossing(values[3], values[7], iso);
1099 avg[2] += 1.0;
1100 ++samples;
1101 }
1102
1103 if (samples > 1) {
1104 const double w = 1.0 / double(samples);
1105 avg *= w;
1106 }
1107
1108 return avg;
1109}
1110
1111
1112/// @brief Computes the average cell point for a given edge group, ignoring edge
1113/// samples present in the @c signsMask configuration.
1114inline int
1115computeMaskedPoint(Vec3d& avg,
1116 const std::array<double, 8>& values,
1117 const unsigned char signs,
1118 const unsigned char signsMask,
1119 const unsigned char edgeGroup,
1120 const double iso)
1121{
1122 avg = Vec3d(0.0, 0.0, 0.0);
1123 int samples = 0;
1124
1125 if (sEdgeGroupTable[signs][1] == edgeGroup &&
1126 sEdgeGroupTable[signsMask][1] == 0) { // Edged: 0 - 1
1127 avg[0] += evalZeroCrossing(values[0], values[1], iso);
1128 ++samples;
1129 }
1130
1131 if (sEdgeGroupTable[signs][2] == edgeGroup &&
1132 sEdgeGroupTable[signsMask][2] == 0) { // Edged: 1 - 2
1133 avg[0] += 1.0;
1134 avg[2] += evalZeroCrossing(values[1], values[2], iso);
1135 ++samples;
1136 }
1137
1138 if (sEdgeGroupTable[signs][3] == edgeGroup &&
1139 sEdgeGroupTable[signsMask][3] == 0) { // Edged: 3 - 2
1140 avg[0] += evalZeroCrossing(values[3], values[2], iso);
1141 avg[2] += 1.0;
1142 ++samples;
1143 }
1144
1145 if (sEdgeGroupTable[signs][4] == edgeGroup &&
1146 sEdgeGroupTable[signsMask][4] == 0) { // Edged: 0 - 3
1147 avg[2] += evalZeroCrossing(values[0], values[3], iso);
1148 ++samples;
1149 }
1150
1151 if (sEdgeGroupTable[signs][5] == edgeGroup &&
1152 sEdgeGroupTable[signsMask][5] == 0) { // Edged: 4 - 5
1153 avg[0] += evalZeroCrossing(values[4], values[5], iso);
1154 avg[1] += 1.0;
1155 ++samples;
1156 }
1157
1158 if (sEdgeGroupTable[signs][6] == edgeGroup &&
1159 sEdgeGroupTable[signsMask][6] == 0) { // Edged: 5 - 6
1160 avg[0] += 1.0;
1161 avg[1] += 1.0;
1162 avg[2] += evalZeroCrossing(values[5], values[6], iso);
1163 ++samples;
1164 }
1165
1166 if (sEdgeGroupTable[signs][7] == edgeGroup &&
1167 sEdgeGroupTable[signsMask][7] == 0) { // Edged: 7 - 6
1168 avg[0] += evalZeroCrossing(values[7], values[6], iso);
1169 avg[1] += 1.0;
1170 avg[2] += 1.0;
1171 ++samples;
1172 }
1173
1174 if (sEdgeGroupTable[signs][8] == edgeGroup &&
1175 sEdgeGroupTable[signsMask][8] == 0) { // Edged: 4 - 7
1176 avg[1] += 1.0;
1177 avg[2] += evalZeroCrossing(values[4], values[7], iso);
1178 ++samples;
1179 }
1180
1181 if (sEdgeGroupTable[signs][9] == edgeGroup &&
1182 sEdgeGroupTable[signsMask][9] == 0) { // Edged: 0 - 4
1183 avg[1] += evalZeroCrossing(values[0], values[4], iso);
1184 ++samples;
1185 }
1186
1187 if (sEdgeGroupTable[signs][10] == edgeGroup &&
1188 sEdgeGroupTable[signsMask][10] == 0) { // Edged: 1 - 5
1189 avg[0] += 1.0;
1190 avg[1] += evalZeroCrossing(values[1], values[5], iso);
1191 ++samples;
1192 }
1193
1194 if (sEdgeGroupTable[signs][11] == edgeGroup &&
1195 sEdgeGroupTable[signsMask][11] == 0) { // Edged: 2 - 6
1196 avg[0] += 1.0;
1197 avg[1] += evalZeroCrossing(values[2], values[6], iso);
1198 avg[2] += 1.0;
1199 ++samples;
1200 }
1201
1202 if (sEdgeGroupTable[signs][12] == edgeGroup &&
1203 sEdgeGroupTable[signsMask][12] == 0) { // Edged: 3 - 7
1204 avg[1] += evalZeroCrossing(values[3], values[7], iso);
1205 avg[2] += 1.0;
1206 ++samples;
1207 }
1208
1209 if (samples > 1) {
1210 const double w = 1.0 / double(samples);
1211 avg *= w;
1212 }
1213
1214 return samples;
1215}
1216
1217
1218/// @brief Computes the average cell point for a given edge group, by computing
1219/// convex weights based on the distance from the sample point @c p.
1220inline Vec3d
1221computeWeightedPoint(const Vec3d& p,
1222 const std::array<double, 8>& values,
1223 const unsigned char signs,
1224 const unsigned char edgeGroup,
1225 const double iso)
1226{
1227 std::vector<Vec3d> samples;
1228 samples.reserve(8);
1229
1230 Vec3d avg(0.0, 0.0, 0.0);
1231
1232 if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1
1233 avg[0] = evalZeroCrossing(values[0], values[1], iso);
1234 avg[1] = 0.0;
1235 avg[2] = 0.0;
1236
1237 samples.push_back(avg);
1238 }
1239
1240 if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2
1241 avg[0] = 1.0;
1242 avg[1] = 0.0;
1243 avg[2] = evalZeroCrossing(values[1], values[2], iso);
1244
1245 samples.push_back(avg);
1246 }
1247
1248 if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2
1249 avg[0] = evalZeroCrossing(values[3], values[2], iso);
1250 avg[1] = 0.0;
1251 avg[2] = 1.0;
1252
1253 samples.push_back(avg);
1254 }
1255
1256 if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3
1257 avg[0] = 0.0;
1258 avg[1] = 0.0;
1259 avg[2] = evalZeroCrossing(values[0], values[3], iso);
1260
1261 samples.push_back(avg);
1262 }
1263
1264 if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5
1265 avg[0] = evalZeroCrossing(values[4], values[5], iso);
1266 avg[1] = 1.0;
1267 avg[2] = 0.0;
1268
1269 samples.push_back(avg);
1270 }
1271
1272 if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6
1273 avg[0] = 1.0;
1274 avg[1] = 1.0;
1275 avg[2] = evalZeroCrossing(values[5], values[6], iso);
1276
1277 samples.push_back(avg);
1278 }
1279
1280 if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6
1281 avg[0] = evalZeroCrossing(values[7], values[6], iso);
1282 avg[1] = 1.0;
1283 avg[2] = 1.0;
1284
1285 samples.push_back(avg);
1286 }
1287
1288 if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7
1289 avg[0] = 0.0;
1290 avg[1] = 1.0;
1291 avg[2] = evalZeroCrossing(values[4], values[7], iso);
1292
1293 samples.push_back(avg);
1294 }
1295
1296 if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4
1297 avg[0] = 0.0;
1298 avg[1] = evalZeroCrossing(values[0], values[4], iso);
1299 avg[2] = 0.0;
1300
1301 samples.push_back(avg);
1302 }
1303
1304 if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5
1305 avg[0] = 1.0;
1306 avg[1] = evalZeroCrossing(values[1], values[5], iso);
1307 avg[2] = 0.0;
1308
1309 samples.push_back(avg);
1310 }
1311
1312 if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6
1313 avg[0] = 1.0;
1314 avg[1] = evalZeroCrossing(values[2], values[6], iso);
1315 avg[2] = 1.0;
1316
1317 samples.push_back(avg);
1318 }
1319
1320 if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7
1321 avg[0] = 0.0;
1322 avg[1] = evalZeroCrossing(values[3], values[7], iso);
1323 avg[2] = 1.0;
1324
1325 samples.push_back(avg);
1326 }
1327
1328 OPENVDB_ASSERT(!samples.empty());
1329 if (samples.size() == 1) {
1330 return samples.front();
1331 }
1332
1333 std::vector<double> weights;
1334 weights.reserve(samples.size());
1335
1336 for (const Vec3d& s : samples) {
1337 weights.emplace_back((s-p).lengthSqr());
1338 }
1339
1340 double minWeight = weights.front();
1341 double maxWeight = weights.front();
1342
1343 for (size_t i = 1, I = weights.size(); i < I; ++i) {
1344 minWeight = std::min(minWeight, weights[i]);
1345 maxWeight = std::max(maxWeight, weights[i]);
1346 }
1347
1348 const double offset = maxWeight + minWeight * 0.1;
1349 for (size_t i = 0, I = weights.size(); i < I; ++i) {
1350 weights[i] = offset - weights[i];
1351 }
1352
1353 double weightSum = 0.0;
1354 for (size_t i = 0, I = weights.size(); i < I; ++i) {
1355 weightSum += weights[i];
1356 }
1357
1358 avg.setZero();
1359 for (size_t i = 0, I = samples.size(); i < I; ++i) {
1360 avg += samples[i] * (weights[i] / weightSum);
1361 }
1362
1363 return avg;
1364}
1365
1366
1367/// @brief Computes the average cell points defined by the sign configuration
1368/// @c signs and the given corner values @c values.
1369inline size_t
1370computeCellPoints(std::array<Vec3d, 4>& points,
1371 const std::array<double, 8>& values,
1372 const unsigned char signs,
1373 const double iso)
1374{
1375 size_t offset = 0;
1376 for (size_t n = 1, N = sEdgeGroupTable[signs][0] + 1; n < N; ++n, ++offset) {
1377 OPENVDB_ASSERT(offset < 4);
1378 points[offset] = computePoint(values, signs, uint8_t(n), iso);
1379 }
1380 return offset;
1381}
1382
1383
1384/// @brief Given a sign configuration @c lhsSigns and an edge group @c groupId,
1385/// finds the corresponding edge group in a different sign configuration
1386/// @c rhsSigns. Returns -1 if no match is found.
1387inline int
1388matchEdgeGroup(unsigned char groupId, unsigned char lhsSigns, unsigned char rhsSigns)
1389{
1390 int id = -1;
1391 for (size_t i = 1; i <= 12; ++i) {
1392 if (sEdgeGroupTable[lhsSigns][i] == groupId && sEdgeGroupTable[rhsSigns][i] != 0) {
1393 id = sEdgeGroupTable[rhsSigns][i];
1394 break;
1395 }
1396 }
1397 return id;
1398}
1399
1400
1401/// @brief Computes the average cell points defined by the sign configuration
1402/// @c signs and the given corner values @c values. Combines data from
1403/// two different level sets to eliminate seam lines when meshing
1404/// fractured segments.
1405inline size_t
1406computeCellPoints(std::array<Vec3d, 4>& points,
1407 std::array<bool, 4>& weightedPointMask,
1408 const std::array<double, 8>& lhsValues,
1409 const std::array<double, 8>& rhsValues,
1410 const unsigned char lhsSigns,
1411 const unsigned char rhsSigns,
1412 const double iso,
1413 const size_t pointIdx,
1414 const uint32_t * seamPointArray)
1415{
1416 size_t offset = 0;
1417 for (size_t n = 1, N = sEdgeGroupTable[lhsSigns][0] + 1; n < N; ++n, ++offset)
1418 {
1419 OPENVDB_ASSERT(offset < 4);
1420 const int id = matchEdgeGroup(uint8_t(n), lhsSigns, rhsSigns);
1421
1422 if (id != -1) {
1423
1424 const unsigned char e = uint8_t(id);
1425 const uint32_t quantizedPoint = seamPointArray[pointIdx + (id - 1)];
1426
1427 if ((quantizedPoint & MASK_DIRTY_BIT) && !(quantizedPoint & MASK_INVALID_BIT)) {
1428 const Vec3d p = unpackPoint(quantizedPoint);
1429 points[offset] = computeWeightedPoint(p, rhsValues, rhsSigns, e, iso);
1430 weightedPointMask[offset] = true;
1431 } else {
1432 points[offset] = computePoint(rhsValues, rhsSigns, e, iso);
1433 weightedPointMask[offset] = false;
1434 }
1435
1436 } else {
1437 points[offset] = computePoint(lhsValues, lhsSigns, uint8_t(n), iso);
1438 weightedPointMask[offset] = false;
1439 }
1440 }
1441 return offset;
1442}
1443
1444
1445template <typename InputTreeType>
1446struct ComputePoints
1447{
1448 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
1449 using InputValueType = typename InputLeafNodeType::ValueType;
1450
1451 using Int16TreeType = typename InputTreeType::template ValueConverter<Int16>::Type;
1452 using Int16LeafNodeType = typename Int16TreeType::LeafNodeType;
1453
1454 using Index32TreeType = typename InputTreeType::template ValueConverter<Index32>::Type;
1455 using Index32LeafNodeType = typename Index32TreeType::LeafNodeType;
1456
1457 ComputePoints(Vec3s * pointArray,
1458 const InputTreeType& inputTree,
1459 const std::vector<Index32LeafNodeType*>& pointIndexLeafNodes,
1460 const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
1461 const std::unique_ptr<Index32[]>& leafNodeOffsets,
1462 const math::Transform& xform,
1463 const double iso);
1464
1465 void setRefData(const InputTreeType& refInputTree,
1466 const Index32TreeType& refPointIndexTree,
1467 const Int16TreeType& refSignFlagsTree,
1468 const uint32_t * quantizedSeamLinePoints,
1469 uint8_t * seamLinePointsFlags);
1470
1471 void operator()(const tbb::blocked_range<size_t>&) const;
1472
1473private:
1474 Vec3s * const mPoints;
1475 InputTreeType const * const mInputTree;
1476 Index32LeafNodeType * const * const mPointIndexNodes;
1477 Int16LeafNodeType const * const * const mSignFlagsNodes;
1478 Index32 const * const mNodeOffsets;
1479 math::Transform const mTransform;
1480 double const mIsovalue;
1481 // reference meshing data
1482 InputTreeType const * mRefInputTree;
1483 Index32TreeType const * mRefPointIndexTree;
1484 Int16TreeType const * mRefSignFlagsTree;
1485 uint32_t const * mQuantizedSeamLinePoints;
1486 uint8_t * mSeamLinePointsFlags;
1487}; // struct ComputePoints
1488
1489
1490template <typename InputTreeType>
1491ComputePoints<InputTreeType>::ComputePoints(
1492 Vec3s * pointArray,
1493 const InputTreeType& inputTree,
1494 const std::vector<Index32LeafNodeType*>& pointIndexLeafNodes,
1495 const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
1496 const std::unique_ptr<Index32[]>& leafNodeOffsets,
1497 const math::Transform& xform,
1498 const double iso)
1499 : mPoints(pointArray)
1500 , mInputTree(&inputTree)
1501 , mPointIndexNodes(pointIndexLeafNodes.data())
1502 , mSignFlagsNodes(signFlagsLeafNodes.data())
1503 , mNodeOffsets(leafNodeOffsets.get())
1504 , mTransform(xform)
1505 , mIsovalue(iso)
1506 , mRefInputTree(nullptr)
1507 , mRefPointIndexTree(nullptr)
1508 , mRefSignFlagsTree(nullptr)
1509 , mQuantizedSeamLinePoints(nullptr)
1510 , mSeamLinePointsFlags(nullptr)
1511{
1512}
1513
1514template <typename InputTreeType>
1515void
1516ComputePoints<InputTreeType>::setRefData(
1517 const InputTreeType& refInputTree,
1518 const Index32TreeType& refPointIndexTree,
1519 const Int16TreeType& refSignFlagsTree,
1520 const uint32_t * quantizedSeamLinePoints,
1521 uint8_t * seamLinePointsFlags)
1522{
1523 mRefInputTree = &refInputTree;
1524 mRefPointIndexTree = &refPointIndexTree;
1525 mRefSignFlagsTree = &refSignFlagsTree;
1526 mQuantizedSeamLinePoints = quantizedSeamLinePoints;
1527 mSeamLinePointsFlags = seamLinePointsFlags;
1528}
1529
1530template <typename InputTreeType>
1531void
1532ComputePoints<InputTreeType>::operator()(const tbb::blocked_range<size_t>& range) const
1533{
1534 using InputTreeAccessor = tree::ValueAccessor<const InputTreeType>;
1535 using Index32TreeAccessor = tree::ValueAccessor<const Index32TreeType>;
1536 using Int16TreeAccessor = tree::ValueAccessor<const Int16TreeType>;
1537
1538 using IndexType = typename Index32TreeType::ValueType;
1539
1540 using IndexArray = std::vector<Index>;
1541 using IndexArrayMap = std::map<IndexType, IndexArray>;
1542
1543 InputTreeAccessor inputAcc(*mInputTree);
1544
1545 Vec3d xyz;
1546 Coord ijk;
1547 std::array<Vec3d, 4> points;
1548 std::array<bool, 4> weightedPointMask;
1549 std::array<double, 8> values, refValues;
1550 const double iso = mIsovalue;
1551
1552 // reference data accessors
1553
1554 std::unique_ptr<InputTreeAccessor> refInputAcc;
1555 std::unique_ptr<Index32TreeAccessor> refPointIndexAcc;
1556 std::unique_ptr<Int16TreeAccessor> refSignFlagsAcc;
1557
1558 const bool hasReferenceData = mRefInputTree && mRefPointIndexTree && mRefSignFlagsTree;
1559
1560 if (hasReferenceData) {
1561 refInputAcc.reset(new InputTreeAccessor(*mRefInputTree));
1562 refPointIndexAcc.reset(new Index32TreeAccessor(*mRefPointIndexTree));
1563 refSignFlagsAcc.reset(new Int16TreeAccessor(*mRefSignFlagsTree));
1564 }
1565
1566 for (size_t n = range.begin(), N = range.end(); n != N; ++n)
1567 {
1568 Index32LeafNodeType& pointIndexNode = *mPointIndexNodes[n];
1569 const Coord& origin = pointIndexNode.origin();
1570
1571 const Int16LeafNodeType& signFlagsNode = *mSignFlagsNodes[n];
1572 const InputLeafNodeType * inputNode = inputAcc.probeConstLeaf(origin);
1573
1574 // get reference data
1575 const InputLeafNodeType * refInputNode = nullptr;
1576 const Index32LeafNodeType * refPointIndexNode = nullptr;
1577 const Int16LeafNodeType * refSignFlagsNode = nullptr;
1578
1579 if (hasReferenceData) {
1580 refInputNode = refInputAcc->probeConstLeaf(origin);
1581 refPointIndexNode = refPointIndexAcc->probeConstLeaf(origin);
1582 refSignFlagsNode = refSignFlagsAcc->probeConstLeaf(origin);
1583 }
1584
1585 IndexType pointOffset = IndexType(mNodeOffsets[n]);
1586 IndexArrayMap regions;
1587
1588 auto* const pidxData = pointIndexNode.buffer().data();
1589 const auto* const sfData = signFlagsNode.buffer().data();
1590
1591 for (auto it = pointIndexNode.beginValueOn(); it; ++it)
1592 {
1593 const Index offset = it.pos();
1594 IndexType& id = pidxData[offset];
1595
1596 if (id != 0) {
1597 if (id != IndexType(util::INVALID_IDX)) {
1598 regions[id].push_back(offset);
1599 }
1600 continue;
1601 }
1602
1603 id = pointOffset;
1604
1605 const Int16 flags = sfData[offset];
1606 const uint8_t signs = uint8_t(SIGNS & flags);
1607 uint8_t refSigns = 0;
1608
1609 if ((flags & SEAM) && refPointIndexNode && refSignFlagsNode) {
1610 if (refSignFlagsNode->isValueOn(offset)) {
1611 refSigns = uint8_t(SIGNS & refSignFlagsNode->getValue(offset));
1612 }
1613 }
1614
1615 ijk = Index32LeafNodeType::offsetToLocalCoord(offset);
1616
1617 const bool inclusiveCell = inputNode && isInternalLeafCoord<InputLeafNodeType>(ijk);
1618
1619 ijk += origin;
1620
1621 if (inclusiveCell) getCellVertexValues(*inputNode, offset, values);
1622 else getCellVertexValues(inputAcc, ijk, values);
1623
1624 size_t count, weightcount;
1625
1626 if (refSigns == 0) {
1627 count = computeCellPoints(points, values, signs, iso);
1628 weightcount = 0;
1629 } else {
1630 if (inclusiveCell && refInputNode) {
1631 getCellVertexValues(*refInputNode, offset, refValues);
1632 } else {
1633 getCellVertexValues(*refInputAcc, ijk, refValues);
1634 }
1635 count = computeCellPoints(points, weightedPointMask, values, refValues, signs, refSigns,
1636 iso, refPointIndexNode->getValue(offset), mQuantizedSeamLinePoints);
1637 weightcount = count;
1638 }
1639
1640 xyz = ijk.asVec3d();
1641
1642 for (size_t i = 0; i < count; ++i) {
1643
1644 Vec3d& point = points[i];
1645
1646 // Checks for both NaN and inf vertex positions, i.e. any value that is not finite.
1647 if (!std::isfinite(point[0]) ||
1648 !std::isfinite(point[1]) ||
1649 !std::isfinite(point[2]))
1650 {
1652 "VolumeToMesh encountered NaNs or infs in the input VDB!"
1653 " Hint: Check the input and consider using the \"Diagnostics\" tool "
1654 "to detect and resolve the NaNs.");
1655 }
1656
1657 point += xyz;
1658 point = mTransform.indexToWorld(point);
1659
1660 Vec3s& pos = mPoints[pointOffset];
1661 pos[0] = float(point[0]);
1662 pos[1] = float(point[1]);
1663 pos[2] = float(point[2]);
1664
1665 if (mSeamLinePointsFlags && weightcount && weightedPointMask[i]) {
1666 mSeamLinePointsFlags[pointOffset] = uint8_t(1);
1667 }
1668
1669 ++pointOffset;
1670 }
1671 }
1672
1673 // generate collapsed region points
1674 for (auto it = regions.begin(); it != regions.end(); ++it)
1675 {
1676 Vec3d avg(0.0);
1677
1678 const IndexArray& voxels = it->second;
1679 for (size_t i = 0, I = voxels.size(); i < I; ++i) {
1680
1681 const Index offset = voxels[i];
1682 ijk = Index32LeafNodeType::offsetToLocalCoord(offset);
1683
1684 const bool inclusiveCell = inputNode && isInternalLeafCoord<InputLeafNodeType>(ijk);
1685
1686 ijk += origin;
1687
1688 pidxData[offset] = pointOffset;
1689
1690 const uint8_t signs = uint8_t(SIGNS & sfData[offset]);
1691
1692 if (inclusiveCell) getCellVertexValues(*inputNode, offset, values);
1693 else getCellVertexValues(inputAcc, ijk, values);
1694
1695 computeCellPoints(points, values, signs, iso);
1696
1697 avg[0] += double(ijk[0]) + points[0][0];
1698 avg[1] += double(ijk[1]) + points[0][1];
1699 avg[2] += double(ijk[2]) + points[0][2];
1700 }
1701
1702 if (voxels.size() > 1) {
1703 const double w = 1.0 / double(voxels.size());
1704 avg *= w;
1705 }
1706
1707 avg = mTransform.indexToWorld(avg);
1708
1709 Vec3s& pos = mPoints[pointOffset];
1710 pos[0] = float(avg[0]);
1711 pos[1] = float(avg[1]);
1712 pos[2] = float(avg[2]);
1713
1714 ++pointOffset;
1715 }
1716 }
1717} // ComputePoints::operator()
1718
1719
1720////////////////////////////////////////
1721
1722
1723template <typename InputTreeType>
1724struct SeamLineWeights
1725{
1726 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
1727 using InputValueType = typename InputLeafNodeType::ValueType;
1728
1729 using Int16TreeType = typename InputTreeType::template ValueConverter<Int16>::Type;
1730 using Int16LeafNodeType = typename Int16TreeType::LeafNodeType;
1731
1732 using Index32TreeType = typename InputTreeType::template ValueConverter<Index32>::Type;
1733 using Index32LeafNodeType = typename Index32TreeType::LeafNodeType;
1734
1735 SeamLineWeights(const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
1736 const InputTreeType& inputTree,
1737 const Index32TreeType& refPointIndexTree,
1738 const Int16TreeType& refSignFlagsTree,
1739 uint32_t * quantizedPoints,
1740 InputValueType iso)
1741 : mSignFlagsNodes(signFlagsLeafNodes.data())
1742 , mInputTree(&inputTree)
1743 , mRefPointIndexTree(&refPointIndexTree)
1744 , mRefSignFlagsTree(&refSignFlagsTree)
1745 , mQuantizedPoints(quantizedPoints)
1746 , mIsovalue(iso)
1747 {
1748 }
1749
1750 void operator()(const tbb::blocked_range<size_t>& range) const
1751 {
1752 tree::ValueAccessor<const InputTreeType> inputTreeAcc(*mInputTree);
1753 tree::ValueAccessor<const Index32TreeType> pointIndexTreeAcc(*mRefPointIndexTree);
1754 tree::ValueAccessor<const Int16TreeType> signFlagsTreeAcc(*mRefSignFlagsTree);
1755
1756 std::array<double, 8> values;
1757 const double iso = double(mIsovalue);
1758 Coord ijk;
1759 Vec3d pos;
1760
1761 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
1762
1763 const Int16LeafNodeType& signFlagsNode = *mSignFlagsNodes[n];
1764 const Coord& origin = signFlagsNode.origin();
1765
1766 const Int16LeafNodeType * refSignNode = signFlagsTreeAcc.probeConstLeaf(origin);
1767 if (!refSignNode) continue;
1768
1769 const Index32LeafNodeType* refPointIndexNode =
1770 pointIndexTreeAcc.probeConstLeaf(origin);
1771 if (!refPointIndexNode) continue;
1772
1773 const InputLeafNodeType* inputNode = inputTreeAcc.probeConstLeaf(origin);
1774
1775 const auto* const sfData = signFlagsNode.buffer().data();
1776 const auto* const rfIdxData = refPointIndexNode->buffer().data();
1777 const auto* const rsData = refSignNode->buffer().data();
1778
1779 for (auto it = signFlagsNode.cbeginValueOn(); it; ++it)
1780 {
1781 const Index offset = it.pos();
1782 const Int16 flags = sfData[offset];
1783
1784 ijk = Index32LeafNodeType::offsetToLocalCoord(offset);
1785
1786 const bool inclusiveCell = inputNode && isInternalLeafCoord<InputLeafNodeType>(ijk);
1787
1788 ijk += origin;
1789
1790 if ((flags & SEAM) && refSignNode->isValueOn(offset)) {
1791
1792 const uint8_t lhsSigns = uint8_t(SIGNS & flags);
1793 const uint8_t rhsSigns = uint8_t(SIGNS & rsData[offset]);
1794
1795 if (inclusiveCell) getCellVertexValues(*inputNode, offset, values);
1796 else getCellVertexValues(inputTreeAcc, ijk, values);
1797
1798 for (unsigned i = 1, I = sEdgeGroupTable[lhsSigns][0] + 1; i < I; ++i) {
1799
1800 const int id = matchEdgeGroup(uint8_t(i), lhsSigns, rhsSigns);
1801
1802 if (id != -1) {
1803
1804 uint32_t& data = mQuantizedPoints[rfIdxData[offset] + (id - 1)];
1805
1806 if (!(data & MASK_DIRTY_BIT)) {
1807
1808 const int samples = computeMaskedPoint(
1809 pos, values, lhsSigns, rhsSigns, uint8_t(i), iso);
1810
1811 if (samples > 0) data = packPoint(pos);
1812 else data = MASK_INVALID_BIT;
1813
1814 data |= MASK_DIRTY_BIT;
1815 }
1816 }
1817 } // end point group loop
1818 }
1819 } // end value on loop
1820 } // end leaf node loop
1821 }
1822
1823private:
1824 Int16LeafNodeType const * const * const mSignFlagsNodes;
1825 InputTreeType const * const mInputTree;
1826 Index32TreeType const * const mRefPointIndexTree;
1827 Int16TreeType const * const mRefSignFlagsTree;
1828 uint32_t * const mQuantizedPoints;
1829 InputValueType const mIsovalue;
1830}; // struct SeamLineWeights
1831
1832
1833template <typename TreeType>
1834struct SetSeamLineFlags
1835{
1836 using LeafNodeType = typename TreeType::LeafNodeType;
1837
1838 SetSeamLineFlags(const std::vector<LeafNodeType*>& signFlagsLeafNodes,
1839 const TreeType& refSignFlagsTree)
1840 : mSignFlagsNodes(signFlagsLeafNodes.data())
1841 , mRefSignFlagsTree(&refSignFlagsTree)
1842 {
1843 }
1844
1845 void operator()(const tbb::blocked_range<size_t>& range) const
1846 {
1847 tree::ValueAccessor<const TreeType> refSignFlagsTreeAcc(*mRefSignFlagsTree);
1848
1849 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
1850
1851 LeafNodeType& signFlagsNode = *mSignFlagsNodes[n];
1852 const Coord& origin = signFlagsNode.origin();
1853
1854 const LeafNodeType* refSignNode = refSignFlagsTreeAcc.probeConstLeaf(origin);
1855 if (!refSignNode) continue;
1856
1857 const auto* const rsData = refSignNode->buffer().data();
1858 auto* const sfData = signFlagsNode.buffer().data();
1859
1860 for (auto it = signFlagsNode.cbeginValueOn(); it; ++it) {
1861 const Index offset = it.pos();
1862
1863 const uint8_t rhsSigns = uint8_t(rsData[offset] & SIGNS);
1864
1865 if (sEdgeGroupTable[rhsSigns][0] > 0) {
1866
1867 typename LeafNodeType::ValueType& value = sfData[offset];
1868 const uint8_t lhsSigns = uint8_t(value & SIGNS);
1869
1870 if (rhsSigns != lhsSigns) {
1871 value |= SEAM;
1872 }
1873 }
1874
1875 } // end value on loop
1876
1877 } // end leaf node loop
1878 }
1879
1880private:
1881 LeafNodeType * const * const mSignFlagsNodes;
1882 TreeType const * const mRefSignFlagsTree;
1883}; // struct SetSeamLineFlags
1884
1885
1886template <typename BoolTreeType, typename SignDataType>
1887struct TransferSeamLineFlags
1888{
1889 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
1890
1891 using SignDataTreeType = typename BoolTreeType::template ValueConverter<SignDataType>::Type;
1892 using SignDataLeafNodeType = typename SignDataTreeType::LeafNodeType;
1893
1894 TransferSeamLineFlags(const std::vector<SignDataLeafNodeType*>& signFlagsLeafNodes,
1895 const BoolTreeType& maskTree)
1896 : mSignFlagsNodes(signFlagsLeafNodes.data())
1897 , mMaskTree(&maskTree)
1898 {
1899 }
1900
1901 void operator()(const tbb::blocked_range<size_t>& range) const
1902 {
1903 tree::ValueAccessor<const BoolTreeType> maskAcc(*mMaskTree);
1904
1905 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
1906
1907 SignDataLeafNodeType& signFlagsNode = *mSignFlagsNodes[n];
1908 const Coord& origin = signFlagsNode.origin();
1909
1910 const BoolLeafNodeType * maskNode = maskAcc.probeConstLeaf(origin);
1911 if (!maskNode) continue;
1912
1913 auto* const sfData = signFlagsNode.buffer().data();
1914
1915 for (auto it = signFlagsNode.cbeginValueOn(); it; ++it) {
1916 const Index offset = it.pos();
1917
1918 if (maskNode->isValueOn(offset)) {
1919 sfData[offset] |= SEAM;
1920 }
1921 } // end value on loop
1922 } // end leaf node loop
1923 }
1924
1925private:
1926 SignDataLeafNodeType * const * const mSignFlagsNodes;
1927 BoolTreeType const * const mMaskTree;
1928}; // struct TransferSeamLineFlags
1929
1930
1931template <typename TreeType>
1932struct MaskSeamLineVoxels
1933{
1934 using LeafNodeType = typename TreeType::LeafNodeType;
1935 using BoolTreeType = typename TreeType::template ValueConverter<bool>::Type;
1936
1937 MaskSeamLineVoxels(const std::vector<LeafNodeType*>& signFlagsLeafNodes,
1938 const TreeType& signFlagsTree,
1939 BoolTreeType& mask)
1940 : mSignFlagsNodes(signFlagsLeafNodes.data())
1941 , mSignFlagsTree(&signFlagsTree)
1942 , mTempMask(false)
1943 , mMask(&mask)
1944 {
1945 }
1946
1947 MaskSeamLineVoxels(MaskSeamLineVoxels& rhs, tbb::split)
1948 : mSignFlagsNodes(rhs.mSignFlagsNodes)
1949 , mSignFlagsTree(rhs.mSignFlagsTree)
1950 , mTempMask(false)
1951 , mMask(&mTempMask)
1952 {
1953 }
1954
1955 void join(MaskSeamLineVoxels& rhs) { mMask->merge(*rhs.mMask); }
1956
1957 void operator()(const tbb::blocked_range<size_t>& range)
1958 {
1959 using ValueOnCIter = typename LeafNodeType::ValueOnCIter;
1960 using ValueType = typename LeafNodeType::ValueType;
1961
1962 tree::ValueAccessor<const TreeType> signFlagsAcc(*mSignFlagsTree);
1963 tree::ValueAccessor<BoolTreeType> maskAcc(*mMask);
1964 Coord ijk;
1965
1966 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
1967
1968 LeafNodeType& signFlagsNode = *mSignFlagsNodes[n];
1969 auto* const sfData = signFlagsNode.buffer().data();
1970
1971 for (ValueOnCIter it = signFlagsNode.cbeginValueOn(); it; ++it) {
1972
1973 const ValueType flags = sfData[it.pos()];
1974
1975 if (!(flags & SEAM) && (flags & EDGES)) {
1976
1977 ijk = it.getCoord();
1978
1979 bool isSeamLineVoxel = false;
1980
1981 if (flags & XEDGE) {
1982 ijk[1] -= 1;
1983 isSeamLineVoxel = (signFlagsAcc.getValue(ijk) & SEAM);
1984 ijk[2] -= 1;
1985 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
1986 ijk[1] += 1;
1987 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
1988 ijk[2] += 1;
1989 }
1990
1991 if (!isSeamLineVoxel && flags & YEDGE) {
1992 ijk[2] -= 1;
1993 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
1994 ijk[0] -= 1;
1995 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
1996 ijk[2] += 1;
1997 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
1998 ijk[0] += 1;
1999 }
2000
2001 if (!isSeamLineVoxel && flags & ZEDGE) {
2002 ijk[1] -= 1;
2003 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
2004 ijk[0] -= 1;
2005 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
2006 ijk[1] += 1;
2007 isSeamLineVoxel = isSeamLineVoxel || (signFlagsAcc.getValue(ijk) & SEAM);
2008 ijk[0] += 1;
2009 }
2010
2011 if (isSeamLineVoxel) {
2012 maskAcc.setValue(it.getCoord(), true);
2013 }
2014 }
2015 } // end value on loop
2016
2017 } // end leaf node loop
2018 }
2019
2020private:
2021 LeafNodeType * const * const mSignFlagsNodes;
2022 TreeType const * const mSignFlagsTree;
2023 BoolTreeType mTempMask;
2024 BoolTreeType * const mMask;
2025}; // struct MaskSeamLineVoxels
2026
2027
2028template<typename SignDataTreeType>
2029inline void
2030markSeamLineData(SignDataTreeType& signFlagsTree, const SignDataTreeType& refSignFlagsTree)
2031{
2032 using SignDataType = typename SignDataTreeType::ValueType;
2033 using SignDataLeafNodeType = typename SignDataTreeType::LeafNodeType;
2034 using BoolTreeType = typename SignDataTreeType::template ValueConverter<bool>::Type;
2035
2036 std::vector<SignDataLeafNodeType*> signFlagsLeafNodes;
2037 signFlagsTree.getNodes(signFlagsLeafNodes);
2038
2039 const tbb::blocked_range<size_t> nodeRange(0, signFlagsLeafNodes.size());
2040
2041 tbb::parallel_for(nodeRange,
2042 SetSeamLineFlags<SignDataTreeType>(signFlagsLeafNodes, refSignFlagsTree));
2043
2044 BoolTreeType seamLineMaskTree(false);
2045
2046 MaskSeamLineVoxels<SignDataTreeType>
2047 maskSeamLine(signFlagsLeafNodes, signFlagsTree, seamLineMaskTree);
2048
2049 tbb::parallel_reduce(nodeRange, maskSeamLine);
2050
2051 tbb::parallel_for(nodeRange,
2052 TransferSeamLineFlags<BoolTreeType, SignDataType>(signFlagsLeafNodes, seamLineMaskTree));
2053}
2054
2055
2056////////////////////////////////////////
2057
2058
2059template <typename InputGridType>
2060struct MergeVoxelRegions
2061{
2062 using InputTreeType = typename InputGridType::TreeType;
2063 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
2064 using InputValueType = typename InputLeafNodeType::ValueType;
2065
2066 using FloatTreeType = typename InputTreeType::template ValueConverter<float>::Type;
2067 using FloatLeafNodeType = typename FloatTreeType::LeafNodeType;
2068 using FloatGridType = Grid<FloatTreeType>;
2069
2070 using Int16TreeType = typename InputTreeType::template ValueConverter<Int16>::Type;
2071 using Int16LeafNodeType = typename Int16TreeType::LeafNodeType;
2072
2073 using Index32TreeType = typename InputTreeType::template ValueConverter<Index32>::Type;
2074 using Index32LeafNodeType = typename Index32TreeType::LeafNodeType;
2075
2076 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
2077 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
2078
2079 using MaskTreeType = typename InputTreeType::template ValueConverter<ValueMask>::Type;
2080 using MaskLeafNodeType = typename MaskTreeType::LeafNodeType;
2081
2082 MergeVoxelRegions(const InputGridType& inputGrid,
2083 const Index32TreeType& pointIndexTree,
2084 const std::vector<Index32LeafNodeType*>& pointIndexLeafNodes,
2085 const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
2086 InputValueType iso,
2087 float adaptivity,
2088 bool invertSurfaceOrientation);
2089
2090 void setSpatialAdaptivity(const FloatGridType& grid)
2091 {
2092 mSpatialAdaptivityTree = &grid.tree();
2093 mSpatialAdaptivityTransform = &grid.transform();
2094 }
2095
2096 void setAdaptivityMask(const BoolTreeType& mask)
2097 {
2098 mMaskTree = &mask;
2099 }
2100
2101 void setRefSignFlagsData(const Int16TreeType& signFlagsData, float internalAdaptivity)
2102 {
2103 mRefSignFlagsTree = &signFlagsData;
2104 mInternalAdaptivity = internalAdaptivity;
2105 }
2106
2107 void operator()(const tbb::blocked_range<size_t>&) const;
2108
2109private:
2110 InputTreeType const * const mInputTree;
2111 math::Transform const * const mInputTransform;
2112
2113 Index32TreeType const * const mPointIndexTree;
2114 Index32LeafNodeType * const * const mPointIndexNodes;
2115 Int16LeafNodeType const * const * const mSignFlagsNodes;
2116
2117 InputValueType mIsovalue;
2118 float mSurfaceAdaptivity, mInternalAdaptivity;
2119 bool mInvertSurfaceOrientation;
2120
2121 FloatTreeType const * mSpatialAdaptivityTree;
2122 BoolTreeType const * mMaskTree;
2123 Int16TreeType const * mRefSignFlagsTree;
2124 math::Transform const * mSpatialAdaptivityTransform;
2125}; // struct MergeVoxelRegions
2126
2127
2128template <typename InputGridType>
2129MergeVoxelRegions<InputGridType>::MergeVoxelRegions(
2130 const InputGridType& inputGrid,
2131 const Index32TreeType& pointIndexTree,
2132 const std::vector<Index32LeafNodeType*>& pointIndexLeafNodes,
2133 const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
2134 InputValueType iso,
2135 float adaptivity,
2136 bool invertSurfaceOrientation)
2137 : mInputTree(&inputGrid.tree())
2138 , mInputTransform(&inputGrid.transform())
2139 , mPointIndexTree(&pointIndexTree)
2140 , mPointIndexNodes(pointIndexLeafNodes.data())
2141 , mSignFlagsNodes(signFlagsLeafNodes.data())
2142 , mIsovalue(iso)
2143 , mSurfaceAdaptivity(adaptivity)
2144 , mInternalAdaptivity(adaptivity)
2145 , mInvertSurfaceOrientation(invertSurfaceOrientation)
2146 , mSpatialAdaptivityTree(nullptr)
2147 , mMaskTree(nullptr)
2148 , mRefSignFlagsTree(nullptr)
2149 , mSpatialAdaptivityTransform(nullptr)
2150{
2151}
2152
2153
2154template <typename InputGridType>
2155void
2156MergeVoxelRegions<InputGridType>::operator()(const tbb::blocked_range<size_t>& range) const
2157{
2158 using Vec3sType = math::Vec3<float>;
2159 using Vec3sLeafNodeType = typename InputLeafNodeType::template ValueConverter<Vec3sType>::Type;
2160
2161 using InputTreeAccessor = tree::ValueAccessor<const InputTreeType>;
2162 using FloatTreeAccessor = tree::ValueAccessor<const FloatTreeType>;
2163 using Index32TreeAccessor = tree::ValueAccessor<const Index32TreeType>;
2164 using Int16TreeAccessor = tree::ValueAccessor<const Int16TreeType>;
2165 using BoolTreeAccessor = tree::ValueAccessor<const BoolTreeType>;
2166
2167 std::unique_ptr<FloatTreeAccessor> spatialAdaptivityAcc;
2168 if (mSpatialAdaptivityTree && mSpatialAdaptivityTransform) {
2169 spatialAdaptivityAcc.reset(new FloatTreeAccessor(*mSpatialAdaptivityTree));
2170 }
2171
2172 std::unique_ptr<BoolTreeAccessor> maskAcc;
2173 if (mMaskTree) {
2174 maskAcc.reset(new BoolTreeAccessor(*mMaskTree));
2175 }
2176
2177 std::unique_ptr<Int16TreeAccessor> refSignFlagsAcc;
2178 if (mRefSignFlagsTree) {
2179 refSignFlagsAcc.reset(new Int16TreeAccessor(*mRefSignFlagsTree));
2180 }
2181
2182 InputTreeAccessor inputAcc(*mInputTree);
2183 Index32TreeAccessor pointIndexAcc(*mPointIndexTree);
2184
2185 MaskLeafNodeType mask;
2186
2187 const bool invertGradientDir = mInvertSurfaceOrientation || isBoolValue<InputValueType>();
2188 std::unique_ptr<Vec3sLeafNodeType> gradientNode;
2189
2190 Coord ijk, end;
2191 const int LeafDim = InputLeafNodeType::DIM;
2192
2193 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
2194
2195 mask.setValuesOff();
2196
2197 const Int16LeafNodeType& signFlagsNode = *mSignFlagsNodes[n];
2198 Index32LeafNodeType& pointIndexNode = *mPointIndexNodes[n];
2199
2200 const Coord& origin = pointIndexNode.origin();
2201 end = origin.offsetBy(LeafDim);
2202
2203 // Mask off seam line adjacent voxels
2204 if (maskAcc) {
2205 const BoolLeafNodeType* maskLeaf = maskAcc->probeConstLeaf(origin);
2206 if (maskLeaf != nullptr) {
2207 for (auto it = maskLeaf->cbeginValueOn(); it; ++it)
2208 {
2209 mask.setActiveState(it.getCoord() & ~1u, true);
2210 }
2211 }
2212 }
2213
2214 float adaptivity = (refSignFlagsAcc && !refSignFlagsAcc->probeConstLeaf(origin)) ?
2215 mInternalAdaptivity : mSurfaceAdaptivity;
2216
2217 bool useGradients = adaptivity < 1.0f;
2218
2219 // Set region adaptivity
2220 FloatLeafNodeType adaptivityLeaf(origin, adaptivity);
2221
2222 if (spatialAdaptivityAcc) {
2223 useGradients = false;
2224 for (Index offset = 0; offset < FloatLeafNodeType::NUM_VALUES; ++offset) {
2225 ijk = adaptivityLeaf.offsetToGlobalCoord(offset);
2226 ijk = mSpatialAdaptivityTransform->worldToIndexCellCentered(
2227 mInputTransform->indexToWorld(ijk));
2228 float weight = spatialAdaptivityAcc->getValue(ijk);
2229 float adaptivityValue = weight * adaptivity;
2230 if (adaptivityValue < 1.0f) useGradients = true;
2231 adaptivityLeaf.setValueOnly(offset, adaptivityValue);
2232 }
2233 }
2234
2235 // Mask off ambiguous voxels
2236 for (auto it = signFlagsNode.cbeginValueOn(); it; ++it) {
2237 const Int16 flags = it.getValue();
2238 const unsigned char signs = static_cast<unsigned char>(SIGNS & int(flags));
2239
2240 if ((flags & SEAM) || !sAdaptable[signs] || sEdgeGroupTable[signs][0] > 1) {
2241
2242 mask.setActiveState(it.getCoord() & ~1u, true);
2243
2244 } else if (flags & EDGES) {
2245
2246 bool maskRegion = false;
2247
2248 ijk = it.getCoord();
2249 if (!pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2250
2251 if (!maskRegion && flags & XEDGE) {
2252 ijk[1] -= 1;
2253 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2254 ijk[2] -= 1;
2255 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2256 ijk[1] += 1;
2257 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2258 ijk[2] += 1;
2259 }
2260
2261 if (!maskRegion && flags & YEDGE) {
2262 ijk[2] -= 1;
2263 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2264 ijk[0] -= 1;
2265 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2266 ijk[2] += 1;
2267 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2268 ijk[0] += 1;
2269 }
2270
2271 if (!maskRegion && flags & ZEDGE) {
2272 ijk[1] -= 1;
2273 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2274 ijk[0] -= 1;
2275 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2276 ijk[1] += 1;
2277 if (!maskRegion && !pointIndexAcc.isValueOn(ijk)) maskRegion = true;
2278 ijk[0] += 1;
2279 }
2280
2281 if (maskRegion) {
2282 mask.setActiveState(it.getCoord() & ~1u, true);
2283 }
2284 }
2285 }
2286
2287 // Mask off topologically ambiguous 2x2x2 voxel sub-blocks
2288 int dim = 2;
2289 for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) {
2290 for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) {
2291 for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) {
2292 if (!mask.isValueOn(ijk) && isNonManifold(inputAcc, ijk, mIsovalue, dim)) {
2293 mask.setActiveState(ijk, true);
2294 }
2295 }
2296 }
2297 }
2298
2299 // Compute the gradient for the remaining voxels
2300
2301 if (useGradients) {
2302
2303 if (gradientNode) {
2304 gradientNode->setValuesOff();
2305 } else {
2306 gradientNode.reset(new Vec3sLeafNodeType());
2307 }
2308
2309 for (auto it = signFlagsNode.cbeginValueOn(); it; ++it)
2310 {
2311 ijk = it.getCoord();
2312 if (!mask.isValueOn(ijk & ~1u))
2313 {
2314 Vec3sType dir(math::ISGradient<math::CD_2ND>::result(inputAcc, ijk));
2315 dir.normalize();
2316
2317 if (invertGradientDir) {
2318 dir = -dir;
2319 }
2320
2321 gradientNode->setValueOn(it.pos(), dir);
2322 }
2323 }
2324 }
2325
2326 // Merge regions
2327 int regionId = 1;
2328 for ( ; dim <= LeafDim; dim = dim << 1) {
2329 const unsigned coordMask = ~((dim << 1) - 1);
2330 for (ijk[0] = origin[0]; ijk[0] < end[0]; ijk[0] += dim) {
2331 for (ijk[1] = origin[1]; ijk[1] < end[1]; ijk[1] += dim) {
2332 for (ijk[2] = origin[2]; ijk[2] < end[2]; ijk[2] += dim) {
2333
2334 adaptivity = adaptivityLeaf.getValue(ijk);
2335
2336 if (mask.isValueOn(ijk)
2337 || isNonManifold(inputAcc, ijk, mIsovalue, dim)
2338 || (useGradients && !isMergeable(*gradientNode, ijk, dim, adaptivity)))
2339 {
2340 mask.setActiveState(ijk & coordMask, true);
2341 } else {
2342 mergeVoxels(pointIndexNode, ijk, dim, regionId++);
2343 }
2344 }
2345 }
2346 }
2347 }
2348 }
2349} // MergeVoxelRegions::operator()
2350
2351
2352////////////////////////////////////////
2353
2354
2355// Constructs qudas
2356struct UniformPrimBuilder
2357{
2358 UniformPrimBuilder(): mIdx(0), mPolygonPool(nullptr) {}
2359
2360 void init(const size_t upperBound, PolygonPool& quadPool)
2361 {
2362 mPolygonPool = &quadPool;
2363 mPolygonPool->resetQuads(upperBound);
2364 mIdx = 0;
2365 }
2366
2367 template<typename IndexType>
2368 void addPrim(const math::Vec4<IndexType>& verts, bool reverse, char flags = 0)
2369 {
2370 if (!reverse) {
2371 mPolygonPool->quad(mIdx) = verts;
2372 } else {
2373 Vec4I& quad = mPolygonPool->quad(mIdx);
2374 quad[0] = verts[3];
2375 quad[1] = verts[2];
2376 quad[2] = verts[1];
2377 quad[3] = verts[0];
2378 }
2379 mPolygonPool->quadFlags(mIdx) = flags;
2380 ++mIdx;
2381 }
2382
2383 void done()
2384 {
2385 mPolygonPool->trimQuads(mIdx);
2386 }
2387
2388private:
2389 size_t mIdx;
2390 PolygonPool* mPolygonPool;
2391};
2392
2393
2394// Constructs qudas and triangles
2395struct AdaptivePrimBuilder
2396{
2397 AdaptivePrimBuilder() : mQuadIdx(0), mTriangleIdx(0), mPolygonPool(nullptr) {}
2398
2399 void init(const size_t upperBound, PolygonPool& polygonPool)
2400 {
2401 mPolygonPool = &polygonPool;
2402 mPolygonPool->resetQuads(upperBound);
2403 mPolygonPool->resetTriangles(upperBound);
2404
2405 mQuadIdx = 0;
2406 mTriangleIdx = 0;
2407 }
2408
2409 template<typename IndexType>
2410 void addPrim(const math::Vec4<IndexType>& verts, bool reverse, char flags = 0)
2411 {
2412 if (verts[0] != verts[1] && verts[0] != verts[2] && verts[0] != verts[3]
2413 && verts[1] != verts[2] && verts[1] != verts[3] && verts[2] != verts[3]) {
2414 mPolygonPool->quadFlags(mQuadIdx) = flags;
2415 addQuad(verts, reverse);
2416 } else if (
2417 verts[0] == verts[3] &&
2418 verts[1] != verts[2] &&
2419 verts[1] != verts[0] &&
2420 verts[2] != verts[0]) {
2421 mPolygonPool->triangleFlags(mTriangleIdx) = flags;
2422 addTriangle(verts[0], verts[1], verts[2], reverse);
2423 } else if (
2424 verts[1] == verts[2] &&
2425 verts[0] != verts[3] &&
2426 verts[0] != verts[1] &&
2427 verts[3] != verts[1]) {
2428 mPolygonPool->triangleFlags(mTriangleIdx) = flags;
2429 addTriangle(verts[0], verts[1], verts[3], reverse);
2430 } else if (
2431 verts[0] == verts[1] &&
2432 verts[2] != verts[3] &&
2433 verts[2] != verts[0] &&
2434 verts[3] != verts[0]) {
2435 mPolygonPool->triangleFlags(mTriangleIdx) = flags;
2436 addTriangle(verts[0], verts[2], verts[3], reverse);
2437 } else if (
2438 verts[2] == verts[3] &&
2439 verts[0] != verts[1] &&
2440 verts[0] != verts[2] &&
2441 verts[1] != verts[2]) {
2442 mPolygonPool->triangleFlags(mTriangleIdx) = flags;
2443 addTriangle(verts[0], verts[1], verts[2], reverse);
2444 }
2445 }
2446
2447
2448 void done()
2449 {
2450 mPolygonPool->trimQuads(mQuadIdx, /*reallocate=*/true);
2451 mPolygonPool->trimTriangles(mTriangleIdx, /*reallocate=*/true);
2452 }
2453
2454private:
2455
2456 template<typename IndexType>
2457 void addQuad(const math::Vec4<IndexType>& verts, bool reverse)
2458 {
2459 if (!reverse) {
2460 mPolygonPool->quad(mQuadIdx) = verts;
2461 } else {
2462 Vec4I& quad = mPolygonPool->quad(mQuadIdx);
2463 quad[0] = verts[3];
2464 quad[1] = verts[2];
2465 quad[2] = verts[1];
2466 quad[3] = verts[0];
2467 }
2468 ++mQuadIdx;
2469 }
2470
2471 void addTriangle(unsigned v0, unsigned v1, unsigned v2, bool reverse)
2472 {
2473 Vec3I& prim = mPolygonPool->triangle(mTriangleIdx);
2474
2475 prim[1] = v1;
2476
2477 if (!reverse) {
2478 prim[0] = v0;
2479 prim[2] = v2;
2480 } else {
2481 prim[0] = v2;
2482 prim[2] = v0;
2483 }
2484 ++mTriangleIdx;
2485 }
2486
2487 size_t mQuadIdx, mTriangleIdx;
2488 PolygonPool *mPolygonPool;
2489};
2490
2491
2492template<typename SignAccT, typename IdxAccT, typename PrimBuilder>
2493inline void
2494constructPolygons(
2495 bool invertSurfaceOrientation,
2496 Int16 flags,
2497 Int16 refFlags,
2498 const Vec3i& offsets,
2499 const Coord& ijk,
2500 const SignAccT& signAcc,
2501 const IdxAccT& idxAcc,
2502 PrimBuilder& mesher)
2503{
2504 using IndexType = typename IdxAccT::ValueType;
2505
2506 IndexType v0 = IndexType(util::INVALID_IDX);
2507 const bool isActive = idxAcc.probeValue(ijk, v0);
2508 if (isActive == false || v0 == IndexType(util::INVALID_IDX)) return;
2509
2510 char tag[2];
2511 tag[0] = (flags & SEAM) ? POLYFLAG_FRACTURE_SEAM : 0;
2512 tag[1] = tag[0] | char(POLYFLAG_EXTERIOR);
2513
2514 bool isInside = flags & INSIDE;
2515
2516 isInside = invertSurfaceOrientation ? !isInside : isInside;
2517
2518 Coord coord = ijk;
2519 math::Vec4<IndexType> quad(0,0,0,0);
2520
2521 if (flags & XEDGE) {
2522
2523 quad[0] = v0 + offsets[0];
2524
2525 // i, j-1, k
2526 coord[1]--;
2527 bool activeValues = idxAcc.probeValue(coord, quad[1]);
2528 uint8_t cell = uint8_t(SIGNS & signAcc.getValue(coord));
2529 quad[1] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][5] - 1 : 0;
2530
2531 // i, j-1, k-1
2532 coord[2]--;
2533 activeValues = activeValues && idxAcc.probeValue(coord, quad[2]);
2534 cell = uint8_t(SIGNS & signAcc.getValue(coord));
2535 quad[2] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][7] - 1 : 0;
2536
2537 // i, j, k-1
2538 coord[1]++;
2539 activeValues = activeValues && idxAcc.probeValue(coord, quad[3]);
2540 cell = uint8_t(SIGNS & signAcc.getValue(coord));
2541 quad[3] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][3] - 1 : 0;
2542
2543 if (activeValues) {
2544 mesher.addPrim(quad, isInside, tag[bool(refFlags & XEDGE)]);
2545 }
2546
2547 coord[2]++; // i, j, k
2548 }
2549
2550
2551 if (flags & YEDGE) {
2552
2553 quad[0] = v0 + offsets[1];
2554
2555 // i, j, k-1
2556 coord[2]--;
2557 bool activeValues = idxAcc.probeValue(coord, quad[1]);
2558 uint8_t cell = uint8_t(SIGNS & signAcc.getValue(coord));
2559 quad[1] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][12] - 1 : 0;
2560
2561 // i-1, j, k-1
2562 coord[0]--;
2563 activeValues = activeValues && idxAcc.probeValue(coord, quad[2]);
2564 cell = uint8_t(SIGNS & signAcc.getValue(coord));
2565 quad[2] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][11] - 1 : 0;
2566
2567 // i-1, j, k
2568 coord[2]++;
2569 activeValues = activeValues && idxAcc.probeValue(coord, quad[3]);
2570 cell = uint8_t(SIGNS & signAcc.getValue(coord));
2571 quad[3] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][10] - 1 : 0;
2572
2573 if (activeValues) {
2574 mesher.addPrim(quad, isInside, tag[bool(refFlags & YEDGE)]);
2575 }
2576
2577 coord[0]++; // i, j, k
2578 }
2579
2580
2581 if (flags & ZEDGE) {
2582
2583 quad[0] = v0 + offsets[2];
2584
2585 // i, j-1, k
2586 coord[1]--;
2587 bool activeValues = idxAcc.probeValue(coord, quad[1]);
2588 uint8_t cell = uint8_t(SIGNS & signAcc.getValue(coord));
2589 quad[1] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][8] - 1 : 0;
2590
2591 // i-1, j-1, k
2592 coord[0]--;
2593 activeValues = activeValues && idxAcc.probeValue(coord, quad[2]);
2594 cell = uint8_t(SIGNS & signAcc.getValue(coord));
2595 quad[2] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][6] - 1 : 0;
2596
2597 // i-1, j, k
2598 coord[1]++;
2599 activeValues = activeValues && idxAcc.probeValue(coord, quad[3]);
2600 cell = uint8_t(SIGNS & signAcc.getValue(coord));
2601 quad[3] += sEdgeGroupTable[cell][0] > 1 ? sEdgeGroupTable[cell][2] - 1 : 0;
2602
2603 if (activeValues) {
2604 mesher.addPrim(quad, !isInside, tag[bool(refFlags & ZEDGE)]);
2605 }
2606 }
2607}
2608
2609
2610////////////////////////////////////////
2611
2612
2613template<typename InputTreeType>
2614struct MaskTileBorders
2615{
2616 using InputValueType = typename InputTreeType::ValueType;
2617 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
2618
2619
2620 MaskTileBorders(const InputTreeType& inputTree, InputValueType iso,
2621 BoolTreeType& mask, const Vec4i* tileArray)
2622 : mInputTree(&inputTree)
2623 , mIsovalue(iso)
2624 , mTempMask(false)
2625 , mMask(&mask)
2626 , mTileArray(tileArray)
2627 {
2628 }
2629
2630 MaskTileBorders(MaskTileBorders& rhs, tbb::split)
2631 : mInputTree(rhs.mInputTree)
2632 , mIsovalue(rhs.mIsovalue)
2633 , mTempMask(false)
2634 , mMask(&mTempMask)
2635 , mTileArray(rhs.mTileArray)
2636 {
2637 }
2638
2639 void join(MaskTileBorders& rhs) { mMask->merge(*rhs.mMask); }
2640
2641 void operator()(const tbb::blocked_range<size_t>&);
2642
2643private:
2644 InputTreeType const * const mInputTree;
2645 InputValueType const mIsovalue;
2646 BoolTreeType mTempMask;
2647 BoolTreeType * const mMask;
2648 Vec4i const * const mTileArray;
2649}; // MaskTileBorders
2650
2651
2652template<typename InputTreeType>
2653void
2654MaskTileBorders<InputTreeType>::operator()(const tbb::blocked_range<size_t>& range)
2655{
2656 tree::ValueAccessor<const InputTreeType> inputTreeAcc(*mInputTree);
2657
2658 CoordBBox region, bbox;
2659 Coord ijk, nijk;
2660
2661 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
2662
2663 const Vec4i& tile = mTileArray[n];
2664
2665 bbox.min()[0] = tile[0];
2666 bbox.min()[1] = tile[1];
2667 bbox.min()[2] = tile[2];
2668 bbox.max() = bbox.min();
2669 bbox.max().offset(tile[3]);
2670
2671 InputValueType value = mInputTree->background();
2672
2673 const bool isInside = isInsideValue(inputTreeAcc.getValue(bbox.min()), mIsovalue);
2674 const int valueDepth = inputTreeAcc.getValueDepth(bbox.min());
2675
2676 // eval x-edges
2677
2678 ijk = bbox.max();
2679 nijk = ijk;
2680 ++nijk[0];
2681
2682 bool processRegion = true;
2683 if (valueDepth >= inputTreeAcc.getValueDepth(nijk)) {
2684 processRegion = isInside != isInsideValue(inputTreeAcc.getValue(nijk), mIsovalue);
2685 }
2686
2687 if (processRegion) {
2688 region = bbox;
2689 region.expand(1);
2690 region.min()[0] = region.max()[0] = ijk[0];
2691 mMask->fill(region, false);
2692 }
2693
2694
2695 ijk = bbox.min();
2696 --ijk[0];
2697
2698 processRegion = true;
2699 if (valueDepth >= inputTreeAcc.getValueDepth(ijk)) {
2700 processRegion = (!inputTreeAcc.probeValue(ijk, value)
2701 && isInside != isInsideValue(value, mIsovalue));
2702 }
2703
2704 if (processRegion) {
2705 region = bbox;
2706 region.expand(1);
2707 region.min()[0] = region.max()[0] = ijk[0];
2708 mMask->fill(region, false);
2709 }
2710
2711
2712 // eval y-edges
2713
2714 ijk = bbox.max();
2715 nijk = ijk;
2716 ++nijk[1];
2717
2718 processRegion = true;
2719 if (valueDepth >= inputTreeAcc.getValueDepth(nijk)) {
2720 processRegion = isInside != isInsideValue(inputTreeAcc.getValue(nijk), mIsovalue);
2721 }
2722
2723 if (processRegion) {
2724 region = bbox;
2725 region.expand(1);
2726 region.min()[1] = region.max()[1] = ijk[1];
2727 mMask->fill(region, false);
2728 }
2729
2730
2731 ijk = bbox.min();
2732 --ijk[1];
2733
2734 processRegion = true;
2735 if (valueDepth >= inputTreeAcc.getValueDepth(ijk)) {
2736 processRegion = (!inputTreeAcc.probeValue(ijk, value)
2737 && isInside != isInsideValue(value, mIsovalue));
2738 }
2739
2740 if (processRegion) {
2741 region = bbox;
2742 region.expand(1);
2743 region.min()[1] = region.max()[1] = ijk[1];
2744 mMask->fill(region, false);
2745 }
2746
2747
2748 // eval z-edges
2749
2750 ijk = bbox.max();
2751 nijk = ijk;
2752 ++nijk[2];
2753
2754 processRegion = true;
2755 if (valueDepth >= inputTreeAcc.getValueDepth(nijk)) {
2756 processRegion = isInside != isInsideValue(inputTreeAcc.getValue(nijk), mIsovalue);
2757 }
2758
2759 if (processRegion) {
2760 region = bbox;
2761 region.expand(1);
2762 region.min()[2] = region.max()[2] = ijk[2];
2763 mMask->fill(region, false);
2764 }
2765
2766 ijk = bbox.min();
2767 --ijk[2];
2768
2769 processRegion = true;
2770 if (valueDepth >= inputTreeAcc.getValueDepth(ijk)) {
2771 processRegion = (!inputTreeAcc.probeValue(ijk, value)
2772 && isInside != isInsideValue(value, mIsovalue));
2773 }
2774
2775 if (processRegion) {
2776 region = bbox;
2777 region.expand(1);
2778 region.min()[2] = region.max()[2] = ijk[2];
2779 mMask->fill(region, false);
2780 }
2781 }
2782} // MaskTileBorders::operator()
2783
2784
2785template<typename InputTreeType>
2786inline void
2787maskActiveTileBorders(const InputTreeType& inputTree,
2788 const typename InputTreeType::ValueType iso,
2789 typename InputTreeType::template ValueConverter<bool>::Type& mask)
2790{
2791 typename InputTreeType::ValueOnCIter tileIter(inputTree);
2792 tileIter.setMaxDepth(InputTreeType::ValueOnCIter::LEAF_DEPTH - 1);
2793
2794 size_t tileCount = 0;
2795 for ( ; tileIter; ++tileIter) {
2796 ++tileCount;
2797 }
2798
2799 if (tileCount > 0) {
2800 std::unique_ptr<Vec4i[]> tiles(new Vec4i[tileCount]);
2801
2802 CoordBBox bbox;
2803 size_t index = 0;
2804
2805 tileIter = inputTree.cbeginValueOn();
2806 tileIter.setMaxDepth(InputTreeType::ValueOnCIter::LEAF_DEPTH - 1);
2807
2808 for (; tileIter; ++tileIter) {
2809 Vec4i& tile = tiles[index++];
2810 tileIter.getBoundingBox(bbox);
2811 tile[0] = bbox.min()[0];
2812 tile[1] = bbox.min()[1];
2813 tile[2] = bbox.min()[2];
2814 tile[3] = bbox.max()[0] - bbox.min()[0];
2815 }
2816
2817 MaskTileBorders<InputTreeType> op(inputTree, iso, mask, tiles.get());
2818 tbb::parallel_reduce(tbb::blocked_range<size_t>(0, tileCount), op);
2819 }
2820}
2821
2822
2823////////////////////////////////////////
2824
2825
2826// Utility class for the volumeToMesh wrapper
2827class PointListCopy
2828{
2829public:
2830 PointListCopy(const PointList& pointsIn, std::vector<Vec3s>& pointsOut)
2831 : mPointsIn(pointsIn) , mPointsOut(pointsOut)
2832 {
2833 }
2834
2835 void operator()(const tbb::blocked_range<size_t>& range) const
2836 {
2837 for (size_t n = range.begin(); n < range.end(); ++n) {
2838 mPointsOut[n] = mPointsIn[n];
2839 }
2840 }
2841
2842private:
2843 const PointList& mPointsIn;
2844 std::vector<Vec3s>& mPointsOut;
2845};
2846
2847
2848////////////////////////////////////////////////////////////////////////////////
2849////////////////////////////////////////////////////////////////////////////////
2850
2851
2852struct LeafNodeVoxelOffsets
2853{
2854 using IndexVector = std::vector<Index>;
2855
2856 template<typename LeafNodeType>
2857 void constructOffsetList();
2858
2859 /// Return internal core voxel offsets.
2860 const IndexVector& core() const { return mCore; }
2861
2862
2863 /// Return front face voxel offsets.
2864 const IndexVector& minX() const { return mMinX; }
2865
2866 /// Return back face voxel offsets.
2867 const IndexVector& maxX() const { return mMaxX; }
2868
2869
2870 /// Return bottom face voxel offsets.
2871 const IndexVector& minY() const { return mMinY; }
2872
2873 /// Return top face voxel offsets.
2874 const IndexVector& maxY() const { return mMaxY; }
2875
2876
2877 /// Return left face voxel offsets.
2878 const IndexVector& minZ() const { return mMinZ; }
2879
2880 /// Return right face voxel offsets.
2881 const IndexVector& maxZ() const { return mMaxZ; }
2882
2883
2884 /// Return voxel offsets with internal neighbours in x + 1.
2885 const IndexVector& internalNeighborsX() const { return mInternalNeighborsX; }
2886
2887 /// Return voxel offsets with internal neighbours in y + 1.
2888 const IndexVector& internalNeighborsY() const { return mInternalNeighborsY; }
2889
2890 /// Return voxel offsets with internal neighbours in z + 1.
2891 const IndexVector& internalNeighborsZ() const { return mInternalNeighborsZ; }
2892
2893
2894private:
2895 IndexVector mCore, mMinX, mMaxX, mMinY, mMaxY, mMinZ, mMaxZ,
2896 mInternalNeighborsX, mInternalNeighborsY, mInternalNeighborsZ;
2897}; // struct LeafNodeOffsets
2898
2899
2900template<typename LeafNodeType>
2901inline void
2902LeafNodeVoxelOffsets::constructOffsetList()
2903{
2904 // internal core voxels
2905 mCore.clear();
2906 mCore.reserve((LeafNodeType::DIM - 2) * (LeafNodeType::DIM - 2));
2907
2908 for (Index x = 1; x < (LeafNodeType::DIM - 1); ++x) {
2909 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
2910 for (Index y = 1; y < (LeafNodeType::DIM - 1); ++y) {
2911 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
2912 for (Index z = 1; z < (LeafNodeType::DIM - 1); ++z) {
2913 mCore.push_back(offsetXY + z);
2914 }
2915 }
2916 }
2917
2918 // internal neighbors in x + 1
2919 mInternalNeighborsX.clear();
2920 mInternalNeighborsX.reserve(LeafNodeType::SIZE - (LeafNodeType::DIM * LeafNodeType::DIM));
2921
2922 for (Index x = 0; x < (LeafNodeType::DIM - 1); ++x) {
2923 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
2924 for (Index y = 0; y < LeafNodeType::DIM; ++y) {
2925 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
2926 for (Index z = 0; z < LeafNodeType::DIM; ++z) {
2927 mInternalNeighborsX.push_back(offsetXY + z);
2928 }
2929 }
2930 }
2931
2932 // internal neighbors in y + 1
2933 mInternalNeighborsY.clear();
2934 mInternalNeighborsY.reserve(LeafNodeType::SIZE - (LeafNodeType::DIM * LeafNodeType::DIM));
2935
2936 for (Index x = 0; x < LeafNodeType::DIM; ++x) {
2937 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
2938 for (Index y = 0; y < (LeafNodeType::DIM - 1); ++y) {
2939 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
2940 for (Index z = 0; z < LeafNodeType::DIM; ++z) {
2941 mInternalNeighborsY.push_back(offsetXY + z);
2942 }
2943 }
2944 }
2945
2946 // internal neighbors in z + 1
2947 mInternalNeighborsZ.clear();
2948 mInternalNeighborsZ.reserve(LeafNodeType::SIZE - (LeafNodeType::DIM * LeafNodeType::DIM));
2949
2950 for (Index x = 0; x < LeafNodeType::DIM; ++x) {
2951 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
2952 for (Index y = 0; y < LeafNodeType::DIM; ++y) {
2953 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
2954 for (Index z = 0; z < (LeafNodeType::DIM - 1); ++z) {
2955 mInternalNeighborsZ.push_back(offsetXY + z);
2956 }
2957 }
2958 }
2959
2960 // min x
2961 mMinX.clear();
2962 mMinX.reserve(LeafNodeType::DIM * LeafNodeType::DIM);
2963 {
2964 for (Index y = 0; y < LeafNodeType::DIM; ++y) {
2965 const Index offsetXY = (y << LeafNodeType::LOG2DIM);
2966 for (Index z = 0; z < LeafNodeType::DIM; ++z) {
2967 mMinX.push_back(offsetXY + z);
2968 }
2969 }
2970 }
2971
2972 // max x
2973 mMaxX.clear();
2974 mMaxX.reserve(LeafNodeType::DIM * LeafNodeType::DIM);
2975 {
2976 const Index offsetX = (LeafNodeType::DIM - 1) << (2 * LeafNodeType::LOG2DIM);
2977 for (Index y = 0; y < LeafNodeType::DIM; ++y) {
2978 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
2979 for (Index z = 0; z < LeafNodeType::DIM; ++z) {
2980 mMaxX.push_back(offsetXY + z);
2981 }
2982 }
2983 }
2984
2985 // min y
2986 mMinY.clear();
2987 mMinY.reserve(LeafNodeType::DIM * LeafNodeType::DIM);
2988 {
2989 for (Index x = 0; x < LeafNodeType::DIM; ++x) {
2990 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
2991 for (Index z = 0; z < (LeafNodeType::DIM - 1); ++z) {
2992 mMinY.push_back(offsetX + z);
2993 }
2994 }
2995 }
2996
2997 // max y
2998 mMaxY.clear();
2999 mMaxY.reserve(LeafNodeType::DIM * LeafNodeType::DIM);
3000 {
3001 const Index offsetY = (LeafNodeType::DIM - 1) << LeafNodeType::LOG2DIM;
3002 for (Index x = 0; x < LeafNodeType::DIM; ++x) {
3003 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
3004 for (Index z = 0; z < (LeafNodeType::DIM - 1); ++z) {
3005 mMaxY.push_back(offsetX + offsetY + z);
3006 }
3007 }
3008 }
3009
3010 // min z
3011 mMinZ.clear();
3012 mMinZ.reserve(LeafNodeType::DIM * LeafNodeType::DIM);
3013 {
3014 for (Index x = 0; x < LeafNodeType::DIM; ++x) {
3015 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
3016 for (Index y = 0; y < LeafNodeType::DIM; ++y) {
3017 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
3018 mMinZ.push_back(offsetXY);
3019 }
3020 }
3021 }
3022
3023 // max z
3024 mMaxZ.clear();
3025 mMaxZ.reserve(LeafNodeType::DIM * LeafNodeType::DIM);
3026 {
3027 for (Index x = 0; x < LeafNodeType::DIM; ++x) {
3028 const Index offsetX = x << (2 * LeafNodeType::LOG2DIM);
3029 for (Index y = 0; y < LeafNodeType::DIM; ++y) {
3030 const Index offsetXY = offsetX + (y << LeafNodeType::LOG2DIM);
3031 mMaxZ.push_back(offsetXY + (LeafNodeType::DIM - 1));
3032 }
3033 }
3034 }
3035}
3036
3037
3038////////////////////////////////////////
3039
3040
3041/// Utility method to marks all voxels that share an edge.
3042template<typename AccessorT, int _AXIS>
3043struct VoxelEdgeAccessor
3044{
3045 enum { AXIS = _AXIS };
3046 AccessorT& acc;
3047
3048 VoxelEdgeAccessor(AccessorT& _acc) : acc(_acc) {}
3049
3050 void set(Coord ijk) {
3051 if (_AXIS == 0) { // x + 1 edge
3052 acc.setActiveState(ijk);
3053 --ijk[1]; // set i, j-1, k
3054 acc.setActiveState(ijk);
3055 --ijk[2]; // set i, j-1, k-1
3056 acc.setActiveState(ijk);
3057 ++ijk[1]; // set i, j, k-1
3058 acc.setActiveState(ijk);
3059 } else if (_AXIS == 1) { // y + 1 edge
3060 acc.setActiveState(ijk);
3061 --ijk[2]; // set i, j, k-1
3062 acc.setActiveState(ijk);
3063 --ijk[0]; // set i-1, j, k-1
3064 acc.setActiveState(ijk);
3065 ++ijk[2]; // set i-1, j, k
3066 acc.setActiveState(ijk);
3067 } else { // z + 1 edge
3068 acc.setActiveState(ijk);
3069 --ijk[1]; // set i, j-1, k
3070 acc.setActiveState(ijk);
3071 --ijk[0]; // set i-1, j-1, k
3072 acc.setActiveState(ijk);
3073 ++ijk[1]; // set i-1, j, k
3074 acc.setActiveState(ijk);
3075 }
3076 }
3077};
3078
3079
3080/// Utility method to check for sign changes along the x + 1, y + 1 or z + 1 directions.
3081/// The direction is determined by the @a edgeAcc parameter. Only voxels that have internal
3082/// neighbours are evaluated.
3083template<typename VoxelEdgeAcc, typename LeafNodeT>
3084void
3085evalInternalVoxelEdges(VoxelEdgeAcc& edgeAcc,
3086 const LeafNodeT& leafnode,
3087 const LeafNodeVoxelOffsets& voxels,
3088 const typename LeafNodeT::ValueType iso)
3089{
3090 Index nvo = 1; // neighbour voxel offset, z + 1 direction assumed initially.
3091 const std::vector<Index>* offsets = &voxels.internalNeighborsZ();
3092
3093 if (VoxelEdgeAcc::AXIS == 0) { // x + 1 direction
3094 nvo = LeafNodeT::DIM * LeafNodeT::DIM;
3095 offsets = &voxels.internalNeighborsX();
3096 } else if (VoxelEdgeAcc::AXIS == 1) { // y + 1 direction
3097 nvo = LeafNodeT::DIM;
3098 offsets = &voxels.internalNeighborsY();
3099 }
3100
3101 const LeafBufferAccessor<LeafNodeT> lhsAcc(leafnode);
3102
3103 for (size_t n = 0, N = offsets->size(); n < N; ++n) {
3104 const Index& pos = (*offsets)[n];
3105 const bool isActive = leafnode.isValueOn(pos) || leafnode.isValueOn(pos + nvo);
3106 if (isActive && (isInsideValue(lhsAcc.get(pos), iso) !=
3107 isInsideValue(lhsAcc.get((pos + nvo)), iso))) {
3108 edgeAcc.set(leafnode.offsetToGlobalCoord(pos));
3109 }
3110 }
3111}
3112
3113
3114/// Utility method to check for sign changes along the x + 1, y + 1 or z + 1 directions.
3115/// The direction is determined by the @a edgeAcc parameter. All voxels that reside in the
3116/// specified leafnode face: back, top or right are evaluated.
3117template<typename LeafNodeT, typename TreeAcc, typename VoxelEdgeAcc>
3118void
3119evalExternalVoxelEdges(VoxelEdgeAcc& edgeAcc,
3120 const TreeAcc& acc,
3121 const LeafNodeT& lhsNode,
3122 const LeafNodeVoxelOffsets& voxels,
3123 const typename LeafNodeT::ValueType iso)
3124{
3125 const std::vector<Index>* lhsOffsets = &voxels.maxX();
3126 const std::vector<Index>* rhsOffsets = &voxels.minX();
3127 Coord ijk = lhsNode.origin();
3128
3129 if (VoxelEdgeAcc::AXIS == 0) { // back leafnode face
3130 ijk[0] += LeafNodeT::DIM;
3131 } else if (VoxelEdgeAcc::AXIS == 1) { // top leafnode face
3132 ijk[1] += LeafNodeT::DIM;
3133 lhsOffsets = &voxels.maxY();
3134 rhsOffsets = &voxels.minY();
3135 } else if (VoxelEdgeAcc::AXIS == 2) { // right leafnode face
3136 ijk[2] += LeafNodeT::DIM;
3137 lhsOffsets = &voxels.maxZ();
3138 rhsOffsets = &voxels.minZ();
3139 }
3140
3141 typename LeafNodeT::ValueType value;
3142 const LeafNodeT* rhsNodePt = acc.probeConstLeaf(ijk);
3143
3144 const LeafBufferAccessor<LeafNodeT> lhsAcc(lhsNode);
3145
3146 if (rhsNodePt) {
3147 const LeafBufferAccessor<LeafNodeT> rhsAcc(*rhsNodePt);
3148
3149 for (size_t n = 0, N = lhsOffsets->size(); n < N; ++n) {
3150 const Index& pos = (*lhsOffsets)[n];
3151 bool isActive = lhsNode.isValueOn(pos) || rhsNodePt->isValueOn((*rhsOffsets)[n]);
3152 if (isActive && (isInsideValue(lhsAcc.get(pos), iso) !=
3153 isInsideValue(rhsAcc.get((*rhsOffsets)[n]), iso))) {
3154 edgeAcc.set(lhsNode.offsetToGlobalCoord(pos));
3155 }
3156 }
3157 } else if (!acc.probeValue(ijk, value)) {
3158 const bool inside = isInsideValue(value, iso);
3159 for (size_t n = 0, N = lhsOffsets->size(); n < N; ++n) {
3160 const Index& pos = (*lhsOffsets)[n];
3161 if (lhsNode.isValueOn(pos) && (inside != isInsideValue(lhsAcc.get(pos), iso))) {
3162 edgeAcc.set(lhsNode.offsetToGlobalCoord(pos));
3163 }
3164 }
3165 }
3166}
3167
3168
3169/// Utility method to check for sign changes along the x - 1, y - 1 or z - 1 directions.
3170/// The direction is determined by the @a edgeAcc parameter. All voxels that reside in the
3171/// specified leafnode face: front, bottom or left are evaluated.
3172template<typename LeafNodeT, typename TreeAcc, typename VoxelEdgeAcc>
3173void
3174evalExternalVoxelEdgesInv(VoxelEdgeAcc& edgeAcc,
3175 const TreeAcc& acc,
3176 const LeafNodeT& leafnode,
3177 const LeafNodeVoxelOffsets& voxels,
3178 const typename LeafNodeT::ValueType iso)
3179{
3180 Coord ijk = leafnode.origin();
3181 if (VoxelEdgeAcc::AXIS == 0) --ijk[0]; // front leafnode face
3182 else if (VoxelEdgeAcc::AXIS == 1) --ijk[1]; // bottom leafnode face
3183 else if (VoxelEdgeAcc::AXIS == 2) --ijk[2]; // left leafnode face
3184
3185 typename LeafNodeT::ValueType value;
3186 if (!acc.probeConstLeaf(ijk) && !acc.probeValue(ijk, value)) {
3187
3188 const std::vector<Index>* offsets = &voxels.internalNeighborsX();
3189 if (VoxelEdgeAcc::AXIS == 1) offsets = &voxels.internalNeighborsY();
3190 else if (VoxelEdgeAcc::AXIS == 2) offsets = &voxels.internalNeighborsZ();
3191
3192 const LeafBufferAccessor<LeafNodeT> lhsAcc(leafnode);
3193
3194 const bool inside = isInsideValue(value, iso);
3195 for (size_t n = 0, N = offsets->size(); n < N; ++n) {
3196
3197 const Index& pos = (*offsets)[n];
3198 if (leafnode.isValueOn(pos)
3199 && (inside != isInsideValue(lhsAcc.get(pos), iso)))
3200 {
3201 ijk = leafnode.offsetToGlobalCoord(pos);
3202 if (VoxelEdgeAcc::AXIS == 0) --ijk[0];
3203 else if (VoxelEdgeAcc::AXIS == 1) --ijk[1];
3204 else if (VoxelEdgeAcc::AXIS == 2) --ijk[2];
3205
3206 edgeAcc.set(ijk);
3207 }
3208 }
3209 }
3210}
3211
3212
3213template<typename InputTreeType>
3214struct IdentifyIntersectingVoxels
3215{
3216 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
3217 using InputValueType = typename InputLeafNodeType::ValueType;
3218
3219 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
3220
3221 IdentifyIntersectingVoxels(
3222 const InputTreeType& inputTree,
3223 const std::vector<const InputLeafNodeType*>& inputLeafNodes,
3224 const LeafNodeVoxelOffsets& offsets,
3225 BoolTreeType& intersectionTree,
3226 InputValueType iso);
3227
3228 IdentifyIntersectingVoxels(IdentifyIntersectingVoxels&, tbb::split);
3229 void operator()(const tbb::blocked_range<size_t>&);
3230 void join(const IdentifyIntersectingVoxels& rhs) {
3231 mIntersectionAccessor.tree().merge(rhs.mIntersectionAccessor.tree());
3232 }
3233
3234private:
3235 tree::ValueAccessor<const InputTreeType> mInputAccessor;
3236 InputLeafNodeType const * const * const mInputNodes;
3237
3238 BoolTreeType mIntersectionTree;
3239 tree::ValueAccessor<BoolTreeType> mIntersectionAccessor;
3240
3241 const LeafNodeVoxelOffsets& mOffsets;
3242 const InputValueType mIsovalue;
3243}; // struct IdentifyIntersectingVoxels
3244
3245
3246template<typename InputTreeType>
3247IdentifyIntersectingVoxels<InputTreeType>::IdentifyIntersectingVoxels(
3248 const InputTreeType& inputTree,
3249 const std::vector<const InputLeafNodeType*>& inputLeafNodes,
3250 const LeafNodeVoxelOffsets& offsets,
3251 BoolTreeType& intersectionTree,
3252 InputValueType iso)
3253 : mInputAccessor(inputTree)
3254 , mInputNodes(inputLeafNodes.data())
3255 , mIntersectionTree(false)
3256 , mIntersectionAccessor(intersectionTree)
3257 , mOffsets(offsets)
3258 , mIsovalue(iso)
3259{
3260}
3261
3262
3263template<typename InputTreeType>
3264IdentifyIntersectingVoxels<InputTreeType>::IdentifyIntersectingVoxels(
3265 IdentifyIntersectingVoxels& rhs, tbb::split)
3266 : mInputAccessor(rhs.mInputAccessor.tree())
3267 , mInputNodes(rhs.mInputNodes)
3268 , mIntersectionTree(false)
3269 , mIntersectionAccessor(mIntersectionTree) // use local tree.
3270 , mOffsets(rhs.mOffsets)
3271 , mIsovalue(rhs.mIsovalue)
3272{
3273}
3274
3275
3276template<typename InputTreeType>
3277void
3278IdentifyIntersectingVoxels<InputTreeType>::operator()(const tbb::blocked_range<size_t>& range)
3279{
3280 VoxelEdgeAccessor<tree::ValueAccessor<BoolTreeType>, 0> xEdgeAcc(mIntersectionAccessor);
3281 VoxelEdgeAccessor<tree::ValueAccessor<BoolTreeType>, 1> yEdgeAcc(mIntersectionAccessor);
3282 VoxelEdgeAccessor<tree::ValueAccessor<BoolTreeType>, 2> zEdgeAcc(mIntersectionAccessor);
3283
3284 for (size_t n = range.begin(); n != range.end(); ++n) {
3285
3286 const InputLeafNodeType& node = *mInputNodes[n];
3287
3288 // internal x + 1 voxel edges
3289 evalInternalVoxelEdges(xEdgeAcc, node, mOffsets, mIsovalue);
3290 // internal y + 1 voxel edges
3291 evalInternalVoxelEdges(yEdgeAcc, node, mOffsets, mIsovalue);
3292 // internal z + 1 voxel edges
3293 evalInternalVoxelEdges(zEdgeAcc, node, mOffsets, mIsovalue);
3294
3295 // external x + 1 voxels edges (back face)
3296 evalExternalVoxelEdges(xEdgeAcc, mInputAccessor, node, mOffsets, mIsovalue);
3297 // external y + 1 voxels edges (top face)
3298 evalExternalVoxelEdges(yEdgeAcc, mInputAccessor, node, mOffsets, mIsovalue);
3299 // external z + 1 voxels edges (right face)
3300 evalExternalVoxelEdges(zEdgeAcc, mInputAccessor, node, mOffsets, mIsovalue);
3301
3302 // The remaining edges are only checked if the leafnode neighbour, in the
3303 // corresponding direction, is an inactive tile.
3304
3305 // external x - 1 voxels edges (front face)
3306 evalExternalVoxelEdgesInv(xEdgeAcc, mInputAccessor, node, mOffsets, mIsovalue);
3307 // external y - 1 voxels edges (bottom face)
3308 evalExternalVoxelEdgesInv(yEdgeAcc, mInputAccessor, node, mOffsets, mIsovalue);
3309 // external z - 1 voxels edges (left face)
3310 evalExternalVoxelEdgesInv(zEdgeAcc, mInputAccessor, node, mOffsets, mIsovalue);
3311 }
3312} // IdentifyIntersectingVoxels::operator()
3313
3314
3315template<typename InputTreeType>
3316inline void
3317identifySurfaceIntersectingVoxels(
3318 typename InputTreeType::template ValueConverter<bool>::Type& intersectionTree,
3319 const InputTreeType& inputTree,
3320 typename InputTreeType::ValueType isovalue)
3321{
3322 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
3323
3324 std::vector<const InputLeafNodeType*> inputLeafNodes;
3325 inputTree.getNodes(inputLeafNodes);
3326
3327 LeafNodeVoxelOffsets offsets;
3328 offsets.constructOffsetList<InputLeafNodeType>();
3329
3330 IdentifyIntersectingVoxels<InputTreeType> op(
3331 inputTree, inputLeafNodes, offsets, intersectionTree, isovalue);
3332
3333 tbb::parallel_reduce(tbb::blocked_range<size_t>(0, inputLeafNodes.size()), op);
3334
3335 maskActiveTileBorders(inputTree, isovalue, intersectionTree);
3336}
3337
3338
3339////////////////////////////////////////
3340
3341
3342template<typename InputTreeType>
3343struct MaskIntersectingVoxels
3344{
3345 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
3346 using InputValueType = typename InputLeafNodeType::ValueType;
3347
3348 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
3349 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
3350
3351 MaskIntersectingVoxels(
3352 const InputTreeType& inputTree,
3353 const std::vector<BoolLeafNodeType*>& nodes,
3354 BoolTreeType& intersectionTree,
3355 InputValueType iso);
3356
3357 MaskIntersectingVoxels(MaskIntersectingVoxels&, tbb::split);
3358 void operator()(const tbb::blocked_range<size_t>&);
3359 void join(const MaskIntersectingVoxels& rhs) {
3360 mIntersectionAccessor.tree().merge(rhs.mIntersectionAccessor.tree());
3361 }
3362
3363private:
3364 tree::ValueAccessor<const InputTreeType> mInputAccessor;
3365 BoolLeafNodeType const * const * const mNodes;
3366
3367 BoolTreeType mIntersectionTree;
3368 tree::ValueAccessor<BoolTreeType> mIntersectionAccessor;
3369
3370 const InputValueType mIsovalue;
3371}; // struct MaskIntersectingVoxels
3372
3373
3374template<typename InputTreeType>
3375MaskIntersectingVoxels<InputTreeType>::MaskIntersectingVoxels(
3376 const InputTreeType& inputTree,
3377 const std::vector<BoolLeafNodeType*>& nodes,
3378 BoolTreeType& intersectionTree,
3379 InputValueType iso)
3380 : mInputAccessor(inputTree)
3381 , mNodes(nodes.data())
3382 , mIntersectionTree(false)
3383 , mIntersectionAccessor(intersectionTree)
3384 , mIsovalue(iso)
3385{
3386}
3387
3388
3389template<typename InputTreeType>
3390MaskIntersectingVoxels<InputTreeType>::MaskIntersectingVoxels(
3391 MaskIntersectingVoxels& rhs, tbb::split)
3392 : mInputAccessor(rhs.mInputAccessor.tree())
3393 , mNodes(rhs.mNodes)
3394 , mIntersectionTree(false)
3395 , mIntersectionAccessor(mIntersectionTree) // use local tree.
3396 , mIsovalue(rhs.mIsovalue)
3397{
3398}
3399
3400
3401template<typename InputTreeType>
3402void
3403MaskIntersectingVoxels<InputTreeType>::operator()(const tbb::blocked_range<size_t>& range)
3404{
3405 VoxelEdgeAccessor<tree::ValueAccessor<BoolTreeType>, 0> xEdgeAcc(mIntersectionAccessor);
3406 VoxelEdgeAccessor<tree::ValueAccessor<BoolTreeType>, 1> yEdgeAcc(mIntersectionAccessor);
3407 VoxelEdgeAccessor<tree::ValueAccessor<BoolTreeType>, 2> zEdgeAcc(mIntersectionAccessor);
3408
3409 Coord ijk;
3410
3411 for (size_t n = range.begin(); n != range.end(); ++n) {
3412
3413 const BoolLeafNodeType& node = *mNodes[n];
3414
3415 for (auto it = node.cbeginValueOn(); it; ++it) {
3416
3417 if (!it.getValue()) {
3418
3419 ijk = it.getCoord();
3420
3421 const bool inside = isInsideValue(mInputAccessor.getValue(ijk), mIsovalue);
3422
3423 if (inside != isInsideValue(mInputAccessor.getValue(ijk.offsetBy(1, 0, 0)), mIsovalue)) {
3424 xEdgeAcc.set(ijk);
3425 }
3426
3427 if (inside != isInsideValue(mInputAccessor.getValue(ijk.offsetBy(0, 1, 0)), mIsovalue)) {
3428 yEdgeAcc.set(ijk);
3429 }
3430
3431 if (inside != isInsideValue(mInputAccessor.getValue(ijk.offsetBy(0, 0, 1)), mIsovalue)) {
3432 zEdgeAcc.set(ijk);
3433 }
3434 }
3435 }
3436 }
3437} // MaskIntersectingVoxels::operator()
3438
3439
3440template<typename BoolTreeType>
3441struct MaskBorderVoxels
3442{
3443 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
3444
3445 MaskBorderVoxels(const BoolTreeType& maskTree,
3446 const std::vector<BoolLeafNodeType*>& maskNodes,
3447 BoolTreeType& borderTree)
3448 : mMaskTree(&maskTree)
3449 , mMaskNodes(maskNodes.data())
3450 , mTmpBorderTree(false)
3451 , mBorderTree(&borderTree) {}
3452
3453 MaskBorderVoxels(MaskBorderVoxels& rhs, tbb::split)
3454 : mMaskTree(rhs.mMaskTree)
3455 , mMaskNodes(rhs.mMaskNodes)
3456 , mTmpBorderTree(false)
3457 , mBorderTree(&mTmpBorderTree) {}
3458
3459 void join(MaskBorderVoxels& rhs) { mBorderTree->merge(*rhs.mBorderTree); }
3460
3461 void operator()(const tbb::blocked_range<size_t>& range)
3462 {
3463 tree::ValueAccessor<const BoolTreeType> maskAcc(*mMaskTree);
3464 tree::ValueAccessor<BoolTreeType> borderAcc(*mBorderTree);
3465 Coord ijk;
3466
3467 for (size_t n = range.begin(); n != range.end(); ++n) {
3468
3469 const BoolLeafNodeType& node = *mMaskNodes[n];
3470
3471 for (auto it = node.cbeginValueOn(); it; ++it) {
3472
3473 ijk = it.getCoord();
3474
3475 const bool lhs = it.getValue();
3476 bool rhs = lhs;
3477
3478 bool isEdgeVoxel = false;
3479
3480 ijk[2] += 1; // i, j, k+1
3481 isEdgeVoxel = (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3482
3483 ijk[1] += 1; // i, j+1, k+1
3484 isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3485
3486 ijk[0] += 1; // i+1, j+1, k+1
3487 isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3488
3489 ijk[1] -= 1; // i+1, j, k+1
3490 isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3491
3492
3493 ijk[2] -= 1; // i+1, j, k
3494 isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3495
3496 ijk[1] += 1; // i+1, j+1, k
3497 isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3498
3499 ijk[0] -= 1; // i, j+1, k
3500 isEdgeVoxel = isEdgeVoxel || (maskAcc.probeValue(ijk, rhs) && lhs != rhs);
3501
3502 if (isEdgeVoxel) {
3503 ijk[1] -= 1; // i, j, k
3504 borderAcc.setValue(ijk, true);
3505 }
3506 }
3507 }
3508 }
3509
3510private:
3511 BoolTreeType const * const mMaskTree;
3512 BoolLeafNodeType const * const * const mMaskNodes;
3513
3514 BoolTreeType mTmpBorderTree;
3515 BoolTreeType * const mBorderTree;
3516}; // struct MaskBorderVoxels
3517
3518
3519template<typename BoolTreeType>
3520struct SyncMaskValues
3521{
3522 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
3523
3524 SyncMaskValues(const std::vector<BoolLeafNodeType*>& nodes, const BoolTreeType& mask)
3525 : mNodes(nodes.data())
3526 , mMaskTree(&mask) {}
3527
3528 void operator()(const tbb::blocked_range<size_t>& range) const
3529 {
3530 using ValueOnIter = typename BoolLeafNodeType::ValueOnIter;
3531
3532 tree::ValueAccessor<const BoolTreeType> maskTreeAcc(*mMaskTree);
3533
3534 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
3535
3536 BoolLeafNodeType& node = *mNodes[n];
3537
3538 const BoolLeafNodeType * maskNode = maskTreeAcc.probeConstLeaf(node.origin());
3539
3540 if (maskNode) {
3541 for (ValueOnIter it = node.beginValueOn(); it; ++it) {
3542 const Index pos = it.pos();
3543 if (maskNode->getValue(pos)) {
3544 node.setValueOnly(pos, true);
3545 }
3546 }
3547 }
3548 }
3549 }
3550
3551private:
3552 BoolLeafNodeType * const * const mNodes;
3553 BoolTreeType const * const mMaskTree;
3554}; // struct SyncMaskValues
3555
3556
3557////////////////////////////////////////
3558
3559
3560template<typename BoolTreeType>
3561struct MaskSurface
3562{
3563 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
3564
3565 MaskSurface(const std::vector<BoolLeafNodeType*>& nodes,
3566 const BoolTreeType& mask,
3567 const math::Transform& inputTransform,
3568 const math::Transform& maskTransform,
3569 const bool invert)
3570 : mNodes(nodes.data())
3571 , mMaskTree(&mask)
3572 , mInputTransform(inputTransform)
3573 , mMaskTransform(maskTransform)
3574 , mMatchingTransforms(mInputTransform == mMaskTransform)
3575 , mInvertMask(invert) {}
3576
3577 void operator()(const tbb::blocked_range<size_t>& range) const
3578 {
3579 using ValueOnIter = typename BoolLeafNodeType::ValueOnIter;
3580
3581 tree::ValueAccessor<const BoolTreeType> maskTreeAcc(*mMaskTree);
3582
3583 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
3584
3585 BoolLeafNodeType& node = *mNodes[n];
3586
3587 if (mMatchingTransforms) {
3588
3589 const BoolLeafNodeType * maskNode = maskTreeAcc.probeConstLeaf(node.origin());
3590
3591 if (maskNode) {
3592
3593 for (ValueOnIter it = node.beginValueOn(); it; ++it) {
3594 const Index pos = it.pos();
3595 if (maskNode->isValueOn(pos) == mInvertMask) {
3596 node.setValueOnly(pos, true);
3597 }
3598 }
3599
3600 } else {
3601
3602 if (maskTreeAcc.isValueOn(node.origin()) == mInvertMask) {
3603 for (ValueOnIter it = node.beginValueOn(); it; ++it) {
3604 node.setValueOnly(it.pos(), true);
3605 }
3606 }
3607
3608 }
3609
3610 } else {
3611
3612 Coord ijk;
3613
3614 for (ValueOnIter it = node.beginValueOn(); it; ++it) {
3615
3616 ijk = mMaskTransform.worldToIndexCellCentered(
3617 mInputTransform.indexToWorld(it.getCoord()));
3618
3619 if (maskTreeAcc.isValueOn(ijk) == mInvertMask) {
3620 node.setValueOnly(it.pos(), true);
3621 }
3622 }
3623
3624 }
3625 }
3626 }
3627
3628private:
3629 BoolLeafNodeType * const * const mNodes;
3630 BoolTreeType const * const mMaskTree;
3631 const math::Transform& mInputTransform;
3632 const math::Transform& mMaskTransform;
3633 const bool mMatchingTransforms;
3634 const bool mInvertMask;
3635}; // struct MaskSurface
3636
3637
3638template<typename InputGridType>
3639inline void
3640applySurfaceMask(
3641 typename InputGridType::TreeType::template ValueConverter<bool>::Type& intersectionTree,
3642 typename InputGridType::TreeType::template ValueConverter<bool>::Type& borderTree,
3643 const InputGridType& inputGrid,
3644 const GridBase::ConstPtr& maskGrid,
3645 const bool invertMask,
3646 const typename InputGridType::ValueType isovalue)
3647{
3648 using InputTreeType = typename InputGridType::TreeType;
3649 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
3650 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
3651 using BoolGridType = Grid<BoolTreeType>;
3652
3653 if (!maskGrid) return;
3654 if (maskGrid->type() != BoolGridType::gridType()) return;
3655
3656 const math::Transform& transform = inputGrid.transform();
3657 const InputTreeType& inputTree = inputGrid.tree();
3658
3659 const BoolGridType * surfaceMask = static_cast<const BoolGridType*>(maskGrid.get());
3660
3661 const BoolTreeType& maskTree = surfaceMask->tree();
3662 const math::Transform& maskTransform = surfaceMask->transform();
3663
3664 // mark masked voxels
3665
3666 std::vector<BoolLeafNodeType*> intersectionLeafNodes;
3667 intersectionTree.getNodes(intersectionLeafNodes);
3668
3669 const tbb::blocked_range<size_t> intersectionRange(0, intersectionLeafNodes.size());
3670
3671 tbb::parallel_for(intersectionRange,
3672 MaskSurface<BoolTreeType>(
3673 intersectionLeafNodes, maskTree, transform, maskTransform, invertMask));
3674
3675
3676 // mask surface-mask border
3677
3678 MaskBorderVoxels<BoolTreeType> borderOp(
3679 intersectionTree, intersectionLeafNodes, borderTree);
3680 tbb::parallel_reduce(intersectionRange, borderOp);
3681
3682
3683 // recompute isosurface intersection mask
3684
3685 BoolTreeType tmpIntersectionTree(false);
3686
3687 MaskIntersectingVoxels<InputTreeType> op(
3688 inputTree, intersectionLeafNodes, tmpIntersectionTree, isovalue);
3689
3690 tbb::parallel_reduce(intersectionRange, op);
3691
3692 std::vector<BoolLeafNodeType*> tmpIntersectionLeafNodes;
3693 tmpIntersectionTree.getNodes(tmpIntersectionLeafNodes);
3694
3695 tbb::parallel_for(tbb::blocked_range<size_t>(0, tmpIntersectionLeafNodes.size()),
3696 SyncMaskValues<BoolTreeType>(tmpIntersectionLeafNodes, intersectionTree));
3697
3698 intersectionTree.clear();
3699 intersectionTree.merge(tmpIntersectionTree);
3700}
3701
3702
3703////////////////////////////////////////
3704
3705
3706template<typename InputTreeType>
3707struct ComputeAuxiliaryData
3708{
3709 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
3710 using InputValueType = typename InputLeafNodeType::ValueType;
3711
3712 using BoolLeafNodeType = tree::LeafNode<bool, InputLeafNodeType::LOG2DIM>;
3713
3714 using Int16TreeType = typename InputTreeType::template ValueConverter<Int16>::Type;
3715 using Index32TreeType = typename InputTreeType::template ValueConverter<Index32>::Type;
3716
3717
3718 ComputeAuxiliaryData(const InputTreeType& inputTree,
3719 const std::vector<const BoolLeafNodeType*>& intersectionLeafNodes,
3720 Int16TreeType& signFlagsTree,
3721 Index32TreeType& pointIndexTree,
3722 InputValueType iso);
3723
3724 ComputeAuxiliaryData(ComputeAuxiliaryData&, tbb::split);
3725 void operator()(const tbb::blocked_range<size_t>&);
3726 void join(const ComputeAuxiliaryData& rhs) {
3727 mSignFlagsAccessor.tree().merge(rhs.mSignFlagsAccessor.tree());
3728 mPointIndexAccessor.tree().merge(rhs.mPointIndexAccessor.tree());
3729 }
3730
3731private:
3732 tree::ValueAccessor<const InputTreeType> mInputAccessor;
3733 BoolLeafNodeType const * const * const mIntersectionNodes;
3734
3735 Int16TreeType mSignFlagsTree;
3736 tree::ValueAccessor<Int16TreeType> mSignFlagsAccessor;
3737 Index32TreeType mPointIndexTree;
3738 tree::ValueAccessor<Index32TreeType> mPointIndexAccessor;
3739
3740 const InputValueType mIsovalue;
3741};
3742
3743
3744template<typename InputTreeType>
3745ComputeAuxiliaryData<InputTreeType>::ComputeAuxiliaryData(
3746 const InputTreeType& inputTree,
3747 const std::vector<const BoolLeafNodeType*>& intersectionLeafNodes,
3748 Int16TreeType& signFlagsTree,
3749 Index32TreeType& pointIndexTree,
3750 InputValueType iso)
3751 : mInputAccessor(inputTree)
3752 , mIntersectionNodes(intersectionLeafNodes.data())
3753 , mSignFlagsTree(0)
3754 , mSignFlagsAccessor(signFlagsTree)
3755 , mPointIndexTree(std::numeric_limits<Index32>::max())
3756 , mPointIndexAccessor(pointIndexTree)
3757 , mIsovalue(iso)
3758{
3759 pointIndexTree.root().setBackground(std::numeric_limits<Index32>::max(), false);
3760}
3761
3762
3763template<typename InputTreeType>
3764ComputeAuxiliaryData<InputTreeType>::ComputeAuxiliaryData(ComputeAuxiliaryData& rhs, tbb::split)
3765 : mInputAccessor(rhs.mInputAccessor.tree())
3766 , mIntersectionNodes(rhs.mIntersectionNodes)
3767 , mSignFlagsTree(0)
3768 , mSignFlagsAccessor(mSignFlagsTree)
3769 , mPointIndexTree(std::numeric_limits<Index32>::max())
3770 , mPointIndexAccessor(mPointIndexTree)
3771 , mIsovalue(rhs.mIsovalue)
3772{
3773}
3774
3775
3776template<typename InputTreeType>
3777void
3778ComputeAuxiliaryData<InputTreeType>::operator()(const tbb::blocked_range<size_t>& range)
3779{
3780 using Int16LeafNodeType = typename Int16TreeType::LeafNodeType;
3781
3782 Coord ijk;
3783 std::array<InputValueType, 8> cellVertexValues;
3784 std::unique_ptr<Int16LeafNodeType> signsNodePt(nullptr);
3785
3786 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
3787
3788 const BoolLeafNodeType& maskNode = *mIntersectionNodes[n];
3789 const Coord& origin = maskNode.origin();
3790
3791 const InputLeafNodeType* leafPt = mInputAccessor.probeConstLeaf(origin);
3792
3793 if (!signsNodePt.get()) signsNodePt.reset(new Int16LeafNodeType(origin, 0));
3794 else signsNodePt->setOrigin(origin);
3795
3796 bool updatedNode = false;
3797
3798 for (auto it = maskNode.cbeginValueOn(); it; ++it) {
3799
3800 const Index pos = it.pos();
3801 ijk = BoolLeafNodeType::offsetToLocalCoord(pos);
3802
3803 const bool inclusiveCell = leafPt && isInternalLeafCoord<InputLeafNodeType>(ijk);
3804
3805 if (inclusiveCell) getCellVertexValues(*leafPt, pos, cellVertexValues);
3806 else getCellVertexValues(mInputAccessor, origin + ijk, cellVertexValues);
3807
3808 uint8_t signFlags = computeSignFlags(cellVertexValues, mIsovalue);
3809
3810 if (signFlags != 0 && signFlags != 0xFF) {
3811
3812 const bool inside = signFlags & 0x1;
3813
3814 int edgeFlags = inside ? INSIDE : 0;
3815
3816 if (!it.getValue()) {
3817 edgeFlags |= inside != ((signFlags & 0x02) != 0) ? XEDGE : 0;
3818 edgeFlags |= inside != ((signFlags & 0x10) != 0) ? YEDGE : 0;
3819 edgeFlags |= inside != ((signFlags & 0x08) != 0) ? ZEDGE : 0;
3820 }
3821
3822 const uint8_t ambiguousCellFlags = sAmbiguousFace[signFlags];
3823 if (ambiguousCellFlags != 0) {
3824 correctCellSigns(signFlags, ambiguousCellFlags, mInputAccessor,
3825 origin + ijk, mIsovalue);
3826 }
3827
3828 edgeFlags |= int(signFlags);
3829
3830 signsNodePt->setValueOn(pos, Int16(edgeFlags));
3831 updatedNode = true;
3832 }
3833 }
3834
3835 if (updatedNode) {
3836 typename Index32TreeType::LeafNodeType* idxNode =
3837 mPointIndexAccessor.touchLeaf(origin);
3838 idxNode->topologyUnion(*signsNodePt);
3839
3840 // zero fill
3841 auto* const idxData = idxNode->buffer().data();
3842 for (auto it = idxNode->beginValueOn(); it; ++it) {
3843 idxData[it.pos()] = 0;
3844 }
3845
3846 mSignFlagsAccessor.addLeaf(signsNodePt.release());
3847 }
3848 }
3849} // ComputeAuxiliaryData::operator()
3850
3851
3852template<typename InputTreeType>
3853inline void
3854computeAuxiliaryData(
3855 typename InputTreeType::template ValueConverter<Int16>::Type& signFlagsTree,
3856 typename InputTreeType::template ValueConverter<Index32>::Type& pointIndexTree,
3857 const typename InputTreeType::template ValueConverter<bool>::Type& intersectionTree,
3858 const InputTreeType& inputTree,
3859 typename InputTreeType::ValueType isovalue)
3860{
3861 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
3862 using BoolLeafNodeType = typename BoolTreeType::LeafNodeType;
3863
3864 std::vector<const BoolLeafNodeType*> intersectionLeafNodes;
3865 intersectionTree.getNodes(intersectionLeafNodes);
3866
3867 ComputeAuxiliaryData<InputTreeType> op(
3868 inputTree, intersectionLeafNodes, signFlagsTree, pointIndexTree, isovalue);
3869
3870 tbb::parallel_reduce(tbb::blocked_range<size_t>(0, intersectionLeafNodes.size()), op);
3871}
3872
3873
3874////////////////////////////////////////
3875
3876
3877template<Index32 LeafNodeLog2Dim>
3878struct LeafNodePointCount
3879{
3880 using Int16LeafNodeType = tree::LeafNode<Int16, LeafNodeLog2Dim>;
3881
3882 LeafNodePointCount(const std::vector<Int16LeafNodeType*>& leafNodes,
3883 std::unique_ptr<Index32[]>& leafNodeCount)
3884 : mLeafNodes(leafNodes.data())
3885 , mData(leafNodeCount.get()) {}
3886
3887 void operator()(const tbb::blocked_range<size_t>& range) const {
3888
3889 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
3890
3891 Index32 count = 0;
3892
3893 Int16 const * p = mLeafNodes[n]->buffer().data();
3894 Int16 const * const endP = p + Int16LeafNodeType::SIZE;
3895
3896 while (p < endP) {
3897 count += Index32(sEdgeGroupTable[(SIGNS & *p)][0]);
3898 ++p;
3899 }
3900
3901 mData[n] = count;
3902 }
3903 }
3904
3905private:
3906 Int16LeafNodeType * const * const mLeafNodes;
3907 Index32 *mData;
3908}; // struct LeafNodePointCount
3909
3910
3911template<typename PointIndexLeafNode>
3912struct AdaptiveLeafNodePointCount
3913{
3914 using Int16LeafNodeType = tree::LeafNode<Int16, PointIndexLeafNode::LOG2DIM>;
3915
3916 AdaptiveLeafNodePointCount(const std::vector<PointIndexLeafNode*>& pointIndexNodes,
3917 const std::vector<Int16LeafNodeType*>& signDataNodes,
3918 std::unique_ptr<Index32[]>& leafNodeCount)
3919 : mPointIndexNodes(pointIndexNodes.data())
3920 , mSignDataNodes(signDataNodes.data())
3921 , mData(leafNodeCount.get()) {}
3922
3923 void operator()(const tbb::blocked_range<size_t>& range) const
3924 {
3925 using IndexType = typename PointIndexLeafNode::ValueType;
3926
3927 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
3928
3929 const PointIndexLeafNode& node = *mPointIndexNodes[n];
3930 const Int16LeafNodeType& signNode = *mSignDataNodes[n];
3931
3932 size_t count = 0;
3933
3934 std::set<IndexType> uniqueRegions;
3935
3936 for (typename PointIndexLeafNode::ValueOnCIter it = node.cbeginValueOn(); it; ++it) {
3937
3938 IndexType id = it.getValue();
3939
3940 if (id == 0) {
3941 count += size_t(sEdgeGroupTable[(SIGNS & signNode.getValue(it.pos()))][0]);
3942 } else if (id != IndexType(util::INVALID_IDX)) {
3943 uniqueRegions.insert(id);
3944 }
3945 }
3946
3947 mData[n] = Index32(count + uniqueRegions.size());
3948 }
3949 }
3950
3951private:
3952 PointIndexLeafNode const * const * const mPointIndexNodes;
3953 Int16LeafNodeType const * const * const mSignDataNodes;
3954 Index32 *mData;
3955}; // struct AdaptiveLeafNodePointCount
3956
3957
3958template<typename PointIndexLeafNode>
3959struct MapPoints
3960{
3961 using Int16LeafNodeType = tree::LeafNode<Int16, PointIndexLeafNode::LOG2DIM>;
3962
3963 MapPoints(const std::vector<PointIndexLeafNode*>& pointIndexNodes,
3964 const std::vector<Int16LeafNodeType*>& signDataNodes,
3965 std::unique_ptr<Index32[]>& leafNodeCount)
3966 : mPointIndexNodes(pointIndexNodes.data())
3967 , mSignDataNodes(signDataNodes.data())
3968 , mData(leafNodeCount.get()) {}
3969
3970 void operator()(const tbb::blocked_range<size_t>& range) const {
3971
3972 for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
3973
3974 const Int16LeafNodeType& signNode = *mSignDataNodes[n];
3975 PointIndexLeafNode& indexNode = *mPointIndexNodes[n];
3976
3977 Index32 pointOffset = mData[n];
3978
3979 for (auto it = indexNode.beginValueOn(); it; ++it) {
3980 const Index pos = it.pos();
3981 indexNode.setValueOnly(pos, pointOffset);
3982 const int signs = SIGNS & int(signNode.getValue(pos));
3983 pointOffset += Index32(sEdgeGroupTable[signs][0]);
3984 }
3985 }
3986 }
3987
3988private:
3989 PointIndexLeafNode * const * const mPointIndexNodes;
3990 Int16LeafNodeType const * const * const mSignDataNodes;
3991 Index32 * const mData;
3992}; // struct MapPoints
3993
3994
3995template<typename TreeType, typename PrimBuilder>
3996struct ComputePolygons
3997{
3998 using Int16TreeType = typename TreeType::template ValueConverter<Int16>::Type;
3999 using Int16LeafNodeType = typename Int16TreeType::LeafNodeType;
4000
4001 using Index32TreeType = typename TreeType::template ValueConverter<Index32>::Type;
4002 using Index32LeafNodeType = typename Index32TreeType::LeafNodeType;
4003
4004 ComputePolygons(
4005 const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
4006 const Int16TreeType& signFlagsTree,
4007 const Index32TreeType& idxTree,
4008 PolygonPoolList& polygons,
4009 bool invertSurfaceOrientation);
4010
4011 void setRefSignTree(const Int16TreeType * r) { mRefSignFlagsTree = r; }
4012
4013 void operator()(const tbb::blocked_range<size_t>&) const;
4014
4015private:
4016 Int16LeafNodeType * const * const mSignFlagsLeafNodes;
4017 Int16TreeType const * const mSignFlagsTree;
4018 Int16TreeType const * mRefSignFlagsTree;
4019 Index32TreeType const * const mIndexTree;
4020 PolygonPoolList * const mPolygonPoolList;
4021 bool const mInvertSurfaceOrientation;
4022}; // struct ComputePolygons
4023
4024
4025template<typename TreeType, typename PrimBuilder>
4026ComputePolygons<TreeType, PrimBuilder>::ComputePolygons(
4027 const std::vector<Int16LeafNodeType*>& signFlagsLeafNodes,
4028 const Int16TreeType& signFlagsTree,
4029 const Index32TreeType& idxTree,
4030 PolygonPoolList& polygons,
4031 bool invertSurfaceOrientation)
4032 : mSignFlagsLeafNodes(signFlagsLeafNodes.data())
4033 , mSignFlagsTree(&signFlagsTree)
4034 , mRefSignFlagsTree(nullptr)
4035 , mIndexTree(&idxTree)
4036 , mPolygonPoolList(&polygons)
4037 , mInvertSurfaceOrientation(invertSurfaceOrientation)
4038{
4039}
4040
4041template<typename InputTreeType, typename PrimBuilder>
4042void
4043ComputePolygons<InputTreeType, PrimBuilder>::operator()(const tbb::blocked_range<size_t>& range) const
4044{
4045 using Int16ValueAccessor = tree::ValueAccessor<const Int16TreeType>;
4046 Int16ValueAccessor signAcc(*mSignFlagsTree);
4047
4049
4050 const bool invertSurfaceOrientation = mInvertSurfaceOrientation;
4051
4052 PrimBuilder mesher;
4053 size_t edgeCount;
4054 Coord ijk, origin;
4055
4056 // reference data
4057 std::unique_ptr<Int16ValueAccessor> refSignAcc;
4058 if (mRefSignFlagsTree) refSignAcc.reset(new Int16ValueAccessor(*mRefSignFlagsTree));
4059
4060 for (size_t n = range.begin(); n != range.end(); ++n) {
4061
4062 const Int16LeafNodeType& node = *mSignFlagsLeafNodes[n];
4063 origin = node.origin();
4064
4065 // Get an upper bound on the number of primitives.
4066 edgeCount = 0;
4067 typename Int16LeafNodeType::ValueOnCIter iter = node.cbeginValueOn();
4068 for (; iter; ++iter) {
4069 if (iter.getValue() & XEDGE) ++edgeCount;
4070 if (iter.getValue() & YEDGE) ++edgeCount;
4071 if (iter.getValue() & ZEDGE) ++edgeCount;
4072 }
4073
4074 if (edgeCount == 0) continue;
4075
4076 mesher.init(edgeCount, (*mPolygonPoolList)[n]);
4077
4078 const Int16LeafNodeType *signleafPt = signAcc.probeConstLeaf(origin);
4079 const Index32LeafNodeType *idxLeafPt = idxAcc.probeConstLeaf(origin);
4080
4081 if (!signleafPt || !idxLeafPt) continue;
4082
4083 const Int16LeafNodeType *refSignLeafPt = nullptr;
4084 if (refSignAcc) refSignLeafPt = refSignAcc->probeConstLeaf(origin);
4085
4086 Vec3i offsets;
4087
4088 for (iter = node.cbeginValueOn(); iter; ++iter) {
4089 ijk = iter.getCoord();
4090
4091 const Int16 flags = iter.getValue();
4092 if (!(flags & 0xE00)) continue;
4093
4094 Int16 refFlags = 0;
4095 if (refSignLeafPt) {
4096 refFlags = refSignLeafPt->getValue(iter.pos());
4097 }
4098
4099 const uint8_t cell = uint8_t(SIGNS & flags);
4100
4101 if (sEdgeGroupTable[cell][0] > 1) {
4102 offsets[0] = (sEdgeGroupTable[cell][1] - 1);
4103 offsets[1] = (sEdgeGroupTable[cell][9] - 1);
4104 offsets[2] = (sEdgeGroupTable[cell][4] - 1);
4105 }
4106 else {
4107 offsets.setZero();
4108 }
4109
4110 if (ijk[0] > origin[0] && ijk[1] > origin[1] && ijk[2] > origin[2]) {
4111 constructPolygons(invertSurfaceOrientation,
4112 flags, refFlags, offsets, ijk, *signleafPt, *idxLeafPt, mesher);
4113 } else {
4114 constructPolygons(invertSurfaceOrientation,
4115 flags, refFlags, offsets, ijk, signAcc, idxAcc, mesher);
4116 }
4117 }
4118
4119 mesher.done();
4120 }
4121
4122} // ComputePolygons::operator()
4123
4124
4125////////////////////////////////////////
4126
4127
4128template<typename T>
4129struct CopyArray
4130{
4131 CopyArray(T * outputArray, const T * inputArray, size_t outputOffset = 0)
4132 : mOutputArray(outputArray), mInputArray(inputArray), mOutputOffset(outputOffset)
4133 {
4134 }
4135
4136 void operator()(const tbb::blocked_range<size_t>& inputArrayRange) const
4137 {
4138 const size_t offset = mOutputOffset;
4139 for (size_t n = inputArrayRange.begin(), N = inputArrayRange.end(); n < N; ++n) {
4140 mOutputArray[offset + n] = mInputArray[n];
4141 }
4142 }
4143
4144private:
4145 T * const mOutputArray;
4146 T const * const mInputArray;
4147 size_t const mOutputOffset;
4148}; // struct CopyArray
4149
4150
4151struct FlagAndCountQuadsToSubdivide
4152{
4153 FlagAndCountQuadsToSubdivide(PolygonPoolList& polygons,
4154 const std::vector<uint8_t>& pointFlags,
4155 std::unique_ptr<openvdb::Vec3s[]>& points,
4156 std::unique_ptr<unsigned[]>& numQuadsToDivide)
4157 : mPolygonPoolList(&polygons)
4158 , mPointFlags(pointFlags.data())
4159 , mPoints(points.get())
4160 , mNumQuadsToDivide(numQuadsToDivide.get())
4161 {
4162 }
4163
4164 void operator()(const tbb::blocked_range<size_t>& range) const
4165 {
4166 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
4167
4168 PolygonPool& polygons = (*mPolygonPoolList)[n];
4169
4170 unsigned count = 0;
4171
4172 // count and tag nonplanar seam line quads.
4173 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
4174
4175 char& flags = polygons.quadFlags(i);
4176
4177 if ((flags & POLYFLAG_FRACTURE_SEAM) && !(flags & POLYFLAG_EXTERIOR)) {
4178
4179 Vec4I& quad = polygons.quad(i);
4180
4181 const bool edgePoly = mPointFlags[quad[0]] || mPointFlags[quad[1]]
4182 || mPointFlags[quad[2]] || mPointFlags[quad[3]];
4183
4184 if (!edgePoly) continue;
4185
4186 const Vec3s& p0 = mPoints[quad[0]];
4187 const Vec3s& p1 = mPoints[quad[1]];
4188 const Vec3s& p2 = mPoints[quad[2]];
4189 const Vec3s& p3 = mPoints[quad[3]];
4190
4191 if (!isPlanarQuad(p0, p1, p2, p3, 1e-6f)) {
4192 flags |= POLYFLAG_SUBDIVIDED;
4193 count++;
4194 }
4195 }
4196 }
4197
4198 mNumQuadsToDivide[n] = count;
4199 }
4200 }
4201
4202private:
4203 PolygonPoolList * const mPolygonPoolList;
4204 uint8_t const * const mPointFlags;
4205 Vec3s const * const mPoints;
4206 unsigned * const mNumQuadsToDivide;
4207}; // struct FlagAndCountQuadsToSubdivide
4208
4209
4210struct SubdivideQuads
4211{
4212 SubdivideQuads(PolygonPoolList& polygons,
4213 const std::unique_ptr<openvdb::Vec3s[]>& points,
4214 size_t pointCount,
4215 std::unique_ptr<openvdb::Vec3s[]>& centroids,
4216 std::unique_ptr<unsigned[]>& numQuadsToDivide,
4217 std::unique_ptr<unsigned[]>& centroidOffsets)
4218 : mPolygonPoolList(&polygons)
4219 , mPoints(points.get())
4220 , mCentroids(centroids.get())
4221 , mNumQuadsToDivide(numQuadsToDivide.get())
4222 , mCentroidOffsets(centroidOffsets.get())
4223 , mPointCount(pointCount)
4224 {
4225 }
4226
4227 void operator()(const tbb::blocked_range<size_t>& range) const
4228 {
4229 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
4230
4231 PolygonPool& polygons = (*mPolygonPoolList)[n];
4232
4233 const size_t nonplanarCount = size_t(mNumQuadsToDivide[n]);
4234
4235 if (nonplanarCount > 0) {
4236
4237 PolygonPool tmpPolygons;
4238 tmpPolygons.resetQuads(polygons.numQuads() - nonplanarCount);
4239 tmpPolygons.resetTriangles(polygons.numTriangles() + size_t(4) * nonplanarCount);
4240
4241 size_t offset = mCentroidOffsets[n];
4242
4243 size_t triangleIdx = 0;
4244
4245 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
4246
4247 const char quadFlags = polygons.quadFlags(i);
4248 if (!(quadFlags & POLYFLAG_SUBDIVIDED)) continue;
4249
4250 unsigned newPointIdx = unsigned(offset + mPointCount);
4251
4252 openvdb::Vec4I& quad = polygons.quad(i);
4253
4254 mCentroids[offset] = (mPoints[quad[0]] + mPoints[quad[1]] +
4255 mPoints[quad[2]] + mPoints[quad[3]]) * 0.25f;
4256
4257 ++offset;
4258
4259 {
4260 Vec3I& triangle = tmpPolygons.triangle(triangleIdx);
4261
4262 triangle[0] = quad[0];
4263 triangle[1] = newPointIdx;
4264 triangle[2] = quad[3];
4265
4266 tmpPolygons.triangleFlags(triangleIdx) = quadFlags;
4267 }
4268
4269 ++triangleIdx;
4270
4271 {
4272 Vec3I& triangle = tmpPolygons.triangle(triangleIdx);
4273
4274 triangle[0] = quad[0];
4275 triangle[1] = quad[1];
4276 triangle[2] = newPointIdx;
4277
4278 tmpPolygons.triangleFlags(triangleIdx) = quadFlags;
4279 }
4280
4281 ++triangleIdx;
4282
4283 {
4284 Vec3I& triangle = tmpPolygons.triangle(triangleIdx);
4285
4286 triangle[0] = quad[1];
4287 triangle[1] = quad[2];
4288 triangle[2] = newPointIdx;
4289
4290 tmpPolygons.triangleFlags(triangleIdx) = quadFlags;
4291 }
4292
4293
4294 ++triangleIdx;
4295
4296 {
4297 Vec3I& triangle = tmpPolygons.triangle(triangleIdx);
4298
4299 triangle[0] = quad[2];
4300 triangle[1] = quad[3];
4301 triangle[2] = newPointIdx;
4302
4303 tmpPolygons.triangleFlags(triangleIdx) = quadFlags;
4304 }
4305
4306 ++triangleIdx;
4307
4308 quad[0] = util::INVALID_IDX; // mark for deletion
4309 }
4310
4311
4312 for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
4313 tmpPolygons.triangle(triangleIdx) = polygons.triangle(i);
4314 tmpPolygons.triangleFlags(triangleIdx) = polygons.triangleFlags(i);
4315 ++triangleIdx;
4316 }
4317
4318 size_t quadIdx = 0;
4319 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
4320 openvdb::Vec4I& quad = polygons.quad(i);
4321
4322 if (quad[0] != util::INVALID_IDX) { // ignore invalid quads
4323 tmpPolygons.quad(quadIdx) = quad;
4324 tmpPolygons.quadFlags(quadIdx) = polygons.quadFlags(i);
4325 ++quadIdx;
4326 }
4327 }
4328
4329 polygons.copy(tmpPolygons);
4330 }
4331 }
4332 }
4333
4334private:
4335 PolygonPoolList * const mPolygonPoolList;
4336 Vec3s const * const mPoints;
4337 Vec3s * const mCentroids;
4338 unsigned * const mNumQuadsToDivide;
4339 unsigned * const mCentroidOffsets;
4340 size_t const mPointCount;
4341}; // struct SubdivideQuads
4342
4343
4344inline void
4345subdivideNonplanarSeamLineQuads(
4346 PolygonPoolList& polygonPoolList,
4347 size_t polygonPoolListSize,
4348 PointList& pointList,
4349 size_t& pointListSize,
4350 std::vector<uint8_t>& pointFlags)
4351{
4352 const tbb::blocked_range<size_t> polygonPoolListRange(0, polygonPoolListSize);
4353
4354 std::unique_ptr<unsigned[]> numQuadsToDivide(new unsigned[polygonPoolListSize]);
4355
4356 tbb::parallel_for(polygonPoolListRange,
4357 FlagAndCountQuadsToSubdivide(polygonPoolList, pointFlags, pointList, numQuadsToDivide));
4358
4359 std::unique_ptr<unsigned[]> centroidOffsets(new unsigned[polygonPoolListSize]);
4360
4361 size_t centroidCount = 0;
4362
4363 {
4364 unsigned sum = 0;
4365 for (size_t n = 0, N = polygonPoolListSize; n < N; ++n) {
4366 centroidOffsets[n] = sum;
4367 sum += numQuadsToDivide[n];
4368 }
4369 centroidCount = size_t(sum);
4370 }
4371
4372 std::unique_ptr<Vec3s[]> centroidList(new Vec3s[centroidCount]);
4373
4374 tbb::parallel_for(polygonPoolListRange,
4375 SubdivideQuads(polygonPoolList, pointList, pointListSize,
4376 centroidList, numQuadsToDivide, centroidOffsets));
4377
4378 if (centroidCount > 0) {
4379
4380 const size_t newPointListSize = centroidCount + pointListSize;
4381
4382 std::unique_ptr<openvdb::Vec3s[]> newPointList(new openvdb::Vec3s[newPointListSize]);
4383
4384 tbb::parallel_for(tbb::blocked_range<size_t>(0, pointListSize),
4385 CopyArray<Vec3s>(newPointList.get(), pointList.get()));
4386
4387 tbb::parallel_for(tbb::blocked_range<size_t>(0, newPointListSize - pointListSize),
4388 CopyArray<Vec3s>(newPointList.get(), centroidList.get(), pointListSize));
4389
4390 pointListSize = newPointListSize;
4391 pointList.swap(newPointList);
4392 pointFlags.resize(pointListSize, 0);
4393 }
4394}
4395
4396
4397struct ReviseSeamLineFlags
4398{
4399 ReviseSeamLineFlags(PolygonPoolList& polygons,
4400 const std::vector<uint8_t>& pointFlags)
4401 : mPolygonPoolList(&polygons)
4402 , mPointFlags(pointFlags.data())
4403 {
4404 }
4405
4406 void operator()(const tbb::blocked_range<size_t>& range) const
4407 {
4408 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
4409
4410 PolygonPool& polygons = (*mPolygonPoolList)[n];
4411
4412 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
4413
4414 char& flags = polygons.quadFlags(i);
4415
4416 if (flags & POLYFLAG_FRACTURE_SEAM) {
4417
4418 openvdb::Vec4I& verts = polygons.quad(i);
4419
4420 const bool hasSeamLinePoint =
4421 mPointFlags[verts[0]] || mPointFlags[verts[1]] ||
4422 mPointFlags[verts[2]] || mPointFlags[verts[3]];
4423
4424 if (!hasSeamLinePoint) {
4425 flags &= ~POLYFLAG_FRACTURE_SEAM;
4426 }
4427 }
4428 } // end quad loop
4429
4430 for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
4431
4432 char& flags = polygons.triangleFlags(i);
4433
4434 if (flags & POLYFLAG_FRACTURE_SEAM) {
4435
4436 openvdb::Vec3I& verts = polygons.triangle(i);
4437
4438 const bool hasSeamLinePoint =
4439 mPointFlags[verts[0]] || mPointFlags[verts[1]] || mPointFlags[verts[2]];
4440
4441 if (!hasSeamLinePoint) {
4442 flags &= ~POLYFLAG_FRACTURE_SEAM;
4443 }
4444
4445 }
4446 } // end triangle loop
4447
4448 } // end polygon pool loop
4449 }
4450
4451private:
4452 PolygonPoolList * const mPolygonPoolList;
4453 uint8_t const * const mPointFlags;
4454}; // struct ReviseSeamLineFlags
4455
4456
4457inline void
4458reviseSeamLineFlags(PolygonPoolList& polygonPoolList, size_t polygonPoolListSize,
4459 std::vector<uint8_t>& pointFlags)
4460{
4461 tbb::parallel_for(tbb::blocked_range<size_t>(0, polygonPoolListSize),
4462 ReviseSeamLineFlags(polygonPoolList, pointFlags));
4463}
4464
4465
4466////////////////////////////////////////
4467
4468
4469template<typename InputTreeType>
4470struct MaskDisorientedTrianglePoints
4471{
4472 MaskDisorientedTrianglePoints(const InputTreeType& inputTree, const PolygonPoolList& polygons,
4473 const PointList& pointList, std::unique_ptr<uint8_t[]>& pointMask,
4474 const math::Transform& transform, bool invertSurfaceOrientation)
4475 : mInputTree(&inputTree)
4476 , mPolygonPoolList(&polygons)
4477 , mPointList(&pointList)
4478 , mPointMask(pointMask.get())
4479 , mTransform(transform)
4480 , mInvertSurfaceOrientation(invertSurfaceOrientation)
4481 {
4482 }
4483
4484 void operator()(const tbb::blocked_range<size_t>& range) const
4485 {
4486 using ValueType = typename InputTreeType::LeafNodeType::ValueType;
4487
4488 tree::ValueAccessor<const InputTreeType> inputAcc(*mInputTree);
4489 Vec3s centroid, normal;
4490 Coord ijk;
4491
4492 const bool invertGradientDir = mInvertSurfaceOrientation || isBoolValue<ValueType>();
4493
4494 for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
4495
4496 const PolygonPool& polygons = (*mPolygonPoolList)[n];
4497
4498 for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
4499
4500 const Vec3I& verts = polygons.triangle(i);
4501
4502 const Vec3s& v0 = (*mPointList)[verts[0]];
4503 const Vec3s& v1 = (*mPointList)[verts[1]];
4504 const Vec3s& v2 = (*mPointList)[verts[2]];
4505
4506 normal = (v2 - v0).cross((v1 - v0));
4507 normal.normalize();
4508
4509 centroid = (v0 + v1 + v2) * (1.0f / 3.0f);
4510 ijk = mTransform.worldToIndexCellCentered(centroid);
4511
4512 Vec3s dir( math::ISGradient<math::CD_2ND>::result(inputAcc, ijk) );
4513 dir.normalize();
4514
4515 if (invertGradientDir) {
4516 dir = -dir;
4517 }
4518
4519 // check if the angle is obtuse
4520 if (dir.dot(normal) < -0.5f) {
4521 // Concurrent writes to same memory address can occur, but
4522 // all threads are writing the same value and char is atomic.
4523 // (It is extremely rare that disoriented triangles share points,
4524 // false sharing related performance impacts are not a concern.)
4525 mPointMask[verts[0]] = 1;
4526 mPointMask[verts[1]] = 1;
4527 mPointMask[verts[2]] = 1;
4528 }
4529
4530 } // end triangle loop
4531
4532 } // end polygon pool loop
4533 }
4534
4535private:
4536 InputTreeType const * const mInputTree;
4537 PolygonPoolList const * const mPolygonPoolList;
4538 PointList const * const mPointList;
4539 uint8_t * const mPointMask;
4540 math::Transform const mTransform;
4541 bool const mInvertSurfaceOrientation;
4542}; // struct MaskDisorientedTrianglePoints
4543
4544
4545template<typename InputTree>
4546inline void
4547relaxDisorientedTriangles(
4548 bool invertSurfaceOrientation,
4549 const InputTree& inputTree,
4550 const math::Transform& transform,
4551 PolygonPoolList& polygonPoolList,
4552 size_t polygonPoolListSize,
4553 PointList& pointList,
4554 const size_t pointListSize)
4555{
4556 const tbb::blocked_range<size_t> polygonPoolListRange(0, polygonPoolListSize);
4557
4558 std::unique_ptr<uint8_t[]> pointMask(new uint8_t[pointListSize]);
4559 fillArray(pointMask.get(), uint8_t(0), pointListSize);
4560
4561 tbb::parallel_for(polygonPoolListRange,
4562 MaskDisorientedTrianglePoints<InputTree>(
4563 inputTree, polygonPoolList, pointList, pointMask, transform, invertSurfaceOrientation));
4564
4565 std::unique_ptr<uint8_t[]> pointUpdates(new uint8_t[pointListSize]);
4566 fillArray(pointUpdates.get(), uint8_t(0), pointListSize);
4567
4568 std::unique_ptr<Vec3s[]> newPoints(new Vec3s[pointListSize]);
4569 fillArray(newPoints.get(), Vec3s(0.0f, 0.0f, 0.0f), pointListSize);
4570
4571 for (size_t n = 0, N = polygonPoolListSize; n < N; ++n) {
4572
4573 PolygonPool& polygons = polygonPoolList[n];
4574
4575 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
4576 openvdb::Vec4I& verts = polygons.quad(i);
4577
4578 for (int v = 0; v < 4; ++v) {
4579
4580 const unsigned pointIdx = verts[v];
4581
4582 if (pointMask[pointIdx] == 1) {
4583
4584 newPoints[pointIdx] +=
4585 pointList[verts[0]] + pointList[verts[1]] +
4586 pointList[verts[2]] + pointList[verts[3]];
4587
4588 pointUpdates[pointIdx] = uint8_t(pointUpdates[pointIdx] + 4);
4589 }
4590 }
4591 }
4592
4593 for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
4594 openvdb::Vec3I& verts = polygons.triangle(i);
4595
4596 for (int v = 0; v < 3; ++v) {
4597
4598 const unsigned pointIdx = verts[v];
4599
4600 if (pointMask[pointIdx] == 1) {
4601 newPoints[pointIdx] +=
4602 pointList[verts[0]] + pointList[verts[1]] + pointList[verts[2]];
4603
4604 pointUpdates[pointIdx] = uint8_t(pointUpdates[pointIdx] + 3);
4605 }
4606 }
4607 }
4608 }
4609
4610 for (size_t n = 0, N = pointListSize; n < N; ++n) {
4611 if (pointUpdates[n] > 0) {
4612 const double weight = 1.0 / double(pointUpdates[n]);
4613 pointList[n] = newPoints[n] * float(weight);
4614 }
4615 }
4616}
4617
4618
4619template<typename GridType>
4620void
4621doVolumeToMesh(
4622 const GridType& grid,
4623 std::vector<Vec3s>& points,
4624 std::vector<Vec3I>& triangles,
4625 std::vector<Vec4I>& quads,
4626 double isovalue,
4627 double adaptivity,
4628 bool relaxDisorientedTriangles)
4629{
4630 static_assert(std::is_scalar<typename GridType::ValueType>::value,
4631 "volume to mesh conversion is supported only for scalar grids");
4632
4633 VolumeToMesh mesher(isovalue, adaptivity, relaxDisorientedTriangles);
4634 mesher(grid);
4635
4636 // Preallocate the point list
4637 points.clear();
4638 points.resize(mesher.pointListSize());
4639
4640 { // Copy points
4641 volume_to_mesh_internal::PointListCopy ptnCpy(mesher.pointList(), points);
4642 tbb::parallel_for(tbb::blocked_range<size_t>(0, points.size()), ptnCpy);
4643 mesher.pointList().reset(nullptr);
4644 }
4645
4646 PolygonPoolList& polygonPoolList = mesher.polygonPoolList();
4647
4648 { // Preallocate primitive lists
4649 size_t numQuads = 0, numTriangles = 0;
4650 for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
4651 openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
4652 numTriangles += polygons.numTriangles();
4653 numQuads += polygons.numQuads();
4654 }
4655
4656 triangles.clear();
4657 triangles.resize(numTriangles);
4658 quads.clear();
4659 quads.resize(numQuads);
4660 }
4661
4662 // Copy primitives
4663 size_t qIdx = 0, tIdx = 0;
4664 for (size_t n = 0, N = mesher.polygonPoolListSize(); n < N; ++n) {
4665 openvdb::tools::PolygonPool& polygons = polygonPoolList[n];
4666
4667 for (size_t i = 0, I = polygons.numQuads(); i < I; ++i) {
4668 quads[qIdx++] = polygons.quad(i);
4669 }
4670
4671 for (size_t i = 0, I = polygons.numTriangles(); i < I; ++i) {
4672 triangles[tIdx++] = polygons.triangle(i);
4673 }
4674 }
4675}
4676
4677
4678} // volume_to_mesh_internal namespace
4679
4680/// @endcond
4681
4682////////////////////////////////////////
4683
4684
4685inline
4687 : mNumQuads(0)
4688 , mNumTriangles(0)
4689 , mQuads(nullptr)
4690 , mTriangles(nullptr)
4691 , mQuadFlags(nullptr)
4692 , mTriangleFlags(nullptr)
4693{
4694}
4695
4696
4697inline
4699 : mNumQuads(numQuads)
4700 , mNumTriangles(numTriangles)
4701 , mQuads(new openvdb::Vec4I[mNumQuads])
4702 , mTriangles(new openvdb::Vec3I[mNumTriangles])
4703 , mQuadFlags(new char[mNumQuads])
4704 , mTriangleFlags(new char[mNumTriangles])
4705{
4706}
4707
4708
4709inline void
4711{
4712 resetQuads(rhs.numQuads());
4714
4715 for (size_t i = 0; i < mNumQuads; ++i) {
4716 mQuads[i] = rhs.mQuads[i];
4717 mQuadFlags[i] = rhs.mQuadFlags[i];
4718 }
4719
4720 for (size_t i = 0; i < mNumTriangles; ++i) {
4721 mTriangles[i] = rhs.mTriangles[i];
4722 mTriangleFlags[i] = rhs.mTriangleFlags[i];
4723 }
4724}
4725
4726
4727inline void
4729{
4730 mNumQuads = size;
4731 mQuads.reset(new openvdb::Vec4I[mNumQuads]);
4732 mQuadFlags.reset(new char[mNumQuads]);
4733}
4734
4735
4736inline void
4738{
4739 mNumQuads = 0;
4740 mQuads.reset(nullptr);
4741 mQuadFlags.reset(nullptr);
4742}
4743
4744
4745inline void
4747{
4748 mNumTriangles = size;
4749 mTriangles.reset(new openvdb::Vec3I[mNumTriangles]);
4750 mTriangleFlags.reset(new char[mNumTriangles]);
4751}
4752
4753
4754inline void
4756{
4757 mNumTriangles = 0;
4758 mTriangles.reset(nullptr);
4759 mTriangleFlags.reset(nullptr);
4760}
4761
4762
4763inline bool
4764PolygonPool::trimQuads(const size_t n, bool reallocate)
4765{
4766 if (!(n < mNumQuads)) return false;
4767
4768 if (reallocate) {
4769
4770 if (n == 0) {
4771 mQuads.reset(nullptr);
4772 } else {
4773
4774 std::unique_ptr<openvdb::Vec4I[]> quads(new openvdb::Vec4I[n]);
4775 std::unique_ptr<char[]> flags(new char[n]);
4776
4777 for (size_t i = 0; i < n; ++i) {
4778 quads[i] = mQuads[i];
4779 flags[i] = mQuadFlags[i];
4780 }
4781
4782 mQuads.swap(quads);
4783 mQuadFlags.swap(flags);
4784 }
4785 }
4786
4787 mNumQuads = n;
4788 return true;
4789}
4790
4791
4792inline bool
4793PolygonPool::trimTriangles(const size_t n, bool reallocate)
4794{
4795 if (!(n < mNumTriangles)) return false;
4796
4797 if (reallocate) {
4798
4799 if (n == 0) {
4800 mTriangles.reset(nullptr);
4801 } else {
4802
4803 std::unique_ptr<openvdb::Vec3I[]> triangles(new openvdb::Vec3I[n]);
4804 std::unique_ptr<char[]> flags(new char[n]);
4805
4806 for (size_t i = 0; i < n; ++i) {
4807 triangles[i] = mTriangles[i];
4808 flags[i] = mTriangleFlags[i];
4809 }
4810
4811 mTriangles.swap(triangles);
4812 mTriangleFlags.swap(flags);
4813 }
4814 }
4815
4816 mNumTriangles = n;
4817 return true;
4818}
4819
4820
4821////////////////////////////////////////
4822
4823
4824inline
4825VolumeToMesh::VolumeToMesh(double isovalue, double adaptivity, bool relaxDisorientedTriangles)
4826 : mPoints(nullptr)
4827 , mPolygons()
4828 , mPointListSize(0)
4829 , mSeamPointListSize(0)
4830 , mPolygonPoolListSize(0)
4831 , mIsovalue(isovalue)
4832 , mPrimAdaptivity(adaptivity)
4833 , mSecAdaptivity(0.0)
4834 , mRefGrid(GridBase::ConstPtr())
4835 , mSurfaceMaskGrid(GridBase::ConstPtr())
4836 , mAdaptivityGrid(GridBase::ConstPtr())
4837 , mAdaptivityMaskTree(TreeBase::ConstPtr())
4838 , mRefSignTree(TreeBase::Ptr())
4839 , mRefIdxTree(TreeBase::Ptr())
4840 , mInvertSurfaceMask(false)
4841 , mRelaxDisorientedTriangles(relaxDisorientedTriangles)
4842 , mQuantizedSeamPoints(nullptr)
4843 , mPointFlags(0)
4844{
4845}
4846
4847
4848inline void
4849VolumeToMesh::setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity)
4850{
4851 mRefGrid = grid;
4852 mSecAdaptivity = secAdaptivity;
4853
4854 // Clear out old auxiliary data
4855 mRefSignTree = TreeBase::Ptr();
4856 mRefIdxTree = TreeBase::Ptr();
4857 mSeamPointListSize = 0;
4858 mQuantizedSeamPoints.reset(nullptr);
4859}
4860
4861
4862inline void
4864{
4865 mSurfaceMaskGrid = mask;
4866 mInvertSurfaceMask = invertMask;
4867}
4868
4869
4870inline void
4872{
4873 mAdaptivityGrid = grid;
4874}
4875
4876
4877inline void
4879{
4880 mAdaptivityMaskTree = tree;
4881}
4882
4883
4884template<typename InputGridType>
4885inline void
4886VolumeToMesh::operator()(const InputGridType& inputGrid)
4887{
4888 // input data types
4889
4890 using InputTreeType = typename InputGridType::TreeType;
4891 using InputLeafNodeType = typename InputTreeType::LeafNodeType;
4892 using InputValueType = typename InputLeafNodeType::ValueType;
4893
4894 // auxiliary data types
4895
4896 using FloatTreeType = typename InputTreeType::template ValueConverter<float>::Type;
4897 using FloatGridType = Grid<FloatTreeType>;
4898 using BoolTreeType = typename InputTreeType::template ValueConverter<bool>::Type;
4899 using Int16TreeType = typename InputTreeType::template ValueConverter<Int16>::Type;
4900 using Int16LeafNodeType = typename Int16TreeType::LeafNodeType;
4901 using Index32TreeType = typename InputTreeType::template ValueConverter<Index32>::Type;
4902 using Index32LeafNodeType = typename Index32TreeType::LeafNodeType;
4903
4904 // clear old data
4905 mPointListSize = 0;
4906 mPoints.reset();
4907 mPolygonPoolListSize = 0;
4908 mPolygons.reset();
4909 mPointFlags.clear();
4910
4911 // settings
4912
4913 const math::Transform& transform = inputGrid.transform();
4914 const InputValueType isovalue = InputValueType(mIsovalue);
4915 const float adaptivityThreshold = float(mPrimAdaptivity);
4916 const bool adaptive = mPrimAdaptivity > 1e-7 || mSecAdaptivity > 1e-7;
4917
4918 // The default surface orientation is setup for level set and bool/mask grids.
4919 // Boolean grids are handled correctly by their value type. Signed distance fields,
4920 // unsigned distance fields and fog volumes have the same value type but use different
4921 // inside value classifications.
4922 const bool invertSurfaceOrientation = (!volume_to_mesh_internal::isBoolValue<InputValueType>()
4923 && (inputGrid.getGridClass() != openvdb::GRID_LEVEL_SET));
4924
4925 // references, masks and auxiliary data
4926
4927 const InputTreeType& inputTree = inputGrid.tree();
4928
4929 BoolTreeType intersectionTree(false), adaptivityMask(false);
4930
4931 if (mAdaptivityMaskTree && mAdaptivityMaskTree->type() == BoolTreeType::treeType()) {
4932 const BoolTreeType *refAdaptivityMask=
4933 static_cast<const BoolTreeType*>(mAdaptivityMaskTree.get());
4934 adaptivityMask.topologyUnion(*refAdaptivityMask);
4935 }
4936
4937 Int16TreeType signFlagsTree(0);
4938 Index32TreeType pointIndexTree(std::numeric_limits<Index32>::max());
4939
4940
4941 // collect auxiliary data
4942
4943 volume_to_mesh_internal::identifySurfaceIntersectingVoxels(
4944 intersectionTree, inputTree, isovalue);
4945
4946 volume_to_mesh_internal::applySurfaceMask(intersectionTree, adaptivityMask,
4947 inputGrid, mSurfaceMaskGrid, mInvertSurfaceMask, isovalue);
4948
4949 if (intersectionTree.empty()) return;
4950
4951 volume_to_mesh_internal::computeAuxiliaryData(
4952 signFlagsTree, pointIndexTree, intersectionTree, inputTree, isovalue);
4953
4954 intersectionTree.clear();
4955
4956 std::vector<Index32LeafNodeType*> pointIndexLeafNodes;
4957 pointIndexTree.getNodes(pointIndexLeafNodes);
4958
4959 std::vector<Int16LeafNodeType*> signFlagsLeafNodes;
4960 signFlagsTree.getNodes(signFlagsLeafNodes);
4961
4962 const tbb::blocked_range<size_t> auxiliaryLeafNodeRange(0, signFlagsLeafNodes.size());
4963
4964
4965 // optionally collect auxiliary data from a reference volume.
4966
4967 Int16TreeType* refSignFlagsTree = nullptr;
4968 Index32TreeType* refPointIndexTree = nullptr;
4969 InputTreeType const* refInputTree = nullptr;
4970
4971 if (mRefGrid && mRefGrid->type() == InputGridType::gridType()) {
4972
4973 const InputGridType* refGrid = static_cast<const InputGridType*>(mRefGrid.get());
4974 refInputTree = &refGrid->tree();
4975
4976 if (!mRefSignTree && !mRefIdxTree) {
4977
4978 // first time, collect and cache auxiliary data.
4979
4980 typename Int16TreeType::Ptr refSignFlagsTreePt(new Int16TreeType(0));
4981 typename Index32TreeType::Ptr refPointIndexTreePt(
4982 new Index32TreeType(std::numeric_limits<Index32>::max()));
4983
4984 BoolTreeType refIntersectionTree(false);
4985
4986 volume_to_mesh_internal::identifySurfaceIntersectingVoxels(
4987 refIntersectionTree, *refInputTree, isovalue);
4988
4989 volume_to_mesh_internal::computeAuxiliaryData(*refSignFlagsTreePt,
4990 *refPointIndexTreePt, refIntersectionTree, *refInputTree, isovalue);
4991
4992 mRefSignTree = refSignFlagsTreePt;
4993 mRefIdxTree = refPointIndexTreePt;
4994 }
4995
4996 if (mRefSignTree && mRefIdxTree) {
4997
4998 // get cached auxiliary data
4999
5000 refSignFlagsTree = static_cast<Int16TreeType*>(mRefSignTree.get());
5001 refPointIndexTree = static_cast<Index32TreeType*>(mRefIdxTree.get());
5002 }
5003
5004
5005 if (refSignFlagsTree && refPointIndexTree) {
5006
5007 // generate seam line sample points
5008
5009 volume_to_mesh_internal::markSeamLineData(signFlagsTree, *refSignFlagsTree);
5010
5011 if (mSeamPointListSize == 0) {
5012
5013 // count unique points on reference surface
5014
5015 std::vector<Int16LeafNodeType*> refSignFlagsLeafNodes;
5016 refSignFlagsTree->getNodes(refSignFlagsLeafNodes);
5017
5018 std::unique_ptr<Index32[]> leafNodeOffsets(
5019 new Index32[refSignFlagsLeafNodes.size()]);
5020
5021 tbb::parallel_for(tbb::blocked_range<size_t>(0, refSignFlagsLeafNodes.size()),
5022 volume_to_mesh_internal::LeafNodePointCount<Int16LeafNodeType::LOG2DIM>(
5023 refSignFlagsLeafNodes, leafNodeOffsets));
5024
5025 {
5026 Index32 count = 0;
5027 for (size_t n = 0, N = refSignFlagsLeafNodes.size(); n < N; ++n) {
5028 const Index32 tmp = leafNodeOffsets[n];
5029 leafNodeOffsets[n] = count;
5030 count += tmp;
5031 }
5032 mSeamPointListSize = size_t(count);
5033 }
5034
5035 if (mSeamPointListSize != 0) {
5036
5037 mQuantizedSeamPoints.reset(new uint32_t[mSeamPointListSize]);
5038
5039 std::memset(mQuantizedSeamPoints.get(), 0, sizeof(uint32_t) * mSeamPointListSize);
5040
5041 std::vector<Index32LeafNodeType*> refPointIndexLeafNodes;
5042 refPointIndexTree->getNodes(refPointIndexLeafNodes);
5043
5044 tbb::parallel_for(tbb::blocked_range<size_t>(0, refPointIndexLeafNodes.size()),
5045 volume_to_mesh_internal::MapPoints<Index32LeafNodeType>(
5046 refPointIndexLeafNodes, refSignFlagsLeafNodes, leafNodeOffsets));
5047 }
5048 }
5049
5050 if (mSeamPointListSize != 0) {
5051
5052 tbb::parallel_for(auxiliaryLeafNodeRange,
5053 volume_to_mesh_internal::SeamLineWeights<InputTreeType>(
5054 signFlagsLeafNodes, inputTree, *refPointIndexTree, *refSignFlagsTree,
5055 mQuantizedSeamPoints.get(), isovalue));
5056 }
5057 }
5058 }
5059
5060 const bool referenceMeshing = refSignFlagsTree && refPointIndexTree && refInputTree;
5061
5062
5063 // adapt and count unique points
5064
5065 std::unique_ptr<Index32[]> leafNodeOffsets(new Index32[signFlagsLeafNodes.size()]);
5066
5067 if (adaptive) {
5068 volume_to_mesh_internal::MergeVoxelRegions<InputGridType> mergeOp(
5069 inputGrid, pointIndexTree, pointIndexLeafNodes, signFlagsLeafNodes,
5070 isovalue, adaptivityThreshold, invertSurfaceOrientation);
5071
5072 if (mAdaptivityGrid && mAdaptivityGrid->type() == FloatGridType::gridType()) {
5073 const FloatGridType* adaptivityGrid =
5074 static_cast<const FloatGridType*>(mAdaptivityGrid.get());
5075 mergeOp.setSpatialAdaptivity(*adaptivityGrid);
5076 }
5077
5078 if (!adaptivityMask.empty()) {
5079 mergeOp.setAdaptivityMask(adaptivityMask);
5080 }
5081
5082 if (referenceMeshing) {
5083 mergeOp.setRefSignFlagsData(*refSignFlagsTree, float(mSecAdaptivity));
5084 }
5085
5086 tbb::parallel_for(auxiliaryLeafNodeRange, mergeOp);
5087
5088 volume_to_mesh_internal::AdaptiveLeafNodePointCount<Index32LeafNodeType>
5089 op(pointIndexLeafNodes, signFlagsLeafNodes, leafNodeOffsets);
5090
5091 tbb::parallel_for(auxiliaryLeafNodeRange, op);
5092
5093 } else {
5094
5095 volume_to_mesh_internal::LeafNodePointCount<Int16LeafNodeType::LOG2DIM>
5096 op(signFlagsLeafNodes, leafNodeOffsets);
5097
5098 tbb::parallel_for(auxiliaryLeafNodeRange, op);
5099 }
5100
5101
5102 {
5103 Index32 pointCount = 0;
5104 for (size_t n = 0, N = signFlagsLeafNodes.size(); n < N; ++n) {
5105 const Index32 tmp = leafNodeOffsets[n];
5106 leafNodeOffsets[n] = pointCount;
5107 pointCount += tmp;
5108 }
5109
5110 mPointListSize = size_t(pointCount);
5111 mPoints.reset(new openvdb::Vec3s[mPointListSize]);
5112 mPointFlags.clear();
5113 }
5114
5115
5116 // compute points
5117
5118 {
5119 volume_to_mesh_internal::ComputePoints<InputTreeType>
5120 op(mPoints.get(), inputTree, pointIndexLeafNodes,
5121 signFlagsLeafNodes, leafNodeOffsets, transform, mIsovalue);
5122
5123 if (referenceMeshing) {
5124 mPointFlags.resize(mPointListSize);
5125 op.setRefData(*refInputTree, *refPointIndexTree, *refSignFlagsTree,
5126 mQuantizedSeamPoints.get(), mPointFlags.data());
5127 }
5128
5129 tbb::parallel_for(auxiliaryLeafNodeRange, op);
5130 }
5131
5132
5133 // compute polygons
5134
5135 mPolygonPoolListSize = signFlagsLeafNodes.size();
5136 mPolygons.reset(new PolygonPool[mPolygonPoolListSize]);
5137
5138 if (adaptive) {
5139
5140 using PrimBuilder = volume_to_mesh_internal::AdaptivePrimBuilder;
5141
5142 volume_to_mesh_internal::ComputePolygons<Int16TreeType, PrimBuilder>
5143 op(signFlagsLeafNodes, signFlagsTree, pointIndexTree,
5144 mPolygons, invertSurfaceOrientation);
5145
5146 if (referenceMeshing) {
5147 op.setRefSignTree(refSignFlagsTree);
5148 }
5149
5150 tbb::parallel_for(auxiliaryLeafNodeRange, op);
5151
5152 } else {
5153
5154 using PrimBuilder = volume_to_mesh_internal::UniformPrimBuilder;
5155
5156 volume_to_mesh_internal::ComputePolygons<Int16TreeType, PrimBuilder>
5157 op(signFlagsLeafNodes, signFlagsTree, pointIndexTree,
5158 mPolygons, invertSurfaceOrientation);
5159
5160 if (referenceMeshing) {
5161 op.setRefSignTree(refSignFlagsTree);
5162 }
5163
5164 tbb::parallel_for(auxiliaryLeafNodeRange, op);
5165 }
5166
5167
5168 signFlagsTree.clear();
5169 pointIndexTree.clear();
5170
5171
5172 if (adaptive && mRelaxDisorientedTriangles) {
5173 volume_to_mesh_internal::relaxDisorientedTriangles(invertSurfaceOrientation,
5174 inputTree, transform, mPolygons, mPolygonPoolListSize, mPoints, mPointListSize);
5175 }
5176
5177
5178 if (referenceMeshing) {
5179 volume_to_mesh_internal::subdivideNonplanarSeamLineQuads(
5180 mPolygons, mPolygonPoolListSize, mPoints, mPointListSize, mPointFlags);
5181
5182 volume_to_mesh_internal::reviseSeamLineFlags(mPolygons, mPolygonPoolListSize, mPointFlags);
5183 }
5184
5185}
5186
5187
5188////////////////////////////////////////
5189
5190
5191template<typename GridType>
5193 const GridType& grid,
5194 std::vector<Vec3s>& points,
5195 std::vector<Vec3I>& triangles,
5196 std::vector<Vec4I>& quads,
5197 double isovalue,
5198 double adaptivity,
5199 bool relaxDisorientedTriangles)
5200{
5201 volume_to_mesh_internal::doVolumeToMesh(grid, points, triangles, quads,
5202 isovalue, adaptivity, relaxDisorientedTriangles);
5203}
5204
5205template<typename GridType>
5207 const GridType& grid,
5208 std::vector<Vec3s>& points,
5209 std::vector<Vec4I>& quads,
5210 double isovalue)
5211{
5212 std::vector<Vec3I> triangles;
5213 volumeToMesh(grid, points, triangles, quads, isovalue, 0.0, true);
5214}
5215
5216
5217////////////////////////////////////////
5218
5219
5220// Explicit Template Instantiation
5221
5222#ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION
5223
5224#ifdef OPENVDB_INSTANTIATE_VOLUMETOMESH
5226#endif
5227
5228#define _FUNCTION(TreeT) \
5229 void volumeToMesh(const Grid<TreeT>&, std::vector<Vec3s>&, std::vector<Vec4I>&, double)
5231#undef _FUNCTION
5232
5233#define _FUNCTION(TreeT) \
5234 void volumeToMesh(const Grid<TreeT>&, std::vector<Vec3s>&, std::vector<Vec3I>&, std::vector<Vec4I>&, double, double, bool)
5236#undef _FUNCTION
5237
5238#endif // OPENVDB_USE_EXPLICIT_INSTANTIATION
5239
5240
5241} // namespace tools
5242} // namespace OPENVDB_VERSION_NAME
5243} // namespace openvdb
5244
5245#endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
#define OPENVDB_DEPRECATED_MESSAGE(msg)
Definition Platform.h:158
ValueAccessors are designed to help accelerate accesses into the OpenVDB Tree structures by storing c...
Abstract base class for typed grids.
Definition Grid.h:79
SharedPtr< const GridBase > ConstPtr
Definition Grid.h:82
Container class that associates a tree with a transform and metadata.
Definition Grid.h:571
void clear() override
Empty this grid, so that all voxels become inactive background voxels.
Definition Grid.h:727
Definition Exceptions.h:65
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:252
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:26
Coord offsetBy(Int32 dx, Int32 dy, Int32 dz) const
Definition Coord.h:92
Coord & reset(Int32 x, Int32 y, Int32 z)
Reset all three coordinates with the specified arguments.
Definition Coord.h:70
Mat3 transpose() const
returns transpose of this
Definition Mat3.h:454
static const Mat3< double > & identity()
Definition Mat3.h:121
Definition Transform.h:40
Definition Vec3.h:25
T dot(const Vec3< T > &v) const
Dot product.
Definition Vec3.h:192
T & z()
Definition Vec3.h:88
bool normalize(T eps=T(1.0e-7))
this = normalized this
Definition Vec3.h:363
Definition Vec4.h:25
Collection of quads and triangles.
Definition VolumeToMesh.h:104
void copy(const PolygonPool &rhs)
Definition VolumeToMesh.h:4710
bool trimTriangles(const size_t n, bool reallocate=false)
Definition VolumeToMesh.h:4793
const openvdb::Vec3I & triangle(size_t n) const
Definition VolumeToMesh.h:130
void resetQuads(size_t size)
Definition VolumeToMesh.h:4728
const size_t & numTriangles() const
Definition VolumeToMesh.h:127
void clearQuads()
Definition VolumeToMesh.h:4737
void clearTriangles()
Definition VolumeToMesh.h:4755
const char & quadFlags(size_t n) const
Definition VolumeToMesh.h:136
openvdb::Vec4I & quad(size_t n)
Definition VolumeToMesh.h:123
openvdb::Vec3I & triangle(size_t n)
Definition VolumeToMesh.h:129
char & triangleFlags(size_t n)
Definition VolumeToMesh.h:138
const size_t & numQuads() const
Definition VolumeToMesh.h:121
bool trimTrinagles(const size_t n, bool reallocate=false)
Definition VolumeToMesh.h:149
const char & triangleFlags(size_t n) const
Definition VolumeToMesh.h:139
void resetTriangles(size_t size)
Definition VolumeToMesh.h:4746
const openvdb::Vec4I & quad(size_t n) const
Definition VolumeToMesh.h:124
PolygonPool()
Definition VolumeToMesh.h:4686
char & quadFlags(size_t n)
Definition VolumeToMesh.h:135
bool trimQuads(const size_t n, bool reallocate=false)
Definition VolumeToMesh.h:4764
SharedPtr< TreeBase > Ptr
Definition Tree.h:40
SharedPtr< const TreeBase > ConstPtr
Definition Tree.h:41
@ IndexArray
Definition NanoVDB.h:415
Mat3< double > Mat3d
Definition Mat3.h:833
Vec3< double > Vec3d
Definition Vec3.h:708
Vec4< int32_t > Vec4i
Definition Vec4.h:602
Vec3< int32_t > Vec3i
Definition Vec3.h:705
Vec3< float > Vec3s
Definition Vec3.h:707
Definition Grid.h:1187
Index64 pointCount(const PointDataTreeT &tree, const FilterT &filter=NullFilter(), const bool threaded=true)
Count the total number of points in a PointDataTree.
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition Composite.h:110
void volumeToMesh(const GridType &grid, std::vector< Vec3s > &points, std::vector< Vec4I > &quads, double isovalue=0.0)
Uniformly mesh any scalar grid that has a continuous isosurface.
Definition VolumeToMesh.h:5206
@ POLYFLAG_FRACTURE_SEAM
Definition VolumeToMesh.h:99
@ POLYFLAG_EXTERIOR
Definition VolumeToMesh.h:99
@ POLYFLAG_SUBDIVIDED
Definition VolumeToMesh.h:99
std::unique_ptr< openvdb::Vec3s[]> PointList
Point and primitive list types.
Definition VolumeToMesh.h:167
std::unique_ptr< PolygonPool[]> PolygonPoolList
Point and primitive list types.
Definition VolumeToMesh.h:168
Vec3d findFeaturePoint(const std::vector< Vec3d > &points, const std::vector< Vec3d > &normals)
Given a set of tangent elements, points with corresponding normals, this method returns the intersect...
Definition VolumeToMesh.h:286
Definition PointDataGrid.h:48
ValueAccessorImpl< TreeType, IsSafe, MutexType, openvdb::make_index_sequence< CacheLevels > > ValueAccessor
Default alias for a ValueAccessor. This is simply a helper alias for the generic definition but takes...
Definition ValueAccessor.h:86
constexpr Index32 INVALID_IDX
Definition Util.h:19
Index32 Index
Definition Types.h:34
math::Vec4< Index32 > Vec4I
Definition Types.h:69
int16_t Int16
Definition Types.h:35
@ GRID_LEVEL_SET
Definition Types.h:526
uint32_t Index32
Definition Types.h:32
math::Vec3< Index32 > Vec3I
Definition Types.h:54
tree::TreeBase TreeBase
Definition Grid.h:27
Definition Exceptions.h:13
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
static Vec3< typename Accessor::ValueType > result(const Accessor &grid, const Coord &ijk)
Definition Operators.h:103
Mesh any scalar grid that has a continuous isosurface.
Definition VolumeToMesh.h:177
VolumeToMesh(double isovalue=0, double adaptivity=0, bool relaxDisorientedTriangles=true)
Definition VolumeToMesh.h:4825
void setRefGrid(const GridBase::ConstPtr &grid, double secAdaptivity=0)
When surfacing fractured SDF fragments, the original unfractured SDF grid can be used to eliminate se...
Definition VolumeToMesh.h:4849
PolygonPoolList & polygonPoolList()
Definition VolumeToMesh.h:195
PointList & pointList()
Definition VolumeToMesh.h:191
size_t polygonPoolListSize() const
Definition VolumeToMesh.h:194
std::vector< uint8_t > & pointFlags()
Definition VolumeToMesh.h:198
size_t pointListSize() const
Definition VolumeToMesh.h:190
const PointList & pointList() const
Definition VolumeToMesh.h:192
const PolygonPoolList & polygonPoolList() const
Definition VolumeToMesh.h:196
void setSpatialAdaptivity(const GridBase::ConstPtr &grid)
Definition VolumeToMesh.h:4871
void operator()(const InputGridType &)
Main call.
Definition VolumeToMesh.h:4886
void setSurfaceMask(const GridBase::ConstPtr &mask, bool invertMask=false)
Definition VolumeToMesh.h:4863
void setAdaptivityMask(const TreeBase::ConstPtr &tree)
Definition VolumeToMesh.h:4878
const std::vector< uint8_t > & pointFlags() const
Definition VolumeToMesh.h:199
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:291
#define OPENVDB_NUMERIC_TREE_INSTANTIATE(Function)
Definition version.h.in:236