Skip to content

Commit 679d57c

Browse files
committed
Add support for implicit conversion - float to half and scalar to bool/mask
Signed-off-by: Dan Bailey <danbailey@ilm.com>
1 parent eb1d31f commit 679d57c

4 files changed

Lines changed: 180 additions & 2 deletions

File tree

openvdb/openvdb/io/Archive.cc

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -885,10 +885,24 @@ Archive::readGridCount(std::istream& is)
885885

886886

887887
io::Codec*
888-
Archive::findCodec(const std::string& gridType, const io::ReadOptions&)
888+
Archive::findCodec(const std::string& gridType, const io::ReadOptions& options)
889889
{
890890
// Determine the I/O codec to use to read this grid
891-
return io::CodecRegistry::get(gridType);
891+
auto codec = io::CodecRegistry::get(gridType);
892+
893+
// if readMode is Half, then search for a codec that converts
894+
// from the storage grid type to the grid type
895+
if (options.readMode == ReadMode::Half) {
896+
codec = io::CodecRegistry::get(gridType + "_to_half");
897+
}
898+
if (options.readMode == ReadMode::Bool) {
899+
codec = io::CodecRegistry::get(gridType + "_to_bool");
900+
}
901+
if (options.readMode == ReadMode::Mask) {
902+
codec = io::CodecRegistry::get(gridType + "_to_mask");
903+
}
904+
905+
return codec;
892906
}
893907

894908

