26 #ifndef NANOVDB_IO_H_HAS_BEEN_INCLUDED
27 #define NANOVDB_IO_H_HAS_BEEN_INCLUDED
40 #ifdef NANOVDB_USE_ZIP
43 #ifdef NANOVDB_USE_BLOSC
50 #if defined(major) || defined(minor)
62 template<
typename BufferT>
66 template<
typename BufferT = HostBuffer,
template<
typename...>
class VecT = std::vector>
81 template<
typename BufferT = HostBuffer>
92 template<
typename BufferT = HostBuffer>
103 template<
typename BufferT = HostBuffer,
template<
typename...>
class VecT = std::vector>
117 static constexpr
fileSize_t MAX_SIZE = 1UL << 30;
119 template<
typename BufferT>
122 template<
typename BufferT>
123 static void read(std::istream& is, BufferT&
buffer,
Codec codec);
138 return (((val) >> 56) & 0x00000000000000FF) | (((
val) >> 40) & 0x000000000000FF00) |
139 (((val) >> 24) & 0x0000000000FF0000) | (((
val) >> 8) & 0x00000000FF000000) |
140 (((val) << 8) & 0x000000FF00000000) | (((
val) << 24) & 0x0000FF0000000000) |
141 (((val) << 40) & 0x00FF000000000000) | (((
val) << 56) & 0xFF00000000000000);
153 static_assert(
sizeof(
FileMetaData) == 176,
"Unexpected sizeof(FileMetaData)");
155 void read(std::istream& is);
156 void write(std::ostream& os)
const;
158 template<
typename ValueT>
170 static_assert(
sizeof(
FileHeader) == 16u,
"Unexpected sizeof(FileHeader)");
172 std::vector<FileGridMetaData>
meta;
174 #ifdef NANOVDB_USE_NEW_MAGIC_NUMBERS
182 template<
typename BufferT>
183 void add(
const GridHandle<BufferT>&
h);
184 bool read(std::istream& is);
185 void write(std::ostream& os)
const;
203 template<
typename BufferT>
204 fileSize_t Internal::write(std::ostream& os,
const GridHandle<BufferT>&
handle,
Codec codec,
unsigned int n)
206 const char*
data =
reinterpret_cast<const char*
>(handle.gridData(n));
207 fileSize_t total = 0, residual = handle.gridSize(n);
211 #ifdef NANOVDB_USE_ZIP
213 std::unique_ptr<Bytef[]> tmp(
new Bytef[size]);
214 const int status = compress(tmp.get(), &
size,
reinterpret_cast<const Bytef*
>(
data), residual);
216 std::runtime_error(
"Internal write error in ZIP");
218 std::cerr <<
"\nWarning: Unexpected ZIP compression from " << residual <<
" to " << size <<
" bytes\n";
220 os.write(reinterpret_cast<const char*>(&outBytes),
sizeof(
fileSize_t));
221 os.write(reinterpret_cast<const char*>(tmp.get()), outBytes);
224 throw std::runtime_error(
"ZIP compression codec was disabled during build");
229 #ifdef NANOVDB_USE_BLOSC
232 std::unique_ptr<char[]> tmp(
new char[size]);
235 std::runtime_error(
"Internal write error in BLOSC");
237 os.write(reinterpret_cast<const char*>(&outBytes),
sizeof(
fileSize_t));
238 os.write(reinterpret_cast<const char*>(tmp.get()), outBytes);
242 }
while (residual > 0);
244 throw std::runtime_error(
"BLOSC compression codec was disabled during build");
249 os.write(data, residual);
252 if (!os)
throw std::runtime_error(
"Failed to write Tree to file");
256 template<
typename BufferT>
257 void Internal::read(std::istream& is, BufferT&
buffer,
Codec codec)
259 Internal::read(is, reinterpret_cast<char*>(buffer.data()), buffer.size(), codec);
267 void Internal::read(std::istream& is,
char* data,
fileSize_t residual,
Codec codec)
272 #ifdef NANOVDB_USE_ZIP
274 is.read(reinterpret_cast<char*>(&size),
sizeof(
fileSize_t));
275 std::unique_ptr<Bytef[]> tmp(
new Bytef[size]);
276 is.read(reinterpret_cast<char*>(tmp.get()), size);
277 uLongf numBytes = residual;
278 int status = uncompress(reinterpret_cast<Bytef*>(data), &numBytes, tmp.get(),
static_cast<uLongf>(
size));
279 if (status != Z_OK) std::runtime_error(
"Internal read error in ZIP");
280 if (
fileSize_t(numBytes) != residual)
throw std::runtime_error(
"UNZIP failed on byte size");
282 throw std::runtime_error(
"ZIP compression codec was disabled during build");
287 #ifdef NANOVDB_USE_BLOSC
290 is.read(reinterpret_cast<char*>(&size),
sizeof(
fileSize_t));
291 std::unique_ptr<char[]> tmp(
new char[size]);
292 is.read(reinterpret_cast<char*>(tmp.get()), size);
293 const fileSize_t chunk = residual < MAX_SIZE ? residual : MAX_SIZE;
296 std::runtime_error(
"Internal read error in BLOSC");
297 if (count !=
int(chunk))
298 throw std::runtime_error(
"BLOSC failed on byte size");
299 data += size_t(chunk);
301 }
while (residual > 0);
303 throw std::runtime_error(
"BLOSC compression codec was disabled during build");
308 is.read(data, residual);
310 if (!is)
throw std::runtime_error(
"Failed to read Tree from file");
315 template<
typename ValueT>
335 nameSize =
static_cast<uint32_t
>(gridName.size() + 1);
336 const uint32_t*
ptr =
reinterpret_cast<const TreeData*
>(&grid.tree())->mNodeCount;
343 os.write(reinterpret_cast<const char*>(
this),
sizeof(
FileMetaData));
344 os.write(gridName.c_str(),
nameSize);
345 if (!os)
throw std::runtime_error(
"Failed writing FileGridMetaData");
350 is.read(reinterpret_cast<char*>(
this),
sizeof(
FileMetaData));
351 std::unique_ptr<char[]> tmp(
new char[
nameSize]);
352 is.read(reinterpret_cast<char*>(tmp.get()), nameSize);
353 gridName.assign(tmp.get());
354 if (!is)
throw std::runtime_error(
"Failed reading FileGridMetaData");
362 for (
auto& m :
meta) sum += m.memUsage();
366 template<
typename BufferT>
369 for (uint32_t i = 0; i < h.
gridCount(); ++i) {
370 if (
auto* grid = h.template grid<float>(i)) {
372 }
else if (
auto* grid = h.template grid<Vec3f>(i)) {
374 }
else if (
auto* grid = h.template grid<double>(i)) {
376 }
else if (
auto* grid = h.template grid<int32_t>(i)) {
378 }
else if (
auto* grid = h.template grid<uint32_t>(i)) {
380 }
else if (
auto* grid = h.template grid<int64_t>(i)) {
382 }
else if (
auto* grid = h.template grid<int16_t>(i)) {
384 }
else if (
auto* grid = h.template grid<Vec3d>(i)) {
386 }
else if (
auto* grid = h.template grid<ValueMask>(i)) {
388 }
else if (
auto* grid = h.template grid<ValueIndex>(i)) {
390 }
else if (
auto* grid = h.template grid<ValueIndexMask>(i)) {
392 }
else if (
auto* grid = h.template grid<ValueOnIndex>(i)) {
394 }
else if (
auto* grid = h.template grid<ValueOnIndexMask>(i)) {
396 }
else if (
auto* grid = h.template grid<bool>(i)) {
398 }
else if (
auto* grid = h.template grid<Rgba8>(i)) {
400 }
else if (
auto* grid = h.template grid<Fp4>(i)) {
402 }
else if (
auto* grid = h.template grid<Fp8>(i)) {
404 }
else if (
auto* grid = h.template grid<Fp16>(i)) {
406 }
else if (
auto* grid = h.template grid<FpN>(i)) {
408 }
else if (
auto* grid = h.template grid<Vec4f>(i)) {
410 }
else if (
auto* grid = h.template grid<Vec4d>(i)) {
413 std::stringstream ss;
414 ss <<
"nanovdb::io::Segment::add: Cannot write grid of unknown type \""<<
toStr(h.
gridType(i));
415 throw std::runtime_error(ss.str() +
"\" to file");
424 throw std::runtime_error(
"Segment contains no grids");
425 }
else if (!os.write(reinterpret_cast<const char*>(&
header),
sizeof(
FileHeader))) {
426 throw std::runtime_error(
"Failed to write FileHeader of Segment");
428 for (
auto& m :
meta) m.write(os);
435 is.clear(std::ios_base::eofbit);
442 throw std::runtime_error(
"This nvdb file has reversed endianness");
444 throw std::runtime_error(
"Magic number error: This is not a valid nvdb file");
447 std::stringstream ss;
449 is.read(reinterpret_cast<char*>(&v),
sizeof(
Version));
451 ss <<
"This file looks like it contains a raw grid buffer and not a standard file with meta data";
457 <<
"Recommendation: Re-compile this tool against the newer version: " <<
header.
version.
getMajor() <<
".X of NanoVDB";
459 throw std::runtime_error(
"An unrecoverable error in nanovdb::Segment::read:\n\tIncompatible file format: " + ss.str());
462 for (
auto& m :
meta) {
471 template<
typename BufferT>
476 const auto start = os.tellp();
478 for (uint32_t i = 0; i < handle.
gridCount(); ++i) {
479 seg.
meta[i].fileSize = Internal::write(os, handle, codec, i);
486 template<
typename BufferT>
491 throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for output");
493 writeGrid<BufferT>(os,
handle, codec);
495 std::cout <<
"Wrote nanovdb::Grid to file named \"" << fileName <<
"\"" << std::endl;
501 template<
typename BufferT =
HostBuffer,
template<
typename...>
class VecT = std::vector>
507 template<
typename BufferT,
template<
typename...>
class VecT>
511 if (!os.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for output");
512 writeGrids<BufferT, VecT>(os, handles, codec);
513 if (verbose) std::cout <<
"Wrote " << handles.size() <<
" nanovdb::Grid(s) to file named \"" << fileName <<
"\"" << std::endl;
518 template<
typename BufferT>
524 handle.
read(is, pool);
525 }
catch(
const std::logic_error&) {
527 uint64_t bufferSize = 0u;
528 uint32_t gridCount = 0u, gridIndex = 0u;
529 const auto start = is.tellg();
530 while (seg.
read(is)) {
531 std::streamoff skipSize = 0;
532 for (
auto& m : seg.
meta) {
534 bufferSize += m.gridSize;
535 skipSize += m.fileSize;
537 is.seekg(skipSize, std::ios_base::cur);
539 auto buffer = BufferT::create(bufferSize, &pool);
540 char *
ptr = (
char*)buffer.data();
542 while (seg.
read(is)) {
543 for (
auto& m : seg.
meta) {
544 Internal::read(is, ptr, m.gridSize, seg.
header.
codec);
553 handle.
read(is, uint32_t(n), pool);
555 }
catch(
const std::logic_error&) {
558 while (seg.
read(is)) {
559 std::streamoff seek = 0;
560 for (
auto& m : seg.
meta) {
561 if (++counter == n) {
562 auto buffer = BufferT::create(m.gridSize, &pool);
570 is.seekg(seek, std::ios_base::cur);
572 if (n != counter)
throw std::runtime_error(
"stream does not contain a #" +
std::to_string(n) +
" grid");
579 template<
typename BufferT>
583 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
584 auto handle = readGrid<BufferT>(is,
n,
buffer);
587 std::cout <<
"Read all NanoGrids from the file named \"" << fileName <<
"\"" << std::endl;
589 std::cout <<
"Read NanoGrid # " << n <<
" from the file named \"" << fileName <<
"\"" << std::endl;
602 template<
typename BufferT>
607 handle.
read(is, gridName, pool);
609 }
catch(
const std::logic_error&) {
612 while (seg.
read(is)) {
613 std::streamoff seek = 0;
614 for (
auto& m : seg.
meta) {
615 if ((m.nameKey == 0u || m.nameKey == key) && m.gridName ==
gridName) {
616 auto buffer = BufferT::create(m.gridSize, &pool);
617 is.seekg(seek, std::ios_base::cur);
625 is.seekg(seek, std::ios_base::cur);
628 throw std::runtime_error(
"Grid name '" + gridName +
"' not found in file");
632 template<
typename BufferT>
636 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
640 std::cout <<
"Read NanoGrid named \"" << gridName <<
"\" from the file named \"" << fileName <<
"\"" << std::endl;
642 std::cout <<
"File named \"" << fileName <<
"\" does not contain a grid named \"" + gridName +
"\"" << std::endl;
650 template<
typename BufferT =
HostBuffer,
template<
typename...>
class VecT = std::vector>
651 VecT<GridHandle<BufferT>>
readGrids(std::istream& is,
const BufferT&
pool = BufferT())
653 VecT<GridHandle<BufferT>> handles;
655 while (seg.
read(is)) {
656 uint64_t bufferSize = 0;
657 for (
auto& m : seg.
meta) bufferSize += m.gridSize;
658 auto buffer = BufferT::create(bufferSize, &
pool);
659 uint64_t bufferOffset = 0;
661 auto *data =
reinterpret_cast<GridData*
>(buffer.data() + bufferOffset);
662 Internal::read(is, (
char*)data, seg.
meta[i].gridSize, seg.
header.
codec);
664 bufferOffset += seg.
meta[i].gridSize;
666 handles.emplace_back(std::move(buffer));
672 template<
typename BufferT,
template<
typename...>
class VecT>
676 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
677 auto handles = readGrids<BufferT, VecT>(is,
buffer);
678 if (verbose) std::cout <<
"Read " << handles.size() <<
" NanoGrid(s) from the file named \"" << fileName <<
"\"" << std::endl;
687 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
694 std::vector<FileGridMetaData> meta;
699 meta = std::move(seg.
meta);
700 }
catch(
const std::logic_error&) {
701 while (seg.
read(is)) {
702 std::streamoff
skip = 0;
703 for (
auto& m : seg.
meta) {
707 is.seekg(skip, std::ios_base::cur);
718 if (!is.is_open())
throw std::ios_base::failure(
"Unable to open file named \"" + fileName +
"\" for input");
726 while (seg.
read(is)) {
727 std::streamoff seek = 0;
728 for (
auto& m : seg.
meta) {
729 if (m.nameKey == key && m.gridName == gridName)
return true;
732 is.seekg(seek, std::ios_base::cur);
743 for (
auto* str = reinterpret_cast<const unsigned char*>(c_str); *str; ++str) {
744 uint64_t overflow = hash >> (64 - 8);
746 hash += *str + overflow;
756 operator<<(std::ostream& os, const BBox<Vec3<T>>&
b)
758 os <<
"(" <<
b[0][0] <<
"," <<
b[0][1] <<
"," <<
b[0][2] <<
") -> "
759 <<
"(" <<
b[1][0] <<
"," <<
b[1][1] <<
"," <<
b[1][2] <<
")";
766 os <<
"(" << b[0][0] <<
"," << b[0][1] <<
"," << b[0][2] <<
") -> "
767 <<
"(" << b[1][0] <<
"," << b[1][1] <<
"," << b[1][2] <<
")";
774 os <<
"(" << ijk[0] <<
"," << ijk[1] <<
"," << ijk[2] <<
")";
780 operator<<(std::ostream& os, const Vec3<T>&
v)
782 os <<
"(" <<
v[0] <<
"," <<
v[1] <<
"," <<
v[2] <<
")";
788 operator<<(std::ostream& os, const Vec4<T>&
v)
790 os <<
"(" <<
v[0] <<
"," <<
v[1] <<
"," <<
v[2] <<
"," <<
v[3] <<
")";
796 #endif // NANOVDB_IO_H_HAS_BEEN_INCLUDED
#define NANOVDB_MAGIC_NUMBER
bool updateGridCount(GridData *data, uint32_t gridIndex, uint32_t gridCount)
Updates the ground index and count, as well as the partial checksum if needed.
uint64_t reverseEndianness(uint64_t val)
Return a uint64_t with its bytes reversed so we can check for endianness.
#define NANOVDB_MAJOR_VERSION_NUMBER
__hostdev__ const TreeT & tree() const
Return a const reference to the tree.
Signed (i, j, k) 32-bit integer coordinate class, similar to openvdb::math::Coord.
__hostdev__ uint32_t getMajor() const
gridName(grid.gridName())
OIIO_NAMESPACE_BEGIN typedef std::ifstream ifstream
void add(const GridHandle< BufferT > &h)
GLsizei const GLchar *const * string
BLOSC_EXPORT int blosc_compress_ctx(int clevel, int doshuffle, size_t typesize, size_t nbytes, const void *src, void *dest, size_t destsize, const char *compressor, size_t blocksize, int numinternalthreads)
const GLuint GLenum const void * binary
VecT< GridHandle< BufferT > > readGrids(const std::string &fileName, int verbose=0, const BufferT &buffer=BufferT())
Read all the grids in the file and return them as a vector of multiple GridHandles, each containing all grids encoded in the same segment of the file (i.e. they where written together)
void read(std::istream &is, const BufferT &pool=BufferT())
Read an entire raw grid buffer from an input stream.
Highest level of the data structure. Contains a tree and a world->index transform (that currently onl...
void write(std::ostream &os) const
void writeGrids(const std::string &fileName, const VecT< GridHandle< BufferT >> &handles, Codec codec=Codec::NONE, int verbose=0)
Write multiple grids to file (over-writing existing content of the file)
This is a buffer that contains a shared or private pool to either externally or internally managed ho...
const char * c_str() const
returns a c-string of the semantic version, i.e. major.minor.patch
Codec
Define compression codecs.
Defines GridHandle, which manages a host, and possibly a device, memory buffer containing one or more...
This class serves to manage a buffer containing one or more NanoVDB Grids.
uint64_t stringHash(const char *cstr)
Standard hash function to use on strings; std::hash may vary by platform/implementation and is know t...
Computes a pair of 32bit checksums, of a Grid, by means of Cyclic Redundancy Check (CRC) ...
__hostdev__ const GridClass & gridClass() const
uint64_t gridSize(uint32_t n=0) const
Return the grid size of the n'th grid in this GridHandle.
void writeGrid(const std::string &fileName, const GridHandle< BufferT > &handle, io::Codec codec=io::Codec::NONE, int verbose=0)
Write a single grid to file (over-writing existing content of the file)
uint8_t * data()
Returns a non-const pointer to the data.
IMATH_HOSTDEVICE constexpr int trunc(T x) IMATH_NOEXCEPT
Implements a light-weight self-contained VDB data-structure in a single file! In other words...
bool read(std::istream &is)
BLOSC_EXPORT int blosc_decompress_ctx(const void *src, void *dest, size_t destsize, int numinternalthreads)
std::ostream & operator<<(std::ostream &os, const AbsDiff &diff)
#define NANOVDB_MAGIC_FILE
Segment(Codec c=Codec::NONE)
This class defines all the data stored in segment of a file.
std::vector< FileGridMetaData > meta
GLboolean GLboolean GLboolean b
__hostdev__ uint64_t activeVoxelCount() const
Computes a AABB of active values in world space.
__hostdev__ const GridType & gridType() const
GLfloat GLfloat GLfloat GLfloat h
std::vector< FileGridMetaData > readGridMetaData(const std::string &fileName)
Reads and returns a vector of meta data for all the grids found in the specified file.
#define BLOSC_MAX_OVERHEAD
__hostdev__ const Vec3d & voxelSize() const
Return a const reference to the size of a voxel in world units.
bool hasGrid(const std::string &fileName, const std::string &gridName)
Return true if the file contains a grid with the specified name.
uint32_t gridCount() const
Return the total number of grids contained in this buffer.
GridType gridType(uint32_t n=0) const
Return the GridType of the n'th grid in this GridHandle.
Bit-compacted representation of all three version numbers.
uint64_t memUsage() const
GridHandle< BufferT > readGrid(const std::string &fileName, int n=0, int verbose=0, const BufferT &buffer=BufferT())
Read and return one or all grids from a file into a single GridHandle.
__hostdev__ bool isCompatible() const
#define BLOSC_LZ4_COMPNAME
**Note that the tasks the is the thread number *for the pool
const char * toStr(Codec codec)