
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0

/*!
    \file   PNanoVDB.h

    \author Andrew Reidmeyer

    \brief  This file is a portable (e.g. pointer-less) C99/GLSL/HLSL port 
            of NanoVDB.h, which is compatible with most graphics APIs.
*/

#ifndef NANOVDB_PNANOVDB_H_HAS_BEEN_INCLUDED
#define NANOVDB_PNANOVDB_H_HAS_BEEN_INCLUDED

// ------------------------------------------------ Configuration -----------------------------------------------------------

// platforms
//#define PNANOVDB_C
//#define PNANOVDB_HLSL
//#define PNANOVDB_GLSL

// addressing mode
// PNANOVDB_ADDRESS_32
// PNANOVDB_ADDRESS_64
#if defined(PNANOVDB_C)
#ifndef PNANOVDB_ADDRESS_32
#define PNANOVDB_ADDRESS_64
#endif
#elif defined(PNANOVDB_HLSL)
#ifndef PNANOVDB_ADDRESS_64
#define PNANOVDB_ADDRESS_32
#endif
#elif defined(PNANOVDB_GLSL)
#ifndef PNANOVDB_ADDRESS_64
#define PNANOVDB_ADDRESS_32
#endif
#endif

// bounds checking
//#define PNANOVDB_BUF_BOUNDS_CHECK

// enable HDDA by default on HLSL/GLSL, make explicit on C
#if defined(PNANOVDB_C)
//#define PNANOVDB_HDDA
#ifdef PNANOVDB_HDDA
#ifndef PNANOVDB_CMATH
#define PNANOVDB_CMATH
#endif
#endif
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_HDDA
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_HDDA
#endif

#ifdef PNANOVDB_CMATH
#include <math.h>
#endif

// ------------------------------------------------ Buffer -----------------------------------------------------------

#if defined(PNANOVDB_BUF_CUSTOM)
// NOP
#elif defined(PNANOVDB_C)
#define PNANOVDB_BUF_C
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_BUF_HLSL
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_BUF_GLSL
#endif

#if defined(PNANOVDB_BUF_C)
#include <stdint.h>
#if defined(_WIN32)
#define PNANOVDB_BUF_FORCE_INLINE static inline __forceinline
#else
#define PNANOVDB_BUF_FORCE_INLINE static inline __attribute__((always_inline))
#endif
typedef struct pnanovdb_buf_t
{
    uint32_t* data;
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
    uint64_t size_in_words;
#endif
}pnanovdb_buf_t;
PNANOVDB_BUF_FORCE_INLINE pnanovdb_buf_t pnanovdb_make_buf(uint32_t* data, uint64_t size_in_words)
{
    pnanovdb_buf_t ret;
    ret.data = data;
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
    ret.size_in_words = size_in_words;
#endif
    return ret;
}
#if defined(PNANOVDB_ADDRESS_32)
PNANOVDB_BUF_FORCE_INLINE uint32_t pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint32_t byte_offset)
{
    uint32_t wordaddress = (byte_offset >> 2u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
    return wordaddress < buf.size_in_words ? buf.data[wordaddress] : 0u;
#else
    return buf.data[wordaddress];
#endif
}
PNANOVDB_BUF_FORCE_INLINE uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint32_t byte_offset)
{
    uint64_t* data64 = (uint64_t*)buf.data;
    uint32_t wordaddress64 = (byte_offset >> 3u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
    uint64_t size_in_words64 = buf.size_in_words >> 1u;
    return wordaddress64 < size_in_words64 ? data64[wordaddress64] : 0llu;
#else
    return data64[wordaddress64];
#endif
}
#elif defined(PNANOVDB_ADDRESS_64)
PNANOVDB_BUF_FORCE_INLINE uint32_t pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint64_t byte_offset)
{
    uint64_t wordaddress = (byte_offset >> 2u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
    return wordaddress < buf.size_in_words ? buf.data[wordaddress] : 0u;
#else
    return buf.data[wordaddress];
#endif
}
PNANOVDB_BUF_FORCE_INLINE uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint64_t byte_offset)
{
    uint64_t* data64 = (uint64_t*)buf.data;
    uint64_t wordaddress64 = (byte_offset >> 3u);
#ifdef PNANOVDB_BUF_BOUNDS_CHECK
    uint64_t size_in_words64 = buf.size_in_words >> 1u;
    return wordaddress64 < size_in_words64 ? data64[wordaddress64] : 0llu;
#else
    return data64[wordaddress64];
#endif
}
#endif
typedef uint32_t pnanovdb_grid_type_t;
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
#elif defined(PNANOVDB_BUF_HLSL)
#if defined(PNANOVDB_ADDRESS_32)
#define pnanovdb_buf_t StructuredBuffer<uint>
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint byte_offset)
{
    return buf[(byte_offset >> 2u)];
}
uint2 pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint byte_offset)
{
    uint2 ret;
    ret.x = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
    ret.y = pnanovdb_buf_read_uint32(buf, byte_offset + 4u);
    return ret;
}
#elif defined(PNANOVDB_ADDRESS_64)
#define pnanovdb_buf_t StructuredBuffer<uint>
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint64_t byte_offset)
{
    return buf[uint(byte_offset >> 2u)];
}
uint64_t pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint64_t byte_offset)
{
    uint64_t ret;
    ret = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
    ret = ret + (uint64_t(pnanovdb_buf_read_uint32(buf, byte_offset + 4u)) << 32u);
    return ret;
}
#endif
#define pnanovdb_grid_type_t uint
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
#elif defined(PNANOVDB_BUF_GLSL)
struct pnanovdb_buf_t
{
    uint unused;    // to satisfy min struct size?
};
uint pnanovdb_buf_read_uint32(pnanovdb_buf_t buf, uint byte_offset)
{
    return pnanovdb_buf_data[(byte_offset >> 2u)];
}
uvec2 pnanovdb_buf_read_uint64(pnanovdb_buf_t buf, uint byte_offset)
{
    uvec2 ret;
    ret.x = pnanovdb_buf_read_uint32(buf, byte_offset + 0u);
    ret.y = pnanovdb_buf_read_uint32(buf, byte_offset + 4u);
    return ret;
}
#define pnanovdb_grid_type_t uint
#define PNANOVDB_GRID_TYPE_GET(grid_typeIn, nameIn) pnanovdb_grid_type_constants[grid_typeIn].nameIn
#endif

// ------------------------------------------------ Basic Types -----------------------------------------------------------

// force inline
#if defined(PNANOVDB_C)
#if defined(_WIN32)
#define PNANOVDB_FORCE_INLINE static inline __forceinline
#else
#define PNANOVDB_FORCE_INLINE static inline __attribute__((always_inline))
#endif
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_FORCE_INLINE
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_FORCE_INLINE
#endif

// struct typedef, static const, inout
#if defined(PNANOVDB_C)
#define PNANOVDB_STRUCT_TYPEDEF(X) typedef struct X X;
#define PNANOVDB_STATIC_CONST static const
#define PNANOVDB_INOUT(X) X*
#define PNANOVDB_IN(X) const X*
#define PNANOVDB_DEREF(X) (*X)
#define PNANOVDB_REF(X) &X
#elif defined(PNANOVDB_HLSL)
#define PNANOVDB_STRUCT_TYPEDEF(X)
#define PNANOVDB_STATIC_CONST static const
#define PNANOVDB_INOUT(X) inout X
#define PNANOVDB_IN(X) X
#define PNANOVDB_DEREF(X) X
#define PNANOVDB_REF(X) X
#elif defined(PNANOVDB_GLSL)
#define PNANOVDB_STRUCT_TYPEDEF(X)
#define PNANOVDB_STATIC_CONST const
#define PNANOVDB_INOUT(X) inout X
#define PNANOVDB_IN(X) X
#define PNANOVDB_DEREF(X) X
#define PNANOVDB_REF(X) X
#endif

// basic types, type conversion
#if defined(PNANOVDB_C)
#define PNANOVDB_NATIVE_64
#include <stdint.h>
#if !defined(PNANOVDB_MEMCPY_CUSTOM)
#include <string.h>
#define pnanovdb_memcpy memcpy
#endif
typedef uint32_t pnanovdb_uint32_t;
typedef int32_t pnanovdb_int32_t;
typedef int32_t pnanovdb_bool_t;
#define PNANOVDB_FALSE 0
#define PNANOVDB_TRUE 1
typedef uint64_t pnanovdb_uint64_t;
typedef int64_t pnanovdb_int64_t;
typedef struct pnanovdb_coord_t
{
    pnanovdb_int32_t x, y, z;
}pnanovdb_coord_t;
typedef struct pnanovdb_vec3_t
{
    float x, y, z;
}pnanovdb_vec3_t;
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return (pnanovdb_int32_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return (pnanovdb_int64_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return (pnanovdb_uint64_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return (pnanovdb_uint32_t)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { float vf; pnanovdb_memcpy(&vf, &v, sizeof(vf)); return vf; }
PNANOVDB_FORCE_INLINE double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { double vf; pnanovdb_memcpy(&vf, &v, sizeof(vf)); return vf; }
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return (pnanovdb_uint32_t)v; }
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return (pnanovdb_uint32_t)(v >> 32u); }
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return ((pnanovdb_uint64_t)x) | (((pnanovdb_uint64_t)y) << 32u); }
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return ((pnanovdb_uint64_t)x); }
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return a == b; }
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a == 0; }
#ifdef PNANOVDB_CMATH
PNANOVDB_FORCE_INLINE float pnanovdb_floor(float v) { return floorf(v); }
#endif
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_float_to_int32(float v) { return (pnanovdb_int32_t)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_int32_to_float(pnanovdb_int32_t v) { return (float)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_uint32_to_float(pnanovdb_uint32_t v) { return (float)v; }
PNANOVDB_FORCE_INLINE float pnanovdb_min(float a, float b) { return a < b ? a : b; }
PNANOVDB_FORCE_INLINE float pnanovdb_max(float a, float b) { return a > b ? a : b; }
#elif defined(PNANOVDB_HLSL)
typedef uint pnanovdb_uint32_t;
typedef int pnanovdb_int32_t;
typedef bool pnanovdb_bool_t;
#define PNANOVDB_FALSE false
#define PNANOVDB_TRUE true
typedef int3 pnanovdb_coord_t;
typedef float3 pnanovdb_vec3_t;
pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return int(v); }
pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return uint(v); }
float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { return asfloat(v); }
float pnanovdb_floor(float v) { return floor(v); }
pnanovdb_int32_t pnanovdb_float_to_int32(float v) { return int(v); }
float pnanovdb_int32_to_float(pnanovdb_int32_t v) { return float(v); }
float pnanovdb_uint32_to_float(pnanovdb_uint32_t v) { return float(v); }
float pnanovdb_min(float a, float b) { return min(a, b); }
float pnanovdb_max(float a, float b) { return max(a, b); }
#if defined(PNANOVDB_ADDRESS_32)
typedef uint2 pnanovdb_uint64_t;
typedef int2 pnanovdb_int64_t;
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return int2(v); }
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uint2(v); }
double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { return asdouble(v.x, v.y); }
pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return v.x; }
pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return v.y; }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return uint2(x, y); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return uint2(x, 0); }
bool pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return (a.x == b.x) && (a.y == b.y); }
bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a.x == 0 && a.y == 0; }
#else
typedef uint64_t pnanovdb_uint64_t;
typedef int64_t pnanovdb_int64_t;
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return int64_t(v); }
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uint64_t(v); }
double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { return asdouble(uint(v), uint(v >> 32u)); }
pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return uint(v); }
pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return uint(v >> 32u); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return uint64_t(x) + (uint64_t(y) << 32u); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return uint64_t(x); }
bool pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return a == b; }
bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a == 0; }
#endif
#elif defined(PNANOVDB_GLSL)
#define pnanovdb_uint32_t uint
#define pnanovdb_int32_t int
#define pnanovdb_bool_t bool
#define PNANOVDB_FALSE false
#define PNANOVDB_TRUE true
#define pnanovdb_uint64_t uvec2
#define pnanovdb_int64_t ivec2
#define pnanovdb_coord_t ivec3
#define pnanovdb_vec3_t vec3
pnanovdb_int32_t pnanovdb_uint32_as_int32(pnanovdb_uint32_t v) { return int(v); }
pnanovdb_int64_t pnanovdb_uint64_as_int64(pnanovdb_uint64_t v) { return ivec2(v); }
pnanovdb_uint64_t pnanovdb_int64_as_uint64(pnanovdb_int64_t v) { return uvec2(v); }
pnanovdb_uint32_t pnanovdb_int32_as_uint32(pnanovdb_int32_t v) { return uint(v); }
float pnanovdb_uint32_as_float(pnanovdb_uint32_t v) { return uintBitsToFloat(v); }
double pnanovdb_uint64_as_double(pnanovdb_uint64_t v) { return packDouble2x32(uvec2(v.x, v.y)); }
pnanovdb_uint32_t pnanovdb_uint64_low(pnanovdb_uint64_t v) { return v.x; }
pnanovdb_uint32_t pnanovdb_uint64_high(pnanovdb_uint64_t v) { return v.y; }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64(pnanovdb_uint32_t x, pnanovdb_uint32_t y) { return uvec2(x, y); }
pnanovdb_uint64_t pnanovdb_uint32_as_uint64_low(pnanovdb_uint32_t x) { return uvec2(x, 0); }
bool pnanovdb_uint64_is_equal(pnanovdb_uint64_t a, pnanovdb_uint64_t b) { return (a.x == b.x) && (a.y == b.y); }
bool pnanovdb_int64_is_zero(pnanovdb_int64_t a) { return a.x == 0 && a.y == 0; }
float pnanovdb_floor(float v) { return floor(v); }
pnanovdb_int32_t pnanovdb_float_to_int32(float v) { return int(v); }
float pnanovdb_int32_to_float(pnanovdb_int32_t v) { return float(v); }
float pnanovdb_uint32_to_float(pnanovdb_uint32_t v) { return float(v); }
float pnanovdb_min(float a, float b) { return min(a, b); }
float pnanovdb_max(float a, float b) { return max(a, b); }
#endif

