OpenVDB  8.0.1
LevelSetFracture.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 //
9 
10 #ifndef OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED
11 #define OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED
12 
13 #include <openvdb/Grid.h>
14 #include <openvdb/math/Quat.h>
16 
17 #include "Composite.h" // for csgIntersectionCopy() and csgDifferenceCopy()
18 #include "GridTransformer.h" // for resampleToMatch()
19 #include "LevelSetUtil.h" // for sdfSegmentation()
20 
21 #include <algorithm> // for std::max(), std::min()
22 #include <limits>
23 #include <list>
24 #include <vector>
25 
26 #include <tbb/blocked_range.h>
27 #include <tbb/parallel_reduce.h>
28 
29 
30 namespace openvdb {
32 namespace OPENVDB_VERSION_NAME {
33 namespace tools {
34 
36 template<class GridType, class InterruptType = util::NullInterrupter>
38 {
39 public:
40  using Vec3sList = std::vector<Vec3s>;
41  using QuatsList = std::vector<math::Quats>;
42  using GridPtrList = std::list<typename GridType::Ptr>;
43  using GridPtrListIter = typename GridPtrList::iterator;
44 
45 
49  explicit LevelSetFracture(InterruptType* interrupter = nullptr);
50 
69  void fracture(GridPtrList& grids, const GridType& cutter, bool segment = false,
70  const Vec3sList* points = nullptr, const QuatsList* rotations = nullptr,
71  bool cutterOverlap = true);
72 
74  GridPtrList& fragments() { return mFragments; }
75 
77  void clear() { mFragments.clear(); }
78 
79 private:
80  // disallow copy by assignment
81  void operator=(const LevelSetFracture&) {}
82 
83  bool wasInterrupted(int percent = -1) const {
84  return mInterrupter && mInterrupter->wasInterrupted(percent);
85  }
86 
87  bool isValidFragment(GridType&) const;
88  void segmentFragments(GridPtrList&) const;
89  void process(GridPtrList&, const GridType& cutter);
90 
91  InterruptType* mInterrupter;
92  GridPtrList mFragments;
93 };
94 
95 
97 
98 
99 // Internal utility objects and implementation details
100 
101 namespace level_set_fracture_internal {
102 
103 
104 template<typename LeafNodeType>
106 
107  using ValueType = typename LeafNodeType::ValueType;
108 
109  FindMinMaxVoxelValue(const std::vector<const LeafNodeType*>& nodes)
110  : minValue(std::numeric_limits<ValueType>::max())
111  , maxValue(-minValue)
112  , mNodes(nodes.empty() ? nullptr : &nodes.front())
113  {
114  }
115 
117  : minValue(std::numeric_limits<ValueType>::max())
118  , maxValue(-minValue)
119  , mNodes(rhs.mNodes)
120  {
121  }
122 
123  void operator()(const tbb::blocked_range<size_t>& range) {
124  for (size_t n = range.begin(), N = range.end(); n < N; ++n) {
125  const ValueType* data = mNodes[n]->buffer().data();
126  for (Index i = 0; i < LeafNodeType::SIZE; ++i) {
127  minValue = std::min(minValue, data[i]);
128  maxValue = std::max(maxValue, data[i]);
129  }
130  }
131  }
132 
134  minValue = std::min(minValue, rhs.minValue);
135  maxValue = std::max(maxValue, rhs.maxValue);
136  }
137 
138  ValueType minValue, maxValue;
139 
140  LeafNodeType const * const * const mNodes;
141 }; // struct FindMinMaxVoxelValue
142 
143 
144 } // namespace level_set_fracture_internal
145 
146 
148 
149 
150 template<class GridType, class InterruptType>
152  : mInterrupter(interrupter)
153  , mFragments()
154 {
155 }
156 
157 
158 template<class GridType, class InterruptType>
159 void
161  bool segmentation, const Vec3sList* points, const QuatsList* rotations, bool cutterOverlap)
162 {
163  // We can process all incoming grids with the same cutter instance,
164  // this optimization is enabled by the requirement of having matching
165  // transforms between all incoming grids and the cutter object.
166  if (points && points->size() != 0) {
167 
168 
169  math::Transform::Ptr originalCutterTransform = cutter.transform().copy();
170  GridType cutterGrid(*const_cast<GridType*>(&cutter), ShallowCopy());
171 
172  const bool hasInstanceRotations =
173  points && rotations && points->size() == rotations->size();
174 
175  // for each instance point..
176  for (size_t p = 0, P = points->size(); p < P; ++p) {
177  int percent = int((float(p) / float(P)) * 100.0);
178  if (wasInterrupted(percent)) break;
179 
180  GridType instCutterGrid;
181  instCutterGrid.setTransform(originalCutterTransform->copy());
182  math::Transform::Ptr xform = originalCutterTransform->copy();
183 
184  if (hasInstanceRotations) {
185  const Vec3s& rot = (*rotations)[p].eulerAngles(math::XYZ_ROTATION);
186  xform->preRotate(rot[0], math::X_AXIS);
187  xform->preRotate(rot[1], math::Y_AXIS);
188  xform->preRotate(rot[2], math::Z_AXIS);
189  xform->postTranslate((*points)[p]);
190  } else {
191  xform->postTranslate((*points)[p]);
192  }
193 
194  cutterGrid.setTransform(xform);
195 
196  // Since there is no scaling, use the generic resampler instead of
197  // the more expensive level set rebuild tool.
198  if (mInterrupter != nullptr) {
199 
200  if (hasInstanceRotations) {
201  doResampleToMatch<BoxSampler>(cutterGrid, instCutterGrid, *mInterrupter);
202  } else {
203  doResampleToMatch<PointSampler>(cutterGrid, instCutterGrid, *mInterrupter);
204  }
205  } else {
206  util::NullInterrupter interrupter;
207  if (hasInstanceRotations) {
208  doResampleToMatch<BoxSampler>(cutterGrid, instCutterGrid, interrupter);
209  } else {
210  doResampleToMatch<PointSampler>(cutterGrid, instCutterGrid, interrupter);
211  }
212  }
213 
214  if (wasInterrupted(percent)) break;
215 
216  if (cutterOverlap && !mFragments.empty()) process(mFragments, instCutterGrid);
217  process(grids, instCutterGrid);
218  }
219 
220  } else {
221  // use cutter in place
222  if (cutterOverlap && !mFragments.empty()) process(mFragments, cutter);
223  process(grids, cutter);
224  }
225 
226  if (segmentation) {
227  segmentFragments(mFragments);
228  segmentFragments(grids);
229  }
230 }
231 
232 
233 template<class GridType, class InterruptType>
234 bool
236 {
237  using LeafNodeType = typename GridType::TreeType::LeafNodeType;
238 
239  if (grid.tree().leafCount() < 9) {
240 
241  std::vector<const LeafNodeType*> nodes;
242  grid.tree().getNodes(nodes);
243 
244  Index64 activeVoxelCount = 0;
245 
246  for (size_t n = 0, N = nodes.size(); n < N; ++n) {
247  activeVoxelCount += nodes[n]->onVoxelCount();
248  }
249 
250  if (activeVoxelCount < 27) return false;
251 
252  level_set_fracture_internal::FindMinMaxVoxelValue<LeafNodeType> op(nodes);
253  tbb::parallel_reduce(tbb::blocked_range<size_t>(0, nodes.size()), op);
254 
255  if ((op.minValue < 0) == (op.maxValue < 0)) return false;
256  }
257 
258  return true;
259 }
260 
261 
262 template<class GridType, class InterruptType>
263 void
264 LevelSetFracture<GridType, InterruptType>::segmentFragments(GridPtrList& grids) const
265 {
266  GridPtrList newFragments;
267 
268  for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) {
269 
270  std::vector<typename GridType::Ptr> segments;
271  segmentSDF(*(*it), segments);
272 
273  for (size_t n = 0, N = segments.size(); n < N; ++n) {
274  newFragments.push_back(segments[n]);
275  }
276  }
277 
278  grids.swap(newFragments);
279 }
280 
281 
282 template<class GridType, class InterruptType>
283 void
284 LevelSetFracture<GridType, InterruptType>::process(
285  GridPtrList& grids, const GridType& cutter)
286 {
287  using GridPtr = typename GridType::Ptr;
288  GridPtrList newFragments;
289 
290  for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) {
291 
292  if (wasInterrupted()) break;
293 
294  GridPtr& grid = *it;
295 
296  GridPtr fragment = csgIntersectionCopy(*grid, cutter);
297  if (!isValidFragment(*fragment)) continue;
298 
299  GridPtr residual = csgDifferenceCopy(*grid, cutter);
300  if (!isValidFragment(*residual)) continue;
301 
302  newFragments.push_back(fragment);
303 
304  grid->tree().clear();
305  grid->tree().merge(residual->tree());
306  }
307 
308  if (!newFragments.empty()) {
309  mFragments.splice(mFragments.end(), newFragments);
310  }
311 }
312 
313 } // namespace tools
314 } // namespace OPENVDB_VERSION_NAME
315 } // namespace openvdb
316 
317 #endif // OPENVDB_TOOLS_LEVELSETFRACTURE_HAS_BEEN_INCLUDED
Functions to efficiently perform various compositing operations on grids.
Miscellaneous utility methods that operate primarily or exclusively on level set grids.
Tag dispatch class that distinguishes shallow copy constructors from deep copy constructors.
Definition: openvdb/Types.h:539
SharedPtr< Transform > Ptr
Definition: Transform.h:42
Level set fracturing.
Definition: LevelSetFracture.h:38
GridPtrList & fragments()
Return a list of new fragments, not including the residuals from the input grids.
Definition: LevelSetFracture.h:74
std::list< typename GridType::Ptr > GridPtrList
Definition: LevelSetFracture.h:42
std::vector< math::Quats > QuatsList
Definition: LevelSetFracture.h:41
typename GridPtrList::iterator GridPtrListIter
Definition: LevelSetFracture.h:43
void clear()
Remove all elements from the fragment list.
Definition: LevelSetFracture.h:77
std::vector< Vec3s > Vec3sList
Definition: LevelSetFracture.h:40
void fracture(GridPtrList &grids, const GridType &cutter, bool segment=false, const Vec3sList *points=nullptr, const QuatsList *rotations=nullptr, bool cutterOverlap=true)
Divide volumes represented by level set grids into multiple, disjoint pieces by intersecting them wit...
Definition: LevelSetFracture.h:160
@ Z_AXIS
Definition: Math.h:907
@ X_AXIS
Definition: Math.h:905
@ Y_AXIS
Definition: Math.h:906
Vec3< float > Vec3s
Definition: Vec3.h:667
@ XYZ_ROTATION
Definition: Math.h:912
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:103
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:107
GridOrTreeT::Ptr csgDifferenceCopy(const GridOrTreeT &a, const GridOrTreeT &b)
Threaded CSG difference operation that produces a new grid or tree from immutable inputs.
Definition: Composite.h:959
void segmentSDF(const GridOrTreeType &volume, std::vector< typename GridOrTreeType::Ptr > &segments)
Separates disjoint SDF surfaces into distinct grids or trees.
Definition: LevelSetUtil.h:2546
GridOrTreeT::Ptr csgIntersectionCopy(const GridOrTreeT &a, const GridOrTreeT &b)
Threaded CSG intersection operation that produces a new grid or tree from immutable inputs.
Definition: Composite.h:945
bool wasInterrupted(T *i, int percent=-1)
Definition: NullInterrupter.h:49
Index32 Index
Definition: openvdb/Types.h:32
uint64_t Index64
Definition: openvdb/Types.h:31
openvdb::GridBase::Ptr GridPtr
Definition: openvdb_houdini/openvdb_houdini/Utils.h:34
Definition: openvdb/Exceptions.h:13
Definition: Coord.h:587
FindMinMaxVoxelValue(FindMinMaxVoxelValue &rhs, tbb::split)
Definition: LevelSetFracture.h:116
void operator()(const tbb::blocked_range< size_t > &range)
Definition: LevelSetFracture.h:123
typename LeafNodeType::ValueType ValueType
Definition: LevelSetFracture.h:107
FindMinMaxVoxelValue(const std::vector< const LeafNodeType * > &nodes)
Definition: LevelSetFracture.h:109
LeafNodeType const *const *const mNodes
Definition: LevelSetFracture.h:140
void join(FindMinMaxVoxelValue &rhs)
Definition: LevelSetFracture.h:133
Dummy NOOP interrupter class defining interface.
Definition: NullInterrupter.h:26
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:101
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:153