HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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 
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 
50 class TestPointMove;
51 
52 
53 namespace openvdb {
55 namespace OPENVDB_VERSION_NAME {
56 namespace points {
57 
58 // dummy object for future use
59 namespace 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)
68 template <typename PointDataGridT, typename DeformerT, typename FilterT = NullFilter>
69 inline 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)
83 template <typename PointDataGridT, typename DeformerT, typename FilterT = NullFilter>
84 inline void movePoints(PointDataGridT& points,
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
93 namespace point_move_internal { using LeafIndex = Index32; }
94 
95 
96 /// @brief A Deformer that caches the resulting positions from evaluating another Deformer
97 template <typename T>
99 {
100 public:
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 
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 
147 private:
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 
159 namespace point_move_internal {
160 
161 using IndexArray = std::vector<Index>;
162 
163 using IndexTriple = std::tuple<LeafIndex, Index, Index>;
164 using IndexTripleArray = tbb::concurrent_vector<IndexTriple>;
165 using GlobalPointIndexMap = std::vector<IndexTripleArray>;
166 using GlobalPointIndexIndices = std::vector<IndexArray>;
167 
168 using IndexPair = std::pair<Index, Index>;
169 using IndexPairArray = std::vector<IndexPair>;
170 using LocalPointIndexMap = std::vector<IndexPairArray>;
171 
172 using LeafIndexArray = std::vector<LeafIndex>;
173 using LeafOffsetArray = std::vector<LeafIndexArray>;
174 using LeafMap = std::unordered_map<Coord, LeafIndex>;
175 
176 
177 template <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 
260 private:
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 
270 template <typename LeafT>
271 inline Index
272 indexOffsetFromVoxel(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 
286 template <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  }
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 
426 private:
427  LeafOffsetArray& mOffsetMap;
428  LeafManagerT& mSourceLeafManager;
429  const Index mAttributeIndex;
430  const GlobalPointIndexMap& mMoveLeafMap;
431  const GlobalPointIndexIndices& mMoveLeafIndices;
432 }; // struct GlobalMovePointsOp
433 
434 
435 template <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  {
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 
513 private:
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 
528 template <typename PointDataGridT, typename DeformerT, typename FilterT>
529 inline void movePoints( PointDataGridT& points,
530  const math::Transform& transform,
531  DeformerT& deformer,
532  const FilterT& filter,
533  future::Advect* objectNotInUse,
534  bool threaded)
535 {
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(),
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 
691 template <typename PointDataGridT, typename DeformerT, typename FilterT>
692 inline 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 
705 template <typename T>
707  : mCache(cache) { }
708 
709 
710 template <typename T>
711 template <typename PointDataGridT, typename DeformerT, typename FilterT>
712 void 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 
791 template <typename T>
792 template <typename LeafT>
793 void 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 
814 template <typename T>
815 template <typename IndexIterT>
816 void 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
LocalMovePointsOp(LeafOffsetArray &offsetMap, const LeafIndexArray &sourceIndices, LeafManagerT &sourceLeafManager, const Index attributeIndex, const LocalPointIndexMap &moveLeafMap)
Definition: PointMove.h:443
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
Vec3d indexToWorld(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:108
GLint first
Definition: glcorearb.h:405
void
Definition: png.h:1083
GLuint GLsizei const GLuint const GLintptr * offsets
Definition: glcorearb.h:2621
std::unordered_map< LeafIndex, Vec3T > LeafMapT
Definition: PointMove.h:104
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:207
void apply(Vec3d &position, const IndexIterT &iter) const
Retrieve the new position from the cache.
Definition: PointMove.h:816
GlobalMovePointsOp(LeafOffsetArray &offsetMap, LeafManagerT &sourceLeafManager, const Index attributeIndex, const GlobalPointIndexMap &moveLeafMap, const GlobalPointIndexIndices &moveLeafIndices)
Definition: PointMove.h:294
A no-op filter that can be used when iterating over all indices.
Definition: IndexIterator.h:50
std::tuple< LeafIndex, Index, Index > IndexTriple
Definition: PointMove.h:163
static Ptr create(AttributeArray &array, const bool expand=true)
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t< Char > &fill) -> OutputIt
Definition: format.h:1262
GLsizeiptr size
Definition: glcorearb.h:664
GLuint GLenum GLenum transform
Definition: glew.h:15055
Definition: core.h:760
void reset(const LeafT &leaf, size_t idx)
Definition: PointMove.h:793
CachedDeformer(Cache &cache)
Cache is expected to be persistent for the lifetime of the CachedDeformer.
Definition: PointMove.h:706
tbb::concurrent_vector< IndexTriple > IndexTripleArray
Definition: PointMove.h:164
Index indexOffsetFromVoxel(const Index voxelOffset, const LeafT &leaf, IndexArray &offsets)
Definition: PointMove.h:272
void clear()
clear data buffers and reset counter
Definition: PointMove.h:112
A Deformer that caches the resulting positions from evaluating another Deformer.
Definition: PointMove.h:98
Deformer Traits for optionally configuring deformers to be applied in index-space. The default is world-space.
Definition: PointMask.h:86
GLuint GLuint end
Definition: glcorearb.h:475
CopyIterator(const LeafT &leaf, const IndexPairArray &indices, IndexArray &offsets)
Definition: PointMove.h:458
GLsizei GLenum const void * indices
Definition: glcorearb.h:406
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:84
void evaluate(PointDataGridT &grid, DeformerT &deformer, const FilterT &filter, bool threaded=true)
Definition: PointMove.h:712
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
std::unordered_map< Coord, LeafIndex > LeafMap
Definition: PointMove.h:174
vfloat4 round(const vfloat4 &a)
Definition: simd.h:7436
std::vector< IndexTripleArray > GlobalPointIndexMap
Definition: PointMove.h:165
buffer(size_t sz) FMT_NOEXCEPT
Definition: core.h:769
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
GLuint GLdouble GLdouble GLint GLint const GLdouble * points
Definition: glew.h:3460
static Ptr create(const AttributeArray &array, const bool collapseOnDestruction=true)
Methods for extracting masks from VDB Point grids.
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
Ordered collection of uniquely-named attribute arrays.
Definition: AttributeSet.h:38
CopyIterator(const LeafT &leaf, const IndexArray &sortedIndices, const IndexTripleArray &moveIndices, IndexArray &offsets)
Definition: PointMove.h:309
#define SIZE
Definition: simple.C:40
Vec3d worldToIndex(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:110
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
void sort(I begin, I end, const Pred &pred)
Definition: pugixml.cpp:7334
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
PcpNodeRef_ChildrenIterator begin(const PcpNodeRef::child_const_range &r)
Support for range-based for loops for PcpNodeRef children ranges.
Definition: node.h:450