// ------------------------------------------------ Coord/Vec3 Utilties -----------------------------------------------------------

#if defined(PNANOVDB_C)
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_uniform(float a)
{
    pnanovdb_vec3_t v;
    v.x = a;
    v.y = a;
    v.z = a;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_add(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
    pnanovdb_vec3_t v;
    v.x = a.x + b.x;
    v.y = a.y + b.y;
    v.z = a.z + b.z;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_sub(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
    pnanovdb_vec3_t v;
    v.x = a.x - b.x;
    v.y = a.y - b.y;
    v.z = a.z - b.z;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_mul(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
    pnanovdb_vec3_t v;
    v.x = a.x * b.x;
    v.y = a.y * b.y;
    v.z = a.z * b.z;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_div(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
    pnanovdb_vec3_t v;
    v.x = a.x / b.x;
    v.y = a.y / b.y;
    v.z = a.z / b.z;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_min(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
    pnanovdb_vec3_t v;
    v.x = a.x < b.x ? a.x : b.x;
    v.y = a.y < b.y ? a.y : b.y;
    v.z = a.z < b.z ? a.z : b.z;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_vec3_max(const pnanovdb_vec3_t a, const pnanovdb_vec3_t b)
{
    pnanovdb_vec3_t v;
    v.x = a.x > b.x ? a.x : b.x;
    v.y = a.y > b.y ? a.y : b.y;
    v.z = a.z > b.z ? a.z : b.z;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_coord_to_vec3(const pnanovdb_coord_t coord)
{
    pnanovdb_vec3_t v;
    v.x = pnanovdb_int32_to_float(coord.x);
    v.y = pnanovdb_int32_to_float(coord.y);
    v.z = pnanovdb_int32_to_float(coord.z);
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_coord_uniform(const pnanovdb_int32_t a)
{
    pnanovdb_coord_t v;
    v.x = a;
    v.y = a;
    v.z = a;
    return v;
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b)
{
    pnanovdb_coord_t v;
    v.x = a.x + b.x;
    v.y = a.y + b.y;
    v.z = a.z + b.z;
    return v;
}
#elif defined(PNANOVDB_HLSL)
pnanovdb_vec3_t pnanovdb_vec3_uniform(float a) { return float3(a, a, a); }
pnanovdb_vec3_t pnanovdb_vec3_add(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a + b; }
pnanovdb_vec3_t pnanovdb_vec3_sub(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a - b; }
pnanovdb_vec3_t pnanovdb_vec3_mul(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a * b; }
pnanovdb_vec3_t pnanovdb_vec3_div(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a / b; }
pnanovdb_vec3_t pnanovdb_vec3_min(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return min(a, b); }
pnanovdb_vec3_t pnanovdb_vec3_max(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return max(a, b); }
pnanovdb_vec3_t pnanovdb_coord_to_vec3(pnanovdb_coord_t coord) { return float3(coord); }
pnanovdb_coord_t pnanovdb_coord_uniform(pnanovdb_int32_t a) { return int3(a, a, a); }
pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b) { return a + b; }
#elif defined(PNANOVDB_GLSL)
pnanovdb_vec3_t pnanovdb_vec3_uniform(float a) { return vec3(a, a, a); }
pnanovdb_vec3_t pnanovdb_vec3_add(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a + b; }
pnanovdb_vec3_t pnanovdb_vec3_sub(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a - b; }
pnanovdb_vec3_t pnanovdb_vec3_mul(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a * b; }
pnanovdb_vec3_t pnanovdb_vec3_div(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return a / b; }
pnanovdb_vec3_t pnanovdb_vec3_min(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return min(a, b); }
pnanovdb_vec3_t pnanovdb_vec3_max(pnanovdb_vec3_t a, pnanovdb_vec3_t b) { return max(a, b); }
pnanovdb_vec3_t pnanovdb_coord_to_vec3(const pnanovdb_coord_t coord) { return vec3(coord); }
pnanovdb_coord_t pnanovdb_coord_uniform(pnanovdb_int32_t a) { return ivec3(a, a, a); }
pnanovdb_coord_t pnanovdb_coord_add(pnanovdb_coord_t a, pnanovdb_coord_t b) { return a + b; }
#endif

// ------------------------------------------------ Address Type -----------------------------------------------------------

#if defined(PNANOVDB_ADDRESS_32)
struct pnanovdb_address_t
{
    pnanovdb_uint32_t byte_offset;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_address_t)

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset += byte_offset;
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_neg(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset -= byte_offset;
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_product(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset, pnanovdb_uint32_t multiplier)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset += byte_offset * multiplier;
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset64(pnanovdb_address_t address, pnanovdb_uint64_t byte_offset)
{
    pnanovdb_address_t ret = address;
    // lose high bits on 32-bit
    ret.byte_offset += pnanovdb_uint64_low(byte_offset);
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_address_mask(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
    return address.byte_offset & mask;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_mask_inv(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset &= (~mask);
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_null()
{
    pnanovdb_address_t ret = { 0 };
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_is_null(pnanovdb_address_t address)
{
    return address.byte_offset == 0u;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_in_interval(pnanovdb_address_t address, pnanovdb_address_t min_address, pnanovdb_address_t max_address)
{
    return address.byte_offset >= min_address.byte_offset && address.byte_offset < max_address.byte_offset;
}
#elif defined(PNANOVDB_ADDRESS_64)
struct pnanovdb_address_t
{
    pnanovdb_uint64_t byte_offset;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_address_t)

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset += byte_offset;
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_neg(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset -= byte_offset;
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset_product(pnanovdb_address_t address, pnanovdb_uint32_t byte_offset, pnanovdb_uint32_t multiplier)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset += pnanovdb_uint32_as_uint64_low(byte_offset) * pnanovdb_uint32_as_uint64_low(multiplier);
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_offset64(pnanovdb_address_t address, pnanovdb_uint64_t byte_offset)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset += byte_offset;
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_address_mask(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
    return pnanovdb_uint64_low(address.byte_offset) & mask;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_mask_inv(pnanovdb_address_t address, pnanovdb_uint32_t mask)
{
    pnanovdb_address_t ret = address;
    ret.byte_offset &= (~pnanovdb_uint32_as_uint64_low(mask));
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_address_null()
{
    pnanovdb_address_t ret = { 0 };
    return ret;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_is_null(pnanovdb_address_t address)
{
    return address.byte_offset == 0llu;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_address_in_interval(pnanovdb_address_t address, pnanovdb_address_t min_address, pnanovdb_address_t max_address)
{
    return address.byte_offset >= min_address.byte_offset && address.byte_offset < max_address.byte_offset;
}
#endif

// ------------------------------------------------ High Level Buffer Read -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_read_uint32(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    return pnanovdb_buf_read_uint32(buf, address.byte_offset);
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_read_uint64(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    return pnanovdb_buf_read_uint64(buf, address.byte_offset);
}
PNANOVDB_FORCE_INLINE pnanovdb_int32_t pnanovdb_read_int32(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    return pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, address));
}
PNANOVDB_FORCE_INLINE float pnanovdb_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    return pnanovdb_uint32_as_float(pnanovdb_read_uint32(buf, address));
}
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_read_int64(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    return pnanovdb_uint64_as_int64(pnanovdb_read_uint64(buf, address));
}
PNANOVDB_FORCE_INLINE double pnanovdb_read_double(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    return pnanovdb_uint64_as_double(pnanovdb_read_uint64(buf, address));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_read_coord(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    pnanovdb_coord_t ret;
    ret.x = pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, 0u)));
    ret.y = pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, 4u)));
    ret.z = pnanovdb_uint32_as_int32(pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, 8u)));
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_read_bit(pnanovdb_buf_t buf, pnanovdb_address_t address, pnanovdb_uint32_t bit_offset)
{
    pnanovdb_address_t word_address = pnanovdb_address_mask_inv(address, 3u);
    pnanovdb_uint32_t bit_index = (pnanovdb_address_mask(address, 3u) << 3u) + bit_offset;
    pnanovdb_uint32_t value_word = pnanovdb_buf_read_uint32(buf, word_address.byte_offset);
    return ((value_word >> bit_index) & 1) != 0u;
}

#if defined(PNANOVDB_C)
PNANOVDB_FORCE_INLINE short pnanovdb_read_half(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, address);
    return (short)(raw >> (pnanovdb_address_mask(address, 2) << 3));
}
#elif defined(PNANOVDB_HLSL)
PNANOVDB_FORCE_INLINE float pnanovdb_read_half(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, address);
    return f16tof32(raw >> (pnanovdb_address_mask(address, 2) << 3));
}
#elif defined(PNANOVDB_GLSL)
PNANOVDB_FORCE_INLINE float pnanovdb_read_half(pnanovdb_buf_t buf, pnanovdb_address_t address)
{
    pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, address);
    return unpackHalf2x16(raw >> (pnanovdb_address_mask(address, 2) << 3)).x;
}
#endif

// ------------------------------------------------ Core Structures -----------------------------------------------------------

#define PNANOVDB_MAGIC_NUMBER 0x304244566f6e614eUL// "NanoVDB0" in hex - little endian (uint64_t)

#define PNANOVDB_MAJOR_VERSION_NUMBER 32// reflects changes to the ABI
#define PNANOVDB_MINOR_VERSION_NUMBER  4// reflects changes to the API but not ABI
#define PNANOVDB_PATCH_VERSION_NUMBER  2// reflects bug-fixes with no ABI or API changes

#define PNANOVDB_GRID_TYPE_UNKNOWN 0
#define PNANOVDB_GRID_TYPE_FLOAT 1
#define PNANOVDB_GRID_TYPE_DOUBLE 2
#define PNANOVDB_GRID_TYPE_INT16 3
#define PNANOVDB_GRID_TYPE_INT32 4
#define PNANOVDB_GRID_TYPE_INT64 5
#define PNANOVDB_GRID_TYPE_VEC3F 6
#define PNANOVDB_GRID_TYPE_VEC3D 7
#define PNANOVDB_GRID_TYPE_MASK 8
#define PNANOVDB_GRID_TYPE_HALF 9
#define PNANOVDB_GRID_TYPE_UINT32 10
#define PNANOVDB_GRID_TYPE_BOOLEAN 11
#define PNANOVDB_GRID_TYPE_RGBA8 12
#define PNANOVDB_GRID_TYPE_FP4 13
#define PNANOVDB_GRID_TYPE_FP8 14
#define PNANOVDB_GRID_TYPE_FP16 15
#define PNANOVDB_GRID_TYPE_FPN 16
#define PNANOVDB_GRID_TYPE_VEC4F 17
#define PNANOVDB_GRID_TYPE_VEC4D 18
#define PNANOVDB_GRID_TYPE_INDEX 19
#define PNANOVDB_GRID_TYPE_END 20

#define PNANOVDB_GRID_CLASS_UNKNOWN 0
#define PNANOVDB_GRID_CLASS_LEVEL_SET 1     // narrow band levelset, e.g. SDF
#define PNANOVDB_GRID_CLASS_FOG_VOLUME 2    // fog volume, e.g. density
#define PNANOVDB_GRID_CLASS_STAGGERED 3     // staggered MAC grid, e.g. velocity
#define PNANOVDB_GRID_CLASS_POINT_INDEX 4   // point index grid
#define PNANOVDB_GRID_CLASS_POINT_DATA 5    // point data grid
#define PNANOVDB_GRID_CLASS_TOPOLOGY 6      // grid with active states only (no values)
#define PNANOVDB_GRID_CLASS_VOXEL_VOLUME 7  // volume of geometric cubes, e.g. minecraft
#define PNANOVDB_GRID_CLASS_INDEX_GRID 8    // grid whose values are offsets, e.g. into an external array
#define PNANOVDB_GRID_CLASS_END 9

#define PNANOVDB_GRID_FLAGS_HAS_LONG_GRID_NAME (1 << 0)
#define PNANOVDB_GRID_FLAGS_HAS_BBOX (1 << 1)
#define PNANOVDB_GRID_FLAGS_HAS_MIN_MAX (1 << 2)
#define PNANOVDB_GRID_FLAGS_HAS_AVERAGE (1 << 3)
#define PNANOVDB_GRID_FLAGS_HAS_STD_DEVIATION (1 << 4)
#define PNANOVDB_GRID_FLAGS_IS_BREADTH_FIRST (1 << 5)
#define PNANOVDB_GRID_FLAGS_END (1 << 6)

#define PNANOVDB_LEAF_TYPE_DEFAULT 0
#define PNANOVDB_LEAF_TYPE_LITE 1
#define PNANOVDB_LEAF_TYPE_FP 2
#define PNANOVDB_LEAF_TYPE_INDEX 3

PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_value_strides_bits[PNANOVDB_GRID_TYPE_END]  = {  0, 32, 64, 16, 32, 64, 96,  192,  0, 16, 32,  1, 32,  4,  8, 16,  0, 128, 256, 0 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_table_strides_bits[PNANOVDB_GRID_TYPE_END] =  { 64, 64, 64, 64, 64, 64, 128, 192, 64, 64, 64, 64, 64, 64, 64, 64, 64, 128, 256, 64 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_strides_bits[PNANOVDB_GRID_TYPE_END] = {  0, 32, 64, 16, 32, 64, 96,  192,  8, 16, 32,  8, 32, 32, 32, 32, 32, 128, 256, 64 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_minmax_aligns_bits[PNANOVDB_GRID_TYPE_END]  = {  0, 32, 64, 16, 32, 64, 32,   64,  8, 16, 32,  8, 32, 32, 32, 32, 32,  32,  64, 64 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_stat_strides_bits[PNANOVDB_GRID_TYPE_END]   = {  0, 32, 64, 32, 32, 64, 32,   64,  8, 32, 32,  8, 32, 32, 32, 32, 32,  32,  64, 64 };
PNANOVDB_STATIC_CONST pnanovdb_uint32_t pnanovdb_grid_type_leaf_type[PNANOVDB_GRID_TYPE_END]           = {  0,  0,  0,  0,  0,  0,  0,    0,  1,  0,  0,  1,  0,  2,  2,  2,  2,   0,   0, 3 };

struct pnanovdb_map_t
{
    float matf[9];
    float invmatf[9];
    float vecf[3];
    float taperf;
    double matd[9];
    double invmatd[9];
    double vecd[3];
    double taperd;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_map_t)
struct pnanovdb_map_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_map_handle_t)

#define PNANOVDB_MAP_SIZE 264

#define PNANOVDB_MAP_OFF_MATF 0
#define PNANOVDB_MAP_OFF_INVMATF 36
#define PNANOVDB_MAP_OFF_VECF 72
#define PNANOVDB_MAP_OFF_TAPERF 84
#define PNANOVDB_MAP_OFF_MATD 88
#define PNANOVDB_MAP_OFF_INVMATD 160
#define PNANOVDB_MAP_OFF_VECD 232
#define PNANOVDB_MAP_OFF_TAPERD 256

PNANOVDB_FORCE_INLINE float pnanovdb_map_get_matf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_MATF + 4u * index));
}
PNANOVDB_FORCE_INLINE float pnanovdb_map_get_invmatf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_INVMATF + 4u * index));
}
PNANOVDB_FORCE_INLINE float pnanovdb_map_get_vecf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_VECF + 4u * index));
}
PNANOVDB_FORCE_INLINE float pnanovdb_map_get_taperf(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_float(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_TAPERF));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_matd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_MATD + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_invmatd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_INVMATD + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_vecd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_VECD + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_map_get_taperd(pnanovdb_buf_t buf, pnanovdb_map_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_MAP_OFF_TAPERD));
}