openvdb/openvdb/io/Codec.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,28 @@ namespace internal {
4242
template <typename GridT>
4343
struct RegisterCodec { inline void operator()() { CodecRegistry::registerCodec<codecs::ScalarCodec<GridT>>(); } };
4444

45+
template <typename GridT>
46+
struct RegisterConvertCodec {
47+
inline void operator()()
48+
{
49+
CodecRegistry::registerCodec<codecs::ScalarCodec<MaskGrid, GridT, CodecMode::ReadOnly>>();
50+
CodecRegistry::registerCodec<codecs::ScalarCodec<BoolGrid, GridT, CodecMode::ReadOnly>>();
51+
}
52+
}; // struct RegisterConvertCodec
53+
4554
void initialize()
4655
{
4756
NumericGridTypes::foreach<RegisterCodec>();
4857
Vec3GridTypes::foreach<RegisterCodec>();
4958

5059
CodecRegistry::registerCodec<codecs::BoolCodec<BoolGrid>>();
5160
CodecRegistry::registerCodec<codecs::ValueMaskCodec<MaskGrid>>();
61+
62+
// register the plugin that converts from scalar to mask/bool
63+
NumericGridTypes::foreach<RegisterConvertCodec>();
64+
65+
// register the plugin that converts from float to half
66+
CodecRegistry::registerCodec<codecs::ScalarCodec<HalfGrid, FloatGrid, CodecMode::ReadOnly>>();
5267
}
5368

5469
void uninitialize()

openvdb/openvdb/io/Codec.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ enum class CodecMode {
3838

3939
enum class ReadMode {
4040
Original, // Read data as-is (default)
41+
Half, // Read data as half precision
42+
Bool, // Read data as boolean
43+
Mask, // Read data as mask
4144
TopologyOnly // Read topology only, no buffer data
4245
};
4346

openvdb/openvdb/unittest/TestCodec.cc

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <openvdb/io/Codec.h>
55
#include <openvdb/io/File.h>
66
#include <openvdb/openvdb.h>
7+
#include <openvdb/codecs/ScalarCodec.h>
78
#include <openvdb/Exceptions.h>
89
#include <openvdb/tools/Clip.h>
910
#include <gtest/gtest.h>
@@ -257,3 +258,148 @@ TEST_F(TestCodec, testVec3SCodecIO) { testCodecIOImpl<openvdb::Vec3SGrid>("vec3s
257258
TEST_F(TestCodec, testVec3DCodecIO) { testCodecIOImpl<openvdb::Vec3DGrid>("vec3d_grid", openvdb::Vec3d(0.0), openvdb::Vec3d(1.0, 2.0, 3.0)); }
258259
TEST_F(TestCodec, testBoolCodecIO) { testCodecIOImpl<openvdb::BoolGrid>("bool_grid", false, true); }
259260
TEST_F(TestCodec, testMaskCodecIO) { testCodecIOImpl<openvdb::MaskGrid>("mask_grid", false, true); }
261+
262+
TEST_F(TestCodec, testFloatToHalfCodecConversion)
263+
{
264+
using namespace openvdb;
265+
using namespace openvdb::io;
266+
267+
openvdb::initialize();
268+
CodecRegistry::clear();
269+
270+
// Verify the conversion codec name
271+
const std::string expectedName = FloatGrid::gridType() + "_to_half";
272+
EXPECT_EQ((codecs::ScalarCodec<HalfGrid, FloatGrid, CodecMode::ReadOnly>::name()),
273+
expectedName);
274+
275+
// Verify the codec is registered after initialize()
276+
io::internal::initialize();
277+
EXPECT_TRUE(CodecRegistry::isRegistered(expectedName));
278+
279+
// Write a FloatGrid with a known fill value (1.5f is exactly representable in half)
280+
const std::string floatPath = "test_float_to_half.vdb";
281+
const std::string gridName = "float_grid";
282+
FloatGrid::Ptr srcGrid = FloatGrid::create(0.0f);
283+
srcGrid->setName(gridName);
284+
srcGrid->fill(CoordBBox(Coord(-5), Coord(5)), 1.5f, true);
285+
286+
{
287+
io::File f(floatPath);
288+
f.write(GridPtrVec{srcGrid});
289+
}
290+
291+
// Read back with ReadMode::Half — triggers the float-to-half conversion codec
292+
ReadOptions halfOpts;
293+
halfOpts.readMode = ReadMode::Half;
294+
295+
GridBase::Ptr base;
296+
{
297+
io::File f(floatPath);
298+
f.open();
299+
base = f.readGrid(gridName, halfOpts);
300+
f.close();
301+
}
302+
ASSERT_TRUE(base);
303+
304+
// The returned grid must be a HalfGrid
305+
EXPECT_TRUE(base->isType<HalfGrid>());
306+
HalfGrid::Ptr halfGrid = gridPtrCast<HalfGrid>(base);
307+
ASSERT_TRUE(halfGrid);
308+
309+
// Topology must match the source FloatGrid
310+
EXPECT_TRUE(srcGrid->tree().hasSameTopology(halfGrid->tree()));
311+
312+
// All active voxel values must equal Half(1.5f)
313+
for (HalfGrid::ValueOnCIter it = halfGrid->cbeginValueOn(); it; ++it) {
314+
EXPECT_EQ(*it, Half(1.5f));
315+
}
316+
317+
// Background value must equal Half(0.0f)
318+
EXPECT_EQ(halfGrid->background(), Half(0.0f));
319+
320+
// Cleanup
321+
std::remove(floatPath.c_str());
322+
}
323+
324+
template <typename SrcGridT, typename DstGridT, openvdb::io::ReadMode mode>
325+
void testConvertCodecImpl()
326+
{
327+
using namespace openvdb;
328+
using namespace openvdb::io;
329+
330+
openvdb::io::CodecRegistry::clear();
331+
openvdb::io::internal::initialize();
332+
333+
// Verify the conversion codec name
334+
const std::string expectedName =
335+
SrcGridT::gridType() + "_to_" + typeNameAsString<typename DstGridT::BuildType>();
336+
EXPECT_EQ((codecs::ScalarCodec<DstGridT, SrcGridT, CodecMode::ReadOnly>::name()),
337+
expectedName);
338+
339+
// Verify the codec is registered after initialize()
340+
EXPECT_TRUE(CodecRegistry::isRegistered(expectedName));
341+
342+
// Write a SrcGridT with a non-zero fill value
343+
const std::string testPath = "test_" + expectedName + ".vdb";
344+
const std::string gridName = SrcGridT::gridType();
345+
typename SrcGridT::Ptr srcGrid =
346+
SrcGridT::create(typename SrcGridT::ValueType(0));
347+
srcGrid->setName(gridName);
348+
srcGrid->fill(CoordBBox(Coord(-5), Coord(5)),
349+
typename SrcGridT::ValueType(1), true);
350+
351+
{
352+
io::File f(testPath);
353+
f.write(GridPtrVec{srcGrid});
354+
}
355+
356+
// Read back with the specified ReadMode — triggers the conversion codec
357+
ReadOptions opts;
358+
opts.readMode = mode;
359+
360+
GridBase::Ptr base;
361+
{
362+
io::File f(testPath);
363+
f.open();
364+
base = f.readGrid(gridName, opts);
365+
f.close();
366+
}
367+
ASSERT_TRUE(base);
368+
369+
// The returned grid must be a DstGridT
370+
EXPECT_TRUE(base->isType<DstGridT>());
371+
typename DstGridT::Ptr dstGrid = gridPtrCast<DstGridT>(base);
372+
ASSERT_TRUE(dstGrid);
373+
374+
// Topology must match the source grid
375+
EXPECT_TRUE(srcGrid->tree().hasSameTopology(dstGrid->tree()));
376+
377+
// All active voxel values must be true
378+
for (typename DstGridT::ValueOnCIter it = dstGrid->cbeginValueOn(); it; ++it) {
379+
EXPECT_EQ(*it, typename DstGridT::ValueType(true));
380+
}
381+
382+
// Background value must be false
383+
EXPECT_EQ(dstGrid->background(), typename DstGridT::ValueType(false));
384+
385+
// Cleanup
386+
std::remove(testPath.c_str());
387+
}
388+
389+
TEST_F(TestCodec, testNumericToBoolCodecConversion)
390+
{
391+
testConvertCodecImpl<openvdb::FloatGrid, openvdb::BoolGrid, openvdb::io::ReadMode::Bool>();
392+
testConvertCodecImpl<openvdb::DoubleGrid, openvdb::BoolGrid, openvdb::io::ReadMode::Bool>();
393+
testConvertCodecImpl<openvdb::Int32Grid, openvdb::BoolGrid, openvdb::io::ReadMode::Bool>();
394+
testConvertCodecImpl<openvdb::Int64Grid, openvdb::BoolGrid, openvdb::io::ReadMode::Bool>();
395+
testConvertCodecImpl<openvdb::HalfGrid, openvdb::BoolGrid, openvdb::io::ReadMode::Bool>();
396+
}
397+
398+
TEST_F(TestCodec, testNumericToMaskCodecConversion)
399+
{
400+
testConvertCodecImpl<openvdb::FloatGrid, openvdb::MaskGrid, openvdb::io::ReadMode::Mask>();
401+
testConvertCodecImpl<openvdb::DoubleGrid, openvdb::MaskGrid, openvdb::io::ReadMode::Mask>();
402+
testConvertCodecImpl<openvdb::Int32Grid, openvdb::MaskGrid, openvdb::io::ReadMode::Mask>();
403+
testConvertCodecImpl<openvdb::Int64Grid, openvdb::MaskGrid, openvdb::io::ReadMode::Mask>();
404+
testConvertCodecImpl<openvdb::HalfGrid, openvdb::MaskGrid, openvdb::io::ReadMode::Mask>();
405+
}

0 commit comments

Comments
 (0)