HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PointConversion.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, Nick Avramoussis
5 ///
6 /// @file points/PointConversion.h
7 ///
8 /// @brief Convert points and attributes to and from VDB Point Data grids.
9 
10 #ifndef OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED
11 #define OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED
12 
13 #include <openvdb/math/Transform.h>
14 
18 
19 #include "AttributeArrayString.h"
20 #include "AttributeSet.h"
21 #include "IndexFilter.h"
22 #include "PointAttribute.h"
23 #include "PointDataGrid.h"
24 #include "PointGroup.h"
25 
26 #include <tbb/parallel_reduce.h>
27 
28 #include <type_traits>
29 
30 namespace openvdb {
32 namespace OPENVDB_VERSION_NAME {
33 namespace points {
34 
35 
36 /// @brief Localises points with position into a @c PointDataGrid into two stages:
37 /// allocation of the leaf attribute data and population of the positions.
38 ///
39 /// @param pointIndexGrid a PointIndexGrid into the points.
40 /// @param positions list of world space point positions.
41 /// @param xform world to index space transform.
42 /// @param positionDefaultValue metadata default position value
43 ///
44 /// @note The position data must be supplied in a Point-Partitioner compatible
45 /// data structure. A convenience PointAttributeVector class is offered.
46 ///
47 /// @note The position data is populated separately to perform world space to
48 /// voxel space conversion and apply quantisation.
49 ///
50 /// @note A @c PointIndexGrid to the points must be supplied to perform this
51 /// operation. Typically this is built implicitly by the PointDataGrid constructor.
52 
53 template<
54  typename CompressionT,
55  typename PointDataGridT,
56  typename PositionArrayT,
57  typename PointIndexGridT>
58 inline typename PointDataGridT::Ptr
59 createPointDataGrid(const PointIndexGridT& pointIndexGrid,
60  const PositionArrayT& positions,
61  const math::Transform& xform,
62  const Metadata* positionDefaultValue = nullptr);
63 
64 
65 /// @brief Convenience method to create a @c PointDataGrid from a std::vector of
66 /// point positions.
67 ///
68 /// @param positions list of world space point positions.
69 /// @param xform world to index space transform.
70 /// @param positionDefaultValue metadata default position value
71 ///
72 /// @note This method implicitly wraps the std::vector for a Point-Partitioner compatible
73 /// data structure and creates the required @c PointIndexGrid to the points.
74 
75 template <typename CompressionT, typename PointDataGridT, typename ValueT>
76 inline typename PointDataGridT::Ptr
77 createPointDataGrid(const std::vector<ValueT>& positions,
78  const math::Transform& xform,
79  const Metadata* positionDefaultValue = nullptr);
80 
81 
82 /// @brief Stores point attribute data in an existing @c PointDataGrid attribute.
83 ///
84 /// @param tree the PointDataGrid to be populated.
85 /// @param pointIndexTree a PointIndexTree into the points.
86 /// @param attributeName the name of the VDB Points attribute to be populated.
87 /// @param data a wrapper to the attribute data.
88 /// @param stride the stride of the attribute
89 /// @param insertMetadata true if strings are to be automatically inserted as metadata.
90 ///
91 /// @note A @c PointIndexGrid to the points must be supplied to perform this
92 /// operation. This is required to ensure the same point index ordering.
93 template <typename PointDataTreeT, typename PointIndexTreeT, typename PointArrayT>
94 inline void
95 populateAttribute( PointDataTreeT& tree,
96  const PointIndexTreeT& pointIndexTree,
97  const openvdb::Name& attributeName,
98  const PointArrayT& data,
99  const Index stride = 1,
100  const bool insertMetadata = true);
101 
102 /// @brief Convert the position attribute from a Point Data Grid
103 ///
104 /// @param positionAttribute the position attribute to be populated.
105 /// @param grid the PointDataGrid to be converted.
106 /// @param pointOffsets a vector of cumulative point offsets for each leaf
107 /// @param startOffset a value to shift all the point offsets by
108 /// @param filter an index filter
109 /// @param inCoreOnly true if out-of-core leaf nodes are to be ignored
110 ///
111 
112 template <typename PositionAttribute, typename PointDataGridT, typename FilterT = NullFilter>
113 inline void
114 convertPointDataGridPosition( PositionAttribute& positionAttribute,
115  const PointDataGridT& grid,
116  const std::vector<Index64>& pointOffsets,
117  const Index64 startOffset,
118  const FilterT& filter = NullFilter(),
119  const bool inCoreOnly = false);
120 
121 
122 /// @brief Convert the attribute from a PointDataGrid
123 ///
124 /// @param attribute the attribute to be populated.
125 /// @param tree the PointDataTree to be converted.
126 /// @param pointOffsets a vector of cumulative point offsets for each leaf.
127 /// @param startOffset a value to shift all the point offsets by
128 /// @param arrayIndex the index in the Descriptor of the array to be converted.
129 /// @param stride the stride of the attribute
130 /// @param filter an index filter
131 /// @param inCoreOnly true if out-of-core leaf nodes are to be ignored
132 template <typename TypedAttribute, typename PointDataTreeT, typename FilterT = NullFilter>
133 inline void
135  const PointDataTreeT& tree,
136  const std::vector<Index64>& pointOffsets,
137  const Index64 startOffset,
138  const unsigned arrayIndex,
139  const Index stride = 1,
140  const FilterT& filter = NullFilter(),
141  const bool inCoreOnly = false);
142 
143 
144 /// @brief Convert the group from a PointDataGrid
145 ///
146 /// @param group the group to be populated.
147 /// @param tree the PointDataTree to be converted.
148 /// @param pointOffsets a vector of cumulative point offsets for each leaf
149 /// @param startOffset a value to shift all the point offsets by
150 /// @param index the group index to be converted.
151 /// @param filter an index filter
152 /// @param inCoreOnly true if out-of-core leaf nodes are to be ignored
153 ///
154 
155 template <typename Group, typename PointDataTreeT, typename FilterT = NullFilter>
156 inline void
158  const PointDataTreeT& tree,
159  const std::vector<Index64>& pointOffsets,
160  const Index64 startOffset,
161  const AttributeSet::Descriptor::GroupIndex index,
162  const FilterT& filter = NullFilter(),
163  const bool inCoreOnly = false);
164 
165 /// @ brief Given a container of world space positions and a target points per voxel,
166 /// compute a uniform voxel size that would best represent the storage of the points in a grid.
167 /// This voxel size is typically used for conversion of the points into a PointDataGrid.
168 ///
169 /// @param positions array of world space positions
170 /// @param pointsPerVoxel the target number of points per voxel, must be positive and non-zero
171 /// @param transform voxel size will be computed using this optional transform if provided
172 /// @param decimalPlaces for readability, truncate voxel size to this number of decimals
173 /// @param interrupter an optional interrupter
174 ///
175 /// @note if none or one point provided in positions, the default voxel size of 0.1 will be returned
176 ///
177 template<typename PositionWrapper, typename InterrupterT = openvdb::util::NullInterrupter>
178 inline float
179 computeVoxelSize( const PositionWrapper& positions,
180  const uint32_t pointsPerVoxel,
182  const Index decimalPlaces = 5,
183  InterrupterT* const interrupter = nullptr);
184 
185 
186 ////////////////////////////////////////
187 
188 
189 /// @brief Point-partitioner compatible STL vector attribute wrapper for convenience
190 template<typename ValueType>
192 public:
193  using PosType = ValueType;
194  using value_type= ValueType;
195 
196  PointAttributeVector(const std::vector<value_type>& data,
197  const Index stride = 1)
198  : mData(data)
199  , mStride(stride) { }
200 
201  size_t size() const { return mData.size(); }
202  void getPos(size_t n, ValueType& xyz) const { xyz = mData[n]; }
203  void get(ValueType& value, size_t n) const { value = mData[n]; }
204  void get(ValueType& value, size_t n, openvdb::Index m) const { value = mData[n * mStride + m]; }
205 
206 private:
207  const std::vector<value_type>& mData;
208  const Index mStride;
209 }; // PointAttributeVector
210 
211 
212 ////////////////////////////////////////
213 
214 
215 namespace point_conversion_internal {
216 
217 
218 // ConversionTraits to create the relevant Attribute Handles from a LeafNode
219 template <typename T> struct ConversionTraits
220 {
223  static T zero() { return zeroVal<T>(); }
224  template <typename LeafT>
225  static typename Handle::Ptr handleFromLeaf(LeafT& leaf, Index index) {
226  const AttributeArray& array = leaf.constAttributeArray(index);
227  return Handle::create(array);
228  }
229  template <typename LeafT>
230  static typename WriteHandle::Ptr writeHandleFromLeaf(LeafT& leaf, Index index) {
231  AttributeArray& array = leaf.attributeArray(index);
232  return WriteHandle::create(array);
233  }
234 }; // ConversionTraits
235 template <> struct ConversionTraits<openvdb::Name>
236 {
239  static openvdb::Name zero() { return ""; }
240  template <typename LeafT>
241  static typename Handle::Ptr handleFromLeaf(LeafT& leaf, Index index) {
242  const AttributeArray& array = leaf.constAttributeArray(index);
243  const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor();
244  return Handle::create(array, descriptor.getMetadata());
245  }
246  template <typename LeafT>
247  static typename WriteHandle::Ptr writeHandleFromLeaf(LeafT& leaf, Index index) {
248  AttributeArray& array = leaf.attributeArray(index);
249  const AttributeSet::Descriptor& descriptor = leaf.attributeSet().descriptor();
250  return WriteHandle::create(array, descriptor.getMetadata());
251  }
252 }; // ConversionTraits<openvdb::Name>
253 
254 template< typename PointDataTreeType,
255  typename PointIndexTreeType,
256  typename AttributeListType>
258 
260  using LeafRangeT = typename LeafManagerT::LeafRange;
261  using PointIndexLeafNode = typename PointIndexTreeType::LeafNodeType;
265 
266  PopulateAttributeOp(const PointIndexTreeType& pointIndexTree,
267  const AttributeListType& data,
268  const size_t index,
269  const Index stride = 1)
270  : mPointIndexTree(pointIndexTree)
271  , mData(data)
272  , mIndex(index)
273  , mStride(stride) { }
274 
275  void operator()(const typename LeafManagerT::LeafRange& range) const {
276 
277  for (auto leaf = range.begin(); leaf; ++leaf) {
278 
279  // obtain the PointIndexLeafNode (using the origin of the current leaf)
280 
281  const PointIndexLeafNode* pointIndexLeaf =
282  mPointIndexTree.probeConstLeaf(leaf->origin());
283 
284  if (!pointIndexLeaf) continue;
285 
286  typename HandleT::Ptr attributeWriteHandle =
287  ConversionTraits<ValueType>::writeHandleFromLeaf(*leaf, static_cast<Index>(mIndex));
288 
289  Index64 index = 0;
290 
291  const IndexArray& indices = pointIndexLeaf->indices();
292 
293  for (const Index64 leafIndex: indices)
294  {
296  for (Index i = 0; i < mStride; i++) {
297  mData.get(value, leafIndex, i);
298  attributeWriteHandle->set(static_cast<Index>(index), i, value);
299  }
300  index++;
301  }
302 
303  // attempt to compact the array
304 
305  attributeWriteHandle->compact();
306  }
307  }
308 
309  //////////
310 
311  const PointIndexTreeType& mPointIndexTree;
312  const AttributeListType& mData;
313  const size_t mIndex;
314  const Index mStride;
315 };
316 
317 template<typename PointDataTreeType, typename Attribute, typename FilterT>
319 
320  using LeafNode = typename PointDataTreeType::LeafNodeType;
321  using ValueType = typename Attribute::ValueType;
322  using HandleT = typename Attribute::Handle;
325  using LeafRangeT = typename LeafManagerT::LeafRange;
326 
328  const std::vector<Index64>& pointOffsets,
329  const Index64 startOffset,
330  const math::Transform& transform,
331  const size_t index,
332  const FilterT& filter,
333  const bool inCoreOnly)
334  : mAttribute(attribute)
335  , mPointOffsets(pointOffsets)
336  , mStartOffset(startOffset)
337  , mTransform(transform)
338  , mIndex(index)
339  , mFilter(filter)
340  , mInCoreOnly(inCoreOnly)
341  {
342  // only accept Vec3f as ValueType
343  static_assert(VecTraits<ValueType>::Size == 3 &&
345  "ValueType is not Vec3f");
346  }
347 
348  template <typename IterT>
349  void convert(IterT& iter, HandleT& targetHandle,
350  SourceHandleT& sourceHandle, Index64& offset) const
351  {
352  for (; iter; ++iter) {
353  const Vec3d xyz = iter.getCoord().asVec3d();
354  const Vec3d pos = sourceHandle.get(*iter);
355  targetHandle.set(static_cast<Index>(offset++), /*stride=*/0,
356  mTransform.indexToWorld(pos + xyz));
357  }
358  }
359 
360  void operator()(const LeafRangeT& range) const
361  {
362  HandleT pHandle(mAttribute);
363 
364  for (auto leaf = range.begin(); leaf; ++leaf) {
365 
366  assert(leaf.pos() < mPointOffsets.size());
367 
368  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
369 
371 
372  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
373 
374  auto handle = SourceHandleT::create(leaf->constAttributeArray(mIndex));
375 
376  if (mFilter.state() == index::ALL) {
377  auto iter = leaf->beginIndexOn();
378  convert(iter, pHandle, *handle, offset);
379  }
380  else {
381  auto iter = leaf->beginIndexOn(mFilter);
382  convert(iter, pHandle, *handle, offset);
383  }
384  }
385  }
386 
387  //////////
388 
390  const std::vector<Index64>& mPointOffsets;
393  const size_t mIndex;
394  const FilterT& mFilter;
395  const bool mInCoreOnly;
396 }; // ConvertPointDataGridPositionOp
397 
398 
399 template<typename PointDataTreeType, typename Attribute, typename FilterT>
401 
402  using LeafNode = typename PointDataTreeType::LeafNodeType;
403  using ValueType = typename Attribute::ValueType;
404  using HandleT = typename Attribute::Handle;
407  using LeafRangeT = typename LeafManagerT::LeafRange;
408 
410  const std::vector<Index64>& pointOffsets,
411  const Index64 startOffset,
412  const size_t index,
413  const Index stride,
414  const FilterT& filter,
415  const bool inCoreOnly)
416  : mAttribute(attribute)
417  , mPointOffsets(pointOffsets)
418  , mStartOffset(startOffset)
419  , mIndex(index)
420  , mStride(stride)
421  , mFilter(filter)
422  , mInCoreOnly(inCoreOnly) { }
423 
424  template <typename IterT>
425  void convert(IterT& iter, HandleT& targetHandle,
426  SourceHandleT& sourceHandle, Index64& offset) const
427  {
428  if (sourceHandle.isUniform()) {
429  const ValueType uniformValue(sourceHandle.get(0));
430  for (; iter; ++iter) {
431  for (Index i = 0; i < mStride; i++) {
432  targetHandle.set(static_cast<Index>(offset), i, uniformValue);
433  }
434  offset++;
435  }
436  }
437  else {
438  for (; iter; ++iter) {
439  for (Index i = 0; i < mStride; i++) {
440  targetHandle.set(static_cast<Index>(offset), i,
441  sourceHandle.get(*iter, /*stride=*/i));
442  }
443  offset++;
444  }
445  }
446  }
447 
448  void operator()(const LeafRangeT& range) const
449  {
450  HandleT pHandle(mAttribute);
451 
452  for (auto leaf = range.begin(); leaf; ++leaf) {
453 
454  assert(leaf.pos() < mPointOffsets.size());
455 
456  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
457 
459 
460  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
461 
462  typename SourceHandleT::Ptr handle = ConversionTraits<ValueType>::handleFromLeaf(
463  *leaf, static_cast<Index>(mIndex));
464 
465  if (mFilter.state() == index::ALL) {
466  auto iter = leaf->beginIndexOn();
467  convert(iter, pHandle, *handle, offset);
468  } else {
469  auto iter = leaf->beginIndexOn(mFilter);
470  convert(iter, pHandle, *handle, offset);
471  }
472  }
473  }
474 
475  //////////
476 
478  const std::vector<Index64>& mPointOffsets;
480  const size_t mIndex;
481  const Index mStride;
482  const FilterT& mFilter;
483  const bool mInCoreOnly;
484 }; // ConvertPointDataGridAttributeOp
485 
486 template<typename PointDataTreeType, typename Group, typename FilterT>
488 
489  using LeafNode = typename PointDataTreeType::LeafNodeType;
490  using GroupIndex = AttributeSet::Descriptor::GroupIndex;
492  using LeafRangeT = typename LeafManagerT::LeafRange;
493 
495  const std::vector<Index64>& pointOffsets,
496  const Index64 startOffset,
497  const AttributeSet::Descriptor::GroupIndex index,
498  const FilterT& filter,
499  const bool inCoreOnly)
500  : mGroup(group)
501  , mPointOffsets(pointOffsets)
502  , mStartOffset(startOffset)
503  , mIndex(index)
504  , mFilter(filter)
505  , mInCoreOnly(inCoreOnly) { }
506 
507  template <typename IterT>
508  void convert(IterT& iter, const GroupAttributeArray& groupArray, Index64& offset) const
509  {
510  const auto bitmask = static_cast<GroupType>(1 << mIndex.second);
511 
512  if (groupArray.isUniform()) {
513  if (groupArray.get(0) & bitmask) {
514  for (; iter; ++iter) {
515  mGroup.setOffsetOn(static_cast<Index>(offset));
516  offset++;
517  }
518  }
519  }
520  else {
521  for (; iter; ++iter) {
522  if (groupArray.get(*iter) & bitmask) {
523  mGroup.setOffsetOn(static_cast<Index>(offset));
524  }
525  offset++;
526  }
527  }
528  }
529 
530  void operator()(const LeafRangeT& range) const
531  {
532  for (auto leaf = range.begin(); leaf; ++leaf) {
533 
534  assert(leaf.pos() < mPointOffsets.size());
535 
536  if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue;
537 
539 
540  if (leaf.pos() > 0) offset += mPointOffsets[leaf.pos() - 1];
541 
542  const AttributeArray& array = leaf->constAttributeArray(mIndex.first);
543  assert(isGroup(array));
544  const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array);
545 
546  if (mFilter.state() == index::ALL) {
547  auto iter = leaf->beginIndexOn();
548  convert(iter, groupArray, offset);
549  }
550  else {
551  auto iter = leaf->beginIndexOn(mFilter);
552  convert(iter, groupArray, offset);
553  }
554  }
555  }
556 
557  //////////
558 
559  Group& mGroup;
560  const std::vector<Index64>& mPointOffsets;
563  const FilterT& mFilter;
564  const bool mInCoreOnly;
565 }; // ConvertPointDataGridGroupOp
566 
567 template<typename PositionArrayT>
569 {
570  CalculatePositionBounds(const PositionArrayT& positions,
571  const math::Mat4d& inverse)
572  : mPositions(positions)
573  , mInverseMat(inverse)
574  , mMin(std::numeric_limits<Real>::max())
575  , mMax(-std::numeric_limits<Real>::max()) {}
576 
578  : mPositions(other.mPositions)
579  , mInverseMat(other.mInverseMat)
580  , mMin(std::numeric_limits<Real>::max())
581  , mMax(-std::numeric_limits<Real>::max()) {}
582 
583  void operator()(const tbb::blocked_range<size_t>& range) {
584  Vec3R pos;
585  for (size_t n = range.begin(), N = range.end(); n != N; ++n) {
586  mPositions.getPos(n, pos);
587  pos = mInverseMat.transform(pos);
588  mMin = math::minComponent(mMin, pos);
589  mMax = math::maxComponent(mMax, pos);
590  }
591  }
592 
593  void join(const CalculatePositionBounds& other) {
594  mMin = math::minComponent(mMin, other.mMin);
595  mMax = math::maxComponent(mMax, other.mMax);
596  }
597 
599  return BBoxd(mMin, mMax);
600  }
601 
602 private:
603  const PositionArrayT& mPositions;
604  const math::Mat4d& mInverseMat;
605  Vec3R mMin, mMax;
606 };
607 
608 } // namespace point_conversion_internal
609 
610 
611 ////////////////////////////////////////
612 
613 
614 template<typename CompressionT, typename PointDataGridT, typename PositionArrayT, typename PointIndexGridT>
615 inline typename PointDataGridT::Ptr
616 createPointDataGrid(const PointIndexGridT& pointIndexGrid,
617  const PositionArrayT& positions,
618  const math::Transform& xform,
619  const Metadata* positionDefaultValue)
620 {
621  using PointDataTreeT = typename PointDataGridT::TreeType;
622  using LeafT = typename PointDataTree::LeafNodeType;
623  using PointIndexLeafT = typename PointIndexGridT::TreeType::LeafNodeType;
624  using PointIndexT = typename PointIndexLeafT::ValueType;
625  using LeafManagerT = typename tree::LeafManager<PointDataTreeT>;
626  using PositionAttributeT = TypedAttributeArray<Vec3f, CompressionT>;
627 
628  const NamePair positionType = PositionAttributeT::attributeType();
629 
630  // construct the Tree using a topology copy of the PointIndexGrid
631 
632  const auto& pointIndexTree = pointIndexGrid.tree();
633  typename PointDataTreeT::Ptr treePtr(new PointDataTreeT(pointIndexTree));
634 
635  // create attribute descriptor from position type
636 
637  auto descriptor = AttributeSet::Descriptor::create(positionType);
638 
639  // add default value for position if provided
640 
641  if (positionDefaultValue) descriptor->setDefaultValue("P", *positionDefaultValue);
642 
643  // retrieve position index
644 
645  const size_t positionIndex = descriptor->find("P");
646  assert(positionIndex != AttributeSet::INVALID_POS);
647 
648  // acquire registry lock to avoid locking when appending attributes in parallel
649 
651 
652  // populate position attribute
653 
654  LeafManagerT leafManager(*treePtr);
655  leafManager.foreach(
656  [&](LeafT& leaf, size_t /*idx*/) {
657 
658  // obtain the PointIndexLeafNode (using the origin of the current leaf)
659 
660  const auto* pointIndexLeaf = pointIndexTree.probeConstLeaf(leaf.origin());
661  assert(pointIndexLeaf);
662 
663  // initialise the attribute storage
664 
665  Index pointCount(static_cast<Index>(pointIndexLeaf->indices().size()));
666  leaf.initializeAttributes(descriptor, pointCount, &lock);
667 
668  // create write handle for position
669 
670  auto attributeWriteHandle = AttributeWriteHandle<Vec3f, CompressionT>::create(
671  leaf.attributeArray(positionIndex));
672 
673  Index index = 0;
674 
675  const PointIndexT
676  *begin = static_cast<PointIndexT*>(nullptr),
677  *end = static_cast<PointIndexT*>(nullptr);
678 
679  // iterator over every active voxel in the point index leaf
680 
681  for (auto iter = pointIndexLeaf->cbeginValueOn(); iter; ++iter) {
682 
683  // find the voxel center
684 
685  const Coord& ijk = iter.getCoord();
686  const Vec3d& positionCellCenter(ijk.asVec3d());
687 
688  // obtain pointers for this voxel from begin to end in the indices array
689 
690  pointIndexLeaf->getIndices(ijk, begin, end);
691 
692  while (begin < end) {
693 
694  typename PositionArrayT::value_type positionWorldSpace;
695  positions.getPos(*begin, positionWorldSpace);
696 
697  // compute the index-space position and then subtract the voxel center
698 
699  const Vec3d positionIndexSpace = xform.worldToIndex(positionWorldSpace);
700  const Vec3f positionVoxelSpace(positionIndexSpace - positionCellCenter);
701 
702  attributeWriteHandle->set(index++, positionVoxelSpace);
703 
704  ++begin;
705  }
706  }
707  },
708  /*threaded=*/true);
709 
710  auto grid = PointDataGridT::create(treePtr);
711  grid->setTransform(xform.copy());
712  return grid;
713 }
714 
715 
716 ////////////////////////////////////////
717 
718 
719 template <typename CompressionT, typename PointDataGridT, typename ValueT>
720 inline typename PointDataGridT::Ptr
721 createPointDataGrid(const std::vector<ValueT>& positions,
722  const math::Transform& xform,
723  const Metadata* positionDefaultValue)
724 {
725  const PointAttributeVector<ValueT> pointList(positions);
726 
727  tools::PointIndexGrid::Ptr pointIndexGrid =
728  tools::createPointIndexGrid<tools::PointIndexGrid>(pointList, xform);
729  return createPointDataGrid<CompressionT, PointDataGridT>(
730  *pointIndexGrid, pointList, xform, positionDefaultValue);
731 }
732 
733 
734 ////////////////////////////////////////
735 
736 
737 template <typename PointDataTreeT, typename PointIndexTreeT, typename PointArrayT>
738 inline void
739 populateAttribute(PointDataTreeT& tree, const PointIndexTreeT& pointIndexTree,
740  const openvdb::Name& attributeName, const PointArrayT& data, const Index stride,
741  const bool insertMetadata)
742 {
744  using ValueType = typename PointArrayT::value_type;
745 
746  auto iter = tree.cbeginLeaf();
747 
748  if (!iter) return;
749 
750  const size_t index = iter->attributeSet().find(attributeName);
751 
752  if (index == AttributeSet::INVALID_POS) {
753  OPENVDB_THROW(KeyError, "Attribute not found to populate - " << attributeName << ".");
754  }
755 
756  if (insertMetadata) {
758  }
759 
760  // populate attribute
761 
762  typename tree::LeafManager<PointDataTreeT> leafManager(tree);
763 
764  PopulateAttributeOp<PointDataTreeT,
765  PointIndexTreeT,
766  PointArrayT> populate(pointIndexTree, data, index, stride);
767  tbb::parallel_for(leafManager.leafRange(), populate);
768 }
769 
770 
771 ////////////////////////////////////////
772 
773 
774 template <typename PositionAttribute, typename PointDataGridT, typename FilterT>
775 inline void
776 convertPointDataGridPosition( PositionAttribute& positionAttribute,
777  const PointDataGridT& grid,
778  const std::vector<Index64>& pointOffsets,
779  const Index64 startOffset,
780  const FilterT& filter,
781  const bool inCoreOnly)
782 {
783  using TreeType = typename PointDataGridT::TreeType;
784  using LeafManagerT = typename tree::LeafManager<const TreeType>;
785 
787 
788  const TreeType& tree = grid.tree();
789  auto iter = tree.cbeginLeaf();
790 
791  if (!iter) return;
792 
793  const size_t positionIndex = iter->attributeSet().find("P");
794 
795  positionAttribute.expand();
796  LeafManagerT leafManager(tree);
797  ConvertPointDataGridPositionOp<TreeType, PositionAttribute, FilterT> convert(
798  positionAttribute, pointOffsets, startOffset, grid.transform(), positionIndex,
799  filter, inCoreOnly);
800  tbb::parallel_for(leafManager.leafRange(), convert);
801  positionAttribute.compact();
802 }
803 
804 
805 ////////////////////////////////////////
806 
807 
808 template <typename TypedAttribute, typename PointDataTreeT, typename FilterT>
809 inline void
811  const PointDataTreeT& tree,
812  const std::vector<Index64>& pointOffsets,
813  const Index64 startOffset,
814  const unsigned arrayIndex,
815  const Index stride,
816  const FilterT& filter,
817  const bool inCoreOnly)
818 {
819  using LeafManagerT = typename tree::LeafManager<const PointDataTreeT>;
820 
822 
823  auto iter = tree.cbeginLeaf();
824 
825  if (!iter) return;
826 
827  attribute.expand();
828  LeafManagerT leafManager(tree);
829  ConvertPointDataGridAttributeOp<PointDataTreeT, TypedAttribute, FilterT> convert(
830  attribute, pointOffsets, startOffset, arrayIndex, stride,
831  filter, inCoreOnly);
832  tbb::parallel_for(leafManager.leafRange(), convert);
833  attribute.compact();
834 }
835 
836 
837 ////////////////////////////////////////
838 
839 
840 template <typename Group, typename PointDataTreeT, typename FilterT>
841 inline void
843  const PointDataTreeT& tree,
844  const std::vector<Index64>& pointOffsets,
845  const Index64 startOffset,
846  const AttributeSet::Descriptor::GroupIndex index,
847  const FilterT& filter,
848  const bool inCoreOnly)
849 {
850  using LeafManagerT= typename tree::LeafManager<const PointDataTreeT>;
851 
853 
854  auto iter = tree.cbeginLeaf();
855  if (!iter) return;
856 
857  LeafManagerT leafManager(tree);
858  ConvertPointDataGridGroupOp<PointDataTree, Group, FilterT> convert(
859  group, pointOffsets, startOffset, index,
860  filter, inCoreOnly);
861  tbb::parallel_for(leafManager.leafRange(), convert);
862 
863  // must call this after modifying point groups in parallel
864 
865  group.finalize();
866 }
867 
868 template<typename PositionWrapper, typename InterrupterT>
869 inline float
870 computeVoxelSize( const PositionWrapper& positions,
871  const uint32_t pointsPerVoxel,
872  const math::Mat4d transform,
873  const Index decimalPlaces,
874  InterrupterT* const interrupter)
875 {
876  using namespace point_conversion_internal;
877 
878  struct Local {
879 
880  static bool voxelSizeFromVolume(const double volume,
881  const size_t estimatedVoxelCount,
882  float& voxelSize)
883  {
884  // dictated by the math::ScaleMap limit
885  static const double minimumVoxelVolume(3e-15);
886  static const double maximumVoxelVolume(std::numeric_limits<float>::max());
887 
888  double voxelVolume = volume / static_cast<double>(estimatedVoxelCount);
889  bool valid = true;
890 
891  if (voxelVolume < minimumVoxelVolume) {
892  voxelVolume = minimumVoxelVolume;
893  valid = false;
894  }
895  else if (voxelVolume > maximumVoxelVolume) {
896  voxelVolume = maximumVoxelVolume;
897  valid = false;
898  }
899 
900  voxelSize = static_cast<float>(math::Pow(voxelVolume, 1.0/3.0));
901  return valid;
902  }
903 
904  static float truncate(const float voxelSize, Index decPlaces)
905  {
906  float truncatedVoxelSize = voxelSize;
907 
908  // attempt to truncate from decPlaces -> 11
909  for (int i = decPlaces; i < 11; i++) {
910  truncatedVoxelSize = static_cast<float>(math::Truncate(double(voxelSize), i));
911  if (truncatedVoxelSize != 0.0f) break;
912  }
913 
914  return truncatedVoxelSize;
915  }
916  };
917 
918  if (pointsPerVoxel == 0) OPENVDB_THROW(ValueError, "Points per voxel cannot be zero.");
919 
920  // constructed with the default voxel size as specified by openvdb interface values
921 
922  float voxelSize(0.1f);
923 
924  const size_t numPoints = positions.size();
925 
926  // return the default voxel size if we have zero or only 1 point
927 
928  if (numPoints <= 1) return voxelSize;
929 
930  size_t targetVoxelCount(numPoints / size_t(pointsPerVoxel));
931  if (targetVoxelCount == 0) targetVoxelCount++;
932 
933  // calculate the world space, transform-oriented bounding box
934 
935  math::Mat4d inverseTransform = transform.inverse();
936  inverseTransform = math::unit(inverseTransform);
937 
938  tbb::blocked_range<size_t> range(0, numPoints);
939  CalculatePositionBounds<PositionWrapper> calculateBounds(positions, inverseTransform);
940  tbb::parallel_reduce(range, calculateBounds);
941 
942  BBoxd bbox = calculateBounds.getBoundingBox();
943 
944  // return default size if points are coincident
945 
946  if (bbox.min() == bbox.max()) return voxelSize;
947 
948  double volume = bbox.volume();
949 
950  // handle points that are collinear or coplanar by expanding the volume
951 
952  if (math::isApproxZero(volume)) {
953  Vec3d extents = bbox.extents().sorted().reversed();
954  if (math::isApproxZero(extents[1])) {
955  // colinear (maxExtent^3)
956  volume = extents[0]*extents[0]*extents[0];
957  }
958  else {
959  // coplanar (maxExtent*nextMaxExtent^2)
960  volume = extents[0]*extents[1]*extents[1];
961  }
962  }
963 
964  double previousVolume = volume;
965 
966  if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) {
967  OPENVDB_LOG_DEBUG("Out of range, clamping voxel size.");
968  return voxelSize;
969  }
970 
971  size_t previousVoxelCount(0);
972  size_t voxelCount(1);
973 
974  if (interrupter) interrupter->start("Computing voxel size");
975 
976  while (voxelCount > previousVoxelCount)
977  {
978  math::Transform::Ptr newTransform;
979 
980  if (!math::isIdentity(transform))
981  {
982  // if using a custom transform, pre-scale by coefficients
983  // which define the new voxel size
984 
985  math::Mat4d matrix(transform);
986  matrix.preScale(Vec3d(voxelSize) / math::getScale(matrix));
987  newTransform = math::Transform::createLinearTransform(matrix);
988  }
989  else
990  {
991  newTransform = math::Transform::createLinearTransform(voxelSize);
992  }
993 
994  // create a mask grid of the points from the calculated voxel size
995  // this is the same function call as tools::createPointMask() which has
996  // been duplicated to provide an interrupter
997 
998  MaskGrid::Ptr mask = createGrid<MaskGrid>(false);
999  mask->setTransform(newTransform);
1000  tools::PointsToMask<MaskGrid, InterrupterT> pointMaskOp(*mask, interrupter);
1001  pointMaskOp.addPoints(positions);
1002 
1003  if (interrupter && util::wasInterrupted(interrupter)) break;
1004 
1005  previousVoxelCount = voxelCount;
1006  voxelCount = mask->activeVoxelCount();
1007  volume = math::Pow3(voxelSize) * static_cast<float>(voxelCount);
1008 
1009  // stop if no change in the volume or the volume has increased
1010 
1011  if (volume >= previousVolume) break;
1012  previousVolume = volume;
1013 
1014  const float previousVoxelSize = voxelSize;
1015 
1016  // compute the new voxel size and if invalid return the previous value
1017 
1018  if (!Local::voxelSizeFromVolume(volume, targetVoxelCount, voxelSize)) {
1019  voxelSize = previousVoxelSize;
1020  break;
1021  }
1022 
1023  // halt convergence if the voxel size has decreased by less
1024  // than 10% in this iteration
1025 
1026  if (voxelSize / previousVoxelSize > 0.9f) break;
1027  }
1028 
1029  if (interrupter) interrupter->end();
1030 
1031  // truncate the voxel size for readability and return the value
1032 
1033  return Local::truncate(voxelSize, decimalPlaces);
1034 }
1035 
1036 
1037 ////////////////////////////////////////
1038 
1039 
1040 // deprecated functions
1041 
1042 template<
1043  typename CompressionT,
1044  typename PointDataGridT,
1045  typename PositionArrayT,
1046  typename PointIndexGridT>
1048 inline typename PointDataGridT::Ptr
1049 createPointDataGrid(const PointIndexGridT& pointIndexGrid,
1050  const PositionArrayT& positions,
1051  const math::Transform& xform,
1052  Metadata::Ptr positionDefaultValue)
1053 {
1054  return createPointDataGrid<CompressionT, PointDataGridT>(
1055  pointIndexGrid, positions, xform, positionDefaultValue.get());
1056 }
1057 
1058 
1059 template <typename CompressionT, typename PointDataGridT, typename ValueT>
1061 inline typename PointDataGridT::Ptr
1062 createPointDataGrid(const std::vector<ValueT>& positions,
1063  const math::Transform& xform,
1064  Metadata::Ptr positionDefaultValue)
1065 {
1066  return createPointDataGrid<CompressionT, PointDataGridT>(
1067  positions, xform, positionDefaultValue.get());
1068 }
1069 
1070 
1071 ////////////////////////////////////////
1072 
1073 
1074 } // namespace points
1075 } // namespace OPENVDB_VERSION_NAME
1076 } // namespace openvdb
1077 
1078 #endif // OPENVDB_POINTS_POINT_CONVERSION_HAS_BEEN_INCLUDED
bool isUniform() const override
Return true if this array is stored as a single uniform value.
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:503
vint4 max(const vint4 &a, const vint4 &b)
Definition: simd.h:4703
void parallel_for(int64_t start, int64_t end, std::function< void(int64_t index)> &&task, parallel_options opt=parallel_options(0, Split_Y, 1))
Definition: parallel.h:153
GLenum GLint * range
Definition: glew.h:3500
Definition: ImfName.h:54
This tool produces a grid where every voxel that contains a point is active. It employes thread-local...
Type Truncate(Type x, unsigned int digits)
Return x truncated to the given number of decimal digits.
Definition: Math.h:863
void operator()(const typename LeafManagerT::LeafRange &range) const
Vec3d indexToWorld(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:108
Type Pow(Type x, int n)
Return xn.
Definition: Math.h:554
LeafRange leafRange(size_t grainsize=1) const
Return a TBB-compatible LeafRange.
Definition: LeafManager.h:347
FMT_CONSTEXPR auto begin(const C &c) -> decltype(c.begin())
Definition: format.h:251
GLuint GLenum GLenum transform
Definition: glew.h:14742
GLuint index
Definition: glew.h:1814
ValueType get(Index n) const
Return the value at index n.
bool isIdentity(const MatType &m)
Determine if a matrix is an identity matrix.
Definition: Mat.h:875
Point group manipulation in a VDB Point Grid.
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:167
void convert(IterT &iter, HandleT &targetHandle, SourceHandleT &sourceHandle, Index64 &offset) const
Index filters primarily designed to be used with a FilterIndexIter.
const GLdouble * m
Definition: glew.h:9124
#define OPENVDB_LOG_DEBUG(mesg)
Definition: logging.h:277
Tto convert(const Tfrom &source)
GLenum GLint GLuint mask
Definition: glew.h:1845
Makes every voxel of a grid active if it contains a point.
Definition: PointsToMask.h:67
MatType unit(const MatType &mat, typename MatType::value_type eps=1.0e-8)
Return a copy of the given matrix with its upper 3×3 rows normalized.
Definition: Mat.h:663
uint64 value_type
Definition: GA_PrimCompat.h:29
static Ptr create(AttributeArray &array, const bool expand=true)
PointDataGridT::Ptr createPointDataGrid(const PointIndexGridT &pointIndexGrid, const PositionArrayT &positions, const math::Transform &xform, const Metadata *positionDefaultValue=nullptr)
Localises points with position into a PointDataGrid into two stages: allocation of the leaf attribute...
bool isGroup(const AttributeArray &array)
CalculatePositionBounds(const PositionArrayT &positions, const math::Mat4d &inverse)
float computeVoxelSize(const PositionWrapper &positions, const uint32_t pointsPerVoxel, const math::Mat4d transform=math::Mat4d::identity(), const Index decimalPlaces=5, InterrupterT *const interrupter=nullptr)
SYS_FORCE_INLINE const_iterator end() const
void populateAttribute(PointDataTreeT &tree, const PointIndexTreeT &pointIndexTree, const openvdb::Name &attributeName, const PointArrayT &data, const Index stride=1, const bool insertMetadata=true)
Stores point attribute data in an existing PointDataGrid attribute.
ConvertPointDataGridPositionOp(Attribute &attribute, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const math::Transform &transform, const size_t index, const FilterT &filter, const bool inCoreOnly)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glew.h:2981
void OIIO_API split(string_view str, std::vector< string_view > &result, string_view sep=string_view(), int maxsplit=-1)
Base class for storing attribute data.
Vec3< T > reversed() const
Return the vector (z, y, x)
Definition: Vec3.h:458
GLclampf f
Definition: glew.h:3499
ConvertPointDataGridAttributeOp(Attribute &attribute, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const size_t index, const Index stride, const FilterT &filter, const bool inCoreOnly)
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:744
GLint GLenum GLsizei GLint GLsizei const void * data
Definition: glew.h:1379
GLuint GLuint GLsizei GLenum const void * indices
Definition: glew.h:1253
Attribute array storage for string data using Descriptor Metadata.
GLuint GLuint end
Definition: glew.h:1253
OPENVDB_API void calculateBounds(const Transform &t, const Vec3d &minWS, const Vec3d &maxWS, Vec3d &minIS, Vec3d &maxIS)
Calculate an axis-aligned bounding box in index space from an axis-aligned bounding box in world spac...
GLsizei n
Definition: glew.h:4040
This class manages a linear array of pointers to a given tree's leaf nodes, as well as optional auxil...
Definition: LeafManager.h:84
Type Pow3(Type x)
Return x3.
Definition: Math.h:545
ConvertPointDataGridGroupOp(Group &group, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const AttributeSet::Descriptor::GroupIndex index, const FilterT &filter, const bool inCoreOnly)
Vec3< T > sorted() const
Return a vector with the components of this in ascending order.
Definition: Vec3.h:448
void convert(IterT &iter, const GroupAttributeArray &groupArray, Index64 &offset) const
Index64 pointCount(const PointDataTreeT &tree, const FilterT &filter=NullFilter(), const bool inCoreOnly=false, const bool threaded=true)
Count the total number of points in a PointDataTree.
Definition: PointCount.h:88
std::pair< Name, Name > NamePair
bool isApproxZero(const Type &x)
Return true if x is equal to zero to within the default floating-point comparison tolerance...
Definition: Math.h:340
GLsizei stride
Definition: glew.h:1523
Point attribute manipulation in a VDB Point Grid.
typename RootNodeType::LeafNodeType LeafNodeType
Definition: Tree.h:183
Typed class for storing attribute data.
Set of Attribute Arrays which tracks metadata about each array.
ElementType volume() const
Return the volume enclosed by this bounding box.
Definition: BBox.h:100
Space-partitioning acceleration structure for points. Partitions the points into voxels to accelerate...
void convertPointDataGridAttribute(TypedAttribute &attribute, const PointDataTreeT &tree, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const unsigned arrayIndex, const Index stride=1, const FilterT &filter=NullFilter(), const bool inCoreOnly=false)
Convert the attribute from a PointDataGrid.
GLuint GLdouble GLdouble GLint GLint const GLdouble * points
Definition: glew.h:3446
Vec4< T0 > transform(const Vec4< T0 > &v) const
Transform a Vec4 by post-multiplication.
Definition: Mat4.h:1006
Base class for storing metadata information in a grid.
Definition: Metadata.h:23
math::BBox< Vec3d > BBoxd
Definition: Types.h:62
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:512
Vec3< typename MatType::value_type > getScale(const MatType &mat)
Return a Vec3 representing the lengths of the passed matrix's upper 3×3's rows.
Definition: Mat.h:648
static const Mat4< double > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:125
Index64 pointOffsets(std::vector< Index64 > &pointOffsets, const PointDataTreeT &tree, const FilterT &filter=NullFilter(), const bool inCoreOnly=false, const bool threaded=true)
Populate an array of cumulative point offsets per leaf node.
Definition: PointCount.h:122
GA_API const UT_StringHolder N
OIIO_API bool attribute(string_view name, TypeDesc type, const void *val)
Point-partitioner compatible STL vector attribute wrapper for convenience.
GLenum array
Definition: glew.h:9066
void convert(IterT &iter, HandleT &targetHandle, SourceHandleT &sourceHandle, Index64 &offset) const
void convertPointDataGridGroup(Group &group, const PointDataTreeT &tree, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const AttributeSet::Descriptor::GroupIndex index, const FilterT &filter=NullFilter(), const bool inCoreOnly=false)
Convert the group from a PointDataGrid.
static Transform::Ptr createLinearTransform(double voxelSize=1.0)
Create and return a shared pointer to a new transform.
Vec3d worldToIndex(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:110
bool wasInterrupted(T *i, int percent=-1)
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:113
GLsizei const GLfloat * value
Definition: glew.h:1849
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
GLuint GLenum matrix
Definition: glew.h:14742
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:493
void convertPointDataGridPosition(PositionAttribute &positionAttribute, const PointDataGridT &grid, const std::vector< Index64 > &pointOffsets, const Index64 startOffset, const FilterT &filter=NullFilter(), const bool inCoreOnly=false)
Convert the position attribute from a Point Data Grid.
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:82
PopulateAttributeOp(const PointIndexTreeType &pointIndexTree, const AttributeListType &data, const size_t index, const Index stride=1)
void addPoints(const PointListT &points, size_t grainSize=1024)
Activates the state of any voxel in the input grid that contains a point.
Definition: PointsToMask.h:123
GLboolean GLuint group
Definition: glew.h:2745
void * Handle
Definition: plugin.h:53
PointAttributeVector(const std::vector< value_type > &data, const Index stride=1)
GLintptr offset
Definition: glew.h:1682