struct pnanovdb_grid_t
{
    pnanovdb_uint64_t magic;                    // 8 bytes,     0
    pnanovdb_uint64_t checksum;                 // 8 bytes,     8
    pnanovdb_uint32_t version;                  // 4 bytes,     16
    pnanovdb_uint32_t flags;                    // 4 bytes,     20
    pnanovdb_uint32_t grid_index;               // 4 bytes,     24
    pnanovdb_uint32_t grid_count;               // 4 bytes,     28
    pnanovdb_uint64_t grid_size;                // 8 bytes,     32
    pnanovdb_uint32_t grid_name[256 / 4];       // 256 bytes,   40
    pnanovdb_map_t map;                         // 264 bytes,   296
    double world_bbox[6];                       // 48 bytes,    560
    double voxel_size[3];                       // 24 bytes,    608
    pnanovdb_uint32_t grid_class;               // 4 bytes,     632
    pnanovdb_uint32_t grid_type;                // 4 bytes,     636
    pnanovdb_int64_t blind_metadata_offset;     // 8 bytes,     640
    pnanovdb_uint32_t blind_metadata_count;     // 4 bytes,     648
    pnanovdb_uint32_t pad[5];                   // 20 bytes,    652
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_t)
struct pnanovdb_grid_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_handle_t)

#define PNANOVDB_GRID_SIZE 672

#define PNANOVDB_GRID_OFF_MAGIC 0
#define PNANOVDB_GRID_OFF_CHECKSUM 8
#define PNANOVDB_GRID_OFF_VERSION 16
#define PNANOVDB_GRID_OFF_FLAGS 20
#define PNANOVDB_GRID_OFF_GRID_INDEX 24
#define PNANOVDB_GRID_OFF_GRID_COUNT 28
#define PNANOVDB_GRID_OFF_GRID_SIZE 32
#define PNANOVDB_GRID_OFF_GRID_NAME 40
#define PNANOVDB_GRID_OFF_MAP 296
#define PNANOVDB_GRID_OFF_WORLD_BBOX 560
#define PNANOVDB_GRID_OFF_VOXEL_SIZE 608
#define PNANOVDB_GRID_OFF_GRID_CLASS 632
#define PNANOVDB_GRID_OFF_GRID_TYPE 636
#define PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET 640
#define PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT 648

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_magic(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_MAGIC));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_checksum(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_CHECKSUM));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_version(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_VERSION));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_flags(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_index(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_INDEX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_count(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_COUNT));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_grid_get_grid_size(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_SIZE));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_name(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_NAME + 4u * index));
}
PNANOVDB_FORCE_INLINE pnanovdb_map_handle_t pnanovdb_grid_get_map(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    pnanovdb_map_handle_t ret;
    ret.address = pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_MAP);
    return ret;
}
PNANOVDB_FORCE_INLINE double pnanovdb_grid_get_world_bbox(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_WORLD_BBOX + 8u * index));
}
PNANOVDB_FORCE_INLINE double pnanovdb_grid_get_voxel_size(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_double(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_VOXEL_SIZE + 8u * index));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_class(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_CLASS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_grid_type(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_GRID_TYPE));
}
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_grid_get_blind_metadata_offset(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_BLIND_METADATA_OFFSET));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_grid_get_blind_metadata_count(pnanovdb_buf_t buf, pnanovdb_grid_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRID_OFF_BLIND_METADATA_COUNT));
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_major(pnanovdb_uint32_t version)
{
    return (version >> 21u) & ((1u << 11u) - 1u);
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_minor(pnanovdb_uint32_t version)
{
    return (version >> 10u) & ((1u << 11u) - 1u);
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_version_get_patch(pnanovdb_uint32_t version)
{
    return version & ((1u << 10u) - 1u);
}

struct pnanovdb_gridblindmetadata_t
{
    pnanovdb_int64_t byte_offset;       // 8 bytes,     0
    pnanovdb_uint64_t element_count;    // 8 bytes,     8
    pnanovdb_uint32_t flags;            // 4 bytes,     16
    pnanovdb_uint32_t semantic;         // 4 bytes,     20
    pnanovdb_uint32_t data_class;       // 4 bytes,     24
    pnanovdb_uint32_t data_type;        // 4 bytes,     28
    pnanovdb_uint32_t name[256 / 4];    // 256 bytes,   32
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_gridblindmetadata_t)
struct pnanovdb_gridblindmetadata_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_gridblindmetadata_handle_t)

#define PNANOVDB_GRIDBLINDMETADATA_SIZE 288

#define PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET 0
#define PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT 8
#define PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS 16
#define PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC 20
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS 24
#define PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE 28
#define PNANOVDB_GRIDBLINDMETADATA_OFF_NAME 32

PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_gridblindmetadata_get_byte_offset(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
    return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_BYTE_OFFSET));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_gridblindmetadata_get_element_count(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_ELEMENT_COUNT));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_flags(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_semantic(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_SEMANTIC));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_data_class(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_CLASS));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_data_type(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_DATA_TYPE));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_gridblindmetadata_get_name(pnanovdb_buf_t buf, pnanovdb_gridblindmetadata_handle_t p, pnanovdb_uint32_t index) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_GRIDBLINDMETADATA_OFF_NAME + 4u * index));
}

struct pnanovdb_tree_t
{
    pnanovdb_uint64_t node_offset_leaf;
    pnanovdb_uint64_t node_offset_lower;
    pnanovdb_uint64_t node_offset_upper;
    pnanovdb_uint64_t node_offset_root;
    pnanovdb_uint32_t node_count_leaf;
    pnanovdb_uint32_t node_count_lower;
    pnanovdb_uint32_t node_count_upper;
    pnanovdb_uint32_t tile_count_leaf;
    pnanovdb_uint32_t tile_count_lower;
    pnanovdb_uint32_t tile_count_upper;
    pnanovdb_uint64_t voxel_count;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_tree_t)
struct pnanovdb_tree_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_tree_handle_t)

#define PNANOVDB_TREE_SIZE 64

#define PNANOVDB_TREE_OFF_NODE_OFFSET_LEAF 0
#define PNANOVDB_TREE_OFF_NODE_OFFSET_LOWER 8
#define PNANOVDB_TREE_OFF_NODE_OFFSET_UPPER 16
#define PNANOVDB_TREE_OFF_NODE_OFFSET_ROOT 24
#define PNANOVDB_TREE_OFF_NODE_COUNT_LEAF 32
#define PNANOVDB_TREE_OFF_NODE_COUNT_LOWER 36
#define PNANOVDB_TREE_OFF_NODE_COUNT_UPPER 40
#define PNANOVDB_TREE_OFF_TILE_COUNT_LEAF 44
#define PNANOVDB_TREE_OFF_TILE_COUNT_LOWER 48
#define PNANOVDB_TREE_OFF_TILE_COUNT_UPPER 52
#define PNANOVDB_TREE_OFF_VOXEL_COUNT 56

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_LEAF));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_LOWER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_UPPER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_node_offset_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_OFFSET_ROOT));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_LEAF));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_LOWER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_node_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_NODE_COUNT_UPPER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_leaf(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LEAF));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_lower(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_LOWER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_tree_get_tile_count_upper(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_TILE_COUNT_UPPER));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_tree_get_voxel_count(pnanovdb_buf_t buf, pnanovdb_tree_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_TREE_OFF_VOXEL_COUNT));
}

struct pnanovdb_root_t
{
    pnanovdb_coord_t bbox_min;
    pnanovdb_coord_t bbox_max;
    pnanovdb_uint32_t table_size;
    pnanovdb_uint32_t pad1;         // background can start here
    // background, min, max
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_t)
struct pnanovdb_root_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_handle_t)

#define PNANOVDB_ROOT_BASE_SIZE 28

#define PNANOVDB_ROOT_OFF_BBOX_MIN 0
#define PNANOVDB_ROOT_OFF_BBOX_MAX 12
#define PNANOVDB_ROOT_OFF_TABLE_SIZE 24

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_root_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_root_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_root_get_bbox_max(pnanovdb_buf_t buf, pnanovdb_root_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_OFF_BBOX_MAX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_root_get_tile_count(pnanovdb_buf_t buf, pnanovdb_root_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_OFF_TABLE_SIZE));
}

struct pnanovdb_root_tile_t
{
    pnanovdb_uint64_t key;
    pnanovdb_int64_t child;         // signed byte offset from root to the child node, 0 means it is a constant tile, so use value
    pnanovdb_uint32_t state;
    pnanovdb_uint32_t pad1;         // value can start here
    // value
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_tile_t)
struct pnanovdb_root_tile_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_root_tile_handle_t)

#define PNANOVDB_ROOT_TILE_BASE_SIZE 20

#define PNANOVDB_ROOT_TILE_OFF_KEY 0
#define PNANOVDB_ROOT_TILE_OFF_CHILD 8
#define PNANOVDB_ROOT_TILE_OFF_STATE 16

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_root_tile_get_key(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_TILE_OFF_KEY));
}
PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_root_tile_get_child(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t p) {
    return pnanovdb_read_int64(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_TILE_OFF_CHILD));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_root_tile_get_state(pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_ROOT_TILE_OFF_STATE));
}

struct pnanovdb_upper_t
{
    pnanovdb_coord_t bbox_min;
    pnanovdb_coord_t bbox_max;
    pnanovdb_uint64_t flags;
    pnanovdb_uint32_t value_mask[1024];
    pnanovdb_uint32_t child_mask[1024];
    // min, max
    // alignas(32) pnanovdb_uint32_t table[];
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_upper_t)
struct pnanovdb_upper_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_upper_handle_t)

#define PNANOVDB_UPPER_TABLE_COUNT 32768
#define PNANOVDB_UPPER_BASE_SIZE 8224

#define PNANOVDB_UPPER_OFF_BBOX_MIN 0
#define PNANOVDB_UPPER_OFF_BBOX_MAX 12
#define PNANOVDB_UPPER_OFF_FLAGS 24
#define PNANOVDB_UPPER_OFF_VALUE_MASK 32
#define PNANOVDB_UPPER_OFF_CHILD_MASK 4128

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_upper_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_upper_get_bbox_max(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_BBOX_MAX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_upper_get_flags(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_upper_get_value_mask(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, pnanovdb_uint32_t bit_index) {
    pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_VALUE_MASK + 4u * (bit_index >> 5u)));
    return ((value >> (bit_index & 31u)) & 1) != 0u;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_upper_get_child_mask(pnanovdb_buf_t buf, pnanovdb_upper_handle_t p, pnanovdb_uint32_t bit_index) {
    pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_UPPER_OFF_CHILD_MASK + 4u * (bit_index >> 5u)));
    return ((value >> (bit_index & 31u)) & 1) != 0u;
}

struct pnanovdb_lower_t
{
    pnanovdb_coord_t bbox_min;
    pnanovdb_coord_t bbox_max;
    pnanovdb_uint64_t flags;
    pnanovdb_uint32_t value_mask[128];
    pnanovdb_uint32_t child_mask[128];
    // min, max
    // alignas(32) pnanovdb_uint32_t table[];
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_lower_t)
struct pnanovdb_lower_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_lower_handle_t)

#define PNANOVDB_LOWER_TABLE_COUNT 4096
#define PNANOVDB_LOWER_BASE_SIZE 1056

