diff --git a/openvdb/openvdb/CMakeLists.txt b/openvdb/openvdb/CMakeLists.txt index 9cc531cdf7..4c4c1bfcdd 100644 --- a/openvdb/openvdb/CMakeLists.txt +++ b/openvdb/openvdb/CMakeLists.txt @@ -403,6 +403,10 @@ set(OPENVDB_LIBRARY_IO_INCLUDE_FILES io/TempFile.h ) +set(OPENVDB_LIBRARY_ADAPTIVE_INCLUDE_FILES + adaptive/AdaptiveGrid.h +) + set(OPENVDB_LIBRARY_MATH_INCLUDE_FILES math/BBox.h math/ConjGradient.h @@ -741,6 +745,7 @@ endif() install(FILES ${OPENVDB_LIBRARY_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/openvdb/version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb) +install(FILES ${OPENVDB_LIBRARY_ADAPTIVE_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/adaptive) install(FILES ${OPENVDB_LIBRARY_IO_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/io) install(FILES ${OPENVDB_LIBRARY_MATH_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/math) install(FILES ${OPENVDB_LIBRARY_POINTS_INCLUDE_FILES} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/openvdb/points) diff --git a/openvdb/openvdb/Grid.h b/openvdb/openvdb/Grid.h index 14aad39122..bd123d2c0f 100644 --- a/openvdb/openvdb/Grid.h +++ b/openvdb/openvdb/Grid.h @@ -586,10 +586,10 @@ class Grid: public GridBase using ValueAllIter = typename _TreeType::ValueAllIter; using ValueAllCIter = typename _TreeType::ValueAllCIter; - using Accessor = typename tree::ValueAccessor<_TreeType, true>; - using ConstAccessor = typename tree::ValueAccessor; - using UnsafeAccessor = typename tree::ValueAccessor<_TreeType, false>; - using ConstUnsafeAccessor = typename tree::ValueAccessor; + using Accessor = typename _TreeType::Accessor; + using ConstAccessor = typename _TreeType::ConstAccessor; + using UnsafeAccessor = typename _TreeType::UnsafeAccessor; + using ConstUnsafeAccessor = typename _TreeType::ConstUnsafeAccessor; /// @brief ValueConverter::Type is the type of a grid having the same /// hierarchy as this grid but a different value type, T. @@ -729,7 +729,7 @@ class Grid: public GridBase /// @brief Return an accessor that provides random read and write access /// to this grid's voxels. /// @details The accessor is safe in the sense that it is registered with this grid's tree. - Accessor getAccessor() { return Accessor(tree()); } + Accessor getAccessor() { return mTree->getAccessor(); } /// @brief Return an unsafe accessor that provides random read and write access /// to this grid's voxels. /// @details The accessor is unsafe in the sense that it is not registered @@ -737,11 +737,11 @@ class Grid: public GridBase /// over a registered accessor, but it is unsafe if the tree topology is modified. /// @warning Only use this method if you're an expert and know the /// risks of using an unregistered accessor (see tree/ValueAccessor.h) - UnsafeAccessor getUnsafeAccessor() { return UnsafeAccessor(tree()); } + UnsafeAccessor getUnsafeAccessor() { return mTree->getUnsafeAccessor(); } /// Return an accessor that provides random read-only access to this grid's voxels. - ConstAccessor getAccessor() const { return ConstAccessor(tree()); } + ConstAccessor getAccessor() const { return mTree->getConstAccessor(); } /// Return an accessor that provides random read-only access to this grid's voxels. - ConstAccessor getConstAccessor() const { return ConstAccessor(tree()); } + ConstAccessor getConstAccessor() const { return mTree->getConstAccessor(); } /// @brief Return an unsafe accessor that provides random read-only access /// to this grid's voxels. /// @details The accessor is unsafe in the sense that it is not registered @@ -749,7 +749,7 @@ class Grid: public GridBase /// over a registered accessor, but it is unsafe if the tree topology is modified. /// @warning Only use this method if you're an expert and know the /// risks of using an unregistered accessor (see tree/ValueAccessor.h) - ConstUnsafeAccessor getConstUnsafeAccessor() const { return ConstUnsafeAccessor(tree()); } + ConstUnsafeAccessor getConstUnsafeAccessor() const { return mTree->getConstUnsafeAccessor(); } /// Return an iterator over all of this grid's active values (tile and voxel). ValueOnIter beginValueOn() { return tree().beginValueOn(); } diff --git a/openvdb/openvdb/Types.h b/openvdb/openvdb/Types.h index ad811e1520..94f9a1dccd 100644 --- a/openvdb/openvdb/Types.h +++ b/openvdb/openvdb/Types.h @@ -236,6 +236,30 @@ using make_index_sequence = //////////////////////////////////////// +/// @brief An enum to distinguish between different types of Tree representation. +enum class TreeRepresentation : uint32_t {Unknown = 0, Sparse = 1, Adaptive = 2, End = 3}; + +/// @brief A TreeTraits struct that can be used to query properties of an input T. +template +struct TreeTraits +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Unknown; +}; + +template +constexpr bool isSparseTree() +{ + return TreeTraits::Representation == TreeRepresentation::Sparse; +} + +template +constexpr bool isAdaptiveTree() +{ + return TreeTraits::Representation == TreeRepresentation::Adaptive; +} + +//////////////////////////////////////// + template::value || IsSpecializationOf::value || @@ -446,6 +470,26 @@ template struct CopyConstness); +/// +/// This results in the desired outcome of generating a compile-time static assert +/// only if the first clause evaluates to false. + +template +inline constexpr bool AlwaysFalseValue = false; + + //////////////////////////////////////// diff --git a/openvdb/openvdb/adaptive/AdaptiveGrid.h b/openvdb/openvdb/adaptive/AdaptiveGrid.h new file mode 100644 index 0000000000..66e7eb0d46 --- /dev/null +++ b/openvdb/openvdb/adaptive/AdaptiveGrid.h @@ -0,0 +1,316 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#ifndef OPENVDB_ADAPTIVE_ADAPTIVE_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_ADAPTIVE_ADAPTIVE_GRID_HAS_BEEN_INCLUDED + +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + + +//////////////////////////////////////// + + +namespace adaptive { + +template +class AdaptiveAccessor +{ +public: + using ValueType = typename TreeT::ValueType; + + explicit AdaptiveAccessor(TreeT& tree): + mTree(tree) { } + + TreeT& tree() const { return mTree; } + +private: + TreeT& mTree; +}; + + +template +class AdaptiveTree final: public TreeBase +{ +public: + using Ptr = SharedPtr; + using ConstPtr = SharedPtr; + + using ValueType = _ValueType; + using BuildType = _ValueType; + + static const Index DEPTH = 1; + + using Accessor = AdaptiveAccessor; + using ConstAccessor = AdaptiveAccessor; + using UnsafeAccessor = Accessor; + using ConstUnsafeAccessor = ConstAccessor; + + AdaptiveTree() = default; + + AdaptiveTree& operator=(const AdaptiveTree&) = delete; // disallow assignment + + /// Deep copy constructor + AdaptiveTree(const AdaptiveTree& other): TreeBase(other), mBackground(other.mBackground) { } + + /// Empty tree constructor + AdaptiveTree(const ValueType& background): mBackground(background) { } + + ~AdaptiveTree() override = default; + + /// Return the name of this type of tree. + static const Name& treeType(); + + /// Return the name of this tree's type. + const Name& type() const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). + Name valueType() const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// Return @c true if this tree is of the same type as the template parameter. + template + bool isType() const { return (this->type() == TreeType::treeType()); } + + /// Return a pointer to a deep copy of this tree + TreeBase::Ptr copy() const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Return this tree's background value. + const ValueType& background() const { return mBackground; } + + /// @brief Return @c true if this tree contains no nodes. + bool empty() const { return true; } + + /// Remove all nodes. + void clear() { } + + /// @brief Not implemented. + void prune(const ValueType& /*tolerance*/ = zeroVal()) { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Not implemented. + void clip(const CoordBBox&) { OPENVDB_THROW(NotImplementedError, ""); } + + // + // Tree methods + // + + /// @brief Return in @a bbox the axis-aligned bounding box of all + /// active tiles and leaf nodes with active values. + /// @details This is faster than calling evalActiveVoxelBoundingBox, + /// which visits the individual active voxels, and hence + /// evalLeafBoundingBox produces a less tight, i.e. approximate, bbox. + /// @return @c false if the bounding box is empty (in which case + /// the bbox is set to its default value). + bool evalLeafBoundingBox(CoordBBox& /*bbox*/) const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Return in @a dim the dimensions of the axis-aligned bounding box + /// of all leaf nodes. + /// @return @c false if the bounding box is empty. + bool evalLeafDim(Coord& /*dim*/) const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Return in @a bbox the axis-aligned bounding box of all + /// active voxels and tiles. + /// @details This method produces a more accurate, i.e. tighter, + /// bounding box than evalLeafBoundingBox which is approximate but + /// faster. + /// @return @c false if the bounding box is empty (in which case + /// the bbox is set to its default value). + bool evalActiveVoxelBoundingBox(CoordBBox& /*bbox*/) const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Return in @a dim the dimensions of the axis-aligned bounding box of all + /// active voxels. This is a tighter bounding box than the leaf node bounding box. + /// @return @c false if the bounding box is empty. + bool evalActiveVoxelDim(Coord& /*dim*/) const override { OPENVDB_THROW(NotImplementedError, ""); } + + void getIndexRange(CoordBBox& /*bbox*/) const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Replace with background tiles any nodes whose voxel buffers + /// have not yet been allocated. + /// @details Typically, unallocated nodes are leaf nodes whose voxel buffers + /// are not yet resident in memory because delayed loading is in effect. + /// @sa readNonresidentBuffers, io::File::open + void clipUnallocatedNodes() override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the total number of unallocated leaf nodes residing in this tree. + Index32 unallocatedLeafCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + + + // + // Statistics + // + /// @brief Return the depth of this tree. + /// + /// A tree with only a root node and leaf nodes has depth 2, for example. + Index treeDepth() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the number of leaf nodes. + Index32 leafCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return a vector with node counts. The number of nodes of type NodeType + /// is given as element NodeType::LEVEL in the return vector. Thus, the size + /// of this vector corresponds to the height (or depth) of this tree. + std::vector nodeCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the number of non-leaf nodes. + Index32 nonLeafCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the number of active voxels stored in leaf nodes. + Index64 activeLeafVoxelCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the number of inactive voxels stored in leaf nodes. + Index64 inactiveLeafVoxelCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the total number of active voxels. + Index64 activeVoxelCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the number of inactive voxels within the bounding box of all active voxels. + Index64 inactiveVoxelCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Return the total number of active tiles. + Index64 activeTileCount() const override { OPENVDB_THROW(NotImplementedError, ""); } + + // + // Accessor methods + // + Accessor getAccessor() { return Accessor(*this); } + UnsafeAccessor getUnsafeAccessor() { return UnsafeAccessor(*this); } + ConstAccessor getAccessor() const { return ConstAccessor(*this); } + ConstAccessor getConstAccessor() const { return ConstAccessor(*this); } + ConstUnsafeAccessor getConstUnsafeAccessor() const { return ConstUnsafeAccessor(*this); } + + // + // I/O methods + // + /// Read all data buffers for this tree. + void readBuffers(std::istream&, bool /*saveFloatAsHalf*/ = false) override { OPENVDB_THROW(NotImplementedError, ""); } + /// Read all of this tree's data buffers that intersect the given bounding box. + void readBuffers(std::istream&, const CoordBBox&, bool /*saveFloatAsHalf*/ = false) override { OPENVDB_THROW(NotImplementedError, ""); } + /// @brief Read all of this tree's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this tree was read from a memory-mapped file, this operation + /// disconnects the tree from the file. + /// @sa clipUnallocatedNodes, io::File::open, io::MappedFile + void readNonresidentBuffers() const override { OPENVDB_THROW(NotImplementedError, ""); } + /// Write out all the data buffers for this tree. + void writeBuffers(std::ostream&, bool /*saveFloatAsHalf*/ = false) const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Print statistics, memory usage and other information about this tree. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print tree configuration only; + /// 2: include node and voxel statistics; + /// 3: include memory usage; + /// 4: include minimum and maximum voxel values + /// @warning @a verboseLevel 4 forces loading of any unallocated nodes. + void print(std::ostream& /*os*/ = std::cout, int /*verboseLevel*/ = 1) const override { OPENVDB_THROW(NotImplementedError, ""); } + + /// @brief Dummy declarations to keep Grid class happy + using LeafNodeType = void; + using ValueAllIter = void; + using ValueAllCIter = void; + using ValueOnIter = void; + using ValueOnCIter = void; + using ValueOffIter = void; + using ValueOffCIter = void; + +private: + ValueType mBackground = zeroVal(); +}; // class AdaptiveTree + + + +//////////////////////////////////////// + + +template +inline const Name& +AdaptiveTree::treeType() +{ + static std::string sTreeTypeName = []() + { + std::ostringstream ostr; + ostr << "Adaptive_Tree_" << typeNameAsString(); + return ostr.str(); + }(); + return sTreeTypeName; +} + + +/// @brief Adaptive grid. +template +using AdaptiveGrid = Grid>; + +using FloatAdaptiveGrid = AdaptiveGrid; + +using AdaptiveGridTypes = TypeList; + +} // namespace adaptive + + +//////////////////////////////////////// + +/// Partial specialization for AdaptiveAccessor types +template +struct TreeAdapter > +{ + using TreeType = _TreeType; + using NonConstTreeType = typename std::remove_const::type; + using TreePtrType = typename TreeType::Ptr; + using ConstTreePtrType = typename TreeType::ConstPtr; + using NonConstTreePtrType = typename NonConstTreeType::Ptr; + using GridType = Grid; + using NonConstGridType = Grid; + using GridPtrType = typename GridType::Ptr; + using NonConstGridPtrType = typename NonConstGridType::Ptr; + using ConstGridPtrType = typename GridType::ConstPtr; + using ValueType = typename TreeType::ValueType; + using AccessorType = typename adaptive::AdaptiveAccessor; + using ConstAccessorType = typename adaptive::AdaptiveAccessor; + using NonConstAccessorType = typename adaptive::AdaptiveAccessor; + + static NonConstTreeType& tree(NonConstTreeType& t) { return t; } + static NonConstTreeType& tree(NonConstGridType& g) { return g.tree(); } + static NonConstTreeType& tree(NonConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& tree(ConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& tree(const NonConstTreeType& t) { return t; } + static const NonConstTreeType& tree(const NonConstGridType& g) { return g.tree(); } + static const NonConstTreeType& tree(const NonConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& tree(const ConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& constTree(NonConstTreeType& t) { return t; } + static const NonConstTreeType& constTree(NonConstGridType& g) { return g.constTree(); } + static const NonConstTreeType& constTree(NonConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& constTree(ConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& constTree(const NonConstTreeType& t) { return t; } + static const NonConstTreeType& constTree(const NonConstGridType& g) { return g.constTree(); } + static const NonConstTreeType& constTree(const NonConstAccessorType& a) { return a.tree(); } + static const NonConstTreeType& constTree(const ConstAccessorType& a) { return a.tree(); } +}; + + +//////////////////////////////////////// + +// Overload the TreeTraits struct to declare a const/non-const AdaptiveTree as adaptive + +template +struct TreeTraits> +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Adaptive; +}; + +template +struct TreeTraits> +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Adaptive; +}; + +// Overload the TreeTraits struct to declare an AdaptiveAccessor as adaptive + +template +struct TreeTraits> +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Adaptive; +}; + + +//////////////////////////////////////// + + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_ADAPTIVE_ADAPTIVE_GRID_HAS_BEEN_INCLUDED diff --git a/openvdb/openvdb/openvdb.cc b/openvdb/openvdb/openvdb.cc index c776409dfc..89a7c0c81c 100644 --- a/openvdb/openvdb/openvdb.cc +++ b/openvdb/openvdb/openvdb.cc @@ -80,7 +80,7 @@ initialize() // Register common grid types. GridBase::clearRegistry(); - GridTypes::foreach(); + AllGridTypes::foreach(); // Register types associated with point index grids. Metadata::registerType(typeNameAsString(), Int32Metadata::createMetadata); diff --git a/openvdb/openvdb/openvdb.h b/openvdb/openvdb/openvdb.h index dbdb88ed41..12f31dedb9 100644 --- a/openvdb/openvdb/openvdb.h +++ b/openvdb/openvdb/openvdb.h @@ -13,6 +13,7 @@ #include "Grid.h" #include "tree/Tree.h" #include "points/PointDataGrid.h" +#include "adaptive/AdaptiveGrid.h" #include "io/File.h" @@ -98,8 +99,8 @@ using NumericGridTypes = RealGridTypes::Append; /// The Vec3 Grid types which OpenVDB will register by default. using Vec3GridTypes = TypeList; -/// The Grid types which OpenVDB will register by default. -using GridTypes = +/// The sparse Grid types. +using SparseGridTypes = NumericGridTypes:: Append:: Append:: @@ -110,6 +111,15 @@ using GridTypes = Append; /// @} +// for backwards-compatibility +using GridTypes = SparseGridTypes; + +/// The Grid types which OpenVDB will register by default. +using AllGridTypes = + SparseGridTypes:: + Append; +/// @} + namespace internal { template using ToTreeType = typename T::TreeType; diff --git a/openvdb/openvdb/tools/Interpolation.h b/openvdb/openvdb/tools/Interpolation.h index 5a3cbc2eca..59c3502c4a 100644 --- a/openvdb/openvdb/tools/Interpolation.h +++ b/openvdb/openvdb/tools/Interpolation.h @@ -629,29 +629,35 @@ template inline void BoxSampler::getValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk) { - data[0][0][0] = inTree.getValue(ijk); // i, j, k + // This algorithm is only defined for sparse grids - ijk[2] += 1; - data[0][0][1] = inTree.getValue(ijk); // i, j, k + 1 + if constexpr (isSparseTree()) { + data[0][0][0] = inTree.getValue(ijk); // i, j, k - ijk[1] += 1; - data[0][1][1] = inTree.getValue(ijk); // i, j+1, k + 1 + ijk[2] += 1; + data[0][0][1] = inTree.getValue(ijk); // i, j, k + 1 - ijk[2] -= 1; - data[0][1][0] = inTree.getValue(ijk); // i, j+1, k + ijk[1] += 1; + data[0][1][1] = inTree.getValue(ijk); // i, j+1, k + 1 - ijk[0] += 1; - ijk[1] -= 1; - data[1][0][0] = inTree.getValue(ijk); // i+1, j, k + ijk[2] -= 1; + data[0][1][0] = inTree.getValue(ijk); // i, j+1, k - ijk[2] += 1; - data[1][0][1] = inTree.getValue(ijk); // i+1, j, k + 1 + ijk[0] += 1; + ijk[1] -= 1; + data[1][0][0] = inTree.getValue(ijk); // i+1, j, k - ijk[1] += 1; - data[1][1][1] = inTree.getValue(ijk); // i+1, j+1, k + 1 + ijk[2] += 1; + data[1][0][1] = inTree.getValue(ijk); // i+1, j, k + 1 - ijk[2] -= 1; - data[1][1][0] = inTree.getValue(ijk); // i+1, j+1, k + ijk[1] += 1; + data[1][1][1] = inTree.getValue(ijk); // i+1, j+1, k + 1 + + ijk[2] -= 1; + data[1][1][0] = inTree.getValue(ijk); // i+1, j+1, k + } else { + static_assert(AlwaysFalseValue, "Not Implemented"); + } } template @@ -744,20 +750,32 @@ inline bool BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord, typename TreeT::ValueType& result) { - using ValueT = typename TreeT::ValueType; - - const Vec3i inIdx = local_util::floorVec3(inCoord); - const Vec3R uvw = inCoord - inIdx; - - // Retrieve the values of the eight voxels surrounding the - // fractional source coordinates. - ValueT data[2][2][2]; - - const bool hasActiveValues = BoxSampler::probeValues(data, inTree, Coord(inIdx)); - - result = BoxSampler::trilinearInterpolation(data, uvw); - - return hasActiveValues; + if constexpr (isSparseTree()) { + using ValueT = typename TreeT::ValueType; + + const Vec3i inIdx = local_util::floorVec3(inCoord); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the eight voxels surrounding the + // fractional source coordinates. + ValueT data[2][2][2]; + + const bool hasActiveValues = BoxSampler::probeValues(data, inTree, Coord(inIdx)); + + result = BoxSampler::trilinearInterpolation(data, uvw); + + return hasActiveValues; + } else if constexpr (isAdaptiveTree()) { + // As an example, return the background value. + // This is where the logic that could sample against an adaptive tree would live. + // Extract the tree from the Tree or ValueAccessor + auto& tree = TreeAdapter::tree(inTree); + result = tree.background(); + return true; + } else { + static_assert(AlwaysFalseValue, "Not Implemented"); + } + std::abort(); // unreachable } @@ -765,18 +783,30 @@ template inline typename TreeT::ValueType BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord) { - using ValueT = typename TreeT::ValueType; + if constexpr (isSparseTree()) { - const Vec3i inIdx = local_util::floorVec3(inCoord); - const Vec3R uvw = inCoord - inIdx; + using ValueT = typename TreeT::ValueType; - // Retrieve the values of the eight voxels surrounding the - // fractional source coordinates. - ValueT data[2][2][2]; + const Vec3i inIdx = local_util::floorVec3(inCoord); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the eight voxels surrounding the + // fractional source coordinates. + ValueT data[2][2][2]; - BoxSampler::getValues(data, inTree, Coord(inIdx)); + BoxSampler::getValues(data, inTree, Coord(inIdx)); - return BoxSampler::trilinearInterpolation(data, uvw); + return BoxSampler::trilinearInterpolation(data, uvw); + } else if constexpr (isAdaptiveTree()) { + // As an example, return the background value. + // This is where the logic that could sample against an adaptive tree would live. + // Extract the tree from the Tree or ValueAccessor + auto& tree = TreeAdapter::tree(inTree); + return tree.background(); + } else { + static_assert(AlwaysFalseValue, "Not Implemented"); + } + std::abort(); // unreachable } diff --git a/openvdb/openvdb/tree/Tree.h b/openvdb/openvdb/tree/Tree.h index 44e77c5307..dab33f6102 100644 --- a/openvdb/openvdb/tree/Tree.h +++ b/openvdb/openvdb/tree/Tree.h @@ -188,6 +188,11 @@ class Tree: public TreeBase static const Index DEPTH = RootNodeType::LEVEL + 1; + using Accessor = ValueAccessor; + using ConstAccessor = ValueAccessor; + using UnsafeAccessor = ValueAccessor; + using ConstUnsafeAccessor = ValueAccessor; + /// @brief ValueConverter::Type is the type of a tree having the same /// hierarchy as this tree but a different value type, T. /// @@ -623,6 +628,31 @@ class Tree: public TreeBase /// Remove all tiles from this tree and all nodes other than the root node. void clear(); + /// @brief Return an accessor that provides random read and write access + /// to this tree's voxels. + /// @details The accessor is safe in the sense that it is registered with this tree. + Accessor getAccessor(); + /// @brief Return an unsafe accessor that provides random read and write access + /// to this tree's voxels. + /// @details The accessor is unsafe in the sense that it is not registered + /// with this tree's tree. In some rare cases this can give a performance advantage + /// over a registered accessor, but it is unsafe if the tree topology is modified. + /// @warning Only use this method if you're an expert and know the + /// risks of using an unregistered accessor (see tree/ValueAccessor.h) + UnsafeAccessor getUnsafeAccessor(); + /// Return an accessor that provides random read-only access to this tree's voxels. + ConstAccessor getAccessor() const; + /// Return an accessor that provides random read-only access to this tree's voxels. + ConstAccessor getConstAccessor() const; + /// @brief Return an unsafe accessor that provides random read-only access + /// to this tree's voxels. + /// @details The accessor is unsafe in the sense that it is not registered + /// with this tree. In some rare cases this can give a performance advantage + /// over a registered accessor, but it is unsafe if the tree topology is modified. + /// @warning Only use this method if you're an expert and know the + /// risks of using an unregistered accessor (see tree/ValueAccessor.h) + ConstUnsafeAccessor getConstUnsafeAccessor(); + /// Clear all registered accessors. void clearAllAccessors(); @@ -1318,6 +1348,41 @@ Tree::clear() //////////////////////////////////////// +template +typename Tree::Accessor +Tree::getAccessor() +{ + return Accessor(*this); +} + +template +typename Tree::UnsafeAccessor +Tree::getUnsafeAccessor() +{ + return UnsafeAccessor(*this); +} + +template +typename Tree::ConstAccessor +Tree::getAccessor() const +{ + return ConstAccessor(*this); +} + +template +typename Tree::ConstAccessor +Tree::getConstAccessor() const +{ + return ConstAccessor(*this); +} + +template +typename Tree::ConstUnsafeAccessor +Tree::getConstUnsafeAccessor() +{ + return ConstUnsafeAccessor(*this); +} + template inline void Tree::attachAccessor(ValueAccessorBase& accessor) const @@ -2060,6 +2125,28 @@ Tree::print(std::ostream& os, int verboseLevel) const } } // namespace tree + + +//////////////////////////////////////// + +// Overload the TreeTraits struct to declare a const/non-const Tree as sparse + +template +struct TreeTraits> +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Sparse; +}; + +template +struct TreeTraits> +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Sparse; +}; + + +//////////////////////////////////////// + + } // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/openvdb/openvdb/tree/ValueAccessor.h b/openvdb/openvdb/tree/ValueAccessor.h index bf749e8785..83619ba041 100644 --- a/openvdb/openvdb/tree/ValueAccessor.h +++ b/openvdb/openvdb/tree/ValueAccessor.h @@ -1021,6 +1021,19 @@ class ValueAccessorImpl final : }; // ValueAccessorImpl } // namespace tree + + +//////////////////////////////////////// + +// Overload the TreeTraits struct to declare a const/non-const ValueAccessorImpl as sparse + +template +struct TreeTraits> +{ + constexpr static TreeRepresentation Representation = TreeRepresentation::Sparse; +}; + + } // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/openvdb/openvdb/unittest/CMakeLists.txt b/openvdb/openvdb/unittest/CMakeLists.txt index 3fa4b9130b..d0cf4ab5d6 100644 --- a/openvdb/openvdb/unittest/CMakeLists.txt +++ b/openvdb/openvdb/unittest/CMakeLists.txt @@ -76,6 +76,7 @@ if(OPENVDB_TESTS) else() list(APPEND UNITTEST_SOURCE_FILES TestActivate.cc + TestAdaptive.cc TestAttributeArray.cc TestAttributeArrayString.cc TestAttributeGroup.cc diff --git a/openvdb/openvdb/unittest/TestAdaptive.cc b/openvdb/openvdb/unittest/TestAdaptive.cc new file mode 100644 index 0000000000..7e894e2714 --- /dev/null +++ b/openvdb/openvdb/unittest/TestAdaptive.cc @@ -0,0 +1,59 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include + +#include + +class TestAdaptive: public ::testing::Test +{ +public: + void SetUp() override { openvdb::initialize(); } + void TearDown() override { openvdb::initialize(); } +}; + + +//////////////////////////////////////// + + +TEST_F(TestAdaptive, test) +{ + openvdb::adaptive::AdaptiveGrid adaptiveGrid(5.0f); + + EXPECT_EQ(adaptiveGrid.background(), 5.0f); +} + +TEST_F(TestAdaptive, testSample) +{ + const float background = 5.0f; + openvdb::adaptive::AdaptiveGrid adaptiveGrid(background); + + float result = openvdb::tools::BoxSampler::sample(adaptiveGrid.tree(), openvdb::Vec3R(1.3, 1.6, 1.8)); + + EXPECT_EQ(result, background); +} + +TEST_F(TestAdaptive, testAdvect) +{ + using AdaptiveGridT = openvdb::adaptive::AdaptiveGrid; + using PointAdvectT = openvdb::tools::PointAdvect; + using PointListT = PointAdvectT::PointListType; + + const openvdb::Vec3s background(0.0f, 1.0f, 0.0f); + AdaptiveGridT adaptiveGrid(background); + + openvdb::tools::PointAdvect pointAdvect(adaptiveGrid); + + PointListT points; + points.push_back(openvdb::Vec3s(0.0f, 0.0f, 0.0f)); + points.push_back(openvdb::Vec3s(1.0f, 2.0f, 3.0f)); + + float dt = 1/24.0f; + pointAdvect.advect(points, dt); + + EXPECT_EQ(points[0], openvdb::Vec3s(0.0f, dt, 0.0f)); + EXPECT_EQ(points[1], openvdb::Vec3s(1.0f, 2.0f + dt, 3.0f)); +} diff --git a/openvdb/openvdb/unittest/TestGrid.cc b/openvdb/openvdb/unittest/TestGrid.cc index 1e9d058b77..9214ece3ef 100644 --- a/openvdb/openvdb/unittest/TestGrid.cc +++ b/openvdb/openvdb/unittest/TestGrid.cc @@ -40,6 +40,11 @@ class ProxyTree: public openvdb::TreeBase using Ptr = openvdb::SharedPtr; using ConstPtr = openvdb::SharedPtr; + using Accessor = void; + using ConstAccessor = void; + using UnsafeAccessor = void; + using ConstUnsafeAccessor = void; + static const openvdb::Index DEPTH; static const ValueType backg;