Compare commits

...
Author SHA1 Message Date
Dan Bailey ae0875bb06 Add an example to demonstrate sampling and point advection using an adaptive grid
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-04-15 23:25:14 -07:00
Dan Bailey 13aa50ac1f Introduce a TreeTraits struct to differentiate between sparse and adaptive trees
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-04-15 23:25:14 -07:00
Dan Bailey 77a424312e Add a type-dependent false expression
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-03-11 22:46:35 -07:00
Dan Bailey 7624beab0a Add TreeAdapter partial specialization for AdaptiveAccessor
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-03-11 22:46:35 -07:00
Dan Bailey 7262f8b113 Extend AdaptiveTree to add an AdaptiveAccessor
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-03-11 22:46:35 -07:00
Dan Bailey a6c7af8109 Add AdaptiveTree and Grid registration
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-03-11 22:46:35 -07:00
Dan Bailey f5f92dcde1 Adjust existing grid API to move ValueAccessor methods to the tree
Signed-off-by: Dan Bailey <danbailey@ilm.com>
2024-03-11 20:45:53 -07:00
12 changed files with 615 additions and 45 deletions
+5
View File
@@ -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)
+9 -9
View File
@@ -586,10 +586,10 @@ public:
using ValueAllIter = typename _TreeType::ValueAllIter;
using ValueAllCIter = typename _TreeType::ValueAllCIter;
using Accessor = typename tree::ValueAccessor<_TreeType, true>;
using ConstAccessor = typename tree::ValueAccessor<const _TreeType, true>;
using UnsafeAccessor = typename tree::ValueAccessor<_TreeType, false>;
using ConstUnsafeAccessor = typename tree::ValueAccessor<const _TreeType, false>;
using Accessor = typename _TreeType::Accessor;
using ConstAccessor = typename _TreeType::ConstAccessor;
using UnsafeAccessor = typename _TreeType::UnsafeAccessor;
using ConstUnsafeAccessor = typename _TreeType::ConstUnsafeAccessor;
/// @brief ValueConverter<T>::Type is the type of a grid having the same
/// hierarchy as this grid but a different value type, T.
@@ -729,7 +729,7 @@ public:
/// @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 @@ public:
/// 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 @@ public:
/// 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(); }
+44
View File
@@ -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<typename T>
struct TreeTraits
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Unknown;
};
template <typename T>
constexpr bool isSparseTree()
{
return TreeTraits<T>::Representation == TreeRepresentation::Sparse;
}
template <typename T>
constexpr bool isAdaptiveTree()
{
return TreeTraits<T>::Representation == TreeRepresentation::Adaptive;
}
////////////////////////////////////////
template<typename T, bool = IsSpecializationOf<T, math::Vec2>::value ||
IsSpecializationOf<T, math::Vec3>::value ||
@@ -446,6 +470,26 @@ template<typename FromType, typename ToType> struct CopyConstness<const FromType
/// @endcond
////////////////////////////////////////
/// @brief A type-dependent expression that always evaluates to false.
/// This is used as a work-around for the much discussed problem of wanting to do:
/// if constexpr (expr) { }
/// else { static_assert(false); }
///
/// At present, C++ evaluates static_assert(false) regardless of the outcome of the
/// compile-time expression. By using a type-dependent expression, the static assert
/// is only evaluated during instantiation:
///
/// static_assert(openvdb::AlwaysFalseValue<T>);
///
/// This results in the desired outcome of generating a compile-time static assert
/// only if the first clause evaluates to false.
template<class>
inline constexpr bool AlwaysFalseValue = false;
////////////////////////////////////////
+316
View File
@@ -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 <openvdb/version.h>
#include <openvdb/Grid.h>
#include <openvdb/tree/Tree.h>
namespace openvdb {
OPENVDB_USE_VERSION_NAMESPACE
namespace OPENVDB_VERSION_NAME {
////////////////////////////////////////
namespace adaptive {
template <typename TreeT>
class AdaptiveAccessor
{
public:
using ValueType = typename TreeT::ValueType;
explicit AdaptiveAccessor(TreeT& tree):
mTree(tree) { }
TreeT& tree() const { return mTree; }
private:
TreeT& mTree;
};
template<typename _ValueType>
class AdaptiveTree final: public TreeBase
{
public:
using Ptr = SharedPtr<AdaptiveTree>;
using ConstPtr = SharedPtr<const AdaptiveTree>;
using ValueType = _ValueType;
using BuildType = _ValueType;
static const Index DEPTH = 1;
using Accessor = AdaptiveAccessor<AdaptiveTree>;
using ConstAccessor = AdaptiveAccessor<const AdaptiveTree>;
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<typename TreeType>
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<ValueType>()) { 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<Index32> 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<ValueType>();
}; // class AdaptiveTree
////////////////////////////////////////
template<typename ValueType>
inline const Name&
AdaptiveTree<ValueType>::treeType()
{
static std::string sTreeTypeName = []()
{
std::ostringstream ostr;
ostr << "Adaptive_Tree_" << typeNameAsString<BuildType>();
return ostr.str();
}();
return sTreeTypeName;
}
/// @brief Adaptive grid.
template <typename T>
using AdaptiveGrid = Grid<AdaptiveTree<T>>;
using FloatAdaptiveGrid = AdaptiveGrid<float>;
using AdaptiveGridTypes = TypeList<FloatAdaptiveGrid>;
} // namespace adaptive
////////////////////////////////////////
/// Partial specialization for AdaptiveAccessor types
template<typename _TreeType>
struct TreeAdapter<adaptive::AdaptiveAccessor<_TreeType> >
{
using TreeType = _TreeType;
using NonConstTreeType = typename std::remove_const<TreeType>::type;
using TreePtrType = typename TreeType::Ptr;
using ConstTreePtrType = typename TreeType::ConstPtr;
using NonConstTreePtrType = typename NonConstTreeType::Ptr;
using GridType = Grid<TreeType>;
using NonConstGridType = Grid<NonConstTreeType>;
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<TreeType>;
using ConstAccessorType = typename adaptive::AdaptiveAccessor<const NonConstTreeType>;
using NonConstAccessorType = typename adaptive::AdaptiveAccessor<NonConstTreeType>;
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<typename ValueT>
struct TreeTraits<adaptive::AdaptiveTree<ValueT>>
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Adaptive;
};
template<typename ValueT>
struct TreeTraits<const adaptive::AdaptiveTree<ValueT>>
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Adaptive;
};
// Overload the TreeTraits struct to declare an AdaptiveAccessor as adaptive
template<typename TreeT>
struct TreeTraits<adaptive::AdaptiveAccessor<TreeT>>
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Adaptive;
};
////////////////////////////////////////
} // namespace OPENVDB_VERSION_NAME
} // namespace openvdb
#endif // OPENVDB_ADAPTIVE_ADAPTIVE_GRID_HAS_BEEN_INCLUDED
+1 -1
View File
@@ -80,7 +80,7 @@ initialize()
// Register common grid types.
GridBase::clearRegistry();
GridTypes::foreach<RegisterGrid>();
AllGridTypes::foreach<RegisterGrid>();
// Register types associated with point index grids.
Metadata::registerType(typeNameAsString<PointIndex32>(), Int32Metadata::createMetadata);
+12 -2
View File
@@ -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<IntegerGridTypes>;
/// The Vec3 Grid types which OpenVDB will register by default.
using Vec3GridTypes = TypeList<Vec3IGrid, Vec3SGrid, Vec3DGrid>;
/// The Grid types which OpenVDB will register by default.
using GridTypes =
/// The sparse Grid types.
using SparseGridTypes =
NumericGridTypes::
Append<Vec3GridTypes>::
Append<tools::PointIndexGrid>::
@@ -110,6 +111,15 @@ using GridTypes =
Append<BoolGrid, MaskGrid>;
/// @}
// for backwards-compatibility
using GridTypes = SparseGridTypes;
/// The Grid types which OpenVDB will register by default.
using AllGridTypes =
SparseGridTypes::
Append<adaptive::AdaptiveGridTypes>;
/// @}
namespace internal {
template <typename T> using ToTreeType = typename T::TreeType;
+63 -33
View File
@@ -629,29 +629,35 @@ template<class ValueT, class TreeT, size_t N>
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<TreeT>()) {
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<TreeT>, "Not Implemented");
}
}
template<class ValueT, class TreeT, size_t N>
@@ -744,20 +750,32 @@ inline bool
BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord,
typename TreeT::ValueType& result)
{
using ValueT = typename TreeT::ValueType;
if constexpr (isSparseTree<TreeT>()) {
using ValueT = typename TreeT::ValueType;
const Vec3i inIdx = local_util::floorVec3(inCoord);
const Vec3R uvw = inCoord - inIdx;
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];
// 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));
const bool hasActiveValues = BoxSampler::probeValues(data, inTree, Coord(inIdx));
result = BoxSampler::trilinearInterpolation(data, uvw);
result = BoxSampler::trilinearInterpolation(data, uvw);
return hasActiveValues;
return hasActiveValues;
} else if constexpr (isAdaptiveTree<TreeT>()) {
// 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<TreeT>::tree(inTree);
result = tree.background();
return true;
} else {
static_assert(AlwaysFalseValue<TreeT>, "Not Implemented");
}
std::abort(); // unreachable
}
@@ -765,18 +783,30 @@ template<class TreeT>
inline typename TreeT::ValueType
BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord)
{
using ValueT = typename TreeT::ValueType;
if constexpr (isSparseTree<TreeT>()) {
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;
BoxSampler::getValues(data, inTree, Coord(inIdx));
// Retrieve the values of the eight voxels surrounding the
// fractional source coordinates.
ValueT data[2][2][2];
return BoxSampler::trilinearInterpolation(data, uvw);
BoxSampler::getValues(data, inTree, Coord(inIdx));
return BoxSampler::trilinearInterpolation(data, uvw);
} else if constexpr (isAdaptiveTree<TreeT>()) {
// 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<TreeT>::tree(inTree);
return tree.background();
} else {
static_assert(AlwaysFalseValue<TreeT>, "Not Implemented");
}
std::abort(); // unreachable
}
+87
View File
@@ -188,6 +188,11 @@ public:
static const Index DEPTH = RootNodeType::LEVEL + 1;
using Accessor = ValueAccessor<Tree, true>;
using ConstAccessor = ValueAccessor<const Tree, true>;
using UnsafeAccessor = ValueAccessor<Tree, false>;
using ConstUnsafeAccessor = ValueAccessor<const Tree, false>;
/// @brief ValueConverter<T>::Type is the type of a tree having the same
/// hierarchy as this tree but a different value type, T.
///
@@ -623,6 +628,31 @@ public:
/// 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<RootNodeType>::clear()
////////////////////////////////////////
template<typename RootNodeType>
typename Tree<RootNodeType>::Accessor
Tree<RootNodeType>::getAccessor()
{
return Accessor(*this);
}
template<typename RootNodeType>
typename Tree<RootNodeType>::UnsafeAccessor
Tree<RootNodeType>::getUnsafeAccessor()
{
return UnsafeAccessor(*this);
}
template<typename RootNodeType>
typename Tree<RootNodeType>::ConstAccessor
Tree<RootNodeType>::getAccessor() const
{
return ConstAccessor(*this);
}
template<typename RootNodeType>
typename Tree<RootNodeType>::ConstAccessor
Tree<RootNodeType>::getConstAccessor() const
{
return ConstAccessor(*this);
}
template<typename RootNodeType>
typename Tree<RootNodeType>::ConstUnsafeAccessor
Tree<RootNodeType>::getConstUnsafeAccessor()
{
return ConstUnsafeAccessor(*this);
}
template<typename RootNodeType>
inline void
Tree<RootNodeType>::attachAccessor(ValueAccessorBase<Tree, true>& accessor) const
@@ -2060,6 +2125,28 @@ Tree<RootNodeType>::print(std::ostream& os, int verboseLevel) const
}
} // namespace tree
////////////////////////////////////////
// Overload the TreeTraits struct to declare a const/non-const Tree as sparse
template<typename NodeT>
struct TreeTraits<tree::Tree<NodeT>>
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Sparse;
};
template<typename NodeT>
struct TreeTraits<const tree::Tree<NodeT>>
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Sparse;
};
////////////////////////////////////////
} // namespace OPENVDB_VERSION_NAME
} // namespace openvdb
+13
View File
@@ -1021,6 +1021,19 @@ private:
}; // ValueAccessorImpl
} // namespace tree
////////////////////////////////////////
// Overload the TreeTraits struct to declare a const/non-const ValueAccessorImpl as sparse
template<typename TreeT, bool IsSafeT, typename MutexT, typename IndexSequenceT>
struct TreeTraits<tree::ValueAccessorImpl<TreeT, IsSafeT, MutexT, IndexSequenceT>>
{
constexpr static TreeRepresentation Representation = TreeRepresentation::Sparse;
};
} // namespace OPENVDB_VERSION_NAME
} // namespace openvdb
+1
View File
@@ -76,6 +76,7 @@ if(OPENVDB_TESTS)
else()
list(APPEND UNITTEST_SOURCE_FILES
TestActivate.cc
TestAdaptive.cc
TestAttributeArray.cc
TestAttributeArrayString.cc
TestAttributeGroup.cc
+59
View File
@@ -0,0 +1,59 @@
// Copyright Contributors to the OpenVDB Project
// SPDX-License-Identifier: MPL-2.0
#include <openvdb/openvdb.h>
#include <openvdb/tools/Interpolation.h>
#include <openvdb/tools/PointAdvect.h>
#include <openvdb/adaptive/AdaptiveGrid.h>
#include <gtest/gtest.h>
class TestAdaptive: public ::testing::Test
{
public:
void SetUp() override { openvdb::initialize(); }
void TearDown() override { openvdb::initialize(); }
};
////////////////////////////////////////
TEST_F(TestAdaptive, test)
{
openvdb::adaptive::AdaptiveGrid<float> adaptiveGrid(5.0f);
EXPECT_EQ(adaptiveGrid.background(), 5.0f);
}
TEST_F(TestAdaptive, testSample)
{
const float background = 5.0f;
openvdb::adaptive::AdaptiveGrid<float> 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<openvdb::Vec3s>;
using PointAdvectT = openvdb::tools::PointAdvect<AdaptiveGridT>;
using PointListT = PointAdvectT::PointListType;
const openvdb::Vec3s background(0.0f, 1.0f, 0.0f);
AdaptiveGridT adaptiveGrid(background);
openvdb::tools::PointAdvect<AdaptiveGridT> 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));
}
+5
View File
@@ -40,6 +40,11 @@ public:
using Ptr = openvdb::SharedPtr<ProxyTree>;
using ConstPtr = openvdb::SharedPtr<const ProxyTree>;
using Accessor = void;
using ConstAccessor = void;
using UnsafeAccessor = void;
using ConstUnsafeAccessor = void;
static const openvdb::Index DEPTH;
static const ValueType backg;