#define PNANOVDB_LOWER_OFF_BBOX_MIN 0
#define PNANOVDB_LOWER_OFF_BBOX_MAX 12
#define PNANOVDB_LOWER_OFF_FLAGS 24
#define PNANOVDB_LOWER_OFF_VALUE_MASK 32
#define PNANOVDB_LOWER_OFF_CHILD_MASK 544

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_lower_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_lower_get_bbox_max(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_BBOX_MAX));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_lower_get_flags(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p) {
    return pnanovdb_read_uint64(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_lower_get_value_mask(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, pnanovdb_uint32_t bit_index) {
    pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_VALUE_MASK + 4u * (bit_index >> 5u)));
    return ((value >> (bit_index & 31u)) & 1) != 0u;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_lower_get_child_mask(pnanovdb_buf_t buf, pnanovdb_lower_handle_t p, pnanovdb_uint32_t bit_index) {
    pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LOWER_OFF_CHILD_MASK + 4u * (bit_index >> 5u)));
    return ((value >> (bit_index & 31u)) & 1) != 0u;
}

struct pnanovdb_leaf_t
{
    pnanovdb_coord_t bbox_min;
    pnanovdb_uint32_t bbox_dif_and_flags;
    pnanovdb_uint32_t value_mask[16];
    // min, max
    // alignas(32) pnanovdb_uint32_t values[];
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_leaf_t)
struct pnanovdb_leaf_handle_t { pnanovdb_address_t address; };
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_leaf_handle_t)

#define PNANOVDB_LEAF_TABLE_COUNT 512
#define PNANOVDB_LEAF_BASE_SIZE 80

#define PNANOVDB_LEAF_OFF_BBOX_MIN 0
#define PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS 12
#define PNANOVDB_LEAF_OFF_VALUE_MASK 16

#define PNANOVDB_LEAF_TABLE_NEG_OFF_BBOX_DIF_AND_FLAGS 84
#define PNANOVDB_LEAF_TABLE_NEG_OFF_MINIMUM 16
#define PNANOVDB_LEAF_TABLE_NEG_OFF_QUANTUM 12

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_leaf_get_bbox_min(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p) {
    return pnanovdb_read_coord(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_BBOX_MIN));
}
PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_get_bbox_dif_and_flags(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p) {
    return pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_BBOX_DIF_AND_FLAGS));
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_get_value_mask(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p, pnanovdb_uint32_t bit_index) {
    pnanovdb_uint32_t value = pnanovdb_read_uint32(buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_VALUE_MASK + 4u * (bit_index >> 5u)));
    return ((value >> (bit_index & 31u)) & 1) != 0u;
}

struct pnanovdb_grid_type_constants_t
{
    pnanovdb_uint32_t root_off_background;
    pnanovdb_uint32_t root_off_min;
    pnanovdb_uint32_t root_off_max;
    pnanovdb_uint32_t root_off_ave;
    pnanovdb_uint32_t root_off_stddev;
    pnanovdb_uint32_t root_size;
    pnanovdb_uint32_t value_stride_bits;
    pnanovdb_uint32_t table_stride;
    pnanovdb_uint32_t root_tile_off_value;
    pnanovdb_uint32_t root_tile_size;
    pnanovdb_uint32_t upper_off_min;
    pnanovdb_uint32_t upper_off_max;
    pnanovdb_uint32_t upper_off_ave;
    pnanovdb_uint32_t upper_off_stddev;
    pnanovdb_uint32_t upper_off_table;
    pnanovdb_uint32_t upper_size;
    pnanovdb_uint32_t lower_off_min;
    pnanovdb_uint32_t lower_off_max;
    pnanovdb_uint32_t lower_off_ave;
    pnanovdb_uint32_t lower_off_stddev;
    pnanovdb_uint32_t lower_off_table;
    pnanovdb_uint32_t lower_size;
    pnanovdb_uint32_t leaf_off_min;
    pnanovdb_uint32_t leaf_off_max;
    pnanovdb_uint32_t leaf_off_ave;
    pnanovdb_uint32_t leaf_off_stddev;
    pnanovdb_uint32_t leaf_off_table;
    pnanovdb_uint32_t leaf_size;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_grid_type_constants_t)

PNANOVDB_STATIC_CONST pnanovdb_grid_type_constants_t pnanovdb_grid_type_constants[PNANOVDB_GRID_TYPE_END] =
{
    {28, 28, 28, 28, 28, 32,  0, 8, 20, 32,  8224, 8224, 8224, 8224, 8224, 270368,  1056, 1056, 1056, 1056, 1056, 33824,  80, 80, 80, 80, 96, 96},
    {28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
    {32, 40, 48, 56, 64, 96,  64, 8, 24, 32,  8224, 8232, 8240, 8248, 8256, 270400,  1056, 1064, 1072, 1080, 1088, 33856,  80, 88, 96, 104, 128, 4224},
    {28, 30, 32, 36, 40, 64,  16, 8, 20, 32,  8224, 8226, 8228, 8232, 8256, 270400,  1056, 1058, 1060, 1064, 1088, 33856,  80, 82, 84, 88, 96, 1120},
    {28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
    {32, 40, 48, 56, 64, 96,  64, 8, 24, 32,  8224, 8232, 8240, 8248, 8256, 270400,  1056, 1064, 1072, 1080, 1088, 33856,  80, 88, 96, 104, 128, 4224},
    {28, 40, 52, 64, 68, 96,  96, 16, 20, 32,  8224, 8236, 8248, 8252, 8256, 532544,  1056, 1068, 1080, 1084, 1088, 66624,  80, 92, 104, 108, 128, 6272},
    {32, 56, 80, 104, 112, 128,  192, 24, 24, 64,  8224, 8248, 8272, 8280, 8288, 794720,  1056, 1080, 1104, 1112, 1120, 99424,  80, 104, 128, 136, 160, 12448},
    {28, 29, 30, 31, 32, 64,  0, 8, 20, 32,  8224, 8225, 8226, 8227, 8256, 270400,  1056, 1057, 1058, 1059, 1088, 33856,  80, 80, 80, 80, 96, 96},
    {28, 30, 32, 36, 40, 64,  16, 8, 20, 32,  8224, 8226, 8228, 8232, 8256, 270400,  1056, 1058, 1060, 1064, 1088, 33856,  80, 82, 84, 88, 96, 1120},
    {28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
    {28, 29, 30, 31, 32, 64,  1, 8, 20, 32,  8224, 8225, 8226, 8227, 8256, 270400,  1056, 1057, 1058, 1059, 1088, 33856,  80, 80, 80, 80, 96, 160},
    {28, 32, 36, 40, 44, 64,  32, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  80, 84, 88, 92, 96, 2144},
    {28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 352},
    {28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 608},
    {28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 1120},
    {28, 32, 36, 40, 44, 64,  0, 8, 20, 32,  8224, 8228, 8232, 8236, 8256, 270400,  1056, 1060, 1064, 1068, 1088, 33856,  88, 90, 92, 94, 96, 96},
    {28, 44, 60, 76, 80, 96,  128, 16, 20, 64,  8224, 8240, 8256, 8260, 8288, 532576,  1056, 1072, 1088, 1092, 1120, 66656,  80, 96, 112, 116, 128, 8320},
    {32, 64, 96, 128, 136, 160,  256, 32, 24, 64,  8224, 8256, 8288, 8296, 8320, 1056896,  1056, 1088, 1120, 1128, 1152, 132224,  80, 112, 144, 152, 160, 16544},
    {32, 40, 48, 56, 64, 96,  0, 8, 24, 32,  8224, 8232, 8240, 8248, 8256, 270400,  1056, 1064, 1072, 1080, 1088, 33856,  80, 80, 80, 80, 88, 96},
};

// ------------------------------------------------ Basic Lookup -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_gridblindmetadata_handle_t pnanovdb_grid_get_gridblindmetadata(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, pnanovdb_uint32_t index)
{
    pnanovdb_gridblindmetadata_handle_t meta = { grid.address };
    pnanovdb_uint64_t byte_offset = pnanovdb_grid_get_blind_metadata_offset(buf, grid);
    meta.address = pnanovdb_address_offset64(meta.address, byte_offset);
    meta.address = pnanovdb_address_offset_product(meta.address, PNANOVDB_GRIDBLINDMETADATA_SIZE, index);
    return meta;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanodvb_grid_get_gridblindmetadata_value_address(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, pnanovdb_uint32_t index)
{
    pnanovdb_gridblindmetadata_handle_t meta = pnanovdb_grid_get_gridblindmetadata(buf, grid, index);
    pnanovdb_int64_t byte_offset = pnanovdb_gridblindmetadata_get_byte_offset(buf, meta);
    pnanovdb_address_t address = grid.address;
    address = pnanovdb_address_offset64(address, pnanovdb_int64_as_uint64(byte_offset));
    return address;
}

PNANOVDB_FORCE_INLINE pnanovdb_tree_handle_t pnanovdb_grid_get_tree(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid)
{
    pnanovdb_tree_handle_t tree = { grid.address };
    tree.address = pnanovdb_address_offset(tree.address, PNANOVDB_GRID_SIZE);
    return tree;
}

PNANOVDB_FORCE_INLINE pnanovdb_root_handle_t pnanovdb_tree_get_root(pnanovdb_buf_t buf, pnanovdb_tree_handle_t tree)
{
    pnanovdb_root_handle_t root = { tree.address };
    pnanovdb_uint64_t byte_offset = pnanovdb_tree_get_node_offset_root(buf, tree);
    root.address = pnanovdb_address_offset64(root.address, byte_offset);
    return root;
}

PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_get_tile(pnanovdb_grid_type_t grid_type, pnanovdb_root_handle_t root, pnanovdb_uint32_t n)
{
    pnanovdb_root_tile_handle_t tile = { root.address };
    tile.address = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_size));
    tile.address = pnanovdb_address_offset_product(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_size), n);
    return tile;
}

PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_get_tile_zero(pnanovdb_grid_type_t grid_type, pnanovdb_root_handle_t root)
{
    pnanovdb_root_tile_handle_t tile = { root.address };
    tile.address = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_size));
    return tile;
}

PNANOVDB_FORCE_INLINE pnanovdb_upper_handle_t pnanovdb_root_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, pnanovdb_root_tile_handle_t tile)
{
    pnanovdb_upper_handle_t upper = { root.address };
    upper.address = pnanovdb_address_offset64(upper.address, pnanovdb_int64_as_uint64(pnanovdb_root_tile_get_child(buf, tile)));
    return upper;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_coord_to_key(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
#if defined(PNANOVDB_NATIVE_64)
    pnanovdb_uint64_t iu = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x) >> 12u;
    pnanovdb_uint64_t ju = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).y) >> 12u;
    pnanovdb_uint64_t ku = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).z) >> 12u;
    return (ku) | (ju << 21u) | (iu << 42u);
#else
    pnanovdb_uint32_t iu = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x) >> 12u;
    pnanovdb_uint32_t ju = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).y) >> 12u;
    pnanovdb_uint32_t ku = pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).z) >> 12u;
    pnanovdb_uint32_t key_x = ku | (ju << 21);
    pnanovdb_uint32_t key_y = (iu << 10) | (ju >> 11);
    return pnanovdb_uint32_as_uint64(key_x, key_y);
#endif
}

PNANOVDB_FORCE_INLINE pnanovdb_root_tile_handle_t pnanovdb_root_find_tile(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t tile_count = pnanovdb_uint32_as_int32(pnanovdb_root_get_tile_count(buf, root));
    pnanovdb_root_tile_handle_t tile = pnanovdb_root_get_tile_zero(grid_type, root);
    pnanovdb_uint64_t key = pnanovdb_coord_to_key(ijk);
    for (pnanovdb_uint32_t i = 0u; i < tile_count; i++)
    {
        if (pnanovdb_uint64_is_equal(key, pnanovdb_root_tile_get_key(buf, tile)))
        {
            return tile;
        }
        tile.address = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_size));
    }
    pnanovdb_root_tile_handle_t null_handle = { pnanovdb_address_null() };
    return null_handle;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return (((PNANOVDB_DEREF(ijk).x & 7) >> 0) << (2 * 3)) +
        (((PNANOVDB_DEREF(ijk).y & 7) >> 0) << (3)) +
        ((PNANOVDB_DEREF(ijk).z & 7) >> 0);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_min);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_max);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_ave);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_stddev);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_table_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t node, pnanovdb_uint32_t n)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, leaf_off_table) + ((PNANOVDB_GRID_TYPE_GET(grid_type, value_stride_bits) * n) >> 3u);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
    return pnanovdb_leaf_get_table_address(grid_type, buf, leaf, n);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t value_log_bits)
{
    //  value_log_bits                                                          //   2     3       4
    pnanovdb_uint32_t value_bits = 1u << value_log_bits;                        //   4     8      16
    pnanovdb_uint32_t value_mask = (1u << value_bits) - 1u;                     // 0xF  0xFF  0xFFFF
    pnanovdb_uint32_t values_per_word_bits = 5u - value_log_bits;               //   3     2       1
    pnanovdb_uint32_t values_per_word_mask = (1u << values_per_word_bits) - 1u; //   7     3       1

    pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
    float minimum = pnanovdb_read_float(buf, pnanovdb_address_offset_neg(address, PNANOVDB_LEAF_TABLE_NEG_OFF_MINIMUM));
    float quantum = pnanovdb_read_float(buf, pnanovdb_address_offset_neg(address, PNANOVDB_LEAF_TABLE_NEG_OFF_QUANTUM));
    pnanovdb_uint32_t raw = pnanovdb_read_uint32(buf, pnanovdb_address_offset(address, ((n >> values_per_word_bits) << 2u)));
    pnanovdb_uint32_t value_compressed = (raw >> ((n & values_per_word_mask) << value_log_bits)) & value_mask;
    return pnanovdb_uint32_to_float(value_compressed) * quantum + minimum;
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp4_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return pnanovdb_leaf_fp_read_float(buf, address, ijk, 2u);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp8_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return pnanovdb_leaf_fp_read_float(buf, address, ijk, 3u);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fp16_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return pnanovdb_leaf_fp_read_float(buf, address, ijk, 4u);
}

