OpenVDB  6.0.0
PointMove.h
Go to the documentation of this file.
1 //
3 // Copyright (c) 2012-2018 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 
58 
59 #ifndef OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED
60 #define OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED
61 
62 #include <openvdb/openvdb.h>
63 
66 
67 #include <tbb/concurrent_vector.h>
68 
69 #include <algorithm>
70 #include <iterator> // for std::begin(), std::end()
71 #include <map>
72 #include <numeric> // for std::iota()
73 #include <tuple>
74 #include <unordered_map>
75 #include <vector>
76 
77 class TestPointMove;
78 
79 
80 namespace openvdb {
82 namespace OPENVDB_VERSION_NAME {
83 namespace points {
84 
85 // dummy object for future use
86 namespace future { struct Advect { }; }
87 
88 
95 template <typename PointDataGridT, typename DeformerT, typename FilterT = NullFilter>
96 inline void movePoints(PointDataGridT& points,
97  DeformerT& deformer,
98  const FilterT& filter = NullFilter(),
99  future::Advect* objectNotInUse = nullptr,
100  bool threaded = true);
101 
102 
110 template <typename PointDataGridT, typename DeformerT, typename FilterT = NullFilter>
111 inline void movePoints(PointDataGridT& points,
112  const math::Transform& transform,
113  DeformerT& deformer,
114  const FilterT& filter = NullFilter(),
115  future::Advect* objectNotInUse = nullptr,
116  bool threaded = true);
117 
118 
119 // define leaf index in use as 32-bit
120 namespace point_move_internal { using LeafIndex = Index32; }
121 
122 
124 template <typename T>
126 {
127 public:
129  using Vec3T = typename math::Vec3<T>;
130  using LeafVecT = std::vector<Vec3T>;
131  using LeafMapT = std::unordered_map<LeafIndex, Vec3T>;
132 
133  // Internal data cache to allow the deformer to offer light-weight copying
134  struct Cache
135  {
136  struct Leaf
137  {
139  void clear() {
140  vecData.clear();
141  mapData.clear();
142  totalSize = 0;
143  }
144 
147  Index totalSize = 0;
148  }; // struct Leaf
149 
150  std::vector<Leaf> leafs;
151  }; // struct Cache
152 
154  explicit CachedDeformer(Cache& cache);
155 
161  template <typename PointDataGridT, typename DeformerT, typename FilterT>
162  void evaluate(PointDataGridT& grid, DeformerT& deformer, const FilterT& filter,
163  bool threaded = true);
164 
167  template <typename LeafT>
168  void reset(const LeafT& leaf, size_t idx);
169 
171  template <typename IndexIterT>
172  void apply(Vec3d& position, const IndexIterT& iter) const;
173 
174 private:
175  friend class ::TestPointMove;
176 
177  Cache& mCache;
178  LeafVecT mLocalLeafVec;
179  const LeafVecT* mLeafVec = nullptr;
180  const LeafMapT* mLeafMap = nullptr;
181 }; // class CachedDeformer
182 
183 
185 
186 
187 namespace point_move_internal {
188 
189 
190 using IndexTriple = std::tuple<LeafIndex, Index, Index>;
191 using IndexTripleArray = tbb::concurrent_vector<IndexTriple>;
192 using GlobalPointIndexMap = std::vector<IndexTripleArray>;
193 
194 using IndexPair = std::pair<Index, Index>;
195 using IndexPairArray = std::vector<IndexPair>;
196 using LocalPointIndexMap = std::vector<IndexPairArray>;
197 
198 using IndexArray = std::vector<Index>;
199 
200 using LeafIndexArray = std::vector<LeafIndex>;
201 using LeafOffsetArray = std::vector<LeafIndexArray>;
202 using LeafMap = std::map<Coord, LeafIndex>;
203 
204 // TODO: The following infrastructure - ArrayProcessor, PerformTypedMoveOp, processTypedArray()
205 // is required to improve AttributeArray copying performance beyond using the virtual function
206 // AttributeArray::set(Index, AttributeArray&, Index), however an ABI=6+ addition to AttributeArray
207 // could eliminate this cost for the typical case where ValueType and CodecType are the same in
208 // source and target arrays, as they are here.
209 
210 
212 template<typename ValueType, typename OpType>
214  static inline void call(OpType& op, const AttributeArray& array) {
215 #ifdef _MSC_VER
216  op.operator()<ValueType>(array);
217 #else
218  op.template operator()<ValueType>(array);
219 #endif
220  }
221 };
222 
225 template<typename ArrayType, typename OpType>
226 bool
227 processTypedArray(const ArrayType& array, OpType& op)
228 {
229  using namespace openvdb;
230  using namespace openvdb::math;
231  if (array.template hasValueType<bool>()) ArrayProcessor<bool, OpType>::call(op, array);
232  else if (array.template hasValueType<int16_t>()) ArrayProcessor<int16_t, OpType>::call(op, array);
233  else if (array.template hasValueType<int32_t>()) ArrayProcessor<int32_t, OpType>::call(op, array);
234  else if (array.template hasValueType<int64_t>()) ArrayProcessor<int64_t, OpType>::call(op, array);
235  else if (array.template hasValueType<float>()) ArrayProcessor<float, OpType>::call(op, array);
236  else if (array.template hasValueType<double>()) ArrayProcessor<double, OpType>::call(op, array);
237  else if (array.template hasValueType<Vec3<int32_t>>()) ArrayProcessor<Vec3<int32_t>, OpType>::call(op, array);
238  else if (array.template hasValueType<Vec3<float>>()) ArrayProcessor<Vec3<float>, OpType>::call(op, array);
239  else if (array.template hasValueType<Vec3<double>>()) ArrayProcessor<Vec3<double>, OpType>::call(op, array);
240  else if (array.template hasValueType<GroupType>()) ArrayProcessor<GroupType, OpType>::call(op, array);
241  else if (array.template hasValueType<StringIndexType>()) ArrayProcessor<StringIndexType, OpType>::call(op, array);
242  else if (array.template hasValueType<Mat3<float>>()) ArrayProcessor<Mat3<float>, OpType>::call(op, array);
243  else if (array.template hasValueType<Mat3<double>>()) ArrayProcessor<Mat3<double>, OpType>::call(op, array);
244  else if (array.template hasValueType<Mat4<float>>()) ArrayProcessor<Mat4<float>, OpType>::call(op, array);
245  else if (array.template hasValueType<Mat4<double>>()) ArrayProcessor<Mat4<double>, OpType>::call(op, array);
246  else if (array.template hasValueType<Quat<float>>()) ArrayProcessor<Quat<float>, OpType>::call(op, array);
247  else if (array.template hasValueType<Quat<double>>()) ArrayProcessor<Quat<double>, OpType>::call(op, array);
248  else return false;
249  return true;
250 }
251 
252 
255 {
256  using HandleArray = std::vector<AttributeHandle<int>::Ptr>;
257 
258  AttributeHandles(const size_t size)
259  : mHandles() { mHandles.reserve(size); }
260 
261  AttributeArray& getArray(const Index leafOffset)
262  {
263  auto* handle = reinterpret_cast<AttributeWriteHandle<int>*>(mHandles[leafOffset].get());
264  assert(handle);
265  return handle->array();
266  }
267 
268  const AttributeArray& getConstArray(const Index leafOffset) const
269  {
270  const auto* handle = mHandles[leafOffset].get();
271  assert(handle);
272  return handle->array();
273  }
274 
275  template <typename ValueT>
277  {
278  auto* handle = reinterpret_cast<AttributeHandle<ValueT>*>(mHandles[leafOffset].get());
279  assert(handle);
280  return *handle;
281  }
282 
283  template <typename ValueT>
285  {
286  auto* handle = reinterpret_cast<AttributeWriteHandle<ValueT>*>(mHandles[leafOffset].get());
287  assert(handle);
288  return *handle;
289  }
290 
293  {
295  : mHandles(handles) { }
296 
297  template<typename ValueT>
298  void operator()(const AttributeArray& array) const
299  {
300  auto* handleAsInt = reinterpret_cast<AttributeHandle<int>*>(
301  new AttributeHandle<ValueT>(array));
302  mHandles.emplace_back(handleAsInt);
303  }
304 
305  private:
306  HandleArray& mHandles;
307  }; // struct CacheHandleOp
308 
309  template <typename LeafRangeT>
310  void cache(const LeafRangeT& range, const Index attributeIndex)
311  {
312  using namespace openvdb::math;
313 
314  mHandles.clear();
315  CacheHandleOp op(mHandles);
316 
317  for (auto leaf = range.begin(); leaf; ++leaf) {
318  auto& array = leaf->attributeArray(attributeIndex);
319  processTypedArray(array, op);
320  }
321  }
322 
323 private:
324  HandleArray mHandles;
325 }; // struct AttributeHandles
326 
327 
328 template <typename DeformerT, typename TreeT, typename FilterT>
330 {
331  using LeafT = typename TreeT::LeafNodeType;
332  using LeafArrayT = std::vector<LeafT*>;
334 
335  BuildMoveMapsOp(const DeformerT& deformer,
336  GlobalPointIndexMap& globalMoveLeafMap,
337  LocalPointIndexMap& localMoveLeafMap,
338  const LeafMap& targetLeafMap,
339  const math::Transform& targetTransform,
340  const math::Transform& sourceTransform,
341  const FilterT& filter)
342  : mDeformer(deformer)
343  , mGlobalMoveLeafMap(globalMoveLeafMap)
344  , mLocalMoveLeafMap(localMoveLeafMap)
345  , mTargetLeafMap(targetLeafMap)
346  , mTargetTransform(targetTransform)
347  , mSourceTransform(sourceTransform)
348  , mFilter(filter) { }
349 
350  void operator()(LeafT& leaf, size_t idx) const
351  {
352  DeformerT deformer(mDeformer);
353  deformer.reset(leaf, idx);
354 
355  // determine source leaf node origin and offset in the source leaf vector
356 
357  Coord sourceLeafOrigin = leaf.origin();
358 
359  auto sourceHandle = AttributeWriteHandle<Vec3f>::create(leaf.attributeArray("P"));
360 
361  for (auto iter = leaf.beginIndexOn(mFilter); iter; iter++) {
362 
363  const bool useIndexSpace = DeformerTraits<DeformerT>::IndexSpace;
364 
365  // extract index-space position and apply index-space deformation (if applicable)
366 
367  Vec3d positionIS = sourceHandle->get(*iter) + iter.getCoord().asVec3d();
368  if (useIndexSpace) {
369  deformer.apply(positionIS, iter);
370  }
371 
372  // transform to world-space position and apply world-space deformation (if applicable)
373 
374  Vec3d positionWS = mSourceTransform.indexToWorld(positionIS);
375  if (!useIndexSpace) {
376  deformer.apply(positionWS, iter);
377  }
378 
379  // transform to index-space position of target grid
380 
381  positionIS = mTargetTransform.worldToIndex(positionWS);
382 
383  // determine target voxel and offset
384 
385  Coord targetVoxel = Coord::round(positionIS);
386  Index targetOffset = LeafT::coordToOffset(targetVoxel);
387 
388  // set new local position in source transform space (if point has been deformed)
389 
390  Vec3d voxelPosition(positionIS - targetVoxel.asVec3d());
391  sourceHandle->set(*iter, voxelPosition);
392 
393  // determine target leaf node origin and offset in the target leaf vector
394 
395  Coord targetLeafOrigin = targetVoxel & ~(LeafT::DIM - 1);
396  assert(mTargetLeafMap.find(targetLeafOrigin) != mTargetLeafMap.end());
397  const LeafIndex targetLeafOffset(mTargetLeafMap.at(targetLeafOrigin));
398 
399  // insert into move map based on whether point ends up in a new leaf node or not
400 
401  if (targetLeafOrigin == sourceLeafOrigin) {
402  mLocalMoveLeafMap[targetLeafOffset].emplace_back(targetOffset, *iter);
403  }
404  else {
405  mGlobalMoveLeafMap[targetLeafOffset].push_back(IndexTriple(
406  LeafIndex(static_cast<LeafIndex>(idx)), targetOffset, *iter));
407  }
408  }
409  }
410 
411 private:
412  const DeformerT& mDeformer;
413  GlobalPointIndexMap& mGlobalMoveLeafMap;
414  LocalPointIndexMap& mLocalMoveLeafMap;
415  const LeafMap& mTargetLeafMap;
416  const math::Transform& mTargetTransform;
417  const math::Transform& mSourceTransform;
418  const FilterT& mFilter;
419 }; // struct BuildMoveMapsOp
420 
421 template <typename LeafT>
422 inline Index
423 indexOffsetFromVoxel(const Index voxelOffset, const LeafT& leaf, IndexArray& offsets)
424 {
425  // compute the target point index by summing the point index of the previous
426  // voxel with the current number of points added to this voxel, tracked by the
427  // offsets array
428 
429  Index targetOffset = offsets[voxelOffset]++;
430  if (voxelOffset > 0) {
431  targetOffset += static_cast<Index>(leaf.getValue(voxelOffset - 1));
432  }
433  return targetOffset;
434 }
435 
436 template <typename TreeT>
438 {
439  using LeafT = typename TreeT::LeafNodeType;
440  using LeafArrayT = std::vector<LeafT*>;
442 
444  AttributeHandles& targetHandles,
445  AttributeHandles& sourceHandles,
446  const Index attributeIndex,
447  const GlobalPointIndexMap& moveLeafMap)
448  : mOffsetMap(offsetMap)
449  , mTargetHandles(targetHandles)
450  , mSourceHandles(sourceHandles)
451  , mAttributeIndex(attributeIndex)
452  , mMoveLeafMap(moveLeafMap) { }
453 
455  {
456  PerformTypedMoveOp(AttributeHandles& targetHandles, AttributeHandles& sourceHandles,
457  Index targetOffset, const LeafT& targetLeaf,
458  IndexArray& offsets, const IndexTripleArray& indices)
459  : mTargetHandles(targetHandles)
460  , mSourceHandles(sourceHandles)
461  , mTargetOffset(targetOffset)
462  , mTargetLeaf(targetLeaf)
463  , mOffsets(offsets)
464  , mIndices(indices) { }
465 
466  template<typename ValueT>
467  void operator()(const AttributeArray&) const
468  {
469  auto& targetHandle = mTargetHandles.getWriteHandle<ValueT>(mTargetOffset);
470  targetHandle.expand();
471 
472  // build a sorted index vector that references the indices in order of their source
473  // leafs and voxels to ensure determinism in the resulting point orders
474 
475  std::vector<int> sortedIndices(mIndices.size());
476  std::iota(std::begin(sortedIndices), std::end(sortedIndices), 0);
477  std::sort(std::begin(sortedIndices), std::end(sortedIndices),
478  [&](int i, int j)
479  {
480  const Index& indexI0(std::get<0>(mIndices[i]));
481  const Index& indexJ0(std::get<0>(mIndices[j]));
482  if (indexI0 < indexJ0) return true;
483  if (indexI0 > indexJ0) return false;
484  return std::get<2>(mIndices[i]) < std::get<2>(mIndices[j]);
485  }
486  );
487 
488  for (const auto& index : sortedIndices) {
489  const auto& it = mIndices[index];
490  const auto& sourceHandle = mSourceHandles.getHandle<ValueT>(std::get<0>(it));
491  const Index targetIndex = indexOffsetFromVoxel(std::get<1>(it), mTargetLeaf, mOffsets);
492  for (Index i = 0; i < sourceHandle.stride(); i++) {
493  ValueT sourceValue = sourceHandle.get(std::get<2>(it), i);
494  targetHandle.set(targetIndex, i, sourceValue);
495  }
496  }
497  }
498 
499  private:
500  AttributeHandles& mTargetHandles;
501  AttributeHandles& mSourceHandles;
502  Index mTargetOffset;
503  const LeafT& mTargetLeaf;
504  IndexArray& mOffsets;
505  const IndexTripleArray& mIndices;
506  }; // struct PerformTypedMoveOp
507 
508  void performMove(Index targetOffset, const LeafT& targetLeaf,
509  IndexArray& offsets, const IndexTripleArray& indices) const
510  {
511  auto& targetArray = mTargetHandles.getArray(targetOffset);
512 
513  for (const auto& it : indices) {
514  const auto& sourceArray = mSourceHandles.getConstArray(std::get<0>(it));
515  const Index tgtOffset = indexOffsetFromVoxel(std::get<1>(it), targetLeaf, offsets);
516  targetArray.set(tgtOffset, sourceArray, std::get<2>(it));
517  }
518  }
519 
520  void operator()(LeafT& leaf, size_t aIdx) const
521  {
522  const Index idx(static_cast<Index>(aIdx));
523  const auto& moveIndices = mMoveLeafMap.at(aIdx);
524  if (moveIndices.empty()) return;
525 
526  // extract per-voxel offsets for this leaf
527 
528  auto& offsets = mOffsetMap[aIdx];
529 
530  const auto& array = leaf.constAttributeArray(mAttributeIndex);
531 
532  PerformTypedMoveOp op(mTargetHandles, mSourceHandles, idx, leaf, offsets, moveIndices);
533  if (!processTypedArray(array, op)) {
534  this->performMove(idx, leaf, offsets, moveIndices);
535  }
536  }
537 
538 private:
539  LeafOffsetArray& mOffsetMap;
540  AttributeHandles& mTargetHandles;
541  AttributeHandles& mSourceHandles;
542  const Index mAttributeIndex;
543  const GlobalPointIndexMap& mMoveLeafMap;
544 }; // struct GlobalMovePointsOp
545 
546 template <typename TreeT>
548 {
549  using LeafT = typename TreeT::LeafNodeType;
550  using LeafArrayT = std::vector<LeafT*>;
552 
554  AttributeHandles& targetHandles,
555  const LeafIndexArray& sourceIndices,
556  AttributeHandles& sourceHandles,
557  const Index attributeIndex,
558  const LocalPointIndexMap& moveLeafMap)
559  : mOffsetMap(offsetMap)
560  , mTargetHandles(targetHandles)
561  , mSourceIndices(sourceIndices)
562  , mSourceHandles(sourceHandles)
563  , mAttributeIndex(attributeIndex)
564  , mMoveLeafMap(moveLeafMap) { }
565 
567  {
568  PerformTypedMoveOp(AttributeHandles& targetHandles, AttributeHandles& sourceHandles,
569  Index targetOffset, Index sourceOffset, const LeafT& targetLeaf,
570  IndexArray& offsets, const IndexPairArray& indices)
571  : mTargetHandles(targetHandles)
572  , mSourceHandles(sourceHandles)
573  , mTargetOffset(targetOffset)
574  , mSourceOffset(sourceOffset)
575  , mTargetLeaf(targetLeaf)
576  , mOffsets(offsets)
577  , mIndices(indices) { }
578 
579  template<typename ValueT>
580  void operator()(const AttributeArray&) const
581  {
582  auto& targetHandle = mTargetHandles.getWriteHandle<ValueT>(mTargetOffset);
583  const auto& sourceHandle = mSourceHandles.getHandle<ValueT>(mSourceOffset);
584 
585  targetHandle.expand();
586 
587  for (const auto& it : mIndices) {
588  const Index targetIndex = indexOffsetFromVoxel(it.first, mTargetLeaf, mOffsets);
589  for (Index i = 0; i < sourceHandle.stride(); i++) {
590  ValueT sourceValue = sourceHandle.get(it.second, i);
591  targetHandle.set(targetIndex, i, sourceValue);
592  }
593  }
594  }
595 
596  private:
597  AttributeHandles& mTargetHandles;
598  AttributeHandles& mSourceHandles;
599  Index mTargetOffset;
600  Index mSourceOffset;
601  const LeafT& mTargetLeaf;
602  IndexArray& mOffsets;
603  const IndexPairArray& mIndices;
604  }; // struct PerformTypedMoveOp
605 
606  template <typename ValueT>
607  void performTypedMove(Index sourceOffset, Index targetOffset, const LeafT& targetLeaf,
608  IndexArray& offsets, const IndexPairArray& indices) const
609  {
610  auto& targetHandle = mTargetHandles.getWriteHandle<ValueT>(targetOffset);
611  const auto& sourceHandle = mSourceHandles.getHandle<ValueT>(sourceOffset);
612 
613  targetHandle.expand();
614 
615  for (const auto& it : indices) {
616  const Index tgtOffset = indexOffsetFromVoxel(it.first, targetLeaf, offsets);
617  for (Index i = 0; i < sourceHandle.stride(); i++) {
618  ValueT sourceValue = sourceHandle.get(it.second, i);
619  targetHandle.set(tgtOffset, i, sourceValue);
620  }
621  }
622  }
623 
624  void performMove(Index targetOffset, Index sourceOffset, const LeafT& targetLeaf,
625  IndexArray& offsets, const IndexPairArray& indices) const
626  {
627  auto& targetArray = mTargetHandles.getArray(targetOffset);
628  const auto& sourceArray = mSourceHandles.getConstArray(sourceOffset);
629 
630  for (const auto& it : indices) {
631  const Index tgtOffset = indexOffsetFromVoxel(it.first, targetLeaf, offsets);
632  targetArray.set(tgtOffset, sourceArray, it.second);
633  }
634  }
635 
636  void operator()(const LeafT& leaf, size_t aIdx) const
637  {
638  const Index idx(static_cast<Index>(aIdx));
639  const auto& moveIndices = mMoveLeafMap.at(aIdx);
640  if (moveIndices.empty()) return;
641 
642  // extract target leaf and per-voxel offsets for this leaf
643 
644  auto& offsets = mOffsetMap[aIdx];
645 
646  // extract source leaf that has the same origin as the target leaf (if any)
647 
648  assert(aIdx < mSourceIndices.size());
649  const Index sourceOffset(mSourceIndices[aIdx]);
650 
651  const auto& array = leaf.constAttributeArray(mAttributeIndex);
652 
653  PerformTypedMoveOp op(mTargetHandles, mSourceHandles,
654  idx, sourceOffset, leaf, offsets, moveIndices);
655  if (!processTypedArray(array, op)) {
656  this->performMove(idx, sourceOffset, leaf, offsets, moveIndices);
657  }
658  }
659 
660 private:
661  LeafOffsetArray& mOffsetMap;
662  AttributeHandles& mTargetHandles;
663  const LeafIndexArray& mSourceIndices;
664  AttributeHandles& mSourceHandles;
665  const Index mAttributeIndex;
666  const LocalPointIndexMap& mMoveLeafMap;
667 }; // struct LocalMovePointsOp
668 
669 } // namespace point_move_internal
670 
671 
673 
674 
675 template <typename PointDataGridT, typename DeformerT, typename FilterT>
676 inline void movePoints( PointDataGridT& points,
677  const math::Transform& transform,
678  DeformerT& deformer,
679  const FilterT& filter,
680  future::Advect* objectNotInUse,
681  bool threaded)
682 {
684  using PointDataTreeT = typename PointDataGridT::TreeType;
685  using LeafT = typename PointDataTreeT::LeafNodeType;
686  using LeafManagerT = typename tree::LeafManager<PointDataTreeT>;
687 
688  using namespace point_move_internal;
689 
690  // this object is for future use only
691  assert(!objectNotInUse);
692  (void)objectNotInUse;
693 
694  PointDataTreeT& tree = points.tree();
695 
696  // early exit if no LeafNodes
697 
698  PointDataTree::LeafCIter iter = tree.cbeginLeaf();
699 
700  if (!iter) return;
701 
702  // build voxel topology taking into account any point group deletion
703 
704  auto newPoints = point_mask_internal::convertPointsToScalar<PointDataGrid>(
705  points, transform, filter, deformer, threaded);
706  auto& newTree = newPoints->tree();
707 
708  // create leaf managers for both trees
709 
710  LeafManagerT sourceLeafManager(tree);
711  LeafManagerT targetLeafManager(newTree);
712 
713  // initialize attribute handles
714  AttributeHandles sourceHandles(sourceLeafManager.leafCount());
715  AttributeHandles targetHandles(targetLeafManager.leafCount());
716 
717  // extract the existing attribute set
718  const auto& existingAttributeSet = points.tree().cbeginLeaf()->attributeSet();
719 
720  // build a coord -> index map for looking up target leafs by origin and a faster
721  // unordered map for finding the source index from a target index
722 
723  LeafMap targetLeafMap;
724  LeafIndexArray sourceIndices(targetLeafManager.leafCount(),
726 
727  LeafOffsetArray offsetMap(targetLeafManager.leafCount());
728 
729  {
730  LeafMap sourceLeafMap;
731  auto sourceRange = sourceLeafManager.leafRange();
732  for (auto leaf = sourceRange.begin(); leaf; ++leaf) {
733  sourceLeafMap.insert({leaf->origin(), LeafIndex(static_cast<LeafIndex>(leaf.pos()))});
734  }
735  auto targetRange = targetLeafManager.leafRange();
736  for (auto leaf = targetRange.begin(); leaf; ++leaf) {
737  targetLeafMap.insert({leaf->origin(), LeafIndex(static_cast<LeafIndex>(leaf.pos()))});
738  }
739 
740  // perform four independent per-leaf operations in parallel
741  targetLeafManager.foreach(
742  [&](LeafT& leaf, size_t idx) {
743  // map frequency => cumulative histogram
744  auto* buffer = leaf.buffer().data();
745  for (Index i = 1; i < leaf.buffer().size(); i++) {
746  buffer[i] = buffer[i-1] + buffer[i];
747  }
748  // replace attribute set with a copy of the existing one
749  leaf.replaceAttributeSet(new AttributeSet(existingAttributeSet, leaf.getLastValue()),
750  /*allowMismatchingDescriptors=*/true);
751  // store the index of the source leaf in a corresponding target leaf array
752  const auto it = sourceLeafMap.find(leaf.origin());
753  if (it != sourceLeafMap.end()) {
754  sourceIndices[idx] = it->second;
755  }
756  // allocate offset maps
757  offsetMap[idx].resize(LeafT::SIZE);
758  },
759  threaded);
760  }
761 
762  // moving leaf
763 
764  GlobalPointIndexMap globalMoveLeafMap(targetLeafManager.leafCount());
765  LocalPointIndexMap localMoveLeafMap(targetLeafManager.leafCount());
766 
767  // build global and local move leaf maps and update local positions
768 
769  if (filter.state() == index::ALL) {
770  NullFilter nullFilter;
771  BuildMoveMapsOp<DeformerT, PointDataTreeT, NullFilter> op(deformer,
772  globalMoveLeafMap, localMoveLeafMap, targetLeafMap,
773  transform, points.transform(), nullFilter);
774  sourceLeafManager.foreach(op, threaded);
775  } else {
776  BuildMoveMapsOp<DeformerT, PointDataTreeT, FilterT> op(deformer,
777  globalMoveLeafMap, localMoveLeafMap, targetLeafMap,
778  transform, points.transform(), filter);
779  sourceLeafManager.foreach(op, threaded);
780  }
781 
782  for (const auto& it : existingAttributeSet.descriptor().map()) {
783 
784  const Index attributeIndex = static_cast<Index>(it.second);
785 
786  // zero offsets
787  targetLeafManager.foreach(
788  [&offsetMap](const LeafT& /*leaf*/, size_t idx) {
789  std::fill(offsetMap[idx].begin(), offsetMap[idx].end(), 0);
790  },
791  threaded);
792 
793  // cache attribute handles
794 
795  sourceHandles.cache(sourceLeafManager.leafRange(), attributeIndex);
796  targetHandles.cache(targetLeafManager.leafRange(), attributeIndex);
797 
798  // move points between leaf nodes
799 
800  GlobalMovePointsOp<PointDataTreeT> globalMoveOp(offsetMap, targetHandles,
801  sourceHandles, attributeIndex, globalMoveLeafMap);
802  targetLeafManager.foreach(globalMoveOp, threaded);
803 
804  // move points within leaf nodes
805 
806  LocalMovePointsOp<PointDataTreeT> localMoveOp(offsetMap, targetHandles,
807  sourceIndices, sourceHandles,
808  attributeIndex, localMoveLeafMap);
809  targetLeafManager.foreach(localMoveOp, threaded);
810  }
811 
812  points.setTree(newPoints->treePtr());
813 }
814 
815 
816 template <typename PointDataGridT, typename DeformerT, typename FilterT>
817 inline void movePoints( PointDataGridT& points,
818  DeformerT& deformer,
819  const FilterT& filter,
820  future::Advect* objectNotInUse,
821  bool threaded)
822 {
823  movePoints(points, points.transform(), deformer, filter, objectNotInUse, threaded);
824 }
825 
826 
828 
829 
830 template <typename T>
832  : mCache(cache) { }
833 
834 
835 template <typename T>
836 template <typename PointDataGridT, typename DeformerT, typename FilterT>
837 void CachedDeformer<T>::evaluate(PointDataGridT& grid, DeformerT& deformer, const FilterT& filter,
838  bool threaded)
839 {
840  using TreeT = typename PointDataGridT::TreeType;
841  using LeafT = typename TreeT::LeafNodeType;
842  using LeafManagerT = typename tree::LeafManager<TreeT>;
843  LeafManagerT leafManager(grid.tree());
844 
845  // initialize cache
846  auto& leafs = mCache.leafs;
847  leafs.resize(leafManager.leafCount());
848 
849  const auto& transform = grid.transform();
850 
851  // insert deformed positions into the cache
852 
853  auto cachePositionsOp = [&](const LeafT& leaf, size_t idx) {
854 
855  const Index64 totalPointCount = leaf.pointCount();
856  if (totalPointCount == 0) return;
857 
858  // deformer is copied to ensure that it is unique per-thread
859 
860  DeformerT newDeformer(deformer);
861 
862  // if more than half the number of total points are evaluated by the filter, prefer
863  // accessing the data from a vector instead of a hash map for faster performance
864  const Index64 vectorThreshold = totalPointCount / 2;
865 
866  newDeformer.reset(leaf, idx);
867 
868  auto handle = AttributeHandle<Vec3f>::create(leaf.constAttributeArray("P"));
869 
870  auto& cache = leafs[idx];
871  cache.clear();
872 
873  // only insert into a vector directly if the filter evaluates all points and the
874  // number of active points is greater than the vector threshold
875  const bool useVector = filter.state() == index::ALL &&
876  (leaf.isDense() || (leaf.onPointCount() > vectorThreshold));
877  if (useVector) {
878  cache.vecData.resize(totalPointCount);
879  }
880 
881  for (auto iter = leaf.beginIndexOn(filter); iter; iter++) {
882 
883  // extract index-space position and apply index-space deformation (if defined)
884 
885  Vec3d position = handle->get(*iter) + iter.getCoord().asVec3d();
886 
887  // if deformer is designed to be used in index-space, perform deformation prior
888  // to transforming position to world-space, otherwise perform deformation afterwards
889 
891  newDeformer.apply(position, iter);
892  position = transform.indexToWorld(position);
893  }
894  else {
895  position = transform.indexToWorld(position);
896  newDeformer.apply(position, iter);
897  }
898 
899  // insert new position into the cache
900 
901  if (useVector) {
902  cache.vecData[*iter] = static_cast<Vec3T>(position);
903  }
904  else {
905  cache.mapData.insert({*iter, static_cast<Vec3T>(position)});
906  }
907  }
908 
909  // after insertion, move the data into a vector if the threshold is reached
910 
911  if (!useVector && cache.mapData.size() > vectorThreshold) {
912  cache.vecData.resize(totalPointCount);
913  for (const auto& it : cache.mapData) {
914  cache.vecData[it.first] = it.second;
915  }
916  cache.mapData.clear();
917  }
918 
919  // store the total number of points to allow use of an expanded vector on access
920 
921  if (!cache.mapData.empty()) {
922  cache.totalSize = static_cast<Index>(totalPointCount);
923  }
924  };
925 
926  leafManager.foreach(cachePositionsOp, threaded);
927 }
928 
929 
930 template <typename T>
931 template <typename LeafT>
932 void CachedDeformer<T>::reset(const LeafT& /*leaf*/, size_t idx)
933 {
934  if (idx >= mCache.leafs.size()) {
935  if (mCache.leafs.empty()) {
936  throw IndexError("No leafs in cache, perhaps CachedDeformer has not been evaluated?");
937  } else {
938  throw IndexError("Leaf index is out-of-range of cache leafs.");
939  }
940  }
941  auto& cache = mCache.leafs[idx];
942  if (!cache.mapData.empty()) {
943  // expand into a local vector if there are greater than 16 values in the hash map
944  // and the expanded vector would contain fewer values than 256 times those in the
945  // hash map, this trades a little extra storage for faster random access performance
946  if (cache.mapData.size() > 16 &&
947  cache.totalSize < (cache.mapData.size() * 256)) {
948  if (cache.totalSize < cache.mapData.size()) {
949  throw ValueError("Cache total size is not valid.");
950  }
951  mLocalLeafVec.resize(cache.totalSize);
952  for (const auto& it : cache.mapData) {
953  assert(it.first < cache.totalSize);
954  mLocalLeafVec[it.first] = it.second;
955  }
956  mLeafVec = &mLocalLeafVec;
957  mLeafMap = nullptr;
958  }
959  else {
960  mLeafMap = &cache.mapData;
961  mLeafVec = nullptr;
962  }
963  }
964  else {
965  mLeafVec = &cache.vecData;
966  mLeafMap = nullptr;
967  }
968 }
969 
970 
971 template <typename T>
972 template <typename IndexIterT>
973 void CachedDeformer<T>::apply(Vec3d& position, const IndexIterT& iter) const
974 {
975  assert(*iter >= 0);
976 
977  if (mLeafMap) {
978  auto it = mLeafMap->find(*iter);
979  if (it == mLeafMap->end()) return;
980  position = static_cast<openvdb::Vec3d>(it->second);
981  }
982  else {
983  assert(mLeafVec);
984 
985  if (mLeafVec->empty()) return;
986  assert(*iter < mLeafVec->size());
987  position = static_cast<openvdb::Vec3d>((*mLeafVec)[*iter]);
988  }
989 }
990 
991 
992 } // namespace points
993 } // namespace OPENVDB_VERSION_NAME
994 } // namespace openvdb
995 
996 #endif // OPENVDB_POINTS_POINT_MOVE_HAS_BEEN_INCLUDED
997 
998 // Copyright (c) 2012-2018 DreamWorks Animation LLC
999 // All rights reserved. This software is distributed under the
1000 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
Index indexOffsetFromVoxel(const Index voxelOffset, const LeafT &leaf, IndexArray &offsets)
Definition: PointMove.h:423
std::unordered_map< LeafIndex, Vec3T > LeafMapT
Definition: PointMove.h:131
std::vector< LeafIndex > LeafIndexArray
Definition: PointMove.h:200
Deformer Traits for optionally configuring deformers to be applied in index-space....
Definition: PointMask.h:103
A no-op filter that can be used when iterating over all indices.
Definition: IndexIterator.h:77
4x4 -matrix class.
Definition: Mat3.h:49
AttributeArray & getArray(const Index leafOffset)
Definition: PointMove.h:261
Index32 Index
Definition: Types.h:61
uint64_t Index64
Definition: Types.h:60
typename tree::LeafManager< TreeT > LeafManagerT
Definition: PointMove.h:441
Base class for tree-traversal iterators over all leaf nodes (but not leaf voxels)
Definition: TreeIterator.h:1235
std::map< Coord, LeafIndex > LeafMap
Definition: PointMove.h:202
Write-able version of AttributeHandle.
Definition: AttributeArray.h:903
void performMove(Index targetOffset, Index sourceOffset, const LeafT &targetLeaf, IndexArray &offsets, const IndexPairArray &indices) const
Definition: PointMove.h:624
void operator()(const LeafT &leaf, size_t aIdx) const
Definition: PointMove.h:636
std::vector< IndexPairArray > LocalPointIndexMap
Definition: PointMove.h:196
std::vector< Leaf > leafs
Definition: PointMove.h:150
Cache read and write attribute handles to amortize construction cost.
Definition: PointMove.h:254
std::vector< LeafT * > LeafArrayT
Definition: PointMove.h:550
void clear()
clear data buffers and reset counter
Definition: PointMove.h:139
Vec3< double > Vec3d
Definition: Vec3.h:679
std::vector< Index > IndexArray
Definition: PointMove.h:198
Definition: Mat.h:197
void evaluate(PointDataGridT &grid, DeformerT &deformer, const FilterT &filter, bool threaded=true)
Definition: PointMove.h:837
void performTypedMove(Index sourceOffset, Index targetOffset, const LeafT &targetLeaf, IndexArray &offsets, const IndexPairArray &indices) const
Definition: PointMove.h:607
GlobalMovePointsOp(LeafOffsetArray &offsetMap, AttributeHandles &targetHandles, AttributeHandles &sourceHandles, const Index attributeIndex, const GlobalPointIndexMap &moveLeafMap)
Definition: PointMove.h:443
LeafVecT vecData
Definition: PointMove.h:145
void apply(Vec3d &position, const IndexIterT &iter) const
Retrieve the new position from the cache.
Definition: PointMove.h:973
typename TreeT::LeafNodeType LeafT
Definition: PointMove.h:439
void reset(const LeafT &leaf, size_t idx)
Definition: PointMove.h:932
3x3 matrix class.
Definition: Mat3.h:55
std::vector< LeafT * > LeafArrayT
Definition: PointMove.h:332
typename TreeT::LeafNodeType LeafT
Definition: PointMove.h:331
AttributeHandle< ValueT > & getHandle(const Index leafOffset)
Definition: PointMove.h:276
Signed (x, y, z) 32-bit integer coordinates.
Definition: Coord.h:51
std::vector< IndexTripleArray > GlobalPointIndexMap
Definition: PointMove.h:192
std::vector< LeafIndexArray > LeafOffsetArray
Definition: PointMove.h:201
void operator()(LeafT &leaf, size_t aIdx) const
Definition: PointMove.h:520
static Ptr create(const AttributeArray &array, const bool collapseOnDestruction=true)
Definition: AttributeArray.h:2095
void cache(const LeafRangeT &range, const Index attributeIndex)
Definition: PointMove.h:310
void operator()(const AttributeArray &) const
Definition: PointMove.h:580
Create a handle and reinterpret cast as an int handle to store.
Definition: PointMove.h:292
PerformTypedMoveOp(AttributeHandles &targetHandles, AttributeHandles &sourceHandles, Index targetOffset, Index sourceOffset, const LeafT &targetLeaf, IndexArray &offsets, const IndexPairArray &indices)
Definition: PointMove.h:568
LocalMovePointsOp(LeafOffsetArray &offsetMap, AttributeHandles &targetHandles, const LeafIndexArray &sourceIndices, AttributeHandles &sourceHandles, const Index attributeIndex, const LocalPointIndexMap &moveLeafMap)
Definition: PointMove.h:553
AttributeHandles(const size_t size)
Definition: PointMove.h:258
uint32_t Index32
Definition: Types.h:59
Ordered collection of uniquely-named attribute arrays.
Definition: AttributeSet.h:62
A Deformer that caches the resulting positions from evaluating another Deformer.
Definition: PointMove.h:125
AttributeWriteHandle< ValueT > & getWriteHandle(const Index leafOffset)
Definition: PointMove.h:284
Vec3d indexToWorld(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:135
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:136
Vec3d asVec3d() const
Definition: Coord.h:170
bool processTypedArray(const ArrayType &array, OpType &op)
Utility function that, given a generic attribute array, calls a functor with the fully-resolved value...
Definition: PointMove.h:227
Definition: Exceptions.h:40
Definition: Mat.h:196
typename tree::LeafManager< TreeT > LeafManagerT
Definition: PointMove.h:333
const AttributeArray & getConstArray(const Index leafOffset) const
Definition: PointMove.h:268
Definition: Exceptions.h:84
Definition: Transform.h:66
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:335
void operator()(const AttributeArray &) const
Definition: PointMove.h:467
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:110
void performMove(Index targetOffset, const LeafT &targetLeaf, IndexArray &offsets, const IndexTripleArray &indices) const
Definition: PointMove.h:508
std::vector< Vec3T > LeafVecT
Definition: PointMove.h:130
typename TreeT::LeafNodeType LeafT
Definition: PointMove.h:549
void operator()(const AttributeArray &array) const
Definition: PointMove.h:298
Methods for extracting masks from VDB Point grids.
void movePoints(PointDataGridT &points, const math::Transform &transform, DeformerT &deformer, const FilterT &filter=NullFilter(), future::Advect *objectNotInUse=nullptr, bool threaded=true)
Move points in a PointDataGrid using a custom deformer and a new transform.
Definition: PointMove.h:676
CacheHandleOp(HandleArray &handles)
Definition: PointMove.h:294
static Coord round(const Vec3< T > &xyz)
Return xyz rounded to the closest integer coordinates (cell centered conversion).
Definition: Coord.h:77
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:133
LeafMapT mapData
Definition: PointMove.h:146
Base class for storing attribute data.
Definition: AttributeArray.h:118
Definition: AttributeArray.h:832
Definition: IndexIterator.h:70
void operator()(LeafT &leaf, size_t idx) const
Definition: PointMove.h:350
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
std::tuple< LeafIndex, Index, Index > IndexTriple
Definition: PointMove.h:190
tbb::concurrent_vector< IndexTriple > IndexTripleArray
Definition: PointMove.h:191
std::pair< Index, Index > IndexPair
Definition: PointMove.h:194
PerformTypedMoveOp(AttributeHandles &targetHandles, AttributeHandles &sourceHandles, Index targetOffset, const LeafT &targetLeaf, IndexArray &offsets, const IndexTripleArray &indices)
Definition: PointMove.h:456
Index32 LeafIndex
Definition: PointMove.h:120
typename math::Vec3< T > Vec3T
Definition: PointMove.h:129
Definition: Exceptions.h:92
point_move_internal::LeafIndex LeafIndex
Definition: PointMove.h:128
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:188
Helper class used internally by processTypedArray()
Definition: PointMove.h:213
static void call(OpType &op, const AttributeArray &array)
Definition: PointMove.h:214
typename tree::LeafManager< TreeT > LeafManagerT
Definition: PointMove.h:551
Definition: PointMove.h:86
Index size() const
Definition: AttributeArray.h:855
std::vector< AttributeHandle< int >::Ptr > HandleArray
Definition: PointMove.h:256
std::vector< IndexPair > IndexPairArray
Definition: PointMove.h:195
std::vector< LeafT * > LeafArrayT
Definition: PointMove.h:440