HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PointMask.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file points/PointMask.h
5 ///
6 /// @author Dan Bailey
7 ///
8 /// @brief Methods for extracting masks from VDB Point grids.
9 
10 #ifndef OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
11 #define OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
12 
13 #include <openvdb/openvdb.h>
14 #include <openvdb/tools/ValueTransformer.h> // valxform::SumOp
15 
16 #include "PointDataGrid.h"
17 #include "IndexFilter.h"
18 
19 #include <tbb/combinable.h>
20 
21 #include <type_traits>
22 #include <vector>
23 
24 
25 namespace openvdb {
27 namespace OPENVDB_VERSION_NAME {
28 namespace points {
29 
30 /// @brief Extract a Mask Tree from a Point Data Tree
31 /// @param tree the PointDataTree to extract the mask from.
32 /// @param filter an optional index filter
33 /// @param threaded enable or disable threading (threading is enabled by default)
34 template <typename PointDataTreeT,
35  typename MaskTreeT = typename PointDataTreeT::template ValueConverter<bool>::Type,
36  typename FilterT = NullFilter>
39 convertPointsToMask(const PointDataTreeT& tree,
40  const FilterT& filter = NullFilter(),
41  bool threaded = true);
42 
43 /// @brief Extract a Mask Grid from a Point Data Grid
44 /// @param grid the PointDataGrid to extract the mask from.
45 /// @param filter an optional index filter
46 /// @param threaded enable or disable threading (threading is enabled by default)
47 /// @note this method is only available for Bool Grids and Mask Grids
48 template <typename PointDataGridT,
49  typename MaskGridT = typename PointDataGridT::template ValueConverter<bool>::Type,
50  typename FilterT = NullFilter>
53 convertPointsToMask(const PointDataGridT& grid,
54  const FilterT& filter = NullFilter(),
55  bool threaded = true);
56 
57 /// @brief Extract a Mask Grid from a Point Data Grid using a new transform
58 /// @param grid the PointDataGrid to extract the mask from.
59 /// @param transform target transform for the mask.
60 /// @param filter an optional index filter
61 /// @param threaded enable or disable threading (threading is enabled by default)
62 /// @note this method is only available for Bool Grids and Mask Grids
63 template <typename PointDataGridT,
64  typename MaskT = typename PointDataGridT::template ValueConverter<bool>::Type,
65  typename FilterT = NullFilter>
67  typename MaskT::Ptr>::type
68 convertPointsToMask(const PointDataGridT& grid,
69  const openvdb::math::Transform& transform,
70  const FilterT& filter = NullFilter(),
71  bool threaded = true);
72 
73 /// @brief No-op deformer (adheres to the deformer interface documented in PointMove.h)
75 {
76  template <typename LeafT>
77  void reset(LeafT&, size_t /*idx*/ = 0) { }
78 
79  template <typename IterT>
80  void apply(Vec3d&, IterT&) const { }
81 };
82 
83 /// @brief Deformer Traits for optionally configuring deformers to be applied
84 /// in index-space. The default is world-space.
85 template <typename DeformerT>
87 {
88  static const bool IndexSpace = false;
89 };
90 
91 
92 ////////////////////////////////////////
93 
94 /// @cond OPENVDB_DOCS_INTERNAL
95 
96 namespace point_mask_internal {
97 
98 template <typename LeafT>
99 void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value)
100 {
101  leaf.modifyValue(offset, tools::valxform::SumOp<typename LeafT::ValueType>(value));
102 }
103 
104 // overload PointDataLeaf access to use setOffsetOn(), as modifyValue()
105 // is intentionally disabled to avoid accidental usage
106 
107 template <typename T, Index Log2Dim>
108 void voxelSum(PointDataLeafNode<T, Log2Dim>& leaf, const Index offset,
109  const typename PointDataLeafNode<T, Log2Dim>::ValueType& value)
110 {
111  leaf.setOffsetOn(offset, leaf.getValue(offset) + value);
112 }
113 
114 
115 /// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values
116 /// This class is designed to work with thread local storage containers such as tbb::combinable
117 template<typename GridT>
118 struct GridCombinerOp
119 {
120  using CombinableT = typename tbb::combinable<GridT>;
121 
122  using TreeT = typename GridT::TreeType;
123  using LeafT = typename TreeT::LeafNodeType;
124  using ValueType = typename TreeT::ValueType;
125  using SumOp = tools::valxform::SumOp<typename TreeT::ValueType>;
126 
127  GridCombinerOp(GridT& grid)
128  : mTree(grid.tree()) {}
129 
130  void operator()(const GridT& grid)
131  {
132  for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) {
133  auto* newLeaf = mTree.probeLeaf(leaf->origin());
134  if (!newLeaf) {
135  // if the leaf doesn't yet exist in the new tree, steal it
136  auto& tree = const_cast<GridT&>(grid).tree();
137  mTree.addLeaf(tree.template stealNode<LeafT>(leaf->origin(),
138  zeroVal<ValueType>(), false));
139  }
140  else {
141  // otherwise increment existing values
142  for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
143  voxelSum(*newLeaf, iter.offset(), ValueType(*iter));
144  }
145  }
146  }
147  }
148 
149 private:
150  TreeT& mTree;
151 }; // struct GridCombinerOp
152 
153 
154 /// @brief Compute scalar grid from PointDataGrid while evaluating the point filter
155 template <typename TreeT, typename PointDataTreeT, typename FilterT>
156 struct PointsToScalarOp
157 {
158  using LeafT = typename TreeT::LeafNodeType;
159  using ValueT = typename LeafT::ValueType;
160  // This method is also used by PointCount so ValueT may not be bool
161  static constexpr bool IsBool =
163 
164  PointsToScalarOp(const PointDataTreeT& tree,
165  const FilterT& filter)
166  : mPointDataAccessor(tree)
167  , mFilter(filter) {}
168 
169  void operator()(LeafT& leaf, size_t /*idx*/) const
170  {
171  // assumes matching topology
172  const auto* const pointLeaf =
173  mPointDataAccessor.probeConstLeaf(leaf.origin());
174  assert(pointLeaf);
175 
176  for (auto value = leaf.beginValueOn(); value; ++value) {
177  const auto iter = pointLeaf->beginIndexVoxel(value.getCoord(), mFilter);
178  if (IsBool) {
179  if (!iter) value.setValueOn(false);
180  }
181  else {
182  const Index64 count = points::iterCount(iter);
183  if (count > Index64(0)) value.setValue(ValueT(count));
184  else value.setValueOn(false);
185  }
186  }
187  }
188 
189 private:
190  const tree::ValueAccessor<const PointDataTreeT> mPointDataAccessor;
191  const FilterT& mFilter;
192 }; // struct PointsToScalarOp
193 
194 
195 /// @brief Compute scalar grid from PointDataGrid using a different transform
196 /// and while evaluating the point filter
197 template <typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
198 struct PointsToTransformedScalarOp
199 {
200  using PointDataLeafT = typename PointDataGridT::TreeType::LeafNodeType;
201  using ValueT = typename GridT::TreeType::ValueType;
202  using HandleT = AttributeHandle<Vec3f>;
203  using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
204 
205  PointsToTransformedScalarOp(const math::Transform& targetTransform,
206  const math::Transform& sourceTransform,
207  const FilterT& filter,
208  const DeformerT& deformer,
209  CombinableT& combinable)
210  : mTargetTransform(targetTransform)
211  , mSourceTransform(sourceTransform)
212  , mFilter(filter)
213  , mDeformer(deformer)
214  , mCombinable(combinable) { }
215 
216  void operator()(const PointDataLeafT& leaf, size_t idx) const
217  {
218  DeformerT deformer(mDeformer);
219 
220  auto& grid = mCombinable.local();
221  auto& countTree = grid.tree();
222  tree::ValueAccessor<typename GridT::TreeType> accessor(countTree);
223 
224  deformer.reset(leaf, idx);
225 
226  auto handle = HandleT::create(leaf.constAttributeArray("P"));
227 
228  for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
229 
230  // extract index-space position
231 
232  Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
233 
234  // if deformer is designed to be used in index-space, perform deformation prior
235  // to transforming position to world-space, otherwise perform deformation afterwards
236 
237  if (DeformerTraits<DeformerT>::IndexSpace) {
238  deformer.template apply<decltype(iter)>(position, iter);
239  position = mSourceTransform.indexToWorld(position);
240  }
241  else {
242  position = mSourceTransform.indexToWorld(position);
243  deformer.template apply<decltype(iter)>(position, iter);
244  }
245 
246  // determine coord of target grid
247 
248  const Coord ijk = mTargetTransform.worldToIndexCellCentered(position);
249 
250  // increment count in target voxel
251 
252  auto* newLeaf = accessor.touchLeaf(ijk);
253  assert(newLeaf);
254  voxelSum(*newLeaf, newLeaf->coordToOffset(ijk), ValueT(1));
255  }
256  }
257 
258 private:
259  const openvdb::math::Transform& mTargetTransform;
260  const openvdb::math::Transform& mSourceTransform;
261  const FilterT& mFilter;
262  const DeformerT& mDeformer;
263  CombinableT& mCombinable;
264 }; // struct PointsToTransformedScalarOp
265 
266 
267 template<typename TreeT, typename PointDataTreeT, typename FilterT>
268 inline typename TreeT::Ptr convertPointsToScalar(
269  const PointDataTreeT& points,
270  const FilterT& filter,
271  bool threaded = true)
272 {
273  using point_mask_internal::PointsToScalarOp;
274 
275  using ValueT = typename TreeT::ValueType;
276 
277  // copy the topology from the points tree
278 
279  typename TreeT::Ptr tree(new TreeT(/*background=*/false));
280  tree->topologyUnion(points);
281 
282  // early exit if no leaves
283 
284  if (points.leafCount() == 0) return tree;
285 
286  // early exit if mask and no group logic
287 
288  if (std::is_same<ValueT, bool>::value && filter.state() == index::ALL) return tree;
289 
290  // evaluate point group filters to produce a subset of the generated mask
291 
292  tree::LeafManager<TreeT> leafManager(*tree);
293 
294  if (filter.state() == index::ALL) {
295  NullFilter nullFilter;
296  PointsToScalarOp<TreeT, PointDataTreeT, NullFilter> pointsToScalarOp(
297  points, nullFilter);
298  leafManager.foreach(pointsToScalarOp, threaded);
299  } else {
300  // build mask from points in parallel only where filter evaluates to true
301  PointsToScalarOp<TreeT, PointDataTreeT, FilterT> pointsToScalarOp(
302  points, filter);
303  leafManager.foreach(pointsToScalarOp, threaded);
304  }
305 
306  return tree;
307 }
308 
309 
310 template<typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
311 inline typename GridT::Ptr convertPointsToScalar(
312  PointDataGridT& points,
313  const math::Transform& transform,
314  const FilterT& filter,
315  const DeformerT& deformer,
316  bool threaded = true)
317 {
318  using point_mask_internal::PointsToTransformedScalarOp;
319  using point_mask_internal::GridCombinerOp;
320 
321  using CombinerOpT = GridCombinerOp<GridT>;
322  using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
323 
324  typename GridT::Ptr grid = GridT::create();
325  grid->setTransform(transform.copy());
326 
327  // use the simpler method if the requested transform matches the existing one
328 
329  const math::Transform& pointsTransform = points.constTransform();
330 
331  if (transform == pointsTransform && std::is_same<NullDeformer, DeformerT>()) {
332  using TreeT = typename GridT::TreeType;
333  typename TreeT::Ptr tree =
334  convertPointsToScalar<TreeT>(points.tree(), filter, threaded);
335  grid->setTree(tree);
336  return grid;
337  }
338 
339  // early exit if no leaves
340 
341  if (points.constTree().leafCount() == 0) return grid;
342 
343  // compute mask grids in parallel using new transform
344 
345  CombinableT combiner;
346 
347  tree::LeafManager<typename PointDataGridT::TreeType> leafManager(points.tree());
348 
349  if (filter.state() == index::ALL) {
350  NullFilter nullFilter;
351  PointsToTransformedScalarOp<GridT, PointDataGridT, NullFilter, DeformerT> pointsToScalarOp(
352  transform, pointsTransform, nullFilter, deformer, combiner);
353  leafManager.foreach(pointsToScalarOp, threaded);
354  } else {
355  PointsToTransformedScalarOp<GridT, PointDataGridT, FilterT, DeformerT> pointsToScalarOp(
356  transform, pointsTransform, filter, deformer, combiner);
357  leafManager.foreach(pointsToScalarOp, threaded);
358  }
359 
360  // combine the mask grids into one
361 
362  CombinerOpT combineOp(*grid);
363  combiner.combine_each(combineOp);
364 
365  return grid;
366 }
367 
368 
369 } // namespace point_mask_internal
370 
371 /// @endcond
372 
373 ////////////////////////////////////////
374 
375 
376 template <typename PointDataTreeT, typename MaskTreeT, typename FilterT>
379 convertPointsToMask(const PointDataTreeT& tree,
380  const FilterT& filter,
381  bool threaded)
382 {
383  return point_mask_internal::convertPointsToScalar<MaskTreeT>(
384  tree, filter, threaded);
385 }
386 
387 
388 template<typename PointDataGridT, typename MaskGridT, typename FilterT>
392  const PointDataGridT& points,
393  const FilterT& filter,
394  bool threaded)
395 {
396  using PointDataTreeT = typename PointDataGridT::TreeType;
397  using MaskTreeT = typename MaskGridT::TreeType;
398 
399  typename MaskTreeT::Ptr tree =
400  convertPointsToMask<PointDataTreeT, MaskTreeT, FilterT>
401  (points.tree(), filter, threaded);
402 
403  typename MaskGridT::Ptr grid(new MaskGridT(tree));
404  grid->setTransform(points.transform().copy());
405  return grid;
406 }
407 
408 
409 template<typename PointDataGridT, typename MaskT, typename FilterT>
411  typename MaskT::Ptr>::type
413  const PointDataGridT& points,
414  const openvdb::math::Transform& transform,
415  const FilterT& filter,
416  bool threaded)
417 {
418  // This is safe because the PointDataGrid can only be modified by the deformer
420  auto& nonConstPoints = const_cast<typename AdapterT::NonConstGridType&>(points);
421 
422  NullDeformer deformer;
423  return point_mask_internal::convertPointsToScalar<MaskT>(
424  nonConstPoints, transform, filter, deformer, threaded);
425 }
426 
427 
428 ////////////////////////////////////////
429 
430 
431 } // namespace points
432 } // namespace OPENVDB_VERSION_NAME
433 } // namespace openvdb
434 
435 #endif // OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:207
Index filters primarily designed to be used with a FilterIndexIter.
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition: Grid.h:1064
GLuint GLenum GLenum transform
Definition: glew.h:15055
Deformer Traits for optionally configuring deformers to be applied in index-space. The default is world-space.
Definition: PointMask.h:86
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
GLint GLsizei count
Definition: glcorearb.h:405
No-op deformer (adheres to the deformer interface documented in PointMove.h)
Definition: PointMask.h:74
std::enable_if< std::is_base_of< TreeBase, PointDataTreeT >::value &&std::is_same< typename MaskTreeT::ValueType, bool >::value, typename MaskTreeT::Ptr >::type convertPointsToMask(const PointDataTreeT &tree, const FilterT &filter=NullFilter(), bool threaded=true)
Extract a Mask Tree from a Point Data Tree.
Definition: PointMask.h:379
GLuint GLdouble GLdouble GLint GLint const GLdouble * points
Definition: glew.h:3460
GLsizei const GLfloat * value
Definition: glcorearb.h:824
Definition: core.h:1131
GLintptr offset
Definition: glcorearb.h:665
Index64 iterCount(const IterT &iter)
Count up the number of times the iterator can iterate.
type
Definition: core.h:1059
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...