PNANOVDB_FORCE_INLINE float pnanovdb_leaf_fpn_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t bbox_dif_and_flags = pnanovdb_read_uint32(buf, pnanovdb_address_offset_neg(address, PNANOVDB_LEAF_TABLE_NEG_OFF_BBOX_DIF_AND_FLAGS));
    pnanovdb_uint32_t flags = bbox_dif_and_flags >> 24u;
    pnanovdb_uint32_t value_log_bits = flags >> 5; // b = 0, 1, 2, 3, 4 corresponding to 1, 2, 4, 8, 16 bits
    return pnanovdb_leaf_fp_read_float(buf, address, ijk, value_log_bits);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_uint32_countbits(pnanovdb_uint32_t value)
{
#if defined(PNANOVDB_C)
#if defined(_MSC_VER) && (_MSC_VER >= 1928) && defined(PNANOVDB_USE_INTRINSICS)
    return __popcnt(value);
#elif (defined(__GNUC__) || defined(__clang__)) && defined(PNANOVDB_USE_INTRINSICS)
    return __builtin_popcount(value);
#else
    value = value - ((value >> 1) & 0x55555555);
    value = (value & 0x33333333) + ((value >> 2) & 0x33333333);
    value = (value + (value >> 4)) & 0x0F0F0F0F;
    return (value * 0x01010101) >> 24;
#endif
#elif defined(PNANOVDB_HLSL)
    return countbits(value);
#elif defined(PNANOVDB_GLSL)
    return bitCount(value);
#endif
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_count_on_range(pnanovdb_buf_t buf, pnanovdb_leaf_handle_t p, pnanovdb_uint32_t max_index)
{
    pnanovdb_uint32_t mask_idx_max = max_index >> 5u;
    pnanovdb_uint32_t sum = 0u;
    pnanovdb_uint32_t mask_val = 0u;
    for (pnanovdb_uint32_t mask_idx = 0u; mask_idx < mask_idx_max; mask_idx++)
    {
        mask_val = pnanovdb_read_uint32(
            buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_VALUE_MASK + 4u * mask_idx));
        sum += pnanovdb_uint32_countbits(mask_val);
    }
    mask_val = pnanovdb_read_uint32(
        buf, pnanovdb_address_offset(p.address, PNANOVDB_LEAF_OFF_VALUE_MASK + 4u * mask_idx_max));
    sum += pnanovdb_uint32_countbits(mask_val & ((1u << (max_index & 31u)) - 1u));
    return sum;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_uint64_offset(pnanovdb_uint64_t a, pnanovdb_uint32_t b)
{
#if defined(PNANOVDB_ADDRESS_32)
    pnanovdb_uint32_t low = pnanovdb_uint64_low(a);
    pnanovdb_uint32_t high = pnanovdb_uint64_high(a);
    low += b;
    if (low < b)
    {
        high += 1u;
    }
    return pnanovdb_uint32_as_uint64(low, high);
#else
    return a + b;
#endif
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_index_get_min_index(pnanovdb_buf_t buf, pnanovdb_address_t min_address)
{
    return pnanovdb_read_uint64(buf, min_address);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_index_get_max_index(pnanovdb_buf_t buf, pnanovdb_address_t max_address)
{
    return pnanovdb_uint64_offset(pnanovdb_read_uint64(buf, max_address), 1u);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_index_get_ave_index(pnanovdb_buf_t buf, pnanovdb_address_t ave_address)
{
    return pnanovdb_uint64_offset(pnanovdb_read_uint64(buf, ave_address), 2u);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_index_get_dev_index(pnanovdb_buf_t buf, pnanovdb_address_t dev_address)
{
    return pnanovdb_uint64_offset(pnanovdb_read_uint64(buf, dev_address), 3u);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint64_t pnanovdb_leaf_index_get_value_index(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
    pnanovdb_uint32_t bbox_dif_and_flags = pnanovdb_leaf_get_bbox_dif_and_flags(buf, leaf);
    pnanovdb_address_t value_address = pnanovdb_leaf_get_table_address(grid_type, buf, leaf, 0u);
    if ((bbox_dif_and_flags & 0x10000000) != 0u)
    {
        if (pnanovdb_leaf_get_value_mask(buf, leaf, n))
        {
            n = pnanovdb_leaf_count_on_range(buf, leaf, n);
        }
        else
        {
            value_address = pnanovdb_address_null();
            n = 0;
        }
    }
    return pnanovdb_uint64_offset(pnanovdb_read_uint64(buf, value_address), n);
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_lower_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return (((PNANOVDB_DEREF(ijk).x & 127) >> 3) << (2 * 4)) +
           (((PNANOVDB_DEREF(ijk).y & 127) >> 3) << (4)) +
            ((PNANOVDB_DEREF(ijk).z & 127) >> 3);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_min);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_max);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_ave);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_stddev);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_table_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node, pnanovdb_uint32_t n)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, lower_off_table) + PNANOVDB_GRID_TYPE_GET(grid_type, table_stride) * n;
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_lower_get_table_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t node, pnanovdb_uint32_t n)
{
    pnanovdb_address_t table_address = pnanovdb_lower_get_table_address(grid_type, buf, node, n);
    return pnanovdb_read_int64(buf, table_address);
}

PNANOVDB_FORCE_INLINE pnanovdb_leaf_handle_t pnanovdb_lower_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, pnanovdb_uint32_t n)
{
    pnanovdb_leaf_handle_t leaf = { lower.address };
    leaf.address = pnanovdb_address_offset64(leaf.address, pnanovdb_int64_as_uint64(pnanovdb_lower_get_table_child(grid_type, buf, lower, n)));
    return leaf;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
    pnanovdb_address_t value_address;
    if (pnanovdb_lower_get_child_mask(buf, lower, n))
    {
        pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
        value_address = pnanovdb_leaf_get_value_address(grid_type, buf, child, ijk);
        PNANOVDB_DEREF(level) = 0u;
    }
    else
    {
        value_address = pnanovdb_lower_get_table_address(grid_type, buf, lower, n);
        PNANOVDB_DEREF(level) = 1u;
    }
    return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t level;
    return pnanovdb_lower_get_value_address_and_level(grid_type, buf, lower, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_upper_coord_to_offset(PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return (((PNANOVDB_DEREF(ijk).x & 4095) >> 7) << (2 * 5)) +
           (((PNANOVDB_DEREF(ijk).y & 4095) >> 7) << (5)) +
            ((PNANOVDB_DEREF(ijk).z & 4095) >> 7);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_min);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_max);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_ave);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_stddev);
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_table_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node, pnanovdb_uint32_t n)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, upper_off_table) + PNANOVDB_GRID_TYPE_GET(grid_type, table_stride) * n;
    return pnanovdb_address_offset(node.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_int64_t pnanovdb_upper_get_table_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t node, pnanovdb_uint32_t n)
{
    pnanovdb_address_t bufAddress = pnanovdb_upper_get_table_address(grid_type, buf, node, n);
    return pnanovdb_read_int64(buf, bufAddress);
}

PNANOVDB_FORCE_INLINE pnanovdb_lower_handle_t pnanovdb_upper_get_child(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, pnanovdb_uint32_t n)
{
    pnanovdb_lower_handle_t lower = { upper.address };
    lower.address = pnanovdb_address_offset64(lower.address, pnanovdb_int64_as_uint64(pnanovdb_upper_get_table_child(grid_type, buf, upper, n)));
    return lower;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
    pnanovdb_address_t value_address;
    if (pnanovdb_upper_get_child_mask(buf, upper, n))
    {
        pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
        value_address = pnanovdb_lower_get_value_address_and_level(grid_type, buf, child, ijk, level);
    }
    else
    {
        value_address = pnanovdb_upper_get_table_address(grid_type, buf, upper, n);
        PNANOVDB_DEREF(level) = 2u;
    }
    return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t level;
    return pnanovdb_upper_get_value_address_and_level(grid_type, buf, upper, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_min_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_min);
    return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_max_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_max);
    return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_ave_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_ave);
    return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_stddev_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_off_stddev);
    return pnanovdb_address_offset(root.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_tile_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_tile_handle_t root_tile)
{
    pnanovdb_uint32_t byte_offset = PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_off_value);
    return pnanovdb_address_offset(root_tile.address, byte_offset);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
    pnanovdb_address_t ret;
    if (pnanovdb_address_is_null(tile.address))
    {
        ret = pnanovdb_address_offset(root.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_off_background));
        PNANOVDB_DEREF(level) = 4u;
    }
    else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
    {
        ret = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_off_value));
        PNANOVDB_DEREF(level) = 3u;
    }
    else
    {
        pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
        ret = pnanovdb_upper_get_value_address_and_level(grid_type, buf, child, ijk, level);
    }
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t level;
    return pnanovdb_root_get_value_address_and_level(grid_type, buf, root, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_bit(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) bit_index)
{
    pnanovdb_uint32_t level;
    pnanovdb_address_t address = pnanovdb_root_get_value_address_and_level(grid_type, buf, root, ijk, PNANOVDB_REF(level));
    PNANOVDB_DEREF(bit_index) = level == 0u ? pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x & 7) : 0u;
    return address;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fp4_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
    float ret;
    if (level == 0)
    {
        ret = pnanovdb_leaf_fp4_read_float(buf, address, ijk);
    }
    else
    {
        ret = pnanovdb_read_float(buf, address);
    }
    return ret;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fp8_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
    float ret;
    if (level == 0)
    {
        ret = pnanovdb_leaf_fp8_read_float(buf, address, ijk);
    }
    else
    {
        ret = pnanovdb_read_float(buf, address);
    }
    return ret;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fp16_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
    float ret;
    if (level == 0)
    {
        ret = pnanovdb_leaf_fp16_read_float(buf, address, ijk);
    }
    else
    {
        ret = pnanovdb_read_float(buf, address);
    }
    return ret;
}

PNANOVDB_FORCE_INLINE float pnanovdb_root_fpn_read_float(pnanovdb_buf_t buf, pnanovdb_address_t address, PNANOVDB_IN(pnanovdb_coord_t) ijk, pnanovdb_uint32_t level)
{
    float ret;
    if (level == 0)
    {
        ret = pnanovdb_leaf_fpn_read_float(buf, address, ijk);
    }
    else
    {
        ret = pnanovdb_read_float(buf, address);
    }
    return ret;
}

// ------------------------------------------------ ReadAccessor -----------------------------------------------------------

struct pnanovdb_readaccessor_t
{
    pnanovdb_coord_t key;
    pnanovdb_leaf_handle_t leaf;
    pnanovdb_lower_handle_t lower;
    pnanovdb_upper_handle_t upper;
    pnanovdb_root_handle_t root;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_readaccessor_t)

PNANOVDB_FORCE_INLINE void pnanovdb_readaccessor_init(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, pnanovdb_root_handle_t root)
{
    PNANOVDB_DEREF(acc).key.x = 0x7FFFFFFF;
    PNANOVDB_DEREF(acc).key.y = 0x7FFFFFFF;
    PNANOVDB_DEREF(acc).key.z = 0x7FFFFFFF;
    PNANOVDB_DEREF(acc).leaf.address = pnanovdb_address_null();
    PNANOVDB_DEREF(acc).lower.address = pnanovdb_address_null();
    PNANOVDB_DEREF(acc).upper.address = pnanovdb_address_null();
    PNANOVDB_DEREF(acc).root = root;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_iscached0(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, int dirty)
{
    if (pnanovdb_address_is_null(PNANOVDB_DEREF(acc).leaf.address)) { return PNANOVDB_FALSE; }
    if ((dirty & ~((1u << 3) - 1u)) != 0)
    {
        PNANOVDB_DEREF(acc).leaf.address = pnanovdb_address_null();
        return PNANOVDB_FALSE;
    }
    return PNANOVDB_TRUE;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_iscached1(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, int dirty)
{
    if (pnanovdb_address_is_null(PNANOVDB_DEREF(acc).lower.address)) { return PNANOVDB_FALSE; }
    if ((dirty & ~((1u << 7) - 1u)) != 0)
    {
        PNANOVDB_DEREF(acc).lower.address = pnanovdb_address_null();
        return PNANOVDB_FALSE;
    }
    return PNANOVDB_TRUE;
}
PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_iscached2(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, int dirty)
{
    if (pnanovdb_address_is_null(PNANOVDB_DEREF(acc).upper.address)) { return PNANOVDB_FALSE; }
    if ((dirty & ~((1u << 12) - 1u)) != 0)
    {
        PNANOVDB_DEREF(acc).upper.address = pnanovdb_address_null();
        return PNANOVDB_FALSE;
    }
    return PNANOVDB_TRUE;
}
PNANOVDB_FORCE_INLINE int pnanovdb_readaccessor_computedirty(PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    return (PNANOVDB_DEREF(ijk).x ^ PNANOVDB_DEREF(acc).key.x) | (PNANOVDB_DEREF(ijk).y ^ PNANOVDB_DEREF(acc).key.y) | (PNANOVDB_DEREF(ijk).z ^ PNANOVDB_DEREF(acc).key.z);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_leaf_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
    return pnanovdb_leaf_get_table_address(grid_type, buf, leaf, n);
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
    pnanovdb_address_t value_address;
    if (pnanovdb_lower_get_child_mask(buf, lower, n))
    {
        pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
        PNANOVDB_DEREF(acc).leaf = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        value_address = pnanovdb_leaf_get_value_address_and_cache(grid_type, buf, child, ijk, acc);
        PNANOVDB_DEREF(level) = 0u;
    }
    else
    {
        value_address = pnanovdb_lower_get_table_address(grid_type, buf, lower, n);
        PNANOVDB_DEREF(level) = 1u;
    }
    return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_lower_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t level;
    return pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, lower, ijk, acc, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
    pnanovdb_address_t value_address;
    if (pnanovdb_upper_get_child_mask(buf, upper, n))
    {
        pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
        PNANOVDB_DEREF(acc).lower = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        value_address = pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, child, ijk, acc, level);
    }
    else
    {
        value_address = pnanovdb_upper_get_table_address(grid_type, buf, upper, n);
        PNANOVDB_DEREF(level) = 2u;
    }
    return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_upper_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t level;
    return pnanovdb_upper_get_value_address_and_level_and_cache(grid_type, buf, upper, ijk, acc, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_and_level_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
    pnanovdb_address_t ret;
    if (pnanovdb_address_is_null(tile.address))
    {
        ret = pnanovdb_address_offset(root.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_off_background));
        PNANOVDB_DEREF(level) = 4u;
    }
    else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
    {
        ret = pnanovdb_address_offset(tile.address, PNANOVDB_GRID_TYPE_GET(grid_type, root_tile_off_value));
        PNANOVDB_DEREF(level) = 3u;
    }
    else
    {
        pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
        PNANOVDB_DEREF(acc).upper = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        ret = pnanovdb_upper_get_value_address_and_level_and_cache(grid_type, buf, child, ijk, acc, level);
    }
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_root_get_value_address_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t level;
    return pnanovdb_root_get_value_address_and_level_and_cache(grid_type, buf, root, ijk, acc, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_readaccessor_get_value_address_and_level(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) level)
{
    int dirty = pnanovdb_readaccessor_computedirty(acc, ijk);

    pnanovdb_address_t value_address;
    if (pnanovdb_readaccessor_iscached0(acc, dirty))
    {
        value_address = pnanovdb_leaf_get_value_address_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).leaf, ijk, acc);
        PNANOVDB_DEREF(level) = 0u;
    }
    else if (pnanovdb_readaccessor_iscached1(acc, dirty))
    {
        value_address = pnanovdb_lower_get_value_address_and_level_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).lower, ijk, acc, level);
    }
    else if (pnanovdb_readaccessor_iscached2(acc, dirty))
    {
        value_address = pnanovdb_upper_get_value_address_and_level_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).upper, ijk, acc, level);
    }
    else
    {
        value_address = pnanovdb_root_get_value_address_and_level_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).root, ijk, acc, level);
    }
    return value_address;
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_readaccessor_get_value_address(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    pnanovdb_uint32_t level;
    return pnanovdb_readaccessor_get_value_address_and_level(grid_type, buf, acc, ijk, PNANOVDB_REF(level));
}

