OpenVDB 9.0.0
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
31/// @brief Extract a Mask Grid from a Point Data Grid
32/// @param grid the PointDataGrid to extract the mask from.
33/// @param filter an optional index filter
34/// @param threaded enable or disable threading (threading is enabled by default)
35/// @note this method is only available for Bool Grids and Mask Grids
36template <typename PointDataGridT,
37 typename MaskT = typename PointDataGridT::template ValueConverter<bool>::Type,
38 typename FilterT = NullFilter>
40 typename MaskT::Ptr>::type
41convertPointsToMask(const PointDataGridT& grid,
42 const FilterT& filter = NullFilter(),
43 bool threaded = true);
44
45
46/// @brief Extract a Mask Grid from a Point Data Grid using a new transform
47/// @param grid the PointDataGrid to extract the mask from.
48/// @param transform target transform for the mask.
49/// @param filter an optional index filter
50/// @param threaded enable or disable threading (threading is enabled by default)
51/// @note this method is only available for Bool Grids and Mask Grids
52template <typename PointDataGridT,
53 typename MaskT = typename PointDataGridT::template ValueConverter<bool>::Type,
54 typename FilterT = NullFilter>
56 typename MaskT::Ptr>::type
57convertPointsToMask(const PointDataGridT& grid,
58 const openvdb::math::Transform& transform,
59 const FilterT& filter = NullFilter(),
60 bool threaded = true);
61
62
63/// @brief No-op deformer (adheres to the deformer interface documented in PointMove.h)
65{
66 template <typename LeafT>
67 void reset(LeafT&, size_t /*idx*/ = 0) { }
68
69 template <typename IterT>
70 void apply(Vec3d&, IterT&) const { }
71};
72
73/// @brief Deformer Traits for optionally configuring deformers to be applied
74/// in index-space. The default is world-space.
75template <typename DeformerT>
77{
78 static const bool IndexSpace = false;
79};
80
81
82////////////////////////////////////////
83
84/// @cond OPENVDB_DOCS_INTERNAL
85
86namespace point_mask_internal {
87
88template <typename LeafT>
89void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value)
90{
92}
93
94// overload PointDataLeaf access to use setOffsetOn(), as modifyValue()
95// is intentionally disabled to avoid accidental usage
96
97template <typename T, Index Log2Dim>
98void voxelSum(PointDataLeafNode<T, Log2Dim>& leaf, const Index offset,
99 const typename PointDataLeafNode<T, Log2Dim>::ValueType& value)
100{
101 leaf.setOffsetOn(offset, leaf.getValue(offset) + value);
102}
103
104
105/// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values
106/// This class is designed to work with thread local storage containers such as tbb::combinable
107template<typename GridT>
108struct GridCombinerOp
109{
110 using CombinableT = typename tbb::combinable<GridT>;
111
112 using TreeT = typename GridT::TreeType;
113 using LeafT = typename TreeT::LeafNodeType;
114 using ValueType = typename TreeT::ValueType;
116
117 GridCombinerOp(GridT& grid)
118 : mTree(grid.tree()) {}
119
120 void operator()(const GridT& grid)
121 {
122 for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) {
123 auto* newLeaf = mTree.probeLeaf(leaf->origin());
124 if (!newLeaf) {
125 // if the leaf doesn't yet exist in the new tree, steal it
126 auto& tree = const_cast<GridT&>(grid).tree();
127 mTree.addLeaf(tree.template stealNode<LeafT>(leaf->origin(),
128 zeroVal<ValueType>(), false));
129 }
130 else {
131 // otherwise increment existing values
132 for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
133 voxelSum(*newLeaf, iter.offset(), ValueType(*iter));
134 }
135 }
136 }
137 }
138
139private:
140 TreeT& mTree;
141}; // struct GridCombinerOp
142
143
144/// @brief Compute scalar grid from PointDataGrid while evaluating the point filter
145template <typename GridT, typename PointDataGridT, typename FilterT>
146struct PointsToScalarOp
147{
148 using LeafT = typename GridT::TreeType::LeafNodeType;
149 using ValueT = typename LeafT::ValueType;
150
151 PointsToScalarOp( const PointDataGridT& grid,
152 const FilterT& filter)
153 : mPointDataAccessor(grid.getConstAccessor())
154 , mFilter(filter) { }
155
156 void operator()(LeafT& leaf, size_t /*idx*/) const {
157
158 const auto* const pointLeaf =
159 mPointDataAccessor.probeConstLeaf(leaf.origin());
160
161 // assumes matching topology
162 assert(pointLeaf);
163
164 for (auto value = leaf.beginValueOn(); value; ++value) {
165 const Index64 count = points::iterCount(
166 pointLeaf->beginIndexVoxel(value.getCoord(), mFilter));
167 if (count > Index64(0)) {
168 value.setValue(ValueT(count));
169 } else {
170 // disable any empty voxels
171 value.setValueOn(false);
172 }
173 }
174 }
175
176private:
177 const typename PointDataGridT::ConstAccessor mPointDataAccessor;
178 const FilterT& mFilter;
179}; // struct PointsToScalarOp
180
181
182/// @brief Compute scalar grid from PointDataGrid using a different transform
183/// and while evaluating the point filter
184template <typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
185struct PointsToTransformedScalarOp
186{
187 using PointDataLeafT = typename PointDataGridT::TreeType::LeafNodeType;
188 using ValueT = typename GridT::TreeType::ValueType;
189 using HandleT = AttributeHandle<Vec3f>;
190 using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
191
192 PointsToTransformedScalarOp(const math::Transform& targetTransform,
193 const math::Transform& sourceTransform,
194 const FilterT& filter,
195 const DeformerT& deformer,
196 CombinableT& combinable)
197 : mTargetTransform(targetTransform)
198 , mSourceTransform(sourceTransform)
199 , mFilter(filter)
200 , mDeformer(deformer)
201 , mCombinable(combinable) { }
202
203 void operator()(const PointDataLeafT& leaf, size_t idx) const
204 {
205 DeformerT deformer(mDeformer);
206
207 auto& grid = mCombinable.local();
208 auto& countTree = grid.tree();
209 tree::ValueAccessor<typename GridT::TreeType> accessor(countTree);
210
211 deformer.reset(leaf, idx);
212
213 auto handle = HandleT::create(leaf.constAttributeArray("P"));
214
215 for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
216
217 // extract index-space position
218
219 Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
220
221 // if deformer is designed to be used in index-space, perform deformation prior
222 // to transforming position to world-space, otherwise perform deformation afterwards
223
224 if (DeformerTraits<DeformerT>::IndexSpace) {
225 deformer.template apply<decltype(iter)>(position, iter);
226 position = mSourceTransform.indexToWorld(position);
227 }
228 else {
229 position = mSourceTransform.indexToWorld(position);
230 deformer.template apply<decltype(iter)>(position, iter);
231 }
232
233 // determine coord of target grid
234
235 const Coord ijk = mTargetTransform.worldToIndexCellCentered(position);
236
237 // increment count in target voxel
238
239 auto* newLeaf = accessor.touchLeaf(ijk);
240 assert(newLeaf);
241 voxelSum(*newLeaf, newLeaf->coordToOffset(ijk), ValueT(1));
242 }
243 }
244
245private:
246 const openvdb::math::Transform& mTargetTransform;
247 const openvdb::math::Transform& mSourceTransform;
248 const FilterT& mFilter;
249 const DeformerT& mDeformer;
250 CombinableT& mCombinable;
251}; // struct PointsToTransformedScalarOp
252
253
254template<typename GridT, typename PointDataGridT, typename FilterT>
255inline typename GridT::Ptr convertPointsToScalar(
256 const PointDataGridT& points,
257 const FilterT& filter,
258 bool threaded = true)
259{
260 using point_mask_internal::PointsToScalarOp;
261
262 using GridTreeT = typename GridT::TreeType;
263 using ValueT = typename GridTreeT::ValueType;
264
265 // copy the topology from the points grid
266
267 typename GridTreeT::Ptr tree(new GridTreeT(points.constTree(),
268 false, openvdb::TopologyCopy()));
269 typename GridT::Ptr grid = GridT::create(tree);
270 grid->setTransform(points.transform().copy());
271
272 // early exit if no leaves
273
274 if (points.constTree().leafCount() == 0) return grid;
275
276 // early exit if mask and no group logic
277
278 if (std::is_same<ValueT, bool>::value && filter.state() == index::ALL) return grid;
279
280 // evaluate point group filters to produce a subset of the generated mask
281
282 tree::LeafManager<GridTreeT> leafManager(*tree);
283
284 if (filter.state() == index::ALL) {
285 NullFilter nullFilter;
286 PointsToScalarOp<GridT, PointDataGridT, NullFilter> pointsToScalarOp(
287 points, nullFilter);
288 leafManager.foreach(pointsToScalarOp, threaded);
289 } else {
290 // build mask from points in parallel only where filter evaluates to true
291 PointsToScalarOp<GridT, PointDataGridT, FilterT> pointsToScalarOp(
292 points, filter);
293 leafManager.foreach(pointsToScalarOp, threaded);
294 }
295
296 return grid;
297}
298
299
300template<typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
301inline typename GridT::Ptr convertPointsToScalar(
302 PointDataGridT& points,
303 const openvdb::math::Transform& transform,
304 const FilterT& filter,
305 const DeformerT& deformer,
306 bool threaded = true)
307{
308 using point_mask_internal::PointsToTransformedScalarOp;
309 using point_mask_internal::GridCombinerOp;
310
311 using CombinerOpT = GridCombinerOp<GridT>;
312 using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
313
314 // use the simpler method if the requested transform matches the existing one
315
316 const openvdb::math::Transform& pointsTransform = points.constTransform();
317
318 if (transform == pointsTransform && std::is_same<NullDeformer, DeformerT>()) {
319 return convertPointsToScalar<GridT>(points, filter, threaded);
320 }
321
322 typename GridT::Ptr grid = GridT::create();
323 grid->setTransform(transform.copy());
324
325 // early exit if no leaves
326
327 if (points.constTree().leafCount() == 0) return grid;
328
329 // compute mask grids in parallel using new transform
330
331 CombinableT combiner;
332
333 tree::LeafManager<typename PointDataGridT::TreeType> leafManager(points.tree());
334
335 if (filter.state() == index::ALL) {
336 NullFilter nullFilter;
337 PointsToTransformedScalarOp<GridT, PointDataGridT, NullFilter, DeformerT> pointsToScalarOp(
338 transform, pointsTransform, nullFilter, deformer, combiner);
339 leafManager.foreach(pointsToScalarOp, threaded);
340 } else {
341 PointsToTransformedScalarOp<GridT, PointDataGridT, FilterT, DeformerT> pointsToScalarOp(
342 transform, pointsTransform, filter, deformer, combiner);
343 leafManager.foreach(pointsToScalarOp, threaded);
344 }
345
346 // combine the mask grids into one
347
348 CombinerOpT combineOp(*grid);
349 combiner.combine_each(combineOp);
350
351 return grid;
352}
353
354
355} // namespace point_mask_internal
356
357/// @endcond
358
359////////////////////////////////////////
360
361
362template<typename PointDataGridT, typename MaskT, typename FilterT>
364 typename MaskT::Ptr>::type
366 const PointDataGridT& points,
367 const FilterT& filter,
368 bool threaded)
369{
370 return point_mask_internal::convertPointsToScalar<MaskT>(
371 points, filter, threaded);
372}
373
374
375template<typename PointDataGridT, typename MaskT, typename FilterT>
377 typename MaskT::Ptr>::type
379 const PointDataGridT& points,
380 const openvdb::math::Transform& transform,
381 const FilterT& filter,
382 bool threaded)
383{
384 // This is safe because the PointDataGrid can only be modified by the deformer
386 auto& nonConstPoints = const_cast<typename AdapterT::NonConstGridType&>(points);
387
388 NullDeformer deformer;
389 return point_mask_internal::convertPointsToScalar<MaskT>(
390 nonConstPoints, transform, filter, deformer, threaded);
391}
392
393
394////////////////////////////////////////
395
396
397} // namespace points
398} // namespace OPENVDB_VERSION_NAME
399} // namespace openvdb
400
401#endif // OPENVDB_POINTS_POINT_MASK_HAS_BEEN_INCLUDED
ValueT value
Definition: GridBuilder.h:1287
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...
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:564
Vec3< double > Vec3d
Definition: NanoVDB.h:1174
@ ALL
Definition: IndexIterator.h:43
Index64 iterCount(const IterT &iter)
Count up the number of times the iterator can iterate.
Definition: IndexIterator.h:314
std::enable_if< std::is_same< typenameMaskT::ValueType, bool >::value, typenameMaskT::Ptr >::type convertPointsToMask(const PointDataGridT &grid, const openvdb::math::Transform &transform, const FilterT &filter=NullFilter(), bool threaded=true)
Extract a Mask Grid from a Point Data Grid using a new transform.
Definition: PointMask.h:378
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:1071
Deformer Traits for optionally configuring deformers to be applied in index-space....
Definition: PointMask.h:77
No-op deformer (adheres to the deformer interface documented in PointMove.h)
Definition: PointMask.h:65
void reset(LeafT &, size_t=0)
Definition: PointMask.h:67
void apply(Vec3d &, IterT &) const
Definition: PointMask.h:70
Definition: ValueTransformer.h:251
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:116
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:202