OpenVDB: openvdb/points/impl/PointMaskImpl.h Source File

OpenVDB 12.1.1

Loading...
Searching...
No Matches
PointMaskImpl.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 PointMaskImpl.h
5///
6/// @author Dan Bailey
7///
8
9#ifndef OPENVDB_POINTS_POINT_MASK_IMPL_HAS_BEEN_INCLUDED
10#define OPENVDB_POINTS_POINT_MASK_IMPL_HAS_BEEN_INCLUDED
11
12namespace openvdb {
13 OPENVDB_USE_VERSION_NAMESPACE
14namespace OPENVDB_VERSION_NAME {
15namespace points {
16
17/// @cond OPENVDB_DOCS_INTERNAL
18
19namespace point_mask_internal {
20
21template <typename LeafT>
22void voxelSum(LeafT& leaf, const Index offset, const typename LeafT::ValueType& value)
23{
24 leaf.modifyValue(offset, tools::valxform::SumOp<typename LeafT::ValueType>(value));
25}
26
27// overload PointDataLeaf access to use setOffsetOn(), as modifyValue()
28// is intentionally disabled to avoid accidental usage
29
30template <typename T, Index Log2Dim>
31void voxelSum(PointDataLeafNode<T, Log2Dim>& leaf, const Index offset,
32 const typename PointDataLeafNode<T, Log2Dim>::ValueType& value)
33{
34 leaf.setOffsetOn(offset, leaf.getValue(offset) + value);
35}
36
37
38/// @brief Combines multiple grids into one by stealing leaf nodes and summing voxel values
39/// This class is designed to work with thread local storage containers such as tbb::combinable
40template<typename GridT>
41struct GridCombinerOp
42{
43 using CombinableT = typename tbb::combinable<GridT>;
44
45 using TreeT = typename GridT::TreeType;
46 using LeafT = typename TreeT::LeafNodeType;
47 using ValueType = typename TreeT::ValueType;
48 using SumOp = tools::valxform::SumOp<typename TreeT::ValueType>;
49
50 GridCombinerOp(GridT& grid)
51 : mTree(grid.tree()) {}
52
53 void operator()(const GridT& grid)
54 {
55 for (auto leaf = grid.tree().beginLeaf(); leaf; ++leaf) {
56 auto* newLeaf = mTree.probeLeaf(leaf->origin());
57 if (!newLeaf) {
58 // if the leaf doesn't yet exist in the new tree, steal it
59 auto& tree = const_cast<GridT&>(grid).tree();
60 mTree.addLeaf(tree.template stealNode<LeafT>(leaf->origin(),
61 zeroVal<ValueType>(), false));
62 }
63 else {
64 // otherwise increment existing values
65 for (auto iter = leaf->cbeginValueOn(); iter; ++iter) {
66 voxelSum(*newLeaf, iter.offset(), ValueType(*iter));
67 }
68 }
69 }
70 }
71
72private:
73 TreeT& mTree;
74}; // struct GridCombinerOp
75
76
77/// @brief Compute scalar grid from PointDataGrid while evaluating the point filter
78template <typename TreeT, typename PointDataTreeT, typename FilterT>
79struct PointsToScalarOp
80{
81 using LeafT = typename TreeT::LeafNodeType;
82 using ValueT = typename LeafT::ValueType;
83 // This method is also used by PointCount so ValueT may not be bool
84 static constexpr bool IsBool =
85 std::is_same<ValueT, bool>::value;
86
87 PointsToScalarOp(const PointDataTreeT& tree,
88 const FilterT& filter)
89 : mPointDataAccessor(tree)
90 , mFilter(filter) {}
91
92 void operator()(LeafT& leaf, size_t /*idx*/) const
93 {
94 // assumes matching topology
95 const auto* const pointLeaf =
96 mPointDataAccessor.probeConstLeaf(leaf.origin());
97 OPENVDB_ASSERT(pointLeaf);
98
99 for (auto value = leaf.beginValueOn(); value; ++value) {
100 const auto iter = pointLeaf->beginIndexVoxel(value.getCoord(), mFilter);
101 if (IsBool) {
102 if (!iter) value.setValueOn(false);
103 }
104 else {
105 const Index64 count = points::iterCount(iter);
106 if (count > Index64(0)) value.setValue(ValueT(count));
107 else value.setValueOn(false);
108 }
109 }
110 }
111
112private:
113 const tree::ValueAccessor<const PointDataTreeT> mPointDataAccessor;
114 const FilterT& mFilter;
115}; // struct PointsToScalarOp
116
117
118/// @brief Compute scalar grid from PointDataGrid using a different transform
119/// and while evaluating the point filter
120template <typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
121struct PointsToTransformedScalarOp
122{
123 using PointDataLeafT = typename PointDataGridT::TreeType::LeafNodeType;
124 using ValueT = typename GridT::TreeType::ValueType;
125 using HandleT = AttributeHandle<Vec3f>;
126 using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
127
128 PointsToTransformedScalarOp(const math::Transform& targetTransform,
129 const math::Transform& sourceTransform,
130 const FilterT& filter,
131 const DeformerT& deformer,
132 CombinableT& combinable)
133 : mTargetTransform(targetTransform)
134 , mSourceTransform(sourceTransform)
135 , mFilter(filter)
136 , mDeformer(deformer)
137 , mCombinable(combinable) { }
138
139 void operator()(const PointDataLeafT& leaf, size_t idx) const
140 {
141 DeformerT deformer(mDeformer);
142
143 auto& grid = mCombinable.local();
144 auto& countTree = grid.tree();
145 tree::ValueAccessor<typename GridT::TreeType> accessor(countTree);
146
147 deformer.reset(leaf, idx);
148
149 auto handle = HandleT::create(leaf.constAttributeArray("P"));
150
151 for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
152
153 // extract index-space position
154
155 Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
156
157 // if deformer is designed to be used in index-space, perform deformation prior
158 // to transforming position to world-space, otherwise perform deformation afterwards
159
160 if (DeformerTraits<DeformerT>::IndexSpace) {
161 deformer.template apply<decltype(iter)>(position, iter);
162 position = mSourceTransform.indexToWorld(position);
163 }
164 else {
165 position = mSourceTransform.indexToWorld(position);
166 deformer.template apply<decltype(iter)>(position, iter);
167 }
168
169 // determine coord of target grid
170
171 const Coord ijk = mTargetTransform.worldToIndexCellCentered(position);
172
173 // increment count in target voxel
174
175 auto* newLeaf = accessor.touchLeaf(ijk);
176 OPENVDB_ASSERT(newLeaf);
177 voxelSum(*newLeaf, newLeaf->coordToOffset(ijk), ValueT(1));
178 }
179 }
180
181private:
182 const openvdb::math::Transform& mTargetTransform;
183 const openvdb::math::Transform& mSourceTransform;
184 const FilterT& mFilter;
185 const DeformerT& mDeformer;
186 CombinableT& mCombinable;
187}; // struct PointsToTransformedScalarOp
188
189
190template<typename TreeT, typename PointDataTreeT, typename FilterT>
191inline typename TreeT::Ptr convertPointsToScalar(
192 const PointDataTreeT& points,
193 const FilterT& filter,
194 bool threaded = true)
195{
196 using point_mask_internal::PointsToScalarOp;
197
198 using ValueT = typename TreeT::ValueType;
199
200 // copy the topology from the points tree
201
202 typename TreeT::Ptr tree(new TreeT(/*background=*/false));
203 tree->topologyUnion(points);
204
205 // early exit if no leaves
206
207 if (points.leafCount() == 0) return tree;
208
209 // early exit if mask and no group logic
210
211 if (std::is_same<ValueT, bool>::value && filter.state() == index::ALL) return tree;
212
213 // evaluate point group filters to produce a subset of the generated mask
214
215 tree::LeafManager<TreeT> leafManager(*tree);
216
217 if (filter.state() == index::ALL) {
218 NullFilter nullFilter;
219 PointsToScalarOp<TreeT, PointDataTreeT, NullFilter> pointsToScalarOp(
220 points, nullFilter);
221 leafManager.foreach(pointsToScalarOp, threaded);
222 } else {
223 // build mask from points in parallel only where filter evaluates to true
224 PointsToScalarOp<TreeT, PointDataTreeT, FilterT> pointsToScalarOp(
225 points, filter);
226 leafManager.foreach(pointsToScalarOp, threaded);
227 }
228
229 return tree;
230}
231
232
233template<typename GridT, typename PointDataGridT, typename FilterT, typename DeformerT>
234inline typename GridT::Ptr convertPointsToScalar(
235 PointDataGridT& points,
236 const math::Transform& transform,
237 const FilterT& filter,
238 const DeformerT& deformer,
239 bool threaded = true)
240{
241 using point_mask_internal::PointsToTransformedScalarOp;
242 using point_mask_internal::GridCombinerOp;
243
244 using CombinerOpT = GridCombinerOp<GridT>;
245 using CombinableT = typename GridCombinerOp<GridT>::CombinableT;
246
247 typename GridT::Ptr grid = GridT::create();
248 grid->setTransform(transform.copy());
249
250 // use the simpler method if the requested transform matches the existing one
251
252 const math::Transform& pointsTransform = points.constTransform();
253
254 if (transform == pointsTransform && std::is_same<NullDeformer, DeformerT>()) {
255 using TreeT = typename GridT::TreeType;
256 typename TreeT::Ptr tree =
257 convertPointsToScalar<TreeT>(points.tree(), filter, threaded);
258 grid->setTree(tree);
259 return grid;
260 }
261
262 // early exit if no leaves
263
264 if (points.constTree().leafCount() == 0) return grid;
265
266 // compute mask grids in parallel using new transform
267
268 CombinableT combiner;
269
270 tree::LeafManager<typename PointDataGridT::TreeType> leafManager(points.tree());
271
272 if (filter.state() == index::ALL) {
273 NullFilter nullFilter;
274 PointsToTransformedScalarOp<GridT, PointDataGridT, NullFilter, DeformerT> pointsToScalarOp(
275 transform, pointsTransform, nullFilter, deformer, combiner);
276 leafManager.foreach(pointsToScalarOp, threaded);
277 } else {
278 PointsToTransformedScalarOp<GridT, PointDataGridT, FilterT, DeformerT> pointsToScalarOp(
279 transform, pointsTransform, filter, deformer, combiner);
280 leafManager.foreach(pointsToScalarOp, threaded);
281 }
282
283 // combine the mask grids into one
284
285 CombinerOpT combineOp(*grid);
286 combiner.combine_each(combineOp);
287
288 return grid;
289}
290
291
292} // namespace point_mask_internal
293
294/// @endcond
295
296////////////////////////////////////////
297
298
299template <typename PointDataTreeT, typename MaskTreeT, typename FilterT>
300inline typename std::enable_if<std::is_base_of<TreeBase, PointDataTreeT>::value &&
301 std::is_same<typename MaskTreeT::ValueType, bool>::value, typename MaskTreeT::Ptr>::type
302convertPointsToMask(const PointDataTreeT& tree,
303 const FilterT& filter,
304 bool threaded)
305{
306 return point_mask_internal::convertPointsToScalar<MaskTreeT>(
307 tree, filter, threaded);
308}
309
310
311template<typename PointDataGridT, typename MaskGridT, typename FilterT>
312inline typename std::enable_if<std::is_base_of<GridBase, PointDataGridT>::value &&
313 std::is_same<typename MaskGridT::ValueType, bool>::value, typename MaskGridT::Ptr>::type
314 convertPointsToMask(
315 const PointDataGridT& points,
316 const FilterT& filter,
317 bool threaded)
318{
319 using PointDataTreeT = typename PointDataGridT::TreeType;
320 using MaskTreeT = typename MaskGridT::TreeType;
321
322 typename MaskTreeT::Ptr tree =
323 convertPointsToMask<PointDataTreeT, MaskTreeT, FilterT>
324 (points.tree(), filter, threaded);
325
326 typename MaskGridT::Ptr grid(new MaskGridT(tree));
327 grid->setTransform(points.transform().copy());
328 return grid;
329}
330
331
332template<typename PointDataGridT, typename MaskT, typename FilterT>
333inline typename std::enable_if<std::is_same<typename MaskT::ValueType, bool>::value,
334 typename MaskT::Ptr>::type
335 convertPointsToMask(
336 const PointDataGridT& points,
337 const openvdb::math::Transform& transform,
338 const FilterT& filter,
339 bool threaded)
340{
341 // This is safe because the PointDataGrid can only be modified by the deformer
342 using AdapterT = TreeAdapter<typename PointDataGridT::TreeType>;
343 auto& nonConstPoints = const_cast<typename AdapterT::NonConstGridType&>(points);
344
345 NullDeformer deformer;
346 return point_mask_internal::convertPointsToScalar<MaskT>(
347 nonConstPoints, transform, filter, deformer, threaded);
348}
349
350
351////////////////////////////////////////
352
353
354} // namespace points
355} // namespace OPENVDB_VERSION_NAME
356} // namespace openvdb
357
358#endif // OPENVDB_POINTS_POINT_MASK_IMPL_HAS_BEEN_INCLUDED
OPENVDB_ASSERT
#define OPENVDB_ASSERT(X)
Definition Assert.h:41
openvdb::v12_1::math::Vec3d
Vec3< double > Vec3d
Definition Vec3.h:665
openvdb::v12_1::points
Definition AttributeArray.h:42
openvdb::v12_1::points::convertPointsToMask
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 PointMaskImpl.h:302
openvdb::v12_1::tree
Definition PointDataGrid.h:170
openvdb::v12_1::Index64
uint64_t Index64
Definition Types.h:53
openvdb
Definition Exceptions.h:13
openvdb::v12_1::TreeAdapter
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
openvdb::v12_1::points::NullDeformer
No-op deformer (adheres to the deformer interface documented in PointMove.h).
Definition PointMask.h:75
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:218