OpenVDB 10.0.1
Loading...
Searching...
No Matches
PointMove.h
Go to the documentation of this file.
1// Copyright Contributors to the OpenVDB Project
2// SPDX-License-Identifier: MPL-2.0
3
4/// @author Dan Bailey
5///
6/// @file PointMove.h
7///
8/// @brief Ability to move VDB Points using a custom deformer.
9///
10/// Deformers used when moving points are in world space by default and must adhere
11/// to the interface described in the example below:
12/// @code
13/// struct MyDeformer
14/// {
15/// // A reset is performed on each leaf in turn before the points in that leaf are
16/// // deformed. A leaf and leaf index (standard leaf traversal order) are supplied as
17/// // the arguments, which matches the functor interface for LeafManager::foreach().
18/// template <typename LeafNoteType>
19/// void reset(LeafNoteType& leaf, size_t idx);
20///
21/// // Evaluate the deformer and modify the given position to generate the deformed
22/// // position. An index iterator is supplied as the argument to allow querying the
23/// // point offset or containing voxel coordinate.
24/// template <typename IndexIterT>
25/// void apply(Vec3d& position, const IndexIterT& iter) const;
26/// };
27/// @endcode
28///
29/// @note The DeformerTraits struct (defined in PointMask.h) can be used to configure
30/// a deformer to evaluate in index space.
31
32#ifndef OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED
33#define OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED
34
35#include <openvdb/openvdb.h>
36
37#include "PointDataGrid.h"
38#include "PointMask.h"
39
40#include <tbb/concurrent_vector.h>
41
42#include <algorithm>
43#include <iterator> // for std::begin(), std::end()
44#include <map>
45#include <numeric> // for std::iota()
46#include <tuple>
47#include <unordered_map>
48#include <vector>
49
50class TestPointMove;
51
52
53namespace openvdb {
55namespace OPENVDB_VERSION_NAME {
56namespace points {
57
58// dummy object for future use
59namespace future { struct Advect { }; }
60
61
62/// @brief Move points in a PointDataGrid using a custom deformer
63/// @param points the PointDataGrid containing the points to be moved.
64/// @param deformer a custom deformer that defines how to move the points.
65/// @param filter an optional index filter
66/// @param objectNotInUse for future use, this object is currently ignored
67/// @param threaded enable or disable threading (threading is enabled by default)
68template <typename PointDataGridT, typename DeformerT, typename FilterT = NullFilter>
69inline void movePoints(PointDataGridT& points,
70 DeformerT& deformer,
71 const FilterT& filter = NullFilter(),
72 future::Advect* objectNotInUse = nullptr,
73 bool threaded = true);
74
75
76/// @brief Move points in a PointDataGrid using a custom deformer and a new transform
77/// @param points the PointDataGrid containing the points to be moved.
78/// @param transform target transform to use for the resulting points.
79/// @param deformer a custom deformer that defines how to move the points.
80/// @param filter an optional index filter
81/// @param objectNotInUse for future use, this object is currently ignored
82/// @param threaded enable or disable threading (threading is enabled by default)
83template <typename PointDataGridT, typename DeformerT, typename FilterT = NullFilter>
84inline void movePoints(PointDataGridT& points,
85 const math::Transform& transform,
86 DeformerT& deformer,
87 const FilterT& filter = NullFilter(),
88 future::Advect* objectNotInUse = nullptr,
89 bool threaded = true);
90
91
92// define leaf index in use as 32-bit
93namespace point_move_internal { using LeafIndex = Index32; }
94
95
96/// @brief A Deformer that caches the resulting positions from evaluating another Deformer
97template <typename T>
99{
100public:
102 using Vec3T = typename math::Vec3<T>;
103 using LeafVecT = std::vector<Vec3T>;
104 using LeafMapT = std::unordered_map<LeafIndex, Vec3T>;
105
106 // Internal data cache to allow the deformer to offer light-weight copying
107 struct Cache
108 {
109 struct Leaf
110 {
111 /// @brief clear data buffers and reset counter
112 void clear() {
113 vecData.clear();
114 mapData.clear();
115 totalSize = 0;
116 }
117
120 Index totalSize = 0;
121 }; // struct Leaf
122
123 std::vector<Leaf> leafs;
124 }; // struct Cache
125
126 /// Cache is expected to be persistent for the lifetime of the CachedDeformer
127 explicit CachedDeformer(Cache& cache);
128
129 /// Caches the result of evaluating the supplied point grid using the deformer and filter
130 /// @param grid the points to be moved
131 /// @param deformer the deformer to apply to the points
132 /// @param filter the point filter to use when evaluating the points
133 /// @param threaded enable or disable threading (threading is enabled by default)
134 template <typename PointDataGridT, typename DeformerT, typename FilterT>
135 void evaluate(PointDataGridT& grid, DeformerT& deformer, const FilterT& filter,
136 bool threaded = true);
137
138 /// Stores pointers to the vector or map and optionally expands the map into a vector
139 /// @throw IndexError if idx is out-of-range of the leafs in the cache
140 template <typename LeafT>
141 void reset(const LeafT& leaf, size_t idx);
142
143 /// Retrieve the new position from the cache
144 template <typename IndexIterT>
145 void apply(Vec3d& position, const IndexIterT& iter) const;
146
147private:
148 friend class ::TestPointMove;
149
150 Cache& mCache;
151 const LeafVecT* mLeafVec = nullptr;
152 const LeafMapT* mLeafMap = nullptr;
153}; // class CachedDeformer
154
155
156////////////////////////////////////////
157
158
159namespace point_move_internal {
160
161using IndexArray = std::vector<Index>;
162
163using IndexTriple = std::tuple<LeafIndex, Index, Index>;
164using IndexTripleArray = tbb::concurrent_vector<IndexTriple>;
165using GlobalPointIndexMap = std::vector<IndexTripleArray>;
166using GlobalPointIndexIndices = std::vector<IndexArray>;
167
168using IndexPair = std::pair<Index, Index>;
169using IndexPairArray = std::vector<IndexPair>;
170using LocalPointIndexMap = std::vector<IndexPairArray>;
171
172using LeafIndexArray = std::vector<LeafIndex>;
173using LeafOffsetArray = std::vector<LeafIndexArray>;
174using LeafMap = std::unordered_map<Coord, LeafIndex>;
175
176
177template <typename DeformerT, typename TreeT, typename FilterT>
179{
180 using LeafT = typename TreeT::LeafNodeType;
181 using LeafArrayT = std::vector<LeafT*>;
183
184 BuildMoveMapsOp(const DeformerT& deformer,
185 GlobalPointIndexMap& globalMoveLeafMap,
186 LocalPointIndexMap& localMoveLeafMap,
187 const LeafMap& targetLeafMap,
188 const math::Transform& targetTransform,
189 const math::Transform& sourceTransform,
190 const FilterT& filter)
191 : mDeformer(deformer)
192 , mGlobalMoveLeafMap(globalMoveLeafMap)
193 , mLocalMoveLeafMap(localMoveLeafMap)
194 , mTargetLeafMap(targetLeafMap)
195 , mTargetTransform(targetTransform)
196 , mSourceTransform(sourceTransform)
197 , mFilter(filter) { }
198
199 void operator()(LeafT& leaf, size_t idx) const
200 {
201 DeformerT deformer(mDeformer);
202 deformer.reset(leaf, idx);
203
204 // determine source leaf node origin and offset in the source leaf vector
205
206 Coord sourceLeafOrigin = leaf.origin();
207
208 auto sourceHandle = AttributeWriteHandle<Vec3f>::create(leaf.attributeArray("P"));
209
210 for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
211
212 const bool useIndexSpace = DeformerTraits<DeformerT>::IndexSpace;
213
214 // extract index-space position and apply index-space deformation (if applicable)
215
216 Vec3d positionIS = sourceHandle->get(*iter) + iter.getCoord().asVec3d();
217 if (useIndexSpace) {
218 deformer.apply(positionIS, iter);
219 }
220
221 // transform to world-space position and apply world-space deformation (if applicable)
222
223 Vec3d positionWS = mSourceTransform.indexToWorld(positionIS);
224 if (!useIndexSpace) {
225 deformer.apply(positionWS, iter);
226 }
227
228 // transform to index-space position of target grid
229
230 positionIS = mTargetTransform.worldToIndex(positionWS);
231
232 // determine target voxel and offset
233
234 Coord targetVoxel = Coord::round(positionIS);
235 Index targetOffset = LeafT::coordToOffset(targetVoxel);
236
237 // set new local position in source transform space (if point has been deformed)
238
239 Vec3d voxelPosition(positionIS - targetVoxel.asVec3d());
240 sourceHandle->set(*iter, voxelPosition);
241
242 // determine target leaf node origin and offset in the target leaf vector
243
244 Coord targetLeafOrigin = targetVoxel & ~(LeafT::DIM - 1);
245 assert(mTargetLeafMap.find(targetLeafOrigin) != mTargetLeafMap.end());
246 const LeafIndex targetLeafOffset(mTargetLeafMap.at(targetLeafOrigin));
247
248 // insert into move map based on whether point ends up in a new leaf node or not
249
250 if (targetLeafOrigin == sourceLeafOrigin) {
251 mLocalMoveLeafMap[targetLeafOffset].emplace_back(targetOffset, *iter);
252 }
253 else {
254 mGlobalMoveLeafMap[targetLeafOffset].push_back(IndexTriple(
255 LeafIndex(static_cast<LeafIndex>(idx)), targetOffset, *iter));
256 }
257 }
258 }
259
260private:
261 const DeformerT& mDeformer;
262 GlobalPointIndexMap& mGlobalMoveLeafMap;
263 LocalPointIndexMap& mLocalMoveLeafMap;
264 const LeafMap& mTargetLeafMap;
265 const math::Transform& mTargetTransform;
266 const math::Transform& mSourceTransform;
267 const FilterT& mFilter;
268}; // struct BuildMoveMapsOp
269
270template <typename LeafT>
271inline Index
272indexOffsetFromVoxel(const Index voxelOffset, const LeafT& leaf, IndexArray& offsets)
273{
274 // compute the target point index by summing the point index of the previous
275 // voxel with the current number of points added to this voxel, tracked by the
276 // offsets array
277
278 Index targetOffset = offsets[voxelOffset]++;
279 if (voxelOffset > 0) {
280 targetOffset += static_cast<Index>(leaf.getValue(voxelOffset - 1));
281 }
282 return targetOffset;
283}
284
285
286template <typename TreeT>
288{
289 using LeafT = typename TreeT::LeafNodeType;
290 using LeafArrayT = std::vector<LeafT*>;
292 using AttributeArrays = std::vector<AttributeArray*>;
293
295 LeafManagerT& sourceLeafManager,
296 const Index attributeIndex,
297 const GlobalPointIndexMap& moveLeafMap,
298 const GlobalPointIndexIndices& moveLeafIndices)
299 : mOffsetMap(offsetMap)
300 , mSourceLeafManager(sourceLeafManager)
301 , mAttributeIndex(attributeIndex)
302 , mMoveLeafMap(moveLeafMap)
303 , mMoveLeafIndices(moveLeafIndices) { }
304
305 // A CopyIterator is designed to use the indices in a GlobalPointIndexMap for this leaf
306 // and match the interface required for AttributeArray::copyValues()
308 {
309 CopyIterator(const LeafT& leaf, const IndexArray& sortedIndices,
310 const IndexTripleArray& moveIndices, IndexArray& offsets)
311 : mLeaf(leaf)
312 , mSortedIndices(sortedIndices)
313 , mMoveIndices(moveIndices)
314 , mOffsets(offsets) { }
315
316 operator bool() const { return bool(mIt); }
317
318 void reset(Index startIndex, Index endIndex)
319 {
320 mIndex = startIndex;
321 mEndIndex = endIndex;
322 this->advance();
323 }
324
326 {
327 this->advance();
328 return *this;
329 }
330
332 {
333 if (i < mSortedIndices.size()) {
334 return std::get<0>(this->leafIndexTriple(i));
335 }
336 return std::numeric_limits<Index>::max();
337 }
338
340 {
341 assert(mIt);
342 return std::get<2>(*mIt);
343 }
344
346 {
347 assert(mIt);
348 return indexOffsetFromVoxel(std::get<1>(*mIt), mLeaf, mOffsets);
349 }
350
351 private:
352 void advance()
353 {
354 if (mIndex >= mEndIndex || mIndex >= mSortedIndices.size()) {
355 mIt = nullptr;
356 }
357 else {
358 mIt = &this->leafIndexTriple(mIndex);
359 }
360 ++mIndex;
361 }
362
363 const IndexTriple& leafIndexTriple(Index i) const
364 {
365 return mMoveIndices[mSortedIndices[i]];
366 }
367
368 private:
369 const LeafT& mLeaf;
370 Index mIndex;
371 Index mEndIndex;
372 const IndexArray& mSortedIndices;
373 const IndexTripleArray& mMoveIndices;
374 IndexArray& mOffsets;
375 const IndexTriple* mIt = nullptr;
376 }; // struct CopyIterator
377
378 void operator()(LeafT& leaf, size_t idx) const
379 {
380 const IndexTripleArray& moveIndices = mMoveLeafMap[idx];
381 if (moveIndices.empty()) return;
382 const IndexArray& sortedIndices = mMoveLeafIndices[idx];
383
384 // extract per-voxel offsets for this leaf
385
386 LeafIndexArray& offsets = mOffsetMap[idx];
387
388 // extract target array and ensure data is out-of-core and non-uniform
389
390 auto& targetArray = leaf.attributeArray(mAttributeIndex);
391 targetArray.loadData();
392 targetArray.expand();
393
394 // perform the copy
395
396 CopyIterator copyIterator(leaf, sortedIndices, moveIndices, offsets);
397
398 // use the sorted indices to track the index of the source leaf
399
400 Index sourceLeafIndex = copyIterator.leafIndex(0);
401 Index startIndex = 0;
402
403 for (size_t i = 1; i <= sortedIndices.size(); i++) {
404 Index endIndex = static_cast<Index>(i);
405 Index newSourceLeafIndex = copyIterator.leafIndex(endIndex);
406
407 // when it changes, do a batch-copy of all the indices that lie within this range
408 // TODO: this step could use nested parallelization for cases where there are a
409 // large number of points being moved per attribute
410
411 if (newSourceLeafIndex > sourceLeafIndex) {
412 copyIterator.reset(startIndex, endIndex);
413
414 const LeafT& sourceLeaf = mSourceLeafManager.leaf(sourceLeafIndex);
415 const auto& sourceArray = sourceLeaf.constAttributeArray(mAttributeIndex);
416 sourceArray.loadData();
417
418 targetArray.copyValuesUnsafe(sourceArray, copyIterator);
419
420 sourceLeafIndex = newSourceLeafIndex;
421 startIndex = endIndex;
422 }
423 }
424 }
425
426private:
427 LeafOffsetArray& mOffsetMap;
428 LeafManagerT& mSourceLeafManager;
429 const Index mAttributeIndex;
430 const GlobalPointIndexMap& mMoveLeafMap;
431 const GlobalPointIndexIndices& mMoveLeafIndices;
432}; // struct GlobalMovePointsOp
433
434
435template <typename TreeT>
437{
438 using LeafT = typename TreeT::LeafNodeType;
439 using LeafArrayT = std::vector<LeafT*>;
441 using AttributeArrays = std::vector<AttributeArray*>;
442
444 const LeafIndexArray& sourceIndices,
445 LeafManagerT& sourceLeafManager,
446 const Index attributeIndex,
447 const LocalPointIndexMap& moveLeafMap)
448 : mOffsetMap(offsetMap)
449 , mSourceIndices(sourceIndices)
450 , mSourceLeafManager(sourceLeafManager)
451 , mAttributeIndex(attributeIndex)
452 , mMoveLeafMap(moveLeafMap) { }
453
454 // A CopyIterator is designed to use the indices in a LocalPointIndexMap for this leaf
455 // and match the interface required for AttributeArray::copyValues()
457 {
458 CopyIterator(const LeafT& leaf, const IndexPairArray& indices, IndexArray& offsets)
459 : mLeaf(leaf)
460 , mIndices(indices)
461 , mOffsets(offsets) { }
462
463 operator bool() const { return mIndex < static_cast<int>(mIndices.size()); }
464
465 CopyIterator& operator++() { ++mIndex; return *this; }
466
468 {
469 return mIndices[mIndex].second;
470 }
471
473 {
474 return indexOffsetFromVoxel(mIndices[mIndex].first, mLeaf, mOffsets);
475 }
476
477 private:
478 const LeafT& mLeaf;
479 const IndexPairArray& mIndices;
480 IndexArray& mOffsets;
481 int mIndex = 0;
482 }; // struct CopyIterator
483
484 void operator()(LeafT& leaf, size_t idx) const
485 {
486 const IndexPairArray& moveIndices = mMoveLeafMap[idx];
487 if (moveIndices.empty()) return;
488
489 // extract per-voxel offsets for this leaf
490
491 LeafIndexArray& offsets = mOffsetMap[idx];
492
493 // extract source array that has the same origin as the target leaf
494
495 assert(idx < mSourceIndices.size());
496 const Index sourceLeafOffset(mSourceIndices[idx]);
497 LeafT& sourceLeaf = mSourceLeafManager.leaf(sourceLeafOffset);
498 const auto& sourceArray = sourceLeaf.constAttributeArray(mAttributeIndex);
499 sourceArray.loadData();
500
501 // extract target array and ensure data is out-of-core and non-uniform
502
503 auto& targetArray = leaf.attributeArray(mAttributeIndex);
504 targetArray.loadData();
505 targetArray.expand();
506
507 // perform the copy
508
509 CopyIterator copyIterator(leaf, moveIndices, offsets);
510 targetArray.copyValuesUnsafe(sourceArray, copyIterator);
511 }
512
513private:
514 LeafOffsetArray& mOffsetMap;
515 const LeafIndexArray& mSourceIndices;
516 LeafManagerT& mSourceLeafManager;
517 const Index mAttributeIndex;
518 const LocalPointIndexMap& mMoveLeafMap;
519}; // struct LocalMovePointsOp
520
521
522} // namespace point_move_internal
523
524
525////////////////////////////////////////
526
527
528template <typename PointDataGridT, typename DeformerT, typename FilterT>
529inline void movePoints( PointDataGridT& points,
530 const math::Transform& transform,
531 DeformerT& deformer,
532 const FilterT& filter,
533 future::Advect* objectNotInUse,
534 bool threaded)
535{
536 using LeafIndex = point_move_internal::LeafIndex;
537 using PointDataTreeT = typename PointDataGridT::TreeType;
538 using LeafT = typename PointDataTreeT::LeafNodeType;
539 using LeafManagerT = typename tree::LeafManager<PointDataTreeT>;
540
541 using namespace point_move_internal;
542
543 // this object is for future use only
544 assert(!objectNotInUse);
545 (void)objectNotInUse;
546
547 PointDataTreeT& tree = points.tree();
548
549 // early exit if no LeafNodes
550
551 auto iter = tree.cbeginLeaf();
552
553 if (!iter) return;
554
555 // build voxel topology taking into account any point group deletion
556
557 auto newPoints = point_mask_internal::convertPointsToScalar<PointDataGridT>(
558 points, transform, filter, deformer, threaded);
559 auto& newTree = newPoints->tree();
560
561 // create leaf managers for both trees
562
563 LeafManagerT sourceLeafManager(tree);
564 LeafManagerT targetLeafManager(newTree);
565
566 // extract the existing attribute set
567 const auto& existingAttributeSet = points.tree().cbeginLeaf()->attributeSet();
568
569 // build a coord -> index map for looking up target leafs by origin and a faster
570 // unordered map for finding the source index from a target index
571
572 LeafMap targetLeafMap;
573 LeafIndexArray sourceIndices(targetLeafManager.leafCount(),
574 std::numeric_limits<LeafIndex>::max());
575
576 LeafOffsetArray offsetMap(targetLeafManager.leafCount());
577
578 {
579 LeafMap sourceLeafMap;
580 auto sourceRange = sourceLeafManager.leafRange();
581 for (auto leaf = sourceRange.begin(); leaf; ++leaf) {
582 sourceLeafMap.insert({leaf->origin(), LeafIndex(static_cast<LeafIndex>(leaf.pos()))});
583 }
584 auto targetRange = targetLeafManager.leafRange();
585 for (auto leaf = targetRange.begin(); leaf; ++leaf) {
586 targetLeafMap.insert({leaf->origin(), LeafIndex(static_cast<LeafIndex>(leaf.pos()))});
587 }
588
589 // acquire registry lock to avoid locking when appending attributes in parallel
590
592
593 // perform four independent per-leaf operations in parallel
594 targetLeafManager.foreach(
595 [&](LeafT& leaf, size_t idx) {
596 // map frequency => cumulative histogram
597 auto* buffer = leaf.buffer().data();
598 for (Index i = 1; i < leaf.buffer().size(); i++) {
599 buffer[i] = buffer[i-1] + buffer[i];
600 }
601 // replace attribute set with a copy of the existing one
602 leaf.replaceAttributeSet(
603 new AttributeSet(existingAttributeSet, leaf.getLastValue(), &lock),
604 /*allowMismatchingDescriptors=*/true);
605 // store the index of the source leaf in a corresponding target leaf array
606 const auto it = sourceLeafMap.find(leaf.origin());
607 if (it != sourceLeafMap.end()) {
608 sourceIndices[idx] = it->second;
609 }
610 // allocate offset maps
611 offsetMap[idx].resize(LeafT::SIZE);
612 },
613 threaded);
614 }
615
616 // moving leaf
617
618 GlobalPointIndexMap globalMoveLeafMap(targetLeafManager.leafCount());
619 LocalPointIndexMap localMoveLeafMap(targetLeafManager.leafCount());
620
621 // build global and local move leaf maps and update local positions
622
623 if (filter.state() == index::ALL) {
624 NullFilter nullFilter;
625 BuildMoveMapsOp<DeformerT, PointDataTreeT, NullFilter> op(deformer,
626 globalMoveLeafMap, localMoveLeafMap, targetLeafMap,
627 transform, points.transform(), nullFilter);
628 sourceLeafManager.foreach(op, threaded);
629 } else {
630 BuildMoveMapsOp<DeformerT, PointDataTreeT, FilterT> op(deformer,
631 globalMoveLeafMap, localMoveLeafMap, targetLeafMap,
632 transform, points.transform(), filter);
633 sourceLeafManager.foreach(op, threaded);
634 }
635
636 // build a sorted index vector for each leaf that references the global move map
637 // indices in order of their source leafs and voxels to ensure determinism in the
638 // resulting point orders
639
640 GlobalPointIndexIndices globalMoveLeafIndices(globalMoveLeafMap.size());
641
642 targetLeafManager.foreach(
643 [&](LeafT& /*leaf*/, size_t idx) {
644 const IndexTripleArray& moveIndices = globalMoveLeafMap[idx];
645 if (moveIndices.empty()) return;
646
647 IndexArray& sortedIndices = globalMoveLeafIndices[idx];
648 sortedIndices.resize(moveIndices.size());
649 std::iota(std::begin(sortedIndices), std::end(sortedIndices), 0);
650 std::sort(std::begin(sortedIndices), std::end(sortedIndices),
651 [&](int i, int j)
652 {
653 const Index& indexI0(std::get<0>(moveIndices[i]));
654 const Index& indexJ0(std::get<0>(moveIndices[j]));
655 if (indexI0 < indexJ0) return true;
656 if (indexI0 > indexJ0) return false;
657 return std::get<2>(moveIndices[i]) < std::get<2>(moveIndices[j]);
658 }
659 );
660 },
661 threaded);
662
663 for (const auto& it : existingAttributeSet.descriptor().map()) {
664
665 const Index attributeIndex = static_cast<Index>(it.second);
666
667 // zero offsets
668 targetLeafManager.foreach(
669 [&offsetMap](const LeafT& /*leaf*/, size_t idx) {
670 std::fill(offsetMap[idx].begin(), offsetMap[idx].end(), 0);
671 },
672 threaded);
673
674 // move points between leaf nodes
675
676 GlobalMovePointsOp<PointDataTreeT> globalMoveOp(offsetMap,
677 sourceLeafManager, attributeIndex, globalMoveLeafMap, globalMoveLeafIndices);
678 targetLeafManager.foreach(globalMoveOp, threaded);
679
680 // move points within leaf nodes
681
682 LocalMovePointsOp<PointDataTreeT> localMoveOp(offsetMap,
683 sourceIndices, sourceLeafManager, attributeIndex, localMoveLeafMap);
684 targetLeafManager.foreach(localMoveOp, threaded);
685 }
686
687 points.setTree(newPoints->treePtr());
688}
689
690
691template <typename PointDataGridT, typename DeformerT, typename FilterT>
692inline void movePoints( PointDataGridT& points,
693 DeformerT& deformer,
694 const FilterT& filter,
695 future::Advect* objectNotInUse,
696 bool threaded)
697{
698 movePoints(points, points.transform(), deformer, filter, objectNotInUse, threaded);
699}
700
701
702////////////////////////////////////////
703
704
705template <typename T>
707 : mCache(cache) { }
708
709
710template <typename T>
711template <typename PointDataGridT, typename DeformerT, typename FilterT>
712void CachedDeformer<T>::evaluate(PointDataGridT& grid, DeformerT& deformer, const FilterT& filter,
713 bool threaded)
714{
715 using TreeT = typename PointDataGridT::TreeType;
716 using LeafT = typename TreeT::LeafNodeType;
717 using LeafManagerT = typename tree::LeafManager<TreeT>;
718 LeafManagerT leafManager(grid.tree());
719
720 // initialize cache
721 auto& leafs = mCache.leafs;
722 leafs.resize(leafManager.leafCount());
723
724 const auto& transform = grid.transform();
725
726 // insert deformed positions into the cache
727
728 auto cachePositionsOp = [&](const LeafT& leaf, size_t idx) {
729
730 const Index64 totalPointCount = leaf.pointCount();
731 if (totalPointCount == 0) return;
732
733 // deformer is copied to ensure that it is unique per-thread
734
735 DeformerT newDeformer(deformer);
736
737 newDeformer.reset(leaf, idx);
738
739 auto handle = AttributeHandle<Vec3f>::create(leaf.constAttributeArray("P"));
740
741 auto& cache = leafs[idx];
742 cache.clear();
743
744 // only insert into a vector directly if the filter evaluates all points
745 // and all points are stored in active voxels
746 const bool useVector = filter.state() == index::ALL &&
747 (leaf.isDense() || (leaf.onPointCount() == leaf.pointCount()));
748 if (useVector) {
749 cache.vecData.resize(totalPointCount);
750 }
751
752 for (auto iter = leaf.beginIndexOn(filter); iter; iter++) {
753
754 // extract index-space position and apply index-space deformation (if defined)
755
756 Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
757
758 // if deformer is designed to be used in index-space, perform deformation prior
759 // to transforming position to world-space, otherwise perform deformation afterwards
760
762 newDeformer.apply(position, iter);
763 position = transform.indexToWorld(position);
764 }
765 else {
766 position = transform.indexToWorld(position);
767 newDeformer.apply(position, iter);
768 }
769
770 // insert new position into the cache
771
772 if (useVector) {
773 cache.vecData[*iter] = static_cast<Vec3T>(position);
774 }
775 else {
776 cache.mapData.insert({*iter, static_cast<Vec3T>(position)});
777 }
778 }
779
780 // store the total number of points to allow use of an expanded vector on access
781
782 if (!cache.mapData.empty()) {
783 cache.totalSize = static_cast<Index>(totalPointCount);
784 }
785 };
786
787 leafManager.foreach(cachePositionsOp, threaded);
788}
789
790
791template <typename T>
792template <typename LeafT>
793void CachedDeformer<T>::reset(const LeafT& /*leaf*/, size_t idx)
794{
795 if (idx >= mCache.leafs.size()) {
796 if (mCache.leafs.empty()) {
797 throw IndexError("No leafs in cache, perhaps CachedDeformer has not been evaluated?");
798 } else {
799 throw IndexError("Leaf index is out-of-range of cache leafs.");
800 }
801 }
802 auto& cache = mCache.leafs[idx];
803 if (!cache.mapData.empty()) {
804 mLeafMap = &cache.mapData;
805 mLeafVec = nullptr;
806 }
807 else {
808 mLeafVec = &cache.vecData;
809 mLeafMap = nullptr;
810 }
811}
812
813
814template <typename T>
815template <typename IndexIterT>
816void CachedDeformer<T>::apply(Vec3d& position, const IndexIterT& iter) const
817{
818 assert(*iter >= 0);
819
820 if (mLeafMap) {
821 auto it = mLeafMap->find(*iter);
822 if (it == mLeafMap->end()) return;
823 position = static_cast<openvdb::Vec3d>(it->second);
824 }
825 else {
826 assert(mLeafVec);
827
828 if (mLeafVec->empty()) return;
829 assert(*iter < mLeafVec->size());
830 position = static_cast<openvdb::Vec3d>((*mLeafVec)[*iter]);
831 }
832}
833
834
835} // namespace points
836} // namespace OPENVDB_VERSION_NAME
837} // namespace openvdb
838
839#endif // OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Methods for extracting masks from VDB Point grids.
Definition Exceptions.h:57
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:25
Vec3d asVec3d() const
Definition Coord.h:143
Definition Transform.h:40
Vec3d indexToWorld(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition Transform.h:108
Definition Vec3.h:24
static Ptr create(const AttributeArray &array, const bool collapseOnDestruction=true)
Definition AttributeArray.h:2116
Ordered collection of uniquely-named attribute arrays.
Definition AttributeSet.h:39
Write-able version of AttributeHandle.
Definition AttributeArray.h:907
A Deformer that caches the resulting positions from evaluating another Deformer.
Definition PointMove.h:99
void reset(const LeafT &leaf, size_t idx)
Definition PointMove.h:793
void apply(Vec3d &position, const IndexIterT &iter) const
Retrieve the new position from the cache.
Definition PointMove.h:816
point_move_internal::LeafIndex LeafIndex
Definition PointMove.h:101
std::unordered_map< LeafIndex, Vec3T > LeafMapT
Definition PointMove.h:104
typename math::Vec3< T > Vec3T
Definition PointMove.h:102
std::vector< Vec3T > LeafVecT
Definition PointMove.h:103
void evaluate(PointDataGridT &grid, DeformerT &deformer, const FilterT &filter, bool threaded=true)
Definition PointMove.h:712
A no-op filter that can be used when iterating over all indices.
Definition IndexIterator.h:51
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition LeafManager.h:85
@ ALL
Definition IndexIterator.h:43
Index32 LeafIndex
Definition PointMove.h:93
std::vector< IndexTripleArray > GlobalPointIndexMap
Definition PointMove.h:165
std::vector< IndexPair > IndexPairArray
Definition PointMove.h:169
std::unordered_map< Coord, LeafIndex > LeafMap
Definition PointMove.h:174
std::vector< IndexPairArray > LocalPointIndexMap
Definition PointMove.h:170
std::vector< LeafIndex > LeafIndexArray
Definition PointMove.h:172
std::vector< LeafIndexArray > LeafOffsetArray
Definition PointMove.h:173
std::vector< IndexArray > GlobalPointIndexIndices
Definition PointMove.h:166
tbb::concurrent_vector< IndexTriple > IndexTripleArray
Definition PointMove.h:164
Index indexOffsetFromVoxel(const Index voxelOffset, const LeafT &leaf, IndexArray &offsets)
Definition PointMove.h:272
std::vector< Index > IndexArray
Definition PointMove.h:161
std::pair< Index, Index > IndexPair
Definition PointMove.h:168
std::tuple< LeafIndex, Index, Index > IndexTriple
Definition PointMove.h:163
void movePoints(PointDataGridT &points, DeformerT &deformer, const FilterT &filter=NullFilter(), future::Advect *objectNotInUse=nullptr, bool threaded=true)
Move points in a PointDataGrid using a custom deformer.
Definition PointMove.h:692
Index32 Index
Definition Types.h:54
uint32_t Index32
Definition Types.h:52
uint64_t Index64
Definition Types.h:53
Definition Exceptions.h:13
LeafMapT mapData
Definition PointMove.h:119
LeafVecT vecData
Definition PointMove.h:118
void clear()
clear data buffers and reset counter
Definition PointMove.h:112
std::vector< Leaf > leafs
Definition PointMove.h:123
Deformer Traits for optionally configuring deformers to be applied in index-space....
Definition PointMask.h:87
std::vector< LeafT * > LeafArrayT
Definition PointMove.h:181
void operator()(LeafT &leaf, size_t idx) const
Definition PointMove.h:199
typename tree::LeafManager< TreeT > LeafManagerT
Definition PointMove.h:182
typename TreeT::LeafNodeType LeafT
Definition PointMove.h:180
BuildMoveMapsOp(const DeformerT &deformer, GlobalPointIndexMap &globalMoveLeafMap, LocalPointIndexMap &localMoveLeafMap, const LeafMap &targetLeafMap, const math::Transform &targetTransform, const math::Transform &sourceTransform, const FilterT &filter)
Definition PointMove.h:184
void reset(Index startIndex, Index endIndex)
Definition PointMove.h:318
CopyIterator(const LeafT &leaf, const IndexArray &sortedIndices, const IndexTripleArray &moveIndices, IndexArray &offsets)
Definition PointMove.h:309
std::vector< LeafT * > LeafArrayT
Definition PointMove.h:290
void operator()(LeafT &leaf, size_t idx) const
Definition PointMove.h:378
std::vector< AttributeArray * > AttributeArrays
Definition PointMove.h:292
typename tree::LeafManager< TreeT > LeafManagerT
Definition PointMove.h:291
typename TreeT::LeafNodeType LeafT
Definition PointMove.h:289
GlobalMovePointsOp(LeafOffsetArray &offsetMap, LeafManagerT &sourceLeafManager, const Index attributeIndex, const GlobalPointIndexMap &moveLeafMap, const GlobalPointIndexIndices &moveLeafIndices)
Definition PointMove.h:294
CopyIterator(const LeafT &leaf, const IndexPairArray &indices, IndexArray &offsets)
Definition PointMove.h:458
std::vector< LeafT * > LeafArrayT
Definition PointMove.h:439
void operator()(LeafT &leaf, size_t idx) const
Definition PointMove.h:484
LocalMovePointsOp(LeafOffsetArray &offsetMap, const LeafIndexArray &sourceIndices, LeafManagerT &sourceLeafManager, const Index attributeIndex, const LocalPointIndexMap &moveLeafMap)
Definition PointMove.h:443
std::vector< AttributeArray * > AttributeArrays
Definition PointMove.h:441
typename tree::LeafManager< TreeT > LeafManagerT
Definition PointMove.h:440
typename TreeT::LeafNodeType LeafT
Definition PointMove.h:438
#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