Skip to content
8 changes: 8 additions & 0 deletions openvdb/openvdb/Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,14 @@
#if defined __INTEL_COMPILER
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END
#elif defined __clang__
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wconversion\"") \
_Pragma("clang diagnostic ignored \"-Wfloat-conversion\"") \
_Pragma("clang diagnostic ignored \"-Wimplicit-float-conversion\"")
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_END \
_Pragma("clang diagnostic pop")
#elif defined __GNUC__
// -Wfloat-conversion was only introduced in GCC 4.9
#define OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN \
Expand Down
48 changes: 36 additions & 12 deletions openvdb/openvdb/codecs/TopologyCodec.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ struct ReadTopologyOp
using LeafT = typename TreeT::LeafNodeType;
using StorageValueT = typename StorageTreeT::ValueType;

// A ValueMask target records active state, not cast storage values: value
// must equal active state at every level, so the background and tile/node
// values are forced from activity instead of being converted from storage.
static constexpr bool isMaskTarget = std::is_same_v<typename TreeT::BuildType, ValueMask>;

ReadTopologyOp(std::istream& _is, bool _saveFloatAsHalf, io::ReadDiagnostics& _diagnostics,
const std::string& _gridName)
: is(_is)
Expand All @@ -140,7 +145,11 @@ struct ReadTopologyOp
// Read a RootNode that was stored in the current format.

is.read(reinterpret_cast<char*>(&storageBackground), sizeof(StorageValueT));
background = static_cast<ValueT>(storageBackground);
if constexpr (isMaskTarget) {
background = false;
} else {
background = static_cast<ValueT>(storageBackground);
}

Index numTiles = 0, numChildren = 0;
is.read(reinterpret_cast<char*>(&numTiles), sizeof(Index));
Expand All @@ -156,7 +165,9 @@ struct ReadTopologyOp
is.read(reinterpret_cast<char*>(&value), sizeof(StorageValueT));
is.read(reinterpret_cast<char*>(&active), sizeof(bool));
Coord origin(vec);
if constexpr (std::is_same_v<ValueT, StorageValueT>) {
if constexpr (isMaskTarget) {
root.addTile(origin, active, active);
} else if constexpr (std::is_same_v<ValueT, StorageValueT>) {
root.addTile(origin, value, active);
} else {
root.addTile(origin, static_cast<ValueT>(value), active);
Expand Down Expand Up @@ -194,20 +205,32 @@ struct ReadTopologyOp
StorageValueT* values = valuePtr.get();
io::readCompressedValues(is, values, numValues, valueMask, saveFloatAsHalf, &storageBackground);

// Copy values from the array into this node's table.
// Copy values from the array into this node's table. For a
// ValueMask target the decoded values array is only read to keep
// the stream position correct; the value comes from the value
// mask instead, so that value equals active state.
if (oldVersion) {
// The node's member child mask is still empty at this point
// (PartialCreate; setChildUnsafe runs below), so iterate the
// local childMask's off-bits to match the legacy ordering and
// avoid over-reading the countOff-sized values array.
Index n = 0;
for (auto iter = childMask.beginOff(); iter; ++iter) {
node.setValueOnlyUnsafe(iter.pos(), static_cast<ValueT>(values[n++]));
if constexpr (isMaskTarget) {
node.setValueOnlyUnsafe(iter.pos(), valueMask.isOn(iter.pos()));
} else {
node.setValueOnlyUnsafe(iter.pos(), static_cast<ValueT>(values[n]));
}
++n;
}
OPENVDB_ASSERT(n == numValues);
} else {
for (auto iter = node.beginValueAll(); iter; ++iter) {
node.setValueOnlyUnsafe(iter.pos(), static_cast<ValueT>(values[iter.pos()]));
if constexpr (isMaskTarget) {
node.setValueOnlyUnsafe(iter.pos(), valueMask.isOn(iter.pos()));
} else {
node.setValueOnlyUnsafe(iter.pos(), static_cast<ValueT>(values[iter.pos()]));
}
}
}
}
Expand Down Expand Up @@ -312,17 +335,18 @@ void topologyCodecReadTopology(GridBase& gridBase, std::istream& is, const io::R
internal::setTilesToBackground(grid.tree());
// allocate leaf buffers in parallel and fill with the background value;
// ReadTopologyOp uses PartialCreate which leaves buffers unallocated.
const auto background = grid.tree().root().background();
tree::LeafManager<typename GridT::TreeType> leafManager(grid.tree());
leafManager.foreach([&background](auto& leaf, size_t) {
using LeafType = std::decay_t<decltype(leaf)>;
if constexpr (!std::is_same_v<typename LeafType::ValueType, bool>) {
// (skip BoolGrids/MaskGrids whose buffers are bit masks that are always
// allocated and so provide no empty()/allocate())
if constexpr (!std::is_same_v<typename GridT::TreeType::ValueType, bool>) {
const auto background = grid.tree().root().background();
tree::LeafManager<typename GridT::TreeType> leafManager(grid.tree());
leafManager.foreach([&background](auto& leaf, size_t) {
if (leaf.buffer().empty()) {
leaf.buffer().allocate();
leaf.buffer().fill(background);
}
}
});
});
}
return;
}
}
Expand Down
18 changes: 12 additions & 6 deletions openvdb/openvdb/io/Codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,18 @@ enum class ReadMode {
/// then constructing a @c MaskGrid from the active-voxel set, but may
/// be performed more efficiently inside the codec.
Mask,
/// Deserialize topology only; value buffers are skipped. The resulting
/// grid has a valid tree structure (active/inactive state, node
/// hierarchy) and all leaf buffers are allocated and filled with the
/// grid's background value. Useful when only the active-voxel mask is
/// needed and avoiding the cost of reading large value buffers is
/// desirable.
/// Deserialize topology only; reading of value buffers may be skipped.
/// Useful when only the active-voxel mask is needed and avoiding the cost
/// of reading large value buffers is desirable.
///
/// @warning Only the topology is guaranteed. The resulting grid has a
/// valid tree structure (node hierarchy, tiles and active/inactive state),
/// but the voxel and tile values are unspecified. They are typically the
/// grid's background value, however a grid read from a file without grid
/// offsets is fully cached up-front, so the returned grid keeps the values
/// that were read. Deep-copying such a grid to reset its values would cost
/// more than the read this mode is meant to avoid. Callers must not rely
/// on the values being the background value.
TopologyOnly,
/// Deserialize grid metadata and transform only; no topology, no value
/// buffers. The codec is still used to construct the correct grid type,
Expand Down
Loading
Loading