1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 // Uncomment to temporarily disable testing of PNanoVDB
5 //#define DISABLE_PNANOVDB
6 
7 #include <iostream>
8 #include <cstdlib>
9 #include <sstream> // for std::stringstream
10 #include <vector>
11 #include <limits.h> // CHAR_BIT
12 #include <algorithm> // for std::is_sorted
13 #include <cmath>
14 #include <cstdlib>
15 
16 #include <nanovdb/util/IO.h>
17 #include <nanovdb/util/GridBuilder.h>
18 #include <nanovdb/util/Primitives.h>
19 #include <nanovdb/util/GridStats.h>
20 #include <nanovdb/util/GridValidator.h>
21 #include <nanovdb/util/Ray.h>
22 #include <nanovdb/util/HDDA.h>
23 #include <nanovdb/util/DitherLUT.h>
24 #include <nanovdb/util/SampleFromVoxels.h>
25 #include <nanovdb/util/Stencils.h>
26 #include <nanovdb/util/Range.h>
27 #include <nanovdb/util/ForEach.h>
28 #include <nanovdb/util/Invoke.h>
29 #include <nanovdb/util/Reduce.h>
30 #include <nanovdb/util/GridChecksum.h>
31 #include <nanovdb/util/NodeManager.h>
32 #include "../examples/ex_util/CpuTimer.h"
33 
34 #if !defined(_MSC_VER) // does not compile in msvc c++ due to zero-sized arrays.
35 #include <nanovdb/CNanoVDB.h>
36 #endif
37 
38 #if !defined(DISABLE_PNANOVDB)
39 #define PNANOVDB_C
40 #include <nanovdb/PNanoVDB.h>
41 #include "pnanovdb_validate_strides.h"
42 #endif
43 
44 #include <gtest/gtest.h>
45 
46 
47 namespace nanovdb {// this namespace is required by gtest
48 std::ostream&
operator <<(std::ostream & os,const Coord & ijk)49 operator<<(std::ostream& os, const Coord& ijk)
50 {
51     os << "(" << ijk[0] << "," << ijk[1] << "," << ijk[2] << ")";
52     return os;
53 }
54 
55 std::ostream&
operator <<(std::ostream & os,const CoordBBox & b)56 operator<<(std::ostream& os, const CoordBBox& b)
57 {
58     os << b[0] << " -> " << b[1];
59     return os;
60 }
61 
62 template<typename T>
63 std::ostream&
operator <<(std::ostream & os,const Vec3<T> & v)64 operator<<(std::ostream& os, const Vec3<T>& v)
65 {
66     os << "(" << v[0] << "," << v[1] << "," << v[2] << ")";
67     return os;
68 }
69 }// namespace nanovdb
70 
71 namespace {
72 template<typename ValueT>
73 struct Sphere
74 {
Sphere__anonfeb603ba0111::Sphere75     Sphere(const nanovdb::Vec3<ValueT>& center,
76            ValueT                       radius,
77            ValueT                       voxelSize = 1.0,
78            ValueT                       halfWidth = 3.0)
79         : mCenter(center)
80         , mRadius(radius)
81         , mVoxelSize(voxelSize)
82         , mBackground(voxelSize * halfWidth)
83     {
84     }
85 
background__anonfeb603ba0111::Sphere86     ValueT background() const { return mBackground; }
87 
88     /// @brief Only method required by GridBuilder
operator ()__anonfeb603ba0111::Sphere89     ValueT operator()(const nanovdb::Coord& ijk) const
90     {
91         const ValueT dst = this->sdf(ijk);
92         return dst >= mBackground ? mBackground : dst <= -mBackground ? -mBackground : dst;
93     }
operator ()__anonfeb603ba0111::Sphere94     ValueT operator()(const nanovdb::Vec3<ValueT>& p) const
95     {
96         const ValueT dst = this->sdf(p);
97         return dst >= mBackground ? mBackground : dst <= -mBackground ? -mBackground : dst;
98     }
isInside__anonfeb603ba0111::Sphere99     bool isInside(const nanovdb::Coord& ijk) const
100     {
101         return this->sdf(ijk) < 0;
102     }
isOutside__anonfeb603ba0111::Sphere103     bool isOutside(const nanovdb::Coord& ijk) const
104     {
105         return this->sdf(ijk) > 0;
106     }
inNarrowBand__anonfeb603ba0111::Sphere107     bool inNarrowBand(const nanovdb::Coord& ijk) const
108     {
109         const ValueT d = this->sdf(ijk);
110         return d < mBackground && d > -mBackground;
111     }
112 
113 private:
sdf__anonfeb603ba0111::Sphere114     ValueT sdf(nanovdb::Vec3<ValueT> xyz) const
115     {
116         xyz *= mVoxelSize;
117         xyz -= mCenter;
118         return xyz.length() - mRadius;
119     }
sdf__anonfeb603ba0111::Sphere120     ValueT sdf(const nanovdb::Coord& ijk) const { return this->sdf(nanovdb::Vec3<ValueT>(ijk[0], ijk[1], ijk[2])); }
121     static_assert(nanovdb::is_floating_point<float>::value, "Sphere: expect floating point");
122     const nanovdb::Vec3<ValueT> mCenter;
123     const ValueT                mRadius, mVoxelSize, mBackground;
124 }; // Sphere
125 } // namespace
126 
127 // The fixture for testing class.
128 class TestNanoVDB : public ::testing::Test
129 {
130 protected:
TestNanoVDB()131     TestNanoVDB() {}
132 
~TestNanoVDB()133     ~TestNanoVDB() override {}
134 
135     // If the constructor and destructor are not enough for setting up
136     // and cleaning up each test, you can define the following methods:
137 
SetUp()138     void SetUp() override
139     {
140         // Code here will be called immediately after the constructor (right
141         // before each test).
142     }
143 
TearDown()144     void TearDown() override
145     {
146         // Code here will be called immediately after each test (right
147         // before the destructor).
148     }
149 
getEnvVar(const std::string & name) const150     std::string getEnvVar(const std::string& name) const
151     {
152         const char* str = std::getenv(name.c_str());
153         return str == nullptr ? std::string("") : std::string(str);
154     }
155 
156     template<typename T>
printType(const std::string & s)157     void printType(const std::string& s)
158     {
159         const auto n = sizeof(T);
160         std::cerr << "Size of " << s << ": " << n << " bytes which is" << (n % 32 == 0 ? " " : " NOT ") << "32 byte aligned" << std::endl;
161     }
162     nanovdb::CpuTimer<> mTimer;
163 }; // TestNanoVDB
164 
165 template <typename T>
166 class TestOffsets : public ::testing::Test
167 {
168 protected:
TestOffsets()169     TestOffsets() {}
170 
~TestOffsets()171     ~TestOffsets() override {}
172 
SetUp()173     void SetUp() override
174     {
175     }
176 
TearDown()177     void TearDown() override
178     {
179     }
180 
181 }; // TestOffsets<T>
182 
183 using MyTypes = ::testing::Types<float,
184                                  double,
185                                  nanovdb::Fp4,
186                                  nanovdb::Fp8,
187                                  nanovdb::Fp16,
188                                  nanovdb::FpN,
189                                  int16_t,
190                                  int32_t,
191                                  int64_t,
192                                  nanovdb::Vec3f,
193                                  nanovdb::Vec3d,
194                                  nanovdb::ValueMask,
195                                  bool,
196                                  int16_t,
197                                  uint32_t>;
198 
199 TYPED_TEST_SUITE(TestOffsets, MyTypes);
200 
TEST_F(TestNanoVDB,Version)201 TEST_F(TestNanoVDB, Version)
202 {
203     EXPECT_EQ( 4u, sizeof(uint32_t));
204     EXPECT_EQ( 4u, sizeof(nanovdb::Version));
205     {// default constructor
206         nanovdb::Version v;
207         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), v.getMajor());
208         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), v.getMinor());
209         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), v.getPatch());
210         std::stringstream ss;
211         ss << NANOVDB_MAJOR_VERSION_NUMBER << "."
212            << NANOVDB_MINOR_VERSION_NUMBER << "."
213            << NANOVDB_PATCH_VERSION_NUMBER;
214         auto c_str = v.c_str();
215         EXPECT_EQ(ss.str(), std::string(c_str));
216         std::free(const_cast<char*>(c_str));
217         //std::cerr << v.c_str() << std::endl;
218     }
219     {// detailed constructor
220         const uint32_t major = (1u << 11) - 1;// maximum allowed value
221         const uint32_t minor = (1u << 11) - 1;// maximum allowed value
222         const uint32_t patch = (1u << 10) - 1;// maximum allowed value
223         nanovdb::Version v( major, minor, patch);
224         EXPECT_EQ(major, v.getMajor());
225         EXPECT_EQ(minor, v.getMinor());
226         EXPECT_EQ(patch, v.getPatch());
227         std::stringstream ss;
228         ss << major << "." << minor << "." << patch;
229         auto c_str = v.c_str();
230         EXPECT_EQ(ss.str(), std::string(c_str));
231         std::free(const_cast<char*>(c_str));
232         //std::cerr << v.c_str() << std::endl;
233     }
234     {// smallest possible version number
235         const uint32_t major = 1u;
236         const uint32_t minor = 0u;
237         const uint32_t patch = 0u;
238         nanovdb::Version v( major, minor, patch);
239         EXPECT_EQ(major, v.getMajor());
240         EXPECT_EQ(minor, v.getMinor());
241         EXPECT_EQ(patch, v.getPatch());
242         std::stringstream ss;
243         ss << major << "." << minor << "." << patch;
244         auto c_str = v.c_str();
245         EXPECT_EQ(ss.str(), std::string(c_str));
246         std::free(const_cast<char*>(c_str));
247         //std::cerr << "version.data = " << v.id() << std::endl;
248     }
249     {// test comparison operators
250         EXPECT_EQ( nanovdb::Version(28, 2, 7), nanovdb::Version( 28, 2, 7) );
251         EXPECT_LE( nanovdb::Version(28, 2, 7), nanovdb::Version( 28, 2, 7) );
252         EXPECT_GE( nanovdb::Version(28, 2, 7), nanovdb::Version( 28, 2, 7) );
253         EXPECT_LT( nanovdb::Version(28, 2, 7), nanovdb::Version( 28, 2, 8) );
254         EXPECT_LT( nanovdb::Version(28, 2, 7), nanovdb::Version( 28, 3, 7) );
255         EXPECT_LT( nanovdb::Version(28, 2, 7), nanovdb::Version( 29, 2, 7) );
256         EXPECT_LT( nanovdb::Version(28, 2, 7), nanovdb::Version( 29, 3, 8) );
257         EXPECT_GT( nanovdb::Version(29, 0, 0), nanovdb::Version( 28, 2, 8) );
258     }
259 
260     // nanovdb::Version was introduce in 29.0.0! For backwards compatibility with 28.X.X
261     // we need to distinguish between old formats (either uint32_t or two uint16_t) and
262     // the new format (nanovdb::Version which internally stored a single uint32_t).
263     {
264         // Define a struct that memory-maps all three possible representations
265         struct T {
266             union {
267                 nanovdb::Version version;
268                 uint32_t id;
269                 struct {uint16_t major, minor;};
270             };
271             T(uint32_t _major) : id(_major) {}
272             T(uint32_t _major, uint32_t _minor) : major(_major), minor(_minor) {}
273             T(uint32_t _major, uint32_t _minor, uint32_t _patch) : version(_major, _minor, _patch) {}
274         };
275         EXPECT_EQ( sizeof(uint32_t), sizeof(T) );
276         // Verify that T(1,0,0).id() is the smallest instance of Version
277         for (uint32_t major = 1; major < 30; ++major) {
278             for (uint32_t minor = 0; minor < 10; ++minor) {
279                 for (uint32_t patch = 0; patch < 10; ++patch) {
280                     T tmp(major, minor, patch);
281                     EXPECT_LE(T(1,0,0).id, tmp.id);
282                     EXPECT_LE(T(1,0,0).version, tmp.version);
283                 }
284             }
285         }
286         // Verify that all relevant "uint16_t major,minor" instances are smaller than T(29,0,0)
287         for (uint32_t major = 1; major <= 28; ++major) {
288             for (uint32_t minor = 0; minor < 30; ++minor) {
289                 T tmp(major, minor);
290                 EXPECT_LT(tmp.id, T(29,0,0).id);
291                 EXPECT_LT(tmp.version, T(29,0,0).version);
292             }
293         }
294         // Verify that all relevant "uint32_t major" instances are smaller than T(29,0,0)
295         for (uint32_t major = 1; major <= 28; ++major) {
296             T tmp(major);
297             EXPECT_LT(tmp.id, T(29,0,0).id);
298             EXPECT_LT(tmp.version, T(29,0,0).version);
299         }
300     }
301 }
302 
TEST_F(TestNanoVDB,Basic)303 TEST_F(TestNanoVDB, Basic)
304 {
305     { // CHAR_BIT
306         EXPECT_EQ(8, CHAR_BIT);
307     }
308     {
309         std::vector<int> v = {3, 1, 7, 0};
310         EXPECT_FALSE(std::is_sorted(v.begin(), v.end()));
311         std::map<int, void*> m;
312         for (const auto& i : v)
313             m[i] = nullptr;
314         v.clear();
315         for (const auto& i : m)
316             v.push_back(i.first);
317         EXPECT_TRUE(std::is_sorted(v.begin(), v.end()));
318     }
319     {
320         enum tmp { a = 0,
321                    b,
322                    c,
323                    d } t;
324         EXPECT_EQ(sizeof(int), sizeof(t));
325     }
326     {// Check size of io::MetaData
327         EXPECT_EQ(176u, sizeof(nanovdb::io::MetaData));
328         //std::cerr << "sizeof(MetaData) = " << sizeof(nanovdb::io::MetaData) << std::endl;
329     }
330 }
331 
TEST_F(TestNanoVDB,Assumptions)332 TEST_F(TestNanoVDB, Assumptions)
333 {
334     struct A
335     {
336         int32_t i;
337     };
338     EXPECT_EQ(sizeof(int32_t), sizeof(A));
339     A a{-1};
340     EXPECT_EQ(-1, a.i);
341     EXPECT_EQ(reinterpret_cast<uint8_t*>(&a), reinterpret_cast<uint8_t*>(&a.i));
342     struct B
343     {
344         A a;
345     };
346     B b{-1};
347     EXPECT_EQ(-1, b.a.i);
348     EXPECT_EQ(reinterpret_cast<uint8_t*>(&b), reinterpret_cast<uint8_t*>(&(b.a)));
349     EXPECT_EQ(reinterpret_cast<uint8_t*>(&(b.a)), reinterpret_cast<uint8_t*>(&(b.a.i)));
350     EXPECT_EQ(nanovdb::AlignUp<32>(48), 64U);
351     EXPECT_EQ(nanovdb::AlignUp<8>(16), 16U);
352 }
353 
TEST_F(TestNanoVDB,Magic)354 TEST_F(TestNanoVDB, Magic)
355 {
356     EXPECT_EQ(0x304244566f6e614eUL, NANOVDB_MAGIC_NUMBER); // Magic number: "NanoVDB0" in hex)
357     EXPECT_EQ(0x4e616e6f56444230UL, nanovdb::io::reverseEndianness(NANOVDB_MAGIC_NUMBER));
358 
359     // Verify little endian representation
360     const char* str = "NanoVDB0"; // note it's exactly 8 bytes
361     EXPECT_EQ(8u, strlen(str));
362     std::stringstream ss1;
363     ss1 << "0x";
364     for (int i = 7; i >= 0; --i)
365         ss1 << std::hex << unsigned(str[i]);
366     ss1 << "UL";
367     //std::cerr << ss1.str() << std::endl;
368     EXPECT_EQ("0x304244566f6e614eUL", ss1.str());
369 
370     uint64_t magic;
371     ss1 >> magic;
372     EXPECT_EQ(magic, NANOVDB_MAGIC_NUMBER);
373 
374     // Verify big endian representation
375     std::stringstream ss2;
376     ss2 << "0x";
377     for (size_t i = 0; i < 8; ++i)
378         ss2 << std::hex << unsigned(str[i]);
379     ss2 << "UL";
380     //std::cerr << ss2.str() << std::endl;
381     EXPECT_EQ("0x4e616e6f56444230UL", ss2.str());
382 
383     ss2 >> magic;
384     EXPECT_EQ(magic, nanovdb::io::reverseEndianness(NANOVDB_MAGIC_NUMBER));
385 }
386 
TEST_F(TestNanoVDB,FindBits)387 TEST_F(TestNanoVDB, FindBits)
388 {
389     for (uint32_t i = 0; i < 32; ++i) {
390         uint32_t word = uint32_t(1) << i;
391         EXPECT_EQ(i, nanovdb::FindLowestOn(word));
392         EXPECT_EQ(i, nanovdb::FindHighestOn(word));
393     }
394     for (uint32_t i = 0; i < 64; ++i) {
395         uint64_t word = uint64_t(1) << i;
396         EXPECT_EQ(i, nanovdb::FindLowestOn(word));
397         EXPECT_EQ(i, nanovdb::FindHighestOn(word));
398     }
399 }
400 
TEST_F(TestNanoVDB,CRC32)401 TEST_F(TestNanoVDB, CRC32)
402 {
403     { // test function that uses iterators
404         const std::string s{"The quick brown fox jumps over the lazy dog"};
405         std::stringstream ss;
406         ss << std::hex << std::setw(8) << std::setfill('0') << nanovdb::crc32(s.begin(), s.end());
407         EXPECT_EQ("414fa339", ss.str());
408     }
409     { // test the checksum for a modified string
410         const std::string s{"The quick brown Fox jumps over the lazy dog"};
411         std::stringstream ss;
412         ss << std::hex << std::setw(8) << std::setfill('0') << nanovdb::crc32(s.begin(), s.end());
413         EXPECT_NE("414fa339", ss.str());
414     }
415     { // test function that uses void pointer and byte size
416         const std::string s{"The quick brown fox jumps over the lazy dog"};
417         std::stringstream ss;
418         ss << std::hex << std::setw(8) << std::setfill('0') << nanovdb::crc32(s.data(), s.size());
419         EXPECT_EQ("414fa339", ss.str());
420     }
421     { // test accumulation
422         nanovdb::CRC32    crc;
423         const std::string s1{"The quick brown fox jum"};
424         crc(s1.begin(), s1.end());
425         const std::string s2{"ps over the lazy dog"};
426         crc(s2.begin(), s2.end());
427         std::stringstream ss;
428         ss << std::hex << std::setw(8) << std::setfill('0') << crc.checksum();
429         EXPECT_EQ("414fa339", ss.str());
430     }
431 }
432 
TEST_F(TestNanoVDB,Range1D)433 TEST_F(TestNanoVDB, Range1D)
434 {
435     nanovdb::Range1D r1(0, 20, 2);
436     EXPECT_FALSE(r1.empty());
437     EXPECT_EQ(2U, r1.grainsize());
438     EXPECT_EQ(20U, r1.size());
439     EXPECT_EQ(10U, r1.middle());
440     EXPECT_TRUE(r1.is_divisible());
441     EXPECT_EQ(0U, r1.begin());
442     EXPECT_EQ(20U, r1.end());
443 
444     nanovdb::Range1D r2(r1, nanovdb::Split());
445 
446     EXPECT_FALSE(r1.empty());
447     EXPECT_EQ(2U, r1.grainsize());
448     EXPECT_EQ(10U, r1.size());
449     EXPECT_EQ(5U, r1.middle());
450     EXPECT_TRUE(r1.is_divisible());
451     EXPECT_EQ(0U, r1.begin());
452     EXPECT_EQ(10U, r1.end());
453 
454     EXPECT_FALSE(r2.empty());
455     EXPECT_EQ(2U, r2.grainsize());
456     EXPECT_EQ(10U, r2.size());
457     EXPECT_EQ(15U, r2.middle());
458     EXPECT_TRUE(r2.is_divisible());
459     EXPECT_EQ(10U, r2.begin());
460     EXPECT_EQ(20U, r2.end());
461 }
462 
TEST_F(TestNanoVDB,Range2D)463 TEST_F(TestNanoVDB, Range2D)
464 {
465     nanovdb::Range<2, int> r1(-20, 20, 1u, 0, 20, 2u);
466 
467     EXPECT_FALSE(r1.empty());
468     EXPECT_EQ(1U, r1[0].grainsize());
469     EXPECT_EQ(40U, r1[0].size());
470     EXPECT_EQ(0, r1[0].middle());
471     EXPECT_TRUE(r1[0].is_divisible());
472     EXPECT_EQ(-20, r1[0].begin());
473     EXPECT_EQ(20, r1[0].end());
474 
475     EXPECT_EQ(2U, r1[1].grainsize());
476     EXPECT_EQ(20U, r1[1].size());
477     EXPECT_EQ(10, r1[1].middle());
478     EXPECT_TRUE(r1[1].is_divisible());
479     EXPECT_EQ(0, r1[1].begin());
480     EXPECT_EQ(20, r1[1].end());
481 
482     nanovdb::Range<2, int> r2(r1, nanovdb::Split());
483 
484     EXPECT_FALSE(r1.empty());
485     EXPECT_EQ(1U, r1[0].grainsize());
486     EXPECT_EQ(20U, r1[0].size());
487     EXPECT_EQ(-10, r1[0].middle());
488     EXPECT_TRUE(r1[0].is_divisible());
489     EXPECT_EQ(-20, r1[0].begin());
490     EXPECT_EQ(0, r1[0].end());
491 
492     EXPECT_EQ(2U, r1[1].grainsize());
493     EXPECT_EQ(20U, r1[1].size());
494     EXPECT_EQ(10, r1[1].middle());
495     EXPECT_TRUE(r1[1].is_divisible());
496     EXPECT_EQ(0, r1[1].begin());
497     EXPECT_EQ(20, r1[1].end());
498 
499     EXPECT_FALSE(r2.empty());
500     EXPECT_EQ(1U, r2[0].grainsize());
501     EXPECT_EQ(20U, r2[0].size());
502     EXPECT_EQ(10, r2[0].middle());
503     EXPECT_TRUE(r2[0].is_divisible());
504     EXPECT_EQ(0, r2[0].begin());
505     EXPECT_EQ(20, r2[0].end());
506 
507     EXPECT_EQ(2U, r2[1].grainsize());
508     EXPECT_EQ(20U, r2[1].size());
509     EXPECT_EQ(10, r2[1].middle());
510     EXPECT_TRUE(r2[1].is_divisible());
511     EXPECT_EQ(0, r2[1].begin());
512     EXPECT_EQ(20, r2[1].end());
513     EXPECT_EQ(r1[1], r2[1]);
514 }
515 
TEST_F(TestNanoVDB,Range3D)516 TEST_F(TestNanoVDB, Range3D)
517 {
518     nanovdb::Range<3, int> r1(-20, 20, 1u, 0, 20, 2u, 0, 10, 5);
519 
520     EXPECT_FALSE(r1.empty());
521     EXPECT_EQ(1U, r1[0].grainsize());
522     EXPECT_EQ(40U, r1[0].size());
523     EXPECT_EQ(0, r1[0].middle());
524     EXPECT_TRUE(r1[0].is_divisible());
525     EXPECT_EQ(-20, r1[0].begin());
526     EXPECT_EQ(20, r1[0].end());
527 
528     EXPECT_EQ(2U, r1[1].grainsize());
529     EXPECT_EQ(20U, r1[1].size());
530     EXPECT_EQ(10, r1[1].middle());
531     EXPECT_TRUE(r1[1].is_divisible());
532     EXPECT_EQ(0, r1[1].begin());
533     EXPECT_EQ(20, r1[1].end());
534 
535     EXPECT_EQ(5U, r1[2].grainsize());
536     EXPECT_EQ(10U, r1[2].size());
537     EXPECT_EQ(5, r1[2].middle());
538     EXPECT_TRUE(r1[2].is_divisible());
539     EXPECT_EQ(0, r1[2].begin());
540     EXPECT_EQ(10, r1[2].end());
541 
542     nanovdb::Range<3, int> r2(r1, nanovdb::Split());
543 
544     EXPECT_FALSE(r1.empty());
545     EXPECT_EQ(1U, r1[0].grainsize());
546     EXPECT_EQ(20U, r1[0].size());
547     EXPECT_EQ(-10, r1[0].middle());
548     EXPECT_TRUE(r1[0].is_divisible());
549     EXPECT_EQ(-20, r1[0].begin());
550     EXPECT_EQ(0, r1[0].end());
551 
552     EXPECT_EQ(2U, r1[1].grainsize());
553     EXPECT_EQ(20U, r1[1].size());
554     EXPECT_EQ(10, r1[1].middle());
555     EXPECT_TRUE(r1[1].is_divisible());
556     EXPECT_EQ(0, r1[1].begin());
557     EXPECT_EQ(20, r1[1].end());
558 
559     EXPECT_EQ(5U, r1[2].grainsize());
560     EXPECT_EQ(10U, r1[2].size());
561     EXPECT_EQ(5, r1[2].middle());
562     EXPECT_TRUE(r1[2].is_divisible());
563     EXPECT_EQ(0, r1[2].begin());
564     EXPECT_EQ(10, r1[2].end());
565 
566     EXPECT_FALSE(r2.empty());
567     EXPECT_EQ(1U, r2[0].grainsize());
568     EXPECT_EQ(20U, r2[0].size());
569     EXPECT_EQ(10, r2[0].middle());
570     EXPECT_TRUE(r2[0].is_divisible());
571     EXPECT_EQ(0, r2[0].begin());
572     EXPECT_EQ(20, r2[0].end());
573 
574     EXPECT_EQ(2U, r2[1].grainsize());
575     EXPECT_EQ(20U, r2[1].size());
576     EXPECT_EQ(10, r2[1].middle());
577     EXPECT_TRUE(r2[1].is_divisible());
578     EXPECT_EQ(0, r2[1].begin());
579     EXPECT_EQ(20, r2[1].end());
580     EXPECT_EQ(r1[1], r2[1]);
581 
582     EXPECT_EQ(5U, r2[2].grainsize());
583     EXPECT_EQ(10U, r2[2].size());
584     EXPECT_EQ(5, r2[2].middle());
585     EXPECT_TRUE(r2[2].is_divisible());
586     EXPECT_EQ(0, r2[2].begin());
587     EXPECT_EQ(10, r2[2].end());
588     EXPECT_EQ(r1[2], r2[2]);
589 }
590 
TEST_F(TestNanoVDB,invoke)591 TEST_F(TestNanoVDB, invoke)
592 {
593     const int size = 4;
594     std::vector<int> array(size, 0);
595     for (int i=0; i<size; ++i) {
596         EXPECT_EQ(0, array[i]);
597     }
598     auto kernel0 = [&array](){array[0]=0; };
599     auto kernel1 = [&array](){array[1]=1; };
600     auto kernel2 = [&array](){array[2]=2; };
601     auto kernel3 = [&array](){array[3]=3; };
602     nanovdb::invoke(kernel0, kernel1, kernel2, kernel3);
603     for (int i=0; i<size; ++i) {
604         EXPECT_EQ(i, array[i]);
605     }
606 }
607 
TEST_F(TestNanoVDB,forEach)608 TEST_F(TestNanoVDB, forEach)
609 {
610     const int size = 1000;
611     std::vector<int> array(size, 0);
612     for (int i=0; i<size; ++i) {
613         EXPECT_EQ(0, array[i]);
614     }
615     auto kernel = [&array](const nanovdb::Range1D &r){for (auto i=r.begin(); i!=r.end(); ++i) array[i]=i; };
616     nanovdb::forEach(array, kernel);
617     for (int i=0; i<size; ++i) {
618         EXPECT_EQ(i, array[i]);
619     }
620 }
621 
TEST_F(TestNanoVDB,reduce)622 TEST_F(TestNanoVDB, reduce)
623 {
624     const int size = 1000;
625     std::vector<int> array(size);
626     int expected = 0;
627     for (int i=0; i<size; ++i) {
628         array[i] = i;
629         expected += i;
630     }
631     const int identity = 0;
632     auto func = [&array](const nanovdb::Range1D &r, int a){for (auto i=r.begin(); i!=r.end(); ++i) a+=array[i]; return a; };
633     auto join = [](int a, int b){return a + b;};
634     EXPECT_EQ(expected, nanovdb::reduce(nanovdb::Range1D(0, size), identity, func, join));
635     EXPECT_EQ(expected, nanovdb::reduce(array, identity, func, join));
636     EXPECT_EQ(expected, nanovdb::reduce(array, 8, identity, func, join));
637     for (int i=0; i<size; ++i) {
638         EXPECT_EQ(i, array[i]);
639     }
640 }
641 
TEST_F(TestNanoVDB,DitherLUT)642 TEST_F(TestNanoVDB, DitherLUT)
643 {
644     nanovdb::DitherLUT lut;
645     float min = 1.0f, max = 0.0f;
646     for (int i=-10; i<1024; ++i) {
647         const float offset = lut(i);
648         if (offset < min) min = offset;
649         if (offset > max) max = offset;
650         EXPECT_TRUE( offset > 0.0f);
651         EXPECT_TRUE( offset < 1.0f);
652     }
653     //std::cout << "Dither: min = " << min << ", max = " << max << std::endl;
654 }
655 
TEST_F(TestNanoVDB,Rgba8)656 TEST_F(TestNanoVDB, Rgba8)
657 {
658     EXPECT_EQ(sizeof(uint32_t), sizeof(nanovdb::Rgba8));
659     {
660         nanovdb::Rgba8 p;
661         EXPECT_EQ(0u, p[0]);
662         EXPECT_EQ(0u, p[1]);
663         EXPECT_EQ(0u, p[2]);
664         EXPECT_EQ(0u, p[3]);
665         EXPECT_EQ(0u, p.r());
666         EXPECT_EQ(0u, p.g());
667         EXPECT_EQ(0u, p.b());
668         EXPECT_EQ(0u, p.a());
669         EXPECT_EQ(0u, p.packed());
670         EXPECT_EQ(nanovdb::Rgba8(), p);
671     }
672     {
673         nanovdb::Rgba8 p(uint8_t(1));
674         EXPECT_EQ(1u, p[0]);
675         EXPECT_EQ(1u, p[1]);
676         EXPECT_EQ(1u, p[2]);
677         EXPECT_EQ(1u, p[3]);
678         EXPECT_EQ(1u, p.r());
679         EXPECT_EQ(1u, p.g());
680         EXPECT_EQ(1u, p.b());
681         EXPECT_EQ(1u, p.a());
682         EXPECT_LT(nanovdb::Rgba8(), p);
683     }
684     {
685         nanovdb::Rgba8 p(uint8_t(1), uint8_t(2), uint8_t(3), uint8_t(4));
686         EXPECT_EQ(1u, p[0]);
687         EXPECT_EQ(2u, p[1]);
688         EXPECT_EQ(3u, p[2]);
689         EXPECT_EQ(4u, p[3]);
690         EXPECT_EQ(1u, p.r());
691         EXPECT_EQ(2u, p.g());
692         EXPECT_EQ(3u, p.b());
693         EXPECT_EQ(4u, p.a());
694         EXPECT_LT(nanovdb::Rgba8(), p);
695     }
696     {
697         nanovdb::Rgba8 p(uint8_t(255), uint8_t(255), uint8_t(255), uint8_t(255));
698         EXPECT_EQ(255u, p[0]);
699         EXPECT_EQ(255u, p[1]);
700         EXPECT_EQ(255u, p[2]);
701         EXPECT_EQ(255u, p[3]);
702         EXPECT_EQ(255u, p.r());
703         EXPECT_EQ(255u, p.g());
704         EXPECT_EQ(255u, p.b());
705         EXPECT_EQ(255u, p.a());
706         EXPECT_LT(nanovdb::Rgba8(), p);
707         EXPECT_NEAR(p.lengthSqr(), 3.0f, 1e-6);
708         EXPECT_NEAR(p.length(), sqrt(3.0f), 1e-6);
709     }
710     {
711         nanovdb::Rgba8 p(1.0f, 0.0f, 0.0f, 1.0f);
712         EXPECT_EQ(255u, p[0]);
713         EXPECT_EQ(0u,   p[1]);
714         EXPECT_EQ(0u,   p[2]);
715         EXPECT_EQ(255u, p[3]);
716         EXPECT_EQ(255u, p.r());
717         EXPECT_EQ(0u,   p.g());
718         EXPECT_EQ(0u,   p.b());
719         EXPECT_EQ(255u, p.a());
720         EXPECT_LT(nanovdb::Rgba8(), p);
721         EXPECT_NEAR(p.lengthSqr(), 1.0f, 1e-6);
722         EXPECT_NEAR(p.length(), 1.0f, 1e-6);
723     }
724     {
725         nanovdb::Rgba8 p(0.0f, 1.0f, 0.5f, 0.1f);
726         EXPECT_EQ(0u,   p[0]);
727         EXPECT_EQ(255u, p[1]);
728         EXPECT_EQ(128u, p[2]);
729         EXPECT_EQ(26u,  p[3]);
730         EXPECT_EQ(0u,   p.r());
731         EXPECT_EQ(255u, p.g());
732         EXPECT_EQ(128u, p.b());
733         EXPECT_EQ(26u,  p.a());
734         EXPECT_LT(nanovdb::Rgba8(), p);
735     }
736 }
737 
TEST_F(TestNanoVDB,Coord)738 TEST_F(TestNanoVDB, Coord)
739 {
740     EXPECT_EQ(size_t(3 * 4), nanovdb::Coord::memUsage()); // due to padding
741     {
742         nanovdb::Coord ijk;
743         EXPECT_EQ(sizeof(ijk), size_t(3 * 4));
744         EXPECT_EQ(0, ijk[0]);
745         EXPECT_EQ(0, ijk[1]);
746         EXPECT_EQ(0, ijk[2]);
747         EXPECT_EQ(0, ijk.x());
748         EXPECT_EQ(0, ijk.y());
749         EXPECT_EQ(0, ijk.z());
750     }
751     {
752         nanovdb::Coord ijk(1, 2, 3);
753         EXPECT_EQ(1, ijk[0]);
754         EXPECT_EQ(2, ijk[1]);
755         EXPECT_EQ(3, ijk[2]);
756         EXPECT_EQ(1, ijk.x());
757         EXPECT_EQ(2, ijk.y());
758         EXPECT_EQ(3, ijk.z());
759         ijk[1] = 4;
760         EXPECT_EQ(1, ijk[0]);
761         EXPECT_EQ(4, ijk[1]);
762         EXPECT_EQ(3, ijk[2]);
763         ijk.x() += -2;
764         EXPECT_EQ(-1, ijk[0]);
765         EXPECT_EQ(4, ijk[1]);
766         EXPECT_EQ(3, ijk[2]);
767     }
768     { // hash
769         EXPECT_EQ(0, nanovdb::Coord(1, 2, 3).octant());
770         EXPECT_EQ(0, nanovdb::Coord(1, 9, 3).octant());
771         EXPECT_EQ(1, nanovdb::Coord(-1, 2, 3).octant());
772         EXPECT_EQ(2, nanovdb::Coord(1, -2, 3).octant());
773         EXPECT_EQ(3, nanovdb::Coord(-1, -2, 3).octant());
774         EXPECT_EQ(4, nanovdb::Coord(1, 2, -3).octant());
775         EXPECT_EQ(5, nanovdb::Coord(-1, 2, -3).octant());
776         EXPECT_EQ(6, nanovdb::Coord(1, -2, -3).octant());
777         EXPECT_EQ(7, nanovdb::Coord(-1, -2, -3).octant());
778         for (int i = 0; i < 5; ++i)
779             EXPECT_EQ(i / 2, i >> 1);
780     }
781 }
782 
TEST_F(TestNanoVDB,BBox)783 TEST_F(TestNanoVDB, BBox)
784 {
785     nanovdb::BBox<nanovdb::Vec3f> bbox;
786     EXPECT_EQ(sizeof(bbox), size_t(2 * 3 * 4));
787     EXPECT_EQ(std::numeric_limits<float>::max(), bbox[0][0]);
788     EXPECT_EQ(std::numeric_limits<float>::max(), bbox[0][1]);
789     EXPECT_EQ(std::numeric_limits<float>::max(), bbox[0][2]);
790     EXPECT_EQ(-std::numeric_limits<float>::max(), bbox[1][0]);
791     EXPECT_EQ(-std::numeric_limits<float>::max(), bbox[1][1]);
792     EXPECT_EQ(-std::numeric_limits<float>::max(), bbox[1][2]);
793     EXPECT_TRUE(bbox.empty());
794 
795     bbox.expand(nanovdb::Vec3f(57.0f, -31.0f, 60.0f));
796     EXPECT_TRUE(bbox.empty());
797     EXPECT_EQ(nanovdb::Vec3f(0.0f), bbox.dim());
798     EXPECT_EQ(57.0f, bbox[0][0]);
799     EXPECT_EQ(-31.0f, bbox[0][1]);
800     EXPECT_EQ(60.0f, bbox[0][2]);
801     EXPECT_EQ(57.0f, bbox[1][0]);
802     EXPECT_EQ(-31.0f, bbox[1][1]);
803     EXPECT_EQ(60.0f, bbox[1][2]);
804 
805     bbox.expand(nanovdb::Vec3f(58.0f, 0.0f, 62.0f));
806     EXPECT_FALSE(bbox.empty());
807     EXPECT_EQ(nanovdb::Vec3f(1.0f, 31.0f, 2.0f), bbox.dim());
808     EXPECT_EQ(57.0f, bbox[0][0]);
809     EXPECT_EQ(-31.0f, bbox[0][1]);
810     EXPECT_EQ(60.0f, bbox[0][2]);
811     EXPECT_EQ(58.0f, bbox[1][0]);
812     EXPECT_EQ(0.0f, bbox[1][1]);
813     EXPECT_EQ(62.0f, bbox[1][2]);
814 }
815 
TEST_F(TestNanoVDB,CoordBBox)816 TEST_F(TestNanoVDB, CoordBBox)
817 {
818     nanovdb::CoordBBox bbox;
819     EXPECT_EQ(sizeof(bbox), size_t(2 * 3 * 4));
820     EXPECT_EQ(std::numeric_limits<int32_t>::max(), bbox[0][0]);
821     EXPECT_EQ(std::numeric_limits<int32_t>::max(), bbox[0][1]);
822     EXPECT_EQ(std::numeric_limits<int32_t>::max(), bbox[0][2]);
823     EXPECT_EQ(std::numeric_limits<int32_t>::min(), bbox[1][0]);
824     EXPECT_EQ(std::numeric_limits<int32_t>::min(), bbox[1][1]);
825     EXPECT_EQ(std::numeric_limits<int32_t>::min(), bbox[1][2]);
826     EXPECT_TRUE(bbox.empty());
827 
828     bbox.expand(nanovdb::Coord(57, -31, 60));
829     EXPECT_FALSE(bbox.empty());
830     EXPECT_EQ(nanovdb::Coord(1), bbox.dim());
831     EXPECT_EQ(57, bbox[0][0]);
832     EXPECT_EQ(-31, bbox[0][1]);
833     EXPECT_EQ(60, bbox[0][2]);
834     EXPECT_EQ(57, bbox[1][0]);
835     EXPECT_EQ(-31, bbox[1][1]);
836     EXPECT_EQ(60, bbox[1][2]);
837 
838     bbox.expand(nanovdb::Coord(58, 0, 62));
839     EXPECT_FALSE(bbox.empty());
840     EXPECT_EQ(nanovdb::Coord(2, 32, 3), bbox.dim());
841     EXPECT_EQ(57, bbox[0][0]);
842     EXPECT_EQ(-31, bbox[0][1]);
843     EXPECT_EQ(60, bbox[0][2]);
844     EXPECT_EQ(58, bbox[1][0]);
845     EXPECT_EQ(0, bbox[1][1]);
846     EXPECT_EQ(62, bbox[1][2]);
847 
848     { // test convert
849         auto bbox2 = bbox.asReal<float>();
850         EXPECT_FALSE(bbox2.empty());
851         EXPECT_EQ(nanovdb::Vec3f(57.0f, -31.0f, 60.0f), bbox2.min());
852         EXPECT_EQ(nanovdb::Vec3f(59.0f, 1.0f, 63.0f), bbox2.max());
853     }
854 
855     { // test prefix iterator
856         auto iter = bbox.begin();
857         EXPECT_TRUE(iter);
858         for (int i = bbox.min()[0]; i <= bbox.max()[0]; ++i) {
859             for (int j = bbox.min()[1]; j <= bbox.max()[1]; ++j) {
860                 for (int k = bbox.min()[2]; k <= bbox.max()[2]; ++k) {
861                     EXPECT_TRUE(bbox.isInside(*iter));
862                     EXPECT_TRUE(iter);
863                     const auto& ijk = *iter; // note, copy by reference
864                     EXPECT_EQ(ijk[0], i);
865                     EXPECT_EQ(ijk[1], j);
866                     EXPECT_EQ(ijk[2], k);
867                     ++iter;
868                 }
869             }
870         }
871         EXPECT_FALSE(iter);
872     }
873 
874     { // test postfix iterator
875         auto iter = bbox.begin();
876         EXPECT_TRUE(iter);
877         for (int i = bbox.min()[0]; i <= bbox.max()[0]; ++i) {
878             for (int j = bbox.min()[1]; j <= bbox.max()[1]; ++j) {
879                 for (int k = bbox.min()[2]; k <= bbox.max()[2]; ++k) {
880                     EXPECT_TRUE(iter);
881                     const auto ijk = *iter++; // note, copy by value!
882                     EXPECT_EQ(ijk[0], i);
883                     EXPECT_EQ(ijk[1], j);
884                     EXPECT_EQ(ijk[2], k);
885                 }
886             }
887         }
888         EXPECT_FALSE(iter);
889     }
890 }
891 
TEST_F(TestNanoVDB,Vec3)892 TEST_F(TestNanoVDB, Vec3)
893 {
894     bool test = nanovdb::is_specialization<double, nanovdb::Vec3>::value;
895     EXPECT_FALSE(test);
896     test = nanovdb::TensorTraits<double>::IsVector;
897     EXPECT_FALSE(test);
898     test = nanovdb::is_specialization<nanovdb::Vec3R, nanovdb::Vec3>::value;
899     EXPECT_TRUE(test);
900     test = nanovdb::is_same<double, nanovdb::Vec3R::ValueType>::value;
901     EXPECT_TRUE(test);
902     test = nanovdb::TensorTraits<nanovdb::Vec3R>::IsVector;
903     EXPECT_TRUE(test);
904     test = nanovdb::is_same<double, nanovdb::TensorTraits<nanovdb::Vec3R>::ElementType>::value;
905     EXPECT_TRUE(test);
906     test = nanovdb::is_same<double, nanovdb::FloatTraits<nanovdb::Vec3R>::FloatType>::value;
907     EXPECT_TRUE(test);
908     EXPECT_EQ(size_t(3 * 8), sizeof(nanovdb::Vec3R));
909 
910     nanovdb::Vec3R xyz(1.0, 2.0, 3.0);
911     EXPECT_EQ(1.0, xyz[0]);
912     EXPECT_EQ(2.0, xyz[1]);
913     EXPECT_EQ(3.0, xyz[2]);
914 
915     xyz[1] = -2.0;
916     EXPECT_EQ(1.0, xyz[0]);
917     EXPECT_EQ(-2.0, xyz[1]);
918     EXPECT_EQ(3.0, xyz[2]);
919 
920     EXPECT_EQ(1.0 + 4.0 + 9.0, xyz.lengthSqr());
921     EXPECT_EQ(sqrt(1.0 + 4.0 + 9.0), xyz.length());
922 
923     EXPECT_EQ(nanovdb::Vec3f(1, 2, 3), nanovdb::Vec3f(1, 2, 3));
924     EXPECT_NE(nanovdb::Vec3f(1, 2, 3), nanovdb::Vec3f(1, 2, 4));
925 
926     {// alignment to largest type
927         EXPECT_EQ(size_t(3 * 4), sizeof(nanovdb::Vec3f));
928         union {uint64_t a; nanovdb::Vec3f b;} c;
929         EXPECT_EQ(2 * sizeof(uint64_t), sizeof(c));
930         EXPECT_EQ(nanovdb::AlignUp<8>(sizeof(nanovdb::Vec3f)), sizeof(c));
931     }
932 }
933 
TEST_F(TestNanoVDB,Vec4)934 TEST_F(TestNanoVDB, Vec4)
935 {
936     bool test = nanovdb::is_specialization<double, nanovdb::Vec4>::value;
937     EXPECT_FALSE(test);
938     test = nanovdb::TensorTraits<double>::IsVector;
939     EXPECT_FALSE(test);
940     test = nanovdb::TensorTraits<double>::IsScalar;
941     EXPECT_TRUE(test);
942     int rank = nanovdb::TensorTraits<double>::Rank;
943     EXPECT_EQ(0, rank);
944     rank = nanovdb::TensorTraits<nanovdb::Vec3R>::Rank;
945     EXPECT_EQ(1, rank);
946     test = nanovdb::is_same<double, nanovdb::FloatTraits<float>::FloatType>::value;
947     EXPECT_FALSE(test);
948     test = nanovdb::is_same<double, nanovdb::FloatTraits<double>::FloatType>::value;
949     EXPECT_TRUE(test);
950     test = nanovdb::is_same<float, nanovdb::FloatTraits<uint32_t>::FloatType>::value;
951     EXPECT_TRUE(test);
952     test = nanovdb::is_same<double, nanovdb::FloatTraits<uint64_t>::FloatType>::value;
953     EXPECT_TRUE(test);
954     test = nanovdb::is_specialization<nanovdb::Vec4R, nanovdb::Vec4>::value;
955     EXPECT_TRUE(test);
956     test = nanovdb::is_specialization<nanovdb::Vec3R, nanovdb::Vec4>::value;
957     EXPECT_FALSE(test);
958     test = nanovdb::is_same<double, nanovdb::Vec4R::ValueType>::value;
959     EXPECT_TRUE(test);
960     test = nanovdb::TensorTraits<nanovdb::Vec3R>::IsVector;
961     EXPECT_TRUE(test);
962     test = nanovdb::is_same<double, nanovdb::TensorTraits<nanovdb::Vec4R>::ElementType>::value;
963     EXPECT_TRUE(test);
964     test = nanovdb::is_same<double, nanovdb::TensorTraits<double>::ElementType>::value;
965     EXPECT_TRUE(test);
966     test = nanovdb::is_same<float, nanovdb::TensorTraits<float>::ElementType>::value;
967     EXPECT_TRUE(test);
968     test = nanovdb::is_same<uint32_t, nanovdb::TensorTraits<uint32_t>::ElementType>::value;
969     EXPECT_TRUE(test);
970     test = nanovdb::is_same<double, nanovdb::FloatTraits<nanovdb::Vec4R>::FloatType>::value;
971     EXPECT_TRUE(test);
972     EXPECT_EQ(size_t(4 * 8), sizeof(nanovdb::Vec4R));
973 
974     nanovdb::Vec4R xyz(1.0, 2.0, 3.0, 4.0);
975     EXPECT_EQ(1.0, xyz[0]);
976     EXPECT_EQ(2.0, xyz[1]);
977     EXPECT_EQ(3.0, xyz[2]);
978     EXPECT_EQ(4.0, xyz[3]);
979 
980     xyz[1] = -2.0;
981     EXPECT_EQ(1.0, xyz[0]);
982     EXPECT_EQ(-2.0, xyz[1]);
983     EXPECT_EQ(3.0, xyz[2]);
984     EXPECT_EQ(4.0, xyz[3]);
985 
986     EXPECT_EQ(1.0 + 4.0 + 9.0 + 16.0, xyz.lengthSqr());
987     EXPECT_EQ(sqrt(1.0 + 4.0 + 9.0 + 16.0), xyz.length());
988 
989     EXPECT_EQ(nanovdb::Vec4f(1, 2, 3, 4), nanovdb::Vec4f(1, 2, 3, 4));
990     EXPECT_NE(nanovdb::Vec4f(1, 2, 3, 4), nanovdb::Vec4f(1, 2, 3, 5));
991 }// Vec4
992 
TEST_F(TestNanoVDB,Extrema)993 TEST_F(TestNanoVDB, Extrema)
994 {
995     { // int
996         nanovdb::Extrema<int> e(-1);
997         EXPECT_EQ(-1, e.min());
998         EXPECT_EQ(-1, e.max());
999         e.add(-2);
1000         e.add(5);
1001         EXPECT_TRUE(e);
1002         EXPECT_EQ(-2, e.min());
1003         EXPECT_EQ(5, e.max());
1004     }
1005     { // float
1006         nanovdb::Extrema<float> e(-1.0f);
1007         EXPECT_EQ(-1.0f, e.min());
1008         EXPECT_EQ(-1.0f, e.max());
1009         e.add(-2.0f);
1010         e.add(5.0f);
1011         EXPECT_TRUE(e);
1012         EXPECT_EQ(-2.0f, e.min());
1013         EXPECT_EQ(5.0f, e.max());
1014     }
1015     { // Vec3f
1016         nanovdb::Extrema<nanovdb::Vec3f> e(nanovdb::Vec3f(1.0f, 1.0f, 0.0f));
1017         EXPECT_EQ(nanovdb::Vec3f(1.0f, 1.0f, 0.0f), e.min());
1018         EXPECT_EQ(nanovdb::Vec3f(1.0f, 1.0f, 0.0f), e.max());
1019         e.add(nanovdb::Vec3f(1.0f, 0.0f, 0.0f));
1020         e.add(nanovdb::Vec3f(1.0f, 1.0f, 1.0f));
1021         EXPECT_TRUE(e);
1022         EXPECT_EQ(nanovdb::Vec3f(1.0f, 0.0f, 0.0f), e.min());
1023         EXPECT_EQ(nanovdb::Vec3f(1.0f, 1.0f, 1.0f), e.max());
1024     }
1025 }
1026 
TEST_F(TestNanoVDB,RayEmptyBBox)1027 TEST_F(TestNanoVDB, RayEmptyBBox)
1028 {
1029     using RealT = float;
1030     using Vec3T = nanovdb::Vec3<RealT>;
1031     using CoordT = nanovdb::Coord;
1032     using CoordBBoxT = nanovdb::BBox<CoordT>;
1033     using BBoxT = nanovdb::BBox<Vec3T>;
1034     using RayT = nanovdb::Ray<RealT>;
1035 
1036     // test bbox clip
1037     const Vec3T dir(1.0, 0.0, 0.0);
1038     const Vec3T eye(-1.0, 0.5, 0.5);
1039     RealT       t0 = 0.0, t1 = 10000.0;
1040     RayT        ray(eye, dir, t0, t1);
1041 
1042     const CoordBBoxT bbox1;
1043     EXPECT_TRUE(bbox1.empty());
1044     EXPECT_FALSE(ray.intersects(bbox1, t0, t1));
1045 
1046     const BBoxT bbox2;
1047     EXPECT_TRUE(bbox2.empty());
1048     EXPECT_FALSE(ray.intersects(bbox2, t0, t1));
1049 }
1050 
TEST_F(TestNanoVDB,RayBasic)1051 TEST_F(TestNanoVDB, RayBasic)
1052 {
1053     using RealT = float;
1054     using Vec3T = nanovdb::Vec3<RealT>;
1055     using CoordT = nanovdb::Coord;
1056     using CoordBBoxT = nanovdb::BBox<CoordT>;
1057     using BBoxT = nanovdb::BBox<Vec3T>;
1058     using RayT = nanovdb::Ray<RealT>;
1059 
1060     // test bbox clip
1061     const Vec3T dir(1.0, 0.0, 0.0);
1062     const Vec3T eye(-1.0, 0.5, 0.5);
1063     RealT       t0 = 0.0, t1 = 10000.0;
1064     RayT        ray(eye, dir, t0, t1);
1065 
1066     const CoordBBoxT bbox(CoordT(0, 0, 0), CoordT(0, 0, 0)); // only contains a single point (0,0,0)
1067     EXPECT_FALSE(bbox.empty());
1068     EXPECT_EQ(bbox.dim(), CoordT(1, 1, 1));
1069 
1070     const BBoxT bbox2(CoordT(0, 0, 0), CoordT(0, 0, 0));
1071     EXPECT_EQ(bbox2, bbox.asReal<float>());
1072     EXPECT_FALSE(bbox2.empty());
1073     EXPECT_EQ(bbox2.dim(), Vec3T(1.0f, 1.0f, 1.0f));
1074     EXPECT_EQ(bbox2[0], Vec3T(0.0f, 0.0f, 0.0f));
1075     EXPECT_EQ(bbox2[1], Vec3T(1.0f, 1.0f, 1.0f));
1076 
1077     EXPECT_TRUE(ray.clip(bbox)); // ERROR: how can a non-empty bbox have no intersections!?
1078     //EXPECT_TRUE( ray.clip(bbox.asReal<float>()));// correct!
1079 
1080     // intersects the two faces of the box perpendicular to the x-axis!
1081     EXPECT_EQ(1.0f, ray.t0());
1082     EXPECT_EQ(2.0f, ray.t1());
1083     EXPECT_EQ(ray(1.0f), Vec3T(0.0f, 0.5f, 0.5f)); //lower y component of intersection
1084     EXPECT_EQ(ray(2.0f), Vec3T(1.0f, 0.5f, 0.5f)); //higher y component of intersection
1085 } // RayBasic
1086 
TEST_F(TestNanoVDB,Ray)1087 TEST_F(TestNanoVDB, Ray)
1088 {
1089     using RealT = float;
1090     using Vec3T = nanovdb::Vec3<RealT>;
1091     using CoordT = nanovdb::Coord;
1092     using CoordBBoxT = nanovdb::BBox<CoordT>;
1093     using BBoxT = nanovdb::BBox<Vec3T>;
1094     using RayT = nanovdb::Ray<RealT>;
1095 
1096     // test bbox clip
1097     const Vec3T dir(-1.0, 2.0, 3.0);
1098     const Vec3T eye(2.0, 1.0, 1.0);
1099     RealT       t0 = 0.1, t1 = 12589.0;
1100     RayT        ray(eye, dir, t0, t1);
1101 
1102     // intersects the two faces of the box perpendicular to the y-axis!
1103     EXPECT_TRUE(ray.clip(CoordBBoxT(CoordT(0, 2, 2), CoordT(2, 4, 6))));
1104     //std::cerr << ray(0.5) << ", " << ray(2.0) << std::endl;
1105     EXPECT_EQ(0.5, ray.t0());
1106     EXPECT_EQ(2.0, ray.t1());
1107     EXPECT_EQ(ray(0.5)[1], 2); //lower y component of intersection
1108     EXPECT_EQ(ray(2.0)[1], 5); //higher y component of intersection
1109 
1110     ray.reset(eye, dir, t0, t1);
1111     // intersects the lower edge anlong the z-axis of the box
1112     EXPECT_TRUE(ray.clip(BBoxT(Vec3T(1.5, 2.0, 2.0), Vec3T(4.5, 4.0, 6.0))));
1113     //std::cerr << ray(0.5) << ", " << ray(2.0) << std::endl;
1114     EXPECT_EQ(0.5, ray.t0());
1115     EXPECT_EQ(0.5, ray.t1());
1116     EXPECT_EQ(ray(0.5)[0], 1.5); //lower y component of intersection
1117     EXPECT_EQ(ray(0.5)[1], 2.0); //higher y component of intersection
1118 
1119     ray.reset(eye, dir, t0, t1);
1120     // no intersections
1121     EXPECT_TRUE(!ray.clip(CoordBBoxT(CoordT(4, 2, 2), CoordT(6, 4, 6))));
1122     EXPECT_EQ(t0, ray.t0());
1123     EXPECT_EQ(t1, ray.t1());
1124 }
1125 
TEST_F(TestNanoVDB,HDDA)1126 TEST_F(TestNanoVDB, HDDA)
1127 {
1128     using RealT = float;
1129     using CoordT = nanovdb::Coord;
1130     using RayT = nanovdb::Ray<RealT>;
1131     using Vec3T = RayT::Vec3T;
1132     using DDAT = nanovdb::HDDA<RayT, CoordT>;
1133 
1134     { // basic test
1135         const RayT::Vec3T dir(1.0, 0.0, 0.0);
1136         const RayT::Vec3T eye(-1.0, 0.0, 0.0);
1137         const RayT        ray(eye, dir);
1138         DDAT              dda(ray, 1 << (3 + 4 + 5));
1139         EXPECT_EQ(nanovdb::Delta<RealT>::value(), dda.time());
1140         EXPECT_EQ(1.0, dda.next());
1141         dda.step();
1142         EXPECT_EQ(1.0, dda.time());
1143         EXPECT_EQ(4096 + 1.0, dda.next());
1144     }
1145     { // Check for the notorious +-0 issue!
1146 
1147         const Vec3T dir1(1.0, 0.0, 0.0);
1148         const Vec3T eye1(2.0, 0.0, 0.0);
1149         const RayT  ray1(eye1, dir1);
1150         DDAT        dda1(ray1, 1 << 3);
1151         dda1.step();
1152 
1153         const Vec3T dir2(1.0, -0.0, -0.0);
1154         const Vec3T eye2(2.0, 0.0, 0.0);
1155         const RayT  ray2(eye2, dir2);
1156         DDAT        dda2(ray2, 1 << 3);
1157         dda2.step();
1158 
1159         const Vec3T dir3(1.0, -1e-9, -1e-9);
1160         const Vec3T eye3(2.0, 0.0, 0.0);
1161         const RayT  ray3(eye3, dir3);
1162         DDAT        dda3(ray3, 1 << 3);
1163         dda3.step();
1164 
1165         const Vec3T dir4(1.0, -1e-9, -1e-9);
1166         const Vec3T eye4(2.0, 0.0, 0.0);
1167         const RayT  ray4(eye3, dir4);
1168         DDAT        dda4(ray4, 1 << 3);
1169         dda4.step();
1170 
1171         EXPECT_EQ(dda1.time(), dda2.time());
1172         EXPECT_EQ(dda2.time(), dda3.time());
1173         EXPECT_EQ(dda3.time(), dda4.time());
1174         EXPECT_EQ(dda1.next(), dda2.next());
1175         EXPECT_EQ(dda2.next(), dda3.next());
1176         EXPECT_EQ(dda3.next(), dda4.next());
1177     }
1178     { // test voxel traversal along both directions of each axis
1179         const Vec3T eye(0, 0, 0);
1180         for (int s = -1; s <= 1; s += 2) {
1181             for (int a = 0; a < 3; ++a) {
1182                 const int   d[3] = {s * (a == 0), s * (a == 1), s * (a == 2)};
1183                 const Vec3T dir(d[0], d[1], d[2]);
1184                 RayT        ray(eye, dir);
1185                 DDAT        dda(ray, 1 << 0);
1186                 for (int i = 1; i <= 10; ++i) {
1187                     EXPECT_TRUE(dda.step());
1188                     EXPECT_EQ(i, dda.time());
1189                 }
1190             }
1191         }
1192     }
1193     { // test Node traversal along both directions of each axis
1194         const Vec3T eye(0, 0, 0);
1195 
1196         for (int s = -1; s <= 1; s += 2) {
1197             for (int a = 0; a < 3; ++a) {
1198                 const int   d[3] = {s * (a == 0), s * (a == 1), s * (a == 2)};
1199                 const Vec3T dir(d[0], d[1], d[2]);
1200                 RayT        ray(eye, dir);
1201                 DDAT        dda(ray, 1 << 3);
1202                 for (int i = 1; i <= 10; ++i) {
1203                     EXPECT_TRUE(dda.step());
1204                     EXPECT_EQ(8 * i, dda.time());
1205                 }
1206             }
1207         }
1208     }
1209     { // test accelerated Node traversal along both directions of each axis
1210         const Vec3T eye(0, 0, 0);
1211 
1212         for (int s = -1; s <= 1; s += 2) {
1213             for (int a = 0; a < 3; ++a) {
1214                 const int   d[3] = {s * (a == 0), s * (a == 1), s * (a == 2)};
1215                 const Vec3T dir(2.0f * d[0], 2.0f * d[1], 2.0f * d[2]);
1216                 RayT        ray(eye, dir);
1217                 DDAT        dda(ray, 1 << 3);
1218                 double      next = 0;
1219                 for (int i = 1; i <= 10; ++i) {
1220                     EXPECT_TRUE(dda.step());
1221                     EXPECT_EQ(4 * i, dda.time());
1222                     if (i > 1) {
1223                         EXPECT_EQ(dda.time(), next);
1224                     }
1225                     next = dda.next();
1226                 }
1227             }
1228         }
1229     }
1230 #if 0
1231   if (!this->getEnvVar( "VDB_DATA_PATH" ).empty()) {// bug when ray-tracing dragon model
1232     const Vec3T eye(1563.350342,-390.161621,697.749023);
1233     const Vec3T dir(-0.963871,0.048393,-0.626651);
1234     RayT ray( eye, dir );
1235     auto srcGrid = this->getSrcGrid();
1236     auto handle = nanovdb::openToNanoVDB( *srcGrid );
1237     EXPECT_TRUE( handle );
1238     auto *grid = handle.grid<float>();
1239     EXPECT_TRUE( grid );
1240     EXPECT_TRUE( grid->isLevelSet() );
1241     //ray = ray.applyMapF( map );
1242     auto acc = grid->getAccessor();
1243     CoordT ijk;
1244     float v0;
1245     EXPECT_TRUE(nanovdb::ZeroCrossing( ray, acc, ijk, v0 ) );
1246     std::cerr << "hit with v0 =" << v0 << " background = " << grid->tree().background() << std::endl;
1247   }
1248 #endif
1249 } // HDDA
1250 
TEST_F(TestNanoVDB,Mask)1251 TEST_F(TestNanoVDB, Mask)
1252 {
1253     using MaskT = nanovdb::Mask<3>;
1254     EXPECT_EQ(8u, MaskT::wordCount());
1255     EXPECT_EQ(512u, MaskT::bitCount());
1256     EXPECT_EQ(size_t(8 * 8), MaskT::memUsage());
1257 
1258     MaskT mask;
1259     EXPECT_EQ(0U, mask.countOn());
1260     EXPECT_TRUE(mask.isOff());
1261     EXPECT_FALSE(mask.isOn());
1262     EXPECT_FALSE(mask.beginOn());
1263     for (uint32_t i = 0; i < MaskT::bitCount(); ++i)
1264         EXPECT_FALSE(mask.isOn(i));
1265 
1266     mask.setOn(256);
1267     EXPECT_FALSE(mask.isOff());
1268     EXPECT_FALSE(mask.isOn());
1269     auto iter = mask.beginOn();
1270     EXPECT_TRUE(iter);
1271     EXPECT_EQ(256U, *iter);
1272     EXPECT_FALSE(++iter);
1273     for (uint32_t i = 0; i < MaskT::bitCount(); ++i) {
1274         if (i != 256)
1275             EXPECT_FALSE(mask.isOn(i));
1276         else
1277             EXPECT_TRUE(mask.isOn(i));
1278     }
1279 
1280     mask.set(256, false);
1281     EXPECT_TRUE(mask.isOff());
1282     EXPECT_FALSE(mask.isOn());
1283     EXPECT_FALSE(mask.isOn(256));
1284 
1285     mask.set(256, true);
1286     EXPECT_FALSE(mask.isOff());
1287     EXPECT_FALSE(mask.isOn());
1288     EXPECT_TRUE(mask.isOn(256));
1289 }
1290 
TEST_F(TestNanoVDB,LeafNode)1291 TEST_F(TestNanoVDB, LeafNode)
1292 {
1293     using LeafT = nanovdb::LeafNode<float>;
1294     //EXPECT_FALSE(LeafT::IgnoreValues);
1295     EXPECT_EQ(8u, LeafT::dim());
1296     EXPECT_EQ(512u, LeafT::voxelCount());
1297     EXPECT_EQ(size_t(
1298                   3 * 4 + // mBBoxMin
1299                   4 * 1 + // mBBoxDif[3] + mFlags
1300                   8 * 8 + // mValueMask,
1301                   2 * 4 + // mMinimum, mMaximum
1302                   2 * 4 + // mAverage, mVariance
1303                   512 * 4 // mValues[512]
1304                   ),
1305               sizeof(LeafT));
1306     // this particular value type happens to be exactly 32B aligned!
1307     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(
1308                   3 * 4 + // mBBoxMin
1309                   4 * 1 + // mBBoxDif[3] + mFlags
1310                   8 * 8 + // mValueMask,
1311                   2 * 4 + // mMinimum, mMaximum
1312                   2 * 4 + // mAverage, mVariance
1313                   512 * 4 // mValues[512]
1314                   ),
1315               sizeof(LeafT));
1316 
1317     // allocate buffer
1318     std::unique_ptr<uint8_t[]> buffer(new uint8_t[LeafT::memUsage()]);
1319     std::memset(buffer.get(), 0, LeafT::memUsage());
1320     LeafT*                     leaf = reinterpret_cast<LeafT*>(buffer.get());
1321 
1322     { // set members of the leaf node
1323         auto& data = *reinterpret_cast<LeafT::DataType*>(buffer.get());
1324         data.mValueMask.setOff();
1325         auto* values = data.mValues;
1326         for (int i = 0; i < 256; ++i)
1327             *values++ = 0.0f;
1328         for (uint32_t i = 256; i < LeafT::voxelCount(); ++i) {
1329             data.mValueMask.setOn(i);
1330             *values++ = 1.234f;
1331         }
1332         data.mMinimum = 0.0f;
1333         data.mMaximum = 1.234f;
1334         data.mFlags = uint8_t(2);// set bit # 1 on since leaf contains active values
1335     }
1336 
1337     EXPECT_TRUE( leaf->isActive() );
1338 
1339     { // compute BBox
1340         auto& data = *reinterpret_cast<LeafT::DataType*>(buffer.get());
1341         EXPECT_EQ(8u, data.mValueMask.wordCount());
1342 
1343         nanovdb::CoordBBox bbox(nanovdb::Coord(-1), nanovdb::Coord(-1));
1344         uint64_t           word = 0u;
1345         for (int i = 0; i < 8; ++i) {
1346             if (uint64_t w = data.mValueMask.getWord<uint64_t>(i)) {
1347                 word |= w;
1348                 if (bbox[0][0] == -1)
1349                     bbox[0][0] = i;
1350                 bbox[1][0] = i;
1351             }
1352         }
1353         EXPECT_TRUE(word != 0u);
1354         bbox[0][1] = nanovdb::FindLowestOn(word) >> 3;
1355         bbox[1][1] = nanovdb::FindHighestOn(word) >> 3;
1356 
1357         const uint8_t* p = reinterpret_cast<const uint8_t*>(&word);
1358         uint32_t       b = p[0] | p[1] | p[2] | p[3] | p[4] | p[5] | p[6] | p[7];
1359         EXPECT_TRUE(b != 0u);
1360         bbox[0][2] = nanovdb::FindLowestOn(b);
1361         bbox[1][2] = nanovdb::FindHighestOn(b);
1362         //std::cerr << bbox << std::endl;
1363         EXPECT_EQ(bbox[0], nanovdb::Coord(4, 0, 0));
1364         EXPECT_EQ(bbox[1], nanovdb::Coord(7, 7, 7));
1365     }
1366 
1367     EXPECT_TRUE( leaf->isActive() );
1368 
1369     // check values
1370     auto* ptr = reinterpret_cast<LeafT::DataType*>(buffer.get())->mValues;
1371     for (uint32_t i = 0; i < LeafT::voxelCount(); ++i) {
1372         if (i < 256) {
1373             EXPECT_FALSE(leaf->valueMask().isOn(i));
1374             EXPECT_EQ(0.0f, *ptr++);
1375         } else {
1376             EXPECT_TRUE(leaf->valueMask().isOn(i));
1377             EXPECT_EQ(1.234f, *ptr++);
1378         }
1379     }
1380     EXPECT_EQ(0.0f, leaf->minimum());
1381     EXPECT_EQ(1.234f, leaf->maximum());
1382 
1383     { // test stand-alone implementation
1384         auto localBBox = [](const LeafT* leaf) {
1385             // static_assert(8u == LeafT::dim(), "Expected dim = 8");
1386             nanovdb::CoordBBox bbox(nanovdb::Coord(-1, 0, 0), nanovdb::Coord(-1, 7, 7));
1387             uint64_t           word64 = 0u;
1388             for (int i = 0; i < 8; ++i) {
1389                 if (uint64_t w = leaf->valueMask().getWord<uint64_t>(i)) {
1390                     word64 |= w;
1391                     if (bbox[0][0] == -1)
1392                         bbox[0][0] = i; // only set once
1393                     bbox[1][0] = i;
1394                 }
1395             }
1396             assert(word64);
1397             if (word64 == ~uint64_t(0))
1398                 return bbox; // early out of dense leaf
1399             bbox[0][1] = nanovdb::FindLowestOn(word64) >> 3;
1400             bbox[1][1] = nanovdb::FindHighestOn(word64) >> 3;
1401             const uint32_t *p = reinterpret_cast<const uint32_t*>(&word64), word32 = p[0] | p[1];
1402             const uint16_t *q = reinterpret_cast<const uint16_t*>(&word32), word16 = q[0] | q[1];
1403             const uint8_t * b = reinterpret_cast<const uint8_t*>(&word16), byte = b[0] | b[1];
1404             assert(byte);
1405             bbox[0][2] = nanovdb::FindLowestOn(uint32_t(byte));
1406             bbox[1][2] = nanovdb::FindHighestOn(uint32_t(byte));
1407             return bbox;
1408         }; // bboxOp
1409 
1410         // test
1411         leaf->data()->mValueMask.setOff();
1412         const nanovdb::Coord min(1, 2, 3), max(5, 6, 7);
1413         leaf->setValue(min, 1.0f);
1414         leaf->setValue(max, 2.0f);
1415         EXPECT_EQ(1.0f, leaf->getValue(min));
1416         EXPECT_EQ(2.0f, leaf->getValue(max));
1417         const auto bbox = localBBox(leaf);
1418         //std::cerr << "bbox = " << bbox << std::endl;
1419         EXPECT_EQ(bbox[0], min);
1420         EXPECT_EQ(bbox[1], max);
1421     }
1422 
1423     { // test LeafNode::updateBBox
1424         leaf->data()->mValueMask.setOff();
1425         const nanovdb::Coord min(1, 2, 3), max(5, 6, 7);
1426         leaf->setValue(min, 1.0f);
1427         leaf->setValue(max, 2.0f);
1428         EXPECT_EQ(1.0f, leaf->getValue(min));
1429         EXPECT_EQ(2.0f, leaf->getValue(max));
1430         leaf->updateBBox();
1431         const auto bbox = leaf->bbox();
1432         //std::cerr << "bbox = " << bbox << std::endl;
1433         EXPECT_EQ(bbox[0], min);
1434         EXPECT_EQ(bbox[1], max);
1435     }
1436 
1437 } // LeafNode
1438 
TEST_F(TestNanoVDB,LeafNodeBool)1439 TEST_F(TestNanoVDB, LeafNodeBool)
1440 {
1441     using LeafT = nanovdb::LeafNode<bool>;
1442     EXPECT_EQ(8u, LeafT::dim());
1443     EXPECT_EQ(512u, LeafT::voxelCount());
1444     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(8 * 8 + // mValueMask
1445                                                        8 * 8 + // mMask
1446                                                        3 * 4 + // mBBoxMin
1447                                                        4 * 1), // mBBoxDif[3] + mFlags
1448               sizeof(LeafT));
1449 
1450     // allocate buffer
1451     std::unique_ptr<uint8_t[]> buffer(new uint8_t[LeafT::memUsage()]);
1452     LeafT*                     leaf = reinterpret_cast<LeafT*>(buffer.get());
1453 
1454     { // set members of the leaf node
1455         auto& data = *reinterpret_cast<LeafT::DataType*>(buffer.get());
1456         data.mValueMask.setOff();
1457         data.mValues.setOn();
1458 
1459         for (uint32_t i = 256; i < LeafT::voxelCount(); ++i) {
1460             data.mValueMask.setOn(i);
1461             data.mValues.setOff(i);
1462         }
1463     }
1464 
1465     // check values
1466     for (uint32_t i = 0; i < LeafT::voxelCount(); ++i) {
1467         if (i < 256) {
1468             EXPECT_FALSE(leaf->valueMask().isOn(i));
1469             EXPECT_TRUE(leaf->getValue(i));
1470         } else {
1471             EXPECT_TRUE(leaf->valueMask().isOn(i));
1472             EXPECT_FALSE(leaf->getValue(i));
1473         }
1474     }
1475     EXPECT_EQ(false, leaf->minimum());
1476     EXPECT_EQ(false, leaf->maximum());
1477 } // LeafNodeBool
1478 
TEST_F(TestNanoVDB,LeafNodeValueMask)1479 TEST_F(TestNanoVDB, LeafNodeValueMask)
1480 {
1481     using LeafT = nanovdb::LeafNode<nanovdb::ValueMask>;
1482     //EXPECT_TRUE(LeafT::IgnoreValues);
1483     EXPECT_EQ(8u, LeafT::dim());
1484     EXPECT_EQ(512u, LeafT::voxelCount());
1485     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(8 * 8 + // mValueMask
1486                                                        3 * 4 + // mBBoxMin
1487                                                        4 * 1), // mBBoxDif[3] + mFlags
1488               sizeof(LeafT));
1489 
1490     // allocate buffer
1491     std::unique_ptr<uint8_t[]> buffer(new uint8_t[LeafT::memUsage()]);
1492     LeafT*                     leaf = reinterpret_cast<LeafT*>(buffer.get());
1493 
1494     { // set members of the leaf node
1495         auto& data = *reinterpret_cast<LeafT::DataType*>(buffer.get());
1496         data.mValueMask.setOff();
1497 
1498         for (uint32_t i = 256; i < LeafT::voxelCount(); ++i) {
1499             data.mValueMask.setOn(i);
1500         }
1501     }
1502 
1503     // check values
1504     for (uint32_t i = 0; i < LeafT::voxelCount(); ++i) {
1505         if (i < 256) {
1506             EXPECT_FALSE(leaf->valueMask().isOn(i));
1507             EXPECT_FALSE(leaf->getValue(i));
1508         } else {
1509             EXPECT_TRUE(leaf->valueMask().isOn(i));
1510             EXPECT_TRUE(leaf->getValue(i));
1511         }
1512     }
1513     EXPECT_EQ(false, leaf->minimum());
1514     EXPECT_EQ(false, leaf->maximum());
1515 } // LeafNodeValueMask
1516 
TEST_F(TestNanoVDB,InternalNode)1517 TEST_F(TestNanoVDB, InternalNode)
1518 {
1519     using LeafT = nanovdb::LeafNode<float>;
1520     using NodeT = nanovdb::InternalNode<LeafT>;
1521     EXPECT_EQ(8 * 16u, NodeT::dim());
1522     //         2 x bit-masks         tiles    Vmin&Vmax offset + bbox + padding
1523     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(size_t(2 * (16 * 16 * 16 / 64) * 8 + 16 * 16 * 16 * 8 + 2 * 4 + 4 + 2 * 3 * 4 + 4)), NodeT::memUsage());
1524 
1525     // an empty InternalNode
1526     std::unique_ptr<uint8_t[]> buffer(new uint8_t[NodeT::memUsage()]);
1527     NodeT*                     node = reinterpret_cast<NodeT*>(buffer.get());
1528 
1529     { // set members of the node
1530         auto& data = *reinterpret_cast<NodeT::DataType*>(buffer.get());
1531         data.mValueMask.setOff();
1532         data.mChildMask.setOff();
1533         auto* tiles = data.mTable;
1534         for (uint32_t i = 0; i < NodeT::SIZE / 2; ++i, ++tiles)
1535             tiles->value = 0.0f;
1536         for (uint32_t i = NodeT::SIZE / 2; i < NodeT::SIZE; ++i, ++tiles) {
1537             data.mValueMask.setOn(i);
1538             tiles->value = 1.234f;
1539         }
1540         data.mMinimum = 0.0f;
1541         data.mMaximum = 1.234f;
1542     }
1543 
1544     // check values
1545     auto* ptr = reinterpret_cast<NodeT::DataType*>(buffer.get())->mTable;
1546     for (uint32_t i = 0; i < NodeT::SIZE; ++i, ++ptr) {
1547         EXPECT_FALSE(node->childMask().isOn(i));
1548         if (i < NodeT::SIZE / 2) {
1549             EXPECT_FALSE(node->valueMask().isOn(i));
1550             EXPECT_EQ(0.0f, ptr->value);
1551         } else {
1552             EXPECT_TRUE(node->valueMask().isOn(i));
1553             EXPECT_EQ(1.234f, ptr->value);
1554         }
1555     }
1556     EXPECT_EQ(0.0f, node->minimum());
1557     EXPECT_EQ(1.234f, node->maximum());
1558 } //  InternalNode
1559 
TEST_F(TestNanoVDB,InternalNodeValueMask)1560 TEST_F(TestNanoVDB, InternalNodeValueMask)
1561 {
1562     using LeafT = nanovdb::LeafNode<nanovdb::ValueMask>;
1563     using NodeT = nanovdb::InternalNode<LeafT>;
1564     //EXPECT_TRUE(LeafT::IgnoreValues);
1565     //EXPECT_TRUE(NodeT::IgnoreValues);
1566     EXPECT_EQ(8 * 16u, NodeT::dim());
1567 
1568     /*
1569     BBox<CoordT> mBBox; // 24B. node bounding box.
1570     uint64_t     mFlags; // 8B. node flags.
1571     MaskT        mValueMask; // LOG2DIM(5): 4096B, LOG2DIM(4): 512B
1572     MaskT        mChildMask; // LOG2DIM(5): 4096B, LOG2DIM(4): 512B
1573 
1574     ValueT mMinimum;
1575     ValueT mMaximum;
1576     alignas(32) Tile mTable[1u << (3 * LOG2DIM)];
1577     */
1578     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(size_t(24 + 4 + 4 + 512 + 512 + 4 + 4 + (16 * 16 * 16) * 8)), NodeT::memUsage());
1579 
1580     // an empty InternalNode
1581     std::unique_ptr<uint8_t[]> buffer(new uint8_t[NodeT::memUsage()]);
1582     NodeT*                     node = reinterpret_cast<NodeT*>(buffer.get());
1583 
1584     { // set members of the node
1585         auto& data = *reinterpret_cast<NodeT::DataType*>(buffer.get());
1586         data.mValueMask.setOff();
1587         data.mChildMask.setOff();
1588         auto* tiles = data.mTable;
1589         for (uint32_t i = 0; i < NodeT::SIZE / 2; ++i, ++tiles)
1590             tiles->value = 0u;
1591         for (uint32_t i = NodeT::SIZE / 2; i < NodeT::SIZE; ++i, ++tiles) {
1592             data.mValueMask.setOn(i);
1593             tiles->value = 1u;
1594         }
1595         data.mMinimum = 0u;
1596         data.mMaximum = 1u;
1597     }
1598 
1599     // check values
1600     auto* ptr = reinterpret_cast<NodeT::DataType*>(buffer.get())->mTable;
1601     for (uint32_t i = 0; i < NodeT::SIZE; ++i, ++ptr) {
1602         EXPECT_FALSE(node->childMask().isOn(i));
1603         if (i < NodeT::SIZE / 2) {
1604             EXPECT_FALSE(node->valueMask().isOn(i));
1605             EXPECT_EQ(0u, ptr->value);
1606         } else {
1607             EXPECT_TRUE(node->valueMask().isOn(i));
1608             EXPECT_EQ(1u, ptr->value);
1609         }
1610     }
1611     EXPECT_EQ(0u, node->minimum());
1612     EXPECT_EQ(1u, node->maximum());
1613 } //  InternalNodeValueMask
1614 
TEST_F(TestNanoVDB,RootNode)1615 TEST_F(TestNanoVDB, RootNode)
1616 {
1617     using NodeT0 = nanovdb::LeafNode<float>;
1618     using NodeT1 = nanovdb::InternalNode<NodeT0>;
1619     using NodeT2 = nanovdb::InternalNode<NodeT1>;
1620     using NodeT3 = nanovdb::RootNode<NodeT2>;
1621     using CoordT = NodeT3::CoordType;
1622     using KeyT   = NodeT3::DataType::KeyT;
1623 
1624     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(sizeof(nanovdb::CoordBBox) + sizeof(uint32_t) + (5 * sizeof(float))), NodeT3::memUsage(0));
1625 
1626     // an empty RootNode
1627     std::unique_ptr<uint8_t[]> buffer(new uint8_t[NodeT3::memUsage(0)]);
1628     NodeT3*                    root = reinterpret_cast<NodeT3*>(buffer.get());
1629 
1630     { // set members of the node
1631         auto& data = *reinterpret_cast<NodeT3::DataType*>(buffer.get());
1632         data.mBackground = data.mMinimum = data.mMaximum = 1.234f;
1633         data.mTableSize = 0;
1634     }
1635 
1636     EXPECT_EQ(1.234f, root->background());
1637     EXPECT_EQ(1.234f, root->minimum());
1638     EXPECT_EQ(1.234f, root->maximum());
1639     EXPECT_EQ(0u, root->tileCount());
1640     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(sizeof(nanovdb::CoordBBox) + sizeof(uint32_t) + (5 * sizeof(float))), root->memUsage()); // background, min, max, tileCount + bbox
1641     EXPECT_EQ(1.234f, root->getValue(CoordT(1, 2, 3)));
1642 
1643     { // test RootData::CoordToKey and RotData::KetToCoord
1644         const int dim = NodeT2::DIM;// dimension of the root's child nodes
1645         EXPECT_EQ(4096, dim);
1646         auto coordToKey = [](int i, int j, int k) { return NodeT3::DataType::CoordToKey(CoordT(i, j, k)); };
1647         auto keyToCoord = [](KeyT key) { return NodeT3::DataType::KeyToCoord(key); };
1648         EXPECT_TRUE(coordToKey(0, 0, 0) <  coordToKey(dim, 0, 0));
1649         EXPECT_TRUE(coordToKey(0, 0, 0) <  coordToKey(0, dim, 0));
1650         EXPECT_TRUE(coordToKey(0, 0, 0) <  coordToKey(0, 0, dim));
1651         EXPECT_TRUE(coordToKey(0, 0, 0) == coordToKey(dim-1, dim-1, dim-1));
1652 #ifdef USE_SINGLE_ROOT_KEY
1653         EXPECT_TRUE((std::is_same<uint64_t, KeyT>::value));
1654 
1655         EXPECT_EQ(uint64_t(0), coordToKey(0, 0, 0));
1656         EXPECT_EQ(uint64_t(0), coordToKey(dim-1, dim-1, dim-1));
1657 
1658         EXPECT_EQ(uint64_t(1), coordToKey(0, 0, dim));
1659         EXPECT_EQ(uint64_t(1), coordToKey(dim-1, dim-1, dim));
1660 
1661         EXPECT_EQ(uint64_t(1)<<21, coordToKey(0, dim, 0));
1662         EXPECT_EQ(uint64_t(1)<<21, coordToKey(dim-1, dim, dim-1));
1663 
1664         EXPECT_EQ(uint64_t(1)<<42, coordToKey(dim, 0, 0));
1665         EXPECT_EQ(uint64_t(1)<<42, coordToKey(dim, dim-1, dim-1));
1666 
1667         EXPECT_EQ(CoordT(0,0,0), keyToCoord(0u));
1668         //std::cerr << "keyToCoord(1u) = " << keyToCoord(1u) << std::endl;
1669         EXPECT_EQ(CoordT(0, 0, dim), keyToCoord(1u));
1670         EXPECT_EQ(CoordT(0, dim, 0), keyToCoord(uint64_t(1)<<21));
1671         EXPECT_EQ(CoordT(dim, 0, 0), keyToCoord(uint64_t(1)<<42));
1672 #endif
1673     }
1674 } // RootNode
1675 
TEST_F(TestNanoVDB,Offsets)1676 TEST_F(TestNanoVDB, Offsets)
1677 {
1678     { // check GridData memory alignment, total 672 bytes
1679     /*
1680         static const int MaxNameSize = 256;// due to NULL termination the maximum length is one less
1681         uint64_t         mMagic; // 8B magic to validate it is valid grid data.
1682         uint64_t         mChecksum; // 8B. Checksum of grid buffer.
1683         Version          mVersion;// 4B major, minor, and patch version numbers
1684         uint32_t         mFlags; // 4B. flags for grid.
1685         uint32_t         mGridIndex;// 4B. Index of this grid in the buffer
1686         uint32_t         mGridCount; // 4B. Total number of grids in the buffer
1687         uint64_t         mGridSize; // 8B. byte count of this entire grid occupied in the buffer.
1688         char             mGridName[MaxNameSize]; // 256B
1689         Map              mMap; // 264B. affine transformation between index and world space in both single and double precision
1690         BBox<Vec3R>      mWorldBBox; // 48B. floating-point AABB of active values in WORLD SPACE (2 x 3 doubles)
1691         Vec3R            mVoxelSize; // 24B. size of a voxel in world units
1692         GridClass        mGridClass; // 4B.
1693         GridType         mGridType; //  4B.
1694         int64_t          mBlindMetadataOffset; // 8B. offset of GridBlindMetaData structures that follow this grid.
1695         uint32_t         mBlindMetadataCount; // 4B. count of GridBlindMetaData structures that follow this grid.
1696     */
1697         int offset = 0;
1698         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mMagic), offset);
1699         offset += 8;
1700         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mChecksum), offset);
1701         offset += 8;
1702         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mVersion), offset);
1703         offset += 4;
1704         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mFlags), offset);
1705         offset += 4;
1706         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridIndex), offset);
1707         offset += 4;
1708         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridCount), offset);
1709         offset += 4;
1710         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridSize), offset);
1711         offset += 8;
1712         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridName), offset);
1713         offset += 256;
1714         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mMap), offset);
1715         offset += 264;
1716         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mWorldBBox), offset);
1717         offset += 48;
1718         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mVoxelSize), offset);
1719         offset += 24;
1720         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridClass), offset);
1721         offset += 4;
1722         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridType), offset);
1723         offset += 4;
1724         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mBlindMetadataOffset), offset);
1725         offset += 8;
1726         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mBlindMetadataCount), offset);
1727         offset += 4;
1728         offset = nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(offset);
1729         //std::cerr << "GridData: Offset = " << offset << std::endl;
1730         EXPECT_EQ(offset, (int)sizeof(nanovdb::GridData));
1731     }
1732     {// check TreeData memory alignment, total 64 bytes
1733         /*
1734             uint64_t mNodeOffset[4];//32B, byte offset from this tree to first leaf, lower, upper and root node
1735             uint32_t mNodeCount[3];// 12B, total number of nodes of type: leaf, lower internal, upper internal
1736             uint32_t mTileCount[3];// 12B, total number of tiles of type: leaf, lower internal, upper internal (node, only active tiles!)
1737             uint64_t mVoxelCount;//    8B, total number of active voxels in the root and all its child nodes.
1738         */
1739         int offset = 0;
1740         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::TreeData<>, mNodeOffset), offset);
1741         offset += 4*8;
1742         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::TreeData<>, mNodeCount), offset);
1743         offset += 12;
1744         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::TreeData<>, mTileCount), offset);
1745         offset += 12;
1746         EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::TreeData<>, mVoxelCount), offset);
1747         offset += 8;
1748         offset = nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(offset);
1749         //std::cerr << "TreeData: Offset = " << offset << std::endl;
1750         EXPECT_EQ(offset, (int)sizeof(nanovdb::TreeData<>));
1751     }
1752 }
1753 
1754 template <typename ValueT>
1755 void checkLeaf(int &offset);
1756 
TYPED_TEST(TestOffsets,NanoVDB)1757 TYPED_TEST(TestOffsets, NanoVDB)
1758 {
1759     using ValueType = typename nanovdb::BuildToValueMap<TypeParam>::Type;
1760     using T = typename nanovdb::TensorTraits<ValueType>::ElementType;
1761     using StatsT = typename nanovdb::FloatTraits<ValueType>::FloatType;
1762     static const size_t ALIGNMENT = sizeof(T) > sizeof(StatsT) ? sizeof(T) : sizeof(StatsT);
1763     //std::cerr << "Alignment = " << ALIGNMENT << " sizeof(ValueType) = " << sizeof(ValueType) << std::endl;
1764     {// check memory layout of RootData
1765         using DataT = typename nanovdb::NanoRoot<ValueType>::DataType;
1766         bool test = nanovdb::is_same<StatsT, typename DataT::StatsT>::value;
1767         EXPECT_TRUE(test);
1768         int offsets[] = {
1769             NANOVDB_OFFSETOF(DataT, mBBox),
1770             NANOVDB_OFFSETOF(DataT, mTableSize),
1771             NANOVDB_OFFSETOF(DataT, mBackground),
1772             NANOVDB_OFFSETOF(DataT, mMinimum),
1773             NANOVDB_OFFSETOF(DataT, mMaximum),
1774             NANOVDB_OFFSETOF(DataT, mAverage),
1775             NANOVDB_OFFSETOF(DataT, mStdDevi)
1776         };
1777         //for (int i : offsets) std::cout << i << " ";
1778         const int *p = offsets;
1779         int offset = 0;// first data member
1780         EXPECT_EQ(*p++, offset);// mBBox
1781         offset += 24;// 2 * 3 * 4 bytes = 24 bytes
1782         EXPECT_EQ(*p++, offset);// mTableSize
1783         offset += sizeof(uint32_t);
1784         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1785         EXPECT_EQ(*p++, offset);// mBackground
1786         offset += sizeof(ValueType);
1787         EXPECT_EQ(*p++, offset);// mMinimum
1788         offset += sizeof(ValueType);
1789         EXPECT_EQ(*p++, offset);// mMaximum
1790         offset += sizeof(ValueType);
1791         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1792         EXPECT_EQ(*p++, offset);// mAverage
1793         offset += sizeof(StatsT);
1794         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1795         EXPECT_EQ(*p++, offset);// mStdDevi
1796         offset += sizeof(StatsT);
1797         offset = nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(offset);
1798         EXPECT_EQ(offset, (int)sizeof(DataT));// size of RootData
1799     }
1800     {// check  memory layout of upper internal nodes
1801         using DataT = typename nanovdb::NanoUpper<ValueType>::DataType;
1802         bool test = nanovdb::is_same<StatsT, typename DataT::StatsT>::value;
1803         EXPECT_TRUE(test);
1804         int offsets[] = {
1805             NANOVDB_OFFSETOF(DataT, mBBox),
1806             NANOVDB_OFFSETOF(DataT, mFlags),
1807             NANOVDB_OFFSETOF(DataT, mValueMask),
1808             NANOVDB_OFFSETOF(DataT, mChildMask),
1809             NANOVDB_OFFSETOF(DataT, mMinimum),
1810             NANOVDB_OFFSETOF(DataT, mMaximum),
1811             NANOVDB_OFFSETOF(DataT, mAverage),
1812             NANOVDB_OFFSETOF(DataT, mStdDevi),
1813             NANOVDB_OFFSETOF(DataT, mTable),
1814         };
1815         //for (int i : offsets) std::cout << i << " ";
1816         int offset = 0, *p = offsets;
1817         EXPECT_EQ(*p++, offset);
1818         offset += 24;
1819         EXPECT_EQ(*p++, offset);
1820         offset += 8;
1821         EXPECT_EQ(*p++, offset);
1822         offset += 4096;// = 32*32*32/8
1823         EXPECT_EQ(*p++, offset);
1824         offset += 4096;// = 32*32*32/8
1825         EXPECT_EQ(*p++, offset);
1826         offset += sizeof(ValueType);
1827         EXPECT_EQ(*p++, offset);
1828         offset += sizeof(ValueType);
1829         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1830         EXPECT_EQ(*p++, offset);
1831         offset += sizeof(StatsT);
1832         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1833         EXPECT_EQ(*p++, offset);
1834         offset += sizeof(StatsT);
1835         offset = nanovdb::AlignUp<32>(offset);
1836         EXPECT_EQ(*p++, offset);
1837         const size_t tile_size = nanovdb::AlignUp<8>(sizeof(ValueType));
1838         EXPECT_EQ(sizeof(typename DataT::Tile), tile_size);
1839         offset += (32*32*32)*tile_size;
1840         offset = nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(offset);
1841         EXPECT_EQ(sizeof(DataT), (size_t)offset);
1842     }
1843     {// check  memory lower of upper internal nodes
1844         using DataT = typename nanovdb::NanoLower<ValueType>::DataType;
1845         bool test = nanovdb::is_same<StatsT, typename DataT::StatsT>::value;
1846         EXPECT_TRUE(test);
1847         int offsets[] = {
1848             NANOVDB_OFFSETOF(DataT, mBBox),
1849             NANOVDB_OFFSETOF(DataT, mFlags),
1850             NANOVDB_OFFSETOF(DataT, mValueMask),
1851             NANOVDB_OFFSETOF(DataT, mChildMask),
1852             NANOVDB_OFFSETOF(DataT, mMinimum),
1853             NANOVDB_OFFSETOF(DataT, mMaximum),
1854             NANOVDB_OFFSETOF(DataT, mAverage),
1855             NANOVDB_OFFSETOF(DataT, mStdDevi),
1856             NANOVDB_OFFSETOF(DataT, mTable),
1857         };
1858         //for (int i : offsets) std::cout << i << " ";
1859         int offset = 0, *p = offsets;
1860         EXPECT_EQ(*p++, offset);
1861         offset += 24;
1862         EXPECT_EQ(*p++, offset);
1863         offset += 8;
1864         EXPECT_EQ(*p++, offset);
1865         offset += 512;// = 16*16*16/8
1866         EXPECT_EQ(*p++, offset);
1867         offset += 512;// = 16*16*16/8
1868         EXPECT_EQ(*p++, offset);
1869         offset += sizeof(ValueType);
1870         EXPECT_EQ(*p++, offset);
1871         offset += sizeof(ValueType);
1872         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1873         EXPECT_EQ(*p++, offset);
1874         offset += sizeof(StatsT);
1875         offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1876         EXPECT_EQ(*p++, offset);
1877         offset += sizeof(StatsT);
1878         offset = nanovdb::AlignUp<32>(offset);
1879         EXPECT_EQ(*p++, offset);
1880         const size_t tile_size = nanovdb::AlignUp<8>(sizeof(ValueType));
1881         EXPECT_EQ(sizeof(typename DataT::Tile), tile_size);
1882         offset += (16*16*16)*tile_size;
1883         offset = nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(offset);
1884         EXPECT_EQ(sizeof(DataT), (size_t)offset);
1885     }
1886     {// check  memory of leaf nodes
1887         using DataT = typename nanovdb::LeafNode<ValueType>::DataType;
1888         bool test = nanovdb::is_same<StatsT, typename DataT::FloatType>::value;
1889         EXPECT_TRUE(test);
1890         int offsets[] = {
1891             NANOVDB_OFFSETOF(DataT, mBBoxMin),
1892             NANOVDB_OFFSETOF(DataT, mBBoxDif),
1893             NANOVDB_OFFSETOF(DataT, mFlags),
1894             NANOVDB_OFFSETOF(DataT, mValueMask),
1895         };
1896         //for (int i : offsets) std::cout << i << " ";
1897         int offset = 0, *p = offsets;
1898         EXPECT_EQ(*p++, offset);
1899         offset += 12;
1900         EXPECT_EQ(*p++, offset);
1901         offset += 3;
1902         EXPECT_EQ(*p++, offset);
1903         offset += 1;
1904         EXPECT_EQ(*p++, offset);
1905         offset += 64;// = 8*8*8/8
1906         checkLeaf<ValueType>(offset);
1907         offset = nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(offset);
1908         EXPECT_EQ(sizeof(DataT), (size_t)offset);
1909     }
1910 }// TestOffsets NanoVDB
1911 
1912 template<typename ValueType>
checkLeaf(int & offset)1913 void checkLeaf(int &offset)
1914 {
1915     using DataT = typename nanovdb::LeafNode<ValueType>::DataType;
1916     using T = typename nanovdb::TensorTraits<ValueType>::ElementType;
1917     using StatsT = typename nanovdb::FloatTraits<ValueType>::FloatType;
1918     static const size_t ALIGNMENT = sizeof(T) > sizeof(StatsT) ? sizeof(T) : sizeof(StatsT);
1919     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMinimum), offset);
1920     offset += sizeof(ValueType);
1921     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMaximum), offset);
1922     offset += sizeof(ValueType);
1923     offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1924     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mAverage), offset);
1925     offset += sizeof(StatsT);
1926     offset = nanovdb::AlignUp<ALIGNMENT>(offset);
1927     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mStdDevi), offset);
1928     offset += sizeof(StatsT);
1929     offset = nanovdb::AlignUp<32>(offset);
1930     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mValues), offset);
1931     offset += (8*8*8)*sizeof(ValueType);
1932 }
1933 
1934 template<>
checkLeaf(int & offset)1935 void checkLeaf<bool>(int &offset)
1936 {
1937     using DataT = typename nanovdb::LeafNode<bool>::DataType;
1938     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mValues), offset);
1939     offset += 64;// = 8*8*8/8
1940 }
1941 
1942 template<>
checkLeaf(int & offset)1943 void checkLeaf<nanovdb::Fp4>(int &offset)
1944 {
1945     using DataT = typename nanovdb::LeafNode<nanovdb::Fp4>::DataType;
1946     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMinimum), offset);
1947     offset += sizeof(float);
1948     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mQuantum), offset);
1949     offset += sizeof(float);
1950     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMin), offset);
1951     offset += sizeof(uint16_t);
1952     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMax), offset);
1953     offset += sizeof(uint16_t);
1954     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mAvg), offset);
1955     offset += sizeof(uint16_t);
1956     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mDev), offset);
1957     offset += sizeof(uint16_t);
1958     offset = nanovdb::AlignUp<32>(offset);
1959     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mCode), offset);
1960     offset += 256*sizeof(uint8_t);
1961 }
1962 
1963 template<>
checkLeaf(int & offset)1964 void checkLeaf<nanovdb::Fp8>(int &offset)
1965 {
1966     using DataT = typename nanovdb::LeafNode<nanovdb::Fp8>::DataType;
1967     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMinimum), offset);
1968     offset += sizeof(float);
1969     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mQuantum), offset);
1970     offset += sizeof(float);
1971     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMin), offset);
1972     offset += sizeof(uint16_t);
1973     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMax), offset);
1974     offset += sizeof(uint16_t);
1975     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mAvg), offset);
1976     offset += sizeof(uint16_t);
1977     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mDev), offset);
1978     offset += sizeof(uint16_t);
1979     offset = nanovdb::AlignUp<32>(offset);
1980     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mCode), offset);
1981     offset += 512*sizeof(uint8_t);
1982 }
1983 
1984 template<>
checkLeaf(int & offset)1985 void checkLeaf<nanovdb::Fp16>(int &offset)
1986 {
1987     using DataT = typename nanovdb::LeafNode<nanovdb::Fp16>::DataType;
1988     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMinimum), offset);
1989     offset += sizeof(float);
1990     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mQuantum), offset);
1991     offset += sizeof(float);
1992     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMin), offset);
1993     offset += sizeof(uint16_t);
1994     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMax), offset);
1995     offset += sizeof(uint16_t);
1996     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mAvg), offset);
1997     offset += sizeof(uint16_t);
1998     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mDev), offset);
1999     offset += sizeof(uint16_t);
2000     offset = nanovdb::AlignUp<32>(offset);
2001     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mCode), offset);
2002     offset += 512*sizeof(uint16_t);
2003 }
2004 
2005 template<>
checkLeaf(int & offset)2006 void checkLeaf<nanovdb::FpN>(int &offset)
2007 {
2008     using DataT = typename nanovdb::LeafNode<nanovdb::FpN>::DataType;
2009     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMinimum), offset);
2010     offset += sizeof(float);
2011     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mQuantum), offset);
2012     offset += sizeof(float);
2013     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMin), offset);
2014     offset += sizeof(uint16_t);
2015     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mMax), offset);
2016     offset += sizeof(uint16_t);
2017     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mAvg), offset);
2018     offset += sizeof(uint16_t);
2019     EXPECT_EQ(NANOVDB_OFFSETOF(DataT, mDev), offset);
2020     offset += sizeof(uint16_t);
2021     offset = nanovdb::AlignUp<32>(offset);
2022 }
2023 
2024 template<>
checkLeaf(int &)2025 void checkLeaf<nanovdb::ValueMask>(int &) {}
2026 
TEST_F(TestNanoVDB,BasicGrid)2027 TEST_F(TestNanoVDB, BasicGrid)
2028 {
2029     using LeafT  = nanovdb::LeafNode<float>;
2030     using NodeT1 = nanovdb::InternalNode<LeafT>;
2031     using NodeT2 = nanovdb::InternalNode<NodeT1>;
2032     using RootT  = nanovdb::RootNode<NodeT2>;
2033     using TreeT  = nanovdb::Tree<RootT>;
2034     using GridT  = nanovdb::Grid<TreeT>;
2035     using CoordT = LeafT::CoordType;
2036 
2037     const std::string name("test name");
2038     {
2039         // This is just for visual inspection
2040         /*
2041         this->printType<GridT>("Grid");
2042         this->printType<TreeT>("Tree");
2043         this->printType<NodeT2>("Upper InternalNode");
2044         this->printType<NodeT1>("Lower InternalNode");
2045         this->printType<LeafT>("Leaf");
2046         Old: W/O mAverage and mVariance
2047             Size of Grid: 672 bytes which is 32 byte aligned
2048             Size of Tree: 64 bytes which is 32 byte aligned
2049             Size of Upper InternalNode: 139328 bytes which is 32 byte aligned
2050             Size of Lower InternalNode: 17472 bytes which is 32 byte aligned
2051             Size of Leaf: 2144 bytes which is 32 byte aligned
2052 
2053         New: WITH mAverage and mVariance
2054             Size of Grid: 672 bytes which is 32 byte aligned
2055             Size of Tree: 64 bytes which is 32 byte aligned
2056             Size of Upper InternalNode: 139328 bytes which is 32 byte aligned
2057             Size of Lower InternalNode: 17472 bytes which is 32 byte aligned
2058             Size of Leaf: 2144 bytes which is 32 byte aligned
2059         */
2060     }
2061 
2062     EXPECT_EQ(sizeof(GridT), nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(8 + 8 + 4 + 4 + 8 + nanovdb::GridData::MaxNameSize + 48 + sizeof(nanovdb::Map) + 24 + 4 + 4 + 8 + 4));
2063     EXPECT_EQ(sizeof(TreeT), nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(4*8 + 3*4 + 3*4 + 8));
2064     EXPECT_EQ(sizeof(TreeT), size_t(4*8 + 3*4 + 3*4 + 8));// should already be 32 byte aligned
2065 
2066     size_t bytes[6] = {GridT::memUsage(), TreeT::memUsage(), RootT::memUsage(1), NodeT2::memUsage(), NodeT1::memUsage(), LeafT::memUsage()};
2067     for (int i = 1; i < 6; ++i)
2068         bytes[i] += bytes[i - 1]; // Byte offsets to: tree, root, internal nodes, leafs, total
2069     std::unique_ptr<uint8_t[]> buffer(new uint8_t[bytes[5]]);
2070 
2071     // init leaf
2072     LeafT* leaf = reinterpret_cast<LeafT*>(buffer.get() + bytes[4]);
2073     { // set members of the leaf node
2074         auto* data = leaf->data();
2075         data->mValueMask.setOff();
2076         auto* voxels = data->mValues;
2077         for (uint32_t i = 0; i < LeafT::voxelCount() / 2; ++i)
2078             *voxels++ = 0.0f;
2079         for (uint32_t i = LeafT::voxelCount() / 2; i < LeafT::voxelCount(); ++i) {
2080             data->mValueMask.setOn(i);
2081             *voxels++ = 1.0f;
2082         }
2083         data->mMinimum = 1.0f;
2084         data->mMaximum = 1.0f;
2085     }
2086 
2087     // lower internal node
2088     NodeT1* node1 = reinterpret_cast<NodeT1*>(buffer.get() + bytes[3]);
2089     { // set members of the  internal node
2090         auto *data = node1->data();
2091         data->mValueMask.setOff();
2092         data->mChildMask.setOff();
2093         data->mChildMask.setOn(0);
2094         data->setChild(0, leaf);
2095         for (uint32_t i = 1; i < NodeT1::SIZE / 2; ++i)
2096             data->mTable[i].value = 0.0f;
2097         for (uint32_t i = NodeT1::SIZE / 2; i < NodeT1::SIZE; ++i) {
2098             data->mValueMask.setOn(i);
2099             data->mTable[i].value = 2.0f;
2100         }
2101         data->mMinimum = 1.0f;
2102         data->mMaximum = 2.0f;
2103         EXPECT_EQ(leaf, data->getChild(0));
2104     }
2105 
2106     // upper internal node
2107     NodeT2* node2 = reinterpret_cast<NodeT2*>(buffer.get() + bytes[2]);
2108     { // set members of the  internal node
2109         auto *data = node2->data();
2110         data->mValueMask.setOff();
2111         data->mChildMask.setOff();
2112         data->mChildMask.setOn(0);
2113         data->setChild(0, node1);
2114         for (uint32_t i = 1; i < NodeT2::SIZE / 2; ++i)
2115             data->mTable[i].value = 0.0f;
2116         for (uint32_t i = NodeT2::SIZE / 2; i < NodeT2::SIZE; ++i) {
2117             data->mValueMask.setOn(i);
2118             data->mTable[i].value = 3.0f;
2119         }
2120         data->mMinimum = 1.0f;
2121         data->mMaximum = 3.0f;
2122         EXPECT_EQ(node1, data->getChild(0));
2123     }
2124 
2125     // init root
2126     RootT* root = reinterpret_cast<RootT*>(buffer.get() + bytes[1]);
2127     { // set members of the root node
2128         auto* data = root->data();
2129         data->mBackground = 0.0f;
2130         data->mMinimum = 1.0f;
2131         data->mMaximum = 3.0f;
2132         data->mTableSize = 1;
2133         data->tile(0)->setChild(RootT::CoordType(0), node2, data);
2134     }
2135 
2136     // init tree
2137     TreeT* tree = reinterpret_cast<TreeT*>(buffer.get() + bytes[0]);
2138     {
2139         auto* data = tree->data();
2140         data->setRoot(root);
2141         data->setFirstNode(node2);
2142         data->setFirstNode(node1);
2143         data->setFirstNode(leaf);
2144         data->mNodeCount[0] = data->mNodeCount[1] = data->mNodeCount[2] = 1;
2145     }
2146 
2147     GridT* grid = reinterpret_cast<GridT*>(buffer.get());
2148     { // init Grid
2149         auto* data = grid->data();
2150         {
2151             const double dx = 2.0, Tx = 0.0, Ty = 0.0, Tz = 0.0;
2152             const double mat[4][4] = {
2153                 {dx, 0.0, 0.0, 0.0}, // row 0
2154                 {0.0, dx, 0.0, 0.0}, // row 1
2155                 {0.0, 0.0, dx, 0.0}, // row 2
2156                 {Tx, Ty, Tz, 1.0}, // row 3
2157             };
2158             const double invMat[4][4] = {
2159                 {1 / dx, 0.0, 0.0, 0.0}, // row 0
2160                 {0.0, 1 / dx, 0.0, 0.0}, // row 1
2161                 {0.0, 0.0, 1 / dx, 0.0}, // row 2
2162                 {-Tx, -Ty, -Tz, 1.0}, // row 3
2163             };
2164             data->setFlagsOff();
2165             data->setMinMaxOn();
2166             data->mGridIndex = 0;
2167             data->mGridCount = 1;
2168             data->mBlindMetadataOffset = 0;
2169             data->mBlindMetadataCount = 0;
2170             data->mVoxelSize = nanovdb::Vec3R(dx);
2171             data->mMap.set(mat, invMat, 1.0);
2172             data->mGridClass = nanovdb::GridClass::Unknown;
2173             data->mGridType = nanovdb::GridType::Float;
2174             data->mMagic = NANOVDB_MAGIC_NUMBER;
2175             data->mVersion = nanovdb::Version();
2176             memcpy(data->mGridName, name.c_str(), name.size() + 1);
2177         }
2178 
2179         EXPECT_EQ(tree, &grid->tree());
2180         const nanovdb::Vec3R p1(1.0, 2.0, 3.0);
2181         const auto           p2 = grid->worldToIndex(p1);
2182         EXPECT_EQ(nanovdb::Vec3R(0.5, 1.0, 1.5), p2);
2183         const auto p3 = grid->indexToWorld(p2);
2184         EXPECT_EQ(p1, p3);
2185         {
2186             const double dx = 2.0, Tx = p1[0], Ty = p1[1], Tz = p1[2];
2187             const double mat[4][4] = {
2188                 {dx, 0.0, 0.0, 0.0}, // row 0
2189                 {0.0, dx, 0.0, 0.0}, // row 1
2190                 {0.0, 0.0, dx, 0.0}, // row 2
2191                 {Tx, Ty, Tz, 1.0}, // row 3
2192             };
2193             const double invMat[4][4] = {
2194                 {1 / dx, 0.0, 0.0, 0.0}, // row 0
2195                 {0.0, 1 / dx, 0.0, 0.0}, // row 1
2196                 {0.0, 0.0, 1 / dx, 0.0}, // row 2
2197                 {-1 / Tx, -1 / Ty, -1 / Tz, 1.0}, // row 3
2198             };
2199             data->mVoxelSize = nanovdb::Vec3R(dx);
2200             data->mMap.set(mat, invMat, 1.0);
2201         }
2202 
2203 
2204         // Start actual tests
2205 
2206         auto const p4 = grid->worldToIndex(p3);
2207         EXPECT_EQ(nanovdb::Vec3R(0.0, 0.0, 0.0), p4);
2208         const auto p5 = grid->indexToWorld(p4);
2209         EXPECT_EQ(p1, p5);
2210     }
2211 
2212     { // check leaf node
2213         auto* ptr = reinterpret_cast<LeafT::DataType*>(buffer.get() + bytes[4])->mValues;
2214         for (uint32_t i = 0; i < LeafT::voxelCount(); ++i) {
2215             if (i < 256) {
2216                 EXPECT_FALSE(leaf->valueMask().isOn(i));
2217                 EXPECT_EQ(0.0f, *ptr++);
2218             } else {
2219                 EXPECT_TRUE(leaf->valueMask().isOn(i));
2220                 EXPECT_EQ(1.0f, *ptr++);
2221             }
2222         }
2223         EXPECT_EQ(1.0f, leaf->minimum());
2224         EXPECT_EQ(1.0f, leaf->maximum());
2225         EXPECT_EQ(0.0f, leaf->getValue(CoordT(0)));
2226         EXPECT_EQ(1.0f, leaf->getValue(CoordT(8-1)));
2227     }
2228 
2229     { // check lower internal node
2230         auto& data = *reinterpret_cast<NodeT1::DataType*>(buffer.get() + bytes[3]);
2231         EXPECT_TRUE(node1->childMask().isOn(0));
2232         for (uint32_t i = 1; i < NodeT1::SIZE; ++i) {
2233             EXPECT_FALSE(node1->childMask().isOn(i));
2234             if (i < NodeT1::SIZE / 2) {
2235                 EXPECT_FALSE(node1->valueMask().isOn(i));
2236                 EXPECT_EQ(0.0f, data.mTable[i].value);
2237             } else {
2238                 EXPECT_TRUE(node1->valueMask().isOn(i));
2239                 EXPECT_EQ(2.0f, data.mTable[i].value);
2240             }
2241         }
2242         EXPECT_EQ(1.0f, node1->minimum());
2243         EXPECT_EQ(2.0f, node1->maximum());
2244         EXPECT_EQ(0.0f, node1->getValue(CoordT(0)));
2245         EXPECT_EQ(1.0f, node1->getValue(CoordT(8-1)));
2246         EXPECT_EQ(2.0f, node1->getValue(CoordT(8*16-1)));
2247     }
2248     { // check upper internal node
2249         auto& data = *reinterpret_cast<NodeT2::DataType*>(buffer.get() + bytes[2]);
2250         EXPECT_TRUE(node2->childMask().isOn(0));
2251         for (uint32_t i = 1; i < NodeT2::SIZE; ++i) {
2252             EXPECT_FALSE(node2->childMask().isOn(i));
2253             if (i < NodeT2::SIZE / 2) {
2254                 EXPECT_FALSE(node2->valueMask().isOn(i));
2255                 EXPECT_EQ(0.0f, data.mTable[i].value);
2256             } else {
2257                 EXPECT_TRUE(node2->valueMask().isOn(i));
2258                 EXPECT_EQ(3.0f, data.mTable[i].value);
2259             }
2260         }
2261         EXPECT_EQ(1.0f, node2->minimum());
2262         EXPECT_EQ(3.0f, node2->maximum());
2263         EXPECT_EQ(0.0f, node2->getValue(CoordT(0)));
2264         EXPECT_EQ(1.0f, node2->getValue(CoordT(8-1)));
2265         EXPECT_EQ(2.0f, node2->getValue(CoordT(8*16-1)));
2266         EXPECT_EQ(3.0f, node2->getValue(CoordT(8*16*32-1)));
2267     }
2268     { // check root
2269         EXPECT_EQ(0.0f, root->background());
2270         EXPECT_EQ(1.0f, root->minimum());
2271         EXPECT_EQ(3.0f, root->maximum());
2272         EXPECT_EQ(1u,   root->tileCount());
2273         EXPECT_EQ(0.0f, root->getValue(CoordT(0)));
2274         EXPECT_EQ(1.0f, root->getValue(CoordT(8-1)));
2275         EXPECT_EQ(2.0f, root->getValue(CoordT(8*16-1)));
2276         EXPECT_EQ(3.0f, root->getValue(CoordT(8*16*32-1)));
2277     }
2278     { // check tree
2279         EXPECT_EQ(0.0f, tree->background());
2280         float a, b;
2281         tree->extrema(a, b);
2282         EXPECT_EQ(1.0f, a);
2283         EXPECT_EQ(3.0f, b);
2284         EXPECT_EQ(0.0f, tree->getValue(CoordT(0)));
2285         EXPECT_EQ(1.0f, tree->getValue(CoordT(8-1)));
2286         EXPECT_EQ(2.0f, tree->getValue(CoordT(8*16-1)));
2287         EXPECT_EQ(3.0f, tree->getValue(CoordT(8*16*32-1)));
2288         EXPECT_EQ(1u, tree->nodeCount<LeafT>());
2289         EXPECT_EQ(1u, tree->nodeCount<NodeT1>());
2290         EXPECT_EQ(1u, tree->nodeCount<NodeT2>());
2291     }
2292     {// check grid
2293         EXPECT_EQ(nanovdb::Version(), grid->version());
2294         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), grid->version().getMajor());
2295         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), grid->version().getMinor());
2296         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), grid->version().getPatch());
2297         EXPECT_TRUE(grid->isValid());
2298         EXPECT_EQ(grid->gridType(), nanovdb::GridType::Float);
2299         EXPECT_EQ(grid->gridClass(), nanovdb::GridClass::Unknown);
2300         EXPECT_FALSE(grid->isLevelSet());
2301         EXPECT_FALSE(grid->isFogVolume());
2302         EXPECT_FALSE(grid->isStaggered());
2303         EXPECT_FALSE(grid->isPointIndex());
2304         EXPECT_FALSE(grid->isPointData());
2305         EXPECT_FALSE(grid->isMask());
2306         EXPECT_TRUE(grid->isUnknown());
2307         EXPECT_TRUE(grid->hasMinMax());
2308         EXPECT_FALSE(grid->hasBBox());
2309         EXPECT_FALSE(grid->hasLongGridName());
2310         EXPECT_FALSE(grid->hasAverage());
2311         EXPECT_FALSE(grid->hasStdDeviation());
2312         //std::cerr << "\nName = \"" << grid->gridName() << "\"" << std::endl;
2313         EXPECT_EQ(name, std::string(grid->gridName()));
2314     }
2315     {// check ReadAccessor
2316         auto acc = grid->getAccessor();
2317         EXPECT_EQ(0.0f, acc.getValue(CoordT(0)));
2318         EXPECT_EQ(1.0f, acc.getValue(CoordT(8-1)));
2319         EXPECT_EQ(2.0f, acc.getValue(CoordT(8*16-1)));
2320         EXPECT_EQ(3.0f, acc.getValue(CoordT(8*16*32-1)));
2321         EXPECT_FALSE(acc.isActive(CoordT(0)));
2322         EXPECT_TRUE(acc.isActive(CoordT(8-1)));
2323         EXPECT_TRUE(acc.isActive(CoordT(16*8-1)));
2324         EXPECT_TRUE(acc.isActive(CoordT(32*16*8-1)));
2325     }
2326 } // BasicGrid
2327 
TEST_F(TestNanoVDB,GridBuilderEmpty)2328 TEST_F(TestNanoVDB, GridBuilderEmpty)
2329 {
2330     { // empty grid
2331         nanovdb::GridBuilder<float> builder(0.0f);
2332         auto                        srcAcc = builder.getAccessor();
2333         auto                        handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
2334         EXPECT_TRUE(handle);
2335         auto* meta = handle.gridMetaData();
2336         EXPECT_TRUE(meta);
2337         EXPECT_TRUE(meta->isEmpty());
2338         EXPECT_EQ("test", std::string(meta->shortGridName()));
2339         EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
2340         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2341         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2342         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2343         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2344         auto* dstGrid = handle.grid<float>();
2345         EXPECT_TRUE(dstGrid);
2346         EXPECT_EQ("test", std::string(dstGrid->gridName()));
2347         EXPECT_EQ(0u, dstGrid->activeVoxelCount());
2348         auto dstAcc = dstGrid->getAccessor();
2349         EXPECT_EQ(0.0f, srcAcc.getValue(nanovdb::Coord(1, 2, 3)));
2350         EXPECT_FALSE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2351         EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(1, 2, 3)));
2352         EXPECT_EQ(dstGrid->tree().root().minimum(), 0.0f);
2353         EXPECT_EQ(dstGrid->tree().root().maximum(), 0.0f);
2354         EXPECT_EQ(dstGrid->tree().root().average(), 0.0f);
2355         EXPECT_EQ(dstGrid->tree().root().variance(), 0.0f);
2356         EXPECT_EQ(dstGrid->tree().root().stdDeviation(), 0.0f);
2357     }
2358 } // GridBuilderEmpty
2359 
TEST_F(TestNanoVDB,GridBuilderBasic1)2360 TEST_F(TestNanoVDB, GridBuilderBasic1)
2361 {
2362     { // 1 grid point
2363         nanovdb::GridBuilder<float> builder(0.0f);
2364         auto                        srcAcc = builder.getAccessor();
2365         srcAcc.setValue(nanovdb::Coord(1, 2, 3), 1.0f);
2366         EXPECT_EQ(1.0f, srcAcc.getValue(nanovdb::Coord(1, 2, 3)));
2367         auto handle = builder.getHandle<>();
2368         EXPECT_TRUE(handle);
2369         auto* meta = handle.gridMetaData();
2370         EXPECT_TRUE(meta);
2371         EXPECT_FALSE(meta->isEmpty());
2372         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2373         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2374         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2375         EXPECT_EQ("", std::string(meta->shortGridName()));
2376         EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
2377         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2378         auto* dstGrid = handle.grid<float>();
2379         EXPECT_TRUE(dstGrid);
2380         EXPECT_EQ("", std::string(dstGrid->gridName()));
2381         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2382         //EXPECT_EQ(1u, dstGrid->activeVoxelCount());
2383         auto dstAcc = dstGrid->getAccessor();
2384         EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(1, 2, 3)));
2385         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2386         EXPECT_EQ(nanovdb::Coord(1, 2, 3), dstGrid->indexBBox()[0]);
2387         EXPECT_EQ(nanovdb::Coord(1, 2, 3), dstGrid->indexBBox()[1]);
2388         EXPECT_EQ(dstGrid->tree().root().minimum(), 1.0f);// minimum active value
2389         EXPECT_EQ(dstGrid->tree().root().maximum(), 1.0f);// maximum active value
2390         EXPECT_NEAR(dstGrid->tree().root().average(), 1.0f, 1e-6);
2391         EXPECT_NEAR(dstGrid->tree().root().variance(), 0.0f,1e-6);
2392         EXPECT_NEAR(dstGrid->tree().root().stdDeviation(), 0.0f, 1e-6);
2393     }
2394 } // GridBuilderBasic1
2395 
TEST_F(TestNanoVDB,GridBuilderBasic2)2396 TEST_F(TestNanoVDB, GridBuilderBasic2)
2397 {
2398     { // 2 grid points
2399         nanovdb::GridBuilder<float> builder(0.0f);
2400         auto                        srcAcc = builder.getAccessor();
2401         srcAcc.setValue(nanovdb::Coord(1, 2, 3),  1.0f);
2402         srcAcc.setValue(nanovdb::Coord(2, -2, 9),-1.0f);
2403         //srcAcc.setValue(nanovdb::Coord(20,-20,90), 0.0f);// same as background
2404         auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
2405         EXPECT_TRUE(handle);
2406         auto* meta = handle.gridMetaData();
2407         EXPECT_TRUE(meta);
2408         EXPECT_FALSE(meta->isEmpty());
2409         //EXPECT_TRUE(meta->version().isValid());
2410         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2411         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2412         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2413         EXPECT_EQ("test", std::string(meta->shortGridName()));
2414         EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
2415         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2416         auto* dstGrid = handle.grid<float>();
2417         EXPECT_TRUE(dstGrid);
2418         EXPECT_EQ("test", std::string(dstGrid->gridName()));
2419         EXPECT_EQ(2u, dstGrid->activeVoxelCount());
2420         auto dstAcc = dstGrid->getAccessor();
2421         EXPECT_EQ( 1.0f, dstAcc.getValue(nanovdb::Coord(1, 2, 3)));
2422         EXPECT_EQ(-1.0f, dstAcc.getValue(nanovdb::Coord(2, -2, 9)));
2423 
2424         const nanovdb::BBox<nanovdb::Vec3R> indexBBox = dstGrid->indexBBox();
2425         EXPECT_DOUBLE_EQ( 1.0, indexBBox[0][0]);
2426         EXPECT_DOUBLE_EQ(-2.0, indexBBox[0][1]);
2427         EXPECT_DOUBLE_EQ( 3.0, indexBBox[0][2]);
2428         EXPECT_DOUBLE_EQ( 3.0, indexBBox[1][0]);
2429         EXPECT_DOUBLE_EQ( 3.0, indexBBox[1][1]);
2430         EXPECT_DOUBLE_EQ(10.0, indexBBox[1][2]);
2431 
2432         EXPECT_EQ(nanovdb::Coord(1, -2, 3), dstGrid->indexBBox()[0]);
2433         EXPECT_EQ(nanovdb::Coord(2, 2, 9), dstGrid->indexBBox()[1]);
2434 
2435         EXPECT_EQ(dstGrid->tree().root().minimum(),-1.0f);
2436         EXPECT_EQ(dstGrid->tree().root().maximum(), 1.0f);
2437         EXPECT_NEAR(dstGrid->tree().root().average(), 0.0f, 1e-6);
2438         EXPECT_NEAR(dstGrid->tree().root().variance(),1.0f, 1e-6);// Sim (x_i - Avg)^2/N = ((-1)^2 + 1^2)/2 =  1
2439         EXPECT_NEAR(dstGrid->tree().root().stdDeviation(), 1.0f, 1e-6);// stdDev = Sqrt(var)
2440     }
2441 } // GridBuilderBasic2
2442 
TEST_F(TestNanoVDB,GridBuilderPrune)2443 TEST_F(TestNanoVDB, GridBuilderPrune)
2444 {
2445     {
2446         nanovdb::GridBuilder<float> builder(0.0f);
2447         auto                        srcAcc = builder.getAccessor();
2448         const nanovdb::CoordBBox    bbox(nanovdb::Coord(0), nanovdb::Coord(8*16-1));
2449         auto                        func = [](const nanovdb::Coord&) { return 1.0f; };
2450         //auto                        func = [](const nanovdb::Coord&, float &v) { v = 1.0f; return true; };
2451         builder(func, bbox);
2452         for (auto ijk = bbox.begin(); ijk; ++ijk) {
2453             EXPECT_EQ(1.0f, srcAcc.getValue(*ijk));
2454             EXPECT_TRUE(srcAcc.isActive(*ijk));
2455         }
2456 
2457         auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
2458         EXPECT_TRUE(handle);
2459         auto* meta = handle.gridMetaData();
2460         EXPECT_TRUE(meta);
2461         EXPECT_FALSE(meta->isEmpty());
2462         //EXPECT_TRUE(meta->version().isValid());
2463         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2464         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2465         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2466         EXPECT_EQ("test", std::string(meta->shortGridName()));
2467         EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
2468         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2469         auto* dstGrid = handle.grid<float>();
2470         EXPECT_TRUE(dstGrid);
2471         EXPECT_EQ("test", std::string(dstGrid->gridName()));
2472         EXPECT_EQ(512*16*16*16u, dstGrid->activeVoxelCount());//
2473         auto dstAcc = dstGrid->getAccessor();
2474 
2475         for (nanovdb::Coord ijk = bbox[0]; ijk[0] <= bbox[1][0]; ++ijk[0]) {
2476             for (ijk[1] = bbox[0][1]; ijk[1] <= bbox[1][1]; ++ijk[1]) {
2477                 for (ijk[2] = bbox[0][2]; ijk[2] <= bbox[1][2]; ++ijk[2]) {
2478                     EXPECT_EQ(1.0f, dstAcc.getValue(ijk));
2479                 }
2480             }
2481         }
2482         EXPECT_EQ( 0.0f, dstAcc.getValue(nanovdb::Coord(2, -2, 9)));
2483 
2484         const nanovdb::BBox<nanovdb::Vec3R> indexBBox = dstGrid->indexBBox();
2485         EXPECT_DOUBLE_EQ(   0.0, indexBBox[0][0]);
2486         EXPECT_DOUBLE_EQ(   0.0, indexBBox[0][1]);
2487         EXPECT_DOUBLE_EQ(   0.0, indexBBox[0][2]);
2488         EXPECT_DOUBLE_EQ(8*16.0, indexBBox[1][0]);
2489         EXPECT_DOUBLE_EQ(8*16.0, indexBBox[1][1]);
2490         EXPECT_DOUBLE_EQ(8*16.0, indexBBox[1][2]);
2491 
2492         EXPECT_EQ(nanovdb::Coord(0), dstGrid->indexBBox()[0]);
2493         EXPECT_EQ(nanovdb::Coord(8*16-1), dstGrid->indexBBox()[1]);
2494 
2495         EXPECT_EQ(0u, dstGrid->tree().nodeCount(0));// all pruned away
2496         EXPECT_EQ(0u, dstGrid->tree().nodeCount(1));// all pruned away
2497         EXPECT_EQ(1u, dstGrid->tree().nodeCount(2));
2498 
2499         EXPECT_EQ(dstGrid->tree().root().minimum(), 1.0f);
2500         EXPECT_EQ(dstGrid->tree().root().maximum(), 1.0f);
2501         EXPECT_NEAR(dstGrid->tree().root().average(), 1.0f, 1e-6);
2502         EXPECT_NEAR(dstGrid->tree().root().variance(),0.0f, 1e-6);// Sim (x_i - Avg)^2/N = 0
2503         EXPECT_NEAR(dstGrid->tree().root().stdDeviation(), 0.0f, 1e-6);// stdDev = Sqrt(var)
2504     }
2505 } // GridBuilderPrune
2506 
TEST_F(TestNanoVDB,GridBuilder_Vec3f)2507 TEST_F(TestNanoVDB, GridBuilder_Vec3f)
2508 {
2509     using VoxelT = nanovdb::Vec3f;
2510     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(12 + 3 + 1 + 2*4 + 64 + 3*(2*4 + 512*4)), sizeof(nanovdb::NanoLeaf<VoxelT>));
2511     { // 3 grid point
2512         nanovdb::GridBuilder<VoxelT> builder(nanovdb::Vec3f(0.0f));
2513         auto srcAcc = builder.getAccessor();
2514         srcAcc.setValue(nanovdb::Coord(  1,  2,  3), nanovdb::Vec3f(1.0f));
2515         srcAcc.setValue(nanovdb::Coord(-10, 20,-50), nanovdb::Vec3f(2.0f));
2516         srcAcc.setValue(nanovdb::Coord( 50,-12, 30), nanovdb::Vec3f(3.0f));
2517         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2518         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(1, 2, 3)));
2519         EXPECT_EQ(nanovdb::Vec3f(1.0f), srcAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2520         EXPECT_EQ(nanovdb::Vec3f(2.0f), srcAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2521         EXPECT_EQ(nanovdb::Vec3f(3.0f), srcAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2522 
2523         builder.setStats(nanovdb::StatsMode::All);
2524         auto handle = builder.getHandle();
2525 
2526         EXPECT_TRUE(handle);
2527         auto* meta = handle.gridMetaData();
2528         EXPECT_TRUE(meta);
2529         EXPECT_FALSE(meta->isEmpty());
2530         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2531         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2532         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2533         EXPECT_EQ("", std::string(meta->shortGridName()));
2534         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
2535         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2536         auto* dstGrid = handle.grid<VoxelT>();
2537         EXPECT_TRUE(dstGrid);
2538         EXPECT_EQ("", std::string(dstGrid->gridName()));
2539         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
2540         EXPECT_EQ(nanovdb::Vec3f(1.0f), dstGrid->tree().root().minimum());
2541         EXPECT_EQ(nanovdb::Vec3f(3.0f), dstGrid->tree().root().maximum());
2542         EXPECT_EQ((nanovdb::Vec3f(1.0f).lengthSqr() +
2543                    nanovdb::Vec3f(2.0f).lengthSqr() +
2544                    nanovdb::Vec3f(3.0f).lengthSqr())/3.0f, dstGrid->tree().root().average());
2545         EXPECT_TRUE(dstGrid->isBreadthFirst());
2546         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
2547         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
2548         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
2549         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
2550         EXPECT_TRUE(dstGrid->isSequential<2>());
2551         EXPECT_TRUE(dstGrid->isSequential<1>());
2552         EXPECT_TRUE(dstGrid->isSequential<0>());
2553 
2554         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2555         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(1, 2, 3));
2556         EXPECT_TRUE(leaf);
2557         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
2558         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
2559         EXPECT_EQ(nanovdb::Coord(1,2,3), leaf->data()->mBBoxMin);
2560 
2561         EXPECT_EQ(nanovdb::Vec3f(1.0f), dstGrid->tree().getValue(nanovdb::Coord(1, 2, 3)));
2562         auto dstAcc = dstGrid->getAccessor();
2563         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(1, 2, 3)));
2564         EXPECT_EQ(nanovdb::Vec3f(1.0f), dstAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2565         EXPECT_EQ(nanovdb::Vec3f(2.0f), dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2566         EXPECT_EQ(nanovdb::Vec3f(3.0f), dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2567         //std::cerr << dstGrid->indexBBox() << std::endl;
2568         EXPECT_EQ(nanovdb::Coord(-10,-12,-50), dstGrid->indexBBox()[0]);
2569         EXPECT_EQ(nanovdb::Coord( 50, 20, 30), dstGrid->indexBBox()[1]);
2570     }
2571 } // GridBuilder_Vec3f
2572 
TEST_F(TestNanoVDB,GridBuilder_Vec4f)2573 TEST_F(TestNanoVDB, GridBuilder_Vec4f)
2574 {
2575     using VoxelT = nanovdb::Vec4f;
2576     EXPECT_EQ(nanovdb::AlignUp<NANOVDB_DATA_ALIGNMENT>(12 + 3 + 1 + 2*4 + 64 + 4*(2*4 + 512*4)), sizeof(nanovdb::NanoLeaf<VoxelT>));
2577     { // 3 grid point
2578         nanovdb::GridBuilder<VoxelT> builder(nanovdb::Vec4f(0.0f));
2579         auto srcAcc = builder.getAccessor();
2580         srcAcc.setValue(nanovdb::Coord(  1,  2,  3), nanovdb::Vec4f(1.0f));
2581         srcAcc.setValue(nanovdb::Coord(-10, 20,-50), nanovdb::Vec4f(2.0f));
2582         srcAcc.setValue(nanovdb::Coord( 50,-12, 30), nanovdb::Vec4f(3.0f));
2583         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2584         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(1, 2, 3)));
2585         EXPECT_EQ(nanovdb::Vec4f(1.0f), srcAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2586         EXPECT_EQ(nanovdb::Vec4f(2.0f), srcAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2587         EXPECT_EQ(nanovdb::Vec4f(3.0f), srcAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2588 
2589         builder.setStats(nanovdb::StatsMode::All);
2590         auto handle = builder.getHandle();
2591 
2592         EXPECT_TRUE(handle);
2593         auto* meta = handle.gridMetaData();
2594         EXPECT_TRUE(meta);
2595         EXPECT_FALSE(meta->isEmpty());
2596         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2597         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2598         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2599         EXPECT_EQ("", std::string(meta->shortGridName()));
2600         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
2601         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2602         auto* dstGrid = handle.grid<VoxelT>();
2603         EXPECT_TRUE(dstGrid);
2604         EXPECT_EQ("", std::string(dstGrid->gridName()));
2605         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
2606         EXPECT_EQ(nanovdb::Vec4f(1.0f), dstGrid->tree().root().minimum());
2607         EXPECT_EQ(nanovdb::Vec4f(3.0f), dstGrid->tree().root().maximum());
2608         EXPECT_EQ((nanovdb::Vec4f(1.0f).lengthSqr() +
2609                    nanovdb::Vec4f(2.0f).lengthSqr() +
2610                    nanovdb::Vec4f(3.0f).lengthSqr())/3.0f, dstGrid->tree().root().average());
2611         EXPECT_TRUE(dstGrid->isBreadthFirst());
2612         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
2613         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
2614         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
2615         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
2616         EXPECT_TRUE(dstGrid->isSequential<2>());
2617         EXPECT_TRUE(dstGrid->isSequential<1>());
2618         EXPECT_TRUE(dstGrid->isSequential<0>());
2619 
2620         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2621         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(1, 2, 3));
2622         EXPECT_TRUE(leaf);
2623         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
2624         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
2625         EXPECT_EQ(nanovdb::Coord(1,2,3), leaf->data()->mBBoxMin);
2626 
2627         EXPECT_EQ(nanovdb::Vec4f(1.0f), dstGrid->tree().getValue(nanovdb::Coord(1, 2, 3)));
2628         auto dstAcc = dstGrid->getAccessor();
2629         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(1, 2, 3)));
2630         EXPECT_EQ(nanovdb::Vec4f(1.0f), dstAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2631         EXPECT_EQ(nanovdb::Vec4f(2.0f), dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2632         EXPECT_EQ(nanovdb::Vec4f(3.0f), dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2633         //std::cerr << dstGrid->indexBBox() << std::endl;
2634         EXPECT_EQ(nanovdb::Coord(-10,-12,-50), dstGrid->indexBBox()[0]);
2635         EXPECT_EQ(nanovdb::Coord( 50, 20, 30), dstGrid->indexBBox()[1]);
2636     }
2637 } // GridBuilder_Vec4f
2638 
2639 
TEST_F(TestNanoVDB,GridBuilder_Fp4)2640 TEST_F(TestNanoVDB, GridBuilder_Fp4)
2641 {
2642     using VoxelT = nanovdb::Fp4;
2643     EXPECT_EQ(96u + 512u/2, sizeof(nanovdb::NanoLeaf<VoxelT>));
2644     { // 3 grid point
2645         nanovdb::GridBuilder<float, VoxelT> builder(0.0f);
2646         auto srcAcc = builder.getAccessor();
2647         srcAcc.setValue(nanovdb::Coord(  1,  2,  3), 1.0f);
2648         srcAcc.setValue(nanovdb::Coord(-10, 20,-50), 2.0f);
2649         srcAcc.setValue(nanovdb::Coord( 50,-12, 30), 3.0f);
2650         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2651         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(1, 2, 3)));
2652         EXPECT_EQ(1.0f, srcAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2653         EXPECT_EQ(2.0f, srcAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2654         EXPECT_EQ(3.0f, srcAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2655 
2656         builder.setStats(nanovdb::StatsMode::All);
2657         auto handle = builder.getHandle();
2658 
2659         EXPECT_TRUE(handle);
2660         auto* meta = handle.gridMetaData();
2661         EXPECT_TRUE(meta);
2662         EXPECT_FALSE(meta->isEmpty());
2663         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2664         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2665         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2666         EXPECT_EQ("", std::string(meta->shortGridName()));
2667         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
2668         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2669         auto* dstGrid = handle.grid<VoxelT>();
2670         EXPECT_TRUE(dstGrid);
2671         EXPECT_EQ("", std::string(dstGrid->gridName()));
2672         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
2673         EXPECT_EQ(1.0f, dstGrid->tree().root().minimum());
2674         EXPECT_EQ(3.0f, dstGrid->tree().root().maximum());
2675         EXPECT_EQ(2.0f, dstGrid->tree().root().average());
2676         EXPECT_TRUE(dstGrid->isBreadthFirst());
2677         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
2678         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
2679         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
2680         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
2681         EXPECT_TRUE(dstGrid->isSequential<2>());
2682         EXPECT_TRUE(dstGrid->isSequential<1>());
2683         EXPECT_TRUE(dstGrid->isSequential<0>());
2684 
2685         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2686         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(1, 2, 3));
2687         EXPECT_TRUE(leaf);
2688         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
2689         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
2690         EXPECT_EQ(nanovdb::Coord(1,2,3), leaf->data()->mBBoxMin);
2691         //const auto offset = nanovdb::NanoLeaf<nanovdb::Fp4>::CoordToOffset(nanovdb::Coord(1, 2, 3));
2692         //std::cerr << "offset = " << offset << std::endl;
2693         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1]) << std::endl;
2694         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1] >> 4) << std::endl;
2695 
2696         EXPECT_EQ(1.0f, dstGrid->tree().getValue(nanovdb::Coord(1, 2, 3)));
2697         auto dstAcc = dstGrid->getAccessor();
2698         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(1, 2, 3)));
2699         EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2700         EXPECT_EQ(2.0f, dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2701         EXPECT_EQ(3.0f, dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2702         //std::cerr << dstGrid->indexBBox() << std::endl;
2703         EXPECT_EQ(nanovdb::Coord(-10,-12,-50), dstGrid->indexBBox()[0]);
2704         EXPECT_EQ(nanovdb::Coord( 50, 20, 30), dstGrid->indexBBox()[1]);
2705     }
2706     {// Sphere
2707         const double voxelSize = 0.1, halfWidth = 3.0;
2708         const float radius = 10.0f;
2709         const nanovdb::Vec3f center(0);
2710         const nanovdb::Vec3d origin(0);
2711         const float tolerance = 0.5f * voxelSize;
2712 
2713         auto handle = nanovdb::createLevelSetSphere<float, VoxelT>(radius, center,
2714                                                                    voxelSize, halfWidth,
2715                                                                    origin, "sphere",
2716                                                                    nanovdb::StatsMode::Default,
2717                                                                    nanovdb::ChecksumMode::Default,
2718                                                                    tolerance,
2719                                                                    false);
2720         auto* nanoGrid = handle.grid<VoxelT>();
2721         EXPECT_TRUE(nanoGrid);
2722         Sphere<float> sphere(center, radius, float(voxelSize), float(halfWidth));
2723         auto kernel = [&](const nanovdb::CoordBBox& bbox) {
2724             auto nanoAcc = nanoGrid->getAccessor();
2725             for (auto it = bbox.begin(); it; ++it) {
2726                 const nanovdb::Coord p = *it;
2727                 EXPECT_NEAR(nanoAcc.getValue(p), sphere(p), tolerance);
2728             }
2729         };
2730         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
2731 
2732         nanovdb::io::writeGrid("data/sphere_fp4.nvdb", handle);
2733         handle = nanovdb::io::readGrid("data/sphere_fp4.nvdb");
2734         nanoGrid = handle.grid<VoxelT>();
2735         EXPECT_TRUE(nanoGrid);
2736 
2737         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
2738     }
2739 } // GridBuilder_Fp4
2740 
TEST_F(TestNanoVDB,GridBuilder_Fp8)2741 TEST_F(TestNanoVDB, GridBuilder_Fp8)
2742 {
2743     using VoxelT = nanovdb::Fp8;
2744     EXPECT_EQ(96u + 512u, sizeof(nanovdb::NanoLeaf<VoxelT>));
2745     { // 3 grid point
2746         nanovdb::GridBuilder<float, VoxelT> builder(0.0f);
2747         auto srcAcc = builder.getAccessor();
2748         srcAcc.setValue(nanovdb::Coord(  1,  2,  3), 1.0f);
2749         srcAcc.setValue(nanovdb::Coord(-10, 20,-50), 2.0f);
2750         srcAcc.setValue(nanovdb::Coord( 50,-12, 30), 3.0f);
2751         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2752         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(1, 2, 3)));
2753         EXPECT_EQ(1.0f, srcAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2754         EXPECT_EQ(2.0f, srcAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2755         EXPECT_EQ(3.0f, srcAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2756 
2757         builder.setStats(nanovdb::StatsMode::All);
2758         auto handle = builder.getHandle();
2759 
2760         EXPECT_TRUE(handle);
2761         auto* meta = handle.gridMetaData();
2762         EXPECT_TRUE(meta);
2763         EXPECT_FALSE(meta->isEmpty());
2764         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2765         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2766         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2767         EXPECT_EQ("", std::string(meta->shortGridName()));
2768         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
2769         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2770         auto* dstGrid = handle.grid<VoxelT>();
2771         EXPECT_TRUE(dstGrid);
2772         EXPECT_EQ("", std::string(dstGrid->gridName()));
2773         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
2774         EXPECT_EQ(1.0f, dstGrid->tree().root().minimum());
2775         EXPECT_EQ(3.0f, dstGrid->tree().root().maximum());
2776         EXPECT_EQ(2.0f, dstGrid->tree().root().average());
2777         EXPECT_TRUE(dstGrid->isBreadthFirst());
2778         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
2779         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
2780         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
2781         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
2782         EXPECT_TRUE(dstGrid->isSequential<2>());
2783         EXPECT_TRUE(dstGrid->isSequential<1>());
2784         EXPECT_TRUE(dstGrid->isSequential<0>());
2785 
2786         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2787         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(1, 2, 3));
2788         EXPECT_TRUE(leaf);
2789         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
2790         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
2791         EXPECT_EQ(nanovdb::Coord(1,2,3), leaf->data()->mBBoxMin);
2792         //const auto offset = nanovdb::NanoLeaf<nanovdb::Fp4>::CoordToOffset(nanovdb::Coord(1, 2, 3));
2793         //std::cerr << "offset = " << offset << std::endl;
2794         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1]) << std::endl;
2795         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1] >> 4) << std::endl;
2796 
2797         EXPECT_EQ(1.0f, dstGrid->tree().getValue(nanovdb::Coord(1, 2, 3)));
2798         auto dstAcc = dstGrid->getAccessor();
2799         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(1, 2, 3)));
2800         EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2801         EXPECT_EQ(2.0f, dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2802         EXPECT_EQ(3.0f, dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2803         //std::cerr << dstGrid->indexBBox() << std::endl;
2804         EXPECT_EQ(nanovdb::Coord(-10,-12,-50), dstGrid->indexBBox()[0]);
2805         EXPECT_EQ(nanovdb::Coord( 50, 20, 30), dstGrid->indexBBox()[1]);
2806     }
2807     {// Sphere
2808         const double voxelSize = 0.1, halfWidth = 3.0;
2809         const float radius = 10.0f;
2810         const nanovdb::Vec3f center(0);
2811         const nanovdb::Vec3d origin(0);
2812         const float tolerance = 0.05f * voxelSize;
2813 
2814         auto handle = nanovdb::createLevelSetSphere<float, VoxelT>(radius, center,
2815                                                                    voxelSize, halfWidth,
2816                                                                    origin, "sphere",
2817                                                                    nanovdb::StatsMode::Default,
2818                                                                    nanovdb::ChecksumMode::Default,
2819                                                                    tolerance,
2820                                                                    false);
2821         auto* nanoGrid = handle.grid<VoxelT>();
2822         EXPECT_TRUE(nanoGrid);
2823         Sphere<float> sphere(center, radius, float(voxelSize), float(halfWidth));
2824         auto kernel = [&](const nanovdb::CoordBBox& bbox) {
2825             auto nanoAcc = nanoGrid->getAccessor();
2826             for (auto it = bbox.begin(); it; ++it) {
2827                 const nanovdb::Coord p = *it;
2828                 EXPECT_NEAR(nanoAcc.getValue(p), sphere(p), tolerance);
2829             }
2830         };
2831         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
2832 
2833         nanovdb::io::writeGrid("data/sphere_fp8.nvdb", handle);
2834         handle = nanovdb::io::readGrid("data/sphere_fp8.nvdb");
2835         nanoGrid = handle.grid<VoxelT>();
2836         EXPECT_TRUE(nanoGrid);
2837 
2838         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
2839     }
2840 } // GridBuilder_Fp8
2841 
TEST_F(TestNanoVDB,GridBuilder_Fp16)2842 TEST_F(TestNanoVDB, GridBuilder_Fp16)
2843 {
2844     using VoxelT = nanovdb::Fp16;
2845     EXPECT_EQ(96u + 512u*2, sizeof(nanovdb::NanoLeaf<VoxelT>));
2846     { // 3 grid point
2847         nanovdb::GridBuilder<float, VoxelT> builder(0.0f);
2848         auto srcAcc = builder.getAccessor();
2849         srcAcc.setValue(nanovdb::Coord(  1,  2,  3), 1.0f);
2850         srcAcc.setValue(nanovdb::Coord(-10, 20,-50), 2.0f);
2851         srcAcc.setValue(nanovdb::Coord( 50,-12, 30), 3.0f);
2852         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
2853         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(1, 2, 3)));
2854         EXPECT_EQ(1.0f, srcAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2855         EXPECT_EQ(2.0f, srcAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2856         EXPECT_EQ(3.0f, srcAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2857 
2858         builder.setStats(nanovdb::StatsMode::All);
2859         auto handle = builder.getHandle();
2860 
2861         EXPECT_TRUE(handle);
2862         auto* meta = handle.gridMetaData();
2863         EXPECT_TRUE(meta);
2864         EXPECT_FALSE(meta->isEmpty());
2865         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2866         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2867         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2868         EXPECT_EQ("", std::string(meta->shortGridName()));
2869         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
2870         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2871         auto* dstGrid = handle.grid<VoxelT>();
2872         EXPECT_TRUE(dstGrid);
2873         EXPECT_EQ("", std::string(dstGrid->gridName()));
2874         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
2875         EXPECT_EQ(1.0f, dstGrid->tree().root().minimum());
2876         EXPECT_EQ(3.0f, dstGrid->tree().root().maximum());
2877         EXPECT_EQ(2.0f, dstGrid->tree().root().average());
2878         EXPECT_TRUE(dstGrid->isBreadthFirst());
2879         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
2880         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
2881         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
2882         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
2883         EXPECT_TRUE(dstGrid->isSequential<2>());
2884         EXPECT_TRUE(dstGrid->isSequential<1>());
2885         EXPECT_TRUE(dstGrid->isSequential<0>());
2886 
2887         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2888         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(1, 2, 3));
2889         EXPECT_TRUE(leaf);
2890         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
2891         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
2892         EXPECT_EQ(nanovdb::Coord(1,2,3), leaf->data()->mBBoxMin);
2893         //const auto offset = nanovdb::NanoLeaf<nanovdb::Fp4>::CoordToOffset(nanovdb::Coord(1, 2, 3));
2894         //std::cerr << "offset = " << offset << std::endl;
2895         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1]) << std::endl;
2896         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1] >> 4) << std::endl;
2897 
2898         EXPECT_EQ(1.0f, dstGrid->tree().getValue(nanovdb::Coord(1, 2, 3)));
2899         auto dstAcc = dstGrid->getAccessor();
2900         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(1, 2, 3)));
2901         EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(  1,  2,  3)));
2902         EXPECT_EQ(2.0f, dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2903         EXPECT_EQ(3.0f, dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2904         //std::cerr << dstGrid->indexBBox() << std::endl;
2905         EXPECT_EQ(nanovdb::Coord(-10,-12,-50), dstGrid->indexBBox()[0]);
2906         EXPECT_EQ(nanovdb::Coord( 50, 20, 30), dstGrid->indexBBox()[1]);
2907     }
2908     {// Sphere
2909         const double voxelSize = 0.1, halfWidth = 3.0;
2910         const float radius = 10.0f;
2911         const nanovdb::Vec3f center(0);
2912         const nanovdb::Vec3d origin(0);
2913         const float tolerance = 0.005f * voxelSize;
2914 
2915         auto handle = nanovdb::createLevelSetSphere<float, VoxelT>(radius, center,
2916                                                                    voxelSize, halfWidth,
2917                                                                    origin, "sphere",
2918                                                                    nanovdb::StatsMode::Default,
2919                                                                    nanovdb::ChecksumMode::Default,
2920                                                                    tolerance,
2921                                                                    false);
2922         auto* nanoGrid = handle.grid<VoxelT>();
2923         EXPECT_TRUE(nanoGrid);
2924         Sphere<float> sphere(center, radius, float(voxelSize), float(halfWidth));
2925         auto kernel = [&](const nanovdb::CoordBBox& bbox) {
2926             auto nanoAcc = nanoGrid->getAccessor();
2927             for (auto it = bbox.begin(); it; ++it) {
2928                 const nanovdb::Coord p = *it;
2929                 EXPECT_NEAR(nanoAcc.getValue(p), sphere(p), tolerance);
2930             }
2931         };
2932         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
2933 
2934         nanovdb::io::writeGrid("data/sphere_fp16.nvdb", handle);
2935         handle = nanovdb::io::readGrid("data/sphere_fp16.nvdb");
2936         nanoGrid = handle.grid<VoxelT>();
2937         EXPECT_TRUE(nanoGrid);
2938 
2939         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
2940     }
2941 } // GridBuilder_Fp16
2942 
TEST_F(TestNanoVDB,GridBuilder_FpN_Basic1)2943 TEST_F(TestNanoVDB, GridBuilder_FpN_Basic1)
2944 {
2945     using VoxelT = nanovdb::FpN;
2946     EXPECT_EQ(96u, sizeof(nanovdb::NanoLeaf<VoxelT>));
2947     { // 1 grid point
2948         nanovdb::GridBuilder<float, VoxelT> builder(0.0f);
2949         auto srcAcc = builder.getAccessor();
2950         srcAcc.setValue(nanovdb::Coord(  0,  0,  0), 1.0f);
2951         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(0, 0, 0)));
2952         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(0, 0, 0)));
2953         EXPECT_EQ(1.0f, srcAcc.getValue(nanovdb::Coord(  0,  0,  0)));
2954 
2955         builder.setStats(nanovdb::StatsMode::All);
2956         //builder.setVerbose(true);
2957         auto handle = builder.getHandle();
2958 
2959         EXPECT_TRUE(handle);
2960         auto* meta = handle.gridMetaData();
2961         EXPECT_TRUE(meta);
2962         EXPECT_FALSE(meta->isEmpty());
2963         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
2964         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
2965         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
2966         EXPECT_EQ("", std::string(meta->shortGridName()));
2967         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
2968         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
2969         auto* dstGrid = handle.grid<VoxelT>();
2970         EXPECT_TRUE(dstGrid);
2971         EXPECT_EQ("", std::string(dstGrid->gridName()));
2972         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
2973         EXPECT_EQ(1.0f, dstGrid->tree().getValue(nanovdb::Coord(0, 0, 0)));
2974         EXPECT_EQ(1.0f, dstGrid->tree().root().minimum());
2975         EXPECT_EQ(1.0f, dstGrid->tree().root().maximum());
2976         EXPECT_EQ(1.0f, dstGrid->tree().root().average());
2977         EXPECT_TRUE(dstGrid->isBreadthFirst());
2978         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
2979         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
2980         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
2981         EXPECT_FALSE(dstGrid->isSequential<GridT::TreeType::Node0>());
2982         EXPECT_TRUE(dstGrid->isSequential<2>());
2983         EXPECT_TRUE(dstGrid->isSequential<1>());
2984         EXPECT_FALSE(dstGrid->isSequential<0>());
2985 
2986         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
2987         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(0, 0, 0));
2988         EXPECT_TRUE(leaf);
2989         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
2990         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
2991         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->data()->mBBoxMin);
2992 
2993         EXPECT_EQ(1.0f, dstGrid->tree().getValue(nanovdb::Coord(0, 0, 0)));
2994         auto dstAcc = dstGrid->getAccessor();
2995         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(0, 0, 0)));
2996         EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord( 0,  0,  0)));
2997         EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
2998         EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
2999         //std::cerr << dstGrid->indexBBox() << std::endl;
3000         EXPECT_EQ(nanovdb::Coord(0,0,0), dstGrid->indexBBox()[0]);
3001         EXPECT_EQ(nanovdb::Coord(0,0,0), dstGrid->indexBBox()[1]);
3002     }
3003 }// GridBuilder_FpN_Basic1
3004 
TEST_F(TestNanoVDB,GridBuilder_FpN_Basic3)3005 TEST_F(TestNanoVDB, GridBuilder_FpN_Basic3)
3006 {
3007     using VoxelT = nanovdb::FpN;
3008     EXPECT_EQ(96u, sizeof(nanovdb::NanoLeaf<VoxelT>));
3009     { // 3 grid point
3010         nanovdb::GridBuilder<float, VoxelT> builder(0.0f);
3011         auto srcAcc = builder.getAccessor();
3012         srcAcc.setValue(nanovdb::Coord(  1,  2,  3), 1.0f);
3013         srcAcc.setValue(nanovdb::Coord(-10, 20,-50), 2.0f);
3014         srcAcc.setValue(nanovdb::Coord( 50,-12, 30), 3.0f);
3015         EXPECT_TRUE(srcAcc.isActive(nanovdb::Coord(1, 2, 3)));
3016         EXPECT_TRUE(srcAcc.isValueOn(nanovdb::Coord(1, 2, 3)));
3017         EXPECT_EQ(1.0f, srcAcc.getValue(nanovdb::Coord(  1,  2,  3)));
3018         EXPECT_EQ(2.0f, srcAcc.getValue(nanovdb::Coord(-10, 20,-50)));
3019         EXPECT_EQ(3.0f, srcAcc.getValue(nanovdb::Coord( 50,-12, 30)));
3020 
3021         builder.setStats(nanovdb::StatsMode::All);
3022         //builder.setVerbose(true);
3023         auto handle = builder.getHandle();
3024 
3025         EXPECT_TRUE(handle);
3026         auto* meta = handle.gridMetaData();
3027         EXPECT_TRUE(meta);
3028         EXPECT_FALSE(meta->isEmpty());
3029         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
3030         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
3031         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
3032         EXPECT_EQ("", std::string(meta->shortGridName()));
3033         EXPECT_EQ(nanovdb::mapToGridType<VoxelT>(), meta->gridType());
3034         EXPECT_EQ(nanovdb::GridClass::Unknown, meta->gridClass());
3035         auto* dstGrid = handle.grid<VoxelT>();
3036         EXPECT_TRUE(dstGrid);
3037         EXPECT_EQ("", std::string(dstGrid->gridName()));
3038         EXPECT_EQ((const char*)handle.data(), (const char*)dstGrid);
3039         EXPECT_EQ(1.0f, dstGrid->tree().root().minimum());
3040         EXPECT_EQ(3.0f, dstGrid->tree().root().maximum());
3041         EXPECT_EQ(2.0f, dstGrid->tree().root().average());
3042         EXPECT_TRUE(dstGrid->isBreadthFirst());
3043         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
3044         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
3045         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
3046         EXPECT_FALSE(dstGrid->isSequential<GridT::TreeType::Node0>());
3047         EXPECT_TRUE(dstGrid->isSequential<2>());
3048         EXPECT_TRUE(dstGrid->isSequential<1>());
3049         EXPECT_FALSE(dstGrid->isSequential<0>());
3050 
3051         EXPECT_EQ(nanovdb::Vec3R(1.0), dstGrid->voxelSize());
3052         auto *leaf = dstGrid->tree().root().probeLeaf(nanovdb::Coord(1, 2, 3));
3053         EXPECT_TRUE(leaf);
3054         //std::cerr << leaf->origin() << ", " << leaf->data()->mBBoxMin << std::endl;
3055         EXPECT_EQ(nanovdb::Coord(0,0,0), leaf->origin());
3056         EXPECT_EQ(nanovdb::Coord(1,2,3), leaf->data()->mBBoxMin);
3057         //const auto offset = nanovdb::NanoLeaf<nanovdb::Fp4>::CoordToOffset(nanovdb::Coord(1, 2, 3));
3058         //std::cerr << "offset = " << offset << std::endl;
3059         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1]) << std::endl;
3060         //std::cerr << "code = " <<  int(leaf->data()->mCode[offset>>1] >> 4) << std::endl;
3061 
3062         EXPECT_EQ(1.0f, dstGrid->tree().getValue(nanovdb::Coord(1, 2, 3)));
3063         auto dstAcc = dstGrid->getAccessor();
3064         EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(1, 2, 3)));
3065         EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(  1,  2,  3)));
3066         EXPECT_EQ(2.0f, dstAcc.getValue(nanovdb::Coord(-10, 20,-50)));
3067         EXPECT_EQ(3.0f, dstAcc.getValue(nanovdb::Coord( 50,-12, 30)));
3068         //std::cerr << dstGrid->indexBBox() << std::endl;
3069         EXPECT_EQ(nanovdb::Coord(-10,-12,-50), dstGrid->indexBBox()[0]);
3070         EXPECT_EQ(nanovdb::Coord( 50, 20, 30), dstGrid->indexBBox()[1]);
3071     }
3072 }// GridBuilder_FpN_Basic3
3073 
TEST_F(TestNanoVDB,GridBuilder_FpN_Sphere)3074 TEST_F(TestNanoVDB, GridBuilder_FpN_Sphere)
3075 {
3076     using VoxelT = nanovdb::FpN;
3077     EXPECT_EQ(96u, sizeof(nanovdb::NanoLeaf<VoxelT>));
3078     {// Sphere
3079         const double voxelSize = 0.1, halfWidth = 3.0;
3080         const float radius = 10.0f;
3081         const nanovdb::Vec3f center(0);
3082         const nanovdb::Vec3d origin(0);
3083         const float tolerance = 0.5f * voxelSize;
3084 
3085         auto handle = nanovdb::createLevelSetSphere<float, VoxelT>(radius, center,
3086                                                                    voxelSize, halfWidth,
3087                                                                    origin, "sphere",
3088                                                                    nanovdb::StatsMode::Default,
3089                                                                    nanovdb::ChecksumMode::Default,
3090                                                                    tolerance,
3091                                                                    false);
3092         auto* nanoGrid = handle.grid<VoxelT>();
3093         EXPECT_TRUE(nanoGrid);
3094         Sphere<float> sphere(center, radius, float(voxelSize), float(halfWidth));
3095         auto kernel = [&](const nanovdb::CoordBBox& bbox) {
3096             auto nanoAcc = nanoGrid->getAccessor();
3097             for (auto it = bbox.begin(); it; ++it) {
3098                 const nanovdb::Coord p = *it;
3099                 EXPECT_NEAR(nanoAcc.getValue(p), sphere(p), tolerance);
3100             }
3101         };
3102         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
3103 
3104         nanovdb::io::writeGrid("data/sphere_fpN.nvdb", handle);
3105         handle = nanovdb::io::readGrid("data/sphere_fpN.nvdb");
3106         nanoGrid = handle.grid<VoxelT>();
3107         EXPECT_TRUE(nanoGrid);
3108 
3109         nanovdb::forEach(nanoGrid->indexBBox(), kernel);
3110     }
3111 } // GridBuilder_FpN_Sphere
3112 
TEST_F(TestNanoVDB,NodeManager)3113 TEST_F(TestNanoVDB, NodeManager)
3114 {
3115      { // 1 active voxel
3116         nanovdb::GridBuilder<float> builder(0.0f);
3117         auto                        srcAcc = builder.getAccessor();
3118         builder.setGridClass(nanovdb::GridClass::LevelSet);
3119         const nanovdb::Coord x0(1, 2, 3), x1(1, 2, 4);
3120         srcAcc.setValue(x1, 1.0f);
3121         auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
3122         EXPECT_TRUE(handle);
3123         auto* dstGrid = handle.grid<float>();
3124         EXPECT_TRUE(dstGrid);
3125         EXPECT_TRUE(dstGrid->isBreadthFirst());
3126         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
3127         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
3128         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
3129         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
3130 
3131         auto nodeMgr = nanovdb::createNodeMgr(*dstGrid);
3132         auto leafMgr = nanovdb::createLeafMgr(*dstGrid);
3133         EXPECT_FALSE(nodeMgr.empty());
3134         EXPECT_FALSE(leafMgr.empty());
3135         EXPECT_EQ(1u, nodeMgr.nodeCount(2));
3136         EXPECT_EQ(1u, nodeMgr.nodeCount(1));
3137         EXPECT_EQ(1u, nodeMgr.nodeCount(0));
3138         EXPECT_EQ(1u, leafMgr.size());
3139 
3140         EXPECT_EQ(0.0f, nodeMgr.grid()->tree().getValue(x0));
3141         EXPECT_EQ(0.0f, nodeMgr.tree()->getValue(x0));
3142         EXPECT_EQ(0.0f, nodeMgr.root()->getValue(x0));
3143         EXPECT_EQ(0.0f, nodeMgr.upper(0)->getValue(x0));
3144         EXPECT_EQ(0.0f, nodeMgr.lower(0)->getValue(x0));
3145         EXPECT_EQ(0.0f, nodeMgr.leaf(0)->getValue(x0));
3146         EXPECT_EQ(0.0f, leafMgr[0]->getValue(x0));
3147 
3148         EXPECT_EQ(1.0f, nodeMgr.grid()->tree().getValue(x1));
3149         EXPECT_EQ(1.0f, nodeMgr.tree()->getValue(x1));
3150         EXPECT_EQ(1.0f, nodeMgr.root()->getValue(x1));
3151         EXPECT_EQ(1.0f, nodeMgr.upper(0)->getValue(x1));
3152         EXPECT_EQ(1.0f, nodeMgr.lower(0)->getValue(x1));
3153         EXPECT_EQ(1.0f, nodeMgr.leaf(0)->getValue(x1));
3154         EXPECT_EQ(1.0f, leafMgr[0]->getValue(x1));
3155 
3156         EXPECT_EQ(leafMgr[0], nodeMgr.leaf(0));
3157         EXPECT_EQ(leafMgr[0], dstGrid->tree().getFirstNode< 0 >());
3158         EXPECT_EQ(leafMgr[0], dstGrid->tree().getFirstNode< nanovdb::NanoLeaf<float> >());
3159         EXPECT_EQ(nodeMgr.leaf(0),  dstGrid->tree().getFirstNode< nanovdb::NanoLeaf< float> >());
3160         EXPECT_EQ(nodeMgr.lower(0), dstGrid->tree().getFirstNode< nanovdb::NanoLower<float> >());
3161         EXPECT_EQ(nodeMgr.upper(0), dstGrid->tree().getFirstNode< nanovdb::NanoUpper<float> >());
3162         EXPECT_EQ(nodeMgr.leaf(0),  dstGrid->tree().getFirstNode< 0 >());
3163         EXPECT_EQ(nodeMgr.lower(0), dstGrid->tree().getFirstNode< 1 >());
3164         EXPECT_EQ(nodeMgr.upper(0), dstGrid->tree().getFirstNode< 2 >());
3165     }
3166     { // 2 active voxels
3167         nanovdb::GridBuilder<float> builder(0.0f, nanovdb::GridClass::LevelSet);
3168         auto                        srcAcc = builder.getAccessor();
3169         const nanovdb::Coord x0(1, 2, 3), x1(2,-2, 9), x2(1, 2, 4);
3170         srcAcc.setValue(x1, 1.0f);
3171         srcAcc.setValue(x2, 2.0f);
3172         auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
3173         EXPECT_TRUE(handle);
3174         auto* dstGrid = handle.grid<float>();
3175         EXPECT_TRUE(dstGrid);
3176         EXPECT_EQ(1.0f, dstGrid->tree().getValue(x1));
3177         EXPECT_EQ(2.0f, dstGrid->tree().getValue(x2));
3178         EXPECT_TRUE(dstGrid->isBreadthFirst());
3179         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
3180         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
3181         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
3182         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
3183 
3184         nanovdb::NodeManager<nanovdb::FloatGrid> nodeMgr;
3185         EXPECT_TRUE(nodeMgr.empty());
3186         nanovdb::NodeManager<nanovdb::FloatGrid> tmp(*dstGrid);
3187         EXPECT_FALSE(tmp.empty());
3188         //mgr = tmp;// fails to compile since assignment operator is deleted
3189         nodeMgr = std::move(tmp);
3190         auto leafMgr = nanovdb::createLeafMgr(*dstGrid);
3191         EXPECT_FALSE(nodeMgr.empty());
3192         EXPECT_FALSE(leafMgr.empty());
3193         EXPECT_EQ(2u, nodeMgr.nodeCount(2));
3194         EXPECT_EQ(2u, nodeMgr.nodeCount(1));
3195         EXPECT_EQ(2u, nodeMgr.nodeCount(0));
3196         EXPECT_EQ(2u, leafMgr.size());
3197 
3198         EXPECT_EQ(0.0f, nodeMgr.grid()->tree().getValue(x0));
3199         EXPECT_EQ(0.0f, nodeMgr.tree()->getValue(x0));
3200         EXPECT_EQ(0.0f, nodeMgr.root()->getValue(x0));
3201         EXPECT_EQ(0.0f, nodeMgr.upper(0)->getValue(x0));
3202         EXPECT_EQ(0.0f, nodeMgr.lower(0)->getValue(x0));
3203         EXPECT_EQ(0.0f, nodeMgr.leaf(0)->getValue(x0));
3204         EXPECT_EQ(0.0f, leafMgr[0]->getValue(x0));
3205 
3206         EXPECT_EQ(1.0f, nodeMgr.grid()->tree().getValue(x1));
3207         EXPECT_EQ(1.0f, nodeMgr.tree()->getValue(x1));
3208         EXPECT_EQ(1.0f, nodeMgr.root()->getValue(x1));
3209         EXPECT_EQ(1.0f, nodeMgr.upper(0)->getValue(x1));
3210         EXPECT_EQ(1.0f, nodeMgr.lower(0)->getValue(x1));
3211         EXPECT_EQ(1.0f, nodeMgr.leaf(0)->getValue(x1));
3212         EXPECT_EQ(1.0f, leafMgr[0]->getValue(x1));
3213 
3214         EXPECT_EQ(2.0f, nodeMgr.grid()->tree().getValue(x2));
3215         EXPECT_EQ(2.0f, nodeMgr.tree()->getValue(x2));
3216         EXPECT_EQ(2.0f, nodeMgr.root()->getValue(x2));
3217         EXPECT_EQ(2.0f, nodeMgr.upper(1)->getValue(x2));
3218         EXPECT_EQ(2.0f, nodeMgr.lower(1)->getValue(x2));
3219         EXPECT_EQ(2.0f, nodeMgr.leaf(1)->getValue(x2));
3220         EXPECT_EQ(2.0f, leafMgr[1]->getValue(x2));
3221 
3222         EXPECT_EQ(leafMgr[0], nodeMgr.leaf(0));
3223         EXPECT_EQ(leafMgr[1], nodeMgr.leaf(1));
3224     }
3225     {// random points
3226         const size_t voxelCount = 512;
3227         const int min = -10000, max = 10000;
3228         std::vector<nanovdb::Coord> voxels;
3229         std::srand(98765);
3230         auto op = [&](){return rand() % (max - min) + min;};
3231         while (voxels.size() <  voxelCount) {
3232             const nanovdb::Coord ijk(op(), op(), op());
3233             if (voxels.end() == std::find(voxels.begin(), voxels.end(), ijk)) {
3234                 voxels.push_back(ijk);
3235             }
3236         }
3237         EXPECT_EQ(voxelCount, voxels.size());
3238         nanovdb::GridBuilder<float> builder(-1.0f, nanovdb::GridClass::LevelSet);
3239         auto                        srcAcc = builder.getAccessor();
3240         for (size_t i=0; i<voxelCount; ++i) {
3241             srcAcc.setValue(voxels[i], float(i));
3242         }
3243         auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
3244         EXPECT_TRUE(handle);
3245         auto* dstGrid = handle.grid<float>();
3246         EXPECT_TRUE(dstGrid);
3247         EXPECT_TRUE(dstGrid->isBreadthFirst());
3248         using GridT = std::remove_pointer<decltype(dstGrid)>::type;
3249         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node2>());
3250         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node1>());
3251         EXPECT_TRUE(dstGrid->isSequential<GridT::TreeType::Node0>());
3252 
3253         auto nodeMgr = nanovdb::createNodeMgr(*dstGrid);
3254         auto leafMgr = nanovdb::createLeafMgr(*dstGrid);
3255         EXPECT_FALSE(nodeMgr.empty());
3256         EXPECT_FALSE(leafMgr.empty());
3257         EXPECT_EQ(nodeMgr.nodeCount(0), leafMgr.size());
3258         auto dstAcc = dstGrid->getAccessor();
3259         for (size_t i=0; i<voxelCount; ++i) {
3260             EXPECT_EQ(float(i), dstAcc.getValue(voxels[i]));
3261             EXPECT_EQ(nodeMgr.leaf(i), leafMgr[i]);
3262         }
3263     }
3264 } // NodeManager
3265 
TEST_F(TestNanoVDB,GridBuilderBasicDense)3266 TEST_F(TestNanoVDB, GridBuilderBasicDense)
3267 {
3268     { // dense functor
3269         nanovdb::GridBuilder<float> builder(0.0f, nanovdb::GridClass::LevelSet);
3270         auto                        srcAcc = builder.getAccessor();
3271         const nanovdb::CoordBBox    bbox(nanovdb::Coord(0), nanovdb::Coord(100));
3272         auto                        func = [](const nanovdb::Coord&) { return 1.0f; };
3273         //auto                        func = [](const nanovdb::Coord&, float &v) { v = 1.0f; return true; };
3274         builder(func, bbox);
3275         for (auto ijk = bbox.begin(); ijk; ++ijk) {
3276             EXPECT_EQ(1.0f, srcAcc.getValue(*ijk));
3277             EXPECT_TRUE(srcAcc.isActive(*ijk));
3278         }
3279         auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
3280         EXPECT_TRUE(handle);
3281         auto* meta = handle.gridMetaData();
3282         EXPECT_TRUE(meta);
3283         EXPECT_FALSE(meta->isEmpty());
3284         //EXPECT_TRUE(meta->version().isValid());
3285         EXPECT_EQ(uint32_t(NANOVDB_MAJOR_VERSION_NUMBER), meta->version().getMajor());
3286         EXPECT_EQ(uint32_t(NANOVDB_MINOR_VERSION_NUMBER), meta->version().getMinor());
3287         EXPECT_EQ(uint32_t(NANOVDB_PATCH_VERSION_NUMBER), meta->version().getPatch());
3288         EXPECT_EQ("test", std::string(meta->shortGridName()));
3289         EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3290         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta->gridClass());
3291         auto* dstGrid = handle.grid<float>();
3292         EXPECT_TRUE(dstGrid);
3293         EXPECT_EQ("test", std::string(dstGrid->gridName()));
3294         const nanovdb::Coord dim = bbox.dim();
3295         EXPECT_EQ(nanovdb::Coord(101), dim);
3296         EXPECT_EQ(101u * 101u * 101u, dstGrid->activeVoxelCount());
3297         auto dstAcc = dstGrid->getAccessor();
3298         for (nanovdb::Coord ijk = bbox[0]; ijk[0] <= bbox[1][0]; ++ijk[0]) {
3299             for (ijk[1] = bbox[0][1]; ijk[1] <= bbox[1][1]; ++ijk[1]) {
3300                 for (ijk[2] = bbox[0][2]; ijk[2] <= bbox[1][2]; ++ijk[2]) {
3301                     EXPECT_EQ(1.0f, dstAcc.getValue(ijk));
3302                 }
3303             }
3304         }
3305         EXPECT_EQ(bbox[0], dstGrid->indexBBox()[0]);
3306         EXPECT_EQ(bbox[1], dstGrid->indexBBox()[1]);
3307 
3308         EXPECT_EQ(dstGrid->tree().root().minimum(), 1.0f);// smallest active value
3309         EXPECT_EQ(dstGrid->tree().root().maximum(), 1.0f);// largest active value
3310         EXPECT_EQ(dstGrid->tree().root().average(),  1.0f);
3311         EXPECT_EQ(dstGrid->tree().root().variance(), 0.0f);
3312         EXPECT_EQ(dstGrid->tree().root().stdDeviation(), 0.0f);
3313     }
3314 } // GridBuilderDense
3315 
TEST_F(TestNanoVDB,GridBuilderBackground)3316 TEST_F(TestNanoVDB, GridBuilderBackground)
3317 {
3318     {
3319         nanovdb::GridBuilder<float> builder(0.5f);
3320         auto                        acc = builder.getAccessor();
3321 
3322         acc.setValue(nanovdb::Coord(1), 1);
3323         acc.setValue(nanovdb::Coord(2), 0);
3324 
3325         EXPECT_EQ(0.5f, acc.getValue(nanovdb::Coord(0)));
3326         EXPECT_FALSE(acc.isActive(nanovdb::Coord(0)));
3327         EXPECT_EQ(1, acc.getValue(nanovdb::Coord(1)));
3328         EXPECT_TRUE(acc.isActive(nanovdb::Coord(1)));
3329         EXPECT_EQ(0, acc.getValue(nanovdb::Coord(2)));
3330         EXPECT_TRUE(acc.isActive(nanovdb::Coord(1)));
3331 
3332         auto gridHdl = builder.getHandle<>();
3333         auto grid = gridHdl.grid<float>();
3334         EXPECT_TRUE(grid);
3335         EXPECT_FALSE(grid->isEmpty());
3336         EXPECT_EQ(0.5, grid->tree().getValue(nanovdb::Coord(0)));
3337         EXPECT_EQ(1, grid->tree().getValue(nanovdb::Coord(1)));
3338         EXPECT_EQ(0, grid->tree().getValue(nanovdb::Coord(2)));
3339     }
3340 } // GridBuilderBackground
3341 
TEST_F(TestNanoVDB,GridBuilderSphere)3342 TEST_F(TestNanoVDB, GridBuilderSphere)
3343 {
3344     Sphere<float> sphere(nanovdb::Vec3<float>(50), 20.0f);
3345     EXPECT_EQ(3.0f, sphere.background());
3346     EXPECT_EQ(3.0f, sphere(nanovdb::Coord(100)));
3347     EXPECT_EQ(-3.0f, sphere(nanovdb::Coord(50)));
3348     EXPECT_EQ(0.0f, sphere(nanovdb::Coord(50, 50, 70)));
3349     EXPECT_EQ(-1.0f, sphere(nanovdb::Coord(50, 50, 69)));
3350     EXPECT_EQ(2.0f, sphere(nanovdb::Coord(50, 50, 72)));
3351 
3352     nanovdb::GridBuilder<float> builder(sphere.background(), nanovdb::GridClass::LevelSet);
3353     auto                        srcAcc = builder.getAccessor();
3354 
3355     const nanovdb::CoordBBox bbox(nanovdb::Coord(-100), nanovdb::Coord(100));
3356     //mTimer.start("GridBulder Sphere");
3357     builder(sphere, bbox);
3358     //mTimer.stop();
3359 
3360 
3361     auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
3362     EXPECT_TRUE(handle);
3363     EXPECT_EQ(1u, handle.gridCount());
3364     auto* meta = handle.gridMetaData();
3365     EXPECT_TRUE(meta);
3366     EXPECT_EQ("test", std::string(meta->shortGridName()));
3367     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3368     EXPECT_EQ(nanovdb::GridClass::LevelSet, meta->gridClass());
3369     auto* dstGrid = handle.grid<float>();
3370     EXPECT_TRUE(dstGrid);
3371     EXPECT_EQ("test", std::string(dstGrid->gridName()));
3372     EXPECT_TRUE(dstGrid->hasBBox());
3373     EXPECT_TRUE(dstGrid->hasMinMax());
3374     EXPECT_TRUE(dstGrid->hasAverage());
3375     EXPECT_TRUE(dstGrid->hasStdDeviation());
3376 
3377     //mTimer.start("GridBulder NodeMananger");
3378     nanovdb::NodeManager<nanovdb::FloatGrid> mgr(*dstGrid);
3379     //mTimer.stop();
3380     EXPECT_EQ(dstGrid->tree().nodeCount(0), mgr.nodeCount(0));
3381     EXPECT_EQ(dstGrid->tree().nodeCount(1), mgr.nodeCount(1));
3382     EXPECT_EQ(dstGrid->tree().nodeCount(2), mgr.nodeCount(2));
3383 
3384     //std::cerr << "bbox.min = (" << dstGrid->indexBBox()[0][0] << ", " <<  dstGrid->indexBBox()[0][1] << ", " <<  dstGrid->indexBBox()[0][2] << ")" << std::endl;
3385     //std::cerr << "bbox.max = (" << dstGrid->indexBBox()[1][0] << ", " <<  dstGrid->indexBBox()[1][1] << ", " <<  dstGrid->indexBBox()[1][2] << ")" << std::endl;
3386 
3387     uint64_t count = 0;
3388     auto    dstAcc = dstGrid->getAccessor();
3389     for (nanovdb::Coord ijk = bbox[0]; ijk[0] <= bbox[1][0]; ++ijk[0]) {
3390         for (ijk[1] = bbox[0][1]; ijk[1] <= bbox[1][1]; ++ijk[1]) {
3391             for (ijk[2] = bbox[0][2]; ijk[2] <= bbox[1][2]; ++ijk[2]) {
3392                 if (dstAcc.isActive(ijk))
3393                     ++count;
3394                 EXPECT_EQ(sphere(ijk), dstAcc.getValue(ijk));
3395                 EXPECT_EQ(srcAcc.getValue(ijk), dstAcc.getValue(ijk));
3396             }
3397         }
3398     }
3399 
3400     EXPECT_EQ(count, dstGrid->activeVoxelCount());
3401 
3402 } // GridBuilderSphere
3403 
TEST_F(TestNanoVDB,createLevelSetSphere)3404 TEST_F(TestNanoVDB, createLevelSetSphere)
3405 {
3406     Sphere<float> sphere(nanovdb::Vec3<float>(50), 20.0f);
3407     EXPECT_EQ(3.0f, sphere.background());
3408     EXPECT_EQ(3.0f, sphere(nanovdb::Coord(100)));
3409     EXPECT_EQ(-3.0f, sphere(nanovdb::Coord(50)));
3410     EXPECT_EQ(0.0f, sphere(nanovdb::Coord(50, 50, 70)));
3411     EXPECT_EQ(-1.0f, sphere(nanovdb::Coord(50, 50, 69)));
3412     EXPECT_EQ(2.0f, sphere(nanovdb::Coord(50, 50, 72)));
3413 
3414     auto handle = nanovdb::createLevelSetSphere(20.0f, nanovdb::Vec3f(50),
3415                                                 1.0, 3.0, nanovdb::Vec3d(0), "sphere_20");
3416 
3417     const nanovdb::CoordBBox bbox(nanovdb::Coord(0), nanovdb::Coord(100));
3418 
3419     EXPECT_TRUE(handle);
3420     EXPECT_EQ(1u, handle.gridCount());
3421     auto* meta = handle.gridMetaData();
3422     EXPECT_TRUE(meta);
3423     EXPECT_EQ("sphere_20", std::string(meta->shortGridName()));
3424     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3425     EXPECT_EQ(nanovdb::GridClass::LevelSet, meta->gridClass());
3426     auto* dstGrid = handle.grid<float>();
3427     EXPECT_TRUE(dstGrid);
3428     EXPECT_EQ("sphere_20", std::string(dstGrid->gridName()));
3429 
3430     EXPECT_TRUE(dstGrid->hasBBox());
3431     EXPECT_TRUE(dstGrid->hasMinMax());
3432     EXPECT_TRUE(dstGrid->hasAverage());
3433     EXPECT_TRUE(dstGrid->hasStdDeviation());
3434 
3435     EXPECT_NEAR( -3.0f, dstGrid->tree().root().minimum(), 0.04f);
3436     EXPECT_NEAR(  3.0f, dstGrid->tree().root().maximum(), 0.04f);
3437     EXPECT_NEAR(  0.0f, dstGrid->tree().root().average(), 0.3f);
3438     //std::cerr << dstGrid->tree().root().minimum() << std::endl;
3439     //std::cerr << dstGrid->tree().root().maximum() << std::endl;
3440     //std::cerr << dstGrid->tree().root().average() << std::endl;
3441     //std::cerr << dstGrid->tree().root().stdDeviation() << std::endl;
3442 
3443 
3444     EXPECT_EQ(nanovdb::Coord(50 - 20 - 2), dstGrid->indexBBox()[0]);
3445     EXPECT_EQ(nanovdb::Coord(50 + 20 + 2), dstGrid->indexBBox()[1]);
3446 
3447     //std::cerr << "bbox.min = (" << dstGrid->indexBBox()[0][0] << ", " <<  dstGrid->indexBBox()[0][1] << ", " <<  dstGrid->indexBBox()[0][2] << ")" << std::endl;
3448     //std::cerr << "bbox.max = (" << dstGrid->indexBBox()[1][0] << ", " <<  dstGrid->indexBBox()[1][1] << ", " <<  dstGrid->indexBBox()[1][2] << ")" << std::endl;
3449     uint64_t count = 0;
3450     auto     dstAcc = dstGrid->getAccessor();
3451     for (nanovdb::Coord ijk = bbox[0]; ijk[0] <= bbox[1][0]; ++ijk[0]) {
3452         for (ijk[1] = bbox[0][1]; ijk[1] <= bbox[1][1]; ++ijk[1]) {
3453             for (ijk[2] = bbox[0][2]; ijk[2] <= bbox[1][2]; ++ijk[2]) {
3454                 if (sphere.inNarrowBand(ijk))
3455                     ++count;
3456                 EXPECT_EQ(sphere(ijk), dstAcc.getValue(ijk));
3457                 EXPECT_EQ(sphere.inNarrowBand(ijk), dstAcc.isActive(ijk));
3458             }
3459         }
3460     }
3461 
3462     EXPECT_EQ(count, dstGrid->activeVoxelCount());
3463 
3464 } // createLevelSetSphere
3465 
TEST_F(TestNanoVDB,createFogVolumeSphere)3466 TEST_F(TestNanoVDB, createFogVolumeSphere)
3467 {
3468     auto                     handle = nanovdb::createFogVolumeSphere(20.0f, nanovdb::Vec3f(50),
3469                                                                      1.0, 3.0, nanovdb::Vec3d(0), "sphere_20");
3470     const nanovdb::CoordBBox bbox(nanovdb::Coord(0), nanovdb::Coord(100));
3471 
3472     EXPECT_TRUE(handle);
3473     EXPECT_EQ(1u, handle.gridCount());
3474     auto* meta = handle.gridMetaData();
3475     EXPECT_TRUE(meta);
3476     EXPECT_EQ("sphere_20", std::string(meta->shortGridName()));
3477     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3478     EXPECT_EQ(nanovdb::GridClass::FogVolume, meta->gridClass());
3479     auto* dstGrid = handle.grid<float>();
3480     EXPECT_TRUE(dstGrid);
3481     EXPECT_EQ("sphere_20", std::string(dstGrid->gridName()));
3482     EXPECT_TRUE(dstGrid->hasBBox());
3483     EXPECT_TRUE(dstGrid->hasMinMax());
3484     EXPECT_TRUE(dstGrid->hasAverage());
3485     EXPECT_TRUE(dstGrid->hasStdDeviation());
3486 
3487     EXPECT_NEAR(  0.0f, dstGrid->tree().root().minimum(),1e-6);
3488     EXPECT_NEAR(  1.0f, dstGrid->tree().root().maximum(), 1e-6);
3489     EXPECT_NEAR(  0.8f, dstGrid->tree().root().average(), 1e-3);
3490     EXPECT_NEAR(  0.3f, dstGrid->tree().root().stdDeviation(), 1e-2);
3491     //std::cerr << dstGrid->tree().root().minimum() << std::endl;
3492     //std::cerr << dstGrid->tree().root().maximum() << std::endl;
3493     //std::cerr << dstGrid->tree().root().average() << std::endl;
3494     //std::cerr << dstGrid->tree().root().stdDeviation() << std::endl;
3495 
3496     //std::cerr << "bbox.min = (" << dstGrid->indexBBox()[0][0] << ", " <<  dstGrid->indexBBox()[0][1] << ", " <<  dstGrid->indexBBox()[0][2] << ")" << std::endl;
3497     //std::cerr << "bbox.max = (" << dstGrid->indexBBox()[1][0] << ", " <<  dstGrid->indexBBox()[1][1] << ", " <<  dstGrid->indexBBox()[1][2] << ")" << std::endl;
3498 
3499     EXPECT_EQ(nanovdb::Coord(50 - 20), dstGrid->indexBBox()[0]);
3500     EXPECT_EQ(nanovdb::Coord(50 + 20), dstGrid->indexBBox()[1]);
3501 
3502     Sphere<float> sphere(nanovdb::Vec3<float>(50), 20.0f);
3503     uint64_t      count = 0;
3504     auto          dstAcc = dstGrid->getAccessor();
3505     for (nanovdb::Coord ijk = bbox[0]; ijk[0] <= bbox[1][0]; ++ijk[0]) {
3506         for (ijk[1] = bbox[0][1]; ijk[1] <= bbox[1][1]; ++ijk[1]) {
3507             for (ijk[2] = bbox[0][2]; ijk[2] <= bbox[1][2]; ++ijk[2]) {
3508                 if (sphere(ijk) > 0) {
3509                     EXPECT_FALSE(dstAcc.isActive(ijk));
3510                     EXPECT_EQ(0, dstAcc.getValue(ijk));
3511                 } else {
3512                     ++count;
3513                     EXPECT_TRUE(dstAcc.isActive(ijk));
3514                     EXPECT_TRUE(dstAcc.getValue(ijk) >= 0);
3515                     EXPECT_TRUE(dstAcc.getValue(ijk) <= 1);
3516                 }
3517             }
3518         }
3519     }
3520 
3521     EXPECT_EQ(count, dstGrid->activeVoxelCount());
3522 
3523 } // createFogVolumeSphere
3524 
TEST_F(TestNanoVDB,createPointSphere)3525 TEST_F(TestNanoVDB, createPointSphere)
3526 {
3527     Sphere<float> sphere(nanovdb::Vec3<float>(0), 100.0f, 1.0f, 1.0f);
3528     EXPECT_EQ(1.0f, sphere.background());
3529     EXPECT_EQ(1.0f, sphere(nanovdb::Coord(101, 0, 0)));
3530     EXPECT_EQ(-1.0f, sphere(nanovdb::Coord(0)));
3531     EXPECT_EQ(0.0f, sphere(nanovdb::Coord(0, 0, 100)));
3532     EXPECT_EQ(-1.0f, sphere(nanovdb::Coord(0, 0, 99)));
3533     EXPECT_EQ(1.0f, sphere(nanovdb::Coord(0, 0, 101)));
3534 
3535     auto handle = nanovdb::createPointSphere<float>(1,// pointer per voxel
3536                                                     100.0f,// radius of sphere
3537                                                     nanovdb::Vec3f(0),// center sphere
3538                                                     1.0,// voxel size
3539                                                     nanovdb::Vec3d(0),// origin of grid
3540                                                     "point_sphere");
3541 
3542     const nanovdb::CoordBBox bbox(nanovdb::Coord(-100), nanovdb::Coord(100));
3543 
3544     EXPECT_TRUE(handle);
3545     EXPECT_EQ(1u, handle.gridCount());
3546     auto* meta = handle.gridMetaData();
3547     EXPECT_TRUE(meta);
3548     EXPECT_EQ("point_sphere", std::string(meta->shortGridName()));
3549     EXPECT_EQ(nanovdb::GridType::UInt32, meta->gridType());
3550     EXPECT_EQ(nanovdb::GridClass::PointData, meta->gridClass());
3551     auto* dstGrid = handle.grid<uint32_t>();
3552     EXPECT_TRUE(dstGrid);
3553     EXPECT_EQ("point_sphere", std::string(dstGrid->gridName()));
3554     EXPECT_TRUE(dstGrid->hasBBox());
3555     EXPECT_TRUE(dstGrid->hasMinMax());
3556     EXPECT_FALSE(dstGrid->hasAverage());
3557     EXPECT_FALSE(dstGrid->hasStdDeviation());
3558 
3559     EXPECT_EQ(bbox[0], dstGrid->indexBBox()[0]);
3560     EXPECT_EQ(bbox[1], dstGrid->indexBBox()[1]);
3561 
3562     //std::cerr << "bbox.min = (" << dstGrid->indexBBox()[0][0] << ", " <<  dstGrid->indexBBox()[0][1] << ", " <<  dstGrid->indexBBox()[0][2] << ")" << std::endl;
3563     //std::cerr << "bbox.max = (" << dstGrid->indexBBox()[1][0] << ", " <<  dstGrid->indexBBox()[1][1] << ", " <<  dstGrid->indexBBox()[1][2] << ")" << std::endl;
3564 
3565     uint64_t                               count = 0;
3566     nanovdb::PointAccessor<nanovdb::Vec3f> acc(*dstGrid);
3567     const nanovdb::Vec3f *                 begin = nullptr, *end = nullptr;
3568     for (nanovdb::Coord ijk = bbox[0]; ijk[0] <= bbox[1][0]; ++ijk[0]) {
3569         for (ijk[1] = bbox[0][1]; ijk[1] <= bbox[1][1]; ++ijk[1]) {
3570             for (ijk[2] = bbox[0][2]; ijk[2] <= bbox[1][2]; ++ijk[2]) {
3571                 if (nanovdb::Abs(sphere(ijk)) < 0.5f) {
3572                     ++count;
3573                     EXPECT_TRUE(acc.isActive(ijk));
3574                     EXPECT_TRUE(acc.getValue(ijk) != std::numeric_limits<uint32_t>::max());
3575                     EXPECT_EQ(1u, acc.voxelPoints(ijk, begin, end)); // exactly one point per voxel
3576                     const nanovdb::Vec3f p = *begin + ijk.asVec3s();// local voxel coordinate + global index coordinates
3577                     EXPECT_TRUE(nanovdb::Abs(sphere(p)) <= 1.0f);
3578                 } else {
3579                     EXPECT_FALSE(acc.isActive(ijk));
3580                     EXPECT_TRUE(acc.getValue(ijk) < 512 || acc.getValue(ijk) == std::numeric_limits<uint32_t>::max());
3581                 }
3582             }
3583         }
3584     }
3585     EXPECT_EQ(count, dstGrid->activeVoxelCount());
3586 } // createPointSphere
3587 
TEST_F(TestNanoVDB,createLevelSetTorus)3588 TEST_F(TestNanoVDB, createLevelSetTorus)
3589 {
3590     auto handle = nanovdb::createLevelSetTorus(100.0f, 50.0f, nanovdb::Vec3f(50),
3591                                                1.0, 3.0, nanovdb::Vec3d(0), "torus_100");
3592 
3593     EXPECT_TRUE(handle);
3594     EXPECT_EQ(1u, handle.gridCount());
3595     auto* meta = handle.gridMetaData();
3596     EXPECT_TRUE(meta);
3597     EXPECT_EQ("torus_100", std::string(meta->shortGridName()));
3598     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3599     EXPECT_EQ(nanovdb::GridClass::LevelSet, meta->gridClass());
3600     auto* dstGrid = handle.grid<float>();
3601     EXPECT_TRUE(dstGrid);
3602     EXPECT_EQ("torus_100", std::string(dstGrid->gridName()));
3603     EXPECT_TRUE(dstGrid->hasBBox());
3604     EXPECT_TRUE(dstGrid->hasMinMax());
3605     EXPECT_TRUE(dstGrid->hasAverage());
3606     EXPECT_TRUE(dstGrid->hasStdDeviation());
3607 
3608     EXPECT_EQ(nanovdb::Coord(50 - 100 - 50 - 2, 50 - 50 - 2, 50 - 100 - 50 - 2), dstGrid->indexBBox()[0]);
3609     EXPECT_EQ(nanovdb::Coord(50 + 100 + 50 + 2, 50 + 50 + 2, 50 + 100 + 50 + 2), dstGrid->indexBBox()[1]);
3610 
3611     auto dstAcc = dstGrid->getAccessor();
3612     EXPECT_EQ(3.0f, dstAcc.getValue(nanovdb::Coord(50)));
3613     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(50)));
3614     EXPECT_EQ(-3.0f, dstAcc.getValue(nanovdb::Coord(150, 50, 50)));
3615     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(150, 50, 50)));
3616     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(200, 50, 50)));
3617     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(200, 50, 50)));
3618     EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(201, 50, 50)));
3619     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(201, 50, 50)));
3620     EXPECT_EQ(-2.0f, dstAcc.getValue(nanovdb::Coord(198, 50, 50)));
3621     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(198, 50, 50)));
3622 
3623 } // createLevelSetTorus
3624 
TEST_F(TestNanoVDB,createFogVolumeTorus)3625 TEST_F(TestNanoVDB, createFogVolumeTorus)
3626 {
3627     auto handle = nanovdb::createFogVolumeTorus(100.0f, 50.0f, nanovdb::Vec3f(50),
3628                                                 1.0, 3.0, nanovdb::Vec3d(0), "torus_100");
3629 
3630     EXPECT_TRUE(handle);
3631     EXPECT_EQ(1u, handle.gridCount());
3632     auto* meta = handle.gridMetaData();
3633     EXPECT_TRUE(meta);
3634     EXPECT_EQ("torus_100", std::string(meta->shortGridName()));
3635     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3636     EXPECT_EQ(nanovdb::GridClass::FogVolume, meta->gridClass());
3637     auto* dstGrid = handle.grid<float>();
3638     EXPECT_TRUE(dstGrid);
3639     EXPECT_EQ("torus_100", std::string(dstGrid->gridName()));
3640     EXPECT_TRUE(dstGrid->hasBBox());
3641     EXPECT_TRUE(dstGrid->hasMinMax());
3642     EXPECT_TRUE(dstGrid->hasAverage());
3643     EXPECT_TRUE(dstGrid->hasStdDeviation());
3644 
3645     //std::cerr << "bbox.min = (" << dstGrid->indexBBox()[0][0] << ", " <<  dstGrid->indexBBox()[0][1] << ", " <<  dstGrid->indexBBox()[0][2] << ")" << std::endl;
3646     //std::cerr << "bbox.max = (" << dstGrid->indexBBox()[1][0] << ", " <<  dstGrid->indexBBox()[1][1] << ", " <<  dstGrid->indexBBox()[1][2] << ")" << std::endl;
3647 
3648     EXPECT_EQ(nanovdb::Coord(50 - 100 - 50, 50 - 50, 50 - 100 - 50), dstGrid->indexBBox()[0]);
3649     EXPECT_EQ(nanovdb::Coord(50 + 100 + 50, 50 + 50, 50 + 100 + 50), dstGrid->indexBBox()[1]);
3650 
3651     auto dstAcc = dstGrid->getAccessor();
3652     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(50)));
3653     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(50)));
3654     EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(150, 50, 50)));
3655     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(150, 50, 50)));
3656     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(200, 50, 50)));
3657     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(200, 50, 50)));
3658     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(201, 50, 50)));
3659     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(201, 50, 50)));
3660     EXPECT_EQ(1.0f / 3.0f, dstAcc.getValue(nanovdb::Coord(199, 50, 50)));
3661     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(199, 50, 50)));
3662     EXPECT_EQ(2.0f / 3.0f, dstAcc.getValue(nanovdb::Coord(198, 50, 50)));
3663     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(198, 50, 50)));
3664 } // createFogVolumeTorus
3665 
TEST_F(TestNanoVDB,createLevelSetBox)3666 TEST_F(TestNanoVDB, createLevelSetBox)
3667 {
3668     auto handle = nanovdb::createLevelSetBox<float>(40.0f, 60.0f, 80.0f, nanovdb::Vec3f(50),
3669                                                     1.0, 3.0, nanovdb::Vec3d(0), "box");
3670     EXPECT_TRUE(handle);
3671     EXPECT_EQ(1u, handle.gridCount());
3672     auto* meta = handle.gridMetaData();
3673     EXPECT_TRUE(meta);
3674     EXPECT_EQ("box", std::string(meta->shortGridName()));
3675     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3676     EXPECT_EQ(nanovdb::GridClass::LevelSet, meta->gridClass());
3677     auto* dstGrid = handle.grid<float>();
3678     EXPECT_TRUE(dstGrid);
3679      EXPECT_EQ("box", std::string(dstGrid->gridName()));
3680     EXPECT_TRUE(dstGrid->hasBBox());
3681     EXPECT_TRUE(dstGrid->hasMinMax());
3682     EXPECT_TRUE(dstGrid->hasAverage());
3683     EXPECT_TRUE(dstGrid->hasStdDeviation());
3684 
3685     EXPECT_EQ(nanovdb::Coord(50 - 20 - 2, 50 - 30 - 2, 50 - 40 - 2), dstGrid->indexBBox()[0]);
3686     EXPECT_EQ(nanovdb::Coord(50 + 20 + 2, 50 + 30 + 2, 50 + 40 + 2), dstGrid->indexBBox()[1]);
3687 
3688     auto dstAcc = dstGrid->getAccessor();
3689     EXPECT_EQ(-3.0f, dstAcc.getValue(nanovdb::Coord(50)));
3690     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(50)));
3691     EXPECT_EQ(2.0f, dstAcc.getValue(nanovdb::Coord(72, 50, 50)));
3692     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(72, 50, 50)));
3693     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(70, 50, 50)));
3694     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(70, 50, 50)));
3695     EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(71, 50, 50)));
3696     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(71, 50, 50)));
3697     EXPECT_EQ(-2.0f, dstAcc.getValue(nanovdb::Coord(68, 50, 50)));
3698     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(68, 50, 50)));
3699 
3700 } // createLevelSetBox
3701 
TEST_F(TestNanoVDB,createFogVolumeBox)3702 TEST_F(TestNanoVDB, createFogVolumeBox)
3703 {
3704     auto handle = nanovdb::createFogVolumeBox<float>(40.0f, 60.0f, 80.0f, nanovdb::Vec3f(50),
3705                                                      1.0, 3.0, nanovdb::Vec3d(0), "box");
3706     EXPECT_TRUE(handle);
3707     EXPECT_EQ(1u, handle.gridCount());
3708     auto* meta = handle.gridMetaData();
3709     EXPECT_TRUE(meta);
3710     EXPECT_EQ("box", std::string(meta->shortGridName()));
3711     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3712     EXPECT_EQ(nanovdb::GridClass::FogVolume, meta->gridClass());
3713     auto* dstGrid = handle.grid<float>();
3714     EXPECT_TRUE(dstGrid);
3715     EXPECT_EQ("box", std::string(dstGrid->gridName()));
3716     EXPECT_TRUE(dstGrid->hasBBox());
3717     EXPECT_TRUE(dstGrid->hasMinMax());
3718     EXPECT_TRUE(dstGrid->hasAverage());
3719     EXPECT_TRUE(dstGrid->hasStdDeviation());
3720 
3721     EXPECT_EQ(nanovdb::Coord(50 - 20, 50 - 30, 50 - 40), dstGrid->indexBBox()[0]);
3722     EXPECT_EQ(nanovdb::Coord(50 + 20, 50 + 30, 50 + 40), dstGrid->indexBBox()[1]);
3723 
3724     auto dstAcc = dstGrid->getAccessor();
3725     EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(50)));
3726     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(50)));
3727     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(72, 50, 50)));
3728     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(72, 50, 50)));
3729     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(70, 50, 50)));
3730     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(70, 50, 50)));
3731     EXPECT_EQ(1.0f / 3.0f, dstAcc.getValue(nanovdb::Coord(69, 50, 50)));
3732     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(69, 50, 50)));
3733     EXPECT_EQ(2.0f / 3.0f, dstAcc.getValue(nanovdb::Coord(68, 50, 50)));
3734     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(68, 50, 50)));
3735 
3736 } // createFogVolumeBox
3737 
TEST_F(TestNanoVDB,createLevelSetOctahedron)3738 TEST_F(TestNanoVDB, createLevelSetOctahedron)
3739 {
3740     auto handle = nanovdb::createLevelSetOctahedron<float>(100.0f, nanovdb::Vec3f(50),
3741                                                            1.0f, 3.0f, nanovdb::Vec3d(0), "octahedron");
3742     EXPECT_TRUE(handle);
3743     EXPECT_EQ(1u, handle.gridCount());
3744     auto* meta = handle.gridMetaData();
3745     EXPECT_TRUE(meta);
3746     EXPECT_EQ("octahedron", std::string(meta->shortGridName()));
3747     EXPECT_EQ(nanovdb::GridType::Float, meta->gridType());
3748     EXPECT_EQ(nanovdb::GridClass::LevelSet, meta->gridClass());
3749     auto* dstGrid = handle.grid<float>();
3750     EXPECT_TRUE(dstGrid);
3751     EXPECT_EQ("octahedron", std::string(dstGrid->gridName()));
3752     EXPECT_TRUE(dstGrid->hasBBox());
3753     EXPECT_TRUE(dstGrid->hasMinMax());
3754     EXPECT_TRUE(dstGrid->hasAverage());
3755     EXPECT_TRUE(dstGrid->hasStdDeviation());
3756 
3757     EXPECT_EQ(nanovdb::Coord(50 - 100/2 - 2), dstGrid->indexBBox()[0]);
3758     EXPECT_EQ(nanovdb::Coord(50 + 100/2 + 2), dstGrid->indexBBox()[1]);
3759 
3760     auto dstAcc = dstGrid->getAccessor();
3761     EXPECT_EQ(-3.0f, dstAcc.getValue(nanovdb::Coord(50)));
3762     EXPECT_FALSE(dstAcc.isActive(nanovdb::Coord(50)));
3763     EXPECT_EQ(2.0f, dstAcc.getValue(nanovdb::Coord(102, 50, 50)));
3764     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(102, 50, 50)));
3765     EXPECT_EQ(0.0f, dstAcc.getValue(nanovdb::Coord(100, 50, 50)));
3766     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(100, 50, 50)));
3767     EXPECT_EQ(1.0f, dstAcc.getValue(nanovdb::Coord(101, 50, 50)));
3768     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(101, 50, 50)));
3769     EXPECT_EQ(-nanovdb::Sqrt(4.0f/3.0f), dstAcc.getValue(nanovdb::Coord(98, 50, 50)));
3770     EXPECT_TRUE(dstAcc.isActive(nanovdb::Coord(98, 50, 50)));
3771 
3772 } // createLevelSetOctahedron
3773 
3774 #if !defined(_MSC_VER)
TEST_F(TestNanoVDB,CNanoVDBSize)3775 TEST_F(TestNanoVDB, CNanoVDBSize)
3776 {
3777     // Verify the sizes of structures are what we expect.
3778     EXPECT_EQ(sizeof(cnanovdb_mask3), sizeof(nanovdb::Mask<3>));
3779     EXPECT_EQ(sizeof(cnanovdb_mask4), sizeof(nanovdb::Mask<4>));
3780     EXPECT_EQ(sizeof(cnanovdb_mask5), sizeof(nanovdb::Mask<5>));
3781     EXPECT_EQ(sizeof(cnanovdb_map), sizeof(nanovdb::Map));
3782     EXPECT_EQ(sizeof(cnanovdb_coord), sizeof(nanovdb::Coord));
3783     EXPECT_EQ(sizeof(cnanovdb_Vec3F), sizeof(nanovdb::Vec3f));
3784 
3785     EXPECT_EQ(sizeof(cnanovdb_node0F), sizeof(nanovdb::LeafNode<float>));
3786     EXPECT_EQ(sizeof(cnanovdb_node1F), sizeof(nanovdb::InternalNode<nanovdb::LeafNode<float>>));
3787     EXPECT_EQ(sizeof(cnanovdb_node2F), sizeof(nanovdb::InternalNode<nanovdb::InternalNode<nanovdb::LeafNode<float>>>));
3788     EXPECT_EQ(sizeof(cnanovdb_rootdataF), sizeof(nanovdb::NanoRoot<float>));
3789 
3790     EXPECT_EQ(sizeof(cnanovdb_node0F3), sizeof(nanovdb::LeafNode<nanovdb::Vec3f>));
3791     EXPECT_EQ(sizeof(cnanovdb_node1F3), sizeof(nanovdb::InternalNode<nanovdb::LeafNode<nanovdb::Vec3f>>));
3792     EXPECT_EQ(sizeof(cnanovdb_node2F3), sizeof(nanovdb::InternalNode<nanovdb::InternalNode<nanovdb::LeafNode<nanovdb::Vec3f>>>));
3793     EXPECT_EQ(sizeof(cnanovdb_rootdataF3), sizeof(nanovdb::NanoRoot<nanovdb::Vec3f>));
3794     EXPECT_EQ(sizeof(cnanovdb_treedata), sizeof(nanovdb::NanoTree<float>));
3795     EXPECT_EQ(sizeof(cnanovdb_gridblindmetadata), sizeof(nanovdb::GridBlindMetaData));
3796     EXPECT_EQ(sizeof(cnanovdb_griddata), sizeof(nanovdb::NanoGrid<float>));
3797 } // CNanoVDBSize
3798 #endif
3799 
3800 #if !defined(DISABLE_PNANOVDB) && !defined(_MSC_VER)
TEST_F(TestNanoVDB,PNanoVDB_Basic)3801 TEST_F(TestNanoVDB, PNanoVDB_Basic)
3802 {
3803     EXPECT_EQ(NANOVDB_MAGIC_NUMBER, PNANOVDB_MAGIC_NUMBER);
3804 
3805     EXPECT_EQ(NANOVDB_MAJOR_VERSION_NUMBER, PNANOVDB_MAJOR_VERSION_NUMBER);
3806     EXPECT_EQ(NANOVDB_MINOR_VERSION_NUMBER, PNANOVDB_MINOR_VERSION_NUMBER);
3807     EXPECT_EQ(NANOVDB_PATCH_VERSION_NUMBER, PNANOVDB_PATCH_VERSION_NUMBER);
3808 
3809     EXPECT_EQ((int)nanovdb::GridType::Unknown, PNANOVDB_GRID_TYPE_UNKNOWN);
3810     EXPECT_EQ((int)nanovdb::GridType::Float,   PNANOVDB_GRID_TYPE_FLOAT);
3811     EXPECT_EQ((int)nanovdb::GridType::Double,  PNANOVDB_GRID_TYPE_DOUBLE);
3812     EXPECT_EQ((int)nanovdb::GridType::Int16,   PNANOVDB_GRID_TYPE_INT16);
3813     EXPECT_EQ((int)nanovdb::GridType::Int32,   PNANOVDB_GRID_TYPE_INT32);
3814     EXPECT_EQ((int)nanovdb::GridType::Int64,   PNANOVDB_GRID_TYPE_INT64);
3815     EXPECT_EQ((int)nanovdb::GridType::Vec3f,   PNANOVDB_GRID_TYPE_VEC3F);
3816     EXPECT_EQ((int)nanovdb::GridType::Vec3d,   PNANOVDB_GRID_TYPE_VEC3D);
3817     EXPECT_EQ((int)nanovdb::GridType::Mask,    PNANOVDB_GRID_TYPE_MASK);
3818     EXPECT_EQ((int)nanovdb::GridType::Half,    PNANOVDB_GRID_TYPE_HALF);
3819     EXPECT_EQ((int)nanovdb::GridType::UInt32,  PNANOVDB_GRID_TYPE_UINT32);
3820     EXPECT_EQ((int)nanovdb::GridType::Boolean, PNANOVDB_GRID_TYPE_BOOLEAN);
3821     EXPECT_EQ((int)nanovdb::GridType::RGBA8,   PNANOVDB_GRID_TYPE_RGBA8);
3822     EXPECT_EQ((int)nanovdb::GridType::Fp4,     PNANOVDB_GRID_TYPE_FP4);
3823     EXPECT_EQ((int)nanovdb::GridType::Fp8,     PNANOVDB_GRID_TYPE_FP8);
3824     EXPECT_EQ((int)nanovdb::GridType::Fp16,    PNANOVDB_GRID_TYPE_FP16);
3825     EXPECT_EQ((int)nanovdb::GridType::FpN,     PNANOVDB_GRID_TYPE_FPN);
3826     EXPECT_EQ((int)nanovdb::GridType::Vec4f,   PNANOVDB_GRID_TYPE_VEC4F);
3827     EXPECT_EQ((int)nanovdb::GridType::Vec4d,   PNANOVDB_GRID_TYPE_VEC4D);
3828     EXPECT_EQ((int)nanovdb::GridType::End,     PNANOVDB_GRID_TYPE_END);
3829 
3830     EXPECT_EQ((int)nanovdb::GridClass::Unknown,    PNANOVDB_GRID_CLASS_UNKNOWN);
3831     EXPECT_EQ((int)nanovdb::GridClass::LevelSet,   PNANOVDB_GRID_CLASS_LEVEL_SET);
3832     EXPECT_EQ((int)nanovdb::GridClass::FogVolume,  PNANOVDB_GRID_CLASS_FOG_VOLUME);
3833     EXPECT_EQ((int)nanovdb::GridClass::Staggered,  PNANOVDB_GRID_CLASS_STAGGERED);
3834     EXPECT_EQ((int)nanovdb::GridClass::PointIndex, PNANOVDB_GRID_CLASS_POINT_INDEX);
3835     EXPECT_EQ((int)nanovdb::GridClass::PointData,  PNANOVDB_GRID_CLASS_POINT_DATA);
3836     EXPECT_EQ((int)nanovdb::GridClass::Topology,   PNANOVDB_GRID_CLASS_TOPOLOGY);
3837     EXPECT_EQ((int)nanovdb::GridClass::VoxelVolume,PNANOVDB_GRID_CLASS_VOXEL_VOLUME);
3838     EXPECT_EQ((int)nanovdb::GridClass::End,        PNANOVDB_GRID_CLASS_END);
3839 
3840     // check some basic types
3841     EXPECT_EQ(sizeof(pnanovdb_map_t),   sizeof(nanovdb::Map));
3842     EXPECT_EQ(sizeof(pnanovdb_coord_t), sizeof(nanovdb::Coord));
3843     EXPECT_EQ(sizeof(pnanovdb_vec3_t),  sizeof(nanovdb::Vec3f));
3844 
3845     // check nanovdb::Map
3846     EXPECT_EQ((int)sizeof(nanovdb::Map), PNANOVDB_MAP_SIZE);
3847     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mMatF),    PNANOVDB_MAP_OFF_MATF);
3848     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mInvMatF), PNANOVDB_MAP_OFF_INVMATF);
3849     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mVecF),    PNANOVDB_MAP_OFF_VECF);
3850     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mTaperF),  PNANOVDB_MAP_OFF_TAPERF);
3851     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mMatD),    PNANOVDB_MAP_OFF_MATD);
3852     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mInvMatD), PNANOVDB_MAP_OFF_INVMATD);
3853     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mVecD),    PNANOVDB_MAP_OFF_VECD);
3854     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::Map, mTaperD),  PNANOVDB_MAP_OFF_TAPERD);
3855 
3856     EXPECT_EQ((int)sizeof(pnanovdb_map_t), PNANOVDB_MAP_SIZE);
3857     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, matf),    PNANOVDB_MAP_OFF_MATF);
3858     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, invmatf), PNANOVDB_MAP_OFF_INVMATF);
3859     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, vecf),    PNANOVDB_MAP_OFF_VECF);
3860     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, taperf),  PNANOVDB_MAP_OFF_TAPERF);
3861     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, matd),    PNANOVDB_MAP_OFF_MATD);
3862     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, invmatd), PNANOVDB_MAP_OFF_INVMATD);
3863     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, vecd),    PNANOVDB_MAP_OFF_VECD);
3864     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_map_t, taperd),  PNANOVDB_MAP_OFF_TAPERD);
3865 
3866     EXPECT_TRUE(validate_strides(printf));// checks strides and prints out new ones if they have changed
3867 }
3868 
3869 template <typename ValueT>
validateLeaf(pnanovdb_grid_type_t grid_type)3870 void validateLeaf(pnanovdb_grid_type_t grid_type)
3871 {
3872     using nodedata0_t = typename nanovdb::LeafData<ValueT, nanovdb::Coord, nanovdb::Mask, 3>;
3873     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMinimum), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_min);
3874     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMaximum), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_max);
3875     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mAverage), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_ave);
3876     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mStdDevi), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_stddev);
3877     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mValues),  (int)pnanovdb_grid_type_constants[grid_type].leaf_off_table);
3878 }
3879 
3880 template <>
validateLeaf(pnanovdb_grid_type_t grid_type)3881 void validateLeaf<nanovdb::Fp4>(pnanovdb_grid_type_t grid_type)
3882 {
3883     using nodedata0_t = typename nanovdb::LeafData<nanovdb::Fp4, nanovdb::Coord, nanovdb::Mask, 3>;
3884     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMin), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_min);
3885     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMax), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_max);
3886     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mAvg), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_ave);
3887     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mDev), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_stddev);
3888 }
3889 
3890 template <>
validateLeaf(pnanovdb_grid_type_t grid_type)3891 void validateLeaf<nanovdb::Fp8>(pnanovdb_grid_type_t grid_type)
3892 {
3893     using nodedata0_t = typename nanovdb::LeafData<nanovdb::Fp8, nanovdb::Coord, nanovdb::Mask, 3>;
3894     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMin), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_min);
3895     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMax), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_max);
3896     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mAvg), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_ave);
3897     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mDev), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_stddev);
3898 }
3899 
3900 template <>
validateLeaf(pnanovdb_grid_type_t grid_type)3901 void validateLeaf<nanovdb::Fp16>(pnanovdb_grid_type_t grid_type)
3902 {
3903     using nodedata0_t = typename nanovdb::LeafData<nanovdb::Fp16, nanovdb::Coord, nanovdb::Mask, 3>;
3904     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMin), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_min);
3905     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMax), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_max);
3906     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mAvg), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_ave);
3907     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mDev), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_stddev);
3908 }
3909 
3910 template <>
validateLeaf(pnanovdb_grid_type_t grid_type)3911 void validateLeaf<nanovdb::FpN>(pnanovdb_grid_type_t grid_type)
3912 {
3913     using nodedata0_t = typename nanovdb::LeafData<nanovdb::FpN, nanovdb::Coord, nanovdb::Mask, 3>;
3914     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMin), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_min);
3915     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mMax), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_max);
3916     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mAvg), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_ave);
3917     EXPECT_EQ(NANOVDB_OFFSETOF(nodedata0_t, mDev), (int)pnanovdb_grid_type_constants[grid_type].leaf_off_stddev);
3918 }
3919 
3920 // template specializations for bool types
3921 template <>
validateLeaf(pnanovdb_grid_type_t grid_type)3922 void validateLeaf<bool>(pnanovdb_grid_type_t grid_type)
3923 {
3924     using nodeLeaf_t = typename nanovdb::LeafData<bool, nanovdb::Coord, nanovdb::Mask, 3>;
3925     using leaf_t = typename nanovdb::LeafNode<bool>;
3926 
3927     EXPECT_EQ(sizeof(leaf_t), (pnanovdb_grid_type_constants[grid_type].leaf_size));
3928     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mBBoxMin), PNANOVDB_LEAF_OFF_BBOX_MIN);
3929     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mBBoxDif), PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS);
3930     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mValueMask), PNANOVDB_LEAF_OFF_VALUE_MASK);
3931 }
3932 
3933 // template specializations for nanovdb::ValueMask types
3934 template <>
validateLeaf(pnanovdb_grid_type_t grid_type)3935 void validateLeaf<nanovdb::ValueMask>(pnanovdb_grid_type_t grid_type)
3936 {
3937     using nodeLeaf_t = typename nanovdb::LeafData<nanovdb::ValueMask, nanovdb::Coord, nanovdb::Mask, 3>;
3938     using leaf_t = typename nanovdb::LeafNode<nanovdb::ValueMask>;
3939 
3940     EXPECT_EQ(sizeof(leaf_t), (pnanovdb_grid_type_constants[grid_type].leaf_size));
3941     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mBBoxMin), PNANOVDB_LEAF_OFF_BBOX_MIN);
3942     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mBBoxDif), PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS);
3943     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mValueMask), PNANOVDB_LEAF_OFF_VALUE_MASK);
3944 }
3945 
TYPED_TEST(TestOffsets,PNanoVDB)3946 TYPED_TEST(TestOffsets, PNanoVDB)
3947 {
3948     using ValueType = TypeParam;
3949     pnanovdb_grid_type_t grid_type;
3950     if (std::is_same<float, TypeParam>::value) {
3951         grid_type = PNANOVDB_GRID_TYPE_FLOAT;
3952     } else if (std::is_same<double, TypeParam>::value) {
3953         grid_type = PNANOVDB_GRID_TYPE_DOUBLE;
3954     } else if (std::is_same<int16_t, TypeParam>::value) {
3955         grid_type = PNANOVDB_GRID_TYPE_INT16;
3956     } else if (std::is_same<int32_t, TypeParam>::value) {
3957         grid_type = PNANOVDB_GRID_TYPE_INT32;
3958     } else if (std::is_same<int64_t, TypeParam>::value) {
3959         grid_type = PNANOVDB_GRID_TYPE_INT64;
3960     } else if (std::is_same<nanovdb::Vec3f, TypeParam>::value) {
3961         grid_type = PNANOVDB_GRID_TYPE_VEC3F;
3962     } else if (std::is_same<nanovdb::Vec3d, TypeParam>::value) {
3963         grid_type = PNANOVDB_GRID_TYPE_VEC3D;
3964     } else if (std::is_same<uint32_t, TypeParam>::value) {
3965         grid_type = PNANOVDB_GRID_TYPE_UINT32;
3966     } else if (std::is_same<nanovdb::ValueMask, TypeParam>::value) {
3967         grid_type = PNANOVDB_GRID_TYPE_MASK;
3968     } else if (std::is_same<bool, TypeParam>::value) {
3969         grid_type = PNANOVDB_GRID_TYPE_BOOLEAN;
3970     } else if (std::is_same<nanovdb::Fp4, TypeParam>::value) {
3971         grid_type = PNANOVDB_GRID_TYPE_FP4;
3972     } else if (std::is_same<nanovdb::Fp8, TypeParam>::value) {
3973         grid_type = PNANOVDB_GRID_TYPE_FP8;
3974     } else if (std::is_same<nanovdb::Fp16, TypeParam>::value) {
3975         grid_type = PNANOVDB_GRID_TYPE_FP16;
3976     } else if (std::is_same<nanovdb::FpN, TypeParam>::value) {
3977         grid_type = PNANOVDB_GRID_TYPE_FPN;
3978     } else {
3979         EXPECT_TRUE(false);
3980     }
3981     static const uint32_t rootLevel = 3u;
3982     using nodeLeaf_t = typename nanovdb::LeafData<ValueType, nanovdb::Coord, nanovdb::Mask, 3>;
3983     using leaf_t = typename nanovdb::LeafNode<ValueType>;
3984     using nodeLower_t = typename nanovdb::InternalData<leaf_t, leaf_t::LOG2DIM + 1>;
3985     using lower_t = typename nanovdb::InternalNode<leaf_t>;
3986     using nodeUpper_t = typename nanovdb::InternalData<lower_t, lower_t::LOG2DIM + 1>;
3987     using upper_t = typename nanovdb::InternalNode<lower_t>;
3988     using rootdata_t = typename nanovdb::RootData<upper_t>;
3989     using root_t = typename nanovdb::RootNode<upper_t>;
3990     using rootdata_tile_t = typename nanovdb::RootData<upper_t>::Tile;
3991     using root_tile_t = typename nanovdb::RootNode<upper_t>::Tile;
3992     using treedata_t = typename nanovdb::TreeData<rootLevel>;
3993     using tree_t = typename nanovdb::Tree<root_t>;
3994 
3995     // grid
3996     EXPECT_EQ((int)sizeof(nanovdb::GridData), PNANOVDB_GRID_SIZE);
3997     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mMagic), PNANOVDB_GRID_OFF_MAGIC);
3998     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mChecksum), PNANOVDB_GRID_OFF_CHECKSUM);
3999     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mVersion), PNANOVDB_GRID_OFF_VERSION);
4000     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mFlags), PNANOVDB_GRID_OFF_FLAGS);
4001     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridSize), PNANOVDB_GRID_OFF_GRID_SIZE);
4002     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridName), PNANOVDB_GRID_OFF_GRID_NAME);
4003     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mWorldBBox), PNANOVDB_GRID_OFF_WORLD_BBOX);
4004     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mMap), PNANOVDB_GRID_OFF_MAP);
4005     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mVoxelSize), PNANOVDB_GRID_OFF_VOXEL_SIZE);
4006     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridClass), PNANOVDB_GRID_OFF_GRID_CLASS);
4007     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mGridType), PNANOVDB_GRID_OFF_GRID_TYPE);
4008     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mBlindMetadataOffset), PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET);
4009     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridData, mBlindMetadataCount), PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT);
4010 
4011     EXPECT_EQ((int)sizeof(pnanovdb_grid_t), PNANOVDB_GRID_SIZE);
4012     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, magic), PNANOVDB_GRID_OFF_MAGIC);
4013     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, checksum), PNANOVDB_GRID_OFF_CHECKSUM);
4014     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, version), PNANOVDB_GRID_OFF_VERSION);
4015     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, flags), PNANOVDB_GRID_OFF_FLAGS);
4016     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, grid_size), PNANOVDB_GRID_OFF_GRID_SIZE);
4017     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, grid_name), PNANOVDB_GRID_OFF_GRID_NAME);
4018     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, map), PNANOVDB_GRID_OFF_MAP);
4019     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, world_bbox), PNANOVDB_GRID_OFF_WORLD_BBOX);
4020     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, voxel_size), PNANOVDB_GRID_OFF_VOXEL_SIZE);
4021     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, grid_class), PNANOVDB_GRID_OFF_GRID_CLASS);
4022     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, grid_type), PNANOVDB_GRID_OFF_GRID_TYPE);
4023     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, blind_metadata_offset), PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET);
4024     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_grid_t, blind_metadata_count), PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT);
4025 
4026     // test GridBlindMetaData
4027     EXPECT_EQ((int)sizeof(nanovdb::GridBlindMetaData), PNANOVDB_GRIDBLINDMETADATA_SIZE);
4028     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mByteOffset), PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET);
4029     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mElementCount), PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT);
4030     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mFlags), PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS);
4031     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mSemantic), PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC);
4032     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mDataClass), PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS);
4033     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mDataType), PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE);
4034     EXPECT_EQ(NANOVDB_OFFSETOF(nanovdb::GridBlindMetaData, mName), PNANOVDB_GRIDBLINDMETADATA_OFF_NAME);
4035 
4036     EXPECT_EQ((int)sizeof(pnanovdb_gridblindmetadata_t), PNANOVDB_GRIDBLINDMETADATA_SIZE);
4037     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, byte_offset), PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET);
4038     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, element_count), PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT);
4039     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, flags), PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS);
4040     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, semantic), PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC);
4041     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, data_class), PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS);
4042     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, data_type), PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE);
4043     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_gridblindmetadata_t, name), PNANOVDB_GRIDBLINDMETADATA_OFF_NAME);
4044 
4045     // test tree
4046     EXPECT_EQ((int)sizeof(tree_t), PNANOVDB_TREE_SIZE);
4047     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeOffset[0]), PNANOVDB_TREE_OFF_NODE_OFFSET_LEAF);
4048     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeOffset[1]), PNANOVDB_TREE_OFF_NODE_OFFSET_LOWER);
4049     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeOffset[2]), PNANOVDB_TREE_OFF_NODE_OFFSET_UPPER);
4050     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeOffset[3]), PNANOVDB_TREE_OFF_NODE_OFFSET_ROOT);
4051     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeCount[0]), PNANOVDB_TREE_OFF_NODE_COUNT_LEAF);
4052     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeCount[1]), PNANOVDB_TREE_OFF_NODE_COUNT_LOWER);
4053     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mNodeCount[2]), PNANOVDB_TREE_OFF_NODE_COUNT_UPPER);
4054     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[0]), PNANOVDB_TREE_OFF_TILE_COUNT_LEAF);
4055     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[1]), PNANOVDB_TREE_OFF_TILE_COUNT_LOWER);
4056     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mTileCount[2]), PNANOVDB_TREE_OFF_TILE_COUNT_UPPER);
4057     EXPECT_EQ(NANOVDB_OFFSETOF(treedata_t, mVoxelCount), PNANOVDB_TREE_OFF_VOXEL_COUNT);
4058 
4059     EXPECT_EQ((int)sizeof(pnanovdb_tree_t), PNANOVDB_TREE_SIZE);
4060     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_offset_leaf), PNANOVDB_TREE_OFF_NODE_OFFSET_LEAF);
4061     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_offset_lower), PNANOVDB_TREE_OFF_NODE_OFFSET_LOWER);
4062     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_offset_upper), PNANOVDB_TREE_OFF_NODE_OFFSET_UPPER);
4063     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_offset_root), PNANOVDB_TREE_OFF_NODE_OFFSET_ROOT);
4064     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_count_leaf), PNANOVDB_TREE_OFF_NODE_COUNT_LEAF);
4065     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_count_lower), PNANOVDB_TREE_OFF_NODE_COUNT_LOWER);
4066     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, node_count_upper), PNANOVDB_TREE_OFF_NODE_COUNT_UPPER);
4067     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_leaf), PNANOVDB_TREE_OFF_TILE_COUNT_LEAF);
4068     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_lower), PNANOVDB_TREE_OFF_TILE_COUNT_LOWER);
4069     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, tile_count_upper), PNANOVDB_TREE_OFF_TILE_COUNT_UPPER);
4070     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_tree_t, voxel_count), PNANOVDB_TREE_OFF_VOXEL_COUNT);
4071 
4072     // background value can start at pad1
4073     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_t, pad1), PNANOVDB_ROOT_BASE_SIZE);
4074     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_t, bbox_min), PNANOVDB_ROOT_OFF_BBOX_MIN);
4075     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_t, bbox_max), PNANOVDB_ROOT_OFF_BBOX_MAX);
4076     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_t, table_size), PNANOVDB_ROOT_OFF_TABLE_SIZE);
4077 
4078     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_tile_t, pad1), PNANOVDB_ROOT_TILE_BASE_SIZE);
4079     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_tile_t, key), PNANOVDB_ROOT_TILE_OFF_KEY);
4080     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_tile_t, child), PNANOVDB_ROOT_TILE_OFF_CHILD);
4081     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_root_tile_t, state), PNANOVDB_ROOT_TILE_OFF_STATE);
4082 
4083     EXPECT_EQ((int)sizeof(pnanovdb_upper_t), PNANOVDB_UPPER_BASE_SIZE);
4084     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_upper_t, bbox_min), PNANOVDB_UPPER_OFF_BBOX_MIN);
4085     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_upper_t, bbox_max), PNANOVDB_UPPER_OFF_BBOX_MAX);
4086     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_upper_t, flags), PNANOVDB_UPPER_OFF_FLAGS);
4087     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_upper_t, value_mask), PNANOVDB_UPPER_OFF_VALUE_MASK);
4088     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_upper_t, child_mask), PNANOVDB_UPPER_OFF_CHILD_MASK);
4089 
4090     EXPECT_EQ((int)sizeof(pnanovdb_lower_t), PNANOVDB_LOWER_BASE_SIZE);
4091     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_lower_t, bbox_min), PNANOVDB_LOWER_OFF_BBOX_MIN);
4092     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_lower_t, bbox_max), PNANOVDB_LOWER_OFF_BBOX_MAX);
4093     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_lower_t, flags), PNANOVDB_LOWER_OFF_FLAGS);
4094     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_lower_t, value_mask), PNANOVDB_LOWER_OFF_VALUE_MASK);
4095     EXPECT_EQ(NANOVDB_OFFSETOF(pnanovdb_lower_t, child_mask), PNANOVDB_LOWER_OFF_CHILD_MASK);
4096 
4097     EXPECT_EQ((uint)sizeof(root_t), (pnanovdb_grid_type_constants[grid_type].root_size));
4098     EXPECT_EQ(NANOVDB_OFFSETOF(rootdata_t, mBBox.mCoord[0]), PNANOVDB_ROOT_OFF_BBOX_MIN);
4099     EXPECT_EQ(NANOVDB_OFFSETOF(rootdata_t, mBBox.mCoord[1]), PNANOVDB_ROOT_OFF_BBOX_MAX);
4100     EXPECT_EQ(NANOVDB_OFFSETOF(rootdata_t, mTableSize), PNANOVDB_ROOT_OFF_TABLE_SIZE);
4101     EXPECT_EQ((uint)NANOVDB_OFFSETOF(rootdata_t, mBackground), pnanovdb_grid_type_constants[grid_type].root_off_background);
4102     EXPECT_EQ((uint)NANOVDB_OFFSETOF(rootdata_t, mMinimum), pnanovdb_grid_type_constants[grid_type].root_off_min);
4103     EXPECT_EQ((uint)NANOVDB_OFFSETOF(rootdata_t, mMaximum), pnanovdb_grid_type_constants[grid_type].root_off_max);
4104     EXPECT_EQ((uint)NANOVDB_OFFSETOF(rootdata_t, mAverage), pnanovdb_grid_type_constants[grid_type].root_off_ave);
4105     EXPECT_EQ((uint)NANOVDB_OFFSETOF(rootdata_t, mStdDevi), pnanovdb_grid_type_constants[grid_type].root_off_stddev);
4106 
4107     EXPECT_EQ((uint)sizeof(root_tile_t), (pnanovdb_grid_type_constants[grid_type].root_tile_size));
4108     EXPECT_EQ(NANOVDB_OFFSETOF(rootdata_tile_t, key), PNANOVDB_ROOT_TILE_OFF_KEY);
4109     EXPECT_EQ(NANOVDB_OFFSETOF(rootdata_tile_t, child), PNANOVDB_ROOT_TILE_OFF_CHILD);
4110     EXPECT_EQ(NANOVDB_OFFSETOF(rootdata_tile_t, state), PNANOVDB_ROOT_TILE_OFF_STATE);
4111     EXPECT_EQ((uint)NANOVDB_OFFSETOF(rootdata_tile_t, value), pnanovdb_grid_type_constants[grid_type].root_tile_off_value);
4112 
4113     EXPECT_EQ((uint)sizeof(upper_t), (pnanovdb_grid_type_constants[grid_type].upper_size));
4114     EXPECT_EQ(NANOVDB_OFFSETOF(nodeUpper_t, mBBox.mCoord[0]), PNANOVDB_UPPER_OFF_BBOX_MIN);
4115     EXPECT_EQ(NANOVDB_OFFSETOF(nodeUpper_t, mBBox.mCoord[1]), PNANOVDB_UPPER_OFF_BBOX_MAX);
4116     EXPECT_EQ(NANOVDB_OFFSETOF(nodeUpper_t, mFlags), PNANOVDB_UPPER_OFF_FLAGS);
4117     EXPECT_EQ(NANOVDB_OFFSETOF(nodeUpper_t, mValueMask), PNANOVDB_UPPER_OFF_VALUE_MASK);
4118     EXPECT_EQ(NANOVDB_OFFSETOF(nodeUpper_t, mChildMask), PNANOVDB_UPPER_OFF_CHILD_MASK);
4119     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeUpper_t, mMinimum), pnanovdb_grid_type_constants[grid_type].upper_off_min);
4120     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeUpper_t, mMaximum), pnanovdb_grid_type_constants[grid_type].upper_off_max);
4121     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeUpper_t, mAverage), pnanovdb_grid_type_constants[grid_type].upper_off_ave);
4122     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeUpper_t, mStdDevi), pnanovdb_grid_type_constants[grid_type].upper_off_stddev);
4123     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeUpper_t, mTable), pnanovdb_grid_type_constants[grid_type].upper_off_table);
4124 
4125     EXPECT_EQ((uint)sizeof(lower_t), (pnanovdb_grid_type_constants[grid_type].lower_size));
4126     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLower_t, mBBox.mCoord[0]), PNANOVDB_LOWER_OFF_BBOX_MIN);
4127     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLower_t, mBBox.mCoord[1]), PNANOVDB_LOWER_OFF_BBOX_MAX);
4128     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLower_t, mFlags), PNANOVDB_LOWER_OFF_FLAGS);
4129     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLower_t, mValueMask), PNANOVDB_LOWER_OFF_VALUE_MASK);
4130     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLower_t, mChildMask), PNANOVDB_LOWER_OFF_CHILD_MASK);
4131     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeLower_t, mMinimum), pnanovdb_grid_type_constants[grid_type].lower_off_min);
4132     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeLower_t, mMaximum), pnanovdb_grid_type_constants[grid_type].lower_off_max);
4133     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeLower_t, mAverage), pnanovdb_grid_type_constants[grid_type].lower_off_ave);
4134     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeLower_t, mStdDevi), pnanovdb_grid_type_constants[grid_type].lower_off_stddev);
4135     EXPECT_EQ((uint)NANOVDB_OFFSETOF(nodeLower_t, mTable), pnanovdb_grid_type_constants[grid_type].lower_off_table);
4136 
4137     EXPECT_EQ(8u*sizeof(rootdata_t::mAverage),  pnanovdb_grid_type_stat_strides_bits[grid_type]);
4138     EXPECT_EQ(8u*sizeof(rootdata_t::mStdDevi),  pnanovdb_grid_type_stat_strides_bits[grid_type]);
4139     EXPECT_EQ(8u*sizeof(nodeUpper_t::mAverage), pnanovdb_grid_type_stat_strides_bits[grid_type]);
4140     EXPECT_EQ(8u*sizeof(nodeUpper_t::mStdDevi), pnanovdb_grid_type_stat_strides_bits[grid_type]);
4141     EXPECT_EQ(8u*sizeof(nodeLower_t::mAverage), pnanovdb_grid_type_stat_strides_bits[grid_type]);
4142     EXPECT_EQ(8u*sizeof(nodeLower_t::mStdDevi), pnanovdb_grid_type_stat_strides_bits[grid_type]);
4143 
4144     // leaf nodes
4145     EXPECT_EQ(sizeof(leaf_t), (pnanovdb_grid_type_constants[grid_type].leaf_size));
4146     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mBBoxMin), PNANOVDB_LEAF_OFF_BBOX_MIN);
4147     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mBBoxDif), PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS);
4148     EXPECT_EQ(NANOVDB_OFFSETOF(nodeLeaf_t, mValueMask), PNANOVDB_LEAF_OFF_VALUE_MASK);
4149     validateLeaf<ValueType>(grid_type);
4150 }// PNanoVDB
4151 #endif
4152 
TEST_F(TestNanoVDB,GridStats)4153 TEST_F(TestNanoVDB, GridStats)
4154 {
4155     using GridT = nanovdb::NanoGrid<float>;
4156     Sphere<float>               sphere(nanovdb::Vec3<float>(50), 50.0f);
4157     nanovdb::GridBuilder<float> builder(sphere.background(), nanovdb::GridClass::LevelSet);
4158     const nanovdb::CoordBBox    bbox(nanovdb::Coord(-100), nanovdb::Coord(100));
4159     //mTimer.start("GridBuilder");
4160     builder(sphere, bbox);
4161     //mTimer.stop();
4162     auto handle1 = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
4163     auto handle2 = builder.getHandle<>(1.0, nanovdb::Vec3d(0.0), "test");
4164     EXPECT_TRUE(handle1);
4165     EXPECT_TRUE(handle2);
4166     GridT* grid1 = handle1.grid<float>();
4167     GridT* grid2 = handle2.grid<float>();
4168     EXPECT_TRUE(grid1);
4169     EXPECT_TRUE(grid2);
4170 
4171     //std::cerr << "grid1 = " << grid1->indexBBox() << ", grid2 = " << grid2->indexBBox() << std::endl;
4172     EXPECT_EQ(grid1->activeVoxelCount(), grid2->activeVoxelCount());
4173     EXPECT_EQ(grid1->worldBBox(), grid2->worldBBox());
4174     EXPECT_EQ(grid1->indexBBox(), grid2->indexBBox());
4175     nanovdb::NodeManager<nanovdb::FloatGrid> mgr1(*grid1);
4176     nanovdb::NodeManager<nanovdb::FloatGrid> mgr2(*grid2);
4177 
4178     { // reset stats in grid2
4179         //grid2->tree().data()->mVoxelCount = uint64_t(0);
4180         grid2->data()->mWorldBBox = nanovdb::BBox<nanovdb::Vec3R>();
4181         grid2->tree().root().data()->mBBox = nanovdb::BBox<nanovdb::Coord>();
4182         for (uint32_t i = 0; i < grid2->tree().nodeCount(0); ++i) {
4183             auto* leaf = mgr2.leaf(i);
4184             auto* data = leaf->data();
4185             data->mMinimum = data->mMaximum = 0.0f;
4186             data->mBBoxMin &= ~nanovdb::NanoLeaf<float>::MASK; /// set to origin!
4187             data->mBBoxDif[0] = data->mBBoxDif[1] = data->mBBoxDif[2] = uint8_t(255);
4188             EXPECT_EQ(data->mBBoxDif[0], uint8_t(255));
4189         }
4190         for (uint32_t i = 0; i < grid2->tree().nodeCount(1); ++i) {
4191             auto* node = mgr2.lower(i);
4192             auto* data = node->data();
4193             data->mMinimum = data->mMaximum = 0.0f;
4194             data->mBBox[0] &= ~nanovdb::NanoLower<float>::MASK; /// set to origin!
4195             data->mBBox[1] = nanovdb::Coord(0);
4196         }
4197         for (uint32_t i = 0; i < grid2->tree().nodeCount(2); ++i) {
4198             auto* node = mgr2.upper(i);
4199             auto* data = node->data();
4200             data->mMinimum = data->mMaximum = 0.0f;
4201             data->mBBox[0] &= ~nanovdb::NanoUpper<float>::MASK; /// set to origin!
4202             data->mBBox[1] = nanovdb::Coord(0);
4203         }
4204     }
4205     //std::cerr << "grid1 = " << grid1->indexBBox() << ", grid2 = " << grid2->indexBBox() << std::endl;
4206     //EXPECT_NE(grid1->activeVoxelCount(), grid2->activeVoxelCount());
4207     EXPECT_NE(grid1->indexBBox(), grid2->indexBBox());
4208     EXPECT_NE(grid1->worldBBox(), grid2->worldBBox());
4209 
4210     { // check stats in grid2
4211         EXPECT_EQ(grid1->tree().nodeCount(0), grid2->tree().nodeCount(0));
4212 
4213         for (uint32_t i = 0; i < grid2->tree().nodeCount(0); ++i) {
4214             auto* leaf1 = mgr1.leaf(i);
4215             auto* leaf2 = mgr2.leaf(i);
4216             EXPECT_NE(leaf1->minimum(), leaf2->minimum());
4217             EXPECT_NE(leaf1->maximum(), leaf2->maximum());
4218             EXPECT_NE(leaf1->bbox(), leaf2->bbox());
4219         }
4220 
4221         EXPECT_EQ(grid1->tree().nodeCount(1), grid2->tree().nodeCount(1));
4222         for (uint32_t i = 0; i < grid2->tree().nodeCount(1); ++i) {
4223             auto* node1 = mgr1.lower(i);
4224             auto* node2 = mgr2.lower(i);
4225             EXPECT_NE(node1->minimum(), node2->minimum());
4226             EXPECT_NE(node1->maximum(), node2->maximum());
4227             EXPECT_NE(node1->bbox(), node2->bbox());
4228         }
4229         EXPECT_EQ(grid1->tree().nodeCount(2), grid2->tree().nodeCount(2));
4230         for (uint32_t i = 0; i < grid2->tree().nodeCount(2); ++i) {
4231             auto* node1 = mgr1.upper(i);
4232             auto* node2 = mgr2.upper(i);
4233             EXPECT_NE(node1->minimum(), node2->minimum());
4234             EXPECT_NE(node1->maximum(), node2->maximum());
4235             EXPECT_NE(node1->bbox(), node2->bbox());
4236         }
4237     }
4238 
4239     //mTimer.start("GridStats");
4240     nanovdb::gridStats(*grid2);
4241     //mTimer.stop();
4242 
4243     { // check stats in grid2
4244         EXPECT_EQ(grid1->tree().nodeCount(0), grid2->tree().nodeCount(0));
4245         for (uint32_t i = 0; i < grid2->tree().nodeCount(0); ++i) {
4246             auto* leaf1 = mgr1.leaf(i);
4247             auto* leaf2 = mgr2.leaf(i);
4248             EXPECT_EQ(leaf1->minimum(), leaf2->minimum());
4249             EXPECT_EQ(leaf1->maximum(), leaf2->maximum());
4250             EXPECT_EQ(leaf1->bbox(), leaf2->bbox());
4251         }
4252         EXPECT_EQ(grid1->tree().nodeCount(1), grid2->tree().nodeCount(1));
4253         for (uint32_t i = 0; i < grid2->tree().nodeCount(1); ++i) {
4254             auto* node1 = mgr1.lower(i);
4255             auto* node2 = mgr2.lower(i);
4256             EXPECT_EQ(node1->minimum(), node2->minimum());
4257             EXPECT_EQ(node1->maximum(), node2->maximum());
4258             EXPECT_EQ(node1->bbox(), node2->bbox());
4259         }
4260         EXPECT_EQ(grid1->tree().nodeCount(2), grid2->tree().nodeCount(2));
4261         for (uint32_t i = 0; i < grid2->tree().nodeCount(2); ++i) {
4262             auto* node1 = mgr1.upper(i);
4263             auto* node2 = mgr2.upper(i);
4264             EXPECT_EQ(node1->minimum(), node2->minimum());
4265             EXPECT_EQ(node1->maximum(), node2->maximum());
4266             EXPECT_EQ(node1->bbox(), node2->bbox());
4267         }
4268     }
4269 
4270     //std::cerr << "grid1 = " << grid1->indexBBox() << ", grid2 = " << grid2->indexBBox() << std::endl;
4271     EXPECT_EQ(grid1->activeVoxelCount(), grid2->activeVoxelCount());
4272     EXPECT_EQ(grid1->indexBBox(), grid2->indexBBox());
4273     EXPECT_EQ(grid1->worldBBox(), grid2->worldBBox());
4274 
4275 } // GridStats
4276 
TEST_F(TestNanoVDB,ScalarSampleFromVoxels)4277 TEST_F(TestNanoVDB, ScalarSampleFromVoxels)
4278 {
4279     // create a grid so sample from
4280     const float dx = 0.5f; // voxel size
4281     auto        trilinearWorld = [&](const nanovdb::Vec3d& xyz) -> float {
4282         return 0.34f + 1.6f * xyz[0] + 6.7f * xyz[1] - 3.5f * xyz[2]; // index coordinates
4283     };
4284     auto trilinearIndex = [&](const nanovdb::Coord& ijk) -> float {
4285         return 0.34f + 1.6f * dx * ijk[0] + 6.7f * dx * ijk[1] - 3.5f * dx * ijk[2]; // index coordinates
4286     };
4287 
4288     nanovdb::GridBuilder<float> builder(1.0f);
4289     const nanovdb::CoordBBox    bbox(nanovdb::Coord(0), nanovdb::Coord(128));
4290     builder(trilinearIndex, bbox);
4291     auto handle = builder.getHandle<>(dx);
4292     EXPECT_TRUE(handle);
4293     EXPECT_EQ(1u, handle.gridCount());
4294     auto* grid = handle.grid<float>();
4295     EXPECT_TRUE(grid);
4296 
4297     const nanovdb::Vec3d xyz(13.4, 24.67, 5.23); // in index space
4298     const nanovdb::Coord ijk(13, 25, 5); // in index space (nearest)
4299     const auto           exact = trilinearWorld(grid->indexToWorld(xyz));
4300     const auto           approx = trilinearIndex(ijk);
4301     //std::cerr << "Trilinear: exact = " << exact << ", approx = " << approx << std::endl;
4302 
4303     auto acc = grid->getAccessor();
4304     auto sampler0 = nanovdb::createSampler<0>(grid->tree());
4305     auto sampler1 = nanovdb::createSampler<1>(acc);
4306     auto sampler2 = nanovdb::createSampler<2>(acc);
4307     auto sampler3 = nanovdb::createSampler<3>(acc);
4308     //std::cerr << "0'th order: v = " << sampler0(xyz) << std::endl;
4309     EXPECT_EQ(approx, sampler0(xyz));
4310     EXPECT_NE(exact, sampler0(xyz));
4311     //std::cerr << "1'th order: v = " << sampler1(xyz) << std::endl;
4312     EXPECT_NEAR(exact, sampler1(xyz), 1e-5);
4313     //std::cerr << "2'th order: v = " << sampler2(xyz) << std::endl;
4314     EXPECT_NEAR(exact, sampler2(xyz), 1e-4);
4315     //std::cerr << "3'rd order: v = " << sampler3(xyz) << std::endl;
4316     EXPECT_NEAR(exact, sampler3(xyz), 1e-5);
4317 
4318     EXPECT_FALSE(sampler1.zeroCrossing());
4319     const auto gradIndex = sampler1.gradient(xyz); //in index space
4320     EXPECT_NEAR(1.6f, gradIndex[0] / dx, 2e-5);
4321     EXPECT_NEAR(6.7f, gradIndex[1] / dx, 2e-5);
4322     EXPECT_NEAR(-3.5f, gradIndex[2] / dx, 2e-5);
4323     const auto gradWorld = grid->indexToWorldGrad(gradIndex); // in world units
4324     EXPECT_NEAR(1.6f, gradWorld[0], 2e-5);
4325     EXPECT_NEAR(6.7f, gradWorld[1], 2e-5);
4326     EXPECT_NEAR(-3.5f, gradWorld[2], 2e-5);
4327 
4328     EXPECT_EQ(grid->tree().getValue(ijk), sampler0.accessor().getValue(ijk));
4329     EXPECT_EQ(grid->tree().getValue(ijk), sampler1.accessor().getValue(ijk));
4330     EXPECT_EQ(grid->tree().getValue(ijk), sampler2.accessor().getValue(ijk));
4331     EXPECT_EQ(grid->tree().getValue(ijk), sampler3.accessor().getValue(ijk));
4332 } // ScalarSampleFromVoxels
4333 
TEST_F(TestNanoVDB,VectorSampleFromVoxels)4334 TEST_F(TestNanoVDB, VectorSampleFromVoxels)
4335 {
4336     // create a grid so sample from
4337     const float dx = 0.5f; // voxel size
4338     auto        trilinearWorld = [&](const nanovdb::Vec3d& xyz) -> nanovdb::Vec3f {
4339         return nanovdb::Vec3f(0.34f, 1.6f * xyz[0] + 6.7f * xyz[1], -3.5f * xyz[2]); // index coordinates
4340     };
4341     auto trilinearIndex = [&](const nanovdb::Coord& ijk) -> nanovdb::Vec3f {
4342         return nanovdb::Vec3f(0.34f, 1.6f * dx * ijk[0] + 6.7f * dx * ijk[1], -3.5f * dx * ijk[2]); // index coordinates
4343     };
4344 
4345     nanovdb::GridBuilder<nanovdb::Vec3f> builder(nanovdb::Vec3f(1.0f));
4346     const nanovdb::CoordBBox             bbox(nanovdb::Coord(0), nanovdb::Coord(128));
4347     builder(trilinearIndex, bbox);
4348     auto handle = builder.getHandle<>(dx);
4349     EXPECT_TRUE(handle);
4350     EXPECT_EQ(1u, handle.gridCount());
4351     auto* grid = handle.grid<nanovdb::Vec3f>();
4352     EXPECT_TRUE(grid);
4353 
4354     const nanovdb::Vec3d ijk(13.4, 24.67, 5.23); // in index space
4355     const auto           exact = trilinearWorld(grid->indexToWorld(ijk));
4356     const auto           approx = trilinearIndex(nanovdb::Coord(13, 25, 5));
4357     //std::cerr << "Trilinear: exact = " << exact << ", approx = " << approx << std::endl;
4358 
4359     auto acc = grid->getAccessor();
4360     auto sampler0 = nanovdb::createSampler<0>(acc);
4361     //std::cerr << "0'th order: v = " << sampler0(ijk) << std::endl;
4362     EXPECT_EQ(approx, sampler0(ijk));
4363 
4364     auto sampler1 = nanovdb::createSampler<1>(acc); // faster since it's using an accessor!!!
4365     //std::cerr << "1'th order: v = " << sampler1(ijk) << std::endl;
4366     for (int i = 0; i < 3; ++i)
4367         EXPECT_NEAR(exact[i], sampler1(ijk)[i], 1e-5);
4368     //EXPECT_FALSE(sampler1.zeroCrossing());// triggeres a static_assert error
4369     //EXPECT_FALSE(sampler1.gradient(grid->indexToWorld(ijk)));// triggeres a static_assert error
4370 
4371     nanovdb::SampleFromVoxels<nanovdb::NanoTree<nanovdb::Vec3f>, 3> sampler3(grid->tree());
4372     //auto sampler3 = nanovdb::createSampler<3>( acc );
4373     //std::cerr << "3'rd order: v = " << sampler3(ijk) << std::endl;
4374     for (int i = 0; i < 3; ++i)
4375         EXPECT_NEAR(exact[i], sampler3(ijk)[i], 1e-5);
4376 
4377 } // VectorSampleFromVoxels
4378 
TEST_F(TestNanoVDB,GridChecksum)4379 TEST_F(TestNanoVDB, GridChecksum)
4380 {
4381     EXPECT_TRUE(nanovdb::ChecksumMode::Disable < nanovdb::ChecksumMode::End);
4382     EXPECT_TRUE(nanovdb::ChecksumMode::Partial < nanovdb::ChecksumMode::End);
4383     EXPECT_TRUE(nanovdb::ChecksumMode::Full < nanovdb::ChecksumMode::End);
4384     EXPECT_TRUE(nanovdb::ChecksumMode::Default < nanovdb::ChecksumMode::End);
4385     EXPECT_NE(nanovdb::ChecksumMode::Disable, nanovdb::ChecksumMode::Partial);
4386     EXPECT_NE(nanovdb::ChecksumMode::Disable, nanovdb::ChecksumMode::Full);
4387     EXPECT_NE(nanovdb::ChecksumMode::Full, nanovdb::ChecksumMode::Partial);
4388     EXPECT_NE(nanovdb::ChecksumMode::Default, nanovdb::ChecksumMode::Disable);
4389     EXPECT_EQ(nanovdb::ChecksumMode::Default, nanovdb::ChecksumMode::Partial);
4390     EXPECT_NE(nanovdb::ChecksumMode::Default, nanovdb::ChecksumMode::Full);
4391 
4392     nanovdb::CpuTimer<> timer;
4393     //timer.start("nanovdb::createLevelSetSphere");
4394     auto handle = nanovdb::createLevelSetSphere<float>(100.0f,
4395                                                        nanovdb::Vec3f(50),
4396                                                        1.0,
4397                                                        3.0,
4398                                                        nanovdb::Vec3d(0),
4399                                                        "sphere_20",
4400                                                        nanovdb::StatsMode::Disable,
4401                                                        nanovdb::ChecksumMode::Disable);
4402     //timer.stop();
4403     EXPECT_TRUE(handle);
4404     EXPECT_EQ(1u, handle.gridCount());
4405     auto* grid = handle.grid<float>();
4406     EXPECT_TRUE(grid);
4407 
4408     nanovdb::GridChecksum checksum1, checksum2, checksum3;
4409 
4410     EXPECT_EQ(checksum1, checksum3);
4411 
4412     //timer.start("Partial checksum");
4413     checksum3(*grid, nanovdb::ChecksumMode::Partial);
4414     //timer.stop();
4415 
4416     EXPECT_NE(checksum1, checksum3);
4417 
4418     //timer.start("Full checksum");
4419     checksum1(*grid, nanovdb::ChecksumMode::Full);
4420     //timer.stop();
4421 
4422     checksum2(*grid, nanovdb::ChecksumMode::Full);
4423 
4424     EXPECT_EQ(checksum1, checksum2);
4425 
4426     auto* leaf = grid->tree().getFirstNode<0>();
4427     EXPECT_EQ(leaf, nanovdb::createLeafMgr(*grid)[0]);
4428 
4429     leaf->data()->mValues[0] += 0.00001f; // slightly modify a single voxel value
4430 
4431     checksum2(*grid, nanovdb::ChecksumMode::Full);
4432     EXPECT_NE(checksum1, checksum2);
4433 
4434     leaf->data()->mValues[0] -= 0.00001f; // change back the single voxel value to it's original value
4435 
4436     checksum2(*grid, nanovdb::ChecksumMode::Full);
4437     EXPECT_EQ(checksum1, checksum2);
4438 
4439     leaf->data()->mValueMask.toggle(0); // change a single bit in a value mask
4440 
4441     checksum2(*grid, nanovdb::ChecksumMode::Full);
4442     EXPECT_NE(checksum1, checksum2);
4443 
4444     //timer.start("Incomplete checksum");
4445     checksum2(*grid, nanovdb::ChecksumMode::Partial);
4446     //timer.stop();
4447     EXPECT_EQ(checksum2, checksum3);
4448 } // GridChecksum
4449 
TEST_F(TestNanoVDB,GridValidator)4450 TEST_F(TestNanoVDB, GridValidator)
4451 {
4452     nanovdb::CpuTimer<> timer;
4453     //timer.start("nanovdb::createLevelSetSphere");
4454     auto handle = nanovdb::createLevelSetSphere<float>(100.0f,
4455                                                        nanovdb::Vec3f(50),
4456                                                        1.0, 3.0,
4457                                                        nanovdb::Vec3d(0),
4458                                                        "sphere_20",
4459                                                        nanovdb::StatsMode::All,
4460                                                        nanovdb::ChecksumMode::Full);
4461     //timer.stop();
4462     EXPECT_TRUE(handle);
4463     EXPECT_EQ(1u, handle.gridCount());
4464     auto* grid = handle.grid<float>();
4465     EXPECT_TRUE(grid);
4466 
4467     //timer.start("isValid - not detailed");
4468     EXPECT_TRUE(nanovdb::isValid(*grid, false, true));
4469     //timer.stop();
4470 
4471     //timer.start("isValid - detailed");
4472     EXPECT_TRUE(nanovdb::isValid(*grid, true, true));
4473     //timer.stop();
4474 
4475     //timer.start("Full checksum");
4476     auto fastChecksum = nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full);
4477     //timer.stop();
4478     EXPECT_EQ(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
4479 
4480     auto mgr = nanovdb::createLeafMgr(*grid);
4481     auto* leaf = mgr[0];
4482 
4483     leaf->data()->mValues[0] += 0.00001f; // slightly modify a single voxel value
4484 
4485     EXPECT_NE(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
4486     EXPECT_FALSE(nanovdb::isValid(*grid, true, false));
4487 
4488     leaf->data()->mValues[0] -= 0.00001f; // change back the single voxel value to it's original value
4489 
4490     EXPECT_EQ(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
4491     EXPECT_TRUE(nanovdb::isValid(*grid, true, true));
4492 
4493     leaf->data()->mValueMask.toggle(0); // change a singel bit in a value mask
4494 
4495     EXPECT_NE(fastChecksum, nanovdb::checksum(*grid, nanovdb::ChecksumMode::Full));
4496     EXPECT_FALSE(nanovdb::isValid(*grid, true, false));
4497 } // GridValidator
4498 
TEST_F(TestNanoVDB,RandomReadAccessor)4499 TEST_F(TestNanoVDB, RandomReadAccessor)
4500 {
4501     const float background = 0.0f;
4502     const int voxelCount = 512, min = -10000, max = 10000;
4503     std::srand(98765);
4504     auto op = [&](){return rand() % (max - min) + min;};
4505 
4506     for (int i=0; i<10; ++i) {
4507         nanovdb::GridBuilder<float> builder(background);
4508         auto acc = builder.getAccessor();
4509         std::vector<nanovdb::Coord> voxels(voxelCount);
4510         for (int j=0; j<voxelCount; ++j) {
4511             auto &ijk = voxels[j];
4512             ijk[0] = op();
4513             ijk[1] = op();
4514             ijk[2] = op();
4515             acc.setValue(ijk, 1.0f*j);
4516         }
4517         auto gridHdl = builder.getHandle<>();
4518         EXPECT_TRUE(gridHdl);
4519         EXPECT_EQ(1u, gridHdl.gridCount());
4520         auto grid = gridHdl.grid<float>();
4521         EXPECT_TRUE(grid);
4522         const auto &root = grid->tree().root();
4523 #if 1
4524         auto acc0a = nanovdb::createAccessor<>(root);// no node caching
4525         auto acc1a = nanovdb::createAccessor<0>(root);// cache leaf node only
4526         auto acc1b = nanovdb::createAccessor<1>(root);// cache lower internal node only
4527         auto acc1c = nanovdb::createAccessor<2>(root);// cache upper internal node only
4528         auto acc2a = nanovdb::createAccessor<0, 1>(root);// cache leaf and lower internal nodes
4529         auto acc2b = nanovdb::createAccessor<1, 2>(root);// cache lower and upper internal nodes
4530         auto acc2c = nanovdb::createAccessor<0, 2>(root);// cache leaf and upper internal nodes
4531         auto acc3a = nanovdb::createAccessor<0, 1, 2>(root);// cache leaf and both intern node levels
4532         auto acc3b = root.getAccessor();// same as the one above where all levels are cached
4533         auto acc3c = nanovdb::DefaultReadAccessor<float>(root);// same as the one above where all levels are cached
4534 #else
4535         // Alternative (more verbose) way to create accessors
4536         auto acc0a = nanovdb::ReadAccessor<float>(root);// no node caching
4537         auto acc1a = nanovdb::ReadAccessor<float, 0>(root);// cache leaf node only
4538         auto acc1b = nanovdb::ReadAccessor<float, 1>(root);// cache lower internal node only
4539         auto acc1c = nanovdb::ReadAccessor<float, 2>(root);// cache upper internal node only
4540         auto acc2a = nanovdb::ReadAccessor<float, 0, 1>(root);// cache leaf and lower internal nodes
4541         auto acc2b = nanovdb::ReadAccessor<float, 1, 2>(root);// cache lower and upper internal nodes
4542         auto acc2c = nanovdb::ReadAccessor<float, 0, 2>(root);// cache leaf and upper internal nodes
4543         auto acc3a = nanovdb::ReadAccessor<float, 0, 1, 2>(root);// cache leaf and both intern node levels
4544         auto acc3b = nanovdb::DefaultReadAccessor<float>(root);// same as the one above where all levels are cached
4545 #endif
4546         for (int j=0; j<voxelCount; ++j) {
4547             const float v = 1.0f * j;
4548             const auto &ijk = voxels[j];
4549             //if (j<5) std::cerr << ijk << std::endl;
4550             EXPECT_EQ( v, acc0a.getValue(ijk) );
4551             EXPECT_EQ( v, acc1a.getValue(ijk) );
4552             EXPECT_EQ( v, acc1b.getValue(ijk) );
4553             EXPECT_EQ( v, acc1c.getValue(ijk) );
4554             EXPECT_EQ( v, acc2a.getValue(ijk) );
4555             EXPECT_EQ( v, acc2b.getValue(ijk) );
4556             EXPECT_EQ( v, acc2c.getValue(ijk) );
4557             EXPECT_EQ( v, acc3a.getValue(ijk) );
4558             EXPECT_EQ( v, acc3b.getValue(ijk) );
4559             EXPECT_EQ( v, acc3c.getValue(ijk) );
4560         }
4561     }
4562 }
4563 
TEST_F(TestNanoVDB,StandardDeviation)4564 TEST_F(TestNanoVDB, StandardDeviation)
4565 {
4566     nanovdb::GridBuilder<float> builder(0.5f);
4567 
4568     {
4569         auto acc = builder.getAccessor();
4570         acc.setValue(nanovdb::Coord(-1), 1.0f);
4571         acc.setValue(nanovdb::Coord(0), 2.0f);
4572         acc.setValue(nanovdb::Coord(1), 3.0f);
4573         acc.setValue(nanovdb::Coord(2), 0.0f);
4574     }
4575 
4576     auto gridHdl = builder.getHandle<>();
4577     EXPECT_TRUE(gridHdl);
4578     auto grid = gridHdl.grid<float>();
4579     EXPECT_TRUE(grid);
4580     nanovdb::gridStats(*grid);
4581 
4582     auto acc  = grid->tree().getAccessor();
4583     {
4584         EXPECT_EQ( 1.0f,  acc.getValue(nanovdb::Coord(-1)) );
4585         EXPECT_EQ( 2.0f,  acc.getValue(nanovdb::Coord( 0)) );
4586         EXPECT_EQ( 3.0f,  acc.getValue(nanovdb::Coord( 1)) );
4587         EXPECT_EQ( 0.0f,  acc.getValue(nanovdb::Coord( 2)) );
4588         auto nodeInfo = acc.getNodeInfo(nanovdb::Coord(-1));
4589         EXPECT_EQ(nodeInfo.mAverage, 1.f);
4590         EXPECT_EQ(nodeInfo.mLevel, 0u);
4591         EXPECT_EQ(nodeInfo.mDim, 8u);
4592     }
4593     {
4594         auto nodeInfo = acc.getNodeInfo(nanovdb::Coord(1));
4595         EXPECT_EQ(nodeInfo.mAverage, (2.0f + 3.0f) / 3.0f);
4596         auto getStdDev = [&](int n, float a, float b, float c) {
4597             float m = (a + b + c) / n;
4598             float sd = sqrtf(((a - m) * (a - m) +
4599                               (b - m) * (b - m) +
4600                               (c - m) * (c - m)) /
4601                              n);
4602             return sd;
4603         };
4604         EXPECT_NEAR(nodeInfo.mStdDevi, getStdDev(3.0f, 2.0f, 3.0f, 0), 1e-5);
4605         EXPECT_EQ(nodeInfo.mLevel, 0u);
4606         EXPECT_EQ(nodeInfo.mDim, 8u);
4607     }
4608 } // ReadAccessor
4609 
TEST_F(TestNanoVDB,BoxStencil)4610 TEST_F(TestNanoVDB, BoxStencil)
4611 {
4612     const float a = 0.54f, b[3]={0.12f, 0.78f,-0.34f};
4613     const nanovdb::Coord min(-17, -10, -8), max(10, 21, 13);
4614     const nanovdb::CoordBBox bbox(min, max), bbox2(min, max.offsetBy(-1));
4615     nanovdb::GridBuilder<float> builder(0.0f);
4616     auto func = [&](const nanovdb::Coord &ijk) {
4617         return a + b[0]*ijk[0] + b[1]*ijk[1] + b[2]*ijk[2];
4618     };
4619     builder(func, bbox);
4620     auto handle = builder.getHandle();
4621     EXPECT_TRUE(handle);
4622     EXPECT_EQ(1u, handle.gridCount());
4623     auto* grid = handle.grid<float>();
4624     EXPECT_TRUE(grid);
4625     auto acc = grid->getAccessor();
4626     for (auto it = bbox.begin(); it; ++it) {
4627         EXPECT_EQ(func(*it), acc.getValue(*it));
4628     }
4629     auto func2 = [&](const nanovdb::Vec3f &xyz) {
4630         return a + b[0]*xyz[0] + b[1]*xyz[1] + b[2]*xyz[2];
4631     };
4632     nanovdb::BoxStencil<nanovdb::FloatGrid> s(*grid);
4633     for (auto it = bbox2.begin(); it; ++it) {
4634         const nanovdb::Coord p = *it;
4635         s.moveTo(p);
4636         const nanovdb::Vec3f xyz(p[0] + 0.12f, p[1] + 0.34f, p[2] + 0.07f);
4637         EXPECT_NEAR(func2(xyz), s.interpolation(xyz), 5e-6f);
4638         const auto grad = s.gradient(xyz);
4639         EXPECT_NEAR( b[0], grad[0], 3e-6f);
4640         EXPECT_NEAR( b[1], grad[1], 3e-6f);
4641         EXPECT_NEAR( b[2], grad[2], 3e-6f);
4642     }
4643 }// BoxStencil
4644 
TEST_F(TestNanoVDB,CurvatureStencil)4645 TEST_F(TestNanoVDB, CurvatureStencil)
4646 {
4647     {// test of level set to sphere at (6,8,10) with R=10 and dx=0.5
4648         const float radius = 10.0f;
4649         const nanovdb::Vec3f center(6.0, 8.0, 10.0);//i.e. (12,16,20) in index space
4650         auto handle = nanovdb::createLevelSetSphere<float>(radius,
4651                                                     center,
4652                                                     0.5, // dx
4653                                                     20.0); // half-width so dense inside
4654 
4655         EXPECT_TRUE(handle);
4656         EXPECT_EQ(1u, handle.gridCount());
4657         auto* grid = handle.grid<float>();
4658         EXPECT_TRUE(grid);
4659 
4660         nanovdb::CurvatureStencil<nanovdb::FloatGrid> cs(*grid);
4661         nanovdb::Coord xyz(20,16,20);//i.e. 8 voxel or 4 world units away from the center
4662         cs.moveTo(xyz);
4663 
4664         EXPECT_NEAR(1.0/4.0, cs.meanCurvature(), 0.01);// 1/distance from center
4665         EXPECT_NEAR(1.0/4.0, cs.meanCurvatureNormGrad(), 0.01);// 1/distance from center
4666 
4667         EXPECT_NEAR(1.0/16.0, cs.gaussianCurvature(), 0.01);// 1/distance^2 from center
4668         EXPECT_NEAR(1.0/16.0, cs.gaussianCurvatureNormGrad(), 0.01);// 1/distance^2 from center
4669 
4670         //std::cerr << cs.gradient() << std::endl;
4671         EXPECT_NEAR( 1.0f, cs.gradient()[0], 1e-6f);
4672         EXPECT_NEAR( 0.0f, cs.gradient()[1], 1e-6f);
4673         EXPECT_NEAR( 0.0f, cs.gradient()[2], 1e-6f);
4674 
4675         float mean, gaussian;
4676         cs.curvatures(mean, gaussian);
4677         EXPECT_NEAR(1.0/4.0, mean, 0.01);// 1/distance from center
4678         EXPECT_NEAR(1.0/16.0, gaussian, 0.01);// 1/distance^2 from center
4679 
4680         float minCurv, maxCurv;
4681         cs.principalCurvatures(minCurv, maxCurv);
4682         EXPECT_NEAR(1.0/4.0, minCurv, 0.01);// 1/distance from center
4683         EXPECT_NEAR(1.0/4.0, maxCurv, 0.01);// 1/distance from center
4684 
4685         xyz = nanovdb::Coord(12,16,10);//i.e. 10 voxel or 5 world units away from the center
4686         cs.moveTo(xyz);
4687         EXPECT_NEAR(1.0/5.0, cs.meanCurvature(), 0.01);// 1/distance from center
4688         EXPECT_NEAR(
4689             1.0/5.0, cs.meanCurvatureNormGrad(), 0.01);// 1/distance from center
4690 
4691         EXPECT_NEAR(1.0/25.0, cs.gaussianCurvature(), 0.01);// 1/distance^2 from center
4692         EXPECT_NEAR(
4693             1.0/25.0, cs.gaussianCurvatureNormGrad(), 0.01);// 1/distance^2 from center
4694 
4695        cs.principalCurvatures(minCurv, maxCurv);
4696         EXPECT_NEAR(1.0/5.0, minCurv,  0.01);// 1/distance from center
4697         EXPECT_NEAR(1.0/5.0, maxCurv, 0.01);// 1/distance from center
4698         EXPECT_NEAR(
4699             1.0/5.0, minCurv,  0.01);// 1/distance from center
4700             EXPECT_NEAR(
4701             1.0/5.0, maxCurv, 0.01);// 1/distance from center
4702 
4703         cs.curvaturesNormGrad(mean, gaussian);
4704         EXPECT_NEAR(1.0/5.0, mean, 0.01);// 1/distance from center
4705         EXPECT_NEAR(1.0/25.0, gaussian, 0.01);// 1/distance^2 from center
4706     }
4707 
4708     {// test sparse level set sphere
4709       const double percentage = 0.1/100.0;//i.e. 0.1%
4710       const int dim = 256;
4711 
4712       // sparse level set sphere
4713       nanovdb::Vec3f C(0.35f, 0.35f, 0.35f);
4714       double r = 0.15, voxelSize = 1.0/(dim-1);
4715       auto handle = nanovdb::createLevelSetSphere(float(r), C, voxelSize);
4716       EXPECT_TRUE(handle);
4717       EXPECT_EQ(1u, handle.gridCount());
4718       auto* sphere = handle.grid<float>();
4719       EXPECT_TRUE(sphere);
4720 
4721       nanovdb::CurvatureStencil<nanovdb::FloatGrid> cs(*sphere);
4722       const auto ijk = nanovdb::RoundDown<nanovdb::Coord>(sphere->worldToIndex(nanovdb::Vec3d(0.35, 0.35, 0.35 + 0.15)));
4723       const nanovdb::Vec3d tmp(ijk[0],ijk[1],ijk[2]);
4724       const double radius = (sphere->indexToWorld(tmp)-nanovdb::Vec3d(0.35)).length();
4725       //std::cerr << "\rRadius = " << radius << std::endl;
4726       //std::cerr << "Index coord =" << ijk << std::endl;
4727       cs.moveTo(ijk);
4728       auto acc = sphere->getAccessor();
4729       auto v = cs.getValue< 0, 0, 0>();
4730       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0,  0,  0)), v);
4731       v = cs.getValue<-1, 0, 0>();
4732       EXPECT_EQ(acc.getValue(ijk.offsetBy(-1,  0,  0)), v);
4733       v = cs.getValue< 1, 0, 0>();
4734       EXPECT_EQ(acc.getValue(ijk.offsetBy( 1,  0,  0)), v);
4735       v = cs.getValue< 0,-1, 0>();
4736       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0, -1,  0)), v);
4737       v = cs.getValue< 0, 1, 0>();
4738       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0,  1,  0)), v);
4739       v = cs.getValue< 0, 0,-1>();
4740       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0,  0, -1)), v);
4741       v = cs.getValue< 0, 0, 1>();
4742       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0,  0,  1)), v);
4743 
4744       v = cs.getValue<-1,-1, 0>();
4745       EXPECT_EQ(acc.getValue(ijk.offsetBy(-1, -1,  0)), v);
4746       v = cs.getValue< 1,-1, 0>();
4747       EXPECT_EQ(acc.getValue(ijk.offsetBy( 1, -1,  0)), v);
4748       v = cs.getValue<-1, 1, 0>();
4749       EXPECT_EQ(acc.getValue(ijk.offsetBy(-1,  1,  0)), v);
4750       v = cs.getValue< 1,1, 0>();
4751       EXPECT_EQ(acc.getValue(ijk.offsetBy( 1,  1,  0)), v);
4752 
4753       v = cs.getValue<-1, 0, -1>();
4754       EXPECT_EQ(acc.getValue(ijk.offsetBy(-1, 0, -1)), v);
4755       v = cs.getValue< 1, 0, -1>();
4756       EXPECT_EQ(acc.getValue(ijk.offsetBy( 1, 0, -1)), v);
4757       v = cs.getValue<-1, 0, 1>();
4758       EXPECT_EQ(acc.getValue(ijk.offsetBy(-1, 0, 1)), v);
4759       v = cs.getValue< 1, 0, 1>();
4760       EXPECT_EQ(acc.getValue(ijk.offsetBy( 1, 0, 1)), v);
4761 
4762       v = cs.getValue< 0, -1, -1>();
4763       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0, -1, -1)), v);
4764       v = cs.getValue< 0, 1, -1>();
4765       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0, 1, -1)), v);
4766       v = cs.getValue< 0, -1, 1>();
4767       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0, -1, 1)), v);
4768       v = cs.getValue< 0, 1, 1>();
4769       EXPECT_EQ(acc.getValue(ijk.offsetBy( 0, 1, 1)), v);
4770 
4771       //std::cerr << "Mean curvature = "     << cs.meanCurvature()     << ", 1/r=" << 1.0/radius << std::endl;
4772       //std::cerr << "Gaussian curvature = " << cs.gaussianCurvature() << ", 1/(r*r)=" << 1.0/(radius*radius) << std::endl;
4773       EXPECT_NEAR(1.0/radius,  cs.meanCurvature(), percentage*1.0/radius);
4774       EXPECT_NEAR(1.0/(radius*radius),  cs.gaussianCurvature(), percentage*1.0/(radius*radius));
4775       float mean, gauss;
4776       cs.curvatures(mean, gauss);
4777       //std::cerr << "Mean curvature = "     << mean     << ", 1/r=" << 1.0/radius << std::endl;
4778       //std::cerr << "Gaussian curvature = " << gauss << ", 1/(r*r)=" << 1.0/(radius*radius) << std::endl;
4779       EXPECT_NEAR(1.0/radius,  mean, percentage*1.0/radius);
4780       EXPECT_NEAR(1.0/(radius*radius),  gauss, percentage*1.0/(radius*radius));
4781     }
4782 
4783 }// CurvatureStencil
4784 
TEST_F(TestNanoVDB,GradStencil)4785 TEST_F(TestNanoVDB, GradStencil)
4786 {
4787     {// test of level set to sphere at (6,8,10) with R=10 and dx=0.5
4788         const float radius = 10.0f;// 20 voxels
4789         const nanovdb::Vec3f center(6.0, 8.0, 10.0);//i.e. (12,16,20) in index space
4790         auto handle = nanovdb::createLevelSetSphere(radius,
4791                                                     center,
4792                                                     0.5, // dx
4793                                                     20.0);// width, so dense inside
4794 
4795         EXPECT_TRUE(handle);
4796         EXPECT_EQ(1u, handle.gridCount());
4797         auto* grid = handle.grid<float>();
4798         EXPECT_TRUE(grid);
4799         EXPECT_EQ(0.5f, grid->voxelSize()[0]);
4800 
4801         nanovdb::GradStencil<nanovdb::FloatGrid> cs(*grid);
4802 
4803         nanovdb::Coord ijk(12, 16, 20);// on the surface in the +x direction
4804         const nanovdb::Vec3d xyz(ijk[0], ijk[1], ijk[2]);
4805         EXPECT_NEAR(center[0], grid->indexToWorld(xyz)[0], 1e-6);
4806         EXPECT_NEAR(center[1], grid->indexToWorld(xyz)[1], 1e-6);
4807         EXPECT_NEAR(center[2], grid->indexToWorld(xyz)[2], 1e-6);
4808         cs.moveTo(ijk.offsetBy(20, 0, 0));// on the sphere
4809         const float val = cs.getValue<0,0,0>();
4810         EXPECT_NEAR( 0.0f, val, 1e-6);// on the sphere
4811 
4812         EXPECT_NEAR( 1.0f, cs.normSqGrad(), 2e-3f);
4813 
4814         EXPECT_TRUE( cs.zeroCrossing() );
4815 
4816         //std::cerr << cs.gradient() << std::endl;//second order
4817         EXPECT_NEAR( 1.0f, cs.gradient()[0], 1e-6f);
4818         EXPECT_NEAR( 0.0f, cs.gradient()[1], 1e-6f);
4819         EXPECT_NEAR( 0.0f, cs.gradient()[2], 1e-6f);
4820 
4821         const nanovdb::Vec3f v(-1, 0, 0);// upwind direction
4822         //std::cerr << cs.gradient(v) << std::endl;// first order
4823         EXPECT_NEAR( 1.0f, cs.gradient(v)[0], 1e-6f);
4824         EXPECT_NEAR( 0.0f, cs.gradient(v)[1], 2.5e-2f);
4825         EXPECT_NEAR( 0.0f, cs.gradient(v)[2], 2.5e-2f);
4826 
4827         // mean curvature = 0.5 * laplace of SDF => laplacian = 2 * mean curvature = 2 / radius
4828         //std::cerr << "Laplacian = " << cs.laplacian() << " " << (2/radius) << std::endl;
4829         EXPECT_NEAR(cs.laplacian(), 2/radius, 1e-3);
4830     }
4831 }// GradStencil
4832 
TEST_F(TestNanoVDB,WenoStencil)4833 TEST_F(TestNanoVDB, WenoStencil)
4834 {
4835     {// test of level set to sphere at (6,8,10) with R=10 and dx=0.5
4836         const float radius = 10.0f;// 20 voxels
4837         const nanovdb::Vec3f center(6.0, 8.0, 10.0);//i.e. (12,16,20) in index space
4838         auto handle = nanovdb::createLevelSetSphere(radius,
4839                                                     center,
4840                                                     0.5, // dx
4841                                                     20.0);// width, so dense inside
4842 
4843         EXPECT_TRUE(handle);
4844         EXPECT_EQ(1u, handle.gridCount());
4845         auto* grid = handle.grid<float>();
4846         EXPECT_TRUE(grid);
4847         EXPECT_EQ(0.5f, grid->voxelSize()[0]);
4848 
4849         nanovdb::WenoStencil<nanovdb::FloatGrid> cs(*grid);
4850 
4851         nanovdb::Coord ijk(12, 16, 20);// on the surface in the +x direction
4852         const nanovdb::Vec3d xyz(ijk[0], ijk[1], ijk[2]);
4853         EXPECT_NEAR(center[0], grid->indexToWorld(xyz)[0], 1e-6);
4854         EXPECT_NEAR(center[1], grid->indexToWorld(xyz)[1], 1e-6);
4855         EXPECT_NEAR(center[2], grid->indexToWorld(xyz)[2], 1e-6);
4856         cs.moveTo(ijk.offsetBy(20, 0, 0));// on the sphere
4857         const float val = cs.getValue<0,0,0>();
4858         EXPECT_NEAR( 0.0f, val, 1e-6);// on the sphere
4859 
4860         EXPECT_NEAR( 1.0f, cs.normSqGrad(), 1e-6f);
4861 
4862         EXPECT_TRUE( cs.zeroCrossing() );
4863 
4864         //std::cerr << cs.gradient() << std::endl;
4865         EXPECT_NEAR( 1.0f, cs.gradient()[0], 1e-6f);
4866         EXPECT_NEAR( 0.0f, cs.gradient()[1], 1e-6f);
4867         EXPECT_NEAR( 0.0f, cs.gradient()[2], 1e-6f);
4868 
4869         const nanovdb::Vec3f v(-1, 0, 0);// upwind direction
4870         //std::cerr << cs.gradient(v) << std::endl;
4871         EXPECT_NEAR( 1.0f, cs.gradient(v)[0], 1e-6f);
4872         EXPECT_NEAR( 0.0f, cs.gradient(v)[1], 1e-6f);
4873         EXPECT_NEAR( 0.0f, cs.gradient(v)[2], 1e-6f);
4874 
4875         // mean curvature = 0.5 * laplace of SDF => laplacian = 2 * mean curvature = 2 / radius
4876         //std::cerr << "Laplacian = " << cs.laplacian() << " " << (2/radius) << std::endl;
4877         EXPECT_NEAR(cs.laplacian(), 2/radius, 1e-3);
4878     }
4879 }// WenoStencil
4880 
TEST_F(TestNanoVDB,StencilIntersection)4881 TEST_F(TestNanoVDB, StencilIntersection)
4882 {
4883   const nanovdb::Coord ijk(1,4,-9);
4884   nanovdb::GridBuilder<float> builder(0.0f);
4885   auto acc = builder.getAccessor();
4886   acc.setValue(ijk,-1.0f);
4887   int cases = 0;
4888   for (int mx=0; mx<2; ++mx) {
4889     acc.setValue(ijk.offsetBy(-1,0,0), mx ? 1.0f : -1.0f);
4890     for (int px=0; px<2; ++px) {
4891       acc.setValue(ijk.offsetBy(1,0,0), px ? 1.0f : -1.0f);
4892       for (int my=0; my<2; ++my) {
4893         acc.setValue(ijk.offsetBy(0,-1,0), my ? 1.0f : -1.0f);
4894         for (int py=0; py<2; ++py) {
4895           acc.setValue(ijk.offsetBy(0,1,0), py ? 1.0f : -1.0f);
4896           for (int mz=0; mz<2; ++mz) {
4897             acc.setValue(ijk.offsetBy(0,0,-1), mz ? 1.0f : -1.0f);
4898             for (int pz=0; pz<2; ++pz) {
4899               acc.setValue(ijk.offsetBy(0,0,1), pz ? 1.0f : -1.0f);
4900               ++cases;
4901               auto handle = builder.getHandle<>();
4902               EXPECT_TRUE(handle);
4903               auto grid = handle.grid<float>();
4904               EXPECT_TRUE(grid);
4905               EXPECT_EQ(7, int(grid->activeVoxelCount()));
4906               nanovdb::GradStencil<nanovdb::FloatGrid> stencil(*grid);
4907               stencil.moveTo(ijk);
4908               const int count = mx + px + my + py + mz + pz;// number of intersections
4909               EXPECT_TRUE(stencil.intersects() == (count > 0));
4910               auto mask = stencil.intersectionMask();
4911               EXPECT_TRUE(mask.none() == (count == 0));
4912               EXPECT_TRUE(mask.any() == (count > 0));
4913               EXPECT_EQ(count, mask.count());
4914               EXPECT_TRUE(mask.test(0) == mx);
4915               EXPECT_TRUE(mask.test(1) == px);
4916               EXPECT_TRUE(mask.test(2) == my);
4917               EXPECT_TRUE(mask.test(3) == py);
4918               EXPECT_TRUE(mask.test(4) == mz);
4919               EXPECT_TRUE(mask.test(5) == pz);
4920             }//pz
4921           }//mz
4922         }//py
4923       }//my
4924     }//px
4925   }//mx
4926   EXPECT_EQ(64, cases);// = 2^6
4927 }// StencilIntersection
4928 
TEST_F(TestNanoVDB,MultiFile)4929 TEST_F(TestNanoVDB, MultiFile)
4930 {
4931     std::vector<nanovdb::GridHandle<>> handles;
4932     { // add an int32_t grid
4933         nanovdb::GridBuilder<int> builder(-1);
4934         auto acc = builder.getAccessor();
4935         acc.setValue(nanovdb::Coord(-256), 10);
4936         handles.push_back(builder.getHandle(1.0, nanovdb::Vec3d(0), "Int32 grid"));
4937     }
4938     { // add an empty int32_t grid
4939         nanovdb::GridBuilder<int> builder(-4);
4940         handles.push_back(builder.getHandle(1.0, nanovdb::Vec3d(0), "Int32 grid, empty"));
4941     }
4942     { // add a Vec3f grid
4943         nanovdb::GridBuilder<nanovdb::Vec3f> builder(nanovdb::Vec3f(0.0f, 0.0f, -1.0f));
4944         builder.setGridClass(nanovdb::GridClass::Staggered);
4945         auto acc = builder.getAccessor();
4946         acc.setValue(nanovdb::Coord(-256), nanovdb::Vec3f(1.0f, 0.0f, 0.0f));
4947         handles.push_back(builder.getHandle(1.0, nanovdb::Vec3d(0), "Float vector grid"));
4948     }
4949     { // add an int64_t grid
4950         nanovdb::GridBuilder<int64_t> builder(0);
4951         auto acc = builder.getAccessor();
4952         acc.setValue(nanovdb::Coord(0), 10);
4953         handles.push_back(builder.getHandle(1.0, nanovdb::Vec3d(0), "Int64 grid"));
4954     }
4955     for (int i = 0; i < 10; ++i) {
4956         const float          radius = 100.0f;
4957         const float          voxelSize = 1.0f, width = 3.0f;
4958         const nanovdb::Vec3f center(i * 10.0f, 0.0f, 0.0f);
4959         handles.push_back(nanovdb::createLevelSetSphere(radius, center, voxelSize, width,
4960                           nanovdb::Vec3d(0), "Level set sphere at (" + std::to_string(i * 10) + ",0,0)"));
4961     }
4962     { // add a double grid
4963         nanovdb::GridBuilder<double> builder(0.0);
4964         builder.setGridClass(nanovdb::GridClass::FogVolume);
4965         auto acc = builder.getAccessor();
4966         acc.setValue(nanovdb::Coord(6000), 1.0);
4967         handles.push_back(builder.getHandle(1.0, nanovdb::Vec3d(0), "Double grid"));
4968     }
4969 #if defined(NANOVDB_USE_BLOSC)
4970     nanovdb::io::writeGrids<nanovdb::HostBuffer, std::vector>("data/multi1.nvdb", handles, nanovdb::io::Codec::BLOSC);
4971 #elif defined(NANOVDB_USE_ZIP)
4972     nanovdb::io::writeGrids<nanovdb::HostBuffer, std::vector>("data/multi1.nvdb", handles, nanovdb::io::Codec::ZIP);
4973 #else
4974     nanovdb::io::writeGrids<nanovdb::HostBuffer, std::vector>("data/multi1.nvdb", handles, nanovdb::io::Codec::NONE);
4975 #endif
4976     { // read grid meta data and test it
4977         //mTimer.start("nanovdb::io::readGridMetaData");
4978         auto meta = nanovdb::io::readGridMetaData("data/multi1.nvdb");
4979         //mTimer.stop();
4980         EXPECT_EQ(15u, meta.size());
4981         EXPECT_EQ(std::string("Double grid"), meta.back().gridName);
4982     }
4983     { // read in32 grid and test values
4984         //mTimer.start("Reading multiple grids from file");
4985         auto handles = nanovdb::io::readGrids("data/multi1.nvdb");
4986         //mTimer.stop();
4987         EXPECT_EQ(15u, handles.size());
4988         auto& handle = handles.front();
4989         EXPECT_EQ(1u, handle.gridCount());
4990         EXPECT_EQ(std::string("Int32 grid"), handle.gridMetaData()->shortGridName());
4991         EXPECT_FALSE(handle.grid<float>());
4992         EXPECT_FALSE(handle.grid<double>());
4993         EXPECT_FALSE(handle.grid<int64_t>());
4994         EXPECT_FALSE(handle.grid<nanovdb::Vec3f>());
4995         EXPECT_FALSE(handle.grid<nanovdb::Vec3d>());
4996         auto* grid = handle.grid<int32_t>();
4997         EXPECT_TRUE(grid);
4998         EXPECT_EQ(1u, grid->activeVoxelCount());
4999         const nanovdb::Coord ijk(-256);
5000         const auto&          tree = grid->tree();
5001         EXPECT_EQ(10, tree.getValue(ijk));
5002         EXPECT_EQ(-1, tree.getValue(ijk + nanovdb::Coord(1, 0, 0)));
5003         EXPECT_EQ(10, tree.root().minimum());
5004         EXPECT_EQ(10, tree.root().maximum());
5005         EXPECT_TRUE(grid->tree().isActive(ijk));
5006         EXPECT_FALSE(grid->tree().isActive(nanovdb::Coord( 10, 450, 90)));
5007         EXPECT_FALSE(grid->tree().isActive(nanovdb::Coord(-10,-450,-90)));
5008         EXPECT_FALSE(grid->tree().isActive(ijk + nanovdb::Coord(1, 0, 0)));
5009         const nanovdb::CoordBBox bbox(ijk, ijk);
5010         EXPECT_EQ(bbox, grid->indexBBox());
5011         EXPECT_EQ(handle.gridMetaData()->indexBBox(), grid->indexBBox());
5012         EXPECT_EQ(1u, tree.nodeCount(0));
5013         EXPECT_EQ(1u, tree.nodeCount(1));
5014         EXPECT_EQ(1u, tree.nodeCount(2));
5015         const auto* leaf = tree.getFirstNode<0>();
5016         EXPECT_TRUE(leaf);
5017         EXPECT_EQ(bbox, leaf->bbox());
5018         const auto* node1 = tree.getFirstNode<1>();
5019         EXPECT_TRUE(node1);
5020         EXPECT_EQ(bbox, node1->bbox());
5021         const auto* node2 = tree.getFirstNode<2>();
5022         EXPECT_TRUE(node2);
5023         EXPECT_EQ(bbox, node2->bbox());
5024         EXPECT_FALSE(grid->isLevelSet());
5025         EXPECT_FALSE(grid->isFogVolume());
5026         EXPECT_TRUE(grid->isUnknown());
5027         EXPECT_FALSE(grid->isStaggered());
5028     }
5029     { // read empty in32 grid and test values
5030         //mTimer.start("Reading multiple grids from file");
5031         auto handles = nanovdb::io::readGrids("data/multi1.nvdb");
5032         //mTimer.stop();
5033         EXPECT_EQ(15u, handles.size());
5034         auto& handle = handles[1];
5035         EXPECT_TRUE(handle);
5036         EXPECT_EQ(1u, handle.gridCount());
5037         EXPECT_EQ(std::string("Int32 grid, empty"), handle.gridMetaData()->shortGridName());
5038         EXPECT_FALSE(handle.grid<float>());
5039         EXPECT_FALSE(handle.grid<double>());
5040         EXPECT_FALSE(handle.grid<int64_t>());
5041         EXPECT_FALSE(handle.grid<nanovdb::Vec3f>());
5042         EXPECT_FALSE(handle.grid<nanovdb::Vec3d>());
5043         auto* grid = handle.grid<int32_t>();
5044         EXPECT_TRUE(grid);
5045         EXPECT_EQ(0u, grid->activeVoxelCount());
5046         const nanovdb::Coord ijk(-256);
5047         EXPECT_EQ(-4, grid->tree().getValue(ijk));
5048         EXPECT_EQ(-4, grid->tree().getValue(ijk + nanovdb::Coord(1, 0, 0)));
5049         EXPECT_FALSE(grid->tree().isActive(ijk));
5050         EXPECT_FALSE(grid->tree().isActive(nanovdb::Coord( 10, 450, 90)));
5051         EXPECT_FALSE(grid->tree().isActive(nanovdb::Coord(-10,-450,-90)));
5052         EXPECT_FALSE(grid->tree().isActive(ijk + nanovdb::Coord(1, 0, 0)));
5053         EXPECT_EQ(-4, grid->tree().root().minimum());
5054         EXPECT_EQ(-4, grid->tree().root().maximum());
5055         EXPECT_EQ(nanovdb::Coord(std::numeric_limits<int>::max()), grid->indexBBox().min());
5056         EXPECT_EQ(nanovdb::Coord(std::numeric_limits<int>::min()), grid->indexBBox().max());
5057         EXPECT_EQ(handle.gridMetaData()->indexBBox(), grid->indexBBox());
5058         EXPECT_EQ(0u, grid->tree().nodeCount(0));
5059         EXPECT_EQ(0u, grid->tree().nodeCount(1));
5060         EXPECT_EQ(0u, grid->tree().nodeCount(2));
5061         EXPECT_FALSE(grid->isLevelSet());
5062         EXPECT_FALSE(grid->isFogVolume());
5063         EXPECT_FALSE(grid->isMask());
5064         EXPECT_TRUE(grid->isUnknown());
5065         EXPECT_FALSE(grid->isStaggered());
5066     }
5067     { // read int64 grid and test values
5068         //mTimer.start("Reading multiple grids from file");
5069         auto handles = nanovdb::io::readGrids("data/multi1.nvdb");
5070         //mTimer.stop();
5071         EXPECT_EQ(15u, handles.size());
5072         auto& handle = handles[3];
5073         EXPECT_EQ(1u, handle.gridCount());
5074         EXPECT_TRUE(handle);
5075         EXPECT_EQ(std::string("Int64 grid"), handle.gridMetaData()->shortGridName());
5076         auto* grid = handle.grid<int64_t>();
5077         EXPECT_TRUE(grid);
5078         EXPECT_EQ(handle.gridMetaData()->indexBBox(), grid->indexBBox());
5079         EXPECT_EQ(1u, grid->activeVoxelCount());
5080         const nanovdb::Coord ijk(0);
5081         EXPECT_EQ(10, grid->tree().getValue(ijk));
5082         EXPECT_EQ(0, grid->tree().getValue(ijk + nanovdb::Coord(1, 0, 0)));
5083         EXPECT_EQ(10, grid->tree().root().minimum());
5084         EXPECT_EQ(10, grid->tree().root().maximum());
5085         EXPECT_EQ(nanovdb::CoordBBox(ijk, ijk), grid->indexBBox());
5086         EXPECT_FALSE(grid->isLevelSet());
5087         EXPECT_FALSE(grid->isFogVolume());
5088         EXPECT_TRUE(grid->isUnknown());
5089         EXPECT_FALSE(grid->isStaggered());
5090     }
5091     { // read vec3f grid and test values
5092         auto handles = nanovdb::io::readGrids("data/multi1.nvdb");
5093         EXPECT_EQ(15u, handles.size());
5094         auto& handle = handles[2];
5095         EXPECT_TRUE(handle);
5096         EXPECT_EQ(1u, handle.gridCount());
5097         EXPECT_EQ(std::string("Float vector grid"), handle.gridMetaData()->shortGridName());
5098         auto* grid = handle.grid<nanovdb::Vec3f>();
5099         EXPECT_TRUE(grid);
5100         EXPECT_EQ(1u, grid->activeVoxelCount());
5101         EXPECT_EQ(handle.gridMetaData()->indexBBox(), grid->indexBBox());
5102         const nanovdb::Coord ijk(-256);
5103         EXPECT_EQ(nanovdb::Vec3f(1.0f, 0.0f, 0.0f), grid->tree().getValue(ijk));
5104         EXPECT_EQ(nanovdb::Vec3f(0.0f, 0.0f, -1.0f), grid->tree().getValue(ijk + nanovdb::Coord(1, 0, 0)));
5105         EXPECT_EQ(nanovdb::Vec3f(1.0f, 0.0f, 0.0f), grid->tree().root().minimum());
5106         EXPECT_EQ(nanovdb::Vec3f(1.0f, 0.0f, 0.0f), grid->tree().root().maximum());
5107         EXPECT_EQ(nanovdb::CoordBBox(ijk, ijk), grid->indexBBox());
5108         EXPECT_FALSE(grid->isLevelSet());
5109         EXPECT_FALSE(grid->isFogVolume());
5110         EXPECT_FALSE(grid->isUnknown());
5111         EXPECT_TRUE(grid->isStaggered());
5112     }
5113     { // read double grid and test values
5114         auto handles = nanovdb::io::readGrids("data/multi1.nvdb");
5115         EXPECT_EQ(15u, handles.size());
5116         auto& handle = handles.back();
5117         EXPECT_TRUE(handle);
5118         EXPECT_EQ(1u, handle.gridCount());
5119         EXPECT_EQ(std::string("Double grid"), handle.gridMetaData()->shortGridName());
5120         auto* grid = handle.grid<double>();
5121         EXPECT_TRUE(grid);
5122         EXPECT_EQ(1u, grid->activeVoxelCount());
5123         const nanovdb::Coord ijk(6000);
5124         EXPECT_EQ(1.0, grid->tree().getValue(ijk));
5125         EXPECT_EQ(0.0, grid->tree().getValue(ijk + nanovdb::Coord(1, 0, 0)));
5126         EXPECT_EQ(1.0, grid->tree().root().minimum());
5127         EXPECT_EQ(1.0, grid->tree().root().maximum());
5128         EXPECT_EQ(nanovdb::CoordBBox(ijk, ijk), grid->tree().bbox());
5129         EXPECT_EQ(handle.gridMetaData()->indexBBox(), grid->indexBBox());
5130         EXPECT_FALSE(grid->isLevelSet());
5131         EXPECT_TRUE(grid->isFogVolume());
5132         EXPECT_FALSE(grid->isUnknown());
5133         EXPECT_FALSE(grid->isStaggered());
5134     }
5135 } // MultiFile
5136 
TEST_F(TestNanoVDB,HostBuffer)5137 TEST_F(TestNanoVDB, HostBuffer)
5138 {
5139     {// internal memory - HostBuffer
5140         std::vector<nanovdb::GridHandle<> > gridHdls;
5141 
5142         // create two grids...
5143         gridHdls.push_back(nanovdb::createLevelSetSphere(100.0f, nanovdb::Vec3f(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3R(0), "spheref"));
5144         gridHdls.push_back(nanovdb::createLevelSetSphere(100.0,  nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3R(0), "sphered"));
5145 
5146         EXPECT_TRUE(gridHdls[0]);
5147         auto* meta0 = gridHdls[0].gridMetaData();
5148         EXPECT_TRUE(meta0);
5149         EXPECT_FALSE(meta0->isEmpty());
5150         EXPECT_EQ("spheref", std::string(meta0->shortGridName()));
5151         EXPECT_EQ(nanovdb::GridType::Float, meta0->gridType());
5152         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta0->gridClass());
5153         auto* grid0 = gridHdls[0].grid<float>();
5154         EXPECT_TRUE(grid0);
5155         auto acc0 = grid0->getAccessor();
5156         EXPECT_EQ(0.0f, acc0.getValue(nanovdb::Coord(-20+100, 0, 0)));
5157 
5158         EXPECT_TRUE(gridHdls[1]);
5159         auto* meta1 = gridHdls[1].gridMetaData();
5160         EXPECT_TRUE(meta1);
5161         EXPECT_FALSE(meta1->isEmpty());
5162         EXPECT_EQ("sphered", std::string(meta1->shortGridName()));
5163         EXPECT_EQ(nanovdb::GridType::Double, meta1->gridType());
5164         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta1->gridClass());
5165         auto* grid1 = gridHdls[1].grid<double>();
5166         EXPECT_TRUE(grid1);
5167         auto acc1 = grid1->getAccessor();
5168         EXPECT_EQ(0.0, acc1.getValue(nanovdb::Coord( 20+100, 0, 0)));
5169     }
5170     {// internal memory - bump pool
5171         const size_t poolSize = 1 << 26;// 64 MB
5172         auto pool = nanovdb::HostBuffer::createPool(poolSize);
5173         EXPECT_TRUE(pool.isManaged());
5174         EXPECT_EQ(64ULL * 1024 * 1024, pool.poolSize());
5175         EXPECT_TRUE(pool.isPool());
5176         EXPECT_TRUE(pool.isEmpty());
5177         EXPECT_FALSE(pool.isFull());
5178 
5179         std::vector<nanovdb::GridHandle<> > gridHdls;
5180 
5181         // create two grids...
5182         gridHdls.push_back(nanovdb::createLevelSetSphere(100.0f, nanovdb::Vec3f(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3R(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool));
5183         gridHdls.push_back(nanovdb::createLevelSetSphere(100.0,  nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3R(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool));
5184 
5185         EXPECT_TRUE(gridHdls[0]);
5186         auto* meta0 = gridHdls[0].gridMetaData();
5187         EXPECT_TRUE(meta0);
5188         EXPECT_FALSE(meta0->isEmpty());
5189         EXPECT_EQ("spheref", std::string(meta0->shortGridName()));
5190         EXPECT_EQ(nanovdb::GridType::Float, meta0->gridType());
5191         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta0->gridClass());
5192         auto* grid0 = gridHdls[0].grid<float>();
5193         //printf("Before resize: address of grid0 = %p\n", (void*)grid0);
5194         EXPECT_TRUE(grid0);
5195         auto acc0 = grid0->getAccessor();
5196         EXPECT_EQ(0.0f, acc0.getValue(nanovdb::Coord(-20+100, 0, 0)));
5197 
5198         EXPECT_TRUE(gridHdls[1]);
5199         auto* meta1 = gridHdls[1].gridMetaData();
5200         EXPECT_TRUE(meta1);
5201         EXPECT_FALSE(meta1->isEmpty());
5202         EXPECT_EQ("sphered", std::string(meta1->shortGridName()));
5203         EXPECT_EQ(nanovdb::GridType::Double, meta1->gridType());
5204         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta1->gridClass());
5205         auto* grid1 = gridHdls[1].grid<double>();
5206         EXPECT_TRUE(grid1);
5207         auto acc1 = grid1->getAccessor();
5208         EXPECT_EQ(0.0, acc1.getValue(nanovdb::Coord( 20+100, 0, 0)));
5209 
5210         pool.resizePool( 2*poolSize );
5211         EXPECT_TRUE(pool.isManaged());
5212         EXPECT_EQ(128ULL * 1024 * 1024, pool.poolSize());
5213         EXPECT_TRUE(pool.isPool());
5214         EXPECT_TRUE(pool.isEmpty());// because this buffer does not use the pool
5215         EXPECT_FALSE(pool.isFull());
5216 
5217         EXPECT_TRUE(gridHdls[0]);
5218         meta0 = gridHdls[0].gridMetaData();
5219         EXPECT_TRUE(meta0);
5220         EXPECT_FALSE(meta0->isEmpty());
5221         EXPECT_EQ("spheref", std::string(meta0->shortGridName()));
5222         EXPECT_EQ(nanovdb::GridType::Float, meta0->gridType());
5223         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta0->gridClass());
5224         grid0 = gridHdls[0].grid<float>();
5225         //printf("After  resize: address of grid0 = %p\n", (void*)grid0);
5226         EXPECT_TRUE(grid0);
5227         acc0 = grid0->getAccessor();
5228         EXPECT_EQ(0.0f, acc0.getValue(nanovdb::Coord(-20+100, 0, 0)));
5229 
5230         EXPECT_TRUE(gridHdls[1]);
5231         meta1 = gridHdls[1].gridMetaData();
5232         EXPECT_TRUE(meta1);
5233         EXPECT_FALSE(meta1->isEmpty());
5234         EXPECT_EQ("sphered", std::string(meta1->shortGridName()));
5235         EXPECT_EQ(nanovdb::GridType::Double, meta1->gridType());
5236         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta1->gridClass());
5237         grid1 = gridHdls[1].grid<double>();
5238         EXPECT_TRUE(grid1);
5239         acc1 = grid1->getAccessor();
5240         EXPECT_EQ(0.0, acc1.getValue(nanovdb::Coord( 20+100, 0, 0)));
5241 
5242         pool.reset();
5243         EXPECT_TRUE(pool.isManaged());
5244         EXPECT_EQ(128ULL * 1024 * 1024, pool.poolSize());
5245         EXPECT_TRUE(pool.isPool());
5246         EXPECT_TRUE(pool.isEmpty());// because this buffer does not use the pool
5247         EXPECT_FALSE(pool.isFull());
5248 
5249         EXPECT_FALSE(gridHdls[0]);
5250         EXPECT_FALSE(gridHdls[1]);
5251     }
5252     {// insufficient internal memory
5253         const size_t poolSize = 1 << 6;// 64 B
5254         auto pool = nanovdb::HostBuffer::createPool(poolSize);
5255         EXPECT_EQ(64ULL, pool.poolSize());
5256         EXPECT_TRUE(pool.isPool());
5257         EXPECT_TRUE(pool.isEmpty());
5258         EXPECT_FALSE(pool.isFull());
5259 
5260         std::vector<nanovdb::GridHandle<> > gridHdls;
5261 
5262         // create two grids...
5263         ASSERT_THROW(gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0f, nanovdb::Vec3f(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool)), std::runtime_error);
5264         ASSERT_THROW(gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0,  nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool)), std::runtime_error);
5265     }
5266     {// zero internal memory size
5267         ASSERT_THROW(nanovdb::HostBuffer::createPool(0), std::runtime_error);
5268         ASSERT_THROW(nanovdb::HostBuffer::createFull(0), std::runtime_error);
5269     }
5270     {// external memory
5271 
5272         const size_t poolSize = 1 << 26;// 64 MB
5273         std::unique_ptr<uint8_t[]> buffer(new uint8_t[poolSize]);
5274         auto pool = nanovdb::HostBuffer::createPool(poolSize, buffer.get());
5275         EXPECT_EQ(64ULL * 1024 * 1024, pool.poolSize());
5276         EXPECT_FALSE(pool.isManaged());
5277         EXPECT_TRUE(pool.isPool());
5278         EXPECT_TRUE(pool.isEmpty());
5279         EXPECT_FALSE(pool.isFull());
5280 
5281         std::vector<nanovdb::GridHandle<> > gridHdls;
5282 
5283         // create two grids...
5284         gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0f, nanovdb::Vec3f(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool));
5285         gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0,  nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool));
5286 
5287         EXPECT_TRUE(gridHdls[0]);
5288         auto* meta0 = gridHdls[0].gridMetaData();
5289         EXPECT_TRUE(meta0);
5290         EXPECT_FALSE(meta0->isEmpty());
5291         EXPECT_EQ("spheref", std::string(meta0->shortGridName()));
5292         EXPECT_EQ(nanovdb::GridType::Float, meta0->gridType());
5293         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta0->gridClass());
5294         auto* grid0 = gridHdls[0].grid<float>();
5295         EXPECT_TRUE(grid0);
5296         auto acc0 = grid0->getAccessor();
5297         EXPECT_EQ(0.0f, acc0.getValue(nanovdb::Coord(-20+100, 0, 0)));
5298 
5299         EXPECT_TRUE(gridHdls[1]);
5300         auto* meta1 = gridHdls[1].gridMetaData();
5301         EXPECT_TRUE(meta1);
5302         EXPECT_FALSE(meta1->isEmpty());
5303         EXPECT_EQ("sphered", std::string(meta1->shortGridName()));
5304         EXPECT_EQ(nanovdb::GridType::Double, meta1->gridType());
5305         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta1->gridClass());
5306         auto* grid1 = gridHdls[1].grid<double>();
5307         EXPECT_TRUE(grid1);
5308         auto acc1 = grid1->getAccessor();
5309         EXPECT_EQ(0.0, acc1.getValue(nanovdb::Coord( 20+100, 0, 0)));
5310 
5311         pool.reset();
5312 
5313         EXPECT_FALSE(gridHdls[0]);
5314         EXPECT_FALSE(gridHdls[1]);
5315     }
5316     {// insufficient external memory
5317         const size_t poolSize = 64;// 64 B
5318         uint8_t *data = static_cast<uint8_t*>(std::malloc(poolSize));
5319         auto pool = nanovdb::HostBuffer::createPool(poolSize, data);
5320         EXPECT_EQ(0ULL, pool.size());
5321         EXPECT_EQ(64ULL, pool.poolSize());
5322         EXPECT_EQ(0ULL, pool.poolUsage());
5323         EXPECT_TRUE(pool.isPool());
5324         EXPECT_TRUE(pool.isEmpty());
5325         EXPECT_FALSE(pool.isFull());
5326         EXPECT_FALSE(pool.isManaged());
5327 
5328         auto buffer = nanovdb::HostBuffer::create(32, &pool);
5329         EXPECT_EQ(32ULL, buffer.size());
5330         EXPECT_EQ(64ULL, buffer.poolSize());
5331         EXPECT_EQ(32ULL,  pool.poolUsage());
5332         EXPECT_FALSE(buffer.isPool());
5333         EXPECT_FALSE(buffer.isEmpty());
5334         EXPECT_FALSE(buffer.isFull());
5335         EXPECT_FALSE(buffer.isManaged());
5336 
5337         std::vector<nanovdb::GridHandle<> > gridHdls;
5338 
5339         // create two grids...
5340         ASSERT_THROW(gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0f, nanovdb::Vec3f(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool)), std::runtime_error);
5341         ASSERT_THROW(gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0,  nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool)), std::runtime_error);
5342 
5343         EXPECT_FALSE(pool.isManaged());
5344         pool.resizePool(1<<26);// resize to 64 MB
5345         EXPECT_TRUE(pool.isManaged());
5346         std::free(data);
5347 
5348         EXPECT_EQ(0ULL, pool.size());
5349         EXPECT_EQ(1ULL<<26, pool.poolSize());
5350         EXPECT_TRUE(pool.isPool());
5351         EXPECT_TRUE(pool.isEmpty());
5352         EXPECT_FALSE(pool.isFull());
5353         EXPECT_TRUE(pool.isManaged());
5354 
5355         EXPECT_EQ(32ULL, buffer.size());
5356         EXPECT_EQ(1ULL<<26, buffer.poolSize());
5357         EXPECT_FALSE(buffer.isPool());
5358         EXPECT_FALSE(buffer.isEmpty());
5359         EXPECT_FALSE(buffer.isFull());
5360         EXPECT_TRUE(buffer.isManaged());
5361 
5362         gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0f, nanovdb::Vec3f(-20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "spheref", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool));
5363         gridHdls.push_back(nanovdb::createLevelSetSphere( 100.0,  nanovdb::Vec3d( 20, 0, 0), 1.0, 3.0, nanovdb::Vec3d(0), "sphered", nanovdb::StatsMode::BBox, nanovdb::ChecksumMode::Partial, -1.0f, false, pool));
5364 
5365         EXPECT_TRUE(gridHdls[0]);
5366         auto* meta0 = gridHdls[0].gridMetaData();
5367         EXPECT_TRUE(meta0);
5368         EXPECT_FALSE(meta0->isEmpty());
5369         EXPECT_EQ("spheref", std::string(meta0->shortGridName()));
5370         EXPECT_EQ(nanovdb::GridType::Float, meta0->gridType());
5371         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta0->gridClass());
5372         auto* grid0 = gridHdls[0].grid<float>();
5373         EXPECT_TRUE(grid0);
5374         auto acc0 = grid0->getAccessor();
5375         EXPECT_EQ(0.0f, acc0.getValue(nanovdb::Coord(-20+100, 0, 0)));
5376 
5377         EXPECT_TRUE(gridHdls[1]);
5378         auto* meta1 = gridHdls[1].gridMetaData();
5379         EXPECT_TRUE(meta1);
5380         EXPECT_FALSE(meta1->isEmpty());
5381         EXPECT_EQ("sphered", std::string(meta1->shortGridName()));
5382         EXPECT_EQ(nanovdb::GridType::Double, meta1->gridType());
5383         EXPECT_EQ(nanovdb::GridClass::LevelSet, meta1->gridClass());
5384         auto* grid1 = gridHdls[1].grid<double>();
5385         EXPECT_TRUE(grid1);
5386         auto acc1 = grid1->getAccessor();
5387         EXPECT_EQ(0.0, acc1.getValue(nanovdb::Coord( 20+100, 0, 0)));
5388 
5389         pool.reset();
5390         EXPECT_EQ(0ULL, pool.poolUsage());
5391         EXPECT_TRUE(pool.isManaged());
5392 
5393         EXPECT_FALSE(gridHdls[0]);
5394         EXPECT_FALSE(gridHdls[1]);
5395     }
5396     {// zero external memory size
5397         const size_t poolSize = 1 << 6;// 64 B
5398         uint8_t *data = static_cast<uint8_t*>(std::malloc(poolSize));
5399         ASSERT_THROW(nanovdb::HostBuffer::createPool(0, data), std::runtime_error);
5400         std::free(data);
5401     }
5402     try {// reading multiple grids into a HostBuffer with external memory
5403         const size_t poolSize = 1 << 27;// 128 MB
5404         std::unique_ptr<uint8_t[]> array(new uint8_t[poolSize]);// scoped buffer
5405         auto pool = nanovdb::HostBuffer::createPool(poolSize, array.get());
5406         EXPECT_EQ(128ULL * 1024 * 1024, pool.poolSize());
5407         auto handles = nanovdb::io::readGrids("data/multi1.nvdb", 0, pool);
5408         EXPECT_EQ(15u, handles.size());
5409         for (auto &h : handles) EXPECT_TRUE(h);
5410         EXPECT_EQ(std::string("Int32 grid"), handles[0].grid<int>()->gridName());
5411         EXPECT_EQ(std::string("Int32 grid, empty"), handles[1].grid<int>()->gridName());
5412         EXPECT_EQ(std::string("Float vector grid"), handles[2].grid<nanovdb::Vec3f>()->gridName());
5413         EXPECT_EQ(std::string("Int64 grid"), handles[3].grid<int64_t>()->gridName());
5414         EXPECT_EQ(std::string("Double grid"), handles[14].grid<double>()->gridName());
5415         pool.reset();
5416         for (auto &h : handles) EXPECT_FALSE(h);
5417         handles = nanovdb::io::readGrids("data/multi1.nvdb", 0, pool);
5418         EXPECT_EQ(15u, handles.size());
5419         for (auto &h : handles) EXPECT_TRUE(h);
5420         EXPECT_EQ(std::string("Int32 grid"), handles[0].grid<int>()->gridName());
5421         EXPECT_EQ(std::string("Int32 grid, empty"), handles[1].grid<int>()->gridName());
5422         EXPECT_EQ(std::string("Float vector grid"), handles[2].grid<nanovdb::Vec3f>()->gridName());
5423         EXPECT_EQ(std::string("Int64 grid"), handles[3].grid<int64_t>()->gridName());
5424         EXPECT_EQ(std::string("Double grid"), handles[14].grid<double>()->gridName());
5425     } catch(const std::exception& e) {
5426         std::cout << "Unable to read \"data/multi1.nvdb\" for unit-test\n" << e.what() << std::endl;
5427     }
5428 }// HostBuffer
5429 
main(int argc,char ** argv)5430 int main(int argc, char** argv)
5431 {
5432     ::testing::InitGoogleTest(&argc, argv);
5433     return RUN_ALL_TESTS();
5434 }
5435