Loading...
Searching...
No Matches
CreateNanoGrid.h
Go to the documentation of this file.
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0
/*!
\file CreateNanoGrid.h
\author Ken Museth
\date June 26, 2020
\note In the examples below we assume that @c srcGrid is a exiting grid of type
SrcGridT = @c openvdb::FloatGrid, @c openvdb::FloatGrid or @c nanovdb::build::FloatGrid.
\brief Convert any grid to a nanovdb grid of the same type, e.g. float->float
\code
auto handle = nanovdb::createNanoGrid(srcGrid);
auto *dstGrid = handle.grid<float>();
\endcode
\brief Convert a grid to a nanovdb grid of a different type, e.g. float->half
\code
auto handle = nanovdb::createNanoGrid<SrcGridT,nanovdb::Fp16>(srcGrid);
auto *dstGrid = handle.grid<nanovdb::Fp16>();
\endcode
\brief Convert a grid to a nanovdb grid of the same type but using a CUDA buffer
\code
auto handle = nanovdb::createNanoGrid<SrcGridT, float, nanovdb::CudaDeviceBuffer>(srcGrid);
auto *dstGrid = handle.grid<float>();
\endcode
\brief Create a nanovdb grid that indices values in an existing source grid of any type.
If DstBuildT = nanovdb::ValueIndex both active and in-active values are indexed
and if DstBuildT = nanovdb::ValueOnIndex only active values are indexed.
\code
using DstBuildT = nanovdb::ValueIndex;// index both active an inactive values
auto handle = nanovdb::createNanoGridSrcGridT,DstBuildT>(srcGrid,0,false,false);//no blind data, tile values or stats
auto *dstGrid = handle.grid<DstBuildT>();
\endcode
\brief Create a NanoVDB grid from scratch
\code
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
using SrcGridT = openvdb::FloatGrid;
#else
using SrcGridT = nanovdb::build::FloatGrid;
#endif
SrcGridT srcGrid(0.0f);// create an empty source grid
auto srcAcc = srcGrid.getAccessor();// create an accessor
srcAcc.setValue(nanovdb::Coord(1,2,3), 1.0f);// set a voxel value
auto handle = nanovdb::createNanoGrid(srcGrid);// convert source grid to a grid handle
auto dstGrid = handle.grid<float>();// get a pointer to the destination grid
\endcode
\brief Convert a base-pointer to an openvdb grid, denoted srcGrid, to a nanovdb
grid of the same type, e.g. float -> float or openvdb::Vec3f -> nanovdb::Vec3f
\code
auto handle = nanovdb::openToNanoVDB(*srcGrid);// convert source grid to a grid handle
auto dstGrid = handle.grid<float>();// get a pointer to the destination grid
\endcode
\brief Converts any existing grid to a NanoVDB grid, for example:
nanovdb::build::Grid<SrcBuildT> -> nanovdb::Grid<DstBuildT>
nanovdb::Grid<SrcBuildT> -> nanovdb::Grid<DstBuildT>
nanovdb::Grid<SrcBuildT> -> nanovdb::Grid<ValueIndex or ValueOnIndex>
openvdb::Grid<SrcBuildT> -> nanovdb::Grid<DstBuildT>
openvdb::Grid<PointIndex> -> nanovdb::Grid<PointIndex>
openvdb::Grid<PointData> -> nanovdb::Grid<PointData>
openvdb::Grid<SrcBuildT> -> nanovdb::Grid<ValueIndex or ValueOnIndex>
\note This files replaces GridBuilder.h, IndexGridBuilder.h and OpenToNanoVDB.h
*/
#ifndef NANOVDB_CREATE_NANOGRID_H_HAS_BEEN_INCLUDED
#define NANOVDB_CREATE_NANOGRID_H_HAS_BEEN_INCLUDED
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
#include <openvdb/openvdb.h>
#include <openvdb/points/PointDataGrid.h>
#include <openvdb/tools/PointIndexGrid.h>
#endif
#include "GridBuilder.h"
#include "NodeManager.h"
#include "GridHandle.h"
#include "GridStats.h"
#include "GridChecksum.h"
#include "Range.h"
#include "Invoke.h"
#include "ForEach.h"
#include "Reduce.h"
#include "PrefixSum.h"
#include "DitherLUT.h"// for nanovdb::DitherLUT
#include <limits>
#include <vector>
#include <set>
#include <cstring> // for memcpy
#include <type_traits>
namespace nanovdb {
// Forward declarations (defined below)
template <typename> class CreateNanoGrid;
class AbsDiff;
template <typename> struct MapToNano;
//================================================================================================
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
/// @brief Forward declaration of free-standing function that converts an OpenVDB GridBase into a NanoVDB GridHandle
/// @tparam BufferT Type of the buffer used to allocate the destination grid
/// @param base Shared pointer to a base openvdb grid to be converted
/// @param sMode Mode for computing statistics of the destination grid
/// @param cMode Mode for computing checksums of the destination grid
/// @param verbose Mode of verbosity
/// @return Handle to the destination NanoGrid
template<typename BufferT = HostBuffer>
GridHandle<BufferT>
openToNanoVDB(const openvdb::GridBase::Ptr& base,
StatsMode sMode = StatsMode::Default,
ChecksumMode cMode = ChecksumMode::Default,
int verbose = 0);
#endif
//================================================================================================
/// @brief Freestanding function that creates a NanoGrid<T> from any source grid
/// @tparam SrcGridT Type of in input (source) grid, e.g. openvdb::Grid or nanovdb::Grid
/// @tparam DstBuildT Type of values in the output (destination) nanovdb Grid, e.g. float or nanovdb::Fp16
/// @tparam BufferT Type of the buffer used ti allocate the destination grid
/// @param srcGrid Input (source) grid to be converted
/// @param sMode Mode for computing statistics of the destination grid
/// @param cMode Mode for computing checksums of the destination grid
/// @param verbose Mode of verbosity
/// @param buffer Instance of a buffer used for allocation
/// @return Handle to the destination NanoGrid
template<typename SrcGridT,
typename DstBuildT = typename MapToNano<typename SrcGridT::BuildType>::type,
typename BufferT = HostBuffer>
typename disable_if<BuildTraits<DstBuildT>::is_index || BuildTraits<DstBuildT>::is_Fp, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
StatsMode sMode = StatsMode::Default,
ChecksumMode cMode = ChecksumMode::Default,
int verbose = 0,
const BufferT &buffer = BufferT());
//================================================================================================
/// @brief Freestanding function that creates a NanoGrid<ValueIndex> or NanoGrid<ValueOnIndex> from any source grid
/// @tparam SrcGridT Type of in input (source) grid, e.g. openvdb::Grid or nanovdb::Grid
/// @tparam DstBuildT If ValueIndex all (active and inactive) values are indexed and if
/// it is ValueOnIndex only active values are indexed.
/// @tparam BufferT BufferT Type of the buffer used ti allocate the destination grid
/// @param channels If non-zero the values (active or all) in @c srcGrid are encoded as blind
/// data in the output index grid. @c channels indicates the number of copies
/// of these blind data
/// @param includeStats If true all tree nodes will includes indices for stats, i.e. min/max/avg/std-div
/// @param includeTiles If false on values in leaf nodes are indexed
/// @param verbose Mode of verbosity
/// @param buffer Instance of a buffer used for allocation
/// @return Handle to the destination NanoGrid<T> where T = ValueIndex or ValueOnIndex
template<typename SrcGridT,
typename DstBuildT = typename MapToNano<typename SrcGridT::BuildType>::type,
typename BufferT = HostBuffer>
typename enable_if<BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
uint32_t channels = 0u,
bool includeStats = true,
bool includeTiles = true,
int verbose = 0,
const BufferT &buffer = BufferT());
//================================================================================================
/// @brief Freestanding function to create a NanoGrid<FpN> from any source grid
/// @tparam SrcGridT Type of in input (source) grid, e.g. openvdb::Grid or nanovdb::Grid
/// @tparam DstBuildT = FpN, i.e. variable bit-width of the output grid
/// @tparam OracleT Type of the oracle used to determine the local bit-width, i.e. N in FpN
/// @tparam BufferT Type of the buffer used to allocate the destination grid
/// @param srcGrid Input (source) grid to be converted
/// @param ditherOn switch to enable or disable dithering of quantization error
/// @param sMode Mode for computing statistics of the destination grid
/// @param cMode Mode for computing checksums of the destination grid
/// @param verbose Mode of verbosity
/// @param oracle Instance of a oracle used to determine the local bit-width, i.e. N in FpN
/// @param buffer Instance of a buffer used for allocation
/// @return Handle to the destination NanoGrid
template<typename SrcGridT,
typename DstBuildT = typename MapToNano<typename SrcGridT::BuildType>::type,
typename OracleT = AbsDiff,
typename BufferT = HostBuffer>
typename enable_if<is_same<FpN, DstBuildT>::value, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
StatsMode sMode = StatsMode::Default,
ChecksumMode cMode = ChecksumMode::Default,
bool ditherOn = false,
int verbose = 0,
const OracleT &oracle = OracleT(),
const BufferT &buffer = BufferT());
//================================================================================================
/// @brief Freestanding function to create a NanoGrid<FpX> from any source grid, X=4,8,16
/// @tparam SrcGridT Type of in input (source) grid, e.g. openvdb::Grid or nanovdb::Grid
/// @tparam DstBuildT = Fp4, Fp8 or Fp16, i.e. quantization bit-width of the output grid
/// @tparam BufferT Type of the buffer used to allocate the destination grid
/// @param srcGrid Input (source) grid to be converted
/// @param ditherOn switch to enable or disable dithering of quantization error
/// @param sMode Mode for computing statistics of the destination grid
/// @param cMode Mode for computing checksums of the destination grid
/// @param verbose Mode of verbosity
/// @param buffer Instance of a buffer used for allocation
/// @return Handle to the destination NanoGrid
template<typename SrcGridT,
typename DstBuildT = typename MapToNano<typename SrcGridT::BuildType>::type,
typename BufferT = HostBuffer>
typename enable_if<BuildTraits<DstBuildT>::is_FpX, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
StatsMode sMode = StatsMode::Default,
ChecksumMode cMode = ChecksumMode::Default,
bool ditherOn = false,
int verbose = 0,
const BufferT &buffer = BufferT());
//================================================================================================
/// @brief Compression oracle based on absolute difference
class AbsDiff
{
float mTolerance;// absolute error tolerance
public:
/// @note The default value of -1 means it's un-initialized!
AbsDiff(float tolerance = -1.0f) : mTolerance(tolerance) {}
AbsDiff(const AbsDiff&) = default;
~AbsDiff() = default;
operator bool() const {return mTolerance>=0.0f;}
void init(nanovdb::GridClass gClass, float background) {
if (gClass == GridClass::LevelSet) {
static const float halfWidth = 3.0f;
mTolerance = 0.1f * background / halfWidth;// range of ls: [-3dx; 3dx]
} else if (gClass == GridClass::FogVolume) {
mTolerance = 0.01f;// range of FOG volumes: [0;1]
} else {
mTolerance = 0.0f;
}
}
void setTolerance(float tolerance) { mTolerance = tolerance; }
float getTolerance() const { return mTolerance; }
/// @brief Return true if the approximate value is within the accepted
/// absolute error bounds of the exact value.
///
/// @details Required member method
bool operator()(float exact, float approx) const
{
return Abs(exact - approx) <= mTolerance;
}
};// AbsDiff
inline std::ostream& operator<<(std::ostream& os, const AbsDiff& diff)
{
os << "Absolute tolerance: " << diff.getTolerance();
return os;
}
//================================================================================================
/// @brief Compression oracle based on relative difference
class RelDiff
{
float mTolerance;// relative error tolerance
public:
/// @note The default value of -1 means it's un-initialized!
RelDiff(float tolerance = -1.0f) : mTolerance(tolerance) {}
RelDiff(const RelDiff&) = default;
~RelDiff() = default;
operator bool() const {return mTolerance>=0.0f;}
void setTolerance(float tolerance) { mTolerance = tolerance; }
float getTolerance() const { return mTolerance; }
/// @brief Return true if the approximate value is within the accepted
/// relative error bounds of the exact value.
///
/// @details Required member method
bool operator()(float exact, float approx) const
{
return Abs(exact - approx)/Max(Abs(exact), Abs(approx)) <= mTolerance;
}
};// RelDiff
inline std::ostream& operator<<(std::ostream& os, const RelDiff& diff)
{
os << "Relative tolerance: " << diff.getTolerance();
return os;
}
//================================================================================================
/// @brief The NodeAccessor provides a uniform API for accessing nodes got NanoVDB, OpenVDB and build Grids
///
/// @note General implementation that works with nanovdb::build::Grid
template <typename GridT>
class NodeAccessor
{
public:
static constexpr bool IS_OPENVDB = false;
static constexpr bool IS_NANOVDB = false;
using BuildType = typename GridT::BuildType;
using ValueType = typename GridT::ValueType;
using GridType = GridT;
using TreeType = typename GridT::TreeType;
using RootType = typename TreeType::RootNodeType;
template<int LEVEL>
using NodeType = typename NodeTrait<const TreeType, LEVEL>::type;
NodeAccessor(const GridT &grid) : mMgr(const_cast<GridT&>(grid)) {}
const GridType& grid() const {return mMgr.grid();}
const TreeType& tree() const {return mMgr.tree();}
const RootType& root() const {return mMgr.root();}
uint64_t nodeCount(int level) const { return mMgr.nodeCount(level); }
template <int LEVEL>
const NodeType<LEVEL>& node(uint32_t i) const {return mMgr.template node<LEVEL>(i); }
const std::string& getName() const {return this->grid().getName();};
bool hasLongGridName() const {return this->grid().getName().length() >= GridData::MaxNameSize;}
const nanovdb::Map& map() const {return this->grid().map();}
GridClass gridClass() const {return this->grid().gridClass();}
private:
build::NodeManager<GridT> mMgr;
};// NodeAccessor<GridT>
//================================================================================================
/// @brief Template specialization for nanovdb::Grid which is special since its NodeManage
/// uses a handle in order to support node access on the GPU!
template <typename BuildT>
class NodeAccessor< NanoGrid<BuildT> >
{
public:
static constexpr bool IS_OPENVDB = false;
static constexpr bool IS_NANOVDB = true;
using BuildType = BuildT;
using BufferType = HostBuffer;
using GridType = NanoGrid<BuildT>;
using ValueType = typename GridType::ValueType;
using TreeType = typename GridType::TreeType;
using RootType = typename TreeType::RootType;
template<int LEVEL>
using NodeType = typename NodeTrait<TreeType, LEVEL>::type;
NodeAccessor(const GridType &grid)
: mHandle(createNodeManager<BuildT, BufferType>(grid))
, mMgr(*(mHandle.template mgr<BuildT>())) {}
const GridType& grid() const {return mMgr.grid();}
const TreeType& tree() const {return mMgr.tree();}
const RootType& root() const {return mMgr.root();}
uint64_t nodeCount(int level) const { return mMgr.nodeCount(level); }
template <int LEVEL>
const NodeType<LEVEL>& node(uint32_t i) const {return mMgr.template node<LEVEL>(i); }
std::string getName() const {return std::string(this->grid().gridName());};
bool hasLongGridName() const {return this->grid().hasLongGridName();}
const nanovdb::Map& map() const {return this->grid().map();}
GridClass gridClass() const {return this->grid().gridClass();}
private:
NodeManagerHandle<BufferType> mHandle;
const NodeManager<BuildT> &mMgr;
};// NodeAccessor<nanovdb::Grid>
//================================================================================================
/// @brief Trait that maps any type to the corresponding nanovdb type
/// @tparam T Type to be mapped
template<typename T>
struct MapToNano { using type = T; };
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
template<>
struct MapToNano<openvdb::ValueMask> {using type = nanovdb::ValueMask;};
template<typename T>
struct MapToNano<openvdb::math::Vec3<T>>{using type = nanovdb::Vec3<T>;};
template<typename T>
struct MapToNano<openvdb::math::Vec4<T>>{using type = nanovdb::Vec4<T>;};
template<>
struct MapToNano<openvdb::PointIndex32> {using type = uint32_t;};
template<>
struct MapToNano<openvdb::PointDataIndex32> {using type = uint32_t;};
/// Templated Grid with default 32->16->8 configuration
template <typename BuildT>
using OpenLeaf = openvdb::tree::LeafNode<BuildT,3>;
template <typename BuildT>
using OpenLower = openvdb::tree::InternalNode<OpenLeaf<BuildT>,4>;
template <typename BuildT>
using OpenUpper = openvdb::tree::InternalNode<OpenLower<BuildT>,5>;
template <typename BuildT>
using OpenRoot = openvdb::tree::RootNode<OpenUpper<BuildT>>;
template <typename BuildT>
using OpenTree = openvdb::tree::Tree<OpenRoot<BuildT>>;
template <typename BuildT>
using OpenGrid = openvdb::Grid<OpenTree<BuildT>>;
//================================================================================================
/// @brief Template specialization for openvdb::Grid
template <typename BuildT>
class NodeAccessor<OpenGrid<BuildT>>
{
public:
static constexpr bool IS_OPENVDB = true;
static constexpr bool IS_NANOVDB = false;
using BuildType = BuildT;
using GridType = OpenGrid<BuildT>;
using ValueType = typename GridType::ValueType;
using TreeType = OpenTree<BuildT>;
using RootType = OpenRoot<BuildT>;
template<int LEVEL>
using NodeType = typename NodeTrait<const TreeType, LEVEL>::type;
NodeAccessor(const GridType &grid) : mMgr(const_cast<GridType&>(grid)) {
const auto mat4 = this->grid().transform().baseMap()->getAffineMap()->getMat4();
mMap.set(mat4, mat4.inverse());
}
const GridType& grid() const {return mMgr.grid();}
const TreeType& tree() const {return mMgr.tree();}
const RootType& root() const {return mMgr.root();}
uint64_t nodeCount(int level) const { return mMgr.nodeCount(level); }
template <int LEVEL>
const NodeType<LEVEL>& node(uint32_t i) const {return mMgr.template node<LEVEL>(i); }
std::string getName() const { return this->grid().getName(); };
bool hasLongGridName() const {return this->grid().getName().length() >= GridData::MaxNameSize;}
const nanovdb::Map& map() const {return mMap;}
GridClass gridClass() const {
switch (this->grid().getGridClass()) {
case openvdb::GRID_LEVEL_SET:
if (!is_floating_point<BuildT>::value) OPENVDB_THROW(openvdb::ValueError, "processGrid: Level sets are expected to be floating point types");
return GridClass::LevelSet;
case openvdb::GRID_FOG_VOLUME:
return GridClass::FogVolume;
case openvdb::GRID_STAGGERED:
return GridClass::Staggered;
default:
return GridClass::Unknown;
}
}
private:
build::NodeManager<GridType> mMgr;
nanovdb::Map mMap;
};// NodeAccessor<openvdb::Grid<T>>
//================================================================================================
/// @brief Template specialization for openvdb::tools::PointIndexGrid
template <>
class NodeAccessor<openvdb::tools::PointIndexGrid>
{
public:
static constexpr bool IS_OPENVDB = true;
static constexpr bool IS_NANOVDB = false;
using BuildType = openvdb::PointIndex32;
using GridType = openvdb::tools::PointIndexGrid;
using TreeType = openvdb::tools::PointIndexTree;
using RootType = typename TreeType::RootNodeType;
using ValueType = typename GridType::ValueType;
template<int LEVEL>
using NodeType = typename NodeTrait<const TreeType, LEVEL>::type;
NodeAccessor(const GridType &grid) : mMgr(const_cast<GridType&>(grid)) {
const auto mat4 = this->grid().transform().baseMap()->getAffineMap()->getMat4();
mMap.set(mat4, mat4.inverse());
}
const GridType& grid() const {return mMgr.grid();}
const TreeType& tree() const {return mMgr.tree();}
const RootType& root() const {return mMgr.root();}
uint64_t nodeCount(int level) const { return mMgr.nodeCount(level); }
template <int LEVEL>
const NodeType<LEVEL>& node(uint32_t i) const {return mMgr.template node<LEVEL>(i); }
std::string getName() const { return this->grid().getName(); };
bool hasLongGridName() const {return this->grid().getName().length() >= GridData::MaxNameSize;}
const nanovdb::Map& map() const {return mMap;}
GridClass gridClass() const {return GridClass::PointIndex;}
private:
build::NodeManager<GridType> mMgr;
nanovdb::Map mMap;
};// NodeAccessor<openvdb::tools::PointIndexGrid>
//================================================================================================
// @brief Template specialization for openvdb::points::PointDataGrid
template <>
class NodeAccessor<openvdb::points::PointDataGrid>
{
public:
static constexpr bool IS_OPENVDB = true;
static constexpr bool IS_NANOVDB = false;
using BuildType = openvdb::PointDataIndex32;
using GridType = openvdb::points::PointDataGrid;
using TreeType = openvdb::points::PointDataTree;
using RootType = typename TreeType::RootNodeType;
using ValueType = typename GridType::ValueType;
template<int LEVEL>
using NodeType = typename NodeTrait<const TreeType, LEVEL>::type;
NodeAccessor(const GridType &grid) : mMgr(const_cast<GridType&>(grid)) {
const auto mat4 = this->grid().transform().baseMap()->getAffineMap()->getMat4();
mMap.set(mat4, mat4.inverse());
}
const GridType& grid() const {return mMgr.grid();}
const TreeType& tree() const {return mMgr.tree();}
const RootType& root() const {return mMgr.root();}
uint64_t nodeCount(int level) const { return mMgr.nodeCount(level); }
template <int LEVEL>
const NodeType<LEVEL>& node(uint32_t i) const {return mMgr.template node<LEVEL>(i); }
std::string getName() const { return this->grid().getName(); };
bool hasLongGridName() const {return this->grid().getName().length() >= GridData::MaxNameSize;}
const nanovdb::Map& map() const {return mMap;}
GridClass gridClass() const {return GridClass::PointData;}
private:
build::NodeManager<GridType> mMgr;
nanovdb::Map mMap;
};// NodeAccessor<openvdb::points::PointDataGrid>
#endif// NANOVDB_USE_OPENVDB
//================================================================================================
/// @brief Creates any nanovdb Grid from any source grid (certain combinations are obviously not allowed)
template <typename SrcGridT>
class CreateNanoGrid
{
public:
// SrcGridT can be either openvdb::Grid, nanovdb::Grid or nanovdb::build::Grid
using SrcNodeAccT = NodeAccessor<SrcGridT>;
using SrcBuildT = typename SrcNodeAccT::BuildType;
using SrcValueT = typename SrcNodeAccT::ValueType;
using SrcTreeT = typename SrcNodeAccT::TreeType;
using SrcRootT = typename SrcNodeAccT::RootType;
template <int LEVEL>
using SrcNodeT = typename NodeTrait<SrcRootT, LEVEL>::type;
/// @brief Constructor from a source grid
/// @param srcGrid Source grid of type SrcGridT
CreateNanoGrid(const SrcGridT &srcGrid);
/// @brief Constructor from a source node accessor (defined above)
/// @param srcNodeAcc Source node accessor of type SrcNodeAccT
CreateNanoGrid(const SrcNodeAccT &srcNodeAcc);
/// @brief Set the level of verbosity
/// @param mode level of verbosity, mode=0 means quiet
void setVerbose(int mode = 1) { mVerbose = mode; }
/// @brief Enable or disable dithering, i.e. randomization of the quantization error.
/// @param on enable or disable dithering
/// @warning Dithering only has an affect when DstBuildT = {Fp4, Fp8, Fp16, FpN}
void enableDithering(bool on = true) { mDitherOn = on; }
/// @brief Set the mode used for computing statistics of the destination grid
/// @param mode specify the mode of statistics
void setStats(StatsMode mode = StatsMode::Default) { mStats = mode; }
/// @brief Set the mode used for computing checksums of the destination grid
/// @param mode specify the mode of checksum
void setChecksum(ChecksumMode mode = ChecksumMode::Default) { mChecksum = mode; }
/// @brief Converts the source grid into a nanovdb grid with the specified destination build type
/// @tparam DstBuildT build type of the destination, output, grid
/// @tparam BufferT Type of the buffer used for allocating the destination grid
/// @param buffer instance of the buffer use for allocation
/// @return Return an instance of a GridHandle (invoking move semantics)
/// @note This version is when DstBuildT != {FpN, ValueIndex, ValueOnIndex}
template<typename DstBuildT = typename MapToNano<SrcBuildT>::type, typename BufferT = HostBuffer>
typename disable_if<is_same<DstBuildT, FpN>::value ||
BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
getHandle(const BufferT &buffer = BufferT());
/// @brief Converts the source grid into a nanovdb grid with variable bit quantization
/// @tparam DstBuildT FpN, i.e. the destination grid uses variable bit quantization
/// @tparam OracleT Type of oracle used to determine the N in FpN
/// @tparam BufferT Type of the buffer used for allocating the destination grid
/// @param oracle Instance of the oracle used to determine the N in FpN
/// @param buffer instance of the buffer use for allocation
/// @return Return an instance of a GridHandle (invoking move semantics)
/// @note This version assumes DstBuildT == FpN
template<typename DstBuildT = typename MapToNano<SrcBuildT>::type, typename OracleT = AbsDiff, typename BufferT = HostBuffer>
typename enable_if<is_same<DstBuildT, FpN>::value, GridHandle<BufferT>>::type
getHandle(const OracleT &oracle = OracleT(),
const BufferT &buffer = BufferT());
/// @brief Converts the source grid into a nanovdb grid with indices to external arrays of values
/// @tparam DstBuildT ValueIndex or ValueOnIndex, i.e. index all or just active values
/// @tparam BufferT Type of the buffer used for allocating the destination grid
/// @param channels Number of copies of values encoded as blind data in the destination grid
/// @param includeStats Specify if statics should be indexed
/// @param includeTiles Specify if tile values, i.e. non-leaf-node-values, should be indexed
/// @param buffer instance of the buffer use for allocation
/// @return Return an instance of a GridHandle (invoking move semantics)
template<typename DstBuildT = typename MapToNano<SrcBuildT>::type, typename BufferT = HostBuffer>
typename enable_if<BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
getHandle(uint32_t channels = 0u,
bool includeStats = true,
bool includeTiles = true,
const BufferT &buffer = BufferT());
/// @brief Add blind data to the destination grid
/// @param name String name of the blind data
/// @param dataSemantic Semantics of the blind data
/// @param dataClass Class of the blind data
/// @param dataType Type of the blind data
/// @param count Element count of the blind data
/// @param size Size of each element of the blind data
/// @return Return the index used to access the blind data
uint64_t addBlindData(const std::string& name,
GridBlindDataSemantic dataSemantic,
GridBlindDataClass dataClass,
GridType dataType,
size_t count, size_t size)
{
const size_t order = mBlindMetaData.size();
mBlindMetaData.emplace(name, dataSemantic, dataClass, dataType, order, count, size);
return order;
}
/// @brief This method only has affect when getHandle was called with DstBuildT = ValueIndex or ValueOnIndex
/// @return Return the number of indexed values. If called before getHandle was called with
/// DstBuildT = ValueIndex or ValueOnIndex the return value is zero. Else it is a value larger than zero.
uint64_t valueCount() const {return mValIdx[0].empty() ? 0u : mValIdx[0].back();}
/// @brief Copy values from the source grid into a provided buffer
/// @tparam DstBuildT Must be ValueIndex or ValueOnIndex, i.e. a index grid
/// @param buffer point in which to write values
template <typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_index>::type
copyValues(SrcValueT *buffer);
private:
// =========================================================
template <typename T, int LEVEL>
typename enable_if<!(is_same<T,FpN>::value&&LEVEL==0), typename NodeTrait<NanoRoot<T>, LEVEL>::type*>::type
dstNode(uint64_t i) const {
static_assert(LEVEL==0 || LEVEL==1 || LEVEL==2, "Expected LEVEL== {0,1,2}");
using NodeT = typename NodeTrait<NanoRoot<T>, LEVEL>::type;
return PtrAdd<NodeT>(mBufferPtr, mOffset[5-LEVEL]) + i;
}
template <typename T, int LEVEL>
typename enable_if<is_same<T,FpN>::value && LEVEL==0, NanoLeaf<FpN>*>::type
dstNode(uint64_t i) const {return PtrAdd<NanoLeaf<FpN>>(mBufferPtr, mCodec[i].offset);}
template <typename T> NanoRoot<T>* dstRoot() const {return PtrAdd<NanoRoot<T>>(mBufferPtr, mOffset.root);}
template <typename T> NanoTree<T>* dstTree() const {return PtrAdd<NanoTree<T>>(mBufferPtr, mOffset.tree);}
template <typename T> NanoGrid<T>* dstGrid() const {return PtrAdd<NanoGrid<T>>(mBufferPtr, mOffset.grid);}
GridBlindMetaData* dstMeta(uint32_t i) const { return PtrAdd<GridBlindMetaData>(mBufferPtr, mOffset.meta) + i;};
// =========================================================
template <typename DstBuildT>
typename disable_if<is_same<FpN,DstBuildT>::value || BuildTraits<DstBuildT>::is_index>::type
preProcess();
template <typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_index>::type
preProcess(uint32_t channels);
template <typename DstBuildT, typename OracleT>
typename enable_if<is_same<FpN, DstBuildT>::value>::type
preProcess(OracleT oracle);
// =========================================================
// Below are private methods use to serialize nodes into NanoVDB
template<typename DstBuildT, typename BufferT>
GridHandle<BufferT> initHandle(const BufferT& buffer);
// =========================================================
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_index>::type
postProcess(uint32_t channels);
template <typename DstBuildT>
inline typename disable_if<BuildTraits<DstBuildT>::is_index>::type
postProcess();
// ========================================================
template<typename DstBuildT>
typename disable_if<BuildTraits<DstBuildT>::is_special>::type
processLeafs();
template<typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_index>::type
processLeafs();
template<typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_FpX>::type
processLeafs();
template<typename DstBuildT>
typename enable_if<is_same<FpN, DstBuildT>::value>::type
processLeafs();
template<typename DstBuildT>
typename enable_if<is_same<bool, DstBuildT>::value>::type
processLeafs();
template<typename DstBuildT>
typename enable_if<is_same<ValueMask, DstBuildT>::value>::type
processLeafs();
// =========================================================
template<typename DstBuildT, int LEVEL>
typename enable_if<BuildTraits<DstBuildT>::is_index>::type
processInternalNodes();
template<typename DstBuildT, int LEVEL>
typename enable_if<!BuildTraits<DstBuildT>::is_index>::type
processInternalNodes();
// =========================================================
template <typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_index>::type
processRoot();
template <typename DstBuildT>
typename enable_if<!BuildTraits<DstBuildT>::is_index>::type
processRoot();
// =========================================================
template<typename DstBuildT>
void processTree();
template<typename DstBuildT>
void processGrid();
template <typename DstBuildT, int LEVEL>
typename enable_if<BuildTraits<DstBuildT>::is_index, uint64_t>::type
countTileValues(uint64_t valueCount);
template <typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_index, uint64_t>::type
countValues();
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
template<typename T = SrcGridT>
typename disable_if<is_same<T, openvdb::tools::PointIndexGrid>::value ||
is_same<T, openvdb::points::PointDataGrid>::value, uint64_t>::type
countPoints() const;
template<typename T = SrcGridT>
typename enable_if<is_same<T, openvdb::tools::PointIndexGrid>::value ||
is_same<T, openvdb::points::PointDataGrid>::value, uint64_t>::type
countPoints() const;
template<typename DstBuildT, typename AttT, typename CodecT = openvdb::points::UnknownCodec, typename T = SrcGridT>
typename enable_if<is_same<openvdb::points::PointDataGrid, T>::value>::type
copyPointAttribute(size_t attIdx, AttT *attPtr);
#else
uint64_t countPoints() const {return 0u;}
#endif
uint8_t* mBufferPtr;// pointer to the beginning of the destination nanovdb grid buffer
struct BufferOffsets {
uint64_t grid, tree, root, upper, lower, leaf, meta, blind, size;
uint64_t operator[](int i) const { return *(reinterpret_cast<const uint64_t*>(this)+i); }
} mOffset;
int mVerbose;
uint64_t mLeafNodeSize;// non-trivial when DstBuiltT = FpN
std::unique_ptr<SrcNodeAccT> mSrcNodeAccPtr;// placeholder for potential local instance
const SrcNodeAccT &mSrcNodeAcc;
struct BlindMetaData; // forward declaration
std::set<BlindMetaData> mBlindMetaData; // sorted according to BlindMetaData.order
struct Codec { float min, max; uint64_t offset; uint8_t log2; };// used for adaptive bit-rate quantization
std::unique_ptr<Codec[]> mCodec;// defines a codec per leaf node when DstBuildT = FpN
StatsMode mStats;
ChecksumMode mChecksum;
bool mDitherOn, mIncludeStats, mIncludeTiles;
std::vector<uint64_t> mValIdx[3];// store id of first value in node
}; // CreateNanoGrid
//================================================================================================
template <typename SrcGridT>
CreateNanoGrid<SrcGridT>::CreateNanoGrid(const SrcGridT &srcGrid)
: mVerbose(0)
, mSrcNodeAccPtr(new SrcNodeAccT(srcGrid))
, mSrcNodeAcc(*mSrcNodeAccPtr)
, mStats(StatsMode::Default)
, mChecksum(ChecksumMode::Default)
, mDitherOn(false)
, mIncludeStats(true)
, mIncludeTiles(true)
{
}
//================================================================================================
template <typename SrcGridT>
CreateNanoGrid<SrcGridT>::CreateNanoGrid(const SrcNodeAccT &srcNodeAcc)
: mVerbose(0)
, mSrcNodeAccPtr(nullptr)
, mSrcNodeAcc(srcNodeAcc)
, mStats(StatsMode::Default)
, mChecksum(ChecksumMode::Default)
, mDitherOn(false)
, mIncludeStats(true)
, mIncludeTiles(true)
{
}
//================================================================================================
template <typename SrcGridT>
struct CreateNanoGrid<SrcGridT>::BlindMetaData
{
BlindMetaData(const std::string& name,// name + used to derive GridBlindDataSemantic
const std::string& type,// used to derive GridType of blind data
GridBlindDataClass dataClass,
size_t i, size_t valueCount, size_t valueSize)
: metaData(reinterpret_cast<GridBlindMetaData*>(new char[sizeof(GridBlindMetaData)]))
, order(i)// sorted id of meta data
, size(AlignUp<NANOVDB_DATA_ALIGNMENT>(valueCount * valueSize))
{
std::memset(metaData, 0, sizeof(GridBlindMetaData));// zero out all meta data
if (name.length()>=GridData::MaxNameSize) throw std::runtime_error("blind data name exceeds limit");
std::memcpy(metaData->mName, name.c_str(), name.length() + 1);
metaData->mValueCount = valueCount;
metaData->mSemantic = BlindMetaData::mapToSemantics(name);
metaData->mDataClass = dataClass;
metaData->mDataType = BlindMetaData::mapToType(type);
metaData->mValueSize = valueSize;
NANOVDB_ASSERT(metaData->isValid());
}
BlindMetaData(const std::string& name,// only name
GridBlindDataSemantic dataSemantic,
GridBlindDataClass dataClass,
GridType dataType,
size_t i, size_t valueCount, size_t valueSize)
: metaData(reinterpret_cast<GridBlindMetaData*>(new char[sizeof(GridBlindMetaData)]))
, order(i)// sorted id of meta data
, size(AlignUp<NANOVDB_DATA_ALIGNMENT>(valueCount * valueSize))
{
std::memset(metaData, 0, sizeof(GridBlindMetaData));// zero out all meta data
if (name.length()>=GridData::MaxNameSize) throw std::runtime_error("blind data name exceeds character limit");
std::memcpy(metaData->mName, name.c_str(), name.length() + 1);
metaData->mValueCount = valueCount;
metaData->mSemantic = dataSemantic;
metaData->mDataClass = dataClass;
metaData->mDataType = dataType;
metaData->mValueSize = valueSize;
NANOVDB_ASSERT(metaData->isValid());
}
~BlindMetaData(){ delete [] reinterpret_cast<char*>(metaData); }
bool operator<(const BlindMetaData& other) const { return order < other.order; } // required by std::set
static GridType mapToType(const std::string& name)
{
GridType type = GridType::Unknown;
if ("uint32_t" == name) {
type = GridType::UInt32;
} else if ("float" == name) {
type = GridType::Float;
} else if ("vec3s"== name) {
type = GridType::Vec3f;
} else if ("int32" == name) {
type = GridType::Int32;
} else if ("int64" == name) {
type = GridType::Int64;
}
return type;
}
static GridBlindDataSemantic mapToSemantics(const std::string& name)
{
GridBlindDataSemantic semantic = GridBlindDataSemantic::Unknown;
if ("P" == name) {
semantic = GridBlindDataSemantic::PointPosition;
} else if ("V" == name) {
semantic = GridBlindDataSemantic::PointVelocity;
} else if ("Cd" == name) {
semantic = GridBlindDataSemantic::PointColor;
} else if ("N" == name) {
semantic = GridBlindDataSemantic::PointNormal;
} else if ("id" == name) {
semantic = GridBlindDataSemantic::PointId;
}
return semantic;
}
GridBlindMetaData *metaData;
const size_t order, size;
}; // CreateNanoGrid::BlindMetaData
//================================================================================================
template <typename SrcGridT>
template<typename DstBuildT, typename BufferT>
typename disable_if<is_same<DstBuildT, FpN>::value ||
BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
CreateNanoGrid<SrcGridT>::getHandle(const BufferT& pool)
{
this->template preProcess<DstBuildT>();
auto handle = this->template initHandle<DstBuildT>(pool);
this->template postProcess<DstBuildT>();
return handle;
} // CreateNanoGrid::getHandle<T>
//================================================================================================
template <typename SrcGridT>
template<typename DstBuildT, typename OracleT, typename BufferT>
typename enable_if<is_same<DstBuildT, FpN>::value, GridHandle<BufferT>>::type
CreateNanoGrid<SrcGridT>::getHandle(const OracleT& oracle, const BufferT& pool)
{
this->template preProcess<DstBuildT, OracleT>(oracle);
auto handle = this->template initHandle<DstBuildT>(pool);
this->template postProcess<DstBuildT>();
return handle;
} // CreateNanoGrid::getHandle<FpN>
//================================================================================================
template <typename SrcGridT>
template<typename DstBuildT, typename BufferT>
typename enable_if<BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
CreateNanoGrid<SrcGridT>::getHandle(uint32_t channels,
bool includeStats,
bool includeTiles,
const BufferT &pool)
{
mIncludeStats = includeStats;
mIncludeTiles = includeTiles;
this->template preProcess<DstBuildT>(channels);
auto handle = this->template initHandle<DstBuildT>(pool);
this->template postProcess<DstBuildT>(channels);
return handle;
}// CreateNanoGrid::getHandle<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT, typename BufferT>
GridHandle<BufferT> CreateNanoGrid<SrcGridT>::initHandle(const BufferT& pool)
{
mOffset.grid = 0;// grid is always stored at the start of the buffer!
mOffset.tree = NanoGrid<DstBuildT>::memUsage(); // grid ends and tree begins
mOffset.root = mOffset.tree + NanoTree<DstBuildT>::memUsage(); // tree ends and root node begins
mOffset.upper = mOffset.root + NanoRoot<DstBuildT>::memUsage(mSrcNodeAcc.root().getTableSize()); // root node ends and upper internal nodes begin
mOffset.lower = mOffset.upper + NanoUpper<DstBuildT>::memUsage()*mSrcNodeAcc.nodeCount(2); // upper internal nodes ends and lower internal nodes begin
mOffset.leaf = mOffset.lower + NanoLower<DstBuildT>::memUsage()*mSrcNodeAcc.nodeCount(1); // lower internal nodes ends and leaf nodes begin
mOffset.meta = mOffset.leaf + mLeafNodeSize;// leaf nodes end and blind meta data begins
mOffset.blind = mOffset.meta + sizeof(GridBlindMetaData)*mBlindMetaData.size(); // meta data ends and blind data begins
mOffset.size = mOffset.blind;// end of buffer
for (const auto& b : mBlindMetaData) mOffset.size += b.size; // accumulate all the blind data
auto buffer = BufferT::create(mOffset.size, &pool);
mBufferPtr = buffer.data();
// Concurrent processing of all tree levels!
invoke( [&](){this->template processLeafs<DstBuildT>();},
[&](){this->template processInternalNodes<DstBuildT, 1>();},
[&](){this->template processInternalNodes<DstBuildT, 2>();},
[&](){this->template processRoot<DstBuildT>();},
[&](){this->template processTree<DstBuildT>();},
[&](){this->template processGrid<DstBuildT>();} );
return GridHandle<BufferT>(std::move(buffer));
} // CreateNanoGrid::initHandle
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename disable_if<is_same<FpN, DstBuildT>::value || BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::preProcess()
{
if (const uint64_t pointCount = this->countPoints()) {
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
if constexpr(is_same<openvdb::tools::PointIndexGrid, SrcGridT>::value) {
if (!mBlindMetaData.empty()) throw std::runtime_error("expected no blind meta data");
this->addBlindData("index",
GridBlindDataSemantic::PointId,
GridBlindDataClass::IndexArray,
GridType::UInt32,
pointCount,
sizeof(uint32_t));
} else if constexpr(is_same<openvdb::points::PointDataGrid, SrcGridT>::value) {
if (!mBlindMetaData.empty()) throw std::runtime_error("expected no blind meta data");
auto &srcLeaf = mSrcNodeAcc.template node<0>(0);
const auto& attributeSet = srcLeaf.attributeSet();
const auto& descriptor = attributeSet.descriptor();
const auto& nameMap = descriptor.map();
for (auto it = nameMap.begin(); it != nameMap.end(); ++it) {
const size_t index = it->second;
auto& attArray = srcLeaf.constAttributeArray(index);
mBlindMetaData.emplace(it->first, // name used to derive semantics
descriptor.valueType(index), // type
it->first == "id" ? GridBlindDataClass::IndexArray : GridBlindDataClass::AttributeArray, // class
index, // order
pointCount, // element count
attArray.valueTypeSize()); // element size
}
}
#endif// end NANOVDB_USE_OPENVDB
}
if (mSrcNodeAcc.hasLongGridName()) {
this->addBlindData("grid name",
GridBlindDataSemantic::Unknown,
GridBlindDataClass::GridName,
GridType::Unknown,
mSrcNodeAcc.getName().length() + 1, 1);
}
mLeafNodeSize = mSrcNodeAcc.nodeCount(0)*NanoLeaf<DstBuildT>::DataType::memUsage();
}// CreateNanoGrid::preProcess<T>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT, typename OracleT>
inline typename enable_if<is_same<FpN, DstBuildT>::value>::type
CreateNanoGrid<SrcGridT>::preProcess(OracleT oracle)
{
static_assert(is_same<float, SrcValueT>::value, "preProcess<FpN>: expected SrcValueT == float");
const size_t leafCount = mSrcNodeAcc.nodeCount(0);
if (leafCount==0) {
mLeafNodeSize = 0u;
return;
}
mCodec.reset(new Codec[leafCount]);
if constexpr(is_same<AbsDiff, OracleT>::value) {
if (!oracle) oracle.init(mSrcNodeAcc.gridClass(), mSrcNodeAcc.root().background());
}
DitherLUT lut(mDitherOn);
forEach(0, leafCount, 4, [&](const Range1D &r) {
for (auto i=r.begin(); i!=r.end(); ++i) {
const auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
float &min = mCodec[i].min = std::numeric_limits<float>::max();
float &max = mCodec[i].max = -min;
for (int j=0; j<512; ++j) {
float v = srcLeaf.getValue(j);
if (v<min) min = v;
if (v>max) max = v;
}
const float range = max - min;
uint8_t &logBitWidth = mCodec[i].log2 = 0;// 0,1,2,3,4 => 1,2,4,8,16 bits
while (range > 0.0f && logBitWidth < 4u) {
const uint32_t mask = (uint32_t(1) << (uint32_t(1) << logBitWidth)) - 1u;
const float encode = mask/range;
const float decode = range/mask;
int j = 0;
do {
const float exact = srcLeaf.getValue(j);//data[j];// exact value
const uint32_t code = uint32_t(encode*(exact - min) + lut(j));
const float approx = code * decode + min;// approximate value
j += oracle(exact, approx) ? 1 : 513;
} while(j < 512);
if (j == 512) break;
++logBitWidth;
}
}
});
auto getOffset = [&](size_t i){
--i;
return mCodec[i].offset + NanoLeaf<DstBuildT>::DataType::memUsage(1u << mCodec[i].log2);
};
mCodec[0].offset = NanoGrid<FpN>::memUsage() +
NanoTree<FpN>::memUsage() +
NanoRoot<FpN>::memUsage(mSrcNodeAcc.root().getTableSize()) +
NanoUpper<FpN>::memUsage()*mSrcNodeAcc.nodeCount(2) +
NanoLower<FpN>::memUsage()*mSrcNodeAcc.nodeCount(1);
for (size_t i=1; i<leafCount; ++i) mCodec[i].offset = getOffset(i);
mLeafNodeSize = getOffset(leafCount);
if (mVerbose) {
uint32_t counters[5+1] = {0};
++counters[mCodec[0].log2];
for (size_t i=1; i<leafCount; ++i) ++counters[mCodec[i].log2];
std::cout << "\n" << oracle << std::endl;
std::cout << "Dithering: " << (mDitherOn ? "enabled" : "disabled") << std::endl;
float avg = 0.0f;
for (uint32_t i=0; i<=5; ++i) {
if (uint32_t n = counters[i]) {
avg += n * float(1 << i);
printf("%2i bits: %6u leaf nodes, i.e. %4.1f%%\n",1<<i, n, 100.0f*n/float(leafCount));
}
}
printf("%4.1f bits per value on average\n", avg/float(leafCount));
}
if (mSrcNodeAcc.hasLongGridName()) {
this->addBlindData("grid name",
GridBlindDataSemantic::Unknown,
GridBlindDataClass::GridName,
GridType::Unknown,
mSrcNodeAcc.getName().length() + 1, 1);
}
}// CreateNanoGrid::preProcess<FpN>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT, int LEVEL>
inline typename enable_if<BuildTraits<DstBuildT>::is_index, uint64_t>::type
CreateNanoGrid<SrcGridT>::countTileValues(uint64_t valueCount)
{
const uint64_t stats = mIncludeStats ? 4u : 0u;// minimum, maximum, average, and deviation
mValIdx[LEVEL].clear();
mValIdx[LEVEL].resize(mSrcNodeAcc.nodeCount(LEVEL) + 1, stats);// minimum 1 entry
forEach(1, mValIdx[LEVEL].size(), 8, [&](const Range1D& r){
for (auto i = r.begin(); i!=r.end(); ++i) {
auto &srcNode = mSrcNodeAcc.template node<LEVEL>(i-1);
if constexpr(BuildTraits<DstBuildT>::is_onindex) {// resolved at compile time
mValIdx[LEVEL][i] += srcNode.getValueMask().countOn();
} else {
static const uint64_t maxTileCount = uint64_t(1u) << 3*srcNode.LOG2DIM;
mValIdx[LEVEL][i] += maxTileCount - srcNode.getChildMask().countOn();
}
}
});
mValIdx[LEVEL][0] = valueCount;
for (size_t i=1; i<mValIdx[LEVEL].size(); ++i) mValIdx[LEVEL][i] += mValIdx[LEVEL][i-1];// pre-fixed sum
return mValIdx[LEVEL].back();
}// CreateNanoGrid::countTileValues<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_index, uint64_t>::type
CreateNanoGrid<SrcGridT>::countValues()
{
const uint64_t stats = mIncludeStats ? 4u : 0u;// minimum, maximum, average, and deviation
uint64_t valueCount = 1u;// offset 0 corresponds to the background value
if (mIncludeTiles) {
if constexpr(BuildTraits<DstBuildT>::is_onindex) {
for (auto it = mSrcNodeAcc.root().cbeginValueOn(); it; ++it) ++valueCount;
} else {
for (auto it = mSrcNodeAcc.root().cbeginValueAll(); it; ++it) ++valueCount;
}
valueCount += stats;// optionally append stats for the root node
valueCount = countTileValues<DstBuildT, 2>(valueCount);
valueCount = countTileValues<DstBuildT, 1>(valueCount);
}
mValIdx[0].clear();
mValIdx[0].resize(mSrcNodeAcc.nodeCount(0) + 1, 512u + stats);// minimum 1 entry
if constexpr(BuildTraits<DstBuildT>::is_onindex) {
forEach(1, mValIdx[0].size(), 8, [&](const Range1D& r) {
for (auto i = r.begin(); i != r.end(); ++i) {
mValIdx[0][i] = stats;
mValIdx[0][i] += mSrcNodeAcc.template node<0>(i-1).getValueMask().countOn();
}
});
}
mValIdx[0][0] = valueCount;
prefixSum(mValIdx[0], true);// inclusive prefix sum
return mValIdx[0].back();
}// CreateNanoGrid::countValues<ValueIndex or ValueOnIndex>()
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::preProcess(uint32_t channels)
{
const uint64_t valueCount = this->template countValues<DstBuildT>();
mLeafNodeSize = mSrcNodeAcc.nodeCount(0)*NanoLeaf<DstBuildT>::DataType::memUsage();
uint32_t order = mBlindMetaData.size();
for (uint32_t i=0; i<channels; ++i) {
mBlindMetaData.emplace("channel_"+std::to_string(i),
toStr(mapToGridType<SrcValueT>()),
GridBlindDataClass::AttributeArray,
order++,
valueCount,
sizeof(SrcValueT));
}
if (mSrcNodeAcc.hasLongGridName()) {
this->addBlindData("grid name",
GridBlindDataSemantic::Unknown,
GridBlindDataClass::GridName,
GridType::Unknown,
mSrcNodeAcc.getName().length() + 1, 1);
}
}// preProcess<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename disable_if<BuildTraits<DstBuildT>::is_special>::type
CreateNanoGrid<SrcGridT>::processLeafs()
{
using DstDataT = typename NanoLeaf<DstBuildT>::DataType;
using DstValueT = typename DstDataT::ValueType;
static_assert(DstDataT::FIXED_SIZE, "Expected destination LeafNode<T> to have fixed size");
forEach(0, mSrcNodeAcc.nodeCount(0), 8, [&](const Range1D& r) {
auto *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
if (DstDataT::padding()>0u) {
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
std::memset(reinterpret_cast<void*>(dstData), 0, DstDataT::memUsage());
} else {
dstData->mBBoxDif[0] = dstData->mBBoxDif[1] = dstData->mBBoxDif[2] = 0u;
dstData->mFlags = 0u;// enable rendering, no bbox, no stats
dstData->mMinimum = dstData->mMaximum = typename DstDataT::ValueType();
dstData->mAverage = dstData->mStdDevi = 0;
}
dstData->mBBoxMin = srcLeaf.origin(); // copy origin of node
dstData->mValueMask = srcLeaf.getValueMask(); // copy value mask
DstValueT *dst = dstData->mValues;
if constexpr(is_same<DstValueT, SrcValueT>::value && SrcNodeAccT::IS_OPENVDB) {
const SrcValueT *src = srcLeaf.buffer().data();
for (auto *end = dst + 512u; dst != end; dst += 4, src += 4) {
dst[0] = src[0]; // copy *all* voxel values in sets of four, i.e. loop-unrolling
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
}
} else {
for (uint32_t j=0; j<512u; ++j) *dst++ = static_cast<DstValueT>(srcLeaf.getValue(j));
}
}
});
} // CreateNanoGrid::processLeafs<T>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::processLeafs()
{
using DstDataT = typename NanoLeaf<DstBuildT>::DataType;
static_assert(DstDataT::FIXED_SIZE, "Expected destination LeafNode<ValueIndex> to have fixed size");
static_assert(DstDataT::padding()==0u, "Expected leaf nodes to have no padding");
forEach(0, mSrcNodeAcc.nodeCount(0), 8, [&](const Range1D& r) {
const uint8_t flags = mIncludeStats ? 16u : 0u;// 4th bit indicates stats
DstDataT *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();// fixed size
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
dstData->mBBoxMin = srcLeaf.origin(); // copy origin of node
dstData->mBBoxDif[0] = dstData->mBBoxDif[1] = dstData->mBBoxDif[2] = 0u;
dstData->mFlags = flags;
dstData->mValueMask = srcLeaf.getValueMask(); // copy value mask
dstData->mOffset = mValIdx[0][i];
if constexpr(BuildTraits<DstBuildT>::is_onindex) {
const uint64_t *w = dstData->mValueMask.words();
#ifdef USE_OLD_VALUE_ON_INDEX
int32_t sum = CountOn(*w++);
uint8_t *p = reinterpret_cast<uint8_t*>(&dstData->mPrefixSum), *q = p + 7;
for (int j=0; j<7; ++j) {
*p++ = sum & 255u;
*q |= (sum >> 8) << j;
sum += CountOn(*w++);
}
#else
uint64_t &prefixSum = dstData->mPrefixSum, sum = CountOn(*w++);
prefixSum = sum;
for (int n = 9; n < 55; n += 9) {// n=i*9 where i=1,2,..6
sum += CountOn(*w++);
prefixSum |= sum << n;// each pre-fixed sum is encoded in 9 bits
}
#endif
} else {
dstData->mPrefixSum = 0u;
}
if constexpr(BuildTraits<DstBuildT>::is_indexmask) dstData->mMask = dstData->mValueMask;
}
});
} // CreateNanoGrid::processLeafs<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<is_same<ValueMask, DstBuildT>::value>::type
CreateNanoGrid<SrcGridT>::processLeafs()
{
using DstDataT = typename NanoLeaf<ValueMask>::DataType;
static_assert(DstDataT::FIXED_SIZE, "Expected destination LeafNode<ValueMask> to have fixed size");
forEach(0, mSrcNodeAcc.nodeCount(0), 8, [&](const Range1D& r) {
auto *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
if (DstDataT::padding()>0u) {
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
std::memset(reinterpret_cast<void*>(dstData), 0, DstDataT::memUsage());
} else {
dstData->mBBoxDif[0] = dstData->mBBoxDif[1] = dstData->mBBoxDif[2] = 0u;
dstData->mFlags = 0u;// enable rendering, no bbox, no stats
dstData->mPadding[0] = dstData->mPadding[1] = 0u;
}
dstData->mBBoxMin = srcLeaf.origin(); // copy origin of node
dstData->mValueMask = srcLeaf.getValueMask(); // copy value mask
}
});
} // CreateNanoGrid::processLeafs<ValueMask>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<is_same<bool, DstBuildT>::value>::type
CreateNanoGrid<SrcGridT>::processLeafs()
{
using DstDataT = typename NanoLeaf<bool>::DataType;
static_assert(DstDataT::FIXED_SIZE, "Expected destination LeafNode<bool> to have fixed size");
forEach(0, mSrcNodeAcc.nodeCount(0), 8, [&](const Range1D& r) {
auto *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
if (DstDataT::padding()>0u) {
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
std::memset(reinterpret_cast<void*>(dstData), 0, DstDataT::memUsage());
} else {
dstData->mBBoxDif[0] = dstData->mBBoxDif[1] = dstData->mBBoxDif[2] = 0u;
dstData->mFlags = 0u;// enable rendering, no bbox, no stats
}
dstData->mBBoxMin = srcLeaf.origin(); // copy origin of node
dstData->mValueMask = srcLeaf.getValueMask(); // copy value mask
if constexpr(!is_same<bool, SrcBuildT>::value) {
for (int j=0; j<512; ++j) dstData->mValues.set(j, static_cast<bool>(srcLeaf.getValue(j)));
} else if constexpr(SrcNodeAccT::IS_OPENVDB) {
dstData->mValues = *reinterpret_cast<const Mask<3>*>(srcLeaf.buffer().data());
} else if constexpr(SrcNodeAccT::IS_NANOVDB) {
dstData->mValues = srcLeaf.data()->mValues;
} else {// build::Leaf
dstData->mValues = srcLeaf.mValues; // copy value mask
}
}
});
} // CreateNanoGrid::processLeafs<bool>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_FpX>::type
CreateNanoGrid<SrcGridT>::processLeafs()
{
using DstDataT = typename NanoLeaf<DstBuildT>::DataType;
static_assert(DstDataT::FIXED_SIZE, "Expected destination LeafNode<Fp4|Fp8|Fp16> to have fixed size");
using ArrayT = typename DstDataT::ArrayType;
static_assert(is_same<float, SrcValueT>::value, "Expected ValueT == float");
using FloatT = typename std::conditional<DstDataT::bitWidth()>=16, double, float>::type;// 16 compression and higher requires double
static constexpr FloatT UNITS = FloatT((1 << DstDataT::bitWidth()) - 1);// # of unique non-zero values
DitherLUT lut(mDitherOn);
forEach(0, mSrcNodeAcc.nodeCount(0), 8, [&](const Range1D& r) {
auto *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
if (DstDataT::padding()>0u) {
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
std::memset(reinterpret_cast<void*>(dstData), 0, DstDataT::memUsage());
} else {
dstData->mFlags = dstData->mBBoxDif[2] = dstData->mBBoxDif[1] = dstData->mBBoxDif[0] = 0u;
dstData->mDev = dstData->mAvg = dstData->mMax = dstData->mMin = 0u;
}
dstData->mBBoxMin = srcLeaf.origin(); // copy origin of node
dstData->mValueMask = srcLeaf.getValueMask(); // copy value mask
// compute extrema values
float min = std::numeric_limits<float>::max(), max = -min;
for (uint32_t j=0; j<512u; ++j) {
const float v = srcLeaf.getValue(j);
if (v < min) min = v;
if (v > max) max = v;
}
dstData->init(min, max, DstDataT::bitWidth());
// perform quantization relative to the values in the current leaf node
const FloatT encode = UNITS/(max-min);
uint32_t offset = 0;
auto quantize = [&]()->ArrayT{
const ArrayT tmp = static_cast<ArrayT>(encode * (srcLeaf.getValue(offset) - min) + lut(offset));
++offset;
return tmp;
};
auto *code = reinterpret_cast<ArrayT*>(dstData->mCode);
if (is_same<Fp4, DstBuildT>::value) {// resolved at compile-time
for (uint32_t j=0; j<128u; ++j) {
auto tmp = quantize();
*code++ = quantize() << 4 | tmp;
tmp = quantize();
*code++ = quantize() << 4 | tmp;
}
} else {
for (uint32_t j=0; j<128u; ++j) {
*code++ = quantize();
*code++ = quantize();
*code++ = quantize();
*code++ = quantize();
}
}
}
});
} // CreateNanoGrid::processLeafs<Fp4, Fp8, Fp16>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<is_same<FpN, DstBuildT>::value>::type
CreateNanoGrid<SrcGridT>::processLeafs()
{
static_assert(is_same<float, SrcValueT>::value, "Expected SrcValueT == float");
DitherLUT lut(mDitherOn);
forEach(0, mSrcNodeAcc.nodeCount(0), 8, [&](const Range1D& r) {
for (auto i = r.begin(); i != r.end(); ++i) {
auto &srcLeaf = mSrcNodeAcc.template node<0>(i);
auto *dstData = this->template dstNode<DstBuildT,0>(i)->data();
dstData->mBBoxMin = srcLeaf.origin(); // copy origin of node
dstData->mBBoxDif[0] = dstData->mBBoxDif[1] = dstData->mBBoxDif[2] = 0u;
const uint8_t logBitWidth = mCodec[i].log2;
dstData->mFlags = logBitWidth << 5;// pack logBitWidth into 3 MSB of mFlag
dstData->mValueMask = srcLeaf.getValueMask(); // copy value mask
const float min = mCodec[i].min, max = mCodec[i].max;
dstData->init(min, max, uint8_t(1) << logBitWidth);
// perform quantization relative to the values in the current leaf node
uint32_t offset = 0;
float encode = 0.0f;
auto quantize = [&]()->uint8_t{
const uint8_t tmp = static_cast<uint8_t>(encode * (srcLeaf.getValue(offset) - min) + lut(offset));
++offset;
return tmp;
};
auto *dst = reinterpret_cast<uint8_t*>(dstData+1);
switch (logBitWidth) {
case 0u: {// 1 bit
encode = 1.0f/(max - min);
for (int j=0; j<64; ++j) {
uint8_t a = 0;
for (int k=0; k<8; ++k) a |= quantize() << k;
*dst++ = a;
}
}
break;
case 1u: {// 2 bits
encode = 3.0f/(max - min);
for (int j=0; j<128; ++j) {
auto a = quantize();
a |= quantize() << 2;
a |= quantize() << 4;
*dst++ = quantize() << 6 | a;
}
}
break;
case 2u: {// 4 bits
encode = 15.0f/(max - min);
for (int j=0; j<128; ++j) {
auto a = quantize();
*dst++ = quantize() << 4 | a;
a = quantize();
*dst++ = quantize() << 4 | a;
}
}
break;
case 3u: {// 8 bits
encode = 255.0f/(max - min);
for (int j=0; j<128; ++j) {
*dst++ = quantize();
*dst++ = quantize();
*dst++ = quantize();
*dst++ = quantize();
}
}
break;
default: {// 16 bits - special implementation using higher bit-precision
auto *dst = reinterpret_cast<uint16_t*>(dstData+1);
const double encode = 65535.0/(max - min);// note that double is required!
for (int j=0; j<128; ++j) {
*dst++ = uint16_t(encode * (srcLeaf.getValue(offset) - min) + lut(offset)); ++offset;
*dst++ = uint16_t(encode * (srcLeaf.getValue(offset) - min) + lut(offset)); ++offset;
*dst++ = uint16_t(encode * (srcLeaf.getValue(offset) - min) + lut(offset)); ++offset;
*dst++ = uint16_t(encode * (srcLeaf.getValue(offset) - min) + lut(offset)); ++offset;
}
}
}// end switch
}
});// kernel
} // CreateNanoGrid::processLeafs<FpN>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT, int LEVEL>
inline typename enable_if<!BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::processInternalNodes()
{
using DstNodeT = typename NanoNode<DstBuildT, LEVEL>::type;
using DstValueT = typename DstNodeT::ValueType;
using DstChildT = typename NanoNode<DstBuildT, LEVEL-1>::type;
static_assert(LEVEL == 1 || LEVEL == 2, "Expected internal node");
const uint64_t nodeCount = mSrcNodeAcc.nodeCount(LEVEL);
if (nodeCount > 0) {// compute and temporarily encode IDs of child nodes
uint64_t childCount = 0;
auto *dstData = this->template dstNode<DstBuildT,LEVEL>(0)->data();
for (uint64_t i=0; i<nodeCount; ++i) {
dstData[i].mFlags = childCount;
childCount += mSrcNodeAcc.template node<LEVEL>(i).getChildMask().countOn();
}
}
forEach(0, nodeCount, 4, [&](const Range1D& r) {
auto *dstData = this->template dstNode<DstBuildT,LEVEL>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcNode = mSrcNodeAcc.template node<LEVEL>(i);
uint64_t childID = dstData->mFlags;
if (DstNodeT::DataType::padding()>0u) {
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
std::memset(reinterpret_cast<void*>(dstData), 0, DstNodeT::memUsage());
} else {
dstData->mFlags = 0;// enable rendering, no bbox, no stats
dstData->mMinimum = dstData->mMaximum = typename DstNodeT::ValueType();
dstData->mAverage = dstData->mStdDevi = 0;
}
dstData->mBBox[0] = srcNode.origin(); // copy origin of node
dstData->mValueMask = srcNode.getValueMask(); // copy value mask
dstData->mChildMask = srcNode.getChildMask(); // copy child mask
for (auto it = srcNode.cbeginChildAll(); it; ++it) {
SrcValueT value{}; // default initialization
if (it.probeChild(value)) {
DstChildT *dstChild = this->template dstNode<DstBuildT,LEVEL-1>(childID++);// might be Leaf<FpN>
dstData->setChild(it.pos(), dstChild);
} else {
dstData->setValue(it.pos(), static_cast<DstValueT>(value));
}
}
}
});
} // CreateNanoGrid::processInternalNodes<T>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT, int LEVEL>
inline typename enable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::processInternalNodes()
{
using DstNodeT = typename NanoNode<DstBuildT, LEVEL>::type;
using DstChildT = typename NanoNode<DstBuildT, LEVEL-1>::type;
static_assert(LEVEL == 1 || LEVEL == 2, "Expected internal node");
static_assert(DstNodeT::DataType::padding()==0u, "Expected internal nodes to have no padding");
const uint64_t nodeCount = mSrcNodeAcc.nodeCount(LEVEL);
if (nodeCount > 0) {// compute and temporarily encode IDs of child nodes
uint64_t childCount = 0;
auto *dstData = this->template dstNode<DstBuildT,LEVEL>(0)->data();
for (uint64_t i=0; i<nodeCount; ++i) {
dstData[i].mFlags = childCount;
childCount += mSrcNodeAcc.template node<LEVEL>(i).getChildMask().countOn();
}
}
forEach(0, nodeCount, 4, [&](const Range1D& r) {
auto *dstData = this->template dstNode<DstBuildT,LEVEL>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto &srcNode = mSrcNodeAcc.template node<LEVEL>(i);
uint64_t childID = dstData->mFlags;
dstData->mFlags = 0u;
dstData->mBBox[0] = srcNode.origin(); // copy origin of node
dstData->mValueMask = srcNode.getValueMask(); // copy value mask
dstData->mChildMask = srcNode.getChildMask(); // copy child mask
uint64_t n = mIncludeTiles ? mValIdx[LEVEL][i] : 0u;
for (auto it = srcNode.cbeginChildAll(); it; ++it) {
SrcValueT value;
if (it.probeChild(value)) {
DstChildT *dstChild = this->template dstNode<DstBuildT,LEVEL-1>(childID++);// might be Leaf<FpN>
dstData->setChild(it.pos(), dstChild);
} else {
uint64_t m = 0u;
if (mIncludeTiles && !((BuildTraits<DstBuildT>::is_onindex) && dstData->mValueMask.isOff(it.pos()))) m = n++;
dstData->setValue(it.pos(), m);
}
}
if (mIncludeTiles && mIncludeStats) {// stats are always placed after the tile values
dstData->mMinimum = n++;
dstData->mMaximum = n++;
dstData->mAverage = n++;
dstData->mStdDevi = n++;
} else {// if not tiles or stats set stats to the background offset
dstData->mMinimum = 0u;
dstData->mMaximum = 0u;
dstData->mAverage = 0u;
dstData->mStdDevi = 0u;
}
}
});
} // CreateNanoGrid::processInternalNodes<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<!BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::processRoot()
{
using DstRootT = NanoRoot<DstBuildT>;
using DstValueT = typename DstRootT::ValueType;
auto &srcRoot = mSrcNodeAcc.root();
auto *dstData = this->template dstRoot<DstBuildT>()->data();
const uint32_t tableSize = srcRoot.getTableSize();
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
if (DstRootT::DataType::padding()>0) std::memset(reinterpret_cast<void*>(dstData), 0, DstRootT::memUsage(tableSize));
dstData->mTableSize = tableSize;
dstData->mMinimum = dstData->mMaximum = dstData->mBackground = srcRoot.background();
dstData->mBBox = CoordBBox(); // // set to an empty bounding box
if (tableSize==0) return;
auto *dstChild = this->template dstNode<DstBuildT, 2>(0);// fixed size and linear in memory
auto *dstTile = dstData->tile(0);// fixed size and linear in memory
for (auto it = srcRoot.cbeginChildAll(); it; ++it, ++dstTile) {
SrcValueT value;
if (it.probeChild(value)) {
dstTile->setChild(it.getCoord(), dstChild++, dstData);
} else {
dstTile->setValue(it.getCoord(), it.isValueOn(), static_cast<DstValueT>(value));
}
}
} // CreateNanoGrid::processRoot<T>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::processRoot()
{
using DstRootT = NanoRoot<DstBuildT>;
auto &srcRoot = mSrcNodeAcc.root();
auto *dstData = this->template dstRoot<DstBuildT>()->data();
const uint32_t tableSize = srcRoot.getTableSize();
// Cast to void* to avoid compiler warning about missing trivial copy-assignment
if (DstRootT::DataType::padding()>0) std::memset(reinterpret_cast<void*>(dstData), 0, DstRootT::memUsage(tableSize));
dstData->mTableSize = tableSize;
dstData->mBackground = 0u;
uint64_t valueCount = 0u;// the first entry is always the background value
dstData->mBBox = CoordBBox(); // set to an empty/invalid bounding box
if (tableSize>0) {
auto *dstChild = this->template dstNode<DstBuildT, 2>(0);// fixed size and linear in memory
auto *dstTile = dstData->tile(0);// fixed size and linear in memory
for (auto it = srcRoot.cbeginChildAll(); it; ++it, ++dstTile) {
SrcValueT tmp;
if (it.probeChild(tmp)) {
dstTile->setChild(it.getCoord(), dstChild++, dstData);
} else {
dstTile->setValue(it.getCoord(), it.isValueOn(), 0u);
if (mIncludeTiles && !((BuildTraits<DstBuildT>::is_onindex) && !dstTile->state)) dstTile->value = ++valueCount;
}
}
}
if (mIncludeTiles && mIncludeStats) {// stats are always placed after the tile values
dstData->mMinimum = ++valueCount;
dstData->mMaximum = ++valueCount;
dstData->mAverage = ++valueCount;
dstData->mStdDevi = ++valueCount;
} else if (dstData->padding()==0) {
dstData->mMinimum = 0u;
dstData->mMaximum = 0u;
dstData->mAverage = 0u;
dstData->mStdDevi = 0u;
}
} // CreateNanoGrid::processRoot<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
void CreateNanoGrid<SrcGridT>::processTree()
{
const uint64_t nodeCount[3] = {mSrcNodeAcc.nodeCount(0), mSrcNodeAcc.nodeCount(1), mSrcNodeAcc.nodeCount(2)};
auto *dstTree = this->template dstTree<DstBuildT>();
auto *dstData = dstTree->data();
dstData->setRoot( this->template dstRoot<DstBuildT>() );
dstData->setFirstNode(nodeCount[2] ? this->template dstNode<DstBuildT, 2>(0) : nullptr);
dstData->setFirstNode(nodeCount[1] ? this->template dstNode<DstBuildT, 1>(0) : nullptr);
dstData->setFirstNode(nodeCount[0] ? this->template dstNode<DstBuildT, 0>(0) : nullptr);
dstData->mNodeCount[0] = static_cast<uint32_t>(nodeCount[0]);
dstData->mNodeCount[1] = static_cast<uint32_t>(nodeCount[1]);
dstData->mNodeCount[2] = static_cast<uint32_t>(nodeCount[2]);
// Count number of active leaf level tiles
dstData->mTileCount[0] = reduce(Range1D(0,nodeCount[1]), uint32_t(0), [&](Range1D &r, uint32_t sum){
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<1>(i).getValueMask().countOn();
return sum;}, std::plus<uint32_t>());
// Count number of active lower internal node tiles
dstData->mTileCount[1] = reduce(Range1D(0,nodeCount[2]), uint32_t(0), [&](Range1D &r, uint32_t sum){
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<2>(i).getValueMask().countOn();
return sum;}, std::plus<uint32_t>());
// Count number of active upper internal node tiles
dstData->mTileCount[2] = 0;
for (auto it = mSrcNodeAcc.root().cbeginValueOn(); it; ++it) dstData->mTileCount[2] += 1;
// Count number of active voxels
dstData->mVoxelCount = reduce(Range1D(0, nodeCount[0]), uint64_t(0), [&](Range1D &r, uint64_t sum){
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<0>(i).getValueMask().countOn();
return sum;}, std::plus<uint64_t>());
dstData->mVoxelCount += uint64_t(dstData->mTileCount[0]) << 9;// = 3 * 3
dstData->mVoxelCount += uint64_t(dstData->mTileCount[1]) << 21;// = 3 * (3+4)
dstData->mVoxelCount += uint64_t(dstData->mTileCount[2]) << 36;// = 3 * (3+4+5)
} // CreateNanoGrid::processTree
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
void CreateNanoGrid<SrcGridT>::processGrid()
{
auto* dstData = this->template dstGrid<DstBuildT>()->data();
dstData->init({GridFlags::IsBreadthFirst}, mOffset.size, mSrcNodeAcc.map(),
mapToGridType<DstBuildT>(), mapToGridClass<DstBuildT>(mSrcNodeAcc.gridClass()));
dstData->mBlindMetadataCount = static_cast<uint32_t>(mBlindMetaData.size());
dstData->mData1 = this->valueCount();
if (!isValid(dstData->mGridType, dstData->mGridClass)) {
#if 1
fprintf(stderr,"Warning: Strange combination of GridType(\"%s\") and GridClass(\"%s\"). Consider changing GridClass to \"Unknown\"\n",
toStr(dstData->mGridType), toStr(dstData->mGridClass));
#else
throw std::runtime_error("Invalid combination of GridType("+std::to_string(int(dstData->mGridType))+
") and GridClass("+std::to_string(int(dstData->mGridClass))+"). See NanoVDB.h for details!");
#endif
}
std::memset(dstData->mGridName, '\0', GridData::MaxNameSize);//overwrite mGridName
strncpy(dstData->mGridName, mSrcNodeAcc.getName().c_str(), GridData::MaxNameSize-1);
if (mSrcNodeAcc.hasLongGridName()) dstData->setLongGridNameOn();// grid name is long so store it as blind data
// Partially process blind meta data - they will be complete in postProcess
if (mBlindMetaData.size()>0) {
auto *metaData = this->dstMeta(0);
dstData->mBlindMetadataOffset = PtrDiff(metaData, dstData);
dstData->mBlindMetadataCount = static_cast<uint32_t>(mBlindMetaData.size());
char *blindData = PtrAdd<char>(mBufferPtr, mOffset.blind);
for (const auto &b : mBlindMetaData) {
std::memcpy(metaData, b.metaData, sizeof(GridBlindMetaData));
metaData->setBlindData(blindData);// sets metaData.mOffset
if (metaData->mDataClass == GridBlindDataClass::GridName) strcpy(blindData, mSrcNodeAcc.getName().c_str());
++metaData;
blindData += b.size;
}
mBlindMetaData.clear();
}
} // CreateNanoGrid::processGrid
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename disable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::postProcess()
{
if constexpr(is_same<FpN, DstBuildT>::value) mCodec.reset();
auto *dstGrid = this->template dstGrid<DstBuildT>();
gridStats(*dstGrid, mStats);
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
auto *metaData = this->dstMeta(0);
if constexpr(is_same<openvdb::tools::PointIndexGrid, SrcGridT>::value ||
is_same<openvdb::points::PointDataGrid, SrcGridT>::value) {
static_assert(is_same<DstBuildT, uint32_t>::value, "expected DstBuildT==uint32_t");
auto *dstData0 = this->template dstNode<DstBuildT,0>(0)->data();
dstData0->mMinimum = 0; // start of prefix sum
dstData0->mMaximum = dstData0->mValues[511u];
for (uint32_t i=1, n=mSrcNodeAcc.nodeCount(0); i<n; ++i) {
auto *dstData1 = dstData0 + 1;
dstData1->mMinimum = dstData0->mMinimum + dstData0->mMaximum;
dstData1->mMaximum = dstData1->mValues[511u];
dstData0 = dstData1;
}
for (size_t i = 0, n = dstGrid->blindDataCount(); i < n; ++i, ++metaData) {
if constexpr(is_same<openvdb::tools::PointIndexGrid, SrcGridT>::value) {
if (metaData->mDataClass != GridBlindDataClass::IndexArray) continue;
if (metaData->mDataType == GridType::UInt32) {
uint32_t *blindData = const_cast<uint32_t*>(metaData->template getBlindData<uint32_t>());
forEach(0, mSrcNodeAcc.nodeCount(0), 16, [&](const auto& r) {
auto *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();
for (auto j = r.begin(); j != r.end(); ++j, ++dstData) {
uint32_t* p = blindData + dstData->mMinimum;
for (uint32_t idx : mSrcNodeAcc.template node<0>(j).indices()) *p++ = idx;
}
});
}
} else {// if constexpr(is_same<openvdb::points::PointDataGrid, SrcGridT>::value)
if (metaData->mDataClass != GridBlindDataClass::AttributeArray) continue;
if (auto *blindData = dstGrid->template getBlindData<float>(i)) {
this->template copyPointAttribute<DstBuildT>(i, blindData);
} else if (auto *blindData = dstGrid->template getBlindData<nanovdb::Vec3f>(i)) {
this->template copyPointAttribute<DstBuildT>(i, reinterpret_cast< openvdb::Vec3f*>(blindData));
} else if (auto *blindData = dstGrid->template getBlindData<int32_t>(i)) {
this->template copyPointAttribute<DstBuildT>(i, blindData);
} else if (auto *blindData = dstGrid->template getBlindData<int64_t>(i)) {
this->template copyPointAttribute<DstBuildT>(i, blindData);
} else {
std::cerr << "unsupported point attribute \"" << toStr(metaData->mDataType) << "\"\n";
}
}// if
}// loop
} else { // if
(void)metaData;
}
#endif
updateChecksum(*dstGrid, mChecksum);
}// CreateNanoGrid::postProcess<T>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
inline typename enable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::postProcess(uint32_t channels)
{
const std::string typeName = toStr(mapToGridType<SrcValueT>());
const uint64_t valueCount = this->valueCount();
auto *dstGrid = this->template dstGrid<DstBuildT>();
for (uint32_t i=0; i<channels; ++i) {
const std::string name = "channel_"+std::to_string(i);
int j = dstGrid->findBlindData(name.c_str());
if (j<0) throw std::runtime_error("missing " + name);
auto *metaData = this->dstMeta(j);// partially set in processGrid
metaData->mDataClass = GridBlindDataClass::ChannelArray;
metaData->mDataType = mapToGridType<SrcValueT>();
SrcValueT *blindData = const_cast<SrcValueT*>(metaData->template getBlindData<SrcValueT>());
if (i>0) {// concurrent copy from previous channel
nanovdb::forEach(0,valueCount,1024,[&](const nanovdb::Range1D &r){
SrcValueT *dst=blindData+r.begin(), *end=dst+r.size(), *src=dst-valueCount;
while(dst!=end) *dst++ = *src++;
});
} else {
this->template copyValues<DstBuildT>(blindData);
}
}// loop over channels
gridStats(*(this->template dstGrid<DstBuildT>()), std::min(StatsMode::BBox, mStats));
updateChecksum(*dstGrid, mChecksum);
}// CreateNanoGrid::postProcess<ValueIndex or ValueOnIndex>
//================================================================================================
template <typename SrcGridT>
template <typename DstBuildT>
typename enable_if<BuildTraits<DstBuildT>::is_index>::type
CreateNanoGrid<SrcGridT>::copyValues(SrcValueT *buffer)
{// copy values from the source grid into the provided buffer
assert(mBufferPtr && buffer);
using StatsT = typename FloatTraits<SrcValueT>::FloatType;
if (this->valueCount()==0) this->template countValues<DstBuildT>();
auto copyNodeValues = [&](const auto &node, SrcValueT *v) {
if constexpr(BuildTraits<DstBuildT>::is_onindex) {
for (auto it = node.cbeginValueOn(); it; ++it) *v++ = *it;
} else {
for (auto it = node.cbeginValueAll(); it; ++it) *v++ = *it;
}
if (mIncludeStats) {
if constexpr(SrcNodeAccT::IS_NANOVDB) {// resolved at compile time
*v++ = node.minimum();
*v++ = node.maximum();
if constexpr(is_same<SrcValueT, StatsT>::value) {
*v++ = node.average();
*v++ = node.stdDeviation();
} else {// eg when SrcValueT=Vec3f and StatsT=float
*v++ = SrcValueT(node.average());
*v++ = SrcValueT(node.stdDeviation());
}
} else {// openvdb and nanovdb::build::Grid have no stats
*v++ = buffer[0];// background
*v++ = buffer[0];// background
*v++ = buffer[0];// background
*v++ = buffer[0];// background
}
}
};// copyNodeValues
const SrcRootT &root = mSrcNodeAcc.root();
buffer[0] = root.background();// Value array always starts with the background value
if (mIncludeTiles) {
copyNodeValues(root, buffer + 1u);
forEach(0, mSrcNodeAcc.nodeCount(2), 1, [&](const Range1D& r) {
for (auto i = r.begin(); i!=r.end(); ++i) {
copyNodeValues(mSrcNodeAcc.template node<2>(i), buffer + mValIdx[2][i]);
}
});
forEach(0, mSrcNodeAcc.nodeCount(1), 1, [&](const Range1D& r) {
for (auto i = r.begin(); i!=r.end(); ++i) {
copyNodeValues(mSrcNodeAcc.template node<1>(i), buffer + mValIdx[1][i]);
}
});
}
forEach(0, mSrcNodeAcc.nodeCount(0), 4, [&](const Range1D& r) {
for (auto i = r.begin(); i!=r.end(); ++i) {
copyNodeValues(mSrcNodeAcc.template node<0>(i), buffer + mValIdx[0][i]);
}
});
}// CreateNanoGrid::copyValues<ValueIndex or ValueOnIndex>
//================================================================================================
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
template <typename SrcGridT>
template<typename T>
typename disable_if<is_same<T, openvdb::tools::PointIndexGrid>::value ||
is_same<T, openvdb::points::PointDataGrid>::value, uint64_t>::type
CreateNanoGrid<SrcGridT>::countPoints() const
{
static_assert(is_same<T, SrcGridT>::value, "expected default template parameter");
return 0u;
}// CreateNanoGrid::countPoints<T>
template <typename SrcGridT>
template<typename T>
typename enable_if<is_same<T, openvdb::tools::PointIndexGrid>::value ||
is_same<T, openvdb::points::PointDataGrid>::value, uint64_t>::type
CreateNanoGrid<SrcGridT>::countPoints() const
{
static_assert(is_same<T, SrcGridT>::value, "expected default template parameter");
return reduce(0, mSrcNodeAcc.nodeCount(0), 8, uint64_t(0), [&](auto &r, uint64_t sum) {
for (auto i=r.begin(); i!=r.end(); ++i) sum += mSrcNodeAcc.template node<0>(i).getLastValue();
return sum;}, std::plus<uint64_t>());
}// CreateNanoGrid::countPoints<PointIndexGrid or PointDataGrid>
template <typename SrcGridT>
template<typename DstBuildT, typename AttT, typename CodecT, typename T>
typename enable_if<is_same<openvdb::points::PointDataGrid, T>::value>::type
CreateNanoGrid<SrcGridT>::copyPointAttribute(size_t attIdx, AttT *attPtr)
{
static_assert(std::is_same<SrcGridT, T>::value, "Expected default parameter");
using HandleT = openvdb::points::AttributeHandle<AttT, CodecT>;
forEach(0, mSrcNodeAcc.nodeCount(0), 16, [&](const auto& r) {
auto *dstData = this->template dstNode<DstBuildT,0>(r.begin())->data();
for (auto i = r.begin(); i != r.end(); ++i, ++dstData) {
auto& srcLeaf = mSrcNodeAcc.template node<0>(i);
HandleT handle(srcLeaf.constAttributeArray(attIdx));
AttT *p = attPtr + dstData->mMinimum;
for (auto iter = srcLeaf.beginIndexOn(); iter; ++iter) *p++ = handle.get(*iter);
}
});
}// CreateNanoGrid::copyPointAttribute
#endif
//================================================================================================
template<typename SrcGridT, typename DstBuildT, typename BufferT>
typename disable_if<BuildTraits<DstBuildT>::is_index || BuildTraits<DstBuildT>::is_Fp, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
StatsMode sMode,
ChecksumMode cMode,
int verbose,
const BufferT &buffer)
{
CreateNanoGrid<SrcGridT> converter(srcGrid);
converter.setStats(sMode);
converter.setChecksum(cMode);
converter.setVerbose(verbose);
return converter.template getHandle<DstBuildT, BufferT>(buffer);
}// createNanoGrid<T>
//================================================================================================
template<typename SrcGridT, typename DstBuildT, typename BufferT>
typename enable_if<BuildTraits<DstBuildT>::is_index, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
uint32_t channels,
bool includeStats,
bool includeTiles,
int verbose,
const BufferT &buffer)
{
CreateNanoGrid<SrcGridT> converter(srcGrid);
converter.setVerbose(verbose);
return converter.template getHandle<DstBuildT, BufferT>(channels, includeStats, includeTiles, buffer);
}
//================================================================================================
template<typename SrcGridT, typename DstBuildT, typename OracleT, typename BufferT>
typename enable_if<is_same<FpN, DstBuildT>::value, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
StatsMode sMode,
ChecksumMode cMode,
bool ditherOn,
int verbose,
const OracleT &oracle,
const BufferT &buffer)
{
CreateNanoGrid<SrcGridT> converter(srcGrid);
converter.setStats(sMode);
converter.setChecksum(cMode);
converter.enableDithering(ditherOn);
converter.setVerbose(verbose);
return converter.template getHandle<DstBuildT, OracleT, BufferT>(oracle, buffer);
}// createNanoGrid<FpN>
//================================================================================================
template<typename SrcGridT, typename DstBuildT, typename BufferT>
typename enable_if<BuildTraits<DstBuildT>::is_FpX, GridHandle<BufferT>>::type
createNanoGrid(const SrcGridT &srcGrid,
StatsMode sMode,
ChecksumMode cMode,
bool ditherOn,
int verbose,
const BufferT &buffer)
{
CreateNanoGrid<SrcGridT> converter(srcGrid);
converter.setStats(sMode);
converter.setChecksum(cMode);
converter.enableDithering(ditherOn);
converter.setVerbose(verbose);
return converter.template getHandle<DstBuildT, BufferT>(buffer);
}// createNanoGrid<Fp4,8,16>
//================================================================================================
#if defined(NANOVDB_USE_OPENVDB) && !defined(__CUDACC__)
template<typename BufferT>
GridHandle<BufferT>
openToNanoVDB(const openvdb::GridBase::Ptr& base,
StatsMode sMode,
ChecksumMode cMode,
int verbose)
{
// We need to define these types because they are not defined in OpenVDB
using openvdb_Vec4fTree = typename openvdb::tree::Tree4<openvdb::Vec4f, 5, 4, 3>::Type;
using openvdb_Vec4dTree = typename openvdb::tree::Tree4<openvdb::Vec4d, 5, 4, 3>::Type;
using openvdb_Vec4fGrid = openvdb::Grid<openvdb_Vec4fTree>;
using openvdb_Vec4dGrid = openvdb::Grid<openvdb_Vec4dTree>;
using openvdb_UInt32Grid = openvdb::Grid<openvdb::UInt32Tree>;
if (auto grid = openvdb::GridBase::grid<openvdb::FloatGrid>(base)) {
return createNanoGrid<openvdb::FloatGrid, float, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::DoubleGrid>(base)) {
return createNanoGrid<openvdb::DoubleGrid, double, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::Int32Grid>(base)) {
return createNanoGrid<openvdb::Int32Grid, int32_t,BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::Int64Grid>(base)) {
return createNanoGrid<openvdb::Int64Grid, int64_t, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb_UInt32Grid>(base)) {
return createNanoGrid<openvdb_UInt32Grid, uint32_t, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::Vec3fGrid>(base)) {
return createNanoGrid<openvdb::Vec3fGrid, nanovdb::Vec3f, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::Vec3dGrid>(base)) {
return createNanoGrid<openvdb::Vec3dGrid, nanovdb::Vec3d, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::tools::PointIndexGrid>(base)) {
return createNanoGrid<openvdb::tools::PointIndexGrid, uint32_t, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::points::PointDataGrid>(base)) {
return createNanoGrid<openvdb::points::PointDataGrid, uint32_t, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::MaskGrid>(base)) {
return createNanoGrid<openvdb::MaskGrid, nanovdb::ValueMask, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb::BoolGrid>(base)) {
return createNanoGrid<openvdb::BoolGrid, bool, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb_Vec4fGrid>(base)) {
return createNanoGrid<openvdb_Vec4fGrid, nanovdb::Vec4f, BufferT>(*grid, sMode, cMode, verbose);
} else if (auto grid = openvdb::GridBase::grid<openvdb_Vec4dGrid>(base)) {
return createNanoGrid<openvdb_Vec4dGrid, nanovdb::Vec4d, BufferT>(*grid, sMode, cMode, verbose);
} else {
OPENVDB_THROW(openvdb::RuntimeError, "Unrecognized OpenVDB grid type");
}
}// openToNanoVDB
#endif
} // namespace nanovdb
#endif // NANOVDB_CREATE_NANOGRID_H_HAS_BEEN_INCLUDED
DitherLUT.h
Defines look up table to do dithering of 8^3 leaf nodes.
OPENVDB_THROW
#define OPENVDB_THROW(exception, message)
Definition Exceptions.h:74
ForEach.h
A unified wrapper for tbb::parallel_for and a naive std::thread fallback.
GridBuilder.h
This file defines a minimum set of tree nodes and tools that can be used (instead of OpenVDB) to buil...
GridChecksum.h
Computes a pair of 32bit checksums, of a Grid, by means of Cyclic Redundancy Check (CRC)
GridHandle.h
Defines GridHandle, which manages a host, and possibly a device, memory buffer containing one or more...
GridStats.h
Re-computes min/max/avg/var/bbox information for each node in a pre-existing NanoVDB grid.
Invoke.h
A unified wrapper for tbb::parallel_invoke and a naive std::thread analog.
NANOVDB_DATA_ALIGNMENT
#define NANOVDB_DATA_ALIGNMENT
Definition NanoVDB.h:154
NANOVDB_ASSERT
#define NANOVDB_ASSERT(x)
Definition NanoVDB.h:190
PointDataGrid.h
Attribute-owned data structure for points. Point attributes are stored in leaf nodes and ordered by v...
PointIndexGrid.h
Space-partitioning acceleration structure for points. Partitions the points into voxels to accelerate...
PrefixSum.h
Multi-threaded implementations of inclusive prefix sum.
Range.h
Custom Range class that is compatible with the tbb::blocked_range classes.
Reduce.h
A unified wrapper for tbb::parallel_reduce and a naive std::future analog.
nanovdb::AbsDiff
Compression oracle based on absolute difference.
Definition CreateNanoGrid.h:231
nanovdb::AbsDiff::~AbsDiff
~AbsDiff()=default
nanovdb::AbsDiff::operator()
bool operator()(float exact, float approx) const
Return true if the approximate value is within the accepted absolute error bounds of the exact value.
Definition CreateNanoGrid.h:255
nanovdb::AbsDiff::getTolerance
float getTolerance() const
Definition CreateNanoGrid.h:250
nanovdb::AbsDiff::AbsDiff
AbsDiff(float tolerance=-1.0f)
Definition CreateNanoGrid.h:235
nanovdb::AbsDiff::init
void init(nanovdb::GridClass gClass, float background)
Definition CreateNanoGrid.h:239
nanovdb::AbsDiff::AbsDiff
AbsDiff(const AbsDiff &)=default
nanovdb::AbsDiff::setTolerance
void setTolerance(float tolerance)
Definition CreateNanoGrid.h:249
nanovdb::CreateNanoGrid
Creates any nanovdb Grid from any source grid (certain combinations are obviously not allowed)
Definition CreateNanoGrid.h:524
nanovdb::CreateNanoGrid::addBlindData
uint64_t addBlindData(const std::string &name, GridBlindDataSemantic dataSemantic, GridBlindDataClass dataClass, GridType dataType, size_t count, size_t size)
Add blind data to the destination grid.
Definition CreateNanoGrid.h:607
nanovdb::CreateNanoGrid::SrcBuildT
typename SrcNodeAccT::BuildType SrcBuildT
Definition CreateNanoGrid.h:528
nanovdb::CreateNanoGrid::setStats
void setStats(StatsMode mode=StatsMode::Default)
Set the mode used for computing statistics of the destination grid.
Definition CreateNanoGrid.h:554
nanovdb::CreateNanoGrid::SrcTreeT
typename SrcNodeAccT::TreeType SrcTreeT
Definition CreateNanoGrid.h:530
nanovdb::CreateNanoGrid::setChecksum
void setChecksum(ChecksumMode mode=ChecksumMode::Default)
Set the mode used for computing checksums of the destination grid.
Definition CreateNanoGrid.h:558
nanovdb::CreateNanoGrid::copyValues
enable_if< BuildTraits< DstBuildT >::is_index >::type copyValues(SrcValueT *buffer)
Copy values from the source grid into a provided buffer.
Definition CreateNanoGrid.h:1850
nanovdb::CreateNanoGrid::SrcRootT
typename SrcNodeAccT::RootType SrcRootT
Definition CreateNanoGrid.h:531
nanovdb::CreateNanoGrid::enableDithering
void enableDithering(bool on=true)
Enable or disable dithering, i.e. randomization of the quantization error.
Definition CreateNanoGrid.h:550
nanovdb::CreateNanoGrid::getHandle
disable_if< is_same< DstBuildT, FpN >::value||BuildTraits< DstBuildT >::is_index, GridHandle< BufferT > >::type getHandle(const BufferT &buffer=BufferT())
Converts the source grid into a nanovdb grid with the specified destination build type.
Definition CreateNanoGrid.h:896
nanovdb::CreateNanoGrid::SrcNodeT
typename NodeTrait< SrcRootT, LEVEL >::type SrcNodeT
Definition CreateNanoGrid.h:533
nanovdb::CreateNanoGrid::setVerbose
void setVerbose(int mode=1)
Set the level of verbosity.
Definition CreateNanoGrid.h:545
nanovdb::CreateNanoGrid::CreateNanoGrid
CreateNanoGrid(const SrcGridT &srcGrid)
Constructor from a source grid.
Definition CreateNanoGrid.h:783
nanovdb::CreateNanoGrid::SrcNodeAccT
NodeAccessor< SrcGridT > SrcNodeAccT
Definition CreateNanoGrid.h:527
nanovdb::CreateNanoGrid::valueCount
uint64_t valueCount() const
This method only has affect when getHandle was called with DstBuildT = ValueIndex or ValueOnIndex.
Definition CreateNanoGrid.h:621
nanovdb::CreateNanoGrid::SrcValueT
typename SrcNodeAccT::ValueType SrcValueT
Definition CreateNanoGrid.h:529
nanovdb::GridHandle
This class serves to manage a buffer containing one or more NanoVDB Grids.
Definition GridHandle.h:38
nanovdb::Grid
Highest level of the data structure. Contains a tree and a world->index transform (that currently onl...
Definition NanoVDB.h:3699
nanovdb::Grid::ValueType
typename TreeT::ValueType ValueType
Definition NanoVDB.h:3708
nanovdb::Grid::TreeType
TreeT TreeType
Definition NanoVDB.h:3701
nanovdb::HostBuffer
This is a buffer that contains a shared or private pool to either externally or internally managed ho...
Definition HostBuffer.h:115
nanovdb::InternalNode::memUsage
static size_t memUsage()
Return memory usage in bytes for the class.
Definition NanoVDB.h:5169
nanovdb::LeafNode::DataType
LeafData< BuildT, CoordT, MaskT, Log2Dim > DataType
Definition NanoVDB.h:6036
nanovdb::LeafNode::memUsage
uint64_t memUsage() const
return memory usage in bytes for the leaf node
Definition NanoVDB.h:6242
nanovdb::NodeAccessor< NanoGrid< BuildT > >::tree
const TreeType & tree() const
Definition CreateNanoGrid.h:352
nanovdb::NodeAccessor< NanoGrid< BuildT > >::nodeCount
uint64_t nodeCount(int level) const
Definition CreateNanoGrid.h:354
nanovdb::NodeAccessor< NanoGrid< BuildT > >::node
const NodeType< LEVEL > & node(uint32_t i) const
Definition CreateNanoGrid.h:356
nanovdb::NodeAccessor< NanoGrid< BuildT > >::NodeAccessor
NodeAccessor(const GridType &grid)
Definition CreateNanoGrid.h:348
nanovdb::NodeAccessor< NanoGrid< BuildT > >::NodeType
typename NodeTrait< TreeType, LEVEL >::type NodeType
Definition CreateNanoGrid.h:347
nanovdb::NodeAccessor< NanoGrid< BuildT > >::ValueType
typename GridType::ValueType ValueType
Definition CreateNanoGrid.h:343
nanovdb::NodeAccessor< NanoGrid< BuildT > >::TreeType
typename GridType::TreeType TreeType
Definition CreateNanoGrid.h:344
nanovdb::NodeAccessor< NanoGrid< BuildT > >::getName
std::string getName() const
Definition CreateNanoGrid.h:357
nanovdb::NodeAccessor< NanoGrid< BuildT > >::RootType
typename TreeType::RootType RootType
Definition CreateNanoGrid.h:345
nanovdb::NodeAccessor< NanoGrid< BuildT > >::root
const RootType & root() const
Definition CreateNanoGrid.h:353
nanovdb::NodeAccessor< NanoGrid< BuildT > >::hasLongGridName
bool hasLongGridName() const
Definition CreateNanoGrid.h:358
nanovdb::NodeAccessor< NanoGrid< BuildT > >::grid
const GridType & grid() const
Definition CreateNanoGrid.h:351
nanovdb::NodeAccessor< NanoGrid< BuildT > >::gridClass
GridClass gridClass() const
Definition CreateNanoGrid.h:360
nanovdb::NodeAccessor< NanoGrid< BuildT > >::BuildType
BuildT BuildType
Definition CreateNanoGrid.h:340
nanovdb::NodeAccessor< NanoGrid< BuildT > >::map
const nanovdb::Map & map() const
Definition CreateNanoGrid.h:359
nanovdb::NodeAccessor
The NodeAccessor provides a uniform API for accessing nodes got NanoVDB, OpenVDB and build Grids.
Definition CreateNanoGrid.h:304
nanovdb::NodeAccessor::tree
const TreeType & tree() const
Definition CreateNanoGrid.h:317
nanovdb::NodeAccessor::IS_NANOVDB
static constexpr bool IS_NANOVDB
Definition CreateNanoGrid.h:307
nanovdb::NodeAccessor::nodeCount
uint64_t nodeCount(int level) const
Definition CreateNanoGrid.h:319
nanovdb::NodeAccessor::node
const NodeType< LEVEL > & node(uint32_t i) const
Definition CreateNanoGrid.h:321
nanovdb::NodeAccessor::getName
const std::string & getName() const
Definition CreateNanoGrid.h:322
nanovdb::NodeAccessor::IS_OPENVDB
static constexpr bool IS_OPENVDB
Definition CreateNanoGrid.h:306
nanovdb::NodeAccessor::RootType
typename TreeType::RootNodeType RootType
Definition CreateNanoGrid.h:312
nanovdb::NodeAccessor::BuildType
typename GridT::BuildType BuildType
Definition CreateNanoGrid.h:308
nanovdb::NodeAccessor::NodeType
typename NodeTrait< const TreeType, LEVEL >::type NodeType
Definition CreateNanoGrid.h:314
nanovdb::NodeAccessor::root
const RootType & root() const
Definition CreateNanoGrid.h:318
nanovdb::NodeAccessor::TreeType
typename GridT::TreeType TreeType
Definition CreateNanoGrid.h:311
nanovdb::NodeAccessor::ValueType
typename GridT::ValueType ValueType
Definition CreateNanoGrid.h:309
nanovdb::NodeAccessor::hasLongGridName
bool hasLongGridName() const
Definition CreateNanoGrid.h:323
nanovdb::NodeAccessor::grid
const GridType & grid() const
Definition CreateNanoGrid.h:316
nanovdb::NodeAccessor::NodeAccessor
NodeAccessor(const GridT &grid)
Definition CreateNanoGrid.h:315
nanovdb::NodeAccessor::gridClass
GridClass gridClass() const
Definition CreateNanoGrid.h:325
nanovdb::NodeAccessor::GridType
GridT GridType
Definition CreateNanoGrid.h:310
nanovdb::NodeAccessor::map
const nanovdb::Map & map() const
Definition CreateNanoGrid.h:324
nanovdb::NodeManagerHandle
NodeManagerHandle manages the memory of a NodeManager.
Definition NodeManager.h:55
nanovdb::NodeManager
NodeManager allows for sequential access to nodes.
Definition NodeManager.h:180
nanovdb::Range
Definition Range.h:28
nanovdb::RelDiff
Compression oracle based on relative difference.
Definition CreateNanoGrid.h:271
nanovdb::RelDiff::RelDiff
RelDiff(float tolerance=-1.0f)
Definition CreateNanoGrid.h:275
nanovdb::RelDiff::operator()
bool operator()(float exact, float approx) const
Return true if the approximate value is within the accepted relative error bounds of the exact value.
Definition CreateNanoGrid.h:285
nanovdb::RelDiff::RelDiff
RelDiff(const RelDiff &)=default
nanovdb::RelDiff::getTolerance
float getTolerance() const
Definition CreateNanoGrid.h:280
nanovdb::RelDiff::~RelDiff
~RelDiff()=default
nanovdb::RelDiff::setTolerance
void setTolerance(float tolerance)
Definition CreateNanoGrid.h:279
nanovdb::RootNode
Top-most node of the VDB tree structure.
Definition NanoVDB.h:4330
nanovdb::RootNode::memUsage
uint64_t memUsage() const
Return the actual memory footprint of this root node.
Definition NanoVDB.h:4625
nanovdb::Tree::memUsage
static uint64_t memUsage()
return memory usage in bytes for the class
Definition NanoVDB.h:4024
nanovdb::ValueMask
Dummy type for a voxel whose value equals its binary active state.
Definition NanoVDB.h:273
nanovdb::Vec3
A simple vector class with three components, similar to openvdb::math::Vec3.
Definition NanoVDB.h:1530
nanovdb::Vec4
A simple vector class with four components, similar to openvdb::math::Vec4.
Definition NanoVDB.h:1728
nanovdb::build::NodeManager
Definition GridBuilder.h:2055
openvdb::v11_0::Grid
Container class that associates a tree with a transform and metadata.
Definition Grid.h:571
openvdb::v11_0::RuntimeError
Definition Exceptions.h:63
openvdb::v11_0::ValueError
Definition Exceptions.h:65
openvdb::v11_0::math::Vec3
Definition Vec3.h:24
nanovdb::io::Codec
Codec
Define compression codecs.
Definition NanoVDB.h:7839
nanovdb
Convert a base-pointer to an openvdb grid, denoted srcGrid, to a nanovdb grid of the same type,...
Definition NanoVDB.h:247
nanovdb::AlignUp
uint64_t AlignUp(uint64_t byteCount)
round up byteSize to the nearest wordSize, e.g. to align to machine word: AlignUp<sizeof(size_t)(n)
Definition NanoVDB.h:1288
nanovdb::toStr
const char * toStr(GridType gridType)
Maps a GridType to a c-string.
Definition NanoVDB.h:349
nanovdb::Abs
T Abs(T x)
Definition NanoVDB.h:1185
nanovdb::createNanoGrid
disable_if< BuildTraits< DstBuildT >::is_index||BuildTraits< DstBuildT >::is_Fp, GridHandle< BufferT > >::type createNanoGrid(const SrcGridT &srcGrid, StatsMode sMode=StatsMode::Default, ChecksumMode cMode=ChecksumMode::Default, int verbose=0, const BufferT &buffer=BufferT())
Freestanding function that creates a NanoGrid<T> from any source grid.
Definition CreateNanoGrid.h:1956
nanovdb::reduce
T reduce(RangeT range, const T &identity, const FuncT &func, const JoinT &join)
Definition Reduce.h:42
nanovdb::GridClass
GridClass
Classes (superset of OpenVDB) that are currently supported by NanoVDB.
Definition NanoVDB.h:362
nanovdb::GridClass::FogVolume
@ FogVolume
nanovdb::GridClass::Unknown
@ Unknown
nanovdb::GridClass::PointIndex
@ PointIndex
nanovdb::GridClass::PointData
@ PointData
nanovdb::GridClass::Staggered
@ Staggered
nanovdb::GridClass::LevelSet
@ LevelSet
nanovdb::GridType
GridType
List of types that are currently supported by NanoVDB.
Definition NanoVDB.h:317
nanovdb::GridType::Float
@ Float
nanovdb::GridType::Unknown
@ Unknown
nanovdb::GridType::Int32
@ Int32
nanovdb::GridType::UInt32
@ UInt32
nanovdb::GridType::Vec3f
@ Vec3f
nanovdb::GridType::Int64
@ Int64
nanovdb::GridBlindDataClass
GridBlindDataClass
Blind-data Classes that are currently supported by NanoVDB.
Definition NanoVDB.h:416
nanovdb::GridBlindDataClass::IndexArray
@ IndexArray
nanovdb::GridBlindDataClass::AttributeArray
@ AttributeArray
nanovdb::GridBlindDataClass::GridName
@ GridName
nanovdb::CoordBBox
BBox< Coord > CoordBBox
Definition NanoVDB.h:2535
nanovdb::StatsMode
StatsMode
Grid flags which indicate what extra information is present in the grid buffer.
Definition GridStats.h:38
nanovdb::StatsMode::Default
@ Default
nanovdb::invoke
int invoke(const Func &taskFunc1, Rest... taskFuncN)
Definition Invoke.h:64
nanovdb::operator<<
std::ostream & operator<<(std::ostream &os, const AbsDiff &diff)
Definition CreateNanoGrid.h:261
nanovdb::gridStats
void gridStats(NanoGrid< BuildT > &grid, StatsMode mode=StatsMode::Default)
Re-computes the min/max, stats and bbox information for an existing NanoVDB Grid.
Definition GridStats.h:722
nanovdb::ChecksumMode
ChecksumMode
List of different modes for computing for a checksum.
Definition GridChecksum.h:38
nanovdb::ChecksumMode::Default
@ Default
nanovdb::prefixSum
T prefixSum(std::vector< T > &vec, bool threaded=true, OpT op=OpT())
Computes inclusive prefix sum of a vector.
Definition PrefixSum.h:71
nanovdb::GridBlindDataSemantic
GridBlindDataSemantic
Blind-data Semantics that are currently understood by NanoVDB.
Definition NanoVDB.h:424
nanovdb::GridBlindDataSemantic::PointPosition
@ PointPosition
nanovdb::GridBlindDataSemantic::PointColor
@ PointColor
nanovdb::GridBlindDataSemantic::PointVelocity
@ PointVelocity
nanovdb::GridBlindDataSemantic::Unknown
@ Unknown
nanovdb::GridBlindDataSemantic::PointNormal
@ PointNormal
nanovdb::GridBlindDataSemantic::PointId
@ PointId
nanovdb::Max
Type Max(Type a, Type b)
Definition NanoVDB.h:1110
nanovdb::updateChecksum
void updateChecksum(NanoGrid< BuildT > &grid, ChecksumMode mode=ChecksumMode::Default)
Updates the checksum of a grid.
nanovdb::isValid
bool isValid(GridType gridType, GridClass gridClass)
return true if the combination of GridType and GridClass is valid.
Definition NanoVDB.h:883
nanovdb::forEach
void forEach(RangeT range, const FuncT &func)
simple wrapper for tbb::parallel_for with a naive std fallback
Definition ForEach.h:40
nanovdb::createNodeManager
NodeManagerHandle< BufferT > createNodeManager(const NanoGrid< BuildT > &grid, const BufferT &buffer=BufferT())
brief Construct a NodeManager and return its handle
Definition NodeManager.h:284
nanovdb::Range1D
Range< 1, size_t > Range1D
Definition Range.h:30
openvdb::v11_0::io::getGridClass
OPENVDB_API uint32_t getGridClass(std::ios_base &)
Return the class (GRID_LEVEL_SET, GRID_UNKNOWN, etc.) of the grid currently being read from or writte...
openvdb::v11_0::points::pointCount
Index64 pointCount(const PointDataTreeT &tree, const FilterT &filter=NullFilter(), const bool inCoreOnly=false, const bool threaded=true)
Count the total number of points in a PointDataTree.
Definition PointCountImpl.h:18
openvdb::v11_0::tools::composite::max
const std::enable_if<!VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition Composite.h:110
openvdb::v11_0::tools::composite::min
const std::enable_if<!VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition Composite.h:106
openvdb::v11_0::GRID_FOG_VOLUME
@ GRID_FOG_VOLUME
Definition Types.h:456
openvdb::v11_0::GRID_STAGGERED
@ GRID_STAGGERED
Definition Types.h:457
openvdb::v11_0::GRID_LEVEL_SET
@ GRID_LEVEL_SET
Definition Types.h:455
openvdb::v11_0::PointIndex32
PointIndex< Index32, 0 > PointIndex32
Definition Types.h:178
openvdb::v11_0::PointDataIndex32
PointIndex< Index32, 1 > PointDataIndex32
Definition Types.h:181
openvdb
Definition Exceptions.h:13
NodeManager.h
NodeManager produces linear arrays of all tree nodes allowing for efficient threading and bottom-up p...
openvdb.h
nanovdb::BuildTraits
Define static boolean tests for template build types.
Definition NanoVDB.h:472
nanovdb::BuildTraits::is_onindex
static constexpr bool is_onindex
Definition NanoVDB.h:475
nanovdb::BuildTraits::is_index
static constexpr bool is_index
Definition NanoVDB.h:474
nanovdb::BuildTraits::is_Fp
static constexpr bool is_Fp
Definition NanoVDB.h:481
nanovdb::CreateNanoGrid::BlindMetaData
Definition CreateNanoGrid.h:814
nanovdb::CreateNanoGrid::BlindMetaData::order
const size_t order
Definition CreateNanoGrid.h:887
nanovdb::CreateNanoGrid::BlindMetaData::mapToSemantics
static GridBlindDataSemantic mapToSemantics(const std::string &name)
Definition CreateNanoGrid.h:870
nanovdb::CreateNanoGrid::BlindMetaData::operator<
bool operator<(const BlindMetaData &other) const
Definition CreateNanoGrid.h:853
nanovdb::CreateNanoGrid::BlindMetaData::BlindMetaData
BlindMetaData(const std::string &name, GridBlindDataSemantic dataSemantic, GridBlindDataClass dataClass, GridType dataType, size_t i, size_t valueCount, size_t valueSize)
Definition CreateNanoGrid.h:833
nanovdb::CreateNanoGrid::BlindMetaData::~BlindMetaData
~BlindMetaData()
Definition CreateNanoGrid.h:852
nanovdb::CreateNanoGrid::BlindMetaData::metaData
GridBlindMetaData * metaData
Definition CreateNanoGrid.h:886
nanovdb::CreateNanoGrid::BlindMetaData::BlindMetaData
BlindMetaData(const std::string &name, const std::string &type, GridBlindDataClass dataClass, size_t i, size_t valueCount, size_t valueSize)
Definition CreateNanoGrid.h:815
nanovdb::CreateNanoGrid::BlindMetaData::mapToType
static GridType mapToType(const std::string &name)
Definition CreateNanoGrid.h:854
nanovdb::FloatTraits::FloatType
float FloatType
Definition NanoVDB.h:1995
nanovdb::GridBlindMetaData
Definition NanoVDB.h:3331
nanovdb::GridData::memUsage
static uint64_t memUsage()
Return memory usage in bytes for this class only.
Definition NanoVDB.h:3661
nanovdb::GridData::MaxNameSize
static const int MaxNameSize
Definition NanoVDB.h:3513
nanovdb::Map
Defines an affine transform and its inverse represented as a 3x3 matrix and a vec3 translation.
Definition NanoVDB.h:3158
nanovdb::MapToNano
Trait that maps any type to the corresponding nanovdb type.
Definition CreateNanoGrid.h:371
nanovdb::MapToNano::type
T type
Definition CreateNanoGrid.h:371
nanovdb::NodeTrait
Struct to derive node type from its level in a given grid, tree or root while preserving constness.
Definition NanoVDB.h:3404
nanovdb::disable_if
Definition NanoVDB.h:506
nanovdb::enable_if
C++11 implementation of std::enable_if.
Definition NanoVDB.h:493
nanovdb::is_floating_point::value
static constexpr bool value
Definition NanoVDB.h:464
nanovdb::is_same
C++11 implementation of std::is_same.
Definition NanoVDB.h:442
nanovdb::is_same::value
static constexpr bool value
Definition NanoVDB.h:443