1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 //
5 // Simple C-wrapper for the nanovdb structure
6 // Meant for systems where you lack a C++ compiler.
7 //
8 #ifndef __CNANOVDB__
9 #define __CNANOVDB__
10 
11 #define CNANOVDB_DATA_ALIGNMENT 32
12 #define CNANOVDB_ALIGNMENT_PADDING(x, n) (-(x) & ((n)-1))
13 
14 #define USE_SINGLE_ROOT_KEY
15 
16 #ifdef __OPENCL_VERSION__
17 
18 #define CNANOVDB_GLOBAL __global
19 #define RESTRICT restrict
20 
21 // OpenCL doesn't define these basic types:
22 typedef unsigned long uint64_t;
23 typedef long int64_t;
24 typedef unsigned int uint32_t;
25 typedef int int32_t;
26 typedef short int16_t;
27 typedef unsigned short uint16_t;
28 typedef unsigned char uint8_t;
29 
30 #else
31 
32 #define CNANOVDB_GLOBAL
33 #define RESTRICT __restrict
34 
35 #endif
36 
37 
38 enum cnanovdb_GridType
39 {
40     cnanovdb_GridType_Unknown = 0,
41     cnanovdb_GridType_Float   = 1,
42     cnanovdb_GridType_Double  = 2,
43     cnanovdb_GridType_Int16   = 3,
44     cnanovdb_GridType_Int32   = 4,
45     cnanovdb_GridType_Int64   = 5,
46     cnanovdb_GridType_Vec3f   = 6,
47     cnanovdb_GridType_Vec3d   = 7,
48     cnanovdb_GridType_Mask    = 8,
49     cnanovdb_GridType_FP16    = 9,
50     cnanovdb_GridType_End     = 10
51 };
52 
53 #define ROOT_LEVEL 3
54 
55 #define DEFINEMASK_int(LOG2DIM, SIZE) \
56 typedef struct \
57 { \
58     uint64_t    mWords[SIZE >> 6]; \
59 } cnanovdb_mask##LOG2DIM; \
60 \
61 static void cnanovdb_mask##LOG2DIM##_clear(CNANOVDB_GLOBAL cnanovdb_mask##LOG2DIM *RESTRICT mask) \
62 { for (uint32_t i = 0; i < (SIZE >> 6); i++) mask->mWords[i] = 0; } \
63 \
64 static bool cnanovdb_mask##LOG2DIM##_isOn(const CNANOVDB_GLOBAL cnanovdb_mask##LOG2DIM *RESTRICT mask, uint32_t n) \
65 { return 0 != (mask->mWords[n >> 6] & (((uint64_t)(1)) << (n & 63))); } \
66 /**/
67 
68 #define DEFINEMASK(LOG2DIM) \
69     DEFINEMASK_int(LOG2DIM, (1U << (3*LOG2DIM)))
70 
71 #define INSTANTIATE(LOG2DIM) \
72     DEFINEMASK(LOG2DIM)
73 
74 INSTANTIATE(3)
75 INSTANTIATE(4)
76 INSTANTIATE(5)
77 
78 typedef struct
79 {
80     float  mMatF[9];    // r,c = 3*r + c
81     float  mInvMatF[9]; // r,c = 3*r + c
82     float  mVecF[3];
83     float  mTaperF;
84     double mMatD[9];    // r,c = 3*r + c
85     double mInvMatD[9]; // r,c = 3*r + c
86     double mVecD[3];
87     double mTaperD;
88 } cnanovdb_map;
89 
90 typedef struct
91 {
92     float       mVec[3];
93 } cnanovdb_Vec3F;
94 
95 typedef struct
96 {
97     int32_t     mVec[3];
98 } cnanovdb_coord;
99 
100 static int
cnanovdb_coord_compare(const CNANOVDB_GLOBAL cnanovdb_coord * a,const cnanovdb_coord * b)101 cnanovdb_coord_compare(const CNANOVDB_GLOBAL cnanovdb_coord *a, const cnanovdb_coord *b)
102 {
103     if (a->mVec[0] < b->mVec[0])
104         return -1;
105     if (a->mVec[0] > b->mVec[0])
106         return 1;
107     if (a->mVec[1] < b->mVec[1])
108         return -1;
109     if (a->mVec[1] > b->mVec[1])
110         return 1;
111     if (a->mVec[2] < b->mVec[2])
112         return -1;
113     if (a->mVec[2] > b->mVec[2])
114         return 1;
115     return 0;
116 }
117 
118 #ifdef USE_SINGLE_ROOT_KEY
119 static uint64_t
cnanovdb_coord_to_key(const cnanovdb_coord * RESTRICT ijk)120 cnanovdb_coord_to_key(const cnanovdb_coord *RESTRICT ijk)
121 {
122     // Define to workaround a bug with 64-bit shifts in the AMD OpenCL compiler.
123 #if defined(AVOID_64BIT_SHIFT)
124     uint2 key = (uint2)( ((uint32_t)ijk->mVec[2]) >> 12, 0) |
125                 (uint2)((((uint32_t)ijk->mVec[1]) >> 12) << 21,
126                          ((uint32_t)ijk->mVec[1]) >> 23) |
127              (uint2)(0, (((uint32_t)ijk->mVec[0]) >> 12) << 10);
128     return *(uint64_t *)&key;
129 #else
130     return  ((uint64_t) (((uint32_t)ijk->mVec[2]) >> 12)) |
131            (((uint64_t) (((uint32_t)ijk->mVec[1]) >> 12)) << 21) |
132            (((uint64_t) (((uint32_t)ijk->mVec[0]) >> 12)) << 42);
133 #endif
134 }
135 #else
136 static void
cnanovdb_coord_to_key(cnanovdb_coord * RESTRICT key,const cnanovdb_coord * RESTRICT ijk)137 cnanovdb_coord_to_key(cnanovdb_coord *RESTRICT key, const cnanovdb_coord *RESTRICT ijk)
138 {
139     key->mVec[0] = ijk->mVec[0] & ~((1u << 12) - 1u);
140     key->mVec[1] = ijk->mVec[1] & ~((1u << 12) - 1u);
141     key->mVec[2] = ijk->mVec[2] & ~((1u << 12) - 1u);
142 }
143 #endif
144 
145 static void
cnanovdb_map_apply(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_map * RESTRICT map,const cnanovdb_Vec3F * src)146 cnanovdb_map_apply(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_map *RESTRICT map, const cnanovdb_Vec3F *src)
147 {
148     float sx = src->mVec[0];
149     float sy = src->mVec[1];
150     float sz = src->mVec[2];
151     dst->mVec[0] = sx * map->mMatF[0] + sy * map->mMatF[1] + sz * map->mMatF[2] + map->mVecF[0];
152     dst->mVec[1] = sx * map->mMatF[3] + sy * map->mMatF[4] + sz * map->mMatF[5] + map->mVecF[1];
153     dst->mVec[2] = sx * map->mMatF[6] + sy * map->mMatF[7] + sz * map->mMatF[8] + map->mVecF[2];
154 }
155 
156 static void
cnanovdb_map_applyInverse(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_map * RESTRICT map,const cnanovdb_Vec3F * src)157 cnanovdb_map_applyInverse(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_map *RESTRICT map, const cnanovdb_Vec3F *src)
158 {
159     float sx = src->mVec[0] - map->mVecF[0];
160     float sy = src->mVec[1] - map->mVecF[1];
161     float sz = src->mVec[2] - map->mVecF[2];
162     dst->mVec[0] = sx * map->mInvMatF[0] + sy * map->mInvMatF[1] + sz * map->mInvMatF[2];
163     dst->mVec[1] = sx * map->mInvMatF[3] + sy * map->mInvMatF[4] + sz * map->mInvMatF[5];
164     dst->mVec[2] = sx * map->mInvMatF[6] + sy * map->mInvMatF[7] + sz * map->mInvMatF[8];
165 }
166 
167 static void
cnanovdb_map_applyJacobi(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_map * RESTRICT map,const cnanovdb_Vec3F * src)168 cnanovdb_map_applyJacobi(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_map *RESTRICT map, const cnanovdb_Vec3F *src)
169 {
170     float sx = src->mVec[0];
171     float sy = src->mVec[1];
172     float sz = src->mVec[2];
173     dst->mVec[0] = sx * map->mMatF[0] + sy * map->mMatF[1] + sz * map->mMatF[2];
174     dst->mVec[1] = sx * map->mMatF[3] + sy * map->mMatF[4] + sz * map->mMatF[5];
175     dst->mVec[2] = sx * map->mMatF[6] + sy * map->mMatF[7] + sz * map->mMatF[8];
176 }
177 
178 static void
cnanovdb_map_applyInverseJacobi(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_map * RESTRICT map,const cnanovdb_Vec3F * src)179 cnanovdb_map_applyInverseJacobi(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_map *RESTRICT map, const cnanovdb_Vec3F *src)
180 {
181     float sx = src->mVec[0];
182     float sy = src->mVec[1];
183     float sz = src->mVec[2];
184     dst->mVec[0] = sx * map->mInvMatF[0] + sy * map->mInvMatF[1] + sz * map->mInvMatF[2];
185     dst->mVec[1] = sx * map->mInvMatF[3] + sy * map->mInvMatF[4] + sz * map->mInvMatF[5];
186     dst->mVec[2] = sx * map->mInvMatF[6] + sy * map->mInvMatF[7] + sz * map->mInvMatF[8];
187 }
188 
189 static void
cnanovdb_map_applyIJT(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_map * RESTRICT map,const cnanovdb_Vec3F * src)190 cnanovdb_map_applyIJT(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_map *RESTRICT map, const cnanovdb_Vec3F *src)
191 {
192     float sx = src->mVec[0];
193     float sy = src->mVec[1];
194     float sz = src->mVec[2];
195     dst->mVec[0] = sx * map->mInvMatF[0] + sy * map->mInvMatF[3] + sz * map->mInvMatF[6];
196     dst->mVec[1] = sx * map->mInvMatF[1] + sy * map->mInvMatF[4] + sz * map->mInvMatF[7];
197     dst->mVec[2] = sx * map->mInvMatF[2] + sy * map->mInvMatF[5] + sz * map->mInvMatF[8];
198 }
199 
200 typedef struct
201 {
202     int64_t     mByteOffset;   // byte offset to the blind data, relative to the GridData.
203     uint64_t    mElementCount; // number of elements, e.g. point count
204     uint32_t    mFlags;        // flags
205     uint32_t    mSemantic;     // semantic meaning of the data.
206     uint32_t    mDataClass;    // 4 bytes
207     uint32_t    mDataType;     // 4 bytes
208     char        mName[256];
209     uint8_t     _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(int64_t)+sizeof(uint64_t)+2*sizeof(uint32_t)+2*sizeof(uint32_t)+256*sizeof(char), CNANOVDB_DATA_ALIGNMENT)];
210 } cnanovdb_gridblindmetadata;
211 
212 typedef struct
213 {
214     uint64_t         mMagic; // 8B magic to validate it is valid grid data.
215     uint64_t         mChecksum; // 8B. Checksum of grid buffer.
216     uint32_t         mVersion;// 4B. compacted major.minor.path version number.
217     uint32_t         mFlags; // 4B. flags for grid.
218     uint32_t         mGridIndex;// 4B. Index of this grid in the buffer
219     uint32_t         mGridCount; // 4B. Total number of grids in the buffer
220     uint64_t         mGridSize; // 8B. byte count of this entire grid occupied in the buffer.
221     char             mGridName[256]; // 256B
222     cnanovdb_map     mMap; // 264B. affine transformation between index and world space in both single and double precision
223     double           mBBox[6]; // 48B. floating-point bounds of active values in WORLD SPACE
224     double           mVoxelSize[3]; // 24B. size of a voxel in world units
225     uint32_t         mGridClass; // 4B.
226     uint32_t         mGridType; // 4B.
227     uint64_t         mBlindMetadataOffset; // 8B. offset of GridBlindMetaData structures.
228     int32_t          mBlindMetadataCount; // 4B. count of GridBlindMetaData structures.
229     uint32_t         _reserved[CNANOVDB_ALIGNMENT_PADDING(8 + 8 + 4 + 4 + 4 + 4 + 8 + 256 + 24 + 24 + sizeof(cnanovdb_map) + 24 + 4 + 4 + 8 + 4, CNANOVDB_DATA_ALIGNMENT) / 4];
230 } cnanovdb_griddata;
231 
232 static void
cnanovdb_griddata_worldToIndex(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid,const cnanovdb_Vec3F * src)233 cnanovdb_griddata_worldToIndex(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid, const cnanovdb_Vec3F *src)
234 {
235     cnanovdb_map_applyInverse(dst, &grid->mMap, src);
236 }
237 
238 static void
cnanovdb_griddata_indexToWorld(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid,const cnanovdb_Vec3F * src)239 cnanovdb_griddata_indexToWorld(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid, const cnanovdb_Vec3F *src)
240 {
241     cnanovdb_map_apply(dst, &grid->mMap, src);
242 }
243 
244 static void
cnanovdb_griddata_worldToIndexDir(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid,const cnanovdb_Vec3F * src)245 cnanovdb_griddata_worldToIndexDir(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid, const cnanovdb_Vec3F *src)
246 {
247     cnanovdb_map_applyInverseJacobi(dst, &grid->mMap, src);
248 }
249 
250 static void
cnanovdb_griddata_indexToWorldDir(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid,const cnanovdb_Vec3F * src)251 cnanovdb_griddata_indexToWorldDir(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid, const cnanovdb_Vec3F *src)
252 {
253     cnanovdb_map_applyJacobi(dst, &grid->mMap, src);
254 }
255 
256 static void
cnanovdb_griddata_applyIJT(cnanovdb_Vec3F * dst,const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid,const cnanovdb_Vec3F * src)257 cnanovdb_griddata_applyIJT(cnanovdb_Vec3F *dst, const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid, const cnanovdb_Vec3F *src)
258 {
259     cnanovdb_map_applyIJT(dst, &grid->mMap, src);
260 }
261 
262 typedef struct
263 {
264     uint64_t mNodeOffset[ROOT_LEVEL + 1];
265     uint32_t mNodeCount[ROOT_LEVEL];
266     uint32_t mTileCount[ROOT_LEVEL];
267     uint64_t mVoxelCount;
268     uint8_t  _reserved[CNANOVDB_ALIGNMENT_PADDING(4*sizeof(uint64_t)+(3+3)*sizeof(uint32_t)+sizeof(uint64_t), CNANOVDB_DATA_ALIGNMENT)];
269 } cnanovdb_treedata;
270 
271 static const CNANOVDB_GLOBAL cnanovdb_treedata *
cnanovdb_griddata_tree(const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT griddata)272 cnanovdb_griddata_tree(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT griddata)
273 {
274     return (const CNANOVDB_GLOBAL cnanovdb_treedata *)(griddata + 1);
275 }
276 
277 #define CREATE_TILEENTRY(VALUETYPE, SUFFIX) \
278 typedef union \
279 { \
280     VALUETYPE   value; \
281     uint64_t    child; \
282 } cnanovdb_tileentry##SUFFIX; \
283 /**/
284 
285 typedef struct
286 {
287     cnanovdb_coord              mKey;
288     const CNANOVDB_GLOBAL void *mNode[4];
289 } cnanovdb_readaccessor;
290 
291 
292 static void
cnanovdb_readaccessor_insert(cnanovdb_readaccessor * RESTRICT acc,int childlevel,const CNANOVDB_GLOBAL void * RESTRICT node,const cnanovdb_coord * RESTRICT ijk)293 cnanovdb_readaccessor_insert(cnanovdb_readaccessor *RESTRICT acc, int childlevel, const CNANOVDB_GLOBAL void *RESTRICT node, const cnanovdb_coord *RESTRICT ijk)
294 {
295     acc->mNode[childlevel] = node;
296     acc->mKey.mVec[0] = ijk->mVec[0];
297     acc->mKey.mVec[1] = ijk->mVec[1];
298     acc->mKey.mVec[2] = ijk->mVec[2];
299 }
300 
301 #define CREATE_LEAF_NODE_int(LEVEL, LOG2DIM, CHILDTOTAL, TOTAL, MASK, VALUETYPE, STATSTYPE, SUFFIX) \
302 typedef struct \
303 { \
304     cnanovdb_coord              mBBox_min; \
305     uint8_t                     mBBoxDif[3]; \
306     uint8_t                     mFlags; \
307     cnanovdb_mask##LOG2DIM      mValueMask; \
308     VALUETYPE                   mMinimum; \
309     VALUETYPE                   mMaximum; \
310     STATSTYPE                   mAverage; \
311     STATSTYPE                   mStdDevi; \
312     uint32_t                    _reserved[ CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_mask##LOG2DIM)+2*sizeof(VALUETYPE)+2*sizeof(STATSTYPE)+sizeof(cnanovdb_coord)+sizeof(uint8_t[3])+sizeof(uint8_t), CNANOVDB_DATA_ALIGNMENT)/4]; \
313     VALUETYPE                   mVoxels[1u << (3*LOG2DIM)]; \
314 } cnanovdb_node##LEVEL##SUFFIX; \
315 \
316 static uint32_t \
317 cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(const cnanovdb_coord *RESTRICT ijk) \
318 { \
319     return ( ( ( ijk->mVec[0] & MASK ) >> CHILDTOTAL ) << ( 2 * LOG2DIM ) ) + \
320            ( ( ( ijk->mVec[1] & MASK ) >> CHILDTOTAL ) << ( LOG2DIM ) ) + \
321              ( ( ijk->mVec[2] & MASK ) >> CHILDTOTAL ); \
322 } \
323 \
324 static VALUETYPE \
325 cnanovdb_node##LEVEL##SUFFIX##_getValue(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
326 { \
327     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
328     return node->mVoxels[n]; \
329 } \
330 \
331 static VALUETYPE \
332 cnanovdb_node##LEVEL##SUFFIX##_getValueAndCache(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, cnanovdb_readaccessor *RESTRICT /* DO NOT REMOVE: Required for C99 compliance */ acc) \
333 { \
334     (void)(acc); \
335     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
336     return node->mVoxels[n]; \
337 } \
338 \
339 static bool \
340 cnanovdb_node##LEVEL##SUFFIX##_isActive(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
341 { \
342     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
343     if (cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n)) \
344         return true; \
345     return false; \
346 } \
347 \
348 static bool \
349 cnanovdb_node##LEVEL##SUFFIX##_isActiveAndCache(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, cnanovdb_readaccessor *RESTRICT /* DO NOT REMOVE: Required for C99 compliance */ acc) \
350 { \
351     (void)(acc); \
352     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
353     if (cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n)) \
354         return true; \
355     return false; \
356 } \
357 \
358 static const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX * \
359 cnanovdb_tree_getNode##LEVEL##SUFFIX(const CNANOVDB_GLOBAL cnanovdb_treedata *RESTRICT tree, uint64_t i) \
360 { \
361     const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *basenode = (const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *)((CNANOVDB_GLOBAL uint8_t *)(tree) + tree->mNodeOffset[LEVEL]); \
362     return basenode + i; \
363 } \
364 \
365 /**/
366 
367 #define CREATE_LEAF_NODE(LEVEL, LOG2DIM, TOTAL, VALUETYPE, STATSTYPE, SUFFIX) \
368 CREATE_LEAF_NODE_int(LEVEL, LOG2DIM, (TOTAL-LOG2DIM), TOTAL, ((1u << TOTAL) - 1u), VALUETYPE, STATSTYPE, SUFFIX)
369 
370 #define CREATE_INTERNAL_NODE_int(CHILDLEVEL, LEVEL, LOG2DIM, CHILDTOTAL, TOTAL, MASK, VALUETYPE, STATSTYPE, SUFFIX) \
371 typedef struct \
372 { \
373     cnanovdb_coord               mBBox_min, mBBox_max; \
374     int32_t                      mOffset; \
375     uint32_t                     mFlags; \
376     cnanovdb_mask##LOG2DIM       mValueMask, mChildMask; \
377     VALUETYPE                    mMinimum, mMaximum; \
378     STATSTYPE                    mAverage, mStdDevi; \
379     uint8_t                      _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_mask##LOG2DIM)+sizeof(VALUETYPE)*2+sizeof(STATSTYPE)*2+sizeof(cnanovdb_coord)*2+sizeof(int32_t)+sizeof(uint32_t), CNANOVDB_DATA_ALIGNMENT)]; \
380     cnanovdb_tileentry##SUFFIX   mTable[1u << (3*LOG2DIM)]; \
381 } cnanovdb_node##LEVEL##SUFFIX; \
382 \
383 static uint32_t \
384 cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(const cnanovdb_coord *RESTRICT ijk) \
385 { \
386     return ( ( ( ijk->mVec[0] & MASK ) >> CHILDTOTAL ) << ( 2 * LOG2DIM ) ) + \
387            ( ( ( ijk->mVec[1] & MASK ) >> CHILDTOTAL ) << ( LOG2DIM ) ) + \
388              ( ( ijk->mVec[2] & MASK ) >> CHILDTOTAL ); \
389 } \
390 \
391 static const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX * \
392 cnanovdb_node##LEVEL##SUFFIX##_getChild(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, uint32_t n) \
393 { \
394     const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *childnode = (const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *)( ((CNANOVDB_GLOBAL uint8_t *)node) + node->mTable[n].child); \
395     return childnode; \
396 } \
397 \
398 static VALUETYPE \
399 cnanovdb_node##LEVEL##SUFFIX##_getValue(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
400 { \
401     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
402     if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n)) \
403     { \
404         const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *child = cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n); \
405         return cnanovdb_node##CHILDLEVEL##SUFFIX##_getValue(child, ijk); \
406     } \
407     return node->mTable[n].value; \
408 } \
409 \
410 static VALUETYPE \
411 cnanovdb_node##LEVEL##SUFFIX##_getValueAndCache(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, cnanovdb_readaccessor *RESTRICT acc) \
412 { \
413     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
414     if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n)) \
415     { \
416         const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *child = cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n); \
417         cnanovdb_readaccessor_insert(acc, CHILDLEVEL, child, ijk); \
418         return cnanovdb_node##CHILDLEVEL##SUFFIX##_getValueAndCache(child, ijk, acc); \
419     } \
420     return node->mTable[n].value; \
421 } \
422 \
423 static bool \
424 cnanovdb_node##LEVEL##SUFFIX##_isActive(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk) \
425 { \
426     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
427     if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n)) \
428     { \
429         const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *child = cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n); \
430         return cnanovdb_node##CHILDLEVEL##SUFFIX##_isActive(child, ijk); \
431     } \
432     return cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n) ? true : false; \
433 } \
434 \
435 static bool \
436 cnanovdb_node##LEVEL##SUFFIX##_isActiveAndCache(const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *RESTRICT node, const cnanovdb_coord *RESTRICT ijk, cnanovdb_readaccessor *RESTRICT acc) \
437 { \
438     uint32_t n = cnanovdb_node##LEVEL##SUFFIX##_CoordToOffset(ijk); \
439     if (cnanovdb_mask##LOG2DIM##_isOn(&node->mChildMask, n)) \
440     { \
441         const CNANOVDB_GLOBAL cnanovdb_node##CHILDLEVEL##SUFFIX *child = cnanovdb_node##LEVEL##SUFFIX##_getChild(node, n); \
442         cnanovdb_readaccessor_insert(acc, CHILDLEVEL, child, ijk); \
443         return cnanovdb_node##CHILDLEVEL##SUFFIX##_isActiveAndCache(child, ijk, acc); \
444     } \
445     return cnanovdb_mask##LOG2DIM##_isOn(&node->mValueMask, n) ? true : false; \
446 } \
447 \
448 static const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX * \
449 cnanovdb_tree_getNode##LEVEL##SUFFIX(const CNANOVDB_GLOBAL cnanovdb_treedata *RESTRICT tree, uint64_t i) \
450 { \
451     const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *basenode = (const CNANOVDB_GLOBAL cnanovdb_node##LEVEL##SUFFIX *)((CNANOVDB_GLOBAL uint8_t *)(tree) + tree->mNodeOffset[LEVEL]); \
452     return basenode + i; \
453 } \
454 \
455 /**/
456 
457 #define CREATE_INTERNAL_NODE(CHILDLEVEL, LEVEL, LOG2DIM, TOTAL, VALUETYPE, STATSTYPE, SUFFIX) \
458 CREATE_INTERNAL_NODE_int(CHILDLEVEL, LEVEL, LOG2DIM, (TOTAL-LOG2DIM), TOTAL, ((1u << TOTAL) - 1u), VALUETYPE, STATSTYPE, SUFFIX)
459 
460 
461 #ifdef USE_SINGLE_ROOT_KEY
462 #define DEFINE_KEY(KEY) \
463         uint64_t        KEY;
464 #define KEYSIZE sizeof(uint64_t)
465 
466 #define KEYSEARCH(SUFFIX) \
467     uint64_t                key; \
468     key = cnanovdb_coord_to_key(ijk); \
469 \
470     for (int i = low; i < high; i++) \
471     { \
472         const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX   *tile = tiles + i; \
473         if (tile->key == key) \
474             return tile; \
475     } \
476 /**/
477 #else
478 #define DEFINE_KEY(KEY) \
479         cnanovdb_coord   KEY;
480 #define KEYSIZE sizeof(cnanovdb_coord)
481 #define KEYSEARCH(SUFFIX) \
482     cnanovdb_coord key; \
483     cnanovdb_coord_to_key(&key, ijk); \
484  \
485     while (low != high) \
486     { \
487         int32_t mid = low + (( high - low ) >> 1 ); \
488         const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX   *tile = tiles + mid; \
489  \
490         int             keycmp = cnanovdb_coord_compare(&tile->key, &key); \
491         if (keycmp == 0) \
492         { \
493             return tile; \
494         } \
495  \
496         if (keycmp < 0) \
497             low = mid + 1; \
498         else \
499             high = mid; \
500     } \
501 /**/
502 #endif
503 
504 
505 #define CREATE_ROOTDATA(VALUETYPE, STATSTYPE, SUFFIX) \
506 typedef struct \
507 { \
508     DEFINE_KEY(key); \
509     int64_t             child; \
510     uint32_t            state; \
511     VALUETYPE           value; \
512     uint8_t             _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(KEYSIZE)+sizeof(VALUETYPE)+sizeof(int64_t)+sizeof(uint32_t), CNANOVDB_DATA_ALIGNMENT)]; \
513 } cnanovdb_rootdata_tile##SUFFIX; \
514  \
515 typedef struct \
516 { \
517     cnanovdb_coord mBBox_min, mBBox_max; \
518     uint32_t       mTableSize; \
519     VALUETYPE      mBackground; \
520     VALUETYPE      mMinimum, mMaximum; \
521     STATSTYPE      mAverage, mStdDevi; \
522     uint32_t       _reserved[CNANOVDB_ALIGNMENT_PADDING(sizeof(cnanovdb_coord)*2+sizeof(uint32_t)+sizeof(VALUETYPE)*3+sizeof(STATSTYPE)*2, CNANOVDB_DATA_ALIGNMENT)/4]; \
523 } cnanovdb_rootdata##SUFFIX; \
524  \
525 static const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX * \
526 cnanovdb_treedata_root##SUFFIX(const CNANOVDB_GLOBAL cnanovdb_treedata *RESTRICT treedata) \
527 { \
528     return (const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *) ((const CNANOVDB_GLOBAL uint8_t *)(treedata) + treedata->mNodeOffset[ROOT_LEVEL]); \
529 } \
530  \
531 static const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX * \
532 cnanovdb_rootdata##SUFFIX##_getTile(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, uint32_t n) \
533 { \
534     const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *basetile = (const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *) (rootdata + 1); \
535     return basetile + n; \
536 } \
537  \
538 static const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX * \
539 cnanovdb_rootdata##SUFFIX##_getChild(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *RESTRICT tile) \
540 { \
541     CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *basenode = (CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) (((CNANOVDB_GLOBAL uint8_t *) rootdata) + tile->child); \
542     return basenode; \
543 } \
544  \
545 static const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX * \
546 cnanovdb_rootdata##SUFFIX##_findTile(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk) \
547 { \
548     int32_t                      low = 0, high = rootdata->mTableSize; \
549     const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tiles = cnanovdb_rootdata##SUFFIX##_getTile(rootdata, 0); \
550  \
551     KEYSEARCH(SUFFIX) \
552     return 0; \
553 } \
554  \
555 static VALUETYPE \
556 cnanovdb_rootdata##SUFFIX##_getValue(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk) \
557 { \
558     const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk); \
559     if (!tile) \
560         return rootdata->mBackground; \
561     if (tile->child == 0) \
562         return tile->value; \
563     return cnanovdb_node2##SUFFIX##_getValue( cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile), ijk ); \
564 } \
565  \
566 static VALUETYPE \
567 cnanovdb_rootdata##SUFFIX##_getValueAndCache(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk, cnanovdb_readaccessor *RESTRICT acc) \
568 { \
569     const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk); \
570     if (!tile) \
571         return rootdata->mBackground; \
572     if (tile->child == 0) \
573         return tile->value; \
574     const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *child = cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile); \
575     cnanovdb_readaccessor_insert(acc, 2, child, ijk); \
576     return cnanovdb_node2##SUFFIX##_getValueAndCache( child, ijk, acc ); \
577 } \
578 \
579 static bool \
580 cnanovdb_rootdata##SUFFIX##_isActive(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk) \
581 { \
582     const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk); \
583     if (!tile) \
584         return false; \
585     if (tile->child == 0) \
586         return tile->state; \
587     return cnanovdb_node2##SUFFIX##_isActive( cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile), ijk ); \
588 } \
589  \
590 static bool \
591 cnanovdb_rootdata##SUFFIX##_isActiveAndCache(const CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *RESTRICT rootdata, const cnanovdb_coord *RESTRICT ijk, cnanovdb_readaccessor *RESTRICT acc) \
592 { \
593     const CNANOVDB_GLOBAL cnanovdb_rootdata_tile##SUFFIX *tile = cnanovdb_rootdata##SUFFIX##_findTile(rootdata, ijk); \
594     if (!tile) \
595         return false; \
596     if (tile->child == 0) \
597         return tile->state; \
598     const CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *child = cnanovdb_rootdata##SUFFIX##_getChild(rootdata, tile); \
599     cnanovdb_readaccessor_insert(acc, 2, child, ijk); \
600     return cnanovdb_node2##SUFFIX##_isActiveAndCache( child, ijk, acc ); \
601 } \
602 /**/
603 
604 
605 inline void
cnanovdb_readaccessor_init(cnanovdb_readaccessor * RESTRICT acc,const CNANOVDB_GLOBAL void * RESTRICT rootdata)606 cnanovdb_readaccessor_init(cnanovdb_readaccessor *RESTRICT acc,
607                     const CNANOVDB_GLOBAL void /*cnanovdb_rootdata* */ *RESTRICT rootdata)
608 {
609     acc->mNode[0] = acc->mNode[1] = acc->mNode[2] = 0;
610     acc->mNode[3] = rootdata;
611 }
612 
613 #define DEFINE_ISCACHED(LEVEL, MASK) \
614 inline bool \
615 cnanovdb_readaccessor_isCached##LEVEL(cnanovdb_readaccessor *RESTRICT acc, int32_t dirty) \
616 { \
617     if (!acc->mNode[LEVEL]) \
618         return false; \
619     if (dirty & ~MASK) \
620     { \
621         acc->mNode[LEVEL] = 0; \
622         return false; \
623     } \
624     return true; \
625 } \
626 /**/
627 
628 DEFINE_ISCACHED(0, ((1u <<  3) - 1u) )
629 DEFINE_ISCACHED(1, ((1u <<  7) - 1u) )
630 DEFINE_ISCACHED(2, ((1u << 12) - 1u) )
631 
632 inline int32_t
cnanovdb_readaccessor_computeDirty(const cnanovdb_readaccessor * RESTRICT acc,const cnanovdb_coord * RESTRICT ijk)633 cnanovdb_readaccessor_computeDirty(const cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk)
634 {
635     return (ijk->mVec[0] ^ acc->mKey.mVec[0]) |
636            (ijk->mVec[1] ^ acc->mKey.mVec[1]) |
637            (ijk->mVec[2] ^ acc->mKey.mVec[2]);
638 }
639 
640 #define CREATE_ACCESSOR(VALUETYPE, SUFFIX) \
641 inline VALUETYPE \
642 cnanovdb_readaccessor_getValue##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk) \
643 { \
644     int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk); \
645  \
646     if (cnanovdb_readaccessor_isCached0(acc, dirty)) \
647         return cnanovdb_node0##SUFFIX##_getValue( ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk); \
648     if (cnanovdb_readaccessor_isCached1(acc, dirty)) \
649         return cnanovdb_node1##SUFFIX##_getValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
650     if (cnanovdb_readaccessor_isCached2(acc, dirty)) \
651         return cnanovdb_node2##SUFFIX##_getValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
652  \
653     return cnanovdb_rootdata##SUFFIX##_getValueAndCache( ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
654 } \
655 \
656 inline bool \
657 cnanovdb_readaccessor_isActive##SUFFIX(cnanovdb_readaccessor *RESTRICT acc, const cnanovdb_coord *RESTRICT ijk) \
658 { \
659     int32_t dirty = cnanovdb_readaccessor_computeDirty(acc, ijk); \
660  \
661     if (cnanovdb_readaccessor_isCached0(acc, dirty)) \
662         return cnanovdb_node0##SUFFIX##_isActive( ((CNANOVDB_GLOBAL cnanovdb_node0##SUFFIX *) acc->mNode[0]), ijk); \
663     if (cnanovdb_readaccessor_isCached1(acc, dirty)) \
664         return cnanovdb_node1##SUFFIX##_isActiveAndCache( ((CNANOVDB_GLOBAL cnanovdb_node1##SUFFIX *) acc->mNode[1]), ijk, acc); \
665     if (cnanovdb_readaccessor_isCached2(acc, dirty)) \
666         return cnanovdb_node2##SUFFIX##_isActiveAndCache( ((CNANOVDB_GLOBAL cnanovdb_node2##SUFFIX *) acc->mNode[2]), ijk, acc); \
667  \
668     return cnanovdb_rootdata##SUFFIX##_isActiveAndCache( ((CNANOVDB_GLOBAL cnanovdb_rootdata##SUFFIX *)acc->mNode[3]), ijk, acc); \
669 } \
670 /**/
671 
672 
673 #define CREATE_GRIDTYPE(VALUETYPE, STATSTYPE, SUFFIX) \
674 CREATE_TILEENTRY(VALUETYPE, SUFFIX) \
675 CREATE_LEAF_NODE(0, 3, 3, VALUETYPE, STATSTYPE, SUFFIX) \
676 CREATE_INTERNAL_NODE(0, 1, 4, 7, VALUETYPE, STATSTYPE, SUFFIX) \
677 CREATE_INTERNAL_NODE(1, 2, 5, 12, VALUETYPE, STATSTYPE, SUFFIX) \
678 CREATE_ROOTDATA(VALUETYPE, STATSTYPE, SUFFIX) \
679 CREATE_ACCESSOR(VALUETYPE, SUFFIX) \
680 /**/
681 
CREATE_GRIDTYPE(float,float,F)682 CREATE_GRIDTYPE(float, float, F)
683 CREATE_GRIDTYPE(cnanovdb_Vec3F, float, F3)
684 
685 static int
686 cnanovdb_griddata_valid(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid)
687 {
688     if (!grid)
689         return 0;
690     if (grid->mMagic != 0x304244566f6e614eUL)
691         return 0;
692     return 1;
693 }
694 
695 static int
cnanovdb_griddata_validF(const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid)696 cnanovdb_griddata_validF(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid)
697 {
698     if (!cnanovdb_griddata_valid(grid))
699         return 0;
700     if (grid->mGridType != cnanovdb_GridType_Float)
701         return 0;
702     return 1;
703 }
704 
705 static int
cnanovdb_griddata_validF3(const CNANOVDB_GLOBAL cnanovdb_griddata * RESTRICT grid)706 cnanovdb_griddata_validF3(const CNANOVDB_GLOBAL cnanovdb_griddata *RESTRICT grid)
707 {
708     if (!cnanovdb_griddata_valid(grid))
709         return 0;
710     if (grid->mGridType != cnanovdb_GridType_Vec3f)
711         return 0;
712     return 1;
713 }
714 
715 #endif
716