OpenVDB: nanovdb/util/OpenToNanoVDB.h Source File

OpenVDB 10.1.0

Loading...
Searching...
No Matches
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>
15#include <openvdb/points/PointDataGrid.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
32namespace nanovdb {
33
34/// @brief Converts OpenVDB types to NanoVDB types, e.g. openvdb::Vec3f to nanovdb::Vec3f
35/// Template specializations are defined below.
36template<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
42template<typename BufferT = HostBuffer>
43 GridHandle<BufferT>
44 openToNanoVDB(const openvdb::GridBase::Ptr& base,
45 StatsMode sMode = StatsMode::Default,
46 ChecksumMode cMode = ChecksumMode::Default,
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>
55template<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>
58 GridHandle<BufferT>
59 openToNanoVDB(const openvdb::Grid<OpenTreeT>& grid,
60 StatsMode sMode = StatsMode::Default,
61 ChecksumMode cMode = ChecksumMode::Default,
62 int verbose = 0);
63
64//================================================================================================
65
66/// @brief Template specialization for openvdb::Coord
67template<>
68 struct OpenToNanoType<openvdb::Coord>
69{
70 using Type = nanovdb::Coord;
71 static_assert(sizeof(Type) == sizeof(openvdb::Coord), "Mismatching sizeof");
72};
73
74/// @brief Template specialization for openvdb::CoordBBox
75template<>
76 struct OpenToNanoType<openvdb::CoordBBox>
77{
78 using Type = nanovdb::CoordBBox;
79 static_assert(sizeof(Type) == sizeof(openvdb::CoordBBox), "Mismatching sizeof");
80};
81
82/// @brief Template specialization for openvdb::math::BBox
83template<typename T>
84 struct OpenToNanoType<openvdb::math::BBox<T>>
85{
86 using Type = nanovdb::BBox<T>;
87 static_assert(sizeof(Type) == sizeof(openvdb::math::BBox<T>), "Mismatching sizeof");
88};
89
90/// @brief Template specialization for openvdb::math::Vec3
91template<typename T>
92 struct OpenToNanoType<openvdb::math::Vec3<T>>
93{
94 using Type = nanovdb::Vec3<T>;
95 static_assert(sizeof(Type) == sizeof(openvdb::math::Vec3<T>), "Mismatching sizeof");
96};
97
98/// @brief Template specialization for openvdb::math::Vec4
99template<typename T>
100 struct OpenToNanoType<openvdb::math::Vec4<T>>
101{
102 using Type = nanovdb::Vec4<T>;
103 static_assert(sizeof(Type) == sizeof(openvdb::math::Vec4<T>), "Mismatching sizeof");
104};
105
106/// @brief Template specialization for openvdb::ValueMask
107template<>
108 struct OpenToNanoType<openvdb::ValueMask>
109{
110 using Type = nanovdb::ValueMask;
111};
112
113template<>
114 struct OpenToNanoType<openvdb::PointIndex32>
115{
116 using Type = uint32_t;
117};
118
119template<>
120 struct OpenToNanoType<openvdb::PointDataIndex32>
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
128template <typename BuildT>
129 struct OpenGridType
130{
131 using GridT = openvdb::Grid<typename openvdb::tree::Tree4<BuildT, 5, 4, 3>::Type>;
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
141template <>
142 struct OpenGridType<openvdb::PointIndex32>
143{
144 using GridT = openvdb::tools::PointIndexGrid;// 5, 4, 3
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
154template <>
155 struct OpenGridType<openvdb::PointDataIndex32>
156{
157 using GridT = openvdb::points::PointDataGrid;// 5, 4, 3
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
181template<typename OpenBuildT,
182 typename NanoBuildT,
183 typename OracleT = AbsDiff,
184 typename BufferT = HostBuffer>
185 class OpenToNanoVDB
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
200 using NanoLeafT = NanoLeaf<NanoBuildT>;
201 using NanoLowerT= NanoLower<NanoBuildT>;
202 using NanoUpperT= NanoUpper<NanoBuildT>;
203 using NanoRootT = NanoRoot<NanoBuildT>;
204 using NanoTreeT = NanoTree<NanoBuildT>;
205 using NanoGridT = NanoGrid<NanoBuildT>;
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
224public:
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
251private:
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 &&
281 !std::is_same<Fp4, typename T::NanoNodeT::BuildType>::value &&
282 !std::is_same<Fp8, typename T::NanoNodeT::BuildType>::value &&
283 !std::is_same<Fp16,typename T::NanoNodeT::BuildType>::value &&
284 !std::is_same<FpN, typename T::NanoNodeT::BuildType>::value>::type
285 processLeafs(std::vector<T> &leafs);
286
287 template<typename T>
288 typename std::enable_if<std::is_same<Fp4, typename T::NanoNodeT::BuildType>::value ||
289 std::is_same<Fp8, typename T::NanoNodeT::BuildType>::value ||
290 std::is_same<Fp16, typename T::NanoNodeT::BuildType>::value>::type
291 processLeafs(std::vector<T> &leafs);
292
293 template<typename T>
294 typename std::enable_if<std::is_same<FpN, typename T::NanoNodeT::BuildType>::value>::type
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>
309 typename std::enable_if<!std::is_same<T, openvdb::tools::PointIndexGrid>::value &&
310 !std::is_same<T, openvdb::points::PointDataGrid>::value>::type
311 preProcessMetadata(const T& openGrid);
312
313 template <typename T>
314 typename std::enable_if<std::is_same<T, openvdb::tools::PointIndexGrid>::value>::type
315 preProcessMetadata(const T& openGrid);
316
317 template <typename T>
318 typename std::enable_if<std::is_same<T, openvdb::points::PointDataGrid>::value>::type
319 preProcessMetadata(const T& openGrid);
320
321 //////////////////////
322
323 /// @brief Private methods to process the blind metadata
324 template<typename T>
325 typename std::enable_if<!std::is_same<T, openvdb::tools::PointIndexGrid>::value &&
326 !std::is_same<T, openvdb::points::PointDataGrid>::value, GridBlindMetaData*>::type
327 processMetadata(const T& openGrid);
328
329 template<typename T>
330 typename std::enable_if<std::is_same<T, openvdb::tools::PointIndexGrid>::value, GridBlindMetaData*>::type
331 processMetadata(const T& openGrid);
332
333 template<typename T>
334 typename std::enable_if<std::is_same<T, openvdb::points::PointDataGrid>::value, GridBlindMetaData*>::type
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
359template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
360OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::OpenToNanoVDB()
361 : mVerbose(0)
362 , mStats(StatsMode::Default)
363 , mChecksum(ChecksumMode::Default)
364 , mDitherOn(false)
365 , mOracle()
366{
367}
368
369//================================================================================================
370
371template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
372inline GridHandle<BufferT>
373 OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, 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
388template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
389inline GridHandle<BufferT>
390OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, 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
418 if (std::is_same<OpenBuildT, openvdb::PointIndex32>::value ||
419 std::is_same<OpenBuildT, openvdb::PointDataIndex32>::value) {
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
436template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
437template <typename T>
438inline typename std::enable_if<std::is_same<T, FpN>::value>::type
439 OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::
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();
462 float min = std::numeric_limits<float>::max(), max = -min;
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
519template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
520GridHandle<BufferT> OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, 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
606template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
607NanoGrid<NanoBuildT>* OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, 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
636 case openvdb::GRID_LEVEL_SET:
637 if (!is_floating_point<OpenValueT>::value)
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;
642 case openvdb::GRID_FOG_VOLUME:
643 data->mGridClass = GridClass::FogVolume;
644 break;
645 case openvdb::GRID_STAGGERED:
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;
655 } else if (std::is_same<NanoBuildT, double>::value) {
656 data->mGridType = GridType::Double;
657 } else if (std::is_same<NanoBuildT, int16_t>::value) {
658 data->mGridType = GridType::Int16;
659 } else if (std::is_same<NanoBuildT, int32_t>::value) {
660 data->mGridType = GridType::Int32;
661 } else if (std::is_same<NanoBuildT, int64_t>::value) {
662 data->mGridType = GridType::Int64;
663 } else if (std::is_same<NanoBuildT, Vec3f>::value) {
664 data->mGridType = GridType::Vec3f;
665 } else if (std::is_same<OpenBuildT, openvdb::Index32>::value) {
666 data->mGridType = GridType::UInt32;
667 } else if (std::is_same<OpenBuildT, openvdb::PointIndex32>::value) {
668 data->mGridType = GridType::UInt32;
669 data->mGridClass = GridClass::PointIndex;
670 } else if (std::is_same<OpenBuildT, openvdb::PointDataIndex32>::value) {
671 data->mGridType = GridType::UInt32;
672 data->mGridClass = GridClass::PointData;
673 } else if (std::is_same<NanoBuildT, ValueMask>::value) {
674 data->mGridType = GridType::Mask;
675 data->mGridClass = GridClass::Topology;
676 } else if (std::is_same<NanoBuildT, bool>::value) {
677 data->mGridType = GridType::Boolean;
678 } else if (std::is_same<NanoBuildT, Fp4>::value) {
679 data->mGridType = GridType::Fp4;
680 } else if (std::is_same<NanoBuildT, Fp8>::value) {
681 data->mGridType = GridType::Fp8;
682 } else if (std::is_same<NanoBuildT, Fp16>::value) {
683 data->mGridType = GridType::Fp16;
684 } else if (std::is_same<NanoBuildT, FpN>::value) {
685 data->mGridType = GridType::FpN;
686 } else if (std::is_same<NanoBuildT, Vec4f>::value) {
687 data->mGridType = GridType::Vec4f;
688 } else if (std::is_same<NanoBuildT, Vec4d>::value) {
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
722template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
723NanoTree<NanoBuildT>* OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, 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
783template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
784NanoRoot<NanoBuildT>* OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, 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
814template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
815template<typename OpenNodeT>
816 void OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::
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
847template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
848template<typename T>
849inline 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 &&
851 !std::is_same<Fp4, typename T::NanoNodeT::BuildType>::value &&
852 !std::is_same<Fp8, typename T::NanoNodeT::BuildType>::value &&
853 !std::is_same<Fp16,typename T::NanoNodeT::BuildType>::value &&
854 !std::is_same<FpN, typename T::NanoNodeT::BuildType>::value>::type
855 OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processLeafs(std::vector<T>& openLeafs)
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
887template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
888template<typename T>
889inline typename std::enable_if<std::is_same<Fp4, typename T::NanoNodeT::BuildType>::value ||
890 std::is_same<Fp8, typename T::NanoNodeT::BuildType>::value ||
891 std::is_same<Fp16, typename T::NanoNodeT::BuildType>::value>::type
892OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processLeafs(std::vector<T>& openLeafs)
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
911 float min = std::numeric_limits<float>::max(), max = -min;
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
944template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
945template<typename T>
946inline typename std::enable_if<std::is_same<FpN, typename T::NanoNodeT::BuildType>::value>::type
947OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processLeafs(std::vector<T>& openLeafs)
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
1033template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1034template<typename T>
1035inline typename std::enable_if<std::is_same<T, typename OpenGridType<bool>::LeafT>::value>::type
1036OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processLeafs(std::vector<NodePair<T>>& openLeafs)
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
1057template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1058template<typename T>
1059inline typename std::enable_if<std::is_same<T, typename OpenGridType<openvdb::ValueMask>::LeafT>::value>::type
1060OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processLeafs(std::vector<NodePair<T>>& openLeafs)
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
1080template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1081uint64_t OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::pointCount()
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
1092template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1093template <typename OpenNodeT, typename NanoNodeT>
1094inline void OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::
1095encode(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
1108template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1109template <typename OpenNodeT>
1110inline typename NanoNode<NanoBuildT, OpenNodeT::LEVEL>::Type* OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::
1111decode(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
1125template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1126template <typename NodeT>
1127struct 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
1137template<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
1155template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1156template <typename T>
1157inline typename std::enable_if<!std::is_same<T, openvdb::tools::PointIndexGrid>::value &&
1158 !std::is_same<T, openvdb::points::PointDataGrid>::value>::type
1159 OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::preProcessMetadata(const T& openGrid)
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
1170template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1171template <typename T>
1172inline typename std::enable_if<std::is_same<T, openvdb::tools::PointIndexGrid>::value>::type
1173OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::preProcessMetadata(const T& openGrid)
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
1187template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1188template <typename T>
1189inline typename std::enable_if<std::is_same<T, openvdb::points::PointDataGrid>::value>::type
1190OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::preProcessMetadata(const T& openGrid)
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
1214template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1215template<typename T>
1216inline typename std::enable_if<!std::is_same<T, openvdb::tools::PointIndexGrid>::value &&
1217 !std::is_same<T, openvdb::points::PointDataGrid>::value,GridBlindMetaData*>::type
1218OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::
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
1244template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1245template<typename T>
1246inline typename std::enable_if<std::is_same<T, openvdb::tools::PointIndexGrid>::value,GridBlindMetaData*>::type
1247OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processMetadata(const T& openGrid)
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";
1284 OPENVDB_THROW(openvdb::ValueError, ss.str());
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
1317template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1318template<typename T>
1319inline typename std::enable_if<std::is_same<T, openvdb::points::PointDataGrid>::value,GridBlindMetaData*>::type
1320OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::processMetadata(const T& openGrid)
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";
1361 OPENVDB_THROW(openvdb::ValueError, ss.str());
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 << "\"";
1403 OPENVDB_THROW(openvdb::ValueError, ss.str());
1404 }
1405 }
1406 blindData += it->size;
1407 } // loop over bind data
1408 return metaData;
1409}// OpenToNanoVDB::processMetadata<PointDataIndex32>
1410
1411//================================================================================================
1412
1413
1414template<typename OpenBuildT, typename NanoBuildT, typename OracleT, typename BufferT>
1415template<typename AttT, typename CodecT>
1416inline void OpenToNanoVDB<OpenBuildT, NanoBuildT, OracleT, BufferT>::
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
1438template<typename BufferT, typename OpenTreeT, typename NanoBuildT>
1439GridHandle<BufferT>
1440 openToNanoVDB(const openvdb::Grid<OpenTreeT>& grid,
1441 StatsMode sMode,
1442 ChecksumMode cMode,
1443 int verbose)
1444{
1445 using OpenBuildT = typename OpenTreeT::BuildType;
1446 OpenToNanoVDB<OpenBuildT, NanoBuildT, AbsDiff, BufferT> s;
1447 return s(grid, sMode, cMode, verbose);
1448}// openToNanoVDB
1449
1450//================================================================================================
1451
1452template<typename BufferT>
1453GridHandle<BufferT>
1454openToNanoVDB(const openvdb::GridBase::Ptr& base,
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
DitherLUT.h
Defines look up table to do dithering of 8^3 leaf nodes.
ForEach.h
A unified wrapper for tbb::parallel_for and a naive std::thread fallback.
GridBuilder.h
Generates a NanoVDB grid from any volume or function.
value
ValueT value
Definition GridBuilder.h:1290
GridChecksum.h
Computes a pair of 32bit checksums, og a Grid, by means of Cyclic Redundancy Check (CRC)
GridHandle.h
Defines two classes, a GridRegister the defines the value type (e.g. Double, Float etc) of a NanoVDB ...
GridStats.h
Re-computes min/max/avg/var/bbox information for each node in a pre-existing NanoVDB grid.
Invoke.h
A unified wrapper for tbb::parallel_invoke and a naive std::thread analog.
NANOVDB_DATA_ALIGNMENT
#define NANOVDB_DATA_ALIGNMENT
Definition NanoVDB.h:137
NANOVDB_MAGIC_NUMBER
#define NANOVDB_MAGIC_NUMBER
Definition NanoVDB.h:121
PointDataGrid.h
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
Reduce.h
A unified wrapper for tbb::parallel_reduce and a naive std::future analog.
nanovdb::AbsDiff
Compression oracle based on absolute difference.
Definition GridBuilder.h:39
nanovdb::Coord
Signed (i, j, k) 32-bit integer coordinate class, similar to openvdb::math::Coord.
Definition NanoVDB.h:967
nanovdb::DenseGridHandle
Definition DenseGrid.h:402
nanovdb::DenseGridHandle::buffer
BufferT & buffer()
Definition DenseGrid.h:426
nanovdb::DenseGridHandle::DenseGridHandle
DenseGridHandle()=default
nanovdb::DenseGridHandle::size
uint64_t size() const
Returns the size in bytes of the raw memory buffer managed by this DenseGridHandle's allocator.
Definition DenseGrid.h:440
nanovdb::DenseGridHandle::data
uint8_t * data()
Returns a non-const pointer to the data.
Definition DenseGrid.h:432
nanovdb::DenseGridHandle::reset
void reset()
Definition DenseGrid.h:424
nanovdb::HostBuffer
This is a buffer that contains a shared or private pool to either externally or internally managed ho...
Definition HostBuffer.h:115
nanovdb::OpenToNanoVDB
This class will convert an OpenVDB grid into a NanoVDB grid managed by a GridHandle.
Definition OpenToNanoVDB.h:186
nanovdb::OpenToNanoVDB::setStats
void setStats(StatsMode mode=StatsMode::Default)
Definition OpenToNanoVDB.h:237
nanovdb::OpenToNanoVDB::setChecksum
void setChecksum(ChecksumMode mode=ChecksumMode::Default)
Definition OpenToNanoVDB.h:239
nanovdb::OpenToNanoVDB::enableDithering
void enableDithering(bool on=true)
Definition OpenToNanoVDB.h:235
nanovdb::OpenToNanoVDB::oracle
OracleT & oracle()
return a reference to the compression oracle
Definition OpenToNanoVDB.h:231
nanovdb::OpenToNanoVDB::setVerbose
void setVerbose(int mode=1)
Definition OpenToNanoVDB.h:233
nanovdb::OpenToNanoVDB::operator()
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
nanovdb::OpenToNanoVDB::OpenToNanoVDB
OpenToNanoVDB()
Default c-tor.
Definition OpenToNanoVDB.h:360
nanovdb::ValueMask
Dummy type for a voxel whose value equals its binary active state.
Definition NanoVDB.h:216
openvdb::v10_1::Grid
Container class that associates a tree with a transform and metadata.
Definition Grid.h:571
openvdb::v10_1::Grid::TreeType
_TreeType TreeType
Definition Grid.h:576
openvdb::v10_1::RuntimeError
Definition Exceptions.h:63
openvdb::v10_1::ValueError
Definition Exceptions.h:65
openvdb::v10_1::math::CoordBBox
Axis-aligned bounding box of signed integer coordinates.
Definition Coord.h:251
openvdb::v10_1::math::Coord
Signed (x, y, z) 32-bit integer coordinates.
Definition Coord.h:25
openvdb::v10_1::math::Coord::min
static Coord min()
Return the smallest possible coordinate.
Definition Coord.h:43
openvdb::v10_1::math::Coord::max
static Coord max()
Return the largest possible coordinate.
Definition Coord.h:46
openvdb::v10_1::math::Coord::data
const Int32 * data() const
Definition Coord.h:139
openvdb::v10_1::math::Vec3
Definition Vec3.h:24
nanovdb
Definition NanoVDB.h:208
nanovdb::AlignUp
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
nanovdb::PtrDiff
static int64_t PtrDiff(const T1 *p, const T2 *q)
Definition NanoVDB.h:535
nanovdb::reduce
T reduce(RangeT range, const T &identity, const FuncT &func, const JoinT &join)
Definition Reduce.h:42
nanovdb::GridClass::FogVolume
@ FogVolume
nanovdb::GridClass::Unknown
@ Unknown
nanovdb::GridClass::Topology
@ Topology
nanovdb::GridClass::PointIndex
@ PointIndex
nanovdb::GridClass::PointData
@ PointData
nanovdb::GridClass::Staggered
@ Staggered
nanovdb::GridClass::LevelSet
@ LevelSet
nanovdb::GridType::Float
@ Float
nanovdb::GridType::Boolean
@ Boolean
nanovdb::GridType::Vec4f
@ Vec4f
nanovdb::GridType::Int16
@ Int16
nanovdb::GridType::Fp4
@ Fp4
nanovdb::GridType::Mask
@ Mask
nanovdb::GridType::Vec4d
@ Vec4d
nanovdb::GridType::Fp8
@ Fp8
nanovdb::GridType::Unknown
@ Unknown
nanovdb::GridType::FpN
@ FpN
nanovdb::GridType::Int32
@ Int32
nanovdb::GridType::Double
@ Double
nanovdb::GridType::UInt32
@ UInt32
nanovdb::GridType::Vec3f
@ Vec3f
nanovdb::GridType::Int64
@ Int64
nanovdb::GridType::Fp16
@ Fp16
nanovdb::GridFlags::IsBreadthFirst
@ IsBreadthFirst
nanovdb::Vec3R
Vec3< double > Vec3R
Definition NanoVDB.h:1289
nanovdb::GridBlindDataClass::IndexArray
@ IndexArray
nanovdb::GridBlindDataClass::AttributeArray
@ AttributeArray
nanovdb::GridBlindDataClass::GridName
@ GridName
nanovdb::CoordBBox
BBox< Coord > CoordBBox
Definition NanoVDB.h:1811
nanovdb::StatsMode
StatsMode
Grid flags which indicate what extra information is present in the grid buffer.
Definition GridStats.h:32
nanovdb::StatsMode::BBox
@ BBox
nanovdb::StatsMode::Default
@ Default
nanovdb::updateChecksum
void updateChecksum(NanoGrid< ValueT > &grid, ChecksumMode mode=ChecksumMode::Default)
Updates the checksum of a grid.
Definition GridChecksum.h:277
nanovdb::gridStats
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:710
nanovdb::ChecksumMode
ChecksumMode
List of different modes for computing for a checksum.
Definition GridChecksum.h:33
nanovdb::ChecksumMode::Default
@ Default
nanovdb::GridBlindDataSemantic::PointPosition
@ PointPosition
nanovdb::GridBlindDataSemantic::PointColor
@ PointColor
nanovdb::GridBlindDataSemantic::PointVelocity
@ PointVelocity
nanovdb::GridBlindDataSemantic::Unknown
@ Unknown
nanovdb::GridBlindDataSemantic::PointNormal
@ PointNormal
nanovdb::GridBlindDataSemantic::PointId
@ PointId
nanovdb::openToNanoVDB
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
nanovdb::forEach
void forEach(RangeT range, const FuncT &func)
simple wrapper for tbb::parallel_for with a naive std fallback
Definition ForEach.h:40
nanovdb::Range1D
Range< 1, size_t > Range1D
Definition Range.h:30
openvdb::v10_1::points::pointCount
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 PointCountImpl.h:18
openvdb::v10_1::tools::composite::max
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition Composite.h:110
openvdb::v10_1::tools::composite::min
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition Composite.h:106
openvdb::v10_1::GRID_FOG_VOLUME
@ GRID_FOG_VOLUME
Definition Types.h:456
openvdb::v10_1::GRID_STAGGERED
@ GRID_STAGGERED
Definition Types.h:457
openvdb::v10_1::GRID_LEVEL_SET
@ GRID_LEVEL_SET
Definition Types.h:455
openvdb::v10_1::FloatTree
tree::Tree4< float, 5, 4, 3 >::Type FloatTree
Definition openvdb.h:55
openvdb
Definition Exceptions.h:13
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
CpuTimer.h
openvdb.h
nanovdb::BBox< Coord >
nanovdb::GridBlindMetaData::memUsage
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:2328
nanovdb::GridBlindMetaData::MaxNameSize
static const int MaxNameSize
Definition NanoVDB.h:2318
nanovdb::GridData::MaxNameSize
static const int MaxNameSize
Definition NanoVDB.h:2435
nanovdb::NanoNode
Trait to map from LEVEL to node type.
Definition NanoVDB.h:4569
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::LeafT
typename LowerT::ChildNodeType LeafT
Definition OpenToNanoVDB.h:162
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::RootT
typename TreeT::RootNodeType RootT
Definition OpenToNanoVDB.h:159
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::TreeT
typename GridT::TreeType TreeT
Definition OpenToNanoVDB.h:158
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::LowerT
typename UpperT::ChildNodeType LowerT
Definition OpenToNanoVDB.h:161
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::UpperT
typename RootT::ChildNodeType UpperT
Definition OpenToNanoVDB.h:160
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::GridT
openvdb::points::PointDataGrid GridT
Definition OpenToNanoVDB.h:157
nanovdb::OpenGridType< openvdb::PointDataIndex32 >::ValueT
typename LeafT::ValueType ValueT
Definition OpenToNanoVDB.h:163
nanovdb::OpenGridType< openvdb::PointIndex32 >::LeafT
typename LowerT::ChildNodeType LeafT
Definition OpenToNanoVDB.h:149
nanovdb::OpenGridType< openvdb::PointIndex32 >::RootT
typename TreeT::RootNodeType RootT
Definition OpenToNanoVDB.h:146
nanovdb::OpenGridType< openvdb::PointIndex32 >::TreeT
typename GridT::TreeType TreeT
Definition OpenToNanoVDB.h:145
nanovdb::OpenGridType< openvdb::PointIndex32 >::LowerT
typename UpperT::ChildNodeType LowerT
Definition OpenToNanoVDB.h:148
nanovdb::OpenGridType< openvdb::PointIndex32 >::UpperT
typename RootT::ChildNodeType UpperT
Definition OpenToNanoVDB.h:147
nanovdb::OpenGridType< openvdb::PointIndex32 >::ValueT
typename LeafT::ValueType ValueT
Definition OpenToNanoVDB.h:150
nanovdb::OpenGridType< openvdb::PointIndex32 >::GridT
openvdb::tools::PointIndexGrid GridT
Definition OpenToNanoVDB.h:144
nanovdb::OpenGridType
Grid trait that defines OpenVDB grids with the exact same configuration as NanoVDB grids.
Definition OpenToNanoVDB.h:130
nanovdb::OpenGridType::LeafT
typename LowerT::ChildNodeType LeafT
Definition OpenToNanoVDB.h:136
nanovdb::OpenGridType::RootT
typename TreeT::RootNodeType RootT
Definition OpenToNanoVDB.h:133
nanovdb::OpenGridType::TreeT
typename GridT::TreeType TreeT
Definition OpenToNanoVDB.h:132
nanovdb::OpenGridType::LowerT
typename UpperT::ChildNodeType LowerT
Definition OpenToNanoVDB.h:135
nanovdb::OpenGridType::UpperT
typename RootT::ChildNodeType UpperT
Definition OpenToNanoVDB.h:134
nanovdb::OpenGridType::ValueT
typename LeafT::ValueType ValueT
Definition OpenToNanoVDB.h:137
nanovdb::OpenToNanoType
Converts OpenVDB types to NanoVDB types, e.g. openvdb::Vec3f to nanovdb::Vec3f Template specializatio...
Definition OpenToNanoVDB.h:37
nanovdb::OpenToNanoType::Type
T Type
Definition OpenToNanoVDB.h:37
nanovdb::OpenToNanoVDB::BlindMetaData
Definition OpenToNanoVDB.h:1139
nanovdb::OpenToNanoVDB::BlindMetaData::BlindMetaData
BlindMetaData(const std::string &n, const std::string &t, size_t i, size_t c, size_t s)
Definition OpenToNanoVDB.h:1140
nanovdb::OpenToNanoVDB::BlindMetaData::operator<
bool operator<(const BlindMetaData &other) const
Definition OpenToNanoVDB.h:1150
nanovdb::OpenToNanoVDB::BlindMetaData::count
const size_t count
Definition OpenToNanoVDB.h:1149
nanovdb::OpenToNanoVDB::BlindMetaData::name
const std::string name
Definition OpenToNanoVDB.h:1148
nanovdb::is_floating_point::value
static const bool value
Definition NanoVDB.h:415
nanovdb::is_same
C++11 implementation of std::is_same.
Definition NanoVDB.h:357
nanovdb::is_same::value
static constexpr bool value
Definition NanoVDB.h:358