Skip to content

Commit 786d025

Browse files
committed
Add PointIndexCodec
Signed-off-by: Dan Bailey <danbailey@ilm.com>
1 parent 2a54d6d commit 786d025

5 files changed

Lines changed: 308 additions & 0 deletions

File tree

openvdb/openvdb/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ set(OPENVDB_LIBRARY_INCLUDE_FILES
367367

368368
set(OPENVDB_LIBRARY_CODECS_INCLUDE_FILES
369369
codecs/BoolCodec.h
370+
codecs/PointIndexCodec.h
370371
codecs/ScalarCodec.h
371372
codecs/TopologyCodec.h
372373
codecs/ValueMaskCodec.h
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright Contributors to the OpenVDB Project
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#ifndef OPENVDB_IO_CODECS_POINTINDEXCODEC_HAS_BEEN_INCLUDED
5+
#define OPENVDB_IO_CODECS_POINTINDEXCODEC_HAS_BEEN_INCLUDED
6+
7+
#include <openvdb/io/Codec.h>
8+
9+
#include <openvdb/tools/PointIndexGrid.h>
10+
11+
#include "ScalarLeafCodec.h"
12+
#include "TopologyCodec.h"
13+
14+
namespace openvdb {
15+
OPENVDB_USE_VERSION_NAMESPACE
16+
namespace OPENVDB_VERSION_NAME {
17+
namespace codecs {
18+
namespace internal {
19+
20+
template <typename GridT>
21+
struct ReadPointIndexBuffersOp
22+
{
23+
using TreeT = typename GridT::TreeType;
24+
using RootT = typename TreeT::RootNodeType;
25+
using LeafT = typename TreeT::LeafNodeType;
26+
using ValueT = typename TreeT::ValueType;
27+
28+
ReadPointIndexBuffersOp(std::istream& _is, bool _saveFloatAsHalf,
29+
const ValueT& _background)
30+
: is(_is)
31+
, saveFloatAsHalf(_saveFloatAsHalf)
32+
, background(_background) { }
33+
34+
template <typename NodeT>
35+
void operator()(NodeT&, size_t) { }
36+
37+
void operator()(LeafT& leaf, size_t)
38+
{
39+
using BaseLeaf = typename LeafT::BaseLeaf;
40+
41+
// Read the value mask and voxel data via base class
42+
BaseLeaf& baseLeaf = static_cast<BaseLeaf&>(leaf);
43+
readScalarLeafBuffers(baseLeaf, is, saveFloatAsHalf, background, /*skip=*/false, /*clipBBox=*/nullptr);
44+
45+
// Read the number of indices.
46+
Index64 numIndices = Index64(0);
47+
is.read(reinterpret_cast<char*>(&numIndices), sizeof(Index64));
48+
49+
// Read the indices data.
50+
leaf.indices().resize(size_t(numIndices));
51+
is.read(reinterpret_cast<char*>(leaf.indices().data()), numIndices * sizeof(ValueT));
52+
53+
// Reserved for future use.
54+
Index64 auxDataBytes = Index64(0);
55+
is.read(reinterpret_cast<char*>(&auxDataBytes), sizeof(Index64));
56+
if (auxDataBytes > 0) {
57+
// For now, read and discard any auxiliary data.
58+
std::unique_ptr<char[]> auxData{new char[auxDataBytes]};
59+
is.read(auxData.get(), auxDataBytes);
60+
}
61+
}
62+
63+
std::istream& is;
64+
const bool saveFloatAsHalf;
65+
const ValueT& background;
66+
}; // struct ReadPointIndexBuffersOp
67+
68+
template <typename GridT>
69+
struct WritePointIndexBuffersOp
70+
{
71+
using TreeT = typename GridT::TreeType;
72+
using LeafT = typename TreeT::LeafNodeType;
73+
using ValueT = typename TreeT::ValueType;
74+
75+
WritePointIndexBuffersOp(std::ostream& _os, bool _saveFloatAsHalf)
76+
: os(_os)
77+
, saveFloatAsHalf(_saveFloatAsHalf) { }
78+
79+
template <typename NodeT>
80+
void operator()(const NodeT&, size_t) { }
81+
82+
void operator()(const LeafT& leaf, size_t)
83+
{
84+
using BaseLeaf = typename LeafT::BaseLeaf;
85+
86+
// Write out the value mask and voxel values via base class
87+
const BaseLeaf& baseLeaf = static_cast<const BaseLeaf&>(leaf);
88+
writeScalarLeafBuffers(baseLeaf, os, saveFloatAsHalf);
89+
90+
// Write the number of indices.
91+
Index64 numIndices = Index64(leaf.indices().size());
92+
os.write(reinterpret_cast<const char*>(&numIndices), sizeof(Index64));
93+
94+
// Write the indices data.
95+
os.write(reinterpret_cast<const char*>(leaf.indices().data()), numIndices * sizeof(ValueT));
96+
97+
// Reserved for future use.
98+
const Index64 auxDataBytes = Index64(0);
99+
os.write(reinterpret_cast<const char*>(&auxDataBytes), sizeof(Index64));
100+
}
101+
102+
std::ostream& os;
103+
const bool saveFloatAsHalf;
104+
}; // struct WritePointIndexBuffersOp
105+
106+
} // namespace internal
107+
108+
template <typename GridT>
109+
struct PointIndexCodec : public TopologyCodec<GridT>
110+
{
111+
using Ptr = std::unique_ptr<PointIndexCodec<GridT>>;
112+
113+
~PointIndexCodec() noexcept = default;
114+
115+
static inline std::string name() { return GridT::gridType(); }
116+
117+
void readBuffers(std::istream& is, io::CodecData& data, const io::ReadOptions& options, io::ReadDiagnostics& diagnostics) final
118+
{
119+
GridT& grid = static_cast<GridT&>(*data.grid);
120+
121+
if (grid.hasMultiPassIO()) {
122+
OPENVDB_THROW(IoError, "Multi-pass IO is not supported in PointIndexCodec");
123+
}
124+
125+
io::checkFormatVersion(is);
126+
127+
bool saveFloatAsHalf = grid.saveFloatAsHalf();
128+
129+
auto& tree = grid.tree();
130+
tree.clearAllAccessors();
131+
132+
if (options.clipBBox.isSorted()) {
133+
diagnostics.addWarning(grid.getName(), "bounding box clipping is not supported for PointIndexGrids");
134+
}
135+
136+
internal::ReadPointIndexBuffersOp<GridT> readBuffersOp(is, saveFloatAsHalf, tree.background());
137+
tools::visitNodesDepthFirst(grid.tree(), readBuffersOp, /*idx=*/0, /*topDown=*/false);
138+
}
139+
140+
void writeBuffers(std::ostream& os, const GridBase& gridBase, const io::WriteOptions&) final
141+
{
142+
const GridT& grid = static_cast<const GridT&>(gridBase);
143+
144+
if (grid.hasMultiPassIO()) {
145+
OPENVDB_THROW(IoError, "Multi-pass IO is not supported in PointIndexCodec");
146+
}
147+
148+
internal::WritePointIndexBuffersOp<GridT> writeBuffersOp(os, grid.saveFloatAsHalf());
149+
tools::visitNodesDepthFirst(grid.tree(), writeBuffersOp);
150+
}
151+
}; // struct PointIndexCodec
152+
153+
} // namespace codecs
154+
} // namespace OPENVDB_VERSION_NAME
155+
} // namespace openvdb
156+
157+
#endif // OPENVDB_IO_CODECS_POINTINDEXCODEC_HAS_BEEN_INCLUDED

openvdb/openvdb/io/Codec.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <openvdb/codecs/ScalarCodec.h>
77
#include <openvdb/codecs/BoolCodec.h>
8+
#include <openvdb/codecs/PointIndexCodec.h>
89
#include <openvdb/codecs/ValueMaskCodec.h>
910

1011
#include <openvdb/openvdb.h>
@@ -58,6 +59,7 @@ void initialize()
5859

5960
CodecRegistry::registerCodec<codecs::BoolCodec<BoolGrid>>();
6061
CodecRegistry::registerCodec<codecs::ValueMaskCodec<MaskGrid>>();
62+
CodecRegistry::registerCodec<codecs::PointIndexCodec<tools::PointIndexGrid>>();
6163

6264
// register the plugin that converts from scalar to mask/bool
6365
NumericGridTypes::foreach<RegisterConvertCodec>();

openvdb/openvdb/unittest/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ else()
146146
TestParticlesToLevelSet.cc
147147
TestPointAdvect.cc
148148
TestPointAttribute.cc
149+
TestPointCodec.cc
149150
TestPointConversion.cc
150151
TestPointCount.cc
151152
TestPointDataLeaf.cc
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright Contributors to the OpenVDB Project
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
#include <openvdb/io/Codec.h>
5+
#include <openvdb/io/File.h>
6+
#include <openvdb/openvdb.h>
7+
#include <openvdb/tools/PointIndexGrid.h>
8+
#include <gtest/gtest.h>
9+
#include "util.h" // for unittest_util::genPoints
10+
11+
namespace {
12+
13+
class PointList {
14+
public:
15+
using PosType = openvdb::Vec3R;
16+
PointList(const std::vector<PosType>& points) : mPoints(&points) {}
17+
size_t size() const { return mPoints->size(); }
18+
void getPos(size_t n, PosType& xyz) const { xyz = (*mPoints)[n]; }
19+
private:
20+
std::vector<PosType> const * const mPoints;
21+
};
22+
23+
} // namespace
24+
25+
class TestPointCodec: public ::testing::Test
26+
{
27+
};
28+
29+
TEST_F(TestPointCodec, testPointIndexCodecIO)
30+
{
31+
using namespace openvdb;
32+
using namespace openvdb::io;
33+
using PointIndexGrid = tools::PointIndexGrid;
34+
35+
openvdb::initialize();
36+
CodecRegistry::clear();
37+
38+
// Generate points on a unit sphere and build a PointIndexGrid
39+
std::vector<Vec3R> points;
40+
unittest_util::genPoints(100, points);
41+
PointList pointList(points);
42+
43+
const double voxelSize = 0.1;
44+
math::Transform::Ptr transform = math::Transform::createLinearTransform(voxelSize);
45+
46+
PointIndexGrid::Ptr srcGrid =
47+
tools::createPointIndexGrid<PointIndexGrid>(pointList, *transform);
48+
srcGrid->setName("point_index_grid");
49+
50+
const std::string rawPath = "testPointIndexCodec_raw.vdb";
51+
52+
// Phase 1: write/read without codec
53+
{
54+
io::File f(rawPath);
55+
f.write(GridPtrVec{srcGrid});
56+
}
57+
58+
PointIndexGrid::Ptr rawGrid;
59+
{
60+
io::File f(rawPath);
61+
f.open();
62+
rawGrid = gridPtrCast<PointIndexGrid>(f.readGrid("point_index_grid"));
63+
f.close();
64+
}
65+
ASSERT_TRUE(rawGrid);
66+
67+
PointIndexGrid::Ptr rawTopo;
68+
{
69+
io::File f(rawPath);
70+
f.open();
71+
GridBase::Ptr base;
72+
EXPECT_NO_THROW(base = f.readGrid("point_index_grid"));
73+
rawTopo = gridPtrCast<PointIndexGrid>(base);
74+
f.close();
75+
}
76+
ASSERT_TRUE(rawTopo);
77+
EXPECT_EQ(rawTopo->activeVoxelCount(), Index64(97));
78+
EXPECT_EQ(rawTopo->getName(), std::string("point_index_grid"));
79+
80+
std::remove(rawPath.c_str());
81+
82+
const std::string codecPath = "testPointIndexCodec_codec.vdb";
83+
84+
// Phase 2: register codec, write/read with codec
85+
io::internal::initialize();
86+
ASSERT_TRUE(CodecRegistry::isRegistered(PointIndexGrid::gridType()));
87+
88+
{
89+
io::File f(codecPath);
90+
f.write(GridPtrVec{srcGrid});
91+
}
92+
93+
PointIndexGrid::Ptr codecGrid;
94+
{
95+
io::File f(codecPath);
96+
f.open();
97+
codecGrid = gridPtrCast<PointIndexGrid>(f.readGrid("point_index_grid"));
98+
f.close();
99+
}
100+
ASSERT_TRUE(codecGrid);
101+
102+
// Phase 3: full read comparison
103+
EXPECT_TRUE(srcGrid->tree().hasSameTopology(srcGrid->tree()));
104+
EXPECT_TRUE(srcGrid->tree().hasSameTopology(codecGrid->tree()));
105+
{
106+
auto codecAcc = codecGrid->getConstAccessor();
107+
for (PointIndexGrid::ValueOnCIter it = srcGrid->cbeginValueOn(); it; ++it) {
108+
EXPECT_EQ(*it, codecAcc.getValue(it.getCoord()));
109+
}
110+
}
111+
112+
// Compare leaf indices arrays
113+
{
114+
auto srcLeafIt = srcGrid->tree().cbeginLeaf();
115+
auto codecLeafIt = codecGrid->tree().cbeginLeaf();
116+
for (; srcLeafIt; ++srcLeafIt, ++codecLeafIt) {
117+
ASSERT_TRUE(codecLeafIt);
118+
EXPECT_EQ(srcLeafIt->indices().size(), codecLeafIt->indices().size());
119+
for (size_t i = 0; i < srcLeafIt->indices().size(); ++i) {
120+
EXPECT_EQ(srcLeafIt->indices()[i], codecLeafIt->indices()[i]);
121+
}
122+
}
123+
EXPECT_TRUE(!codecLeafIt);
124+
}
125+
126+
// Phase 4: TopologyOnly read
127+
ReadOptions topoOpts;
128+
topoOpts.readMode = ReadMode::TopologyOnly;
129+
130+
PointIndexGrid::Ptr codecTopo;
131+
{
132+
io::File f(codecPath);
133+
f.open();
134+
GridBase::Ptr base;
135+
EXPECT_NO_THROW(base = f.readGrid("point_index_grid", topoOpts));
136+
codecTopo = gridPtrCast<PointIndexGrid>(base);
137+
f.close();
138+
}
139+
ASSERT_TRUE(codecTopo);
140+
EXPECT_EQ(codecTopo->activeVoxelCount(), Index64(0));
141+
EXPECT_TRUE(codecTopo->tree().leafCount() == 0);
142+
EXPECT_EQ(codecTopo->getName(), std::string("point_index_grid"));
143+
144+
// Cleanup
145+
CodecRegistry::clear();
146+
std::remove(codecPath.c_str());
147+
}

0 commit comments

Comments
 (0)