PNANOVDB_FORCE_INLINE pnanovdb_address_t pnanovdb_readaccessor_get_value_address_bit(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_uint32_t) bit_index)
{
    pnanovdb_uint32_t level;
    pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address_and_level(grid_type, buf, acc, ijk, PNANOVDB_REF(level));
    PNANOVDB_DEREF(bit_index) = level == 0u ? pnanovdb_int32_as_uint32(PNANOVDB_DEREF(ijk).x & 7) : 0u;
    return address;
}

// ------------------------------------------------ ReadAccessor GetDim -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_leaf_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    return 1u;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_lower_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
    pnanovdb_uint32_t ret;
    if (pnanovdb_lower_get_child_mask(buf, lower, n))
    {
        pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
        PNANOVDB_DEREF(acc).leaf = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        ret = pnanovdb_leaf_get_dim_and_cache(grid_type, buf, child, ijk, acc);
    }
    else
    {
        ret = (1u << (3u)); // node 0 dim
    }
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_upper_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
    pnanovdb_uint32_t ret;
    if (pnanovdb_upper_get_child_mask(buf, upper, n))
    {
        pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
        PNANOVDB_DEREF(acc).lower = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        ret = pnanovdb_lower_get_dim_and_cache(grid_type, buf, child, ijk, acc);
    }
    else
    {
        ret = (1u << (4u + 3u)); // node 1 dim
    }
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_root_get_dim_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
    pnanovdb_uint32_t ret;
    if (pnanovdb_address_is_null(tile.address))
    {
        ret = 1u << (5u + 4u + 3u); // background, node 2 dim
    }
    else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
    {
        ret = 1u << (5u + 4u + 3u); // tile value, node 2 dim
    }
    else
    {
        pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
        PNANOVDB_DEREF(acc).upper = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        ret = pnanovdb_upper_get_dim_and_cache(grid_type, buf, child, ijk, acc);
    }
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_uint32_t pnanovdb_readaccessor_get_dim(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    int dirty = pnanovdb_readaccessor_computedirty(acc, ijk);

    pnanovdb_uint32_t dim;
    if (pnanovdb_readaccessor_iscached0(acc, dirty))
    {
        dim = pnanovdb_leaf_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).leaf, ijk, acc);
    }
    else if (pnanovdb_readaccessor_iscached1(acc, dirty))
    {
        dim = pnanovdb_lower_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).lower, ijk, acc);
    }
    else if (pnanovdb_readaccessor_iscached2(acc, dirty))
    {
        dim = pnanovdb_upper_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).upper, ijk, acc);
    }
    else
    {
        dim = pnanovdb_root_get_dim_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).root, ijk, acc);
    }
    return dim;
}

// ------------------------------------------------ ReadAccessor IsActive -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_leaf_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_leaf_handle_t leaf, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t n = pnanovdb_leaf_coord_to_offset(ijk);
    return pnanovdb_leaf_get_value_mask(buf, leaf, n);
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_lower_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_lower_handle_t lower, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t n = pnanovdb_lower_coord_to_offset(ijk);
    pnanovdb_bool_t is_active;
    if (pnanovdb_lower_get_child_mask(buf, lower, n))
    {
        pnanovdb_leaf_handle_t child = pnanovdb_lower_get_child(grid_type, buf, lower, n);
        PNANOVDB_DEREF(acc).leaf = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        is_active = pnanovdb_leaf_is_active_and_cache(grid_type, buf, child, ijk, acc);
    }
    else
    {
        is_active = pnanovdb_lower_get_value_mask(buf, lower, n);
    }
    return is_active;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_upper_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_upper_handle_t upper, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_uint32_t n = pnanovdb_upper_coord_to_offset(ijk);
    pnanovdb_bool_t is_active;
    if (pnanovdb_upper_get_child_mask(buf, upper, n))
    {
        pnanovdb_lower_handle_t child = pnanovdb_upper_get_child(grid_type, buf, upper, n);
        PNANOVDB_DEREF(acc).lower = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        is_active = pnanovdb_lower_is_active_and_cache(grid_type, buf, child, ijk, acc);
    }
    else
    {
        is_active = pnanovdb_upper_get_value_mask(buf, upper, n);
    }
    return is_active;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_root_is_active_and_cache(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, pnanovdb_root_handle_t root, PNANOVDB_IN(pnanovdb_coord_t) ijk, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc)
{
    pnanovdb_root_tile_handle_t tile = pnanovdb_root_find_tile(grid_type, buf, root, ijk);
    pnanovdb_bool_t is_active;
    if (pnanovdb_address_is_null(tile.address))
    {
        is_active = PNANOVDB_FALSE; // background
    }
    else if (pnanovdb_int64_is_zero(pnanovdb_root_tile_get_child(buf, tile)))
    {
        pnanovdb_uint32_t state = pnanovdb_root_tile_get_state(buf, tile);
        is_active = state != 0u; // tile value
    }
    else
    {
        pnanovdb_upper_handle_t child = pnanovdb_root_get_child(grid_type, buf, root, tile);
        PNANOVDB_DEREF(acc).upper = child;
        PNANOVDB_DEREF(acc).key = PNANOVDB_DEREF(ijk);
        is_active = pnanovdb_upper_is_active_and_cache(grid_type, buf, child, ijk, acc);
    }
    return is_active;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_readaccessor_is_active(pnanovdb_grid_type_t grid_type, pnanovdb_buf_t buf, PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc, PNANOVDB_IN(pnanovdb_coord_t) ijk)
{
    int dirty = pnanovdb_readaccessor_computedirty(acc, ijk);

    pnanovdb_bool_t is_active;
    if (pnanovdb_readaccessor_iscached0(acc, dirty))
    {
        is_active = pnanovdb_leaf_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).leaf, ijk, acc);
    }
    else if (pnanovdb_readaccessor_iscached1(acc, dirty))
    {
        is_active = pnanovdb_lower_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).lower, ijk, acc);
    }
    else if (pnanovdb_readaccessor_iscached2(acc, dirty))
    {
        is_active = pnanovdb_upper_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).upper, ijk, acc);
    }
    else
    {
        is_active = pnanovdb_root_is_active_and_cache(grid_type, buf, PNANOVDB_DEREF(acc).root, ijk, acc);
    }
    return is_active;
}

// ------------------------------------------------ Map Transforms -----------------------------------------------------------

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_vec3_t  dst;
    float sx = PNANOVDB_DEREF(src).x;
    float sy = PNANOVDB_DEREF(src).y;
    float sz = PNANOVDB_DEREF(src).z;
    dst.x = sx * pnanovdb_map_get_matf(buf, map, 0) + sy * pnanovdb_map_get_matf(buf, map, 1) + sz * pnanovdb_map_get_matf(buf, map, 2) + pnanovdb_map_get_vecf(buf, map, 0);
    dst.y = sx * pnanovdb_map_get_matf(buf, map, 3) + sy * pnanovdb_map_get_matf(buf, map, 4) + sz * pnanovdb_map_get_matf(buf, map, 5) + pnanovdb_map_get_vecf(buf, map, 1);
    dst.z = sx * pnanovdb_map_get_matf(buf, map, 6) + sy * pnanovdb_map_get_matf(buf, map, 7) + sz * pnanovdb_map_get_matf(buf, map, 8) + pnanovdb_map_get_vecf(buf, map, 2);
    return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply_inverse(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_vec3_t  dst;
    float sx = PNANOVDB_DEREF(src).x - pnanovdb_map_get_vecf(buf, map, 0);
    float sy = PNANOVDB_DEREF(src).y - pnanovdb_map_get_vecf(buf, map, 1);
    float sz = PNANOVDB_DEREF(src).z - pnanovdb_map_get_vecf(buf, map, 2);
    dst.x = sx * pnanovdb_map_get_invmatf(buf, map, 0) + sy * pnanovdb_map_get_invmatf(buf, map, 1) + sz * pnanovdb_map_get_invmatf(buf, map, 2);
    dst.y = sx * pnanovdb_map_get_invmatf(buf, map, 3) + sy * pnanovdb_map_get_invmatf(buf, map, 4) + sz * pnanovdb_map_get_invmatf(buf, map, 5);
    dst.z = sx * pnanovdb_map_get_invmatf(buf, map, 6) + sy * pnanovdb_map_get_invmatf(buf, map, 7) + sz * pnanovdb_map_get_invmatf(buf, map, 8);
    return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply_jacobi(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_vec3_t  dst;
    float sx = PNANOVDB_DEREF(src).x;
    float sy = PNANOVDB_DEREF(src).y;
    float sz = PNANOVDB_DEREF(src).z;
    dst.x = sx * pnanovdb_map_get_matf(buf, map, 0) + sy * pnanovdb_map_get_matf(buf, map, 1) + sz * pnanovdb_map_get_matf(buf, map, 2);
    dst.y = sx * pnanovdb_map_get_matf(buf, map, 3) + sy * pnanovdb_map_get_matf(buf, map, 4) + sz * pnanovdb_map_get_matf(buf, map, 5);
    dst.z = sx * pnanovdb_map_get_matf(buf, map, 6) + sy * pnanovdb_map_get_matf(buf, map, 7) + sz * pnanovdb_map_get_matf(buf, map, 8);
    return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_map_apply_inverse_jacobi(pnanovdb_buf_t buf, pnanovdb_map_handle_t map, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_vec3_t  dst;
    float sx = PNANOVDB_DEREF(src).x;
    float sy = PNANOVDB_DEREF(src).y;
    float sz = PNANOVDB_DEREF(src).z;
    dst.x = sx * pnanovdb_map_get_invmatf(buf, map, 0) + sy * pnanovdb_map_get_invmatf(buf, map, 1) + sz * pnanovdb_map_get_invmatf(buf, map, 2);
    dst.y = sx * pnanovdb_map_get_invmatf(buf, map, 3) + sy * pnanovdb_map_get_invmatf(buf, map, 4) + sz * pnanovdb_map_get_invmatf(buf, map, 5);
    dst.z = sx * pnanovdb_map_get_invmatf(buf, map, 6) + sy * pnanovdb_map_get_invmatf(buf, map, 7) + sz * pnanovdb_map_get_invmatf(buf, map, 8);
    return dst;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_world_to_indexf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
    return pnanovdb_map_apply_inverse(buf, map, src);
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_index_to_worldf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
    return pnanovdb_map_apply(buf, map, src);
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_world_to_index_dirf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
    return pnanovdb_map_apply_inverse_jacobi(buf, map, src);
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_grid_index_to_world_dirf(pnanovdb_buf_t buf, pnanovdb_grid_handle_t grid, PNANOVDB_IN(pnanovdb_vec3_t) src)
{
    pnanovdb_map_handle_t map = pnanovdb_grid_get_map(buf, grid);
    return pnanovdb_map_apply_jacobi(buf, map, src);
}

// ------------------------------------------------ DitherLUT -----------------------------------------------------------

// This table was generated with
/**************

static constexpr inline uint32
SYSwang_inthash(uint32 key)
{
    // From http://www.concentric.net/~Ttwang/tech/inthash.htm
    key += ~(key << 16);
    key ^=  (key >> 5);
    key +=  (key << 3);
    key ^=  (key >> 13);
    key += ~(key << 9);
    key ^=  (key >> 17);
    return key;
}

static void
ut_initDitherR(float *pattern, float offset,
    int x, int y, int z, int res, int goalres)
{
    // These offsets are designed to maximize the difference between
    // dither values in nearby voxels within a given 2x2x2 cell, without
    // producing axis-aligned artifacts.  The are organized in row-major
    // order.
    static const float  theDitherOffset[] = {0,4,6,2,5,1,3,7};
    static const float  theScale = 0.125F;
    int         key = (((z << res) + y) << res) + x;

    if (res == goalres)
    {
    pattern[key] = offset;
    return;
    }

    // Randomly flip (on each axis) the dithering patterns used by the
    // subcells.  This key is xor'd with the subcell index below before
    // looking up in the dither offset list.
    key = SYSwang_inthash(key) & 7;

    x <<= 1;
    y <<= 1;
    z <<= 1;

    offset *= theScale;
    for (int i = 0; i < 8; i++)
    ut_initDitherR(pattern, offset+theDitherOffset[i ^ key]*theScale,
        x+(i&1), y+((i&2)>>1), z+((i&4)>>2), res+1, goalres);
}

// This is a compact algorithm that accomplishes essentially the same thing
// as ut_initDither() above.  We should eventually switch to use this and
// clean the dead code.
static fpreal32 *
ut_initDitherRecursive(int goalres)
{
    const int nfloat = 1 << (goalres*3);
    float   *pattern = new float[nfloat];
    ut_initDitherR(pattern, 1.0F, 0, 0, 0, 0, goalres);

    // This has built an even spacing from 1/nfloat to 1.0.
    // however, our dither pattern should be 1/(nfloat+1) to nfloat/(nfloat+1)
    // So we do a correction here.  Note that the earlier calculations are
    // done with powers of 2 so are exact, so it does make sense to delay
    // the renormalization to this pass.
    float correctionterm = nfloat / (nfloat+1.0F);
    for (int i = 0; i < nfloat; i++)
        pattern[i] *= correctionterm;
    return pattern;
}

    theDitherMatrix = ut_initDitherRecursive(3);

    for (int i = 0; i < 512/8; i ++)
    {
        for (int j = 0; j < 8; j ++)
            std::cout << theDitherMatrix[i*8+j] << "f, ";
        std::cout << std::endl;
    }

 **************/

PNANOVDB_STATIC_CONST float pnanovdb_dither_lut[512] =
{
    0.14425f, 0.643275f, 0.830409f, 0.331384f, 0.105263f, 0.604289f, 0.167641f, 0.666667f,
    0.892788f, 0.393762f, 0.0818713f, 0.580897f, 0.853801f, 0.354776f, 0.916179f, 0.417154f,
    0.612086f, 0.11306f, 0.79922f, 0.300195f, 0.510721f, 0.0116959f, 0.947368f, 0.448343f,
    0.362573f, 0.861598f, 0.0506823f, 0.549708f, 0.261209f, 0.760234f, 0.19883f, 0.697856f,
    0.140351f, 0.639376f, 0.576998f, 0.0779727f, 0.522417f, 0.0233918f, 0.460039f, 0.959064f,
    0.888889f, 0.389864f, 0.327485f, 0.826511f, 0.272904f, 0.77193f, 0.709552f, 0.210526f,
    0.483431f, 0.982456f, 0.296296f, 0.795322f, 0.116959f, 0.615984f, 0.0545809f, 0.553606f,
    0.732943f, 0.233918f, 0.545809f, 0.0467836f, 0.865497f, 0.366472f, 0.803119f, 0.304094f,
    0.518519f, 0.0194932f, 0.45614f, 0.955166f, 0.729045f, 0.230019f, 0.54191f, 0.042885f,
    0.269006f, 0.768031f, 0.705653f, 0.206628f, 0.479532f, 0.978558f, 0.292398f, 0.791423f,
    0.237817f, 0.736842f, 0.424951f, 0.923977f, 0.136452f, 0.635478f, 0.323587f, 0.822612f,
    0.986355f, 0.487329f, 0.674464f, 0.175439f, 0.88499f, 0.385965f, 0.573099f, 0.0740741f,
    0.51462f, 0.0155945f, 0.202729f, 0.701754f, 0.148148f, 0.647174f, 0.834308f, 0.335283f,
    0.265107f, 0.764133f, 0.951267f, 0.452242f, 0.896686f, 0.397661f, 0.08577f, 0.584795f,
    0.8577f, 0.358674f, 0.920078f, 0.421053f, 0.740741f, 0.241715f, 0.678363f, 0.179337f,
    0.109162f, 0.608187f, 0.17154f, 0.670565f, 0.491228f, 0.990253f, 0.42885f, 0.927875f,
    0.0662768f, 0.565302f, 0.62768f, 0.128655f, 0.183236f, 0.682261f, 0.744639f, 0.245614f,
    0.814815f, 0.315789f, 0.378168f, 0.877193f, 0.931774f, 0.432749f, 0.495127f, 0.994152f,
    0.0350877f, 0.534113f, 0.97076f, 0.471735f, 0.214425f, 0.71345f, 0.526316f, 0.0272904f,
    0.783626f, 0.2846f, 0.222222f, 0.721248f, 0.962963f, 0.463938f, 0.276803f, 0.775828f,
    0.966862f, 0.467836f, 0.405458f, 0.904483f, 0.0701754f, 0.569201f, 0.881092f, 0.382066f,
    0.218324f, 0.717349f, 0.654971f, 0.155945f, 0.818713f, 0.319688f, 0.132554f, 0.631579f,
    0.0623782f, 0.561404f, 0.748538f, 0.249513f, 0.912281f, 0.413255f, 0.974659f, 0.475634f,
    0.810916f, 0.311891f, 0.499025f, 0.998051f, 0.163743f, 0.662768f, 0.226121f, 0.725146f,
    0.690058f, 0.191033f, 0.00389864f, 0.502924f, 0.557505f, 0.0584795f, 0.120858f, 0.619883f,
    0.440546f, 0.939571f, 0.752437f, 0.253411f, 0.307992f, 0.807018f, 0.869396f, 0.37037f,
    0.658869f, 0.159844f, 0.346979f, 0.846004f, 0.588694f, 0.0896686f, 0.152047f, 0.651072f,
    0.409357f, 0.908382f, 0.596491f, 0.0974659f, 0.339181f, 0.838207f, 0.900585f, 0.401559f,
    0.34308f, 0.842105f, 0.779727f, 0.280702f, 0.693957f, 0.194932f, 0.25731f, 0.756335f,
    0.592593f, 0.0935673f, 0.0311891f, 0.530214f, 0.444444f, 0.94347f, 0.506823f, 0.00779727f,
    0.68616f, 0.187135f, 0.124756f, 0.623782f, 0.288499f, 0.787524f, 0.350877f, 0.849903f,
    0.436647f, 0.935673f, 0.873294f, 0.374269f, 0.538012f, 0.0389864f, 0.60039f, 0.101365f,
    0.57115f, 0.0721248f, 0.758285f, 0.259259f, 0.719298f, 0.220273f, 0.532164f, 0.0331384f,
    0.321637f, 0.820663f, 0.00974659f, 0.508772f, 0.469786f, 0.968811f, 0.282651f, 0.781676f,
    0.539961f, 0.0409357f, 0.727096f, 0.22807f, 0.500975f, 0.00194932f, 0.563353f, 0.0643275f,
    0.290448f, 0.789474f, 0.477583f, 0.976608f, 0.251462f, 0.750487f, 0.31384f, 0.812865f,
    0.94152f, 0.442495f, 0.879142f, 0.380117f, 0.37232f, 0.871345f, 0.309942f, 0.808967f,
    0.192982f, 0.692008f, 0.130604f, 0.62963f, 0.621832f, 0.122807f, 0.559454f, 0.0604289f,
    0.660819f, 0.161793f, 0.723197f, 0.224172f, 0.403509f, 0.902534f, 0.840156f, 0.341131f,
    0.411306f, 0.910331f, 0.473684f, 0.97271f, 0.653021f, 0.153996f, 0.0916179f, 0.590643f,
    0.196881f, 0.695906f, 0.384016f, 0.883041f, 0.0955166f, 0.594542f, 0.157895f, 0.65692f,
    0.945419f, 0.446394f, 0.633528f, 0.134503f, 0.844055f, 0.345029f, 0.906433f, 0.407407f,
    0.165692f, 0.664717f, 0.103314f, 0.602339f, 0.126706f, 0.625731f, 0.189084f, 0.688109f,
    0.91423f, 0.415205f, 0.851852f, 0.352827f, 0.875244f, 0.376218f, 0.937622f, 0.438596f,
    0.317739f, 0.816764f, 0.255361f, 0.754386f, 0.996101f, 0.497076f, 0.933723f, 0.434698f,
    0.567251f, 0.0682261f, 0.504873f, 0.00584795f, 0.247563f, 0.746589f, 0.185185f, 0.684211f,
    0.037037f, 0.536062f, 0.0994152f, 0.598441f, 0.777778f, 0.278752f, 0.465887f, 0.964912f,
    0.785575f, 0.28655f, 0.847953f, 0.348928f, 0.0292398f, 0.528265f, 0.7154f, 0.216374f,
    0.39961f, 0.898636f, 0.961014f, 0.461988f, 0.0487329f, 0.547758f, 0.111111f, 0.610136f,
    0.649123f, 0.150097f, 0.212476f, 0.711501f, 0.797271f, 0.298246f, 0.859649f, 0.360624f,
    0.118908f, 0.617934f, 0.0565302f, 0.555556f, 0.329435f, 0.82846f, 0.516569f, 0.0175439f,
    0.867446f, 0.368421f, 0.805068f, 0.306043f, 0.578947f, 0.079922f, 0.267057f, 0.766082f,
    0.270955f, 0.76998f, 0.707602f, 0.208577f, 0.668616f, 0.169591f, 0.606238f, 0.107212f,
    0.520468f, 0.0214425f, 0.45809f, 0.957115f, 0.419103f, 0.918129f, 0.356725f, 0.855751f,
    0.988304f, 0.489279f, 0.426901f, 0.925926f, 0.450292f, 0.949318f, 0.512671f, 0.0136452f,
    0.239766f, 0.738791f, 0.676413f, 0.177388f, 0.699805f, 0.20078f, 0.263158f, 0.762183f,
    0.773879f, 0.274854f, 0.337232f, 0.836257f, 0.672515f, 0.173489f, 0.734893f, 0.235867f,
    0.0253411f, 0.524366f, 0.586745f, 0.0877193f, 0.423002f, 0.922027f, 0.48538f, 0.984405f,
    0.74269f, 0.243665f, 0.680312f, 0.181287f, 0.953216f, 0.454191f, 0.1423f, 0.641326f,
    0.493177f, 0.992203f, 0.430799f, 0.929825f, 0.204678f, 0.703704f, 0.890838f, 0.391813f,
    0.894737f, 0.395712f, 0.0838207f, 0.582846f, 0.0448343f, 0.54386f, 0.231969f, 0.730994f,
    0.146199f, 0.645224f, 0.832359f, 0.333333f, 0.793372f, 0.294347f, 0.980507f, 0.481481f,
    0.364522f, 0.863548f, 0.80117f, 0.302144f, 0.824561f, 0.325536f, 0.138402f, 0.637427f,
    0.614035f, 0.11501f, 0.0526316f, 0.551657f, 0.0760234f, 0.575049f, 0.88694f, 0.387914f,
};

PNANOVDB_FORCE_INLINE float pnanovdb_dither_lookup(pnanovdb_bool_t enabled, int offset)
{
    return enabled ? pnanovdb_dither_lut[offset & 511] : 0.5f;
}

// ------------------------------------------------ HDDA -----------------------------------------------------------

#ifdef PNANOVDB_HDDA

// Comment out to disable this explicit round-off check
#define PNANOVDB_ENFORCE_FORWARD_STEPPING

#define PNANOVDB_HDDA_FLOAT_MAX 1e38f

struct pnanovdb_hdda_t
{
    pnanovdb_int32_t dim;
    float tmin;
    float tmax;
    pnanovdb_coord_t voxel;
    pnanovdb_coord_t step;
    pnanovdb_vec3_t delta;
    pnanovdb_vec3_t next;
};
PNANOVDB_STRUCT_TYPEDEF(pnanovdb_hdda_t)

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_hdda_pos_to_ijk(PNANOVDB_IN(pnanovdb_vec3_t) pos)
{
    pnanovdb_coord_t voxel;
    voxel.x = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).x));
    voxel.y = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).y));
    voxel.z = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).z));
    return voxel;
}

PNANOVDB_FORCE_INLINE pnanovdb_coord_t pnanovdb_hdda_pos_to_voxel(PNANOVDB_IN(pnanovdb_vec3_t) pos, int dim)
{
    pnanovdb_coord_t voxel;
    voxel.x = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).x)) & (~(dim - 1));
    voxel.y = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).y)) & (~(dim - 1));
    voxel.z = pnanovdb_float_to_int32(pnanovdb_floor(PNANOVDB_DEREF(pos).z)) & (~(dim - 1));
    return voxel;
}

PNANOVDB_FORCE_INLINE pnanovdb_vec3_t pnanovdb_hdda_ray_start(PNANOVDB_IN(pnanovdb_vec3_t) origin, float tmin, PNANOVDB_IN(pnanovdb_vec3_t) direction)
{
    pnanovdb_vec3_t pos = pnanovdb_vec3_add(
        pnanovdb_vec3_mul(PNANOVDB_DEREF(direction), pnanovdb_vec3_uniform(tmin)),
        PNANOVDB_DEREF(origin)
    );
    return pos;
}

PNANOVDB_FORCE_INLINE void pnanovdb_hdda_init(PNANOVDB_INOUT(pnanovdb_hdda_t) hdda, PNANOVDB_IN(pnanovdb_vec3_t) origin, float tmin, PNANOVDB_IN(pnanovdb_vec3_t) direction, float tmax, int dim)
{
    PNANOVDB_DEREF(hdda).dim = dim;
    PNANOVDB_DEREF(hdda).tmin = tmin;
    PNANOVDB_DEREF(hdda).tmax = tmax;

    pnanovdb_vec3_t pos = pnanovdb_hdda_ray_start(origin, tmin, direction);
    pnanovdb_vec3_t dir_inv = pnanovdb_vec3_div(pnanovdb_vec3_uniform(1.f), PNANOVDB_DEREF(direction));

    PNANOVDB_DEREF(hdda).voxel = pnanovdb_hdda_pos_to_voxel(PNANOVDB_REF(pos), dim);

    // x
    if (PNANOVDB_DEREF(direction).x == 0.f)
    {
        PNANOVDB_DEREF(hdda).next.x = PNANOVDB_HDDA_FLOAT_MAX;
        PNANOVDB_DEREF(hdda).step.x = 0;
        PNANOVDB_DEREF(hdda).delta.x = 0.f;
    }
    else if (dir_inv.x > 0.f)
    {
        PNANOVDB_DEREF(hdda).step.x = 1;
        PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x + dim - pos.x) * dir_inv.x;
        PNANOVDB_DEREF(hdda).delta.x = dir_inv.x;
    }
    else
    {
        PNANOVDB_DEREF(hdda).step.x = -1;
        PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x - pos.x) * dir_inv.x;
        PNANOVDB_DEREF(hdda).delta.x = -dir_inv.x;
    }

    // y
    if (PNANOVDB_DEREF(direction).y == 0.f)
    {
        PNANOVDB_DEREF(hdda).next.y = PNANOVDB_HDDA_FLOAT_MAX;
        PNANOVDB_DEREF(hdda).step.y = 0;
        PNANOVDB_DEREF(hdda).delta.y = 0.f;
    }
    else if (dir_inv.y > 0.f)
    {
        PNANOVDB_DEREF(hdda).step.y = 1;
        PNANOVDB_DEREF(hdda).next.y = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.y + dim - pos.y) * dir_inv.y;
        PNANOVDB_DEREF(hdda).delta.y = dir_inv.y;
    }
    else
    {
        PNANOVDB_DEREF(hdda).step.y = -1;
        PNANOVDB_DEREF(hdda).next.y = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.y - pos.y) * dir_inv.y;
        PNANOVDB_DEREF(hdda).delta.y = -dir_inv.y;
    }

    // z
    if (PNANOVDB_DEREF(direction).z == 0.f)
    {
        PNANOVDB_DEREF(hdda).next.z = PNANOVDB_HDDA_FLOAT_MAX;
        PNANOVDB_DEREF(hdda).step.z = 0;
        PNANOVDB_DEREF(hdda).delta.z = 0.f;
    }
    else if (dir_inv.z > 0.f)
    {
        PNANOVDB_DEREF(hdda).step.z = 1;
        PNANOVDB_DEREF(hdda).next.z = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.z + dim - pos.z) * dir_inv.z;
        PNANOVDB_DEREF(hdda).delta.z = dir_inv.z;
    }
    else
    {
        PNANOVDB_DEREF(hdda).step.z = -1;
        PNANOVDB_DEREF(hdda).next.z = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.z - pos.z) * dir_inv.z;
        PNANOVDB_DEREF(hdda).delta.z = -dir_inv.z;
    }
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_update(PNANOVDB_INOUT(pnanovdb_hdda_t) hdda, PNANOVDB_IN(pnanovdb_vec3_t) origin, PNANOVDB_IN(pnanovdb_vec3_t) direction, int dim)
{
    if (PNANOVDB_DEREF(hdda).dim == dim)
    {
        return PNANOVDB_FALSE;
    }
    PNANOVDB_DEREF(hdda).dim = dim;

    pnanovdb_vec3_t pos = pnanovdb_vec3_add(
        pnanovdb_vec3_mul(PNANOVDB_DEREF(direction), pnanovdb_vec3_uniform(PNANOVDB_DEREF(hdda).tmin)),
        PNANOVDB_DEREF(origin)
    );
    pnanovdb_vec3_t dir_inv = pnanovdb_vec3_div(pnanovdb_vec3_uniform(1.f), PNANOVDB_DEREF(direction));

    PNANOVDB_DEREF(hdda).voxel = pnanovdb_hdda_pos_to_voxel(PNANOVDB_REF(pos), dim);

    if (PNANOVDB_DEREF(hdda).step.x != 0)
    {
        PNANOVDB_DEREF(hdda).next.x = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.x - pos.x) * dir_inv.x;
        if (PNANOVDB_DEREF(hdda).step.x > 0)
        {
            PNANOVDB_DEREF(hdda).next.x += dim * dir_inv.x;
        }
    }
    if (PNANOVDB_DEREF(hdda).step.y != 0)
    {
        PNANOVDB_DEREF(hdda).next.y = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.y - pos.y) * dir_inv.y;
        if (PNANOVDB_DEREF(hdda).step.y > 0)
        {
            PNANOVDB_DEREF(hdda).next.y += dim * dir_inv.y;
        }
    }
    if (PNANOVDB_DEREF(hdda).step.z != 0)
    {
        PNANOVDB_DEREF(hdda).next.z = PNANOVDB_DEREF(hdda).tmin + (PNANOVDB_DEREF(hdda).voxel.z - pos.z) * dir_inv.z;
        if (PNANOVDB_DEREF(hdda).step.z > 0)
        {
            PNANOVDB_DEREF(hdda).next.z += dim * dir_inv.z;
        }
    }

    return PNANOVDB_TRUE;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_step(PNANOVDB_INOUT(pnanovdb_hdda_t) hdda)
{
    pnanovdb_bool_t ret;
    if (PNANOVDB_DEREF(hdda).next.x < PNANOVDB_DEREF(hdda).next.y && PNANOVDB_DEREF(hdda).next.x < PNANOVDB_DEREF(hdda).next.z)
    {
#ifdef PNANOVDB_ENFORCE_FORWARD_STEPPING
        if (PNANOVDB_DEREF(hdda).next.x <= PNANOVDB_DEREF(hdda).tmin)
        {
            PNANOVDB_DEREF(hdda).next.x += PNANOVDB_DEREF(hdda).tmin - 0.999999f * PNANOVDB_DEREF(hdda).next.x + 1.0e-6f;
        }
#endif
        PNANOVDB_DEREF(hdda).tmin = PNANOVDB_DEREF(hdda).next.x;
        PNANOVDB_DEREF(hdda).next.x += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).delta.x;
        PNANOVDB_DEREF(hdda).voxel.x += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).step.x;
        ret = PNANOVDB_DEREF(hdda).tmin <= PNANOVDB_DEREF(hdda).tmax;
    }
    else if (PNANOVDB_DEREF(hdda).next.y < PNANOVDB_DEREF(hdda).next.z)
    {
#ifdef PNANOVDB_ENFORCE_FORWARD_STEPPING
        if (PNANOVDB_DEREF(hdda).next.y <= PNANOVDB_DEREF(hdda).tmin)
        {
            PNANOVDB_DEREF(hdda).next.y += PNANOVDB_DEREF(hdda).tmin - 0.999999f * PNANOVDB_DEREF(hdda).next.y + 1.0e-6f;
        }
#endif
        PNANOVDB_DEREF(hdda).tmin = PNANOVDB_DEREF(hdda).next.y;
        PNANOVDB_DEREF(hdda).next.y += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).delta.y;
        PNANOVDB_DEREF(hdda).voxel.y += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).step.y;
        ret = PNANOVDB_DEREF(hdda).tmin <= PNANOVDB_DEREF(hdda).tmax;
    }
    else
    {
#ifdef PNANOVDB_ENFORCE_FORWARD_STEPPING
        if (PNANOVDB_DEREF(hdda).next.z <= PNANOVDB_DEREF(hdda).tmin)
        {
            PNANOVDB_DEREF(hdda).next.z += PNANOVDB_DEREF(hdda).tmin - 0.999999f * PNANOVDB_DEREF(hdda).next.z + 1.0e-6f;
        }
#endif
        PNANOVDB_DEREF(hdda).tmin = PNANOVDB_DEREF(hdda).next.z;
        PNANOVDB_DEREF(hdda).next.z += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).delta.z;
        PNANOVDB_DEREF(hdda).voxel.z += PNANOVDB_DEREF(hdda).dim * PNANOVDB_DEREF(hdda).step.z;
        ret = PNANOVDB_DEREF(hdda).tmin <= PNANOVDB_DEREF(hdda).tmax;
    }
    return ret;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_ray_clip(
    PNANOVDB_IN(pnanovdb_vec3_t) bbox_min,
    PNANOVDB_IN(pnanovdb_vec3_t) bbox_max,
    PNANOVDB_IN(pnanovdb_vec3_t) origin, PNANOVDB_INOUT(float) tmin,
    PNANOVDB_IN(pnanovdb_vec3_t) direction, PNANOVDB_INOUT(float) tmax
)
{
    pnanovdb_vec3_t dir_inv = pnanovdb_vec3_div(pnanovdb_vec3_uniform(1.f), PNANOVDB_DEREF(direction));
    pnanovdb_vec3_t t0 = pnanovdb_vec3_mul(pnanovdb_vec3_sub(PNANOVDB_DEREF(bbox_min), PNANOVDB_DEREF(origin)), dir_inv);
    pnanovdb_vec3_t t1 = pnanovdb_vec3_mul(pnanovdb_vec3_sub(PNANOVDB_DEREF(bbox_max), PNANOVDB_DEREF(origin)), dir_inv);
    pnanovdb_vec3_t tmin3 = pnanovdb_vec3_min(t0, t1);
    pnanovdb_vec3_t tmax3 = pnanovdb_vec3_max(t0, t1);
    float tnear = pnanovdb_max(tmin3.x, pnanovdb_max(tmin3.y, tmin3.z));
    float tfar = pnanovdb_min(tmax3.x, pnanovdb_min(tmax3.y, tmax3.z));
    pnanovdb_bool_t hit = tnear <= tfar;
    PNANOVDB_DEREF(tmin) = pnanovdb_max(PNANOVDB_DEREF(tmin), tnear);
    PNANOVDB_DEREF(tmax) = pnanovdb_min(PNANOVDB_DEREF(tmax), tfar);
    return hit;
}

PNANOVDB_FORCE_INLINE pnanovdb_bool_t pnanovdb_hdda_zero_crossing(
    pnanovdb_grid_type_t grid_type,
    pnanovdb_buf_t buf,
    PNANOVDB_INOUT(pnanovdb_readaccessor_t) acc,
    PNANOVDB_IN(pnanovdb_vec3_t) origin, float tmin,
    PNANOVDB_IN(pnanovdb_vec3_t) direction, float tmax,
    PNANOVDB_INOUT(float) thit,
    PNANOVDB_INOUT(float) v
)
{
    pnanovdb_coord_t bbox_min = pnanovdb_root_get_bbox_min(buf, PNANOVDB_DEREF(acc).root);
    pnanovdb_coord_t bbox_max = pnanovdb_root_get_bbox_max(buf, PNANOVDB_DEREF(acc).root);
    pnanovdb_vec3_t bbox_minf = pnanovdb_coord_to_vec3(bbox_min);
    pnanovdb_vec3_t bbox_maxf = pnanovdb_coord_to_vec3(pnanovdb_coord_add(bbox_max, pnanovdb_coord_uniform(1)));

    pnanovdb_bool_t hit = pnanovdb_hdda_ray_clip(PNANOVDB_REF(bbox_minf), PNANOVDB_REF(bbox_maxf), origin, PNANOVDB_REF(tmin), direction, PNANOVDB_REF(tmax));
    if (!hit || tmax > 1.0e20f)
    {
        return PNANOVDB_FALSE;
    }

    pnanovdb_vec3_t pos = pnanovdb_hdda_ray_start(origin, tmin, direction);
    pnanovdb_coord_t ijk = pnanovdb_hdda_pos_to_ijk(PNANOVDB_REF(pos));

    pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk));
    float v0 = pnanovdb_read_float(buf, address);

    pnanovdb_int32_t dim = pnanovdb_uint32_as_int32(pnanovdb_readaccessor_get_dim(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk)));
    pnanovdb_hdda_t hdda;
    pnanovdb_hdda_init(PNANOVDB_REF(hdda), origin, tmin, direction, tmax, dim);
    while (pnanovdb_hdda_step(PNANOVDB_REF(hdda)))
    {
        pnanovdb_vec3_t pos_start = pnanovdb_hdda_ray_start(origin, hdda.tmin + 1.0001f, direction);
        ijk = pnanovdb_hdda_pos_to_ijk(PNANOVDB_REF(pos_start));
        dim = pnanovdb_uint32_as_int32(pnanovdb_readaccessor_get_dim(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk)));
        pnanovdb_hdda_update(PNANOVDB_REF(hdda), origin, direction, dim);
        if (hdda.dim > 1 || !pnanovdb_readaccessor_is_active(grid_type, buf, acc, PNANOVDB_REF(ijk)))
        {
            continue;
        }
        while (pnanovdb_hdda_step(PNANOVDB_REF(hdda)) && pnanovdb_readaccessor_is_active(grid_type, buf, acc, PNANOVDB_REF(hdda.voxel)))
        {
            ijk = hdda.voxel;
            pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, PNANOVDB_REF(ijk));
            PNANOVDB_DEREF(v) = pnanovdb_read_float(buf, address);
            if (PNANOVDB_DEREF(v) * v0 < 0.f)
            {
                PNANOVDB_DEREF(thit) = hdda.tmin;
                return PNANOVDB_TRUE;
            }
        }
    }
    return PNANOVDB_FALSE;
}

#endif

#endif // end of NANOVDB_PNANOVDB_H_HAS_BEEN_INCLUDED
