OpenVDB  10.0.1
OpenToNanoVDB.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /*!
5  \file OpenToNanoVDB.h
6 
7  \author Ken Museth
8 
9  \date January 8, 2020
10 
11  \brief This class will serialize an OpenVDB grid into a NanoVDB grid.
12 */
13 
14 #include <openvdb/openvdb.h>
16 #include <openvdb/util/CpuTimer.h>
17 
18 #include "GridHandle.h" // manages and streams the raw memory buffer of a NanoVDB grid.
19 #include "GridChecksum.h" // for nanovdb::checksum
20 #include "GridStats.h" // for nanovdb::Extrema
21 #include "GridBuilder.h" // for nanovdb::AbsDiff
22 #include "ForEach.h"// for nanovdb::forEach
23 #include "Reduce.h"// for nanovdb::reduce
24 #include "Invoke.h"// for nanovdb::invoke
25 #include "DitherLUT.h"// for nanovdb::DitherLUT
26 
27 #include <type_traits>
28 
29 #ifndef NANOVDB_OPENTONANOVDB_H_HAS_BEEN_INCLUDED
30 #define NANOVDB_OPENTONANOVDB_H_HAS_BEEN_INCLUDED
31 
32 namespace nanovdb {
33 
34 /// @brief Converts OpenVDB types to NanoVDB types, e.g. openvdb::Vec3f to nanovdb::Vec3f
35 /// Template specializations are defined below.
36 template<typename T>
37 struct OpenToNanoType { using Type = T; };
38 
39 //================================================================================================
40 
41 /// @brief Forward declaration of free-standing function that converts an OpenVDB GridBase into a NanoVDB GridHandle
42 template<typename BufferT = HostBuffer>
47  int verbose = 0);
48 
49 //================================================================================================
50 
51 /// @brief Forward declaration of free-standing function that converts a typed OpenVDB Grid into a NanoVDB GridHandle
52 ///
53 /// @details Unlike the function above that takes a base openvdb grid, this method is strongly typed and allows
54 /// for compression, e.g. openToNanoVDB<HostBuffer, openvdb::FloatTree, nanovdb::Fp16>
55 template<typename BufferT = HostBuffer,
56  typename OpenTreeT = openvdb::FloatTree,//dummy default type - it will be resolved from the grid argument
57  typename NanoBuildT = typename OpenToNanoType<typename OpenTreeT::BuildType>::Type>
62  int verbose = 0);
63 
64 //================================================================================================
65 
66 /// @brief Template specialization for openvdb::Coord
67 template<>
69 {
71  static_assert(sizeof(Type) == sizeof(openvdb::Coord), "Mismatching sizeof");
72 };
73 
74 /// @brief Template specialization for openvdb::CoordBBox
75 template<>
77 {
79  static_assert(sizeof(Type) == sizeof(openvdb::CoordBBox), "Mismatching sizeof");
80 };
81 
82 /// @brief Template specialization for openvdb::math::BBox
83 template<typename T>
85 {
87  static_assert(sizeof(Type) == sizeof(openvdb::math::BBox<T>), "Mismatching sizeof");
88 };
89 
90 /// @brief Template specialization for openvdb::math::Vec3
91 template<typename T>
92 struct OpenToNanoType<openvdb::math::Vec3<T>>
93 {
95  static_assert(sizeof(Type) == sizeof(openvdb::math::Vec3<T>), "Mismatching sizeof");
96 };
97 
98 /// @brief Template specialization for openvdb::math::Vec4
99 template<typename T>
100 struct OpenToNanoType<openvdb::math::Vec4<T>>
101 {
103  static_assert(sizeof(Type) == sizeof(openvdb::math::Vec4<T>), "Mismatching sizeof");
104 };
105 
106 /// @brief Template specialization for openvdb::ValueMask
107 template<>
109 {
111 };
112 
113 template<>
115 {
116  using Type = uint32_t;
117 };
118 
119 template<>
121 {
122  using Type = uint32_t;
123 };
124 
125 //================================================================================================
126 
127 /// @brief Grid trait that defines OpenVDB grids with the exact same configuration as NanoVDB grids
128 template <typename BuildT>
130 {
132  using TreeT = typename GridT::TreeType;
133  using RootT = typename TreeT::RootNodeType;
134  using UpperT = typename RootT::ChildNodeType;
135  using LowerT = typename UpperT::ChildNodeType;
136  using LeafT = typename LowerT::ChildNodeType;
137  using ValueT = typename LeafT::ValueType;
138 };
139 
140 /// @brief Template specialization for the PointIndexGrid
141 template <>
143 {
145  using TreeT = typename GridT::TreeType;
146  using RootT = typename TreeT::RootNodeType;
147  using UpperT = typename RootT::ChildNodeType;
148  using LowerT = typename UpperT::ChildNodeType;
149  using LeafT = typename LowerT::ChildNodeType;
150  using ValueT = typename LeafT::ValueType;
151 };
152 
153 /// @brief Template specialization for the PointDataGrid
154 template <>
156 {
158  using TreeT = typename GridT::TreeType;
159  using RootT = typename TreeT::RootNodeType;
160  using UpperT = typename RootT::ChildNodeType;
161  using LowerT = typename UpperT::ChildNodeType;
162  using LeafT = typename LowerT::ChildNodeType;
163  using ValueT = typename LeafT::ValueType;
164 };
165 
166 //================================================================================================
167 
168 /// @brief This class will convert an OpenVDB grid into a NanoVDB grid managed by a GridHandle.
169 ///
170 /// @note Note that this converter assumes a 5,4,3 tree configuration of BOTH the OpenVDB and NanoVDB
171 /// grids. This is a consequence of the fact that the OpenVDB tree is defined in OpenGridType and
172 /// that all NanoVDB trees are by design always 5,4,3!
173 ///
174 /// @details While NanoVDB allows root, internal and leaf nodes to reside anywhere in the memory buffer
175 /// this conversion tool uses the following memory layout:
176 ///
177 ///
178 /// Grid | Tree Root... Node2... Node1... Leaf... BlindMetaData... BlindData...
179 /// where "..." means size may vary and "|" means "no gap"
180 
181 template<typename OpenBuildT,
182  typename NanoBuildT,
183  typename OracleT = AbsDiff,
184  typename BufferT = HostBuffer>
186 {
187  struct BlindMetaData; // forward declerations
188  template <typename NodeT> struct NodePair;
189  struct Codec {float min, max; uint16_t log2, size;};// used for adaptive bit-rate quantization
190 
191  using OpenGridT = typename OpenGridType<OpenBuildT>::GridT;// OpenVDB grid
192  using OpenTreeT = typename OpenGridType<OpenBuildT>::TreeT;// OpenVDB tree
193  using OpenRootT = typename OpenGridType<OpenBuildT>::RootT;// OpenVDB root node
194  using OpenUpperT= typename OpenGridType<OpenBuildT>::UpperT;// OpenVDB upper internal node
195  using OpenLowerT= typename OpenGridType<OpenBuildT>::LowerT;// OpenVDB lower internal node
196  using OpenLeafT = typename OpenGridType<OpenBuildT>::LeafT;// OpenVDB leaf node
197  using OpenValueT= typename OpenGridType<OpenBuildT>::ValueT;
198 
199  using NanoValueT= typename BuildToValueMap<NanoBuildT>::Type;// e.g. maps from Fp16 to float
206 
207  static_assert(sizeof(NanoValueT) == sizeof(OpenValueT), "Mismatching sizeof");
208  static_assert(is_same<NanoValueT, typename OpenToNanoType<OpenValueT>::Type>::value, "Mismatching ValueT");
209 
210  NanoValueT mDelta; // skip node if: node.max < -mDelta || node.min > mDelta
211  uint8_t* mBufferPtr;// pointer to the beginning of the buffer
212  uint64_t mBufferOffsets[9];//grid, tree, root, upper. lower, leafs, meta data, blind data, buffer size
213  int mVerbose;
214  std::set<BlindMetaData> mBlindMetaData; // sorted according to index
215  std::vector<NodePair<OpenLeafT >> mArray0; // leaf nodes
216  std::vector<NodePair<OpenLowerT>> mArray1; // lower internal nodes
217  std::vector<NodePair<OpenUpperT>> mArray2; // upper internal nodes
218  std::unique_ptr<Codec[]> mCodec;// defines a codec per leaf node
219  StatsMode mStats;
220  ChecksumMode mChecksum;
221  bool mDitherOn;
222  OracleT mOracle;// used for adaptive bit-rate quantization
223 
224 public:
225  /// @brief Default c-tor
226  OpenToNanoVDB();
227 
228  /// @brief return a reference to the compression oracle
229  ///
230  /// @note Note, the oracle is only used when NanoBuildT = nanovdb::FpN!
231  OracleT& oracle() { return mOracle; }
232 
233  void setVerbose(int mode = 1) { mVerbose = mode; }
234 
235  void enableDithering(bool on = true) { mDitherOn = on; }
236 
237  void setStats(StatsMode mode = StatsMode::Default) { mStats = mode; }
238 
239  void setChecksum(ChecksumMode mode = ChecksumMode::Default) { mChecksum = mode; }
240 
241  /// @brief Return a shared pointer to a NanoVDB grid handle constructed from the specified OpenVDB grid
242  GridHandle<BufferT> operator()(const OpenGridT& grid,
243  const BufferT& allocator = BufferT());
244 
245  GridHandle<BufferT> operator()(const OpenGridT& grid,
246  StatsMode sMode,
247  ChecksumMode cMode,
248  int verbose,
249  const BufferT& allocator = BufferT());
250 
251 private:
252 
253  /// @brief Allocates and return a handle for the buffer
254  GridHandle<BufferT> initHandle(const OpenGridT& openGrid, const BufferT& allocator);
255 
256  template <typename T>
257  inline typename std::enable_if<!std::is_same<T, FpN>::value>::type
258  compression(const OpenGridT&, uint64_t&) {}// no-op
259 
260  template <typename T>
261  inline typename std::enable_if<std::is_same<T, FpN>::value>::type
262  compression(const OpenGridT& openGrid, uint64_t &offset);
263 
264  /// @brief Private method to process the grid
265  NanoGridT* processGrid(const OpenGridT& openGrid);
266 
267  // @brief Private method to process the tree
268  NanoTreeT* processTree(const OpenTreeT& openTree);
269 
270  /// @brief Private method to process the root node
271  NanoRootT* processRoot(const OpenRootT& openRoot);
272 
273  template <typename T>
274  void processNodes(std::vector<NodePair<T>> &nodes);
275 
276  //////////////////////
277 
278  template<typename T>
279  typename std::enable_if<!std::is_same<typename OpenGridType<openvdb::ValueMask>::LeafT, typename T::OpenNodeT>::value &&
280  !std::is_same<typename OpenGridType<bool>::LeafT, typename T::OpenNodeT>::value &&
285  processLeafs(std::vector<T> &leafs);
286 
287  template<typename T>
291  processLeafs(std::vector<T> &leafs);
292 
293  template<typename T>
295  processLeafs(std::vector<T> &leafs);
296 
297  template<typename T>
298  typename std::enable_if<std::is_same<T, typename OpenGridType<openvdb::ValueMask>::LeafT>::value>::type
299  processLeafs(std::vector<NodePair<T>> &leafs);
300 
301  template<typename T>
302  typename std::enable_if<std::is_same<T, typename OpenGridType<bool>::LeafT>::value>::type
303  processLeafs(std::vector<NodePair<T>> &leafs);
304 
305  //////////////////////
306 
307  /// @brief Private methods to pre-process the bind metadata
308  template <typename T>
311  preProcessMetadata(const T& openGrid);
312 
313  template <typename T>
315  preProcessMetadata(const T& openGrid);
316 
317  template <typename T>
319  preProcessMetadata(const T& openGrid);
320 
321  //////////////////////
322 
323  /// @brief Private methods to process the blind metadata
324  template<typename T>
327  processMetadata(const T& openGrid);
328 
329  template<typename T>
331  processMetadata(const T& openGrid);
332 
333  template<typename T>
335  processMetadata(const T& openGrid);
336 
337  //////////////////////
338 
339  uint64_t pointCount();
340 
341  template<typename AttT, typename CodecT = openvdb::points::UnknownCodec>
342  void copyPointAttribute(size_t attIdx, AttT *attPtr);
343 
344  /// @brief Performs: nanoNode.origin = openNode.origin
345  /// openNode.origin = nanoNode offset
346  template <typename OpenNodeT, typename NanoNodeT>
347  void encode(const OpenNodeT *openNode, NanoNodeT *nanoNode);
348 
349  /// @brief Performs: nanoNode offset = openNode.origin
350  /// openNode.origin = nanoNode.origin
351  /// return nanoNode offset
352  template <typename OpenNodeT>
353  typename NanoNode<NanoBuildT, OpenNodeT::LEVEL>::Type* decode(const OpenNodeT *openNode);
354 
355 }; // OpenToNanoVDB class
356 
357 //================================================================================================
358 
359 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
361  : mVerbose(0)
362  , mStats(StatsMode::Default)
363  , mChecksum(ChecksumMode::Default)
364  , mDitherOn(false)
365  , mOracle()
366 {
367 }
368 
369 //================================================================================================
370 
371 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
372 inline GridHandle<BufferT>
374  operator()(const OpenGridT& openGrid,
375  StatsMode sMode,
376  ChecksumMode cMode,
377  int verbose,
378  const BufferT& allocator)
379 {
380  this->setStats(sMode);
381  this->setChecksum(cMode);
382  this->setVerbose(verbose);
383  return (*this)(openGrid, allocator);
384 }
385 
386 //================================================================================================
387 
388 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
389 inline GridHandle<BufferT>
391  operator()(const OpenGridT& openGrid,
392  const BufferT& allocator)
393 {
394  //mVerbose = 2;
395  std::unique_ptr<openvdb::util::CpuTimer> timer(mVerbose > 1 ? new openvdb::util::CpuTimer() : nullptr);
396 
397  if (timer) timer->start("Allocating memory for the NanoVDB buffer");
398  auto handle = this->initHandle(openGrid, allocator);
399  if (timer) timer->stop();
400 
401  if (timer) timer->start("Processing leaf nodes");
402  this->processLeafs(mArray0);
403  if (timer) timer->stop();
404 
405  if (timer) timer->start("Processing lower internal nodes");
406  this->processNodes(mArray1);
407  if (timer) timer->stop();
408 
409  if (timer) timer->start("Processing upper internal nodes");
410  this->processNodes(mArray2);
411  if (timer) timer->stop();
412 
413  if (timer) timer->start("Processing grid, tree and root node");
414  NanoGridT *nanoGrid = this->processGrid(openGrid);
415  if (timer) timer->stop();
416 
417  // Point grids already make use of min/max so they shouldn't be re-computed
420  if (mStats > StatsMode::BBox) mStats = StatsMode::BBox;
421  }
422 
423  if (timer) timer->start("GridStats");
424  gridStats(*nanoGrid, mStats);
425  if (timer) timer->stop();
426 
427  if (timer) timer->start("Checksum");
428  updateChecksum(*nanoGrid, mChecksum);
429  if (timer) timer->stop();
430 
431  return handle; // invokes move constructor
432 } // OpenToNanoVDB::operator()
433 
434 //================================================================================================
435 
436 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
437 template <typename T>
438 inline typename std::enable_if<std::is_same<T, FpN>::value>::type
440  compression(const OpenGridT& openGrid, uint64_t &offset)
441 {
442  static_assert(is_same<float, OpenBuildT>::value, "compression: expected OpenBuildT == float");
443  static_assert(is_same<FpN, NanoBuildT>::value, "compression: expected NanoBuildT == FpN");
444  if (is_same<AbsDiff, OracleT>::value && mOracle.getTolerance() < 0.0f) {// default tolerance for level set and fog volumes
445  if (openGrid.getGridClass() == openvdb::GRID_LEVEL_SET) {
446  mOracle.setTolerance(0.1f * float(openGrid.voxelSize()[0]));// range of ls: [-3dx; 3dx]
447  } else if (openGrid.getGridClass() == openvdb::GRID_FOG_VOLUME) {
448  mOracle.setTolerance(0.01f);// range of FOG volumes: [0;1]
449  } else {
450  mOracle.setTolerance(0.0f);
451  }
452  }
453 
454  const size_t size = mArray0.size();
455  mCodec.reset(new Codec[size]);
456 
457  DitherLUT lut(mDitherOn);
458  auto kernel = [&](const auto &r) {
459  const OracleT oracle = mOracle;// local copy since it's very lightweight
460  for (auto i=r.begin(); i!=r.end(); ++i) {
461  const float *data = mArray0[i].node->buffer().data();
463  for (int j=0; j<512; ++j) {
464  float v = data[j];
465  if (v<min) min=v;
466  if (v>max) max=v;
467  }
468  mCodec[i].min = min;
469  mCodec[i].max = max;
470  const float range = max - min;
471  uint16_t logBitWidth = 0;// 0,1,2,3,4 => 1,2,4,8,16 bits
472  while (range > 0.0f && logBitWidth < 4u) {
473  const uint32_t mask = (uint32_t(1) << (uint32_t(1) << logBitWidth)) - 1u;
474  const float encode = mask/range;
475  const float decode = range/mask;
476  int j = 0;
477  do {
478  const float exact = data[j];// exact value
479  const uint32_t code = uint32_t(encode*(exact - min) + lut(j));
480  const float approx = code * decode + min;// approximate value
481  j += oracle(exact, approx) ? 1 : 513;
482  } while(j < 512);
483  if (j == 512) break;
484  ++logBitWidth;
485  }
486  mCodec[i].log2 = logBitWidth;
487  mCodec[i].size = NanoLeafT::DataType::memUsage(1u<<logBitWidth);
488  }
489  };// kernel
490  forEach(0, size, 4, kernel);
491 
492  if (mVerbose) {
493  uint32_t counters[5+1] = {0};
494  ++counters[mCodec[0].log2];
495  for (size_t i=1; i<size; ++i) {
496  ++counters[mCodec[i].log2];
497  mArray0[i].offset = mArray0[i-1].offset + mCodec[i-1].size;
498  }
499  std::cout << "\n" << mOracle << std::endl;
500  std::cout << "Dithering: " << (mDitherOn ? "enabled" : "disabled") << std::endl;
501  float avg = 0.0f;
502  for (uint32_t i=0; i<=5; ++i) {
503  if (uint32_t n = counters[i]) {
504  avg += n * float(1 << i);
505  printf("%2i bits: %6u leaf nodes, i.e. %4.1f%%\n",1<<i, n, 100.0f*n/float(size));
506  }
507  }
508  printf("%4.1f bits per value on average\n", avg/float(size));
509  } else {
510  for (size_t i=1; i<size; ++i) {
511  mArray0[i].offset = mArray0[i-1].offset + mCodec[i-1].size;
512  }
513  }
514  offset = mArray0[size-1].offset + mCodec[size-1].size;
515 }// OpenToNanoVDB::compression
516 
517 //================================================================================================
518 
519 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
521  initHandle(const OpenGridT& openGrid, const BufferT& buffer)
522 {
523  auto &openTree = openGrid.tree();
524  auto &openRoot = openTree.root();
525 
526  mArray0.clear();
527  mArray1.clear();
528  mArray2.clear();
529  std::vector<uint32_t> nodeCount = openTree.nodeCount();
530  mArray0.reserve(nodeCount[0]);
531  mArray1.reserve(nodeCount[1]);
532  mArray2.reserve(nodeCount[2]);
533 
534  uint64_t offset[3] = {0};
535  for (auto it2 = openRoot.cbeginChildOn(); it2; ++it2) {
536  mArray2.emplace_back(&(*it2), offset[2]);
537  offset[2] += NanoUpperT::memUsage();
538  for (auto it1 = it2->cbeginChildOn(); it1; ++it1) {
539  mArray1.emplace_back(&(*it1), offset[1]);
540  offset[1] += NanoLowerT::memUsage();
541  for (auto it0 = it1->cbeginChildOn(); it0; ++it0) {
542  mArray0.emplace_back(&(*it0), offset[0]);
543  offset[0] += sizeof(NanoLeafT);
544  }
545  }
546  }
547 
548  this->template compression<NanoBuildT>(openGrid, offset[0]);
549 
550  this->preProcessMetadata(openGrid);
551 
552  mBufferOffsets[0] = 0;// grid is always placed at the beginning of the buffer!
553  mBufferOffsets[1] = NanoGridT::memUsage(); // grid ends and tree begins
554  mBufferOffsets[2] = NanoTreeT::memUsage(); // tree ends and root begins
555  mBufferOffsets[3] = NanoRootT::memUsage(openTree.root().getTableSize()); // root ends and upper internal nodes begins
556  mBufferOffsets[4] = offset[2];// upper ends and lower internal nodes
557  mBufferOffsets[5] = offset[1];// lower ends and leaf nodes begins
558  mBufferOffsets[6] = offset[0];// leafs end blind meta data begins
559  mBufferOffsets[7] = GridBlindMetaData::memUsage(mBlindMetaData.size()); // meta ends and blind data begins
560  mBufferOffsets[8] = 0;// blind data
561  for (auto& i : mBlindMetaData) mBufferOffsets[8] += i.size; // blind data
562 
563  // Compute the prefixed sum
564  for (int i = 2; i < 9; ++i) {
565  mBufferOffsets[i] += mBufferOffsets[i - 1];
566  }
567 
568 #if 0
569  std::cerr << "grid starts at " << mBufferOffsets[0] <<" byte" << std::endl;
570  std::cerr << "tree starts at " << mBufferOffsets[1] <<" byte" << std::endl;
571  std::cerr << "root starts at " << mBufferOffsets[2] <<" byte" << std::endl;
572  std::cerr << "node starts at " << mBufferOffsets[3] <<" byte" << " #" << mArray2.size() << std::endl;
573  std::cerr << "node starts at " << mBufferOffsets[4] <<" byte" << " #" << mArray1.size() << std::endl;
574  std::cerr << "leaf starts at " << mBufferOffsets[5] <<" byte" << " #" << mArray0.size() << std::endl;
575  std::cerr << "meta starts at " << mBufferOffsets[6] <<" byte" << std::endl;
576  std::cerr << "data starts at " << mBufferOffsets[7] <<" byte" << std::endl;
577  std::cerr << "buffer ends at " << mBufferOffsets[8] <<" byte" << std::endl;
578  std::cerr << "creating buffer of size " << (mBufferOffsets[8]>>20) << "MB" << std::endl;
579 #endif
580 
581  GridHandle<BufferT> handle(BufferT::create(mBufferOffsets[8], &buffer));
582  mBufferPtr = handle.data();
583 
584  //openvdb::util::CpuTimer timer("zero buffer");
585 #if 1
586  //std::memset(mBufferPtr, '8', mBufferOffsets[8]);
587 #else
588  forEach(0,mBufferOffsets[8],1024*1024,[&](const Range1D &r){
589  //for (uint64_t *p = reinterpret_cast<uint64_t*>(mBufferPtr)+r.begin(), *q=p+r.size(); p!=q; ++p) *p=0;
590  std::memset(mBufferPtr+r.begin(), '8', r.size());
591  });
592  //uint8_t *begin = (mBufferPtr >> 3) << 3;
593  //std::memset((mBufferPtr >> 3) << 3, 0, mBufferPtr - p);
594  //forEach(0,mBufferOffsets[8],10*1024*1024,[&](const Range1D &r){std::memset(mBufferPtr+r.begin(), 0, r.size());});
595 #endif
596  //timer.stop();
597 
598  if (mVerbose) {
599  openvdb::util::printBytes(std::cout, mBufferOffsets[8], "Allocated", " for the NanoVDB grid\n");
600  }
601  return handle;// is converted to r-value so return value is move constructed!
602 }// OpenToNanoVDB::initHandle
603 
604 //================================================================================================
605 
606 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
608  processGrid(const OpenGridT& openGrid)
609 {
610  auto *nanoGrid = reinterpret_cast<NanoGridT*>(mBufferPtr + mBufferOffsets[0]);
611  if (!openGrid.transform().baseMap()->isLinear()) {
612  OPENVDB_THROW(openvdb::ValueError, "processGrid: OpenToNanoVDB only supports grids with affine transforms");
613  }
614  auto affineMap = openGrid.transform().baseMap()->getAffineMap();
615  const std::string gridName = openGrid.getName();
616  auto *data = nanoGrid->data();
617 
618  data->mMagic = NANOVDB_MAGIC_NUMBER;//8B
619  data->mChecksum = 0u;// 8B
620  data->mVersion = Version();//4B
621  data->mFlags = static_cast<uint32_t>(GridFlags::IsBreadthFirst);//4B
622  data->mGridIndex = 0;//4B
623  data->mGridCount = 1;//4B
624  data->mGridSize = mBufferOffsets[8];//8B
625  std::memset(data->mGridName, '\0', GridData::MaxNameSize);// 256B overwrite mGridName
626  strncpy(data->mGridName, gridName.c_str(), GridData::MaxNameSize-1);
627  data->mWorldBBox = BBox<Vec3R>();
628  data->mBlindMetadataOffset = 0;
629  data->mBlindMetadataCount = 0;
630 
631  if (gridName.length() >= GridData::MaxNameSize) {
632  data->setLongGridNameOn();// grid name is long so store it as blind data
633  }
634  mDelta = NanoValueT(0); // dummy value
635  switch (openGrid.getGridClass()) { // set grid class
638  OPENVDB_THROW(openvdb::ValueError, "processGrid: Level sets are expected to be floating point types");
639  data->mGridClass = GridClass::LevelSet;
640  mDelta = NanoValueT(openGrid.voxelSize()[0]); // skip a node if max < -mDelta || min > mDelta
641  break;
643  data->mGridClass = GridClass::FogVolume;
644  break;
646  data->mGridClass = GridClass::Staggered;
647  break;
648  default:
649  data->mGridClass = GridClass::Unknown;
650  }
651 
652  // mapping from the OpenVDB build type to the NanoVDB build type and GridType enum
653  if (std::is_same<NanoBuildT, float>::value) { // resolved at compile time
654  data->mGridType = GridType::Float;
656  data->mGridType = GridType::Double;
658  data->mGridType = GridType::Int16;
660  data->mGridType = GridType::Int32;
662  data->mGridType = GridType::Int64;
664  data->mGridType = GridType::Vec3f;
666  data->mGridType = GridType::UInt32;
668  data->mGridType = GridType::UInt32;
669  data->mGridClass = GridClass::PointIndex;
671  data->mGridType = GridType::UInt32;
672  data->mGridClass = GridClass::PointData;
674  data->mGridType = GridType::Mask;
675  data->mGridClass = GridClass::Topology;
677  data->mGridType = GridType::Boolean;
679  data->mGridType = GridType::Fp4;
681  data->mGridType = GridType::Fp8;
683  data->mGridType = GridType::Fp16;
685  data->mGridType = GridType::FpN;
687  data->mGridType = GridType::Vec4f;
689  data->mGridType = GridType::Vec4d;
690  } else {
691  OPENVDB_THROW(openvdb::ValueError, "processGrid: Unsupported value type");
692  }
693  { // set affine map
694  if (openGrid.hasUniformVoxels()) {
695  data->mVoxelSize = nanovdb::Vec3R(affineMap->voxelSize()[0]);
696  } else {
697  data->mVoxelSize = affineMap->voxelSize();
698  }
699  const auto mat = affineMap->getMat4();
700  // Only support non-tapered at the moment:
701  data->mMap.set(mat, mat.inverse(), 1.0);
702  }
703  data->mData0 = 0u;
704  data->mData1 = 0u;
705  data->mData2 = 0u;
706 
707  this->processTree(openGrid.tree());// calls processRoot
708 
709  if (auto size = mBlindMetaData.size()) {
710  auto *metaData = this->processMetadata(openGrid);
711  data->mBlindMetadataOffset = PtrDiff(metaData, nanoGrid);
712  data->mBlindMetadataCount = static_cast<uint32_t>(size);
713  auto *blindData = reinterpret_cast<char*>(mBufferPtr + mBufferOffsets[7]);
714  metaData->setBlindData(blindData);
715  }
716 
717  return nanoGrid;
718 }// OpenToNanoVDB::processGrid
719 
720 //================================================================================================
721 
722 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
724  processTree(const OpenTreeT& openTree)
725 {
726  auto *nanoTree = reinterpret_cast<NanoTreeT*>(mBufferPtr + mBufferOffsets[1]);
727  auto *data = nanoTree->data();
728 
729  data->setRoot( this->processRoot( openTree.root()) );
730 
731  NanoUpperT *nanoUpper = mArray2.empty() ? nullptr : reinterpret_cast<NanoUpperT*>(mBufferPtr + mBufferOffsets[3]);
732  data->setFirstNode(nanoUpper);
733 
734  NanoLowerT *nanoLower = mArray1.empty() ? nullptr : reinterpret_cast<NanoLowerT*>(mBufferPtr + mBufferOffsets[4]);
735  data->setFirstNode(nanoLower);
736 
737  NanoLeafT *nanoLeaf = mArray0.empty() ? nullptr : reinterpret_cast<NanoLeafT*>(mBufferPtr + mBufferOffsets[5]);
738  data->setFirstNode(nanoLeaf);
739 
740  data->mNodeCount[0] = static_cast<uint32_t>(mArray0.size());
741  data->mNodeCount[1] = static_cast<uint32_t>(mArray1.size());
742  data->mNodeCount[2] = static_cast<uint32_t>(mArray2.size());
743 
744 #if 1// count active tiles and voxels
745 
746  // Count number of active tiles in the lower internal nodes
747  data->mTileCount[0] = reduce(mArray1, uint32_t(0), [&](auto &r, uint32_t sum){
748  for (auto i=r.begin(); i!=r.end(); ++i) sum += mArray1[i].node->getValueMask().countOn();
749  return sum;}, std::plus<uint32_t>());
750 
751  // Count number of active tiles in the upper internal nodes
752  data->mTileCount[1] = reduce(mArray2, uint32_t(0), [&](auto &r, uint32_t sum){
753  for (auto i=r.begin(); i!=r.end(); ++i) sum += mArray2[i].node->getValueMask().countOn();
754  return sum;}, std::plus<uint32_t>());
755 
756  // Count number of active tile in the root node
757  uint32_t sum = 0;
758  for (auto it = openTree.root().cbeginValueOn(); it; ++it) ++sum;
759  data->mTileCount[2] = sum;
760 
761  data->mVoxelCount = reduce(mArray0, uint64_t(0), [&](auto &r, uint64_t sum){
762  for (auto i=r.begin(); i!=r.end(); ++i) sum += mArray0[i].node->valueMask().countOn();
763  return sum;}, std::plus<uint64_t>());
764 
765  data->mVoxelCount += data->mTileCount[0]*NanoLeafT::NUM_VALUES;
766  data->mVoxelCount += data->mTileCount[1]*NanoLowerT::NUM_VALUES;
767  data->mVoxelCount += data->mTileCount[2]*NanoUpperT::NUM_VALUES;
768 
769 #else
770 
771  data->mTileCount[0] = 0;
772  data->mTileCount[1] = 0;
773  data->mTileCount[2] = 0;
774  data->mVoxelCount = 0;
775 
776 #endif
777 
778  return nanoTree;
779 }// OpenToNanoVDB::processTree
780 
781 //================================================================================================
782 
783 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
785  processRoot(const OpenRootT& openRoot)
786 {
787  auto *nanoRoot = reinterpret_cast<NanoRootT*>(mBufferPtr + mBufferOffsets[2]);
788  auto* data = nanoRoot->data();
789  if (data->padding()>0) {
790  //std::cout << "Root has padding\n";
791  std::memset(data, 0, NanoRootT::memUsage(openRoot.getTableSize()));
792  } else {
793  data->mTableSize = 0;// incremented below
794  }
795  data->mBackground = openRoot.background();
796  data->mMinimum = data->mMaximum = data->mBackground;
797  data->mBBox.min() = openvdb::Coord::max(); // set to an empty bounding box
798  data->mBBox.max() = openvdb::Coord::min();
799 
800  OpenValueT value = openvdb::zeroVal<OpenValueT>();// to avoid compiler warning
801  for (auto iter = openRoot.cbeginChildAll(); iter; ++iter) {
802  auto* tile = data->tile(data->mTableSize++);
803  if (const OpenUpperT *openChild = iter.probeChild( value )) {
804  tile->setChild(iter.getCoord(), this->decode(openChild), data);
805  } else {
806  tile->setValue(iter.getCoord(), iter.isValueOn(), value);
807  }
808  }
809  return nanoRoot;
810 } // OpenToNanoVDB::processRoot
811 
812 //================================================================================================
813 
814 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
815 template<typename OpenNodeT>
817  processNodes(std::vector<NodePair<OpenNodeT>>& openNodes)
818 {
819  using NanoNodeT = typename NanoNode<NanoBuildT, OpenNodeT::LEVEL>::Type;
820  //if (NanoNodeT::DataType::padding()>0u) std::cerr << "OpenToNanoVDB: internal node has padding\n";
821  static_assert(NanoNodeT::LEVEL == 1 || NanoNodeT::LEVEL == 2, "Expected internal node");
822  auto kernel = [&](const Range1D& r) {
823  uint8_t* ptr = mBufferPtr + mBufferOffsets[5 - NanoNodeT::LEVEL];// 3 or 4
824  OpenValueT value = openvdb::zeroVal<OpenValueT>();// to avoid compiler warning
825  for (auto i = r.begin(); i != r.end(); ++i) {
826  auto *openNode = openNodes[i].node;
827  auto *nanoNode = PtrAdd<NanoNodeT>(ptr, openNodes[i].offset);
828  auto* data = nanoNode->data();
829  if (NanoNodeT::DataType::padding()>0u) std::memset(data, 0, NanoNodeT::DataType::memUsage());
830  this->encode(openNode, nanoNode);// sets data->mBBoxMin
831  data->mValueMask = openNode->getValueMask(); // copy value mask
832  data->mChildMask = openNode->getChildMask(); // copy child mask
833  for (auto iter = openNode->cbeginChildAll(); iter; ++iter) {
834  if (const auto *openChild = iter.probeChild(value)) {
835  data->setChild(iter.pos(), this->decode(openChild));
836  } else {
837  data->setValue(iter.pos(), value);
838  }
839  }
840  }
841  };
842  forEach(openNodes, 1, kernel);
843 } // OpenToNanoVDB::processNodes
844 
845 //================================================================================================
846 
847 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
848 template<typename T>
849 inline typename std::enable_if<!std::is_same<typename OpenGridType<openvdb::ValueMask>::LeafT, typename T::OpenNodeT>::value &&
850  !std::is_same<typename OpenGridType<bool>::LeafT, typename T::OpenNodeT>::value &&
856 {
857  //if (NanoLeafT::DataType::padding()>0u) std::cerr << "OpenToNanoVDB: leaf has padding\n";
858  auto kernel = [&](const auto& r) {
859  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
860  for (auto i = r.begin(); i != r.end(); ++i) {
861  auto *openLeaf = openLeafs[i].node;
862  auto *nanoLeaf = PtrAdd<NanoLeafT>(ptr, openLeafs[i].offset);
863  auto* data = nanoLeaf->data();
864  if (NanoLeafT::DataType::padding()>0u) {// resolved at compile time
865  std::memset(data, 0, NanoLeafT::DataType::memUsage());
866  } else {
867  data->mFlags = data->mBBoxDif[2] = data->mBBoxDif[1] = data->mBBoxDif[0] = 0u;
868  data->mMaximum = data->mMinimum = typename NanoLeafT::DataType::ValueType(0);
869  data->mStdDevi = data->mAverage = typename NanoLeafT::DataType::FloatType(0);
870  }
871  this->encode(openLeaf, nanoLeaf);// sets data->mBBoxMin
872  data->mValueMask = openLeaf->valueMask(); // copy value mask
873  auto *src = reinterpret_cast<const NanoValueT*>(openLeaf->buffer().data());
874  for (NanoValueT *dst = data->mValues, *end = dst + OpenLeafT::size(); dst != end; dst += 4, src += 4) {
875  dst[0] = src[0]; // copy *all* voxel values in sets of four, i.e. loop-unrolling
876  dst[1] = src[1];
877  dst[2] = src[2];
878  dst[3] = src[3];
879  }
880  }
881  };
882  forEach(openLeafs, 8, kernel);
883 } // OpenToNanoVDB::processLeafs<T>
884 
885 //================================================================================================
886 
887 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
888 template<typename T>
893 {
894  static_assert(NanoLeafT::DataType::padding()==0u, "Expected no padding in LeafNode<Fp{4,8,16}>");
895  using ArrayT = typename NanoLeafT::DataType::ArrayType;
896  using FloatT = typename std::conditional<NanoLeafT::DataType::bitWidth()>=16, double, float>::type;// 16 compression and higher requires double
897  DitherLUT lut(mDitherOn);
898 
899  auto kernel = [&](const auto& r) {
900  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
901  for (auto i = r.begin(); i != r.end(); ++i) {
902  auto *openLeaf = openLeafs[i].node;
903  auto *nanoLeaf = PtrAdd<NanoLeafT>(ptr, openLeafs[i].offset);
904  auto* data = nanoLeaf->data();
905  data->mFlags = data->mBBoxDif[2] = data->mBBoxDif[1] = data->mBBoxDif[0] = 0u;
906  data->mDev = data->mAvg = data->mMax = data->mMin = 0u;
907  this->encode(openLeaf, nanoLeaf);// sets data->mBBoxMin
908  data->mValueMask = openLeaf->valueMask(); // copy value mask
909  auto *src = reinterpret_cast<const float*>(openLeaf->buffer().data());
910  // compute extrema values
912  for (int i=0; i<512; ++i) {
913  const float v = src[i];
914  if (v < min) min = v;
915  if (v > max) max = v;
916  }
917  data->init(min, max, NanoLeafT::DataType::bitWidth());// sets mMinimum and mQuantum
918  // perform quantization relative to the values in the current leaf node
919  const FloatT encode = FloatT((1 << NanoLeafT::DataType::bitWidth()) - 1)/(max-min);
920  auto *code = reinterpret_cast<ArrayT*>(data->mCode);
921  int offset = 0;
922  if (std::is_same<Fp4, NanoBuildT>::value) {// resolved at compile-time
923  for (int i=0; i<128; ++i) {
924  auto tmp = ArrayT(encode * (*src++ - min) + lut(offset++));
925  *code++ = ArrayT(encode * (*src++ - min) + lut(offset++)) << 4 | tmp;
926  tmp = ArrayT(encode * (*src++ - min) + lut(offset++));
927  *code++ = ArrayT(encode * (*src++ - min) + lut(offset++)) << 4 | tmp;
928  }
929  } else {
930  for (int i=0; i<128; ++i) {
931  *code++ = ArrayT(encode * (*src++ - min) + lut(offset++));
932  *code++ = ArrayT(encode * (*src++ - min) + lut(offset++));
933  *code++ = ArrayT(encode * (*src++ - min) + lut(offset++));
934  *code++ = ArrayT(encode * (*src++ - min) + lut(offset++));
935  }
936  }
937  }
938  };
939  forEach(openLeafs, 8, kernel);
940 } // OpenToNanoVDB::processLeafs<Fp4, Fp8, Fp16>
941 
942 //================================================================================================
943 
944 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
945 template<typename T>
948 {
949  static_assert(is_same<float, OpenBuildT>::value, "Expected OpenBuildT == float");
950  static_assert(NanoLeafT::DataType::padding()==0u, "Expected no padding in LeafNode<FpN>");
951  DitherLUT lut(mDitherOn);
952  auto kernel = [&](const auto& r) {
953  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
954  for (auto i = r.begin(); i != r.end(); ++i) {
955  const uint8_t logBitWidth = uint8_t(mCodec[i].log2);
956  auto *openLeaf = openLeafs[i].node;
957  auto *nanoLeaf = PtrAdd<NanoLeafT>(ptr, openLeafs[i].offset);
958  auto* data = nanoLeaf->data();
959  data->mBBoxDif[2] = data->mBBoxDif[1] = data->mBBoxDif[0] = 0u;
960  data->mDev = data->mAvg = data->mMax = data->mMin = 0u;
961  this->encode(openLeaf, nanoLeaf);// sets data->mBBoxMin
962  data->mFlags = logBitWidth << 5;// pack logBitWidth into 3 MSB of mFlag
963  data->mValueMask = openLeaf->valueMask(); // copy value mask
964  auto *src = reinterpret_cast<const float*>(openLeaf->buffer().data());
965  const float min = mCodec[i].min, max = mCodec[i].max;
966  data->init(min, max, uint8_t(1) << logBitWidth);// sets mMinimum and mQuantum
967  // perform quantization relative to the values in the current leaf node
968  int offset = 0;
969  switch (logBitWidth) {
970  case 0u: {// 1 bit
971  auto *dst = reinterpret_cast<uint8_t*>(data+1);
972  const float encode = 1.0f/(max - min);
973  for (int j=0; j<64; ++j) {
974  uint8_t a = 0;
975  for (int k=0; k<8; ++k) {
976  a |= uint8_t(encode * (*src++ - min) + lut(offset++)) << k;
977  }
978  *dst++ = a;
979  }
980  }
981  break;
982  case 1u: {// 2 bits
983  auto *dst = reinterpret_cast<uint8_t*>(data+1);
984  const float encode = 3.0f/(max - min);
985  for (int j=0; j<128; ++j) {
986  auto a = uint8_t(encode * (*src++ - min) + lut(offset++));
987  a |= uint8_t(encode * (*src++ - min) + lut(offset++)) << 2;
988  a |= uint8_t(encode * (*src++ - min) + lut(offset++)) << 4;
989  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++)) << 6 | a;
990  }
991  }
992  break;
993  case 2u: {// 4 bits
994  auto *dst = reinterpret_cast<uint8_t*>(data+1);
995  const float encode = 15.0f/(max - min);
996  for (int j=0; j<128; ++j) {
997  auto a = uint8_t(encode * (*src++ - min) + lut(offset++));
998  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++)) << 4 | a;
999  a = uint8_t(encode * (*src++ - min) + lut(offset++));
1000  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++)) << 4 | a;
1001  }
1002  }
1003  break;
1004  case 3u: {// 8 bits
1005  auto *dst = reinterpret_cast<uint8_t*>(data+1);
1006  const float encode = 255.0f/(max - min);
1007  for (int j=0; j<128; ++j) {
1008  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++));
1009  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++));
1010  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++));
1011  *dst++ = uint8_t(encode * (*src++ - min) + lut(offset++));
1012  }
1013  }
1014  break;
1015  default: {// 16 bits
1016  auto *dst = reinterpret_cast<uint16_t*>(data+1);
1017  const double encode = 65535.0/(max - min);// note that double is required!
1018  for (int j=0; j<128; ++j) {
1019  *dst++ = uint16_t(encode * (*src++ - min) + lut(offset++));
1020  *dst++ = uint16_t(encode * (*src++ - min) + lut(offset++));
1021  *dst++ = uint16_t(encode * (*src++ - min) + lut(offset++));
1022  *dst++ = uint16_t(encode * (*src++ - min) + lut(offset++));
1023  }
1024  }
1025  }// end switch
1026  }
1027  };// kernel
1028  forEach(openLeafs, 8, kernel);
1029 } // OpenToNanoVDB::processLeafs<FpN>
1030 
1031 //================================================================================================
1032 
1033 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1034 template<typename T>
1035 inline typename std::enable_if<std::is_same<T, typename OpenGridType<bool>::LeafT>::value>::type
1037 {
1038  static_assert(NanoLeafT::DataType::padding()==0u, "Expected no padding in LeafNode<bool>");
1039  auto kernel = [&](const auto& r) {
1040  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
1041  for (auto i = r.begin(); i != r.end(); ++i) {
1042  auto *openLeaf = openLeafs[i].node;
1043  auto *nanoLeaf = PtrAdd<NanoLeafT>(ptr, openLeafs[i].offset);
1044  this->encode(openLeaf, nanoLeaf);// sets data->mBBoxMin
1045  auto* data = nanoLeaf->data();
1046  data->mFlags = data->mBBoxDif[2] = data->mBBoxDif[1] = data->mBBoxDif[0] = 0u;
1047  data->mValueMask = openLeaf->valueMask(); // copy value mask
1048  data->mValues = *reinterpret_cast<const nanovdb::Mask<3>*>(openLeaf->buffer().data()); // copy values
1049  data->mPadding[1] = data->mPadding[0] = 0u;
1050  }
1051  };
1052  forEach(openLeafs, 8, kernel);
1053 } // OpenToNanoVDB::processLeafs<bool>
1054 
1055 //================================================================================================
1056 
1057 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1058 template<typename T>
1059 inline typename std::enable_if<std::is_same<T, typename OpenGridType<openvdb::ValueMask>::LeafT>::value>::type
1061 {
1062  static_assert(NanoLeafT::DataType::padding()==0u, "Expected no padding in LeafNode<ValueMask>");
1063  auto kernel = [&](const auto& r) {
1064  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
1065  for (auto i = r.begin(); i != r.end(); ++i) {
1066  auto *openLeaf = openLeafs[i].node;
1067  auto *nanoLeaf = PtrAdd<NanoLeafT>(ptr, openLeafs[i].offset);
1068  this->encode(openLeaf, nanoLeaf);
1069  auto* data = nanoLeaf->data();
1070  data->mFlags = data->mBBoxDif[2] = data->mBBoxDif[1] = data->mBBoxDif[0] = 0u;
1071  data->mValueMask = openLeaf->valueMask(); // copy value mask
1072  data->mPadding[1] = data->mPadding[0] = 0u;
1073  }
1074  };
1075  forEach(openLeafs, 8, kernel);
1076 } // OpenToNanoVDB::processLeafs<ValueMask>
1077 
1078 //================================================================================================
1079 
1080 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1082 {
1083  return reduce(mArray0, uint64_t(0), [&](auto &r, uint64_t sum) {
1084  for (auto i=r.begin(); i!=r.end(); ++i) sum += mArray0[i].node->getLastValue();
1085  return sum;}, std::plus<uint64_t>());
1086 }// OpenToNanoVDB::pointCount
1087 
1088 //================================================================================================
1089 
1090 /// @brief Performs: nanoNode.origin = openNode.origin
1091 /// openNode.origin = nanoNode offset
1092 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1093 template <typename OpenNodeT, typename NanoNodeT>
1095 encode(const OpenNodeT *openNode, NanoNodeT *nanoNode)
1096 {
1097  static_assert(is_same<NanoNodeT, typename NanoNode<NanoBuildT, OpenNodeT::LEVEL>::Type>::value, "Type mismatch");
1098  openvdb::Coord &ijk = const_cast<openvdb::Coord&>(openNode->origin());
1099  nanoNode->data()->setOrigin(ijk);
1100  reinterpret_cast<int64_t&>(ijk) = PtrDiff(nanoNode, mBufferPtr);
1101 }// OpenToNanoVDB::encode
1102 
1103 //================================================================================================
1104 
1105 /// @brief Performs: nanoNode offset = openNode.origin
1106 /// openNode.origin = nanoNode.origin
1107 /// return nanoNode offset
1108 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1109 template <typename OpenNodeT>
1111 decode(const OpenNodeT *openNode)
1112 {
1113  using NanoNodeT = typename NanoNode<NanoBuildT, OpenNodeT::LEVEL>::Type;
1114  openvdb::Coord &ijk = const_cast<openvdb::Coord&>(openNode->origin());
1115  NanoNodeT *nanoNode = PtrAdd<NanoNodeT>(mBufferPtr, reinterpret_cast<int64_t&>(ijk));
1116  Coord tmp = nanoNode->origin();
1117  ijk[0] = tmp[0];
1118  ijk[1] = tmp[1];
1119  ijk[2] = tmp[2];
1120  return nanoNode;
1121 }// OpenToNanoVDB::decode
1122 
1123 //================================================================================================
1124 
1125 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1126 template <typename NodeT>
1127 struct OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::NodePair {
1128  using OpenNodeT = NodeT;
1129  using NanoNodeT = typename NanoNode<NanoBuildT, OpenNodeT::LEVEL>::Type;
1130  NodePair(const NodeT *ptr, size_t n) : node(ptr), offset(n) {}
1131  const NodeT *node;// pointer to OpenVDB node
1132  uint64_t offset;// byte offset to matching NanoVDB node, relative to the first
1133 };// OpenToNanoVDB::NodePair
1134 
1135 //================================================================================================
1136 
1137 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1138 struct OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::BlindMetaData
1139 {
1140  BlindMetaData(const std::string& n, const std::string& t, size_t i, size_t c, size_t s)
1141  : name(n)
1142  , typeName(t)
1143  , index(i)
1144  , count(c)
1145  , size(AlignUp<NANOVDB_DATA_ALIGNMENT>(c * s))
1146  {
1147  }
1148  const std::string name, typeName;
1149  const size_t index, count, size;
1150  bool operator<(const BlindMetaData& other) const { return index < other.index; } // required by std::set
1151 }; // OpenToNanoVDB::BlindMetaData
1152 
1153 //================================================================================================
1154 
1155 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1156 template <typename T>
1160 {
1161  mBlindMetaData.clear();
1162  const size_t length = openGrid.getName().length();
1163  if (length >= GridData::MaxNameSize) {
1164  mBlindMetaData.emplace("grid name", "uint8_t", 0, 1, length + 1);// Null-terminated byte strings
1165  }
1166 }// OpenToNanoVDB::preProcessMetadata<T>
1167 
1168 //================================================================================================
1169 
1170 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1171 template <typename T>
1174 {
1175  mBlindMetaData.clear();
1176  if (const uint64_t pointCount = this->pointCount()) {
1177  mBlindMetaData.emplace("index", "uint32_t", 0, pointCount, sizeof(uint32_t));
1178  }
1179  const size_t length = openGrid.getName().length();
1180  if (length >= GridData::MaxNameSize) {
1181  mBlindMetaData.emplace("grid name", "uint8_t", mBlindMetaData.size(), 1, length + 1);// Null-terminated byte strings
1182  }
1183 }// OpenToNanoVDB::preProcessMetadata<PointIndexGrid>
1184 
1185 //================================================================================================
1186 
1187 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1188 template <typename T>
1191 {
1192  mBlindMetaData.clear();
1193  size_t counter = 0;
1194  if (const uint64_t pointCount = this->pointCount()) {
1195  auto *openLeaf = openGrid.tree().cbeginLeaf().getLeaf();
1196  const auto& attributeSet = openLeaf->attributeSet();
1197  const auto& descriptor = attributeSet.descriptor();
1198  const auto& nameMap = descriptor.map();
1199  for (auto it = nameMap.begin(); it != nameMap.end(); ++it) {
1200  const size_t index = it->second;
1201  auto& attArray = openLeaf->constAttributeArray(index);
1202  mBlindMetaData.emplace(it->first, descriptor.valueType(index), index, pointCount, attArray.valueTypeSize());
1203  }
1204  counter += nameMap.size();
1205  }
1206  const size_t length = openGrid.getName().length();
1207  if (length >= GridData::MaxNameSize) {
1208  mBlindMetaData.emplace("grid name", "uint8_t", counter, 1, length + 1);// Null-terminated byte strings
1209  }
1210 }// OpenToNanoVDB::preProcessMetadata<PointDataGrid>
1211 
1212 //================================================================================================
1213 
1214 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1215 template<typename T>
1219  processMetadata(const T& openGrid)
1220 {
1221  if (mBlindMetaData.empty()) {
1222  return nullptr;
1223  }
1224  assert(mBlindMetaData.size() == 1);// only the grid name is expected
1225  auto it = mBlindMetaData.cbegin();
1226  assert(it->name == "grid name" && it->typeName == "uint8_t" && it->index == 0);
1227  assert(openGrid.getName().length() >= GridData::MaxNameSize);
1228  auto *metaData = reinterpret_cast<GridBlindMetaData*>(mBufferPtr + mBufferOffsets[6]);
1229  auto *blindData = reinterpret_cast<char*>(mBufferPtr + mBufferOffsets[7]);
1230  // write the blind meta data
1231  metaData->setBlindData(blindData);
1232  metaData->mElementCount = it->count;
1233  metaData->mFlags = 0;
1234  metaData->mSemantic = GridBlindDataSemantic::Unknown;
1235  metaData->mDataClass = GridBlindDataClass::GridName;
1236  metaData->mDataType = GridType::Unknown;
1237  // write the actual bind data
1238  strcpy(blindData, openGrid.getName().c_str());
1239  return metaData;
1240 }// OpenToNanoVDB::processMetadata<T>
1241 
1242 //================================================================================================
1243 
1244 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1245 template<typename T>
1248 {
1249  if (mBlindMetaData.empty()) {
1250  return nullptr;
1251  }
1252  assert(mBlindMetaData.size() == 1 || mBlindMetaData.size() == 2);// point index and maybe long grid name
1253  auto *metaData = reinterpret_cast<GridBlindMetaData*>(mBufferPtr + mBufferOffsets[6]);
1254  auto *blindData = reinterpret_cast<char*>(mBufferPtr + mBufferOffsets[7]);
1255 
1256  auto it = mBlindMetaData.cbegin();
1257  const uint32_t leafCount = static_cast<uint32_t>(mArray0.size());
1258 
1259  using LeafDataT = typename NanoLeafT::DataType;
1260  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
1261 
1262  auto *data0 = reinterpret_cast<LeafDataT*>(ptr + mArray0[0].offset);
1263  data0->mMinimum = 0; // start of prefix sum
1264  data0->mMaximum = data0->mValues[NanoLeafT::SIZE - 1u];
1265  for (uint32_t i = 1; i < leafCount; ++i) {
1266  auto *data1 = reinterpret_cast<LeafDataT*>(ptr + mArray0[i].offset);
1267  data1->mMinimum = data0->mMinimum + data0->mMaximum;
1268  data1->mMaximum = data1->mValues[NanoLeafT::SIZE - 1u];
1269  data0 = data1;
1270  }
1271 
1272  // write blind meta data for the point offsets
1273  assert(it->count == data0->mMinimum + data0->mMaximum);
1274  assert(it->name == "index" && it->typeName == "uint32_t" && it->index == 0);
1275  metaData[0].setBlindData( blindData );
1276  metaData[0].mElementCount = it->count;
1277  metaData[0].mFlags = 0;
1278  metaData[0].mSemantic = GridBlindDataSemantic::Unknown;
1279  metaData[0].mDataClass = GridBlindDataClass::IndexArray;
1280  metaData[0].mDataType = GridType::UInt32;
1281  if (it->name.length() >= GridBlindMetaData::MaxNameSize) {
1282  std::stringstream ss;
1283  ss << "Point attribute name \"" << it->name << "\" is more than " << (GridBlindMetaData::MaxNameSize-1) << " characters";
1285  }
1286  std::memset(metaData[0].mName, '\0', GridBlindMetaData::MaxNameSize);//overwrite mName
1287  memcpy(metaData[0].mName, it->name.c_str(), it->name.size() + 1);
1288 
1289  // write point offsets as blind data
1290  forEach(mArray0, 16, [&](const auto& r) {
1291  for (auto i = r.begin(); i != r.end(); ++i) {
1292  auto *data = reinterpret_cast<LeafDataT*>(ptr + mArray0[i].offset);
1293  uint32_t* p = reinterpret_cast<uint32_t*>(blindData) + data->mMinimum;
1294  for (uint32_t idx : mArray0[i].node->indices()) *p++ = idx;
1295  }
1296  });
1297  blindData += it->size;// add point offsets
1298 
1299  // write long grid name if it exists
1300  ++it;
1301  if (it != mBlindMetaData.end()) {
1302  assert(it->name == "grid name" && it->typeName == "uint8_t" && it->index == 1);
1303  assert(openGrid.getName().length() >= GridData::MaxNameSize);
1304  metaData[1].setBlindData( blindData );
1305  metaData[1].mElementCount = it->count;
1306  metaData[1].mFlags = 0;
1307  metaData[1].mSemantic = GridBlindDataSemantic::Unknown;
1308  metaData[1].mDataClass = GridBlindDataClass::GridName;
1309  metaData[1].mDataType = GridType::Unknown;
1310  strcpy(blindData, openGrid.getName().c_str());
1311  }
1312  return metaData;
1313 }// OpenToNanoVDB::processMetadata<PointIndex32>
1314 
1315 //================================================================================================
1316 
1317 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1318 template<typename T>
1321 {
1322  if (mBlindMetaData.empty()) {
1323  return nullptr;
1324  }
1325 
1326  auto *metaData = reinterpret_cast<GridBlindMetaData*>(mBufferPtr + mBufferOffsets[6]);
1327  auto *blindData = reinterpret_cast<char*>(mBufferPtr + mBufferOffsets[7]);
1328 
1329  const uint32_t leafCount = static_cast<uint32_t>(mArray0.size());
1330 
1331  using LeafDataT = typename NanoLeafT::DataType;
1332  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
1333 
1334  auto *data0 = reinterpret_cast<LeafDataT*>(ptr + mArray0[0].offset);
1335  data0->mMinimum = 0; // start of prefix sum
1336  data0->mMaximum = data0->mValues[NanoLeafT::SIZE - 1u];
1337  for (uint32_t i = 1; i < leafCount; ++i) {
1338  auto *data1 = reinterpret_cast<LeafDataT*>(ptr + mArray0[i].offset);
1339  data1->mMinimum = data0->mMinimum + data0->mMaximum;
1340  data1->mMaximum = data1->mValues[NanoLeafT::SIZE - 1u];
1341  data0 = data1;
1342  }
1343 
1344  size_t i=0;
1345  for (auto it = mBlindMetaData.cbegin(); it != mBlindMetaData.end(); ++it, ++i) {
1346  metaData[i].setBlindData( blindData );
1347  metaData[i].mElementCount = it->count;
1348  metaData[i].mFlags = 0;
1349  if (it->name == "grid name") {
1350  metaData[i].mSemantic = GridBlindDataSemantic::Unknown;
1351  metaData[i].mDataClass = GridBlindDataClass::GridName;
1352  metaData[i].mDataType = GridType::Unknown;
1353  assert(openGrid.getName().length() >= GridData::MaxNameSize);
1354  strcpy((char*)blindData, openGrid.getName().c_str());
1355  } else {
1356  assert(it->count == data0->mMinimum + data0->mMaximum);
1357  metaData[i].mDataClass = GridBlindDataClass::AttributeArray;
1358  if (it->name.length()>= GridBlindMetaData::MaxNameSize) {
1359  std::stringstream ss;
1360  ss << "Point attribute name \"" << it->name << "\" is more than " << (GridBlindMetaData::MaxNameSize-1) << " characters";
1362  }
1363 
1364  std::memset(metaData[i].mName, '\0', GridBlindMetaData::MaxNameSize);//overwrite mName
1365  memcpy(metaData[i].mName, it->name.c_str(), it->name.size() + 1);
1366  if (it->typeName == "vec3s") {
1367  metaData[i].mDataType = GridType::Vec3f;
1368  this->copyPointAttribute(it->index, (openvdb::Vec3f*)blindData);
1369  if (it->name == "P") {
1370  metaData[i].mSemantic = GridBlindDataSemantic::PointPosition;
1371  } else if (it->name == "V") {
1372  metaData[i].mSemantic = GridBlindDataSemantic::PointVelocity;
1373  } else if (it->name == "Cd") {
1374  metaData[i].mSemantic = GridBlindDataSemantic::PointColor;
1375  } else if (it->name == "N") {
1376  metaData[i].mSemantic = GridBlindDataSemantic::PointNormal;
1377  } else {
1378  metaData[i].mSemantic = GridBlindDataSemantic::Unknown;
1379  }
1380  } else if (it->typeName == "int32") {
1381  metaData[i].mDataType = GridType::Int32;
1382  this->copyPointAttribute(it->index, (int32_t*)blindData);
1383  if (it->name == "id") {
1384  metaData[i].mSemantic = GridBlindDataSemantic::PointId;
1385  } else {
1386  metaData[i].mSemantic = GridBlindDataSemantic::Unknown;
1387  }
1388  } else if (it->typeName == "int64") {
1389  metaData[i].mDataType = GridType::Int64;
1390  this->copyPointAttribute(it->index, (int64_t*)blindData);
1391  if (it->name == "id") {
1392  metaData[i].mSemantic = GridBlindDataSemantic::PointId;
1393  } else {
1394  metaData[i].mSemantic = GridBlindDataSemantic::Unknown;
1395  }
1396  } else if (it->typeName == "float") {
1397  metaData[i].mDataType = GridType::Float;
1398  metaData[i].mSemantic = GridBlindDataSemantic::Unknown;
1399  this->copyPointAttribute(it->index, (float*)blindData);
1400  } else {
1401  std::stringstream ss;
1402  ss << "Unsupported point attribute type: \"" << it->typeName << "\"";
1404  }
1405  }
1406  blindData += it->size;
1407  } // loop over bind data
1408  return metaData;
1409 }// OpenToNanoVDB::processMetadata<PointDataIndex32>
1410 
1411 //================================================================================================
1412 
1413 
1414 template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1415 template<typename AttT, typename CodecT>
1417  copyPointAttribute(size_t attIdx, AttT *attPtr)
1418 {
1419  static_assert(std::is_same<typename OpenLeafT::ValueType, openvdb::PointDataIndex32>::value, "Expected value to openvdb::PointData");
1420  using LeafDataT = typename NanoLeafT::DataType;
1421  using HandleT = openvdb::points::AttributeHandle<AttT, CodecT>;
1422  forEach(mArray0, 16, [&](const auto& r) {
1423  uint8_t* ptr = mBufferPtr + mBufferOffsets[5];
1424  for (auto i = r.begin(); i != r.end(); ++i) {
1425  auto* openLeaf = mArray0[i].node;
1426  auto *nanoData = reinterpret_cast<LeafDataT*>(ptr + mArray0[i].offset);
1427  HandleT handle(openLeaf->constAttributeArray(attIdx));
1428  AttT* p = attPtr + nanoData->mMinimum;
1429  for (auto iter = openLeaf->beginIndexOn(); iter; ++iter) {
1430  *p++ = handle.get(*iter);
1431  }
1432  }
1433  });
1434 }// OpenToNanoVDB::copyPointAttribute
1435 
1436 //================================================================================================
1437 
1438 template<typename BufferT, typename OpenTreeT, typename NanoBuildT>
1441  StatsMode sMode,
1442  ChecksumMode cMode,
1443  int verbose)
1444 {
1445  using OpenBuildT = typename OpenTreeT::BuildType;
1447  return s(grid, sMode, cMode, verbose);
1448 }// openToNanoVDB
1449 
1450 //================================================================================================
1451 
1452 template<typename BufferT>
1455  StatsMode sMode,
1456  ChecksumMode cMode,
1457  int verbose)
1458 {
1459  // We need to define these types because they are not defined in OpenVDB
1460  using openvdb_Vec4fTree = typename openvdb::tree::Tree4<openvdb::Vec4f, 5, 4, 3>::Type;
1461  using openvdb_Vec4dTree = typename openvdb::tree::Tree4<openvdb::Vec4d, 5, 4, 3>::Type;
1462  using openvdb_Vec4fGrid = openvdb::Grid<openvdb_Vec4fTree>;
1463  using openvdb_Vec4dGrid = openvdb::Grid<openvdb_Vec4dTree>;
1464 
1465  if (auto grid = openvdb::GridBase::grid<openvdb::FloatGrid>(base)) {
1466  return openToNanoVDB<BufferT, openvdb::FloatTree>(*grid, sMode, cMode, verbose);
1467  } else if (auto grid = openvdb::GridBase::grid<openvdb::DoubleGrid>(base)) {
1468  return openToNanoVDB<BufferT, openvdb::DoubleTree>(*grid, sMode, cMode, verbose);
1469  } else if (auto grid = openvdb::GridBase::grid<openvdb::Int32Grid>(base)) {
1470  return openToNanoVDB<BufferT, openvdb::Int32Tree>(*grid, sMode, cMode, verbose);
1471  } else if (auto grid = openvdb::GridBase::grid<openvdb::Int64Grid>(base)) {
1472  return openToNanoVDB<BufferT, openvdb::Int64Tree>(*grid, sMode, cMode, verbose);
1473  } else if (auto grid = openvdb::GridBase::grid<openvdb::Grid<openvdb::UInt32Tree>>(base)) {
1474  return openToNanoVDB<BufferT, openvdb::UInt32Tree>(*grid, sMode, cMode, verbose);
1475  } else if (auto grid = openvdb::GridBase::grid<openvdb::Vec3fGrid>(base)) {
1476  return openToNanoVDB<BufferT, openvdb::Vec3fTree>(*grid, sMode, cMode, verbose);
1477  } else if (auto grid = openvdb::GridBase::grid<openvdb::Vec3dGrid>(base)) {
1478  return openToNanoVDB<BufferT, openvdb::Vec3dTree>(*grid, sMode, cMode, verbose);
1479  } else if (auto grid = openvdb::GridBase::grid<openvdb::tools::PointIndexGrid>(base)) {
1480  return openToNanoVDB<BufferT, openvdb::tools::PointIndexTree>(*grid, sMode, cMode, verbose);
1481  } else if (auto grid = openvdb::GridBase::grid<openvdb::points::PointDataGrid>(base)) {
1482  return openToNanoVDB<BufferT, openvdb::points::PointDataTree>(*grid, sMode, cMode, verbose);
1483  } else if (auto grid = openvdb::GridBase::grid<openvdb::MaskGrid>(base)) {
1484  return openToNanoVDB<BufferT, openvdb::MaskTree>(*grid, sMode, cMode, verbose);
1485  } else if (auto grid = openvdb::GridBase::grid<openvdb::BoolGrid>(base)) {
1486  return openToNanoVDB<BufferT, openvdb::BoolTree>(*grid, sMode, cMode, verbose);
1487  } else if (auto grid = openvdb::GridBase::grid<openvdb_Vec4fGrid>(base)) {
1488  return openToNanoVDB<BufferT, openvdb_Vec4fTree>(*grid, sMode, cMode, verbose);
1489  } else if (auto grid = openvdb::GridBase::grid<openvdb_Vec4dGrid>(base)) {
1490  return openToNanoVDB<BufferT, openvdb_Vec4dTree>(*grid, sMode, cMode, verbose);
1491  } else {
1492  OPENVDB_THROW(openvdb::RuntimeError, "Unrecognized OpenVDB grid type");
1493  }
1494 }// openToNanoVDB
1495 
1496 } // namespace nanovdb
1497 
1498 #endif // NANOVDB_OPENTONANOVDB_H_HAS_BEEN_INCLUDED
typename FloatTraits< BuildT >::FloatType FloatType
Definition: NanoVDB.h:3815
typename UpperT::ChildNodeType LowerT
Definition: OpenToNanoVDB.h:161
typename TreeT::RootNodeType RootT
Definition: OpenToNanoVDB.h:159
uint32_t Type
Definition: OpenToNanoVDB.h:122
static GridType::Ptr grid(const GridBase::Ptr &)
Return the result of downcasting a GridBase pointer to a Grid pointer of the specified type...
Definition: Grid.h:1189
void setRoot(const RootT *root)
Definition: NanoVDB.h:2765
VDB Tree, which is a thin wrapper around a RootNode.
Definition: NanoVDB.h:2798
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:106
Highest level of the data structure. Contains a tree and a world->index transform (that currently onl...
Definition: NanoVDB.h:2555
DataType * data()
Definition: NanoVDB.h:3193
const size_t size
Definition: OpenToNanoVDB.h:1149
A unified wrapper for tbb::parallel_invoke and a naive std::thread analog.
Definition: Types.h:418
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
This class will convert an OpenVDB grid into a NanoVDB grid managed by a GridHandle.
Definition: OpenToNanoVDB.h:185
PointIndex< Index32, 0 > PointIndex32
Definition: Types.h:178
A unified wrapper for tbb::parallel_for and a naive std::thread fallback.
T Type
Definition: OpenToNanoVDB.h:37
ChecksumMode
List of different modes for computing for a checksum.
Definition: GridChecksum.h:33
Defines two classes, a GridRegister the defines the value type (e.g. Double, Float etc) of a NanoVDB ...
uint64_t memUsage() const
Return the actual memory footprint of this root node.
Definition: NanoVDB.h:3228
SharedPtr< GridBase > Ptr
Definition: Grid.h:80
Trait to map from LEVEL to node type.
Definition: NanoVDB.h:4567
bool operator<(const BlindMetaData &other) const
Definition: OpenToNanoVDB.h:1150
void setStats(StatsMode mode=StatsMode::Default)
Definition: OpenToNanoVDB.h:237
A simple vector class with three double components, similar to openvdb::math::Vec4.
Definition: NanoVDB.h:1302
BuildT ValueType
Definition: NanoVDB.h:3813
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:110
typename TreeT::RootNodeType RootT
Definition: OpenToNanoVDB.h:133
static const int MaxNameSize
Definition: NanoVDB.h:2316
LeafData< BuildT, CoordT, MaskT, Log2Dim > DataType
Definition: NanoVDB.h:4261
typename LowerT::ChildNodeType LeafT
Definition: OpenToNanoVDB.h:136
typename RootT::ChildNodeType UpperT
Definition: OpenToNanoVDB.h:147
static uint64_t memUsage()
Definition: NanoVDB.h:3838
Computes a pair of 32bit checksums, og a Grid, by means of Cyclic Redundancy Check (CRC) ...
Bit-compacted representation of all three version numbers.
Definition: NanoVDB.h:647
DataType * data()
Definition: NanoVDB.h:2825
Definition: Types.h:417
typename TreeT::RootNodeType RootT
Definition: OpenToNanoVDB.h:146
static uint64_t memUsage()
return memory usage in bytes for the class
Definition: NanoVDB.h:2830
uint64_t AlignUp(uint64_t byteCount)
round up byteSize to the nearest wordSize, e.g. to align to machine word: AlignUp<sizeof(size_t)(n) ...
Definition: NanoVDB.h:954
void enableDithering(bool on=true)
Definition: OpenToNanoVDB.h:235
typename UpperT::ChildNodeType LowerT
Definition: OpenToNanoVDB.h:135
A unified wrapper for tbb::parallel_reduce and a naive std::future analog.
void forEach(RangeT range, const FuncT &func)
simple wrapper for tbb::parallel_for with a naive std fallback
Definition: ForEach.h:40
Index64 memUsage(const TreeT &tree, bool threaded=true)
Return the total amount of memory in bytes occupied by this tree.
Definition: Count.h:493
tree::Tree4< float, 5, 4, 3 >::Type FloatTree
Definition: openvdb.h:55
Definition: Exceptions.h:65
#define NANOVDB_DATA_ALIGNMENT
Definition: NanoVDB.h:137
const std::string typeName
Definition: OpenToNanoVDB.h:1148
This class serves to manage a raw memory buffer of a NanoVDB Grid.
Definition: GridHandle.h:70
OracleT & oracle()
return a reference to the compression oracle
Definition: OpenToNanoVDB.h:231
typename LeafT::ValueType ValueT
Definition: OpenToNanoVDB.h:137
OpenToNanoVDB()
Default c-tor.
Definition: OpenToNanoVDB.h:360
typename RootT::ChildNodeType UpperT
Definition: OpenToNanoVDB.h:160
Definition: NanoVDB.h:208
typename GridT::TreeType TreeT
Definition: OpenToNanoVDB.h:145
Bit-mask to encode active states and facilitate sequential iterators and a fast codec for I/O compres...
Definition: NanoVDB.h:1956
BBox< Coord > CoordBBox
Definition: NanoVDB.h:1809
typename LowerT::ChildNodeType LeafT
Definition: OpenToNanoVDB.h:149
typename GridT::TreeType TreeT
Definition: OpenToNanoVDB.h:158
A simple vector class with three double components, similar to openvdb::math::Vec3.
Definition: NanoVDB.h:963
Top-most node of the VDB tree structure.
Definition: NanoVDB.h:3073
typename LeafT::ValueType ValueT
Definition: OpenToNanoVDB.h:150
ValueType mMinimum
Definition: NanoVDB.h:3824
GridHandle< BufferT > operator()(const OpenGridT &grid, const BufferT &allocator=BufferT())
Return a shared pointer to a NanoVDB grid handle constructed from the specified OpenVDB grid...
Definition: OpenToNanoVDB.h:391
Maps one type (e.g. the build types above) to other (actual) types.
Definition: NanoVDB.h:440
T reduce(RangeT range, const T &identity, const FuncT &func, const JoinT &join)
Definition: Reduce.h:42
static size_t memUsage()
Return memory usage in bytes for the class.
Definition: NanoVDB.h:3605
#define NANOVDB_MAGIC_NUMBER
Definition: NanoVDB.h:121
_TreeType TreeType
Definition: Grid.h:576
Grid< PointIndexTree > PointIndexGrid
Point index grid.
Definition: PointIndexGrid.h:60
Definition: NanoVDB.h:2314
Definition: Exceptions.h:63
BuildT ArrayType
Definition: NanoVDB.h:3816
BlindMetaData(const std::string &n, const std::string &t, size_t i, size_t c, size_t s)
Definition: OpenToNanoVDB.h:1140
Generates a NanoVDB grid from any volume or function.
void setVerbose(int mode=1)
Definition: OpenToNanoVDB.h:233
static constexpr uint32_t padding()
Return padding of this class in bytes, due to aliasing and 32B alignment.
Definition: NanoVDB.h:3833
Definition: Mat.h:165
typename GridT::TreeType TreeT
Definition: OpenToNanoVDB.h:132
Defines look up table to do dithering of 8^3 leaf nodes.
Definition: DitherLUT.h:19
Definition: Exceptions.h:13
Vec3< double > Vec3R
Definition: NanoVDB.h:1287
Definition: Range.h:28
ValueT value
Definition: GridBuilder.h:1290
typename RootT::ChildNodeType UpperT
Definition: OpenToNanoVDB.h:134
void updateChecksum(NanoGrid< ValueT > &grid, ChecksumMode mode=ChecksumMode::Default)
Updates the checksum of a grid.
Definition: GridChecksum.h:277
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
This is a buffer that contains a shared or private pool to either externally or internally managed ho...
Definition: HostBuffer.h:114
void gridStats(NanoGrid< BuildT > &grid, StatsMode mode=StatsMode::Default)
Re-computes the min/max, stats and bbox information for an existing NanoVDB Grid. ...
Definition: GridStats.h:713
Compression oracle based on absolute difference.
Definition: GridBuilder.h:38
Dummy type for a voxel whose value equals its binary active state.
Definition: NanoVDB.h:216
typename LowerT::ChildNodeType LeafT
Definition: OpenToNanoVDB.h:162
OPENVDB_API int printBytes(std::ostream &os, uint64_t bytes, const std::string &head="", const std::string &tail="\n", bool exact=false, int width=8, int precision=3)
static constexpr uint32_t SIZE
Definition: NanoVDB.h:4329
Definition: OpenToNanoVDB.h:1138
Codec
Optional compression codecs.
Definition: IO.h:61
void setBlindData(void *ptr)
Definition: NanoVDB.h:2331
Internal nodes of a VDB treedim(),.
Definition: NanoVDB.h:3520
const size_t index
Definition: OpenToNanoVDB.h:1149
StatsMode
Grid flags which indicate what extra information is present in the grid buffer.
Definition: GridStats.h:32
GridHandle< BufferT > openToNanoVDB(const openvdb::GridBase::Ptr &base, StatsMode sMode=StatsMode::Default, ChecksumMode cMode=ChecksumMode::Default, int verbose=0)
Forward declaration of free-standing function that converts an OpenVDB GridBase into a NanoVDB GridHa...
Definition: OpenToNanoVDB.h:1454
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
static const int MaxNameSize
Definition: NanoVDB.h:2433
static uint64_t memUsage()
Return memory usage in bytes for this class only.
Definition: NanoVDB.h:2580
static int64_t PtrDiff(const T1 *p, const T2 *q)
Definition: NanoVDB.h:535
Container class that associates a tree with a transform and metadata.
Definition: Grid.h:28
typename LeafT::ValueType ValueT
Definition: OpenToNanoVDB.h:163
Grid< PointDataTree > PointDataGrid
Point data grid.
Definition: PointDataGrid.h:194
static constexpr uint64_t NUM_VALUES
Definition: NanoVDB.h:4332
typename UpperT::ChildNodeType LowerT
Definition: OpenToNanoVDB.h:148
Converts OpenVDB types to NanoVDB types, e.g. openvdb::Vec3f to nanovdb::Vec3f Template specializatio...
Definition: OpenToNanoVDB.h:37
uint32_t Type
Definition: OpenToNanoVDB.h:116
Definition: Types.h:416
openvdb::points::PointDataGrid GridT
Definition: OpenToNanoVDB.h:157
Leaf nodes of the VDB tree. (defaults to 8x8x8 = 512 voxels)
Definition: NanoVDB.h:4251
Signed (i, j, k) 32-bit integer coordinate class, similar to openvdb::math::Coord.
Definition: NanoVDB.h:966
void setChecksum(ChecksumMode mode=ChecksumMode::Default)
Definition: OpenToNanoVDB.h:239
C++11 implementation of std::is_floating_point.
Definition: NanoVDB.h:413
static constexpr uint64_t NUM_VALUES
Definition: NanoVDB.h:3542
PointIndex< Index32, 1 > PointDataIndex32
Definition: Types.h:181
static uint64_t memUsage(uint64_t blindDataCount=0)
return memory usage in bytes for the class (note this computes for all blindMetaData structures...
Definition: NanoVDB.h:2326
C++11 implementation of std::is_same.
Definition: NanoVDB.h:356
Grid trait that defines OpenVDB grids with the exact same configuration as NanoVDB grids...
Definition: OpenToNanoVDB.h:129
Re-computes min/max/avg/var/bbox information for each node in a pre-existing NanoVDB grid...
openvdb::tools::PointIndexGrid GridT
Definition: OpenToNanoVDB.h:144