From d7b2165038f7b12d96ce9b1cb43cd5e740caeabc Mon Sep 17 00:00:00 2001 From: mattloulou Date: Fri, 23 Aug 2024 23:34:37 -0700 Subject: [PATCH] Implemented outputting to the nanoVDB file format. --- .../include/Neon/core/tools/io/ioToNanoVDB.h | 297 ++++++++++++++++++ .../include/Neon/domain/interface/FieldBase.h | 8 + .../Neon/domain/interface/FieldBase_imp.h | 40 +++ .../Neon/domain/interface/GridBaseTemplate.h | 5 + .../domain/interface/GridBaseTemplate_imp.h | 37 ++- 5 files changed, 385 insertions(+), 2 deletions(-) create mode 100644 libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h diff --git a/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h b/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h new file mode 100644 index 00000000..b07e2a0a --- /dev/null +++ b/libNeonCore/include/Neon/core/tools/io/ioToNanoVDB.h @@ -0,0 +1,297 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cuda_fp16.h" +#include + + +#include +#include +#include +#include +#include + + +#include "Neon/core/types/vec.h" + +namespace Neon { + +/** + * Namespace for this tool + */ +namespace ioToNanoVDBns { + +/* + * nanovdb's 3-component vector + */ +template +using Vec3 = nanovdb::math::Vec3; + +/* + * nanovdb's 4-component vector + */ +template +using Vec4 = nanovdb::math::Vec4; + +/** + * Implicit function that defines the data stores by a user fields + */ +template +using UserFieldAccessGenericFunction_t = std::function&, int componentIdx)>; + +/** + * Implicit function that takes in an index, and returns a boolean for if that index is a valid index in the field or not. + */ +template +using UserFieldAccessMask = std::function&)>; + +/** + * Number of components of the field + */ +using nComponent_t = int; + +/** + * Name of the file where the field will be exported into + */ +using FieldName_t = std::string; + +template +struct UserFieldInformation +{ + UserFieldAccessGenericFunction_t m_userFieldAccessGenericFunction; + nComponent_t m_cardinality; + + UserFieldInformation(const UserFieldAccessGenericFunction_t& fun, nComponent_t card) + : m_userFieldAccessGenericFunction(fun), + m_cardinality(card) + { + } + + UserFieldInformation(const std::tuple, nComponent_t>& tuple) + : m_userFieldAccessGenericFunction(std::get<0>(tuple)), + m_cardinality(std::get<1>(tuple)) + { + } +}; + +/* + * Alias for NanoVDB grid types + */ +template +using Grid1Component = std::shared_ptr>; + +template +using Grid3Components = std::shared_ptr>>; + +template +using Grid4Components = std::shared_ptr>>; + +// The possible kinds of grids (containing 1, 3, or 4 components) that can be used +template +using GridPtrVariant = std::variant< + std::shared_ptr>, + std::shared_ptr>>, + std::shared_ptr>> +>; + +namespace helpNs { + +template +struct BackgroundValueVisitor { + real_tt operator()(const typename Neon::ioToNanoVDBns::Grid1Component) { + return std::numeric_limits::min(); + } + Vec3 operator()(const typename Neon::ioToNanoVDBns::Grid3Components) { + return Vec3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); + } + Vec4 operator()(const typename Neon::ioToNanoVDBns::Grid4Components) { + return Vec4(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()); + } +}; + +template +struct GetDataVisitor { + real_tt operator()(const typename Neon::ioToNanoVDBns::Grid1Component, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return fieldData(xyz, 0); + } + Vec3 operator()(const typename Neon::ioToNanoVDBns::Grid3Components, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec3(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2)); + } + Vec4 operator()(const typename Neon::ioToNanoVDBns::Grid4Components, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec4(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2), fieldData(xyz, 3)); + } +}; + +template +struct BuildGridVisitor { + real_tt operator()(const typename Neon::ioToNanoVDBns::Grid1Component grid, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return fieldData(xyz, 0); + } + Vec3 operator()(const typename Neon::ioToNanoVDBns::Grid3Components grid, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec3(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2)); + } + Vec4 operator()(const typename Neon::ioToNanoVDBns::Grid4Components grid, Neon::Integer_3d xyz, const ioToNanoVDBns::UserFieldAccessGenericFunction_t& fieldData) const { + return Vec4(fieldData(xyz, 0), fieldData(xyz, 1), fieldData(xyz, 2), fieldData(xyz, 3)); + } +}; + + +} // namespace helpNs + +template +void ioToNanoVDB(const ioToNanoVDBns::UserFieldInformation& fieldData /*! User data that defines the field */, + ioToNanoVDBns::UserFieldAccessMask mask /*! Stores a mask for which indices in the field should be outputted*/, + const std::string& filename /*! File name */, + const Neon::Integer_3d& dim /*! Dimension of the field */, + double spacingScale = 1.0 /*! Spacing, i.e. size of a voxel */, /* TODOMATT: add spacing data transformation */ + const Neon::Integer_3d& origin = Neon::Integer_3d(0, 0, 0) /*! Origin */, + [[maybe_unused]] int iterationId = -1) +{ + // Create our grid + Neon::ioToNanoVDBns::GridPtrVariant outputGrid; + + // Based on the cardinality, use a scalar, Vec3, or Vec4 + switch (fieldData.m_cardinality) { + case 1: + outputGrid = std::make_shared>( + std::numeric_limits::min(), filename, nanovdb::GridClass::LevelSet); + break; + case 3: + outputGrid = std::make_shared>>( + Vec3(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()), filename, nanovdb::GridClass::LevelSet); + break; + case 4: + outputGrid = std::make_shared>>( + Vec4(std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min(), std::numeric_limits::min()), filename, nanovdb::GridClass::LevelSet); + break; + default: std::string msg = std::string("Too many components specified during attempt at creating nanoVDB output"); + NeonException exception("ioToNanoVDB"); + exception << msg; + NEON_THROW(exception); + } + + // through experimentation, it seems that both coordinates are inclusive. So, the upper corner should be subtracted by one. + const int bboxOffset = -1; + nanovdb::CoordBBox bbox(nanovdb::Coord(origin.x, origin.y, origin.z), nanovdb::Coord(origin.x + dim.x + bboxOffset, origin.y + dim.y + bboxOffset, origin.z + dim.z + bboxOffset)); + + // Write the data to the nanoVDB grid + std::visit([&](auto& grid) { + + // Set the voxel scale: + grid->setTransform(spacingScale); + + // Use a nanoVDB functor to set the grid values + (*grid)([&](const nanovdb::Coord &ijk) { + const Neon::Integer_3d xyz(ijk[0], ijk[1], ijk[2]); + + // If the mask shows that the current coordinate is empty, then we will set it with the background value. + // This background value makes this index unset (to accomodate sparse grids) + if (!mask(xyz)) { + return Neon::ioToNanoVDBns::helpNs::BackgroundValueVisitor{}(grid); + } + + return Neon::ioToNanoVDBns::helpNs::GetDataVisitor{}(grid, xyz, fieldData.m_userFieldAccessGenericFunction); + }, bbox); + + }, outputGrid); + + + try { + // Write the grid out to a file + std::visit([&](auto& grid) { + nanovdb::io::writeGrid(filename, nanovdb::tools::createNanoGrid(*grid), nanovdb::io::Codec::NONE); + }, outputGrid); + + // catch possible file IO errors + } catch (...) { + std::string msg = std::string("An error on file operations where encountered when writing nanoVDB data"); + NeonException exception("ioToNanoVDB output exception"); + exception << msg; + NEON_THROW(exception); + } +} +} // namespace ioToNanoVDBns + +template +struct ioToNanoVDB +{ + ioToNanoVDB(const std::string& filename /*! File name */, + const Neon::Integer_3d& dim /*! IoDense dimension of the field */, + const std::function&, int componentIdx)>& fun /*! Implicit defintion of the user field */, + const nComponent_t card /*! Field cardinality */, + const double scalingData = 1.0 /*! Spacing, i.e. size of a voxel */, + const Neon::Integer_3d& origin = Neon::Integer_3d(0, 0, 0) /*! Minimum Corner && Origin */, + const Neon::ioToNanoVDBns::UserFieldAccessMask mask = [](const Neon::index_3d& idx){return (idx.x == idx.x) ? true: false;}) /*! Used for sparce matrices; returns true for indices that should be stored in vdb output */ + : m_filename(filename), + m_dim(dim), + m_scalingData(scalingData), + m_origin(origin), + m_field(ioToNanoVDBns::UserFieldInformation(fun, card)), + m_mask(mask) + { + std::ofstream out("metadata2"); + out << "dim: " << m_dim.x << " " << m_dim.y << " " << m_dim.z << std::endl; + } + + virtual ~ioToNanoVDB() + { + } + + auto flush() -> void + { + std::string filename; + if (m_iteration == -1) { + filename = m_filename; + } else { + std::stringstream ss; + ss << std::setw(5) << std::setfill('0') << m_iteration; + std::string s = ss.str(); + filename = m_filename + s; + } + filename = filename + ".nvdb"; + ioToNanoVDBns::ioToNanoVDB(m_field, + m_mask, + filename, + m_dim, + m_scalingData, + m_origin, + m_iteration); + } + + + auto setIteration(int iteration) + { + m_iteration = iteration; + } + + auto setFileName(const std::string& fname) + { + m_filename = fname; + } + + + private: + std::string m_filename /*! File name */; + Neon::Integer_3d m_dim /*! IoDense dimension of the field */; + double m_scalingData = 1.0 /*! Spacing, i.e. size of a voxel */; + Neon::Integer_3d m_origin = Neon::Integer_3d(0, 0, 0) /*! Origin */; + ioToNanoVDBns::UserFieldInformation m_field /*! Field data*/; + ioToNanoVDBns::UserFieldAccessMask m_mask; + int m_iteration = -1; +}; + +} // namespace Neon diff --git a/libNeonDomain/include/Neon/domain/interface/FieldBase.h b/libNeonDomain/include/Neon/domain/interface/FieldBase.h index 1401e98b..cdbc782f 100644 --- a/libNeonDomain/include/Neon/domain/interface/FieldBase.h +++ b/libNeonDomain/include/Neon/domain/interface/FieldBase.h @@ -140,6 +140,14 @@ class FieldBase Neon::IoFileType ioFileType = Neon::IoFileType::ASCII, bool isNodeSpace = false) const -> void; + template + auto ioToNanoVDB(const std::string& fileName, + bool isNodeSpace = false) const -> void; + + template + auto ioDomainToNanoVDB(const std::string& fileName, + bool isNodeSpace = false) const -> void; + private: struct Storage diff --git a/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h b/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h index 97d10dc1..27cd00fd 100644 --- a/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h +++ b/libNeonDomain/include/Neon/domain/interface/FieldBase_imp.h @@ -2,6 +2,7 @@ #include "Neon/domain/interface/FieldBase.h" #include "Neon/domain/tools/IOGridVTK.h" +#include "Neon/core/tools/io/ioToNanoVDB.h" namespace Neon::domain::interface { @@ -318,6 +319,45 @@ auto FieldBase::ioToVtk(const std::string& fileName, return; } +template +template +auto FieldBase::ioToNanoVDB(const std::string& fileName, + bool isNodeSpace) const -> void +{ + Neon::ioToNanoVDB io(fileName, + this->getDimension(), + [&](Neon::Integer_3d idx, int card) -> NanoVDBExportType { + return (*this)(idx, card); + }, + this->getCardinality(), + 1.0, + Neon::Integer_3d(0, 0, 0)); + + + io.flush(); + return; +} + +template +template +auto FieldBase::ioDomainToNanoVDB(const std::string& fileName, + bool isNodeSpace) const -> void +{ + Neon::ioToNanoVDB io(fileName, + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + NanoVDBExportType setIdx = NanoVDBExportType(getBaseGridTool().getSetIdx(idx)); + return setIdx; + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + + io.flush(); + return; +} + template auto FieldBase::getClassName() const -> const std::string& { diff --git a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h index 235c2a3f..5741911f 100644 --- a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h +++ b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate.h @@ -31,6 +31,11 @@ class GridBaseTemplate : public GridBase */ auto ioDomainToVtk(const std::string& fileName, Neon::IoFileType vtiIOe = IoFileType::ASCII) const -> void; + + /** + * Exporting the domain active voxel to nanoVDB + */ + auto ioDomainToNanoVDB(const std::string& fileName) const -> void; }; } // namespace Neon::domain::interface diff --git a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h index 03c3eab6..f859fc3c 100644 --- a/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h +++ b/libNeonDomain/include/Neon/domain/interface/GridBaseTemplate_imp.h @@ -1,6 +1,7 @@ #pragma once #include "Neon/core/tools/io/ioToVTK.h" +#include "Neon/core/tools/io/ioToNanoVDB.h" namespace Neon::domain::interface { @@ -14,8 +15,8 @@ auto GridBaseTemplate::ioDomainToVtk(const std::string& fileName, ioFileType); io.addField([&](const Neon::index_3d& idx, int) { - bool isActieVox = isInsideDomain(idx); - return isActieVox; + bool isActiveVox = isInsideDomain(idx); + return isActiveVox; }, 1, "Domain", ioToVTKns::VtiDataType_e::voxel); @@ -32,4 +33,36 @@ auto GridBaseTemplate::ioDomainToVtk(const std::string& fileName, io.flushAndClear(); return; } + +template +auto GridBaseTemplate::ioDomainToNanoVDB(const std::string& fileName) const -> void +{ + ioToNanoVDB io1(fileName + "_domain", + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + bool isActiveVox = isInsideDomain(idx); + return isActiveVox; + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + ioToNanoVDB io2(fileName + "_partition", + this->getDimension(), + [&](const Neon::index_3d& idx, int) { + const auto& cellProperties = this->getProperties(idx); + if (!cellProperties.isInside()) { + return -1; + } + auto setIdx = cellProperties.getSetIdx(); + return setIdx.idx(); + }, + 1, + 1.0, + Neon::Integer_3d(0, 0, 0)); + + io1.flush(); + io2.flush(); + return; +} } // namespace Neon::domain::interface