OpenVDB 10.0.1
Loading...
Searching...
No Matches
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
25namespace openvdb {
27namespace OPENVDB_VERSION_NAME {
28namespace 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)
34template <typename PointDataTreeT,
35 typename MaskTreeT = typename PointDataTreeT::template ValueConverter<bool>::Type,
36 typename FilterT = NullFilter>
37inline typename std::enable_if<std::is_base_of<TreeBase, PointDataTreeT>::value &&
38 std::is_same<typename MaskTreeT::ValueType, bool>::value, typename MaskTreeT::Ptr>::type
39convertPointsToMask(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
48template <typename PointDataGridT,
49 typename MaskGridT = typename PointDataGridT::template ValueConverter<bool>::Type,
50 typename FilterT = NullFilter>
51inline typename std::enable_if<std::is_base_of<GridBase, PointDataGridT>::value &&
52 std::is_same<typename MaskGridT::ValueType, bool>::value, typename MaskGridT::Ptr>::type
53convertPointsToMask(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
63template <typename PointDataGridT,
64 typename MaskT = typename PointDataGridT::template ValueConverter<bool>::Type,
65 typename FilterT = NullFilter>
66inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
67 typename MaskT::Ptr>::type
68convertPointsToMask(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.
85template <typename DeformerT>
87{
88 static const bool IndexSpace = false;
89};
90
91
92////////////////////////////////////////
93
94/// @cond OPENVDB_DOCS_INTERNAL
95
96namespace point_mask_internal {
97
98template <typename LeafT>
99void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value)
100{
102}
103
104// overload PointDataLeaf access to use setOffsetOn(), as modifyValue()
105// is intentionally disabled to avoid accidental usage
106
107template <typename T, Index Log2Dim>
108void 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
117template<typename GridT>
118struct 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;
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
149private:
150 TreeT& mTree;
151}; // struct GridCombinerOp
152
153
154/// @brief Compute scalar grid from PointDataGrid while evaluating the point filter
155template <typename TreeT, typename PointDataTreeT, typename FilterT>
156struct 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 =
162 std::is_same<ValueT, bool>::value;
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
189private:
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
197template <typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
198struct 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
258private:
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
267template<typename TreeT, typename PointDataTreeT, typename FilterT>
268inline 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
310template<typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
311inline 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
376template <typename PointDataTreeT, typename MaskTreeT, typename FilterT>
377inline typename std::enable_if<std::is_base_of<TreeBase, PointDataTreeT>::value &&
378 std::is_same<typename MaskTreeT::ValueType, bool>::value, typename MaskTreeT::Ptr>::type
379convertPointsToMask(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
388template<typename PointDataGridT, typename MaskGridT, typename FilterT>
389inline typename std::enable_if<std::is_base_of<GridBase, PointDataGridT>::value &&
390 std::is_same<typename MaskGridT::ValueType, bool>::value, typename MaskGridT::Ptr>::type
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
409template<typename PointDataGridT, typename MaskT, typename FilterT>
410inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
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
ValueT value
Definition GridBuilder.h:1290
Index filters primarily designed to be used with a FilterIndexIter.
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Vec3< double > Vec3d
Definition Vec3.h:664
std::enable_if< std::is_base_of< TreeBase, PointDataTreeT >::value &&std::is_same< typenameMaskTreeT::ValueType, bool >::value, typenameMaskTreeT::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
Index32 Index
Definition Types.h:54
uint64_t Index64
Definition Types.h:53
Definition Exceptions.h:13
This adapter allows code that is templated on a Tree type to accept either a Tree type or a Grid type...
Definition Grid.h:1060
Deformer Traits for optionally configuring deformers to be applied in index-space....
Definition PointMask.h:87
No-op deformer (adheres to the deformer interface documented in PointMove.h)
Definition PointMask.h:75
void reset(LeafT &, size_t=0)
Definition PointMask.h:77
void apply(Vec3d &, IterT &) const
Definition PointMask.h:80
Definition ValueTransformer.h:249
#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:212