Skip to content

Instantly share code, notes, and snippets.

@brothermechanic
Created March 8, 2020 18:36
Show Gist options
  • Save brothermechanic/6e34670e5369b7a9e9216ea19a49c618 to your computer and use it in GitHub Desktop.
Save brothermechanic/6e34670e5369b7a9e9216ea19a49c618 to your computer and use it in GitHub Desktop.
TestFile.cc
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0
#include <openvdb/Exceptions.h>
#include <openvdb/io/File.h>
#include <openvdb/io/io.h>
#include <openvdb/io/Queue.h>
#include <openvdb/io/Stream.h>
#include <openvdb/Metadata.h>
#include <openvdb/math/Transform.h>
#include <openvdb/tools/LevelSetUtil.h> // for tools::sdfToFogVolume()
#include <openvdb/util/logging.h>
#include <openvdb/version.h>
#include <openvdb/openvdb.h>
#include "util.h" // for unittest_util::makeSphere()
#include <cppunit/extensions/HelperMacros.h>
#include <tbb/tbb_thread.h> // for tbb::this_tbb_thread::sleep()
#include <algorithm> // for std::sort()
#include <cstdio> // for remove() and rename()
#include <fstream>
#include <functional> // for std::bind()
#include <iostream>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <sys/types.h> // for stat()
#include <sys/stat.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#ifdef OPENVDB_USE_BLOSC
#include <blosc.h>
#include <cstring> // for memset()
#endif
class TestFile: public CppUnit::TestCase
{
public:
void setUp() override {}
void tearDown() override { openvdb::uninitialize(); }
CPPUNIT_TEST_SUITE(TestFile);
CPPUNIT_TEST(testHeader);
CPPUNIT_TEST(testWriteGrid);
CPPUNIT_TEST(testWriteMultipleGrids);
CPPUNIT_TEST(testWriteFloatAsHalf);
CPPUNIT_TEST(testWriteInstancedGrids);
CPPUNIT_TEST(testReadGridDescriptors);
CPPUNIT_TEST(testGridNaming);
CPPUNIT_TEST(testEmptyFile);
CPPUNIT_TEST(testEmptyGridIO);
CPPUNIT_TEST(testOpen);
CPPUNIT_TEST(testNonVdbOpen);
CPPUNIT_TEST(testGetMetadata);
CPPUNIT_TEST(testReadAll);
CPPUNIT_TEST(testWriteOpenFile);
CPPUNIT_TEST(testReadGridMetadata);
CPPUNIT_TEST(testReadGrid);
CPPUNIT_TEST(testReadClippedGrid);
CPPUNIT_TEST(testMultiPassIO);
CPPUNIT_TEST(testHasGrid);
CPPUNIT_TEST(testNameIterator);
CPPUNIT_TEST(testReadOldFileFormat);
CPPUNIT_TEST(testCompression);
CPPUNIT_TEST(testAsync);
#ifdef OPENVDB_USE_BLOSC
CPPUNIT_TEST(testBlosc);
#endif
CPPUNIT_TEST(testDelayedLoadMetadata);
CPPUNIT_TEST_SUITE_END();
void testHeader();
void testWriteGrid();
void testWriteMultipleGrids();
void testWriteFloatAsHalf();
void testWriteInstancedGrids();
void testReadGridDescriptors();
void testGridNaming();
void testEmptyFile();
void testEmptyGridIO();
void testOpen();
void testNonVdbOpen();
void testGetMetadata();
void testReadAll();
void testWriteOpenFile();
void testReadGridMetadata();
void testReadGrid();
void testReadClippedGrid();
void testMultiPassIO();
void testHasGrid();
void testNameIterator();
void testReadOldFileFormat();
void testCompression();
void testAsync();
#ifdef OPENVDB_USE_BLOSC
void testBlosc();
#endif
void testDelayedLoadMetadata();
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestFile);
////////////////////////////////////////
void
TestFile::testHeader()
{
using namespace openvdb::io;
File file("something.vdb2");
std::ostringstream
ostr(std::ios_base::binary),
ostr2(std::ios_base::binary);
file.writeHeader(ostr2, /*seekable=*/true);
std::string uuidStr = file.getUniqueTag();
file.writeHeader(ostr, /*seekable=*/true);
// Verify that a file gets a new UUID each time it is written.
CPPUNIT_ASSERT(!file.isIdentical(uuidStr));
uuidStr = file.getUniqueTag();
std::istringstream istr(ostr.str(), std::ios_base::binary);
bool unique=true;
CPPUNIT_ASSERT_NO_THROW(unique=file.readHeader(istr));
CPPUNIT_ASSERT(!unique);//reading same file again
uint32_t version = openvdb::OPENVDB_FILE_VERSION;
CPPUNIT_ASSERT_EQUAL(version, file.fileVersion());
CPPUNIT_ASSERT_EQUAL(openvdb::OPENVDB_LIBRARY_MAJOR_VERSION, file.libraryVersion().first);
CPPUNIT_ASSERT_EQUAL(openvdb::OPENVDB_LIBRARY_MINOR_VERSION, file.libraryVersion().second);
CPPUNIT_ASSERT_EQUAL(uuidStr, file.getUniqueTag());
//std::cerr << "\nuuid=" << uuidStr << std::endl;
CPPUNIT_ASSERT(file.isIdentical(uuidStr));
remove("something.vdb2");
}
void
TestFile::testWriteGrid()
{
using namespace openvdb;
using namespace openvdb::io;
using TreeType = Int32Tree;
using GridType = Grid<TreeType>;
logging::LevelScope suppressLogging{logging::Level::Fatal};
File file("something.vdb2");
std::ostringstream ostr(std::ios_base::binary);
// Create a grid with transform.
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
GridType::Ptr grid = createGrid<GridType>(/*bg=*/1);
TreeType& tree = grid->tree();
grid->setTransform(trans);
tree.setValue(Coord(10, 1, 2), 10);
tree.setValue(Coord(0, 0, 0), 5);
// Add some metadata.
Metadata::clearRegistry();
StringMetadata::registerType();
const std::string meta0Val, meta1Val("Hello, world.");
Metadata::Ptr stringMetadata = Metadata::createMetadata(typeNameAsString<std::string>());
CPPUNIT_ASSERT(stringMetadata);
if (stringMetadata) {
grid->insertMeta("meta0", *stringMetadata);
grid->metaValue<std::string>("meta0") = meta0Val;
grid->insertMeta("meta1", *stringMetadata);
grid->metaValue<std::string>("meta1") = meta1Val;
}
// Create the grid descriptor out of this grid.
GridDescriptor gd(Name("temperature"), grid->type());
// Write out the grid.
file.writeGrid(gd, grid, ostr, /*seekable=*/true);
CPPUNIT_ASSERT(gd.getGridPos() != 0);
CPPUNIT_ASSERT(gd.getBlockPos() != 0);
CPPUNIT_ASSERT(gd.getEndPos() != 0);
// Read in the grid descriptor.
GridDescriptor gd2;
std::istringstream istr(ostr.str(), std::ios_base::binary);
// Since the input is only a fragment of a VDB file (in particular,
// it doesn't have a header), set the file format version number explicitly.
io::setCurrentVersion(istr);
GridBase::Ptr gd2_grid;
CPPUNIT_ASSERT_THROW(gd2.read(istr), openvdb::LookupError);
// Register the grid and the transform and the blocks.
GridBase::clearRegistry();
GridType::registerGrid();
// Register transform maps
math::MapRegistry::clear();
math::AffineMap::registerMap();
math::ScaleMap::registerMap();
math::UniformScaleMap::registerMap();
math::TranslationMap::registerMap();
math::ScaleTranslateMap::registerMap();
math::UniformScaleTranslateMap::registerMap();
math::NonlinearFrustumMap::registerMap();
istr.seekg(0, std::ios_base::beg);
CPPUNIT_ASSERT_NO_THROW(gd2_grid = gd2.read(istr));
CPPUNIT_ASSERT_EQUAL(gd.gridName(), gd2.gridName());
CPPUNIT_ASSERT_EQUAL(GridType::gridType(), gd2_grid->type());
CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), gd2.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), gd2.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd2.getEndPos());
// Position the stream to beginning of the grid storage and read the grid.
gd2.seekToGrid(istr);
Archive::readGridCompression(istr);
gd2_grid->readMeta(istr);
gd2_grid->readTransform(istr);
gd2_grid->readTopology(istr);
// Remove delay load metadata if it exists.
if ((*gd2_grid)["file_delayed_load"]) {
gd2_grid->removeMeta("file_delayed_load");
}
// Ensure that we have the same metadata.
CPPUNIT_ASSERT_EQUAL(grid->metaCount(), gd2_grid->metaCount());
CPPUNIT_ASSERT((*gd2_grid)["meta0"]);
CPPUNIT_ASSERT((*gd2_grid)["meta1"]);
CPPUNIT_ASSERT_EQUAL(meta0Val, gd2_grid->metaValue<std::string>("meta0"));
CPPUNIT_ASSERT_EQUAL(meta1Val, gd2_grid->metaValue<std::string>("meta1"));
// Ensure that we have the same topology and transform.
CPPUNIT_ASSERT_EQUAL(
grid->baseTree().leafCount(), gd2_grid->baseTree().leafCount());
CPPUNIT_ASSERT_EQUAL(
grid->baseTree().nonLeafCount(), gd2_grid->baseTree().nonLeafCount());
CPPUNIT_ASSERT_EQUAL(
grid->baseTree().treeDepth(), gd2_grid->baseTree().treeDepth());
//CPPUNIT_ASSERT_EQUAL(0.1, gd2_grid->getTransform()->getVoxelSizeX());
//CPPUNIT_ASSERT_EQUAL(0.1, gd2_grid->getTransform()->getVoxelSizeY());
//CPPUNIT_ASSERT_EQUAL(0.1, gd2_grid->getTransform()->getVoxelSizeZ());
// Read in the data blocks.
gd2.seekToBlocks(istr);
gd2_grid->readBuffers(istr);
TreeType::Ptr tree2 = DynamicPtrCast<TreeType>(gd2_grid->baseTreePtr());
CPPUNIT_ASSERT(tree2.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(10, tree2->getValue(Coord(10, 1, 2)));
CPPUNIT_ASSERT_EQUAL(5, tree2->getValue(Coord(0, 0, 0)));
CPPUNIT_ASSERT_EQUAL(1, tree2->getValue(Coord(1000, 1000, 16000)));
// Clear registries.
GridBase::clearRegistry();
Metadata::clearRegistry();
math::MapRegistry::clear();
remove("something.vdb2");
}
void
TestFile::testWriteMultipleGrids()
{
using namespace openvdb;
using namespace openvdb::io;
using TreeType = Int32Tree;
using GridType = Grid<TreeType>;
logging::LevelScope suppressLogging{logging::Level::Fatal};
File file("something.vdb2");
std::ostringstream ostr(std::ios_base::binary);
// Create a grid with transform.
GridType::Ptr grid = createGrid<GridType>(/*bg=*/1);
TreeType& tree = grid->tree();
tree.setValue(Coord(10, 1, 2), 10);
tree.setValue(Coord(0, 0, 0), 5);
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
grid->setTransform(trans);
GridType::Ptr grid2 = createGrid<GridType>(/*bg=*/2);
TreeType& tree2 = grid2->tree();
tree2.setValue(Coord(0, 0, 0), 10);
tree2.setValue(Coord(1000, 1000, 1000), 50);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.2);
grid2->setTransform(trans2);
// Create the grid descriptor out of this grid.
GridDescriptor gd(Name("temperature"), grid->type());
GridDescriptor gd2(Name("density"), grid2->type());
// Write out the grids.
file.writeGrid(gd, grid, ostr, /*seekable=*/true);
file.writeGrid(gd2, grid2, ostr, /*seekable=*/true);
CPPUNIT_ASSERT(gd.getGridPos() != 0);
CPPUNIT_ASSERT(gd.getBlockPos() != 0);
CPPUNIT_ASSERT(gd.getEndPos() != 0);
CPPUNIT_ASSERT(gd2.getGridPos() != 0);
CPPUNIT_ASSERT(gd2.getBlockPos() != 0);
CPPUNIT_ASSERT(gd2.getEndPos() != 0);
// register the grid
GridBase::clearRegistry();
GridType::registerGrid();
// register maps
math::MapRegistry::clear();
math::AffineMap::registerMap();
math::ScaleMap::registerMap();
math::UniformScaleMap::registerMap();
math::TranslationMap::registerMap();
math::ScaleTranslateMap::registerMap();
math::UniformScaleTranslateMap::registerMap();
math::NonlinearFrustumMap::registerMap();
// Read in the first grid descriptor.
GridDescriptor gd_in;
std::istringstream istr(ostr.str(), std::ios_base::binary);
io::setCurrentVersion(istr);
GridBase::Ptr gd_in_grid;
CPPUNIT_ASSERT_NO_THROW(gd_in_grid = gd_in.read(istr));
// Ensure read in the right values.
CPPUNIT_ASSERT_EQUAL(gd.gridName(), gd_in.gridName());
CPPUNIT_ASSERT_EQUAL(GridType::gridType(), gd_in_grid->type());
CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), gd_in.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), gd_in.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd_in.getEndPos());
// Position the stream to beginning of the grid storage and read the grid.
gd_in.seekToGrid(istr);
Archive::readGridCompression(istr);
gd_in_grid->readMeta(istr);
gd_in_grid->readTransform(istr);
gd_in_grid->readTopology(istr);
// Ensure that we have the same topology and transform.
CPPUNIT_ASSERT_EQUAL(
grid->baseTree().leafCount(), gd_in_grid->baseTree().leafCount());
CPPUNIT_ASSERT_EQUAL(
grid->baseTree().nonLeafCount(), gd_in_grid->baseTree().nonLeafCount());
CPPUNIT_ASSERT_EQUAL(
grid->baseTree().treeDepth(), gd_in_grid->baseTree().treeDepth());
// CPPUNIT_ASSERT_EQUAL(0.1, gd_in_grid->getTransform()->getVoxelSizeX());
// CPPUNIT_ASSERT_EQUAL(0.1, gd_in_grid->getTransform()->getVoxelSizeY());
// CPPUNIT_ASSERT_EQUAL(0.1, gd_in_grid->getTransform()->getVoxelSizeZ());
// Read in the data blocks.
gd_in.seekToBlocks(istr);
gd_in_grid->readBuffers(istr);
TreeType::Ptr grid_in = DynamicPtrCast<TreeType>(gd_in_grid->baseTreePtr());
CPPUNIT_ASSERT(grid_in.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(10, grid_in->getValue(Coord(10, 1, 2)));
CPPUNIT_ASSERT_EQUAL(5, grid_in->getValue(Coord(0, 0, 0)));
CPPUNIT_ASSERT_EQUAL(1, grid_in->getValue(Coord(1000, 1000, 16000)));
/////////////////////////////////////////////////////////////////
// Now read in the second grid descriptor. Make use of hte end offset.
///////////////////////////////////////////////////////////////
gd_in.seekToEnd(istr);
GridDescriptor gd2_in;
GridBase::Ptr gd2_in_grid;
CPPUNIT_ASSERT_NO_THROW(gd2_in_grid = gd2_in.read(istr));
// Ensure that we read in the right values.
CPPUNIT_ASSERT_EQUAL(gd2.gridName(), gd2_in.gridName());
CPPUNIT_ASSERT_EQUAL(TreeType::treeType(), gd2_in_grid->type());
CPPUNIT_ASSERT_EQUAL(gd2.getGridPos(), gd2_in.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd2.getBlockPos(), gd2_in.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), gd2_in.getEndPos());
// Position the stream to beginning of the grid storage and read the grid.
gd2_in.seekToGrid(istr);
Archive::readGridCompression(istr);
gd2_in_grid->readMeta(istr);
gd2_in_grid->readTransform(istr);
gd2_in_grid->readTopology(istr);
// Ensure that we have the same topology and transform.
CPPUNIT_ASSERT_EQUAL(
grid2->baseTree().leafCount(), gd2_in_grid->baseTree().leafCount());
CPPUNIT_ASSERT_EQUAL(
grid2->baseTree().nonLeafCount(), gd2_in_grid->baseTree().nonLeafCount());
CPPUNIT_ASSERT_EQUAL(
grid2->baseTree().treeDepth(), gd2_in_grid->baseTree().treeDepth());
// CPPUNIT_ASSERT_EQUAL(0.2, gd2_in_grid->getTransform()->getVoxelSizeX());
// CPPUNIT_ASSERT_EQUAL(0.2, gd2_in_grid->getTransform()->getVoxelSizeY());
// CPPUNIT_ASSERT_EQUAL(0.2, gd2_in_grid->getTransform()->getVoxelSizeZ());
// Read in the data blocks.
gd2_in.seekToBlocks(istr);
gd2_in_grid->readBuffers(istr);
TreeType::Ptr grid2_in = DynamicPtrCast<TreeType>(gd2_in_grid->baseTreePtr());
CPPUNIT_ASSERT(grid2_in.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(50, grid2_in->getValue(Coord(1000, 1000, 1000)));
CPPUNIT_ASSERT_EQUAL(10, grid2_in->getValue(Coord(0, 0, 0)));
CPPUNIT_ASSERT_EQUAL(2, grid2_in->getValue(Coord(100000, 100000, 16000)));
// Clear registries.
GridBase::clearRegistry();
math::MapRegistry::clear();
remove("something.vdb2");
}
void
TestFile::testWriteFloatAsHalf()
{
using namespace openvdb;
using namespace openvdb::io;
using TreeType = Vec3STree;
using GridType = Grid<TreeType>;
// Register all grid types.
initialize();
// Ensure that the registry is cleared on exit.
struct Local { static void uninitialize(char*) { openvdb::uninitialize(); } };
SharedPtr<char> onExit(nullptr, Local::uninitialize);
// Create two test grids.
GridType::Ptr grid1 = createGrid<GridType>(/*bg=*/Vec3s(1, 1, 1));
TreeType& tree1 = grid1->tree();
CPPUNIT_ASSERT(grid1.get() != nullptr);
grid1->setTransform(math::Transform::createLinearTransform(0.1));
grid1->setName("grid1");
GridType::Ptr grid2 = createGrid<GridType>(/*bg=*/Vec3s(2, 2, 2));
CPPUNIT_ASSERT(grid2.get() != nullptr);
TreeType& tree2 = grid2->tree();
grid2->setTransform(math::Transform::createLinearTransform(0.2));
// Flag this grid for 16-bit float output.
grid2->setSaveFloatAsHalf(true);
grid2->setName("grid2");
for (int x = 0; x < 40; ++x) {
for (int y = 0; y < 40; ++y) {
for (int z = 0; z < 40; ++z) {
tree1.setValue(Coord(x, y, z), Vec3s(float(x), float(y), float(z)));
tree2.setValue(Coord(x, y, z), Vec3s(float(x), float(y), float(z)));
}
}
}
GridPtrVec grids;
grids.push_back(grid1);
grids.push_back(grid2);
const char* filename = "something.vdb2";
{
// Write both grids to a file.
File vdbFile(filename);
vdbFile.write(grids);
}
{
// Verify that both grids can be read back successfully from the file.
File vdbFile(filename);
vdbFile.open();
GridBase::Ptr
bgrid1 = vdbFile.readGrid("grid1"),
bgrid2 = vdbFile.readGrid("grid2");
vdbFile.close();
CPPUNIT_ASSERT(bgrid1.get() != nullptr);
CPPUNIT_ASSERT(bgrid1->isType<GridType>());
CPPUNIT_ASSERT(bgrid2.get() != nullptr);
CPPUNIT_ASSERT(bgrid2->isType<GridType>());
const TreeType& btree1 = StaticPtrCast<GridType>(bgrid1)->tree();
CPPUNIT_ASSERT_EQUAL(Vec3s(10, 10, 10), btree1.getValue(Coord(10, 10, 10)));
const TreeType& btree2 = StaticPtrCast<GridType>(bgrid2)->tree();
CPPUNIT_ASSERT_EQUAL(Vec3s(10, 10, 10), btree2.getValue(Coord(10, 10, 10)));
}
}
void
TestFile::testWriteInstancedGrids()
{
using namespace openvdb;
// Register data types.
openvdb::initialize();
// Remove something.vdb2 when done. We must declare this here before the
// other grid smart_ptr's because we re-use them in the test several times.
// We will not be able to remove something.vdb2 on Windows if the pointers
// are still referencing data opened by the "file" variable.
const char* filename = "something.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
// Create grids.
Int32Tree::Ptr tree1(new Int32Tree(1));
FloatTree::Ptr tree2(new FloatTree(2.0));
GridBase::Ptr
grid1 = createGrid(tree1),
grid2 = createGrid(tree1), // instance of grid1
grid3 = createGrid(tree2),
grid4 = createGrid(tree2); // instance of grid3
grid1->setName("density");
grid2->setName("density_copy");
// Leave grid3 and grid4 unnamed.
// Create transforms.
math::Transform::Ptr trans1 = math::Transform::createLinearTransform(0.1);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1);
grid1->setTransform(trans1);
grid2->setTransform(trans2);
grid3->setTransform(trans2);
grid4->setTransform(trans1);
// Set some values.
tree1->setValue(Coord(0, 0, 0), 5);
tree1->setValue(Coord(100, 0, 0), 6);
tree2->setValue(Coord(0, 0, 0), 10);
tree2->setValue(Coord(0, 100, 0), 11);
MetaMap::Ptr meta(new MetaMap);
meta->insertMeta("author", StringMetadata("Einstein"));
meta->insertMeta("year", Int32Metadata(2009));
GridPtrVecPtr grids(new GridPtrVec);
grids->push_back(grid1);
grids->push_back(grid2);
grids->push_back(grid3);
grids->push_back(grid4);
// Write the grids to a file and then close the file.
{
io::File vdbFile(filename);
vdbFile.write(*grids, *meta);
}
meta.reset();
// Read the grids back in.
io::File file(filename);
file.open();
grids = file.getGrids();
meta = file.getMetadata();
// Verify the metadata.
CPPUNIT_ASSERT(meta.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(2, int(meta->metaCount()));
CPPUNIT_ASSERT_EQUAL(std::string("Einstein"), meta->metaValue<std::string>("author"));
CPPUNIT_ASSERT_EQUAL(2009, meta->metaValue<int32_t>("year"));
// Verify the grids.
CPPUNIT_ASSERT(grids.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(4, int(grids->size()));
GridBase::Ptr grid = findGridByName(*grids, "density");
CPPUNIT_ASSERT(grid.get() != nullptr);
Int32Tree::Ptr density = gridPtrCast<Int32Grid>(grid)->treePtr();
CPPUNIT_ASSERT(density.get() != nullptr);
grid.reset();
grid = findGridByName(*grids, "density_copy");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<Int32Grid>(grid)->treePtr().get() != nullptr);
// Verify that "density_copy" is an instance of (i.e., shares a tree with) "density".
CPPUNIT_ASSERT_EQUAL(density, gridPtrCast<Int32Grid>(grid)->treePtr());
grid.reset();
grid = findGridByName(*grids, "");
CPPUNIT_ASSERT(grid.get() != nullptr);
FloatTree::Ptr temperature = gridPtrCast<FloatGrid>(grid)->treePtr();
CPPUNIT_ASSERT(temperature.get() != nullptr);
grid.reset();
for (GridPtrVec::reverse_iterator it = grids->rbegin(); !grid && it != grids->rend(); ++it) {
// Search for the second unnamed grid starting from the end of the list.
if ((*it)->getName() == "") grid = *it;
}
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<FloatGrid>(grid)->treePtr().get() != nullptr);
// Verify that the second unnamed grid is an instance of the first.
CPPUNIT_ASSERT_EQUAL(temperature, gridPtrCast<FloatGrid>(grid)->treePtr());
CPPUNIT_ASSERT_DOUBLES_EQUAL(5, density->getValue(Coord(0, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(6, density->getValue(Coord(100, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(10, temperature->getValue(Coord(0, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(11, temperature->getValue(Coord(0, 100, 0)), /*tolerance=*/0);
// Reread with instancing disabled.
file.close();
file.setInstancingEnabled(false);
file.open();
grids = file.getGrids();
CPPUNIT_ASSERT_EQUAL(4, int(grids->size()));
grid = findGridByName(*grids, "density");
CPPUNIT_ASSERT(grid.get() != nullptr);
density = gridPtrCast<Int32Grid>(grid)->treePtr();
CPPUNIT_ASSERT(density.get() != nullptr);
grid = findGridByName(*grids, "density_copy");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<Int32Grid>(grid)->treePtr().get() != nullptr);
// Verify that "density_copy" is *not* an instance of "density".
CPPUNIT_ASSERT(gridPtrCast<Int32Grid>(grid)->treePtr() != density);
// Verify that the two unnamed grids are not instances of each other.
grid = findGridByName(*grids, "");
CPPUNIT_ASSERT(grid.get() != nullptr);
temperature = gridPtrCast<FloatGrid>(grid)->treePtr();
CPPUNIT_ASSERT(temperature.get() != nullptr);
grid.reset();
for (GridPtrVec::reverse_iterator it = grids->rbegin(); !grid && it != grids->rend(); ++it) {
// Search for the second unnamed grid starting from the end of the list.
if ((*it)->getName() == "") grid = *it;
}
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<FloatGrid>(grid)->treePtr().get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<FloatGrid>(grid)->treePtr() != temperature);
// Rewrite with instancing disabled, then reread with instancing enabled.
file.close();
{
/// @todo (FX-7063) For now, write to a new file, then, when there's
/// no longer a need for delayed load from the old file, replace it
/// with the new file.
const char* tempFilename = "somethingelse.vdb";
SharedPtr<const char> scopedTempFile(tempFilename, ::remove);
io::File vdbFile(tempFilename);
vdbFile.setInstancingEnabled(false);
vdbFile.write(*grids, *meta);
grids.reset();
// Note: Windows requires that the destination not exist, before we can rename to it.
std::remove(filename);
std::rename(tempFilename, filename);
}
file.setInstancingEnabled(true);
file.open();
grids = file.getGrids();
CPPUNIT_ASSERT_EQUAL(4, int(grids->size()));
// Verify that "density_copy" is not an instance of "density".
grid = findGridByName(*grids, "density");
CPPUNIT_ASSERT(grid.get() != nullptr);
density = gridPtrCast<Int32Grid>(grid)->treePtr();
CPPUNIT_ASSERT(density.get() != nullptr);
#if OPENVDB_ABI_VERSION_NUMBER >= 4
CPPUNIT_ASSERT(density->unallocatedLeafCount() > 0);
CPPUNIT_ASSERT_EQUAL(density->leafCount(), density->unallocatedLeafCount());
#endif
grid = findGridByName(*grids, "density_copy");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<Int32Grid>(grid)->treePtr().get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<Int32Grid>(grid)->treePtr() != density);
// Verify that the two unnamed grids are not instances of each other.
grid = findGridByName(*grids, "");
CPPUNIT_ASSERT(grid.get() != nullptr);
temperature = gridPtrCast<FloatGrid>(grid)->treePtr();
CPPUNIT_ASSERT(temperature.get() != nullptr);
grid.reset();
for (GridPtrVec::reverse_iterator it = grids->rbegin(); !grid && it != grids->rend(); ++it) {
// Search for the second unnamed grid starting from the end of the list.
if ((*it)->getName() == "") grid = *it;
}
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<FloatGrid>(grid)->treePtr().get() != nullptr);
CPPUNIT_ASSERT(gridPtrCast<FloatGrid>(grid)->treePtr() != temperature);
}
void
TestFile::testReadGridDescriptors()
{
using namespace openvdb;
using namespace openvdb::io;
using GridType = Int32Grid;
using TreeType = GridType::TreeType;
File file("something.vdb2");
std::ostringstream ostr(std::ios_base::binary);
// Create a grid with transform.
GridType::Ptr grid = createGrid<GridType>(1);
TreeType& tree = grid->tree();
tree.setValue(Coord(10, 1, 2), 10);
tree.setValue(Coord(0, 0, 0), 5);
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
grid->setTransform(trans);
// Create another grid with transform.
GridType::Ptr grid2 = createGrid<GridType>(2);
TreeType& tree2 = grid2->tree();
tree2.setValue(Coord(0, 0, 0), 10);
tree2.setValue(Coord(1000, 1000, 1000), 50);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.2);
grid2->setTransform(trans2);
// Create the grid descriptor out of this grid.
GridDescriptor gd(Name("temperature"), grid->type());
GridDescriptor gd2(Name("density"), grid2->type());
// Write out the number of grids.
int32_t gridCount = 2;
ostr.write(reinterpret_cast<char*>(&gridCount), sizeof(int32_t));
// Write out the grids.
file.writeGrid(gd, grid, ostr, /*seekable=*/true);
file.writeGrid(gd2, grid2, ostr, /*seekable=*/true);
// Register the grid and the transform and the blocks.
GridBase::clearRegistry();
GridType::registerGrid();
// register maps
math::MapRegistry::clear();
math::AffineMap::registerMap();
math::ScaleMap::registerMap();
math::UniformScaleMap::registerMap();
math::TranslationMap::registerMap();
math::ScaleTranslateMap::registerMap();
math::UniformScaleTranslateMap::registerMap();
math::NonlinearFrustumMap::registerMap();
// Read in the grid descriptors.
File file2("something.vdb2");
std::istringstream istr(ostr.str(), std::ios_base::binary);
io::setCurrentVersion(istr);
file2.readGridDescriptors(istr);
// Compare with the initial grid descriptors.
File::NameMapCIter it = file2.findDescriptor("temperature");
CPPUNIT_ASSERT(it != file2.gridDescriptors().end());
GridDescriptor file2gd = it->second;
CPPUNIT_ASSERT_EQUAL(gd.gridName(), file2gd.gridName());
CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), file2gd.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), file2gd.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), file2gd.getEndPos());
it = file2.findDescriptor("density");
CPPUNIT_ASSERT(it != file2.gridDescriptors().end());
file2gd = it->second;
CPPUNIT_ASSERT_EQUAL(gd2.gridName(), file2gd.gridName());
CPPUNIT_ASSERT_EQUAL(gd2.getGridPos(), file2gd.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd2.getBlockPos(), file2gd.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), file2gd.getEndPos());
// Clear registries.
GridBase::clearRegistry();
math::MapRegistry::clear();
remove("something.vdb2");
}
void
TestFile::testGridNaming()
{
using namespace openvdb;
using namespace openvdb::io;
using TreeType = Int32Tree;
// Register data types.
openvdb::initialize();
logging::LevelScope suppressLogging{logging::Level::Fatal};
// Create several grids that share a single tree.
TreeType::Ptr tree(new TreeType(1));
tree->setValue(Coord(10, 1, 2), 10);
tree->setValue(Coord(0, 0, 0), 5);
GridBase::Ptr
grid1 = openvdb::createGrid(tree),
grid2 = openvdb::createGrid(tree),
grid3 = openvdb::createGrid(tree);
std::vector<GridBase::Ptr> gridVec;
gridVec.push_back(grid1);
gridVec.push_back(grid2);
gridVec.push_back(grid3);
// Give all grids the same name, but also some metadata to distinguish them.
for (int n = 0; n <= 2; ++n) {
gridVec[n]->setName("grid");
gridVec[n]->insertMeta("index", Int32Metadata(n));
}
const char* filename = "testGridNaming.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
// Test first with grid instancing disabled, then with instancing enabled.
for (int instancing = 0; instancing <= 1; ++instancing) {
{
// Write the grids out to a file.
File file(filename);
file.setInstancingEnabled(instancing);
file.write(gridVec);
}
// Open the file for reading.
File file(filename);
file.setInstancingEnabled(instancing);
file.open();
int n = 0;
for (File::NameIterator i = file.beginName(), e = file.endName(); i != e; ++i, ++n) {
CPPUNIT_ASSERT(file.hasGrid(i.gridName()));
}
// Verify that the file contains three grids.
CPPUNIT_ASSERT_EQUAL(3, n);
// Read each grid.
for (n = -1; n <= 2; ++n) {
openvdb::Name name("grid");
// On the first iteration, read the grid named "grid", then read "grid[0]"
// (which is synonymous with "grid"), then "grid[1]", then "grid[2]".
if (n >= 0) {
name = GridDescriptor::nameAsString(GridDescriptor::addSuffix(name, n));
}
CPPUNIT_ASSERT(file.hasGrid(name));
// Read the current grid.
GridBase::ConstPtr grid = file.readGrid(name);
CPPUNIT_ASSERT(grid.get() != nullptr);
// Verify that the grid is named "grid".
CPPUNIT_ASSERT_EQUAL(openvdb::Name("grid"), grid->getName());
CPPUNIT_ASSERT_EQUAL((n < 0 ? 0 : n), grid->metaValue<openvdb::Int32>("index"));
}
// Read all three grids at once.
GridPtrVecPtr allGrids = file.getGrids();
CPPUNIT_ASSERT(allGrids.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(3, int(allGrids->size()));
GridBase::ConstPtr firstGrid;
std::vector<int> indices;
for (GridPtrVecCIter i = allGrids->begin(), e = allGrids->end(); i != e; ++i) {
GridBase::ConstPtr grid = *i;
CPPUNIT_ASSERT(grid.get() != nullptr);
indices.push_back(grid->metaValue<openvdb::Int32>("index"));
// If instancing is enabled, verify that all grids share the same tree.
if (instancing) {
if (!firstGrid) firstGrid = grid;
CPPUNIT_ASSERT_EQUAL(firstGrid->baseTreePtr(), grid->baseTreePtr());
}
}
// Verify that three distinct grids were read,
// by examining their "index" metadata.
CPPUNIT_ASSERT_EQUAL(3, int(indices.size()));
std::sort(indices.begin(), indices.end());
CPPUNIT_ASSERT_EQUAL(0, indices[0]);
CPPUNIT_ASSERT_EQUAL(1, indices[1]);
CPPUNIT_ASSERT_EQUAL(2, indices[2]);
}
{
// Try writing and then reading a grid with a weird name
// that might conflict with the grid name indexing scheme.
const openvdb::Name weirdName("grid[4]");
gridVec[0]->setName(weirdName);
{
File file(filename);
file.write(gridVec);
}
File file(filename);
file.open();
// Verify that the grid can be read and that its index is 0.
GridBase::ConstPtr grid = file.readGrid(weirdName);
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(weirdName, grid->getName());
CPPUNIT_ASSERT_EQUAL(0, grid->metaValue<openvdb::Int32>("index"));
// Verify that the other grids can still be read successfully.
grid = file.readGrid("grid[0]");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(openvdb::Name("grid"), grid->getName());
// Because there are now only two grids named "grid", the one with
// index 1 is now "grid[0]".
CPPUNIT_ASSERT_EQUAL(1, grid->metaValue<openvdb::Int32>("index"));
grid = file.readGrid("grid[1]");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(openvdb::Name("grid"), grid->getName());
// Because there are now only two grids named "grid", the one with
// index 2 is now "grid[1]".
CPPUNIT_ASSERT_EQUAL(2, grid->metaValue<openvdb::Int32>("index"));
// Verify that there is no longer a third grid named "grid".
CPPUNIT_ASSERT_THROW(file.readGrid("grid[2]"), openvdb::KeyError);
}
}
void
TestFile::testEmptyFile()
{
using namespace openvdb;
using namespace openvdb::io;
const char* filename = "testEmptyFile.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
{
File file(filename);
file.write(GridPtrVec(), MetaMap());
}
File file(filename);
file.open();
GridPtrVecPtr grids = file.getGrids();
MetaMap::Ptr meta = file.getMetadata();
CPPUNIT_ASSERT(grids.get() != nullptr);
CPPUNIT_ASSERT(grids->empty());
CPPUNIT_ASSERT(meta.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(0, int(meta->metaCount()));
}
void
TestFile::testEmptyGridIO()
{
using namespace openvdb;
using namespace openvdb::io;
using GridType = Int32Grid;
logging::LevelScope suppressLogging{logging::Level::Fatal};
const char* filename = "something.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
File file(filename);
std::ostringstream ostr(std::ios_base::binary);
// Create a grid with transform.
GridType::Ptr grid = createGrid<GridType>(/*bg=*/1);
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
grid->setTransform(trans);
// Create another grid with transform.
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.2);
GridType::Ptr grid2 = createGrid<GridType>(/*bg=*/2);
grid2->setTransform(trans2);
// Create the grid descriptor out of this grid.
GridDescriptor gd(Name("temperature"), grid->type());
GridDescriptor gd2(Name("density"), grid2->type());
// Write out the number of grids.
int32_t gridCount = 2;
ostr.write(reinterpret_cast<char*>(&gridCount), sizeof(int32_t));
// Write out the grids.
file.writeGrid(gd, grid, ostr, /*seekable=*/true);
file.writeGrid(gd2, grid2, ostr, /*seekable=*/true);
// Ensure that the block offset and the end offsets are equivalent.
CPPUNIT_ASSERT_EQUAL(0, int(grid->baseTree().leafCount()));
CPPUNIT_ASSERT_EQUAL(0, int(grid2->baseTree().leafCount()));
CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), gd.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), gd2.getBlockPos());
// Register the grid and the transform and the blocks.
GridBase::clearRegistry();
GridType::registerGrid();
// register maps
math::MapRegistry::clear();
math::AffineMap::registerMap();
math::ScaleMap::registerMap();
math::UniformScaleMap::registerMap();
math::TranslationMap::registerMap();
math::ScaleTranslateMap::registerMap();
math::UniformScaleTranslateMap::registerMap();
math::NonlinearFrustumMap::registerMap();
// Read in the grid descriptors.
File file2(filename);
std::istringstream istr(ostr.str(), std::ios_base::binary);
io::setCurrentVersion(istr);
file2.readGridDescriptors(istr);
// Compare with the initial grid descriptors.
File::NameMapCIter it = file2.findDescriptor("temperature");
CPPUNIT_ASSERT(it != file2.gridDescriptors().end());
GridDescriptor file2gd = it->second;
file2gd.seekToGrid(istr);
GridBase::Ptr gd_grid = GridBase::createGrid(file2gd.gridType());
Archive::readGridCompression(istr);
gd_grid->readMeta(istr);
gd_grid->readTransform(istr);
gd_grid->readTopology(istr);
CPPUNIT_ASSERT_EQUAL(gd.gridName(), file2gd.gridName());
CPPUNIT_ASSERT(gd_grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(0, int(gd_grid->baseTree().leafCount()));
//CPPUNIT_ASSERT_EQUAL(8, int(gd_grid->baseTree().nonLeafCount()));
CPPUNIT_ASSERT_EQUAL(4, int(gd_grid->baseTree().treeDepth()));
CPPUNIT_ASSERT_EQUAL(gd.getGridPos(), file2gd.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd.getBlockPos(), file2gd.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd.getEndPos(), file2gd.getEndPos());
it = file2.findDescriptor("density");
CPPUNIT_ASSERT(it != file2.gridDescriptors().end());
file2gd = it->second;
file2gd.seekToGrid(istr);
gd_grid = GridBase::createGrid(file2gd.gridType());
Archive::readGridCompression(istr);
gd_grid->readMeta(istr);
gd_grid->readTransform(istr);
gd_grid->readTopology(istr);
CPPUNIT_ASSERT_EQUAL(gd2.gridName(), file2gd.gridName());
CPPUNIT_ASSERT(gd_grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(0, int(gd_grid->baseTree().leafCount()));
//CPPUNIT_ASSERT_EQUAL(8, int(gd_grid->nonLeafCount()));
CPPUNIT_ASSERT_EQUAL(4, int(gd_grid->baseTree().treeDepth()));
CPPUNIT_ASSERT_EQUAL(gd2.getGridPos(), file2gd.getGridPos());
CPPUNIT_ASSERT_EQUAL(gd2.getBlockPos(), file2gd.getBlockPos());
CPPUNIT_ASSERT_EQUAL(gd2.getEndPos(), file2gd.getEndPos());
// Clear registries.
GridBase::clearRegistry();
math::MapRegistry::clear();
}
void
TestFile::testOpen()
{
using namespace openvdb;
using FloatGrid = openvdb::FloatGrid;
using IntGrid = openvdb::Int32Grid;
using FloatTree = FloatGrid::TreeType;
using IntTree = Int32Grid::TreeType;
// Create a VDB to write.
// Create grids
IntGrid::Ptr grid = createGrid<IntGrid>(/*bg=*/1);
IntTree& tree = grid->tree();
grid->setName("density");
FloatGrid::Ptr grid2 = createGrid<FloatGrid>(/*bg=*/2.0);
FloatTree& tree2 = grid2->tree();
grid2->setName("temperature");
// Create transforms
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1);
grid->setTransform(trans);
grid2->setTransform(trans2);
// Set some values
tree.setValue(Coord(0, 0, 0), 5);
tree.setValue(Coord(100, 0, 0), 6);
tree2.setValue(Coord(0, 0, 0), 10);
tree2.setValue(Coord(0, 100, 0), 11);
MetaMap meta;
meta.insertMeta("author", StringMetadata("Einstein"));
meta.insertMeta("year", Int32Metadata(2009));
GridPtrVec grids;
grids.push_back(grid);
grids.push_back(grid2);
CPPUNIT_ASSERT(findGridByName(grids, "density") == grid);
CPPUNIT_ASSERT(findGridByName(grids, "temperature") == grid2);
CPPUNIT_ASSERT(meta.metaValue<std::string>("author") == "Einstein");
CPPUNIT_ASSERT_EQUAL(2009, meta.metaValue<int32_t>("year"));
// Register grid and transform.
GridBase::clearRegistry();
IntGrid::registerGrid();
FloatGrid::registerGrid();
Metadata::clearRegistry();
StringMetadata::registerType();
Int32Metadata::registerType();
// register maps
math::MapRegistry::clear();
math::AffineMap::registerMap();
math::ScaleMap::registerMap();
math::UniformScaleMap::registerMap();
math::TranslationMap::registerMap();
math::ScaleTranslateMap::registerMap();
math::UniformScaleTranslateMap::registerMap();
math::NonlinearFrustumMap::registerMap();
// Write the vdb out to a file.
io::File vdbfile("something.vdb2");
vdbfile.write(grids, meta);
// Now we can read in the file.
CPPUNIT_ASSERT(!vdbfile.open());//opening the same file
// Can't open same file multiple times without closing.
CPPUNIT_ASSERT_THROW(vdbfile.open(), openvdb::IoError);
vdbfile.close();
CPPUNIT_ASSERT(!vdbfile.open());//opening the same file
CPPUNIT_ASSERT(vdbfile.isOpen());
uint32_t version = OPENVDB_FILE_VERSION;
CPPUNIT_ASSERT_EQUAL(version, vdbfile.fileVersion());
CPPUNIT_ASSERT_EQUAL(version, io::getFormatVersion(vdbfile.inputStream()));
CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MAJOR_VERSION, vdbfile.libraryVersion().first);
CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MINOR_VERSION, vdbfile.libraryVersion().second);
CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MAJOR_VERSION,
io::getLibraryVersion(vdbfile.inputStream()).first);
CPPUNIT_ASSERT_EQUAL(OPENVDB_LIBRARY_MINOR_VERSION,
io::getLibraryVersion(vdbfile.inputStream()).second);
// Ensure that we read in the vdb metadata.
CPPUNIT_ASSERT(vdbfile.getMetadata());
CPPUNIT_ASSERT(vdbfile.getMetadata()->metaValue<std::string>("author") == "Einstein");
CPPUNIT_ASSERT_EQUAL(2009, vdbfile.getMetadata()->metaValue<int32_t>("year"));
// Ensure we got the grid descriptors.
CPPUNIT_ASSERT_EQUAL(1, int(vdbfile.gridDescriptors().count("density")));
CPPUNIT_ASSERT_EQUAL(1, int(vdbfile.gridDescriptors().count("temperature")));
io::File::NameMapCIter it = vdbfile.findDescriptor("density");
CPPUNIT_ASSERT(it != vdbfile.gridDescriptors().end());
io::GridDescriptor gd = it->second;
CPPUNIT_ASSERT_EQUAL(IntTree::treeType(), gd.gridType());
it = vdbfile.findDescriptor("temperature");
CPPUNIT_ASSERT(it != vdbfile.gridDescriptors().end());
gd = it->second;
CPPUNIT_ASSERT_EQUAL(FloatTree::treeType(), gd.gridType());
// Ensure we throw an error if there is no file.
io::File vdbfile2("somethingelses.vdb2");
CPPUNIT_ASSERT_THROW(vdbfile2.open(), openvdb::IoError);
CPPUNIT_ASSERT_THROW(vdbfile2.inputStream(), openvdb::IoError);
// Clear registries.
GridBase::clearRegistry();
Metadata::clearRegistry();
math::MapRegistry::clear();
// Test closing the file.
vdbfile.close();
CPPUNIT_ASSERT(vdbfile.isOpen() == false);
CPPUNIT_ASSERT(vdbfile.fileMetadata().get() == nullptr);
CPPUNIT_ASSERT_EQUAL(0, int(vdbfile.gridDescriptors().size()));
CPPUNIT_ASSERT_THROW(vdbfile.inputStream(), openvdb::IoError);
remove("something.vdb2");
}
void
TestFile::testNonVdbOpen()
{
std::ofstream file("dummy.vdb2", std::ios_base::binary);
int64_t something = 1;
file.write(reinterpret_cast<char*>(&something), sizeof(int64_t));
file.close();
openvdb::io::File vdbfile("dummy.vdb2");
CPPUNIT_ASSERT_THROW(vdbfile.open(), openvdb::IoError);
CPPUNIT_ASSERT_THROW(vdbfile.inputStream(), openvdb::IoError);
remove("dummy.vdb2");
}
void
TestFile::testGetMetadata()
{
using namespace openvdb;
GridPtrVec grids;
MetaMap meta;
meta.insertMeta("author", StringMetadata("Einstein"));
meta.insertMeta("year", Int32Metadata(2009));
// Adjust registry before writing.
Metadata::clearRegistry();
StringMetadata::registerType();
Int32Metadata::registerType();
// Write the vdb out to a file.
io::File vdbfile("something.vdb2");
vdbfile.write(grids, meta);
// Check if reading without opening the file
CPPUNIT_ASSERT_THROW(vdbfile.getMetadata(), openvdb::IoError);
vdbfile.open();
MetaMap::Ptr meta2 = vdbfile.getMetadata();
CPPUNIT_ASSERT_EQUAL(2, int(meta2->metaCount()));
CPPUNIT_ASSERT(meta2->metaValue<std::string>("author") == "Einstein");
CPPUNIT_ASSERT_EQUAL(2009, meta2->metaValue<int32_t>("year"));
// Clear registry.
Metadata::clearRegistry();
remove("something.vdb2");
}
void
TestFile::testReadAll()
{
using namespace openvdb;
using FloatGrid = openvdb::FloatGrid;
using IntGrid = openvdb::Int32Grid;
using FloatTree = FloatGrid::TreeType;
using IntTree = Int32Grid::TreeType;
// Create a vdb to write.
// Create grids
IntGrid::Ptr grid1 = createGrid<IntGrid>(/*bg=*/1);
IntTree& tree = grid1->tree();
grid1->setName("density");
FloatGrid::Ptr grid2 = createGrid<FloatGrid>(/*bg=*/2.0);
FloatTree& tree2 = grid2->tree();
grid2->setName("temperature");
// Create transforms
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1);
grid1->setTransform(trans);
grid2->setTransform(trans2);
// Set some values
tree.setValue(Coord(0, 0, 0), 5);
tree.setValue(Coord(100, 0, 0), 6);
tree2.setValue(Coord(0, 0, 0), 10);
tree2.setValue(Coord(0, 100, 0), 11);
MetaMap meta;
meta.insertMeta("author", StringMetadata("Einstein"));
meta.insertMeta("year", Int32Metadata(2009));
GridPtrVec grids;
grids.push_back(grid1);
grids.push_back(grid2);
// Register grid and transform.
openvdb::initialize();
// Write the vdb out to a file.
io::File vdbfile("something.vdb2");
vdbfile.write(grids, meta);
io::File vdbfile2("something.vdb2");
CPPUNIT_ASSERT_THROW(vdbfile2.getGrids(), openvdb::IoError);
vdbfile2.open();
CPPUNIT_ASSERT(vdbfile2.isOpen());
GridPtrVecPtr grids2 = vdbfile2.getGrids();
MetaMap::Ptr meta2 = vdbfile2.getMetadata();
// Ensure we have the metadata.
CPPUNIT_ASSERT_EQUAL(2, int(meta2->metaCount()));
CPPUNIT_ASSERT(meta2->metaValue<std::string>("author") == "Einstein");
CPPUNIT_ASSERT_EQUAL(2009, meta2->metaValue<int32_t>("year"));
// Ensure we got the grids.
CPPUNIT_ASSERT_EQUAL(2, int(grids2->size()));
GridBase::Ptr grid;
grid.reset();
grid = findGridByName(*grids2, "density");
CPPUNIT_ASSERT(grid.get() != nullptr);
IntTree::Ptr density = gridPtrCast<IntGrid>(grid)->treePtr();
CPPUNIT_ASSERT(density.get() != nullptr);
grid.reset();
grid = findGridByName(*grids2, "temperature");
CPPUNIT_ASSERT(grid.get() != nullptr);
FloatTree::Ptr temperature = gridPtrCast<FloatGrid>(grid)->treePtr();
CPPUNIT_ASSERT(temperature.get() != nullptr);
CPPUNIT_ASSERT_DOUBLES_EQUAL(5, density->getValue(Coord(0, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(6, density->getValue(Coord(100, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(10, temperature->getValue(Coord(0, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(11, temperature->getValue(Coord(0, 100, 0)), /*tolerance=*/0);
// Clear registries.
GridBase::clearRegistry();
Metadata::clearRegistry();
math::MapRegistry::clear();
vdbfile2.close();
remove("something.vdb2");
}
void
TestFile::testWriteOpenFile()
{
using namespace openvdb;
MetaMap::Ptr meta(new MetaMap);
meta->insertMeta("author", StringMetadata("Einstein"));
meta->insertMeta("year", Int32Metadata(2009));
// Register metadata
Metadata::clearRegistry();
StringMetadata::registerType();
Int32Metadata::registerType();
// Write the metadata out to a file.
io::File vdbfile("something.vdb2");
vdbfile.write(GridPtrVec(), *meta);
io::File vdbfile2("something.vdb2");
CPPUNIT_ASSERT_THROW(vdbfile2.getGrids(), openvdb::IoError);
vdbfile2.open();
CPPUNIT_ASSERT(vdbfile2.isOpen());
GridPtrVecPtr grids = vdbfile2.getGrids();
meta = vdbfile2.getMetadata();
// Ensure we have the metadata.
CPPUNIT_ASSERT(meta.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(2, int(meta->metaCount()));
CPPUNIT_ASSERT(meta->metaValue<std::string>("author") == "Einstein");
CPPUNIT_ASSERT_EQUAL(2009, meta->metaValue<int32_t>("year"));
// Ensure we got the grids.
CPPUNIT_ASSERT(grids.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(0, int(grids->size()));
// Cannot write an open file.
CPPUNIT_ASSERT_THROW(vdbfile2.write(*grids), openvdb::IoError);
vdbfile2.close();
CPPUNIT_ASSERT_NO_THROW(vdbfile2.write(*grids));
// Clear registries.
Metadata::clearRegistry();
remove("something.vdb2");
}
void
TestFile::testReadGridMetadata()
{
using namespace openvdb;
openvdb::initialize();
const char* filename = "testReadGridMetadata.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
// Create grids
Int32Grid::Ptr igrid = createGrid<Int32Grid>(/*bg=*/1);
FloatGrid::Ptr fgrid = createGrid<FloatGrid>(/*bg=*/2.0);
// Add metadata.
igrid->setName("igrid");
igrid->insertMeta("author", StringMetadata("Einstein"));
igrid->insertMeta("year", Int32Metadata(2012));
fgrid->setName("fgrid");
fgrid->insertMeta("author", StringMetadata("Einstein"));
fgrid->insertMeta("year", Int32Metadata(2012));
// Add transforms.
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
igrid->setTransform(trans);
fgrid->setTransform(trans);
// Set some values.
igrid->tree().setValue(Coord(0, 0, 0), 5);
igrid->tree().setValue(Coord(100, 0, 0), 6);
fgrid->tree().setValue(Coord(0, 0, 0), 10);
fgrid->tree().setValue(Coord(0, 100, 0), 11);
GridPtrVec srcGrids;
srcGrids.push_back(igrid);
srcGrids.push_back(fgrid);
std::map<std::string, GridBase::Ptr> srcGridMap;
srcGridMap[igrid->getName()] = igrid;
srcGridMap[fgrid->getName()] = fgrid;
enum { OUTPUT_TO_FILE = 0, OUTPUT_TO_STREAM = 1 };
for (int outputMethod = OUTPUT_TO_FILE; outputMethod <= OUTPUT_TO_STREAM; ++outputMethod)
{
if (outputMethod == OUTPUT_TO_FILE) {
// Write the grids to a file.
io::File vdbfile(filename);
vdbfile.write(srcGrids);
} else {
// Stream the grids to a file (i.e., without file offsets).
std::ofstream ostrm(filename, std::ios_base::binary);
io::Stream(ostrm).write(srcGrids);
}
// Read just the grid-level metadata from the file.
io::File vdbfile(filename);
// Verify that reading from an unopened file generates an exception.
CPPUNIT_ASSERT_THROW(vdbfile.readGridMetadata("igrid"), openvdb::IoError);
CPPUNIT_ASSERT_THROW(vdbfile.readGridMetadata("noname"), openvdb::IoError);
CPPUNIT_ASSERT_THROW(vdbfile.readAllGridMetadata(), openvdb::IoError);
vdbfile.open();
CPPUNIT_ASSERT(vdbfile.isOpen());
// Verify that reading a nonexistent grid generates an exception.
CPPUNIT_ASSERT_THROW(vdbfile.readGridMetadata("noname"), openvdb::KeyError);
// Read all grids and store them in a list.
GridPtrVecPtr gridMetadata = vdbfile.readAllGridMetadata();
CPPUNIT_ASSERT(gridMetadata.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(2, int(gridMetadata->size()));
// Read individual grids and append them to the list.
GridBase::Ptr grid = vdbfile.readGridMetadata("igrid");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(std::string("igrid"), grid->getName());
gridMetadata->push_back(grid);
grid = vdbfile.readGridMetadata("fgrid");
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(std::string("fgrid"), grid->getName());
gridMetadata->push_back(grid);
// Verify that the grids' metadata and transforms match the original grids'.
for (size_t i = 0, N = gridMetadata->size(); i < N; ++i) {
grid = (*gridMetadata)[i];
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT(grid->getName() == "igrid" || grid->getName() == "fgrid");
CPPUNIT_ASSERT(grid->baseTreePtr().get() != nullptr);
// Since we didn't read the grid's topology, the tree should be empty.
CPPUNIT_ASSERT_EQUAL(0, int(grid->constBaseTreePtr()->leafCount()));
CPPUNIT_ASSERT_EQUAL(0, int(grid->constBaseTreePtr()->activeVoxelCount()));
// Retrieve the source grid of the same name.
GridBase::ConstPtr srcGrid = srcGridMap[grid->getName()];
// Compare grid types and transforms.
CPPUNIT_ASSERT_EQUAL(srcGrid->type(), grid->type());
CPPUNIT_ASSERT_EQUAL(srcGrid->transform(), grid->transform());
// Compare metadata, ignoring fields that were added when the file was written.
MetaMap::Ptr
statsMetadata = grid->getStatsMetadata(),
otherMetadata = grid->copyMeta(); // shallow copy
CPPUNIT_ASSERT(statsMetadata->metaCount() != 0);
statsMetadata->insertMeta(GridBase::META_FILE_COMPRESSION, StringMetadata(""));
for (MetaMap::ConstMetaIterator it = grid->beginMeta(), end = grid->endMeta();
it != end; ++it)
{
// Keep all fields that exist in the source grid.
if ((*srcGrid)[it->first]) continue;
// Remove any remaining grid statistics fields.
if ((*statsMetadata)[it->first]) {
otherMetadata->removeMeta(it->first);
}
// Remove delay load metadata if it exists.
if ((*otherMetadata)["file_delayed_load"]) {
otherMetadata->removeMeta("file_delayed_load");
}
}
CPPUNIT_ASSERT_EQUAL(srcGrid->str(), otherMetadata->str());
const CoordBBox srcBBox = srcGrid->evalActiveVoxelBoundingBox();
CPPUNIT_ASSERT_EQUAL(srcBBox.min().asVec3i(), grid->metaValue<Vec3i>("file_bbox_min"));
CPPUNIT_ASSERT_EQUAL(srcBBox.max().asVec3i(), grid->metaValue<Vec3i>("file_bbox_max"));
CPPUNIT_ASSERT_EQUAL(srcGrid->activeVoxelCount(),
Index64(grid->metaValue<Int64>("file_voxel_count")));
CPPUNIT_ASSERT_EQUAL(srcGrid->memUsage(),
Index64(grid->metaValue<Int64>("file_mem_bytes")));
}
}
}
void
TestFile::testReadGrid()
{
using namespace openvdb;
using FloatGrid = openvdb::FloatGrid;
using IntGrid = openvdb::Int32Grid;
using FloatTree = FloatGrid::TreeType;
using IntTree = Int32Grid::TreeType;
// Create a vdb to write.
// Create grids
IntGrid::Ptr grid = createGrid<IntGrid>(/*bg=*/1);
IntTree& tree = grid->tree();
grid->setName("density");
FloatGrid::Ptr grid2 = createGrid<FloatGrid>(/*bg=*/2.0);
FloatTree& tree2 = grid2->tree();
grid2->setName("temperature");
// Create transforms
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1);
grid->setTransform(trans);
grid2->setTransform(trans2);
// Set some values
tree.setValue(Coord(0, 0, 0), 5);
tree.setValue(Coord(100, 0, 0), 6);
tree2.setValue(Coord(0, 0, 0), 10);
tree2.setValue(Coord(0, 100, 0), 11);
MetaMap meta;
meta.insertMeta("author", StringMetadata("Einstein"));
meta.insertMeta("year", Int32Metadata(2009));
GridPtrVec grids;
grids.push_back(grid);
grids.push_back(grid2);
// Register grid and transform.
openvdb::initialize();
// Write the vdb out to a file.
io::File vdbfile("something.vdb2");
vdbfile.write(grids, meta);
io::File vdbfile2("something.vdb2");
vdbfile2.open();
CPPUNIT_ASSERT(vdbfile2.isOpen());
// Get Temperature
GridBase::Ptr temperature = vdbfile2.readGrid("temperature");
CPPUNIT_ASSERT(temperature.get() != nullptr);
FloatTree::Ptr typedTemperature = gridPtrCast<FloatGrid>(temperature)->treePtr();
CPPUNIT_ASSERT(typedTemperature.get() != nullptr);
CPPUNIT_ASSERT_DOUBLES_EQUAL(10, typedTemperature->getValue(Coord(0, 0, 0)), 0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(11, typedTemperature->getValue(Coord(0, 100, 0)), 0);
// Get Density
GridBase::Ptr density = vdbfile2.readGrid("density");
CPPUNIT_ASSERT(density.get() != nullptr);
IntTree::Ptr typedDensity = gridPtrCast<IntGrid>(density)->treePtr();
CPPUNIT_ASSERT(typedDensity.get() != nullptr);
CPPUNIT_ASSERT_DOUBLES_EQUAL(5,typedDensity->getValue(Coord(0, 0, 0)), /*tolerance=*/0);
CPPUNIT_ASSERT_DOUBLES_EQUAL(6,typedDensity->getValue(Coord(100, 0, 0)), /*tolerance=*/0);
// Clear registries.
GridBase::clearRegistry();
Metadata::clearRegistry();
math::MapRegistry::clear();
vdbfile2.close();
remove("something.vdb2");
}
////////////////////////////////////////
template<typename GridT>
void
validateClippedGrid(const GridT& clipped, const typename GridT::ValueType& fg)
{
using namespace openvdb;
using ValueT = typename GridT::ValueType;
const CoordBBox bbox = clipped.evalActiveVoxelBoundingBox();
CPPUNIT_ASSERT_EQUAL(4, bbox.min().x());
CPPUNIT_ASSERT_EQUAL(4, bbox.min().y());
CPPUNIT_ASSERT_EQUAL(-6, bbox.min().z());
CPPUNIT_ASSERT_EQUAL(4, bbox.max().x());
CPPUNIT_ASSERT_EQUAL(4, bbox.max().y());
CPPUNIT_ASSERT_EQUAL(6, bbox.max().z());
CPPUNIT_ASSERT_EQUAL(6 + 6 + 1, int(clipped.activeVoxelCount()));
CPPUNIT_ASSERT_EQUAL(2, int(clipped.constTree().leafCount()));
typename GridT::ConstAccessor acc = clipped.getConstAccessor();
const ValueT bg = clipped.background();
Coord xyz;
int &x = xyz[0], &y = xyz[1], &z = xyz[2];
for (x = -10; x <= 10; ++x) {
for (y = -10; y <= 10; ++y) {
for (z = -10; z <= 10; ++z) {
if (x == 4 && y == 4 && z >= -6 && z <= 6) {
CPPUNIT_ASSERT_EQUAL(fg, acc.getValue(Coord(4, 4, z)));
} else {
CPPUNIT_ASSERT_EQUAL(bg, acc.getValue(Coord(x, y, z)));
}
}
}
}
}
// See also TestGrid::testClipping()
void
TestFile::testReadClippedGrid()
{
using namespace openvdb;
// Register types.
openvdb::initialize();
// World-space clipping region
const BBoxd clipBox(Vec3d(4.0, 4.0, -6.0), Vec3d(4.9, 4.9, 6.0));
// Create grids of several types and fill a cubic region of each with a foreground value.
const bool bfg = true;
BoolGrid::Ptr bgrid = BoolGrid::create(/*bg=*/zeroVal<bool>());
bgrid->setName("bgrid");
bgrid->fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/bfg, /*active=*/true);
const float ffg = 5.f;
FloatGrid::Ptr fgrid = FloatGrid::create(/*bg=*/zeroVal<float>());
fgrid->setName("fgrid");
fgrid->fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/ffg, /*active=*/true);
const Vec3s vfg(1.f, -2.f, 3.f);
Vec3SGrid::Ptr vgrid = Vec3SGrid::create(/*bg=*/zeroVal<Vec3s>());
vgrid->setName("vgrid");
vgrid->fill(CoordBBox(Coord(-10), Coord(10)), /*value=*/vfg, /*active=*/true);
GridPtrVec srcGrids;
srcGrids.push_back(bgrid);
srcGrids.push_back(fgrid);
srcGrids.push_back(vgrid);
const char* filename = "testReadClippedGrid.vdb";
SharedPtr<const char> scopedFile(filename, ::remove);
enum { OUTPUT_TO_FILE = 0, OUTPUT_TO_STREAM = 1 };
for (int outputMethod = OUTPUT_TO_FILE; outputMethod <= OUTPUT_TO_STREAM; ++outputMethod)
{
if (outputMethod == OUTPUT_TO_FILE) {
// Write the grids to a file.
io::File vdbfile(filename);
vdbfile.write(srcGrids);
} else {
// Stream the grids to a file (i.e., without file offsets).
std::ofstream ostrm(filename, std::ios_base::binary);
io::Stream(ostrm).write(srcGrids);
}
// Open the file for reading.
io::File vdbfile(filename);
vdbfile.open();
GridBase::Ptr grid;
// Read and clip each grid.
CPPUNIT_ASSERT_NO_THROW(grid = vdbfile.readGrid("bgrid", clipBox));
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_NO_THROW(bgrid = gridPtrCast<BoolGrid>(grid));
validateClippedGrid(*bgrid, bfg);
CPPUNIT_ASSERT_NO_THROW(grid = vdbfile.readGrid("fgrid", clipBox));
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_NO_THROW(fgrid = gridPtrCast<FloatGrid>(grid));
validateClippedGrid(*fgrid, ffg);
CPPUNIT_ASSERT_NO_THROW(grid = vdbfile.readGrid("vgrid", clipBox));
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_NO_THROW(vgrid = gridPtrCast<Vec3SGrid>(grid));
validateClippedGrid(*vgrid, vfg);
}
}
////////////////////////////////////////
namespace {
template<typename T, openvdb::Index Log2Dim> struct MultiPassLeafNode; // forward declaration
// Dummy value type
using MultiPassValue = openvdb::PointIndex<openvdb::Index32, 1000>;
// Tree configured to match the default OpenVDB configuration
using MultiPassTree = openvdb::tree::Tree<
openvdb::tree::RootNode<
openvdb::tree::InternalNode<
openvdb::tree::InternalNode<
MultiPassLeafNode<MultiPassValue, 3>, 4>, 5>>>;
using MultiPassGrid = openvdb::Grid<MultiPassTree>;
template<typename T, openvdb::Index Log2Dim>
struct MultiPassLeafNode: public openvdb::tree::LeafNode<T, Log2Dim>, openvdb::io::MultiPass
{
// The following had to be copied from the LeafNode class
// to make the derived class compatible with the tree structure.
using LeafNodeType = MultiPassLeafNode;
using Ptr = openvdb::SharedPtr<MultiPassLeafNode>;
using BaseLeaf = openvdb::tree::LeafNode<T, Log2Dim>;
using NodeMaskType = openvdb::util::NodeMask<Log2Dim>;
using ValueType = T;
using ValueOnCIter = typename BaseLeaf::template ValueIter<typename NodeMaskType::OnIterator,
const MultiPassLeafNode, const ValueType, typename BaseLeaf::ValueOn>;
using ChildOnIter = typename BaseLeaf::template ChildIter<typename NodeMaskType::OnIterator,
MultiPassLeafNode, typename BaseLeaf::ChildOn>;
using ChildOnCIter = typename BaseLeaf::template ChildIter<
typename NodeMaskType::OnIterator, const MultiPassLeafNode, typename BaseLeaf::ChildOn>;
MultiPassLeafNode(const openvdb::Coord& coords, const T& value, bool active = false)
: BaseLeaf(coords, value, active) {}
MultiPassLeafNode(openvdb::PartialCreate, const openvdb::Coord& coords, const T& value,
bool active = false): BaseLeaf(openvdb::PartialCreate(), coords, value, active) {}
MultiPassLeafNode(const MultiPassLeafNode& rhs): BaseLeaf(rhs) {}
ValueOnCIter cbeginValueOn() const { return ValueOnCIter(this->getValueMask().beginOn(),this); }
ChildOnCIter cbeginChildOn() const { return ChildOnCIter(this->getValueMask().endOn(), this); }
ChildOnIter beginChildOn() { return ChildOnIter(this->getValueMask().endOn(), this); }
// Methods in use for reading and writing multiple buffers
void readBuffers(std::istream& is, const openvdb::CoordBBox&, bool fromHalf = false)
{
this->readBuffers(is, fromHalf);
}
void readBuffers(std::istream& is, bool /*fromHalf*/ = false)
{
const openvdb::io::StreamMetadata::Ptr meta = openvdb::io::getStreamMetadataPtr(is);
if (!meta) {
OPENVDB_THROW(openvdb::IoError,
"Cannot write out a MultiBufferLeaf without StreamMetadata.");
}
// clamp pass to 16-bit integer
const uint32_t pass(static_cast<uint16_t>(meta->pass()));
// Read in the stored pass number.
uint32_t readPass;
is.read(reinterpret_cast<char*>(&readPass), sizeof(uint32_t));
CPPUNIT_ASSERT_EQUAL(pass, readPass);
// Record the pass number.
mReadPasses.push_back(readPass);
if (pass == 0) {
// Read in the node's origin.
openvdb::Coord origin;
is.read(reinterpret_cast<char*>(&origin), sizeof(openvdb::Coord));
CPPUNIT_ASSERT_EQUAL(origin, this->origin());
}
}
void writeBuffers(std::ostream& os, bool /*toHalf*/ = false) const
{
const openvdb::io::StreamMetadata::Ptr meta = openvdb::io::getStreamMetadataPtr(os);
if (!meta) {
OPENVDB_THROW(openvdb::IoError,
"Cannot read in a MultiBufferLeaf without StreamMetadata.");
}
// clamp pass to 16-bit integer
const uint32_t pass(static_cast<uint16_t>(meta->pass()));
// Leaf traversal analysis deduces the number of passes to perform for this leaf
// then updates the leaf traversal value to ensure all passes will be written.
if (meta->countingPasses()) {
if (mNumPasses > pass) meta->setPass(mNumPasses);
return;
}
// Record the pass number.
CPPUNIT_ASSERT(mWritePassesPtr);
const_cast<std::vector<int>&>(*mWritePassesPtr).push_back(pass);
// Write out the pass number.
os.write(reinterpret_cast<const char*>(&pass), sizeof(uint32_t));
if (pass == 0) {
// Write out the node's origin and the pass number.
const auto origin = this->origin();
os.write(reinterpret_cast<const char*>(&origin), sizeof(openvdb::Coord));
}
}
uint32_t mNumPasses = 0;
// Pointer to external vector in which to record passes as they are written
std::vector<int>* mWritePassesPtr = nullptr;
// Vector in which to record passes as they are read
// (this needs to be internal, because leaf nodes are constructed as a grid is read)
std::vector<int> mReadPasses;
}; // struct MultiPassLeafNode
} // anonymous namespace
void
TestFile::testMultiPassIO()
{
using namespace openvdb;
openvdb::initialize();
MultiPassGrid::registerGrid();
// Create a multi-buffer grid.
const MultiPassGrid::Ptr grid = openvdb::createGrid<MultiPassGrid>();
grid->setName("test");
grid->setTransform(math::Transform::createLinearTransform(1.0));
MultiPassGrid::TreeType& tree = grid->tree();
tree.setValue(Coord(0, 0, 0), 5);
tree.setValue(Coord(0, 10, 0), 5);
CPPUNIT_ASSERT_EQUAL(2, int(tree.leafCount()));
const GridPtrVec grids{grid};
// Vector in which to record pass numbers (to ensure blocked ordering)
std::vector<int> writePasses;
{
// Specify the required number of I/O passes for each leaf node.
MultiPassGrid::TreeType::LeafIter leafIter = tree.beginLeaf();
leafIter->mNumPasses = 3;
leafIter->mWritePassesPtr = &writePasses;
++leafIter;
leafIter->mNumPasses = 2;
leafIter->mWritePassesPtr = &writePasses;
}
const char* filename = "testMultiPassIO.vdb";
SharedPtr<const char> scopedFile(filename, ::remove);
{
// Verify that passes are written to a file in the correct order.
io::File(filename).write(grids);
CPPUNIT_ASSERT_EQUAL(6, int(writePasses.size()));
CPPUNIT_ASSERT_EQUAL(0, writePasses[0]); // leaf 0
CPPUNIT_ASSERT_EQUAL(0, writePasses[1]); // leaf 1
CPPUNIT_ASSERT_EQUAL(1, writePasses[2]); // leaf 0
CPPUNIT_ASSERT_EQUAL(1, writePasses[3]); // leaf 1
CPPUNIT_ASSERT_EQUAL(2, writePasses[4]); // leaf 0
CPPUNIT_ASSERT_EQUAL(2, writePasses[5]); // leaf 1
}
{
// Verify that passes are read in the correct order.
io::File file(filename);
file.open();
const auto newGrid = GridBase::grid<MultiPassGrid>(file.readGrid("test"));
auto leafIter = newGrid->tree().beginLeaf();
CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size()));
CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]);
CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]);
CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]);
++leafIter;
CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size()));
CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]);
CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]);
CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]);
}
#if OPENVDB_ABI_VERSION_NUMBER >= 4
{
// Verify that when using multi-pass and bbox clipping that each leaf node
// is still being read before being clipped
io::File file(filename);
file.open();
const auto newGrid = GridBase::grid<MultiPassGrid>(
file.readGrid("test", BBoxd(Vec3d(0), Vec3d(1))));
CPPUNIT_ASSERT_EQUAL(Index32(1), newGrid->tree().leafCount());
auto leafIter = newGrid->tree().beginLeaf();
CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size()));
CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]);
CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]);
CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]);
++leafIter;
CPPUNIT_ASSERT(!leafIter); // second leaf node has now been clipped
}
#endif
// Clear the pass data.
writePasses.clear();
{
// Verify that passes are written to and read from a non-seekable stream
// in the correct order.
std::ostringstream ostr(std::ios_base::binary);
io::Stream(ostr).write(grids);
CPPUNIT_ASSERT_EQUAL(6, int(writePasses.size()));
CPPUNIT_ASSERT_EQUAL(0, writePasses[0]); // leaf 0
CPPUNIT_ASSERT_EQUAL(0, writePasses[1]); // leaf 1
CPPUNIT_ASSERT_EQUAL(1, writePasses[2]); // leaf 0
CPPUNIT_ASSERT_EQUAL(1, writePasses[3]); // leaf 1
CPPUNIT_ASSERT_EQUAL(2, writePasses[4]); // leaf 0
CPPUNIT_ASSERT_EQUAL(2, writePasses[5]); // leaf 1
std::istringstream is(ostr.str(), std::ios_base::binary);
io::Stream strm(is);
const auto streamedGrids = strm.getGrids();
CPPUNIT_ASSERT_EQUAL(1, int(streamedGrids->size()));
const auto newGrid = gridPtrCast<MultiPassGrid>(*streamedGrids->begin());
CPPUNIT_ASSERT(bool(newGrid));
auto leafIter = newGrid->tree().beginLeaf();
CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size()));
CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]);
CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]);
CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]);
++leafIter;
CPPUNIT_ASSERT_EQUAL(3, int(leafIter->mReadPasses.size()));
CPPUNIT_ASSERT_EQUAL(0, leafIter->mReadPasses[0]);
CPPUNIT_ASSERT_EQUAL(1, leafIter->mReadPasses[1]);
CPPUNIT_ASSERT_EQUAL(2, leafIter->mReadPasses[2]);
}
}
////////////////////////////////////////
void
TestFile::testHasGrid()
{
using namespace openvdb;
using namespace openvdb::io;
using FloatGrid = openvdb::FloatGrid;
using IntGrid = openvdb::Int32Grid;
using FloatTree = FloatGrid::TreeType;
using IntTree = Int32Grid::TreeType;
// Create a vdb to write.
// Create grids
IntGrid::Ptr grid = createGrid<IntGrid>(/*bg=*/1);
IntTree& tree = grid->tree();
grid->setName("density");
FloatGrid::Ptr grid2 = createGrid<FloatGrid>(/*bg=*/2.0);
FloatTree& tree2 = grid2->tree();
grid2->setName("temperature");
// Create transforms
math::Transform::Ptr trans = math::Transform::createLinearTransform(0.1);
math::Transform::Ptr trans2 = math::Transform::createLinearTransform(0.1);
grid->setTransform(trans);
grid2->setTransform(trans2);
// Set some values
tree.setValue(Coord(0, 0, 0), 5);
tree.setValue(Coord(100, 0, 0), 6);
tree2.setValue(Coord(0, 0, 0), 10);
tree2.setValue(Coord(0, 100, 0), 11);
MetaMap meta;
meta.insertMeta("author", StringMetadata("Einstein"));
meta.insertMeta("year", Int32Metadata(2009));
GridPtrVec grids;
grids.push_back(grid);
grids.push_back(grid2);
// Register grid and transform.
GridBase::clearRegistry();
IntGrid::registerGrid();
FloatGrid::registerGrid();
Metadata::clearRegistry();
StringMetadata::registerType();
Int32Metadata::registerType();
// register maps
math::MapRegistry::clear();
math::AffineMap::registerMap();
math::ScaleMap::registerMap();
math::UniformScaleMap::registerMap();
math::TranslationMap::registerMap();
math::ScaleTranslateMap::registerMap();
math::UniformScaleTranslateMap::registerMap();
math::NonlinearFrustumMap::registerMap();
// Write the vdb out to a file.
io::File vdbfile("something.vdb2");
vdbfile.write(grids, meta);
io::File vdbfile2("something.vdb2");
CPPUNIT_ASSERT_THROW(vdbfile2.hasGrid("density"), openvdb::IoError);
vdbfile2.open();
CPPUNIT_ASSERT(vdbfile2.hasGrid("density"));
CPPUNIT_ASSERT(vdbfile2.hasGrid("temperature"));
CPPUNIT_ASSERT(!vdbfile2.hasGrid("Temperature"));
CPPUNIT_ASSERT(!vdbfile2.hasGrid("densitY"));
// Clear registries.
GridBase::clearRegistry();
Metadata::clearRegistry();
math::MapRegistry::clear();
vdbfile2.close();
remove("something.vdb2");
}
void
TestFile::testNameIterator()
{
using namespace openvdb;
using namespace openvdb::io;
using FloatGrid = openvdb::FloatGrid;
using FloatTree = FloatGrid::TreeType;
using IntTree = Int32Grid::TreeType;
// Create trees.
IntTree::Ptr itree(new IntTree(1));
itree->setValue(Coord(0, 0, 0), 5);
itree->setValue(Coord(100, 0, 0), 6);
FloatTree::Ptr ftree(new FloatTree(2.0));
ftree->setValue(Coord(0, 0, 0), 10.0);
ftree->setValue(Coord(0, 100, 0), 11.0);
// Create grids.
GridPtrVec grids;
GridBase::Ptr grid = createGrid(itree);
grid->setName("density");
grids.push_back(grid);
grid = createGrid(ftree);
grid->setName("temperature");
grids.push_back(grid);
// Create two unnamed grids.
grids.push_back(createGrid(ftree));
grids.push_back(createGrid(ftree));
// Create two grids with the same name.
grid = createGrid(ftree);
grid->setName("level_set");
grids.push_back(grid);
grid = createGrid(ftree);
grid->setName("level_set");
grids.push_back(grid);
// Register types.
openvdb::initialize();
const char* filename = "testNameIterator.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
// Write the grids out to a file.
{
io::File vdbfile(filename);
vdbfile.write(grids);
}
io::File vdbfile(filename);
// Verify that name iteration fails if the file is not open.
CPPUNIT_ASSERT_THROW(vdbfile.beginName(), openvdb::IoError);
vdbfile.open();
// Names should appear in lexicographic order.
Name names[6] = { "[0]", "[1]", "density", "level_set[0]", "level_set[1]", "temperature" };
int count = 0;
for (io::File::NameIterator iter = vdbfile.beginName(); iter != vdbfile.endName(); ++iter) {
CPPUNIT_ASSERT_EQUAL(names[count], *iter);
CPPUNIT_ASSERT_EQUAL(names[count], iter.gridName());
++count;
grid = vdbfile.readGrid(*iter);
CPPUNIT_ASSERT(grid);
}
CPPUNIT_ASSERT_EQUAL(6, count);
vdbfile.close();
}
void
TestFile::testReadOldFileFormat()
{
/// @todo Save some old-format (prior to OPENVDB_FILE_VERSION) .vdb2 files
/// to /work/rd/fx_tools/vdb_unittest/TestFile::testReadOldFileFormat/
/// Verify that the files can still be read correctly.
}
void
TestFile::testCompression()
{
using namespace openvdb;
using namespace openvdb::io;
using IntGrid = openvdb::Int32Grid;
// Register types.
openvdb::initialize();
// Create reference grids.
IntGrid::Ptr intGrid = IntGrid::create(/*background=*/0);
intGrid->fill(CoordBBox(Coord(0), Coord(49)), /*value=*/999, /*active=*/true);
intGrid->fill(CoordBBox(Coord(6), Coord(43)), /*value=*/0, /*active=*/false);
intGrid->fill(CoordBBox(Coord(21), Coord(22)), /*value=*/1, /*active=*/false);
intGrid->fill(CoordBBox(Coord(23), Coord(24)), /*value=*/2, /*active=*/false);
CPPUNIT_ASSERT_EQUAL(8, int(IntGrid::TreeType::LeafNodeType::DIM));
FloatGrid::Ptr lsGrid = createLevelSet<FloatGrid>();
unittest_util::makeSphere(/*dim=*/Coord(100), /*ctr=*/Vec3f(50, 50, 50), /*r=*/20.0,
*lsGrid, unittest_util::SPHERE_SPARSE_NARROW_BAND);
CPPUNIT_ASSERT_EQUAL(int(GRID_LEVEL_SET), int(lsGrid->getGridClass()));
FloatGrid::Ptr fogGrid = lsGrid->deepCopy();
tools::sdfToFogVolume(*fogGrid);
CPPUNIT_ASSERT_EQUAL(int(GRID_FOG_VOLUME), int(fogGrid->getGridClass()));
GridPtrVec grids;
grids.push_back(intGrid);
grids.push_back(lsGrid);
grids.push_back(fogGrid);
const char* filename = "testCompression.vdb2";
SharedPtr<const char> scopedFile(filename, ::remove);
size_t uncompressedSize = 0;
{
// Write the grids out to a file with compression disabled.
io::File vdbfile(filename);
vdbfile.setCompression(io::COMPRESS_NONE);
vdbfile.write(grids);
vdbfile.close();
// Get the size of the file in bytes.
struct stat buf;
buf.st_size = 0;
CPPUNIT_ASSERT_EQUAL(0, ::stat(filename, &buf));
uncompressedSize = buf.st_size;
}
// Write the grids out with various combinations of compression options
// and verify that they can be read back successfully.
// See io/Compression.h for the flag values.
#ifdef OPENVDB_USE_BLOSC
std::vector<uint32_t> validFlags{0x0,0x1,0x2,0x3,0x4,0x6};
#else
std::vector<uint32_t> validFlags{0x0,0x1,0x2,0x3};
#endif
for (uint32_t flags : validFlags) {
if (flags != io::COMPRESS_NONE) {
io::File vdbfile(filename);
vdbfile.setCompression(flags);
vdbfile.write(grids);
vdbfile.close();
}
if (flags != io::COMPRESS_NONE) {
// Verify that the compressed file is significantly smaller than
// the uncompressed file.
size_t compressedSize = 0;
struct stat buf;
buf.st_size = 0;
CPPUNIT_ASSERT_EQUAL(0, ::stat(filename, &buf));
compressedSize = buf.st_size;
CPPUNIT_ASSERT(compressedSize < size_t(0.75 * double(uncompressedSize)));
}
{
// Verify that the grids can be read back successfully.
io::File vdbfile(filename);
vdbfile.open();
GridPtrVecPtr inGrids = vdbfile.getGrids();
CPPUNIT_ASSERT_EQUAL(3, int(inGrids->size()));
// Verify that the original and input grids are equal.
{
const IntGrid::Ptr grid = gridPtrCast<IntGrid>((*inGrids)[0]);
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(int(intGrid->getGridClass()), int(grid->getGridClass()));
CPPUNIT_ASSERT(grid->tree().hasSameTopology(intGrid->tree()));
CPPUNIT_ASSERT_EQUAL(
intGrid->tree().getValue(Coord(0)),
grid->tree().getValue(Coord(0)));
// Verify that leaf nodes with more than two distinct inactive values
// are handled correctly (FX-7085).
CPPUNIT_ASSERT_EQUAL(
intGrid->tree().getValue(Coord(6)),
grid->tree().getValue(Coord(6)));
CPPUNIT_ASSERT_EQUAL(
intGrid->tree().getValue(Coord(21)),
grid->tree().getValue(Coord(21)));
CPPUNIT_ASSERT_EQUAL(
intGrid->tree().getValue(Coord(23)),
grid->tree().getValue(Coord(23)));
// Verify that the only active value in this grid is 999.
Int32 minVal = -1, maxVal = -1;
grid->evalMinMax(minVal, maxVal);
CPPUNIT_ASSERT_EQUAL(999, minVal);
CPPUNIT_ASSERT_EQUAL(999, maxVal);
}
for (int idx = 1; idx <= 2; ++idx) {
const FloatGrid::Ptr
grid = gridPtrCast<FloatGrid>((*inGrids)[idx]),
refGrid = gridPtrCast<FloatGrid>(grids[idx]);
CPPUNIT_ASSERT(grid.get() != nullptr);
CPPUNIT_ASSERT_EQUAL(int(refGrid->getGridClass()), int(grid->getGridClass()));
CPPUNIT_ASSERT(grid->tree().hasSameTopology(refGrid->tree()));
FloatGrid::ConstAccessor refAcc = refGrid->getConstAccessor();
for (FloatGrid::ValueAllCIter it = grid->cbeginValueAll(); it; ++it) {
CPPUNIT_ASSERT_EQUAL(refAcc.getValue(it.getCoord()), *it);
}
}
}
}
}
////////////////////////////////////////
namespace {
using namespace openvdb;
struct TestAsyncHelper
{
std::set<io::Queue::Id> ids;
std::map<io::Queue::Id, std::string> filenames;
size_t refFileSize;
bool verbose;
TestAsyncHelper(size_t _refFileSize): refFileSize(_refFileSize), verbose(false) {}
~TestAsyncHelper()
{
// Remove output files.
for (std::map<io::Queue::Id, std::string>::iterator it = filenames.begin();
it != filenames.end(); ++it)
{
::remove(it->second.c_str());
}
filenames.clear();
ids.clear();
}
io::Queue::Notifier notifier()
{
return std::bind(&TestAsyncHelper::validate, this,
std::placeholders::_1, std::placeholders::_2);
}
void insert(io::Queue::Id id, const std::string& filename)
{
ids.insert(id);
filenames[id] = filename;
if (verbose) std::cerr << "queued " << filename << " as task " << id << "\n";
}
void validate(io::Queue::Id id, io::Queue::Status status)
{
if (verbose) {
std::ostringstream ostr;
ostr << "task " << id;
switch (status) {
case io::Queue::UNKNOWN: ostr << " is unknown"; break;
case io::Queue::PENDING: ostr << " is pending"; break;
case io::Queue::SUCCEEDED: ostr << " succeeded"; break;
case io::Queue::FAILED: ostr << " failed"; break;
}
std::cerr << ostr.str() << "\n";
}
if (status == io::Queue::SUCCEEDED) {
// If the task completed successfully, verify that the output file's
// size matches the reference file's size.
struct stat buf;
buf.st_size = 0;
CPPUNIT_ASSERT_EQUAL(0, ::stat(filenames[id].c_str(), &buf));
CPPUNIT_ASSERT_EQUAL(Index64(refFileSize), Index64(buf.st_size));
}
if (status == io::Queue::SUCCEEDED || status == io::Queue::FAILED) {
ids.erase(id);
}
}
}; // struct TestAsyncHelper
} // unnamed namespace
void
TestFile::testAsync()
{
using namespace openvdb;
// Register types.
openvdb::initialize();
// Create a grid.
FloatGrid::Ptr lsGrid = createLevelSet<FloatGrid>();
unittest_util::makeSphere(/*dim=*/Coord(100), /*ctr=*/Vec3f(50, 50, 50), /*r=*/20.0,
*lsGrid, unittest_util::SPHERE_SPARSE_NARROW_BAND);
MetaMap fileMetadata;
fileMetadata.insertMeta("author", StringMetadata("Einstein"));
fileMetadata.insertMeta("year", Int32Metadata(2013));
GridPtrVec grids;
grids.push_back(lsGrid);
grids.push_back(lsGrid->deepCopy());
grids.push_back(lsGrid->deepCopy());
size_t refFileSize = 0;
{
// Write a reference file without using asynchronous I/O.
const char* filename = "testAsyncref.vdb";
SharedPtr<const char> scopedFile(filename, ::remove);
io::File f(filename);
f.write(grids, fileMetadata);
// Record the size of the reference file.
struct stat buf;
buf.st_size = 0;
CPPUNIT_ASSERT_EQUAL(0, ::stat(filename, &buf));
refFileSize = buf.st_size;
}
{
// Output multiple files using asynchronous I/O.
// Use polling to get the status of the I/O tasks.
TestAsyncHelper helper(refFileSize);
io::Queue queue;
for (int i = 1; i < 10; ++i) {
std::ostringstream ostr;
ostr << "testAsync." << i << ".vdb";
const std::string filename = ostr.str();
io::Queue::Id id = queue.write(grids, io::File(filename), fileMetadata);
helper.insert(id, filename);
}
tbb::tick_count start = tbb::tick_count::now();
while (!helper.ids.empty()) {
if ((tbb::tick_count::now() - start).seconds() > 60) break; // time out after 1 minute
// Wait one second for tasks to complete.
tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.0/*sec*/));
// Poll each task in the pending map.
std::set<io::Queue::Id> ids = helper.ids; // iterate over a copy
for (std::set<io::Queue::Id>::iterator it = ids.begin(); it != ids.end(); ++it) {
const io::Queue::Id id = *it;
const io::Queue::Status status = queue.status(id);
helper.validate(id, status);
}
}
CPPUNIT_ASSERT(helper.ids.empty());
CPPUNIT_ASSERT(queue.empty());
}
{
// Output multiple files using asynchronous I/O.
// Use notifications to get the status of the I/O tasks.
TestAsyncHelper helper(refFileSize);
io::Queue queue(/*capacity=*/2);
queue.addNotifier(helper.notifier());
for (int i = 1; i < 10; ++i) {
std::ostringstream ostr;
ostr << "testAsync" << i << ".vdb";
const std::string filename = ostr.str();
io::Queue::Id id = queue.write(grids, io::File(filename), fileMetadata);
helper.insert(id, filename);
}
while (!queue.empty()) {
tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.0/*sec*/));
}
}
{
// Test queue timeout.
io::Queue queue(/*capacity=*/1);
queue.setTimeout(0/*sec*/);
SharedPtr<const char>
scopedFile1("testAsyncIOa.vdb", ::remove),
scopedFile2("testAsyncIOb.vdb", ::remove);
std::ofstream
file1(scopedFile1.get()),
file2(scopedFile2.get());
queue.write(grids, io::Stream(file1));
// With the queue length restricted to 1 and the timeout to 0 seconds,
// the next write() call should time out immediately with an exception.
// (It is possible, though highly unlikely, for the previous task to complete
// in time for this write() to actually succeed.)
CPPUNIT_ASSERT_THROW(queue.write(grids, io::Stream(file2)), openvdb::RuntimeError);
while (!queue.empty()) {
tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.0/*sec*/));
}
}
}
#ifdef OPENVDB_USE_BLOSC
// This tests for a data corruption bug that existed in versions of Blosc prior to 1.5.0
// (see https://github.com/Blosc/c-blosc/pull/63).
void
TestFile::testBlosc()
{
openvdb::initialize();
const unsigned char rawdata[] = {
0x93, 0xb0, 0x49, 0xaf, 0x62, 0xad, 0xe3, 0xaa, 0xe4, 0xa5, 0x43, 0x20, 0x24,
0x29, 0xc9, 0xaf, 0xee, 0xad, 0x0b, 0xac, 0x3d, 0xa8, 0x1f, 0x99, 0x53, 0x27,
0xb6, 0x2b, 0x16, 0xb0, 0x5f, 0xae, 0x89, 0xac, 0x51, 0xa9, 0xfc, 0xa1, 0xc9,
0x24, 0x59, 0x2a, 0x2f, 0x2d, 0xb4, 0xae, 0xeb, 0xac, 0x2f, 0xaa, 0xec, 0xa4,
0x53, 0x21, 0x31, 0x29, 0x8f, 0x2c, 0x8e, 0x2e, 0x31, 0xad, 0xd6, 0xaa, 0x6d,
0xa6, 0xad, 0x1b, 0x3e, 0x28, 0x0a, 0x2c, 0xfd, 0x2d, 0xf8, 0x2f, 0x45, 0xab,
0x81, 0xa7, 0x1f, 0x95, 0x02, 0x27, 0x3d, 0x2b, 0x85, 0x2d, 0x75, 0x2f, 0xb6,
0x30, 0x13, 0xa8, 0xb2, 0x9c, 0xf3, 0x25, 0x9c, 0x2a, 0x28, 0x2d, 0x0b, 0x2f,
0x7b, 0x30, 0x68, 0x9e, 0x51, 0x25, 0x31, 0x2a, 0xe6, 0x2c, 0xbc, 0x2e, 0x4e,
0x30, 0x5a, 0xb0, 0xe6, 0xae, 0x0e, 0xad, 0x59, 0xaa, 0x08, 0xa5, 0x89, 0x21,
0x59, 0x29, 0xb0, 0x2c, 0x57, 0xaf, 0x8c, 0xad, 0x6f, 0xab, 0x65, 0xa7, 0xd3,
0x12, 0xf5, 0x27, 0xeb, 0x2b, 0xf6, 0x2d, 0xee, 0xad, 0x27, 0xac, 0xab, 0xa8,
0xb1, 0x9f, 0xa2, 0x25, 0xaa, 0x2a, 0x4a, 0x2d, 0x47, 0x2f, 0x7b, 0xac, 0x6d,
0xa9, 0x45, 0xa3, 0x73, 0x23, 0x9d, 0x29, 0xb7, 0x2c, 0xa8, 0x2e, 0x51, 0x30,
0xf7, 0xa9, 0xec, 0xa4, 0x79, 0x20, 0xc5, 0x28, 0x3f, 0x2c, 0x24, 0x2e, 0x09,
0x30, 0xc8, 0xa5, 0xb1, 0x1c, 0x23, 0x28, 0xc3, 0x2b, 0xba, 0x2d, 0x9c, 0x2f,
0xc3, 0x30, 0x44, 0x18, 0x6e, 0x27, 0x3d, 0x2b, 0x6b, 0x2d, 0x40, 0x2f, 0x8f,
0x30, 0x02, 0x27, 0xed, 0x2a, 0x36, 0x2d, 0xfe, 0x2e, 0x68, 0x30, 0x66, 0xae,
0x9e, 0xac, 0x96, 0xa9, 0x7c, 0xa3, 0xa9, 0x23, 0xc5, 0x29, 0xd8, 0x2c, 0xd7,
0x2e, 0x0e, 0xad, 0x90, 0xaa, 0xe4, 0xa5, 0xf8, 0x1d, 0x82, 0x28, 0x2b, 0x2c,
0x1e, 0x2e, 0x0c, 0x30, 0x53, 0xab, 0x9c, 0xa7, 0xd4, 0x96, 0xe7, 0x26, 0x30,
0x2b, 0x7f, 0x2d, 0x6e, 0x2f, 0xb3, 0x30, 0x74, 0xa8, 0xb1, 0x9f, 0x36, 0x25,
0x3e, 0x2a, 0xfa, 0x2c, 0xdd, 0x2e, 0x65, 0x30, 0xfc, 0xa1, 0xe0, 0x23, 0x82,
0x29, 0x8f, 0x2c, 0x66, 0x2e, 0x23, 0x30, 0x2d, 0x22, 0xfb, 0x28, 0x3f, 0x2c,
0x0a, 0x2e, 0xde, 0x2f, 0xaa, 0x28, 0x0a, 0x2c, 0xc8, 0x2d, 0x8f, 0x2f, 0xb0,
0x30, 0xde, 0x2b, 0xa0, 0x2d, 0x5a, 0x2f, 0x8f, 0x30, 0x12, 0xac, 0x9d, 0xa8,
0x0f, 0xa0, 0x51, 0x25, 0x66, 0x2a, 0x1b, 0x2d, 0x0b, 0x2f, 0x82, 0x30, 0x7b,
0xa9, 0xea, 0xa3, 0x63, 0x22, 0x3f, 0x29, 0x7b, 0x2c, 0x60, 0x2e, 0x26, 0x30,
0x76, 0xa5, 0xf8, 0x1d, 0x4c, 0x28, 0xeb, 0x2b, 0xce, 0x2d, 0xb0, 0x2f, 0xd3,
0x12, 0x1d, 0x27, 0x15, 0x2b, 0x57, 0x2d, 0x2c, 0x2f, 0x85, 0x30, 0x0e, 0x26,
0x74, 0x2a, 0xfa, 0x2c, 0xc3, 0x2e, 0x4a, 0x30, 0x08, 0x2a, 0xb7, 0x2c, 0x74,
0x2e, 0x1d, 0x30, 0x8f, 0x2c, 0x3f, 0x2e, 0xf8, 0x2f, 0x24, 0x2e, 0xd0, 0x2f,
0xc3, 0x30, 0xdb, 0xa6, 0xd3, 0x0e, 0x38, 0x27, 0x3d, 0x2b, 0x78, 0x2d, 0x5a,
0x2f, 0xa3, 0x30, 0x68, 0x9e, 0x51, 0x25, 0x31, 0x2a, 0xe6, 0x2c, 0xbc, 0x2e,
0x4e, 0x30, 0xa9, 0x23, 0x59, 0x29, 0x6e, 0x2c, 0x38, 0x2e, 0x06, 0x30, 0xb8,
0x28, 0x10, 0x2c, 0xce, 0x2d, 0x95, 0x2f, 0xb3, 0x30, 0x9b, 0x2b, 0x7f, 0x2d,
0x39, 0x2f, 0x7f, 0x30, 0x4a, 0x2d, 0xf8, 0x2e, 0x58, 0x30, 0xd0, 0x2e, 0x3d,
0x30, 0x30, 0x30, 0x53, 0x21, 0xc5, 0x28, 0x24, 0x2c, 0xef, 0x2d, 0xc3, 0x2f,
0xda, 0x27, 0x58, 0x2b, 0x6b, 0x2d, 0x33, 0x2f, 0x82, 0x30, 0x9c, 0x2a, 0x00,
0x2d, 0xbc, 0x2e, 0x41, 0x30, 0xb0, 0x2c, 0x60, 0x2e, 0x0c, 0x30, 0x1e, 0x2e,
0xca, 0x2f, 0xc0, 0x30, 0x95, 0x2f, 0x9f, 0x30, 0x8c, 0x30, 0x23, 0x2a, 0xc4,
0x2c, 0x81, 0x2e, 0x23, 0x30, 0x5a, 0x2c, 0x0a, 0x2e, 0xc3, 0x2f, 0xc3, 0x30,
0xad, 0x2d, 0x5a, 0x2f, 0x88, 0x30, 0x0b, 0x2f, 0x5b, 0x30, 0x3a, 0x30, 0x7f,
0x2d, 0x2c, 0x2f, 0x72, 0x30, 0xc3, 0x2e, 0x37, 0x30, 0x09, 0x30, 0xb6, 0x30
};
const char* indata = reinterpret_cast<const char*>(rawdata);
size_t inbytes = sizeof(rawdata);
const int
compbufbytes = int(inbytes + BLOSC_MAX_OVERHEAD),
decompbufbytes = int(inbytes + BLOSC_MAX_OVERHEAD);
std::unique_ptr<char[]>
compresseddata(new char[compbufbytes]),
outdata(new char[decompbufbytes]);
for (int compcode = 0; compcode <= BLOSC_ZLIB; ++compcode) {
char* compname = nullptr;
#if BLOSC_VERSION_MAJOR > 1 || (BLOSC_VERSION_MAJOR == 1 && BLOSC_VERSION_MINOR >= 15)
if (0 > blosc_compcode_to_compname(compcode, const_cast<const char**>(&compname)))
#else
if (0 > blosc_compcode_to_compname(compcode, &compname))
#endif
continue;
/// @todo This changes the compressor setting globally.
if (blosc_set_compressor(compname) < 0) continue;
for (int typesize = 1; typesize <= 4; ++typesize) {
// Compress the data.
::memset(compresseddata.get(), 0, compbufbytes);
int compressedbytes = blosc_compress(
/*clevel=*/9,
/*doshuffle=*/true,
typesize,
/*srcsize=*/inbytes,
/*src=*/indata,
/*dest=*/compresseddata.get(),
/*destsize=*/compbufbytes);
CPPUNIT_ASSERT(compressedbytes > 0);
// Decompress the data.
::memset(outdata.get(), 0, decompbufbytes);
int outbytes = blosc_decompress(
compresseddata.get(), outdata.get(), decompbufbytes);
CPPUNIT_ASSERT(outbytes > 0);
CPPUNIT_ASSERT_EQUAL(int(inbytes), outbytes);
// Compare original and decompressed data.
int diff = 0;
for (size_t i = 0; i < inbytes; ++i) {
if (outdata[i] != indata[i]) ++diff;
}
if (diff > 0) {
const char* mesg = "Your version of the Blosc library is most likely"
" out of date; please install the latest version. "
"(Earlier versions have a bug that can cause data corruption.)";
CPPUNIT_ASSERT_MESSAGE(mesg, diff == 0);
return;
}
}
}
}
#endif
void
TestFile::testDelayedLoadMetadata()
{
openvdb::initialize();
using namespace openvdb;
io::File file("something.vdb2");
// Create a level set grid.
auto lsGrid = createLevelSet<FloatGrid>();
lsGrid->setName("sphere");
unittest_util::makeSphere(/*dim=*/Coord(100), /*ctr=*/Vec3f(50, 50, 50), /*r=*/20.0,
*lsGrid, unittest_util::SPHERE_SPARSE_NARROW_BAND);
// Write the VDB to a string stream.
std::ostringstream ostr(std::ios_base::binary);
// Create the grid descriptor out of this grid.
io::GridDescriptor gd(Name("sphere"), lsGrid->type());
// Write out the grid.
file.writeGrid(gd, lsGrid, ostr, /*seekable=*/true);
// Duplicate VDB string stream.
std::ostringstream ostr2(std::ios_base::binary);
{ // Read back in, clip and write out again to verify metadata is rebuilt.
std::istringstream istr(ostr.str(), std::ios_base::binary);
io::setVersion(istr, file.libraryVersion(), file.fileVersion());
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
const BBoxd clipBbox(Vec3d(-10.0,-10.0,-10.0), Vec3d(10.0,10.0,10.0));
io::Archive::readGrid(grid, gd2, istr, clipBbox);
// Verify clipping is working as expected.
CPPUNIT_ASSERT(grid->baseTreePtr()->leafCount() < lsGrid->tree().leafCount());
file.writeGrid(gd, grid, ostr2, /*seekable=*/true);
}
// Since the input is only a fragment of a VDB file (in particular,
// it doesn't have a header), set the file format version number explicitly.
// On read, the delayed load metadata for OpenVDB library versions less than 6.1
// should be removed to ensure correctness as it possible for the metadata to
// have been treated as unknown and blindly copied over when read and re-written
// using this library version resulting in out-of-sync metadata.
// By default, DelayedLoadMetadata is dropped from the grid during read so
// as not to be exposed to the user.
{ // read using current library version
std::istringstream istr(ostr2.str(), std::ios_base::binary);
io::setVersion(istr, file.libraryVersion(), file.fileVersion());
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
io::Archive::readGrid(grid, gd2, istr);
CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD]));
}
// To test the version mechanism, a stream metadata object is created with
// a non-zero test value and set on the input stream. This disables the
// behaviour where the DelayedLoadMetadata is dropped from the grid.
io::StreamMetadata::Ptr streamMetadata(new io::StreamMetadata);
streamMetadata->__setTest(uint32_t(1));
{ // read using current library version
std::istringstream istr(ostr2.str(), std::ios_base::binary);
io::setVersion(istr, file.libraryVersion(), file.fileVersion());
io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false);
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
io::Archive::readGrid(grid, gd2, istr);
CPPUNIT_ASSERT(((*grid)[GridBase::META_FILE_DELAYED_LOAD]));
}
{ // read using library version of 5.0
std::istringstream istr(ostr2.str(), std::ios_base::binary);
io::setVersion(istr, VersionId(5,0), file.fileVersion());
io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false);
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
io::Archive::readGrid(grid, gd2, istr);
CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD]));
}
{ // read using library version of 4.9
std::istringstream istr(ostr2.str(), std::ios_base::binary);
io::setVersion(istr, VersionId(4,9), file.fileVersion());
io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false);
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
io::Archive::readGrid(grid, gd2, istr);
CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD]));
}
{ // read using library version of 6.1
std::istringstream istr(ostr2.str(), std::ios_base::binary);
io::setVersion(istr, VersionId(6,1), file.fileVersion());
io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false);
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
io::Archive::readGrid(grid, gd2, istr);
CPPUNIT_ASSERT(!((*grid)[GridBase::META_FILE_DELAYED_LOAD]));
}
{ // read using library version of 6.2
std::istringstream istr(ostr2.str(), std::ios_base::binary);
io::setVersion(istr, VersionId(6,2), file.fileVersion());
io::setStreamMetadataPtr(istr, streamMetadata, /*transfer=*/false);
io::GridDescriptor gd2;
GridBase::Ptr grid = gd2.read(istr);
gd2.seekToGrid(istr);
io::Archive::readGrid(grid, gd2, istr);
CPPUNIT_ASSERT(((*grid)[GridBase::META_FILE_DELAYED_LOAD]));
}
remove("something.vdb2");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment