// Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: Apache-2.0 #ifndef OPENVDB_GRID_HAS_BEEN_INCLUDED #define OPENVDB_GRID_HAS_BEEN_INCLUDED #include "Exceptions.h" #include "MetaMap.h" #include "Types.h" #include "io/io.h" #include "math/Transform.h" #include "tree/Tree.h" #include "util/Assert.h" #include "util/logging.h" #include "util/Name.h" #include #include #include #include namespace openvdb { OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { using TreeBase = tree::TreeBase; template class Grid; // forward declaration /// @brief Create a new grid of type @c GridType with a given background value. /// /// @note Calling createGrid(background) is equivalent to calling /// GridType::create(background). template inline typename GridType::Ptr createGrid(const typename GridType::ValueType& background); /// @brief Create a new grid of type @c GridType with background value zero. /// /// @note Calling createGrid() is equivalent to calling GridType::create(). template inline typename GridType::Ptr createGrid(); /// @brief Create a new grid of the appropriate type that wraps the given tree. /// /// @note This function can be called without specifying the template argument, /// i.e., as createGrid(tree). template inline typename Grid::Ptr createGrid(TreePtrType); /// @brief Create a new grid of type @c GridType classified as a "Level Set", /// i.e., a narrow-band level set. /// /// @note @c GridType::ValueType must be a floating-point scalar. /// /// @param voxelSize the size of a voxel in world units /// @param halfWidth the half width of the narrow band in voxel units /// /// @details The voxel size and the narrow band half width define the grid's /// background value as halfWidth*voxelWidth. The transform is linear /// with a uniform scaling only corresponding to the specified voxel size. /// /// @note It is generally advisable to specify a half-width of the narrow band /// that is larger than one voxel unit, otherwise zero crossings are not guaranteed. template typename GridType::Ptr createLevelSet( Real voxelSize = 1.0, Real halfWidth = LEVEL_SET_HALF_WIDTH); //////////////////////////////////////// /// @brief Abstract base class for typed grids class OPENVDB_API GridBase: public MetaMap { public: using Ptr = SharedPtr; using ConstPtr = SharedPtr; using GridFactory = Ptr (*)(); ~GridBase() override {} /// @name Copying /// @{ /// @brief Return a new grid of the same type as this grid whose metadata is a /// deep copy of this grid's and whose tree and transform are shared with this grid. virtual GridBase::Ptr copyGrid() = 0; /// @brief Return a new grid of the same type as this grid whose metadata is a /// deep copy of this grid's and whose tree and transform are shared with this grid. virtual GridBase::ConstPtr copyGrid() const = 0; /// @brief Return a new grid of the same type as this grid whose metadata and /// transform are deep copies of this grid's and whose tree is default-constructed. virtual GridBase::Ptr copyGridWithNewTree() const = 0; /// @brief Return a new grid of the same type as this grid whose tree and transform /// is shared with this grid and whose metadata is provided as an argument. virtual GridBase::ConstPtr copyGridReplacingMetadata(const MetaMap& meta) const = 0; /// @brief Return a new grid of the same type as this grid whose tree is shared with /// this grid, whose metadata is a deep copy of this grid's and whose transform is /// provided as an argument. /// @throw ValueError if the transform pointer is null virtual GridBase::ConstPtr copyGridReplacingTransform(math::Transform::Ptr xform) const = 0; /// @brief Return a new grid of the same type as this grid whose tree is shared with /// this grid and whose transform and metadata are provided as arguments. /// @throw ValueError if the transform pointer is null virtual GridBase::ConstPtr copyGridReplacingMetadataAndTransform(const MetaMap& meta, math::Transform::Ptr xform) const = 0; /// Return a new grid whose metadata, transform and tree are deep copies of this grid's. virtual GridBase::Ptr deepCopyGrid() const = 0; /// @} /// @name Registry /// @{ /// Create a new grid of the given (registered) type. static Ptr createGrid(const Name& type); /// Return @c true if the given grid type name is registered. static bool isRegistered(const Name &type); /// Clear the grid type registry. static void clearRegistry(); /// @} /// @name Type access /// @{ /// Return the name of this grid's type. virtual Name type() const = 0; /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). virtual Name valueType() const = 0; /// Return @c true if this grid is of the same type as the template parameter. template bool isType() const { return (this->type() == GridType::gridType()); } /// @} //@{ /// @brief Return the result of downcasting a GridBase pointer to a Grid pointer /// of the specified type, or return a null pointer if the types are incompatible. template static typename GridType::Ptr grid(const GridBase::Ptr&); template static typename GridType::ConstPtr grid(const GridBase::ConstPtr&); template static typename GridType::ConstPtr constGrid(const GridBase::Ptr&); template static typename GridType::ConstPtr constGrid(const GridBase::ConstPtr&); //@} /// @name Tree /// @{ /// @brief Return a pointer to this grid's tree, which might be /// shared with other grids. The pointer is guaranteed to be non-null. TreeBase::Ptr baseTreePtr(); /// @brief Return a pointer to this grid's tree, which might be /// shared with other grids. The pointer is guaranteed to be non-null. TreeBase::ConstPtr baseTreePtr() const { return this->constBaseTreePtr(); } /// @brief Return a pointer to this grid's tree, which might be /// shared with other grids. The pointer is guaranteed to be non-null. virtual TreeBase::ConstPtr constBaseTreePtr() const = 0; /// @brief Return true if tree is not shared with another grid. virtual bool isTreeUnique() const = 0; /// @brief Return a reference to this grid's tree, which might be /// shared with other grids. /// @note Calling @vdblink::GridBase::setTree() setTree@endlink /// on this grid invalidates all references previously returned by this method. TreeBase& baseTree() { return const_cast(this->constBaseTree()); } /// @brief Return a reference to this grid's tree, which might be /// shared with other grids. /// @note Calling @vdblink::GridBase::setTree() setTree@endlink /// on this grid invalidates all references previously returned by this method. const TreeBase& baseTree() const { return this->constBaseTree(); } /// @brief Return a reference to this grid's tree, which might be /// shared with other grids. /// @note Calling @vdblink::GridBase::setTree() setTree@endlink /// on this grid invalidates all references previously returned by this method. const TreeBase& constBaseTree() const { return *(this->constBaseTreePtr()); } /// @brief Associate the given tree with this grid, in place of its existing tree. /// @throw ValueError if the tree pointer is null /// @throw TypeError if the tree is not of the appropriate type /// @note Invalidates all references previously returned by /// @vdblink::GridBase::baseTree() baseTree@endlink /// or @vdblink::GridBase::constBaseTree() constBaseTree@endlink. virtual void setTree(TreeBase::Ptr) = 0; /// Set a new tree with the same background value as the previous tree. virtual void newTree() = 0; /// @} /// Return @c true if this grid contains only background voxels. virtual bool empty() const = 0; /// Empty this grid, setting all voxels to the background. virtual void clear() = 0; /// @name Tools /// @{ /// @brief Reduce the memory footprint of this grid by increasing its sparseness /// either losslessly (@a tolerance = 0) or lossily (@a tolerance > 0). /// @details With @a tolerance > 0, sparsify regions where voxels have the same /// active state and have values that differ by no more than the tolerance /// (converted to this grid's value type). virtual void pruneGrid(float tolerance = 0.0) = 0; /// @brief Clip this grid to the given world-space bounding box. /// @details Voxels that lie outside the bounding box are set to the background. /// @warning Clipping a level set will likely produce a grid that is /// no longer a valid level set. void clipGrid(const BBoxd&); /// @brief Clip this grid to the given index-space bounding box. /// @details Voxels that lie outside the bounding box are set to the background. /// @warning Clipping a level set will likely produce a grid that is /// no longer a valid level set. virtual void clip(const CoordBBox&) = 0; /// @} /// @{ /// @brief If this grid resolves to one of the listed grid types, /// invoke the given functor on the resolved grid. /// @return @c false if this grid's type is not one of the listed types /// /// @par Example: /// @code /// using AllowedGridTypes = openvdb::TypeList< /// openvdb::Int32Grid, openvdb::Int64Grid, /// openvdb::FloatGrid, openvdb::DoubleGrid>; /// /// const openvdb::CoordBBox bbox{ /// openvdb::Coord{0,0,0}, openvdb::Coord{10,10,10}}; /// /// // Fill the grid if it is one of the allowed types. /// myGridBasePtr->apply( /// [&bbox](auto& grid) { // C++14 /// using GridType = typename std::decay::type; /// grid.fill(bbox, typename GridType::ValueType(1)); /// } /// ); /// @endcode /// /// @see @vdblink::TypeList TypeList@endlink template inline bool apply(OpT&) const; template inline bool apply(OpT&); template inline bool apply(const OpT&) const; template inline bool apply(const OpT&); /// @} /// @name Metadata /// @{ /// Return this grid's user-specified name. std::string getName() const; /// Specify a name for this grid. void setName(const std::string&); /// Return the user-specified description of this grid's creator. std::string getCreator() const; /// Provide a description of this grid's creator. void setCreator(const std::string&); /// @brief Return @c true if this grid should be written out with floating-point /// voxel values (including components of vectors) quantized to 16 bits. bool saveFloatAsHalf() const; void setSaveFloatAsHalf(bool); /// @brief Return the class of volumetric data (level set, fog volume, etc.) /// that is stored in this grid. /// @sa gridClassToString, gridClassToMenuName, stringToGridClass GridClass getGridClass() const; /// @brief Specify the class of volumetric data (level set, fog volume, etc.) /// that is stored in this grid. /// @sa gridClassToString, gridClassToMenuName, stringToGridClass void setGridClass(GridClass); /// Remove the setting specifying the class of this grid's volumetric data. void clearGridClass(); /// @} /// Return the metadata string value for the given class of volumetric data. static std::string gridClassToString(GridClass); /// Return a formatted string version of the grid class. static std::string gridClassToMenuName(GridClass); /// @brief Return the class of volumetric data specified by the given string. /// @details If the string is not one of the ones returned by /// @vdblink::GridBase::gridClassToString() gridClassToString@endlink, /// return @c GRID_UNKNOWN. static GridClass stringToGridClass(const std::string&); /// @name Metadata /// @{ /// @brief Return the type of vector data (invariant, covariant, etc.) stored /// in this grid, assuming that this grid contains a vector-valued tree. /// @sa vecTypeToString, vecTypeExamples, vecTypeDescription, stringToVecType VecType getVectorType() const; /// @brief Specify the type of vector data (invariant, covariant, etc.) stored /// in this grid, assuming that this grid contains a vector-valued tree. /// @sa vecTypeToString, vecTypeExamples, vecTypeDescription, stringToVecType void setVectorType(VecType); /// Remove the setting specifying the type of vector data stored in this grid. void clearVectorType(); /// @} /// Return the metadata string value for the given type of vector data. static std::string vecTypeToString(VecType); /// Return a string listing examples of the given type of vector data /// (e.g., "Gradient/Normal", given VEC_COVARIANT). static std::string vecTypeExamples(VecType); /// @brief Return a string describing how the given type of vector data is affected /// by transformations (e.g., "Does not transform", given VEC_INVARIANT). static std::string vecTypeDescription(VecType); static VecType stringToVecType(const std::string&); /// @name Metadata /// @{ /// Return @c true if this grid's voxel values are in world space and should be /// affected by transformations, @c false if they are in local space and should /// not be affected by transformations. bool isInWorldSpace() const; /// Specify whether this grid's voxel values are in world space or in local space. void setIsInWorldSpace(bool); /// @} // Standard metadata field names // (These fields should normally not be accessed directly, but rather // via the accessor methods above, when available.) // Note: Visual C++ requires these declarations to be separate statements. static const char* const META_GRID_CLASS; static const char* const META_GRID_CREATOR; static const char* const META_GRID_NAME; static const char* const META_SAVE_HALF_FLOAT; static const char* const META_IS_LOCAL_SPACE; static const char* const META_VECTOR_TYPE; static const char* const META_FILE_BBOX_MIN; static const char* const META_FILE_BBOX_MAX; static const char* const META_FILE_COMPRESSION; static const char* const META_FILE_MEM_BYTES; static const char* const META_FILE_VOXEL_COUNT; static const char* const META_FILE_DELAYED_LOAD; /// @name Statistics /// @{ /// Return the number of active voxels. virtual Index64 activeVoxelCount() const = 0; /// Return the axis-aligned bounding box of all active voxels. If /// the grid is empty a default bbox is returned. virtual CoordBBox evalActiveVoxelBoundingBox() const = 0; /// Return the dimensions of the axis-aligned bounding box of all active voxels. virtual Coord evalActiveVoxelDim() const = 0; /// Return the number of bytes of memory used by this grid. virtual Index64 memUsage() const = 0; /// @brief Add metadata to this grid comprising the current values /// of statistics like the active voxel count and bounding box. /// @note This metadata is not automatically kept up-to-date with /// changes to this grid. void addStatsMetadata(); /// @brief Return a new MetaMap containing just the metadata that /// was added to this grid with @vdblink::GridBase::addStatsMetadata() /// addStatsMetadata@endlink. /// @details If @vdblink::GridBase::addStatsMetadata() addStatsMetadata@endlink /// was never called on this grid, return an empty MetaMap. MetaMap::Ptr getStatsMetadata() const; /// @} /// @name Transform /// @{ //@{ /// @brief Return a pointer to this grid's transform, which might be /// shared with other grids. math::Transform::Ptr transformPtr() { return mTransform; } math::Transform::ConstPtr transformPtr() const { return mTransform; } math::Transform::ConstPtr constTransformPtr() const { return mTransform; } //@} //@{ /// @brief Return a reference to this grid's transform, which might be /// shared with other grids. /// @note Calling @vdblink::GridBase::setTransform() setTransform@endlink /// on this grid invalidates all references previously returned by this method. math::Transform& transform() { return *mTransform; } const math::Transform& transform() const { return *mTransform; } const math::Transform& constTransform() const { return *mTransform; } //@} /// @} /// @name Transform /// @{ /// @brief Associate the given transform with this grid, in place of /// its existing transform. /// @throw ValueError if the transform pointer is null /// @note Invalidates all references previously returned by /// @vdblink::GridBase::transform() transform@endlink /// or @vdblink::GridBase::constTransform() constTransform@endlink. void setTransform(math::Transform::Ptr); /// Return the size of this grid's voxels. Vec3d voxelSize() const { return transform().voxelSize(); } /// @brief Return the size of this grid's voxel at position (x, y, z). /// @note Frustum and perspective transforms have position-dependent voxel size. Vec3d voxelSize(const Vec3d& xyz) const { return transform().voxelSize(xyz); } /// Return true if the voxels in world space are uniformly sized cubes bool hasUniformVoxels() const { return mTransform->hasUniformScale(); } /// Apply this grid's transform to the given coordinates. Vec3d indexToWorld(const Vec3d& xyz) const { return transform().indexToWorld(xyz); } /// Apply this grid's transform to the given coordinates. Vec3d indexToWorld(const Coord& ijk) const { return transform().indexToWorld(ijk); } /// Apply the inverse of this grid's transform to the given coordinates. Vec3d worldToIndex(const Vec3d& xyz) const { return transform().worldToIndex(xyz); } /// @} /// @name I/O /// @{ /// @brief Read the grid topology from a stream. /// This will read only the grid structure, not the actual data buffers. virtual void readTopology(std::istream&) = 0; /// @brief Write the grid topology to a stream. /// This will write only the grid structure, not the actual data buffers. virtual void writeTopology(std::ostream&) const = 0; /// Read all data buffers for this grid. virtual void readBuffers(std::istream&) = 0; /// Read all of this grid's data buffers that intersect the given index-space bounding box. virtual void readBuffers(std::istream&, const CoordBBox&) = 0; /// @brief Read all of this grid's data buffers that are not yet resident in memory /// (because delayed loading is in effect). /// @details If this grid was read from a memory-mapped file, this operation /// disconnects the grid from the file. /// @sa io::File::open, io::MappedFile virtual void readNonresidentBuffers() const = 0; /// Write out all data buffers for this grid. virtual void writeBuffers(std::ostream&) const = 0; /// Read in the transform for this grid. void readTransform(std::istream& is) { transform().read(is); } /// Write out the transform for this grid. void writeTransform(std::ostream& os) const { transform().write(os); } /// Output a human-readable description of this grid. virtual void print(std::ostream& = std::cout, int verboseLevel = 1) const = 0; /// @} protected: /// @brief Initialize with an identity linear transform. GridBase(): mTransform(math::Transform::createLinearTransform()) {} /// @brief Initialize with metadata and a transform. /// @throw ValueError if the transform pointer is null GridBase(const MetaMap& meta, math::Transform::Ptr xform); /// @brief Deep copy another grid's metadata and transform. GridBase(const GridBase& other): MetaMap(other), mTransform(other.mTransform->copy()) {} /// @brief Copy another grid's metadata but share its transform. GridBase(GridBase& other, ShallowCopy): MetaMap(other), mTransform(other.mTransform) {} /// Register a grid type along with a factory function. static void registerGrid(const Name& type, GridFactory); /// Remove a grid type from the registry. static void unregisterGrid(const Name& type); private: math::Transform::Ptr mTransform; }; // class GridBase //////////////////////////////////////// using GridPtrVec = std::vector; using GridPtrVecIter = GridPtrVec::iterator; using GridPtrVecCIter = GridPtrVec::const_iterator; using GridPtrVecPtr = SharedPtr; using GridCPtrVec = std::vector; using GridCPtrVecIter = GridCPtrVec::iterator; using GridCPtrVecCIter = GridCPtrVec::const_iterator; using GridCPtrVecPtr = SharedPtr; using GridPtrSet = std::set; using GridPtrSetIter = GridPtrSet::iterator; using GridPtrSetCIter = GridPtrSet::const_iterator; using GridPtrSetPtr = SharedPtr; using GridCPtrSet = std::set; using GridCPtrSetIter = GridCPtrSet::iterator; using GridCPtrSetCIter = GridCPtrSet::const_iterator; using GridCPtrSetPtr = SharedPtr; /// @brief Predicate functor that returns @c true for grids that have a specified name struct OPENVDB_API GridNamePred { GridNamePred(const Name& _name): name(_name) {} bool operator()(const GridBase::ConstPtr& g) const { return g && g->getName() == name; } Name name; }; /// Return the first grid in the given container whose name is @a name. template inline typename GridPtrContainerT::value_type findGridByName(const GridPtrContainerT& container, const Name& name) { using GridPtrT = typename GridPtrContainerT::value_type; typename GridPtrContainerT::const_iterator it = std::find_if(container.begin(), container.end(), GridNamePred(name)); return (it == container.end() ? GridPtrT() : *it); } /// Return the first grid in the given map whose name is @a name. template inline GridPtrT findGridByName(const std::map& container, const Name& name) { using GridPtrMapT = std::map; for (typename GridPtrMapT::const_iterator it = container.begin(), end = container.end(); it != end; ++it) { const GridPtrT& grid = it->second; if (grid && grid->getName() == name) return grid; } return GridPtrT(); } //@} //////////////////////////////////////// /// @brief Container class that associates a tree with a transform and metadata template class Grid: public GridBase { public: using Ptr = SharedPtr; using ConstPtr = SharedPtr; using TreeType = _TreeType; using TreePtrType = typename _TreeType::Ptr; using ConstTreePtrType = typename _TreeType::ConstPtr; using ValueType = typename _TreeType::ValueType; using BuildType = typename _TreeType::BuildType; using ValueOnIter = typename _TreeType::ValueOnIter; using ValueOnCIter = typename _TreeType::ValueOnCIter; using ValueOffIter = typename _TreeType::ValueOffIter; using ValueOffCIter = typename _TreeType::ValueOffCIter; using ValueAllIter = typename _TreeType::ValueAllIter; using ValueAllCIter = typename _TreeType::ValueAllCIter; 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. /// /// For example, FloatGrid::ValueConverter::Type is equivalent to DoubleGrid. /// @note If the source grid type is a template argument, it might be necessary /// to write "typename SourceGrid::template ValueConverter::Type". template struct ValueConverter { using Type = Grid::Type>; }; /// Return a new grid with the given background value. static Ptr create(const ValueType& background); /// Return a new grid with background value zero. static Ptr create(); /// @brief Return a new grid that contains the given tree. /// @throw ValueError if the tree pointer is null static Ptr create(TreePtrType); /// @brief Return a new, empty grid with the same transform and metadata as the /// given grid and with background value zero. static Ptr create(const GridBase& other); /// Construct a new grid with background value zero. Grid(); /// Construct a new grid with the given background value. explicit Grid(const ValueType& background); /// @brief Construct a new grid that shares the given tree and associates with it /// an identity linear transform. /// @throw ValueError if the tree pointer is null explicit Grid(TreePtrType); /// Deep copy another grid's metadata, transform and tree. Grid(const Grid&); /// @brief Deep copy the metadata, transform and tree of another grid whose tree /// configuration is the same as this grid's but whose value type is different. /// Cast the other grid's values to this grid's value type. /// @throw TypeError if the other grid's tree configuration doesn't match this grid's /// or if this grid's ValueType is not constructible from the other grid's ValueType. template explicit Grid(const Grid&); /// Deep copy another grid's metadata and transform, but share its tree. Grid(Grid&, ShallowCopy); /// @brief Deep copy another grid's metadata and transform, but construct a new tree /// with background value zero. explicit Grid(const GridBase&); ~Grid() override {} /// Disallow assignment, since it wouldn't be obvious whether the copy is deep or shallow. Grid& operator=(const Grid&) = delete; /// @name Copying /// @{ /// @brief Return a new grid of the same type as this grid whose metadata and /// transform are deep copies of this grid's and whose tree is shared with this grid. Ptr copy(); /// @brief Return a new grid of the same type as this grid whose metadata and /// transform are deep copies of this grid's and whose tree is shared with this grid. ConstPtr copy() const; /// @brief Return a new grid of the same type as this grid whose metadata and /// transform are deep copies of this grid's and whose tree is default-constructed. Ptr copyWithNewTree() const; /// @brief Return a new grid of the same type as this grid whose metadata is a /// deep copy of this grid's and whose tree and transform are shared with this grid. GridBase::Ptr copyGrid() override; /// @brief Return a new grid of the same type as this grid whose metadata is a /// deep copy of this grid's and whose tree and transform are shared with this grid. GridBase::ConstPtr copyGrid() const override; /// @brief Return a new grid of the same type as this grid whose metadata and /// transform are deep copies of this grid's and whose tree is default-constructed. GridBase::Ptr copyGridWithNewTree() const override; //@} /// @name Copying /// @{ /// @brief Return a new grid of the same type as this grid whose tree and transform /// is shared with this grid and whose metadata is provided as an argument. ConstPtr copyReplacingMetadata(const MetaMap& meta) const; /// @brief Return a new grid of the same type as this grid whose tree is shared with /// this grid, whose metadata is a deep copy of this grid's and whose transform is /// provided as an argument. /// @throw ValueError if the transform pointer is null ConstPtr copyReplacingTransform(math::Transform::Ptr xform) const; /// @brief Return a new grid of the same type as this grid whose tree is shared with /// this grid and whose transform and metadata are provided as arguments. /// @throw ValueError if the transform pointer is null ConstPtr copyReplacingMetadataAndTransform(const MetaMap& meta, math::Transform::Ptr xform) const; /// @brief Return a new grid of the same type as this grid whose tree and transform /// is shared with this grid and whose metadata is provided as an argument. GridBase::ConstPtr copyGridReplacingMetadata(const MetaMap& meta) const override; /// @brief Return a new grid of the same type as this grid whose tree is shared with /// this grid, whose metadata is a deep copy of this grid's and whose transform is /// provided as an argument. /// @throw ValueError if the transform pointer is null GridBase::ConstPtr copyGridReplacingTransform(math::Transform::Ptr xform) const override; /// @brief Return a new grid of the same type as this grid whose tree is shared with /// this grid and whose transform and metadata are provided as arguments. /// @throw ValueError if the transform pointer is null GridBase::ConstPtr copyGridReplacingMetadataAndTransform(const MetaMap& meta, math::Transform::Ptr xform) const override; /// @brief Return a new grid whose metadata, transform and tree are deep copies of this grid's. Ptr deepCopy() const { return Ptr(new Grid(*this)); } /// @brief Return a new grid whose metadata, transform and tree are deep copies of this grid's. GridBase::Ptr deepCopyGrid() const override { return this->deepCopy(); } //@} /// Return the name of this grid's type. Name type() const override { return this->gridType(); } /// Return the name of this type of grid. static Name gridType() { return TreeType::treeType(); } /// Return the name of the type of a voxel's value (e.g., "float" or "vec3d"). Name valueType() const override { return tree().valueType(); } /// @name Voxel access /// @{ /// @brief Return this grid's background value. /// @note Use tools::changeBackground to efficiently modify the background value. const ValueType& background() const { return mTree->background(); } /// Return @c true if this grid contains only inactive background voxels. bool empty() const override { return tree().empty(); } /// Empty this grid, so that all voxels become inactive background voxels. void clear() override { tree().clear(); } /// @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 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 /// with this grid'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 mTree->getUnsafeAccessor(); } /// Return an accessor that provides random read-only access to this grid's voxels. ConstAccessor getAccessor() const { return mTree->getConstAccessor(); } /// Return an accessor that provides random read-only access to this grid's voxels. 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 /// with this grid'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) 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(); } /// Return an iterator over all of this grid's active values (tile and voxel). ValueOnCIter beginValueOn() const { return tree().cbeginValueOn(); } /// Return an iterator over all of this grid's active values (tile and voxel). ValueOnCIter cbeginValueOn() const { return tree().cbeginValueOn(); } /// Return an iterator over all of this grid's inactive values (tile and voxel). ValueOffIter beginValueOff() { return tree().beginValueOff(); } /// Return an iterator over all of this grid's inactive values (tile and voxel). ValueOffCIter beginValueOff() const { return tree().cbeginValueOff(); } /// Return an iterator over all of this grid's inactive values (tile and voxel). ValueOffCIter cbeginValueOff() const { return tree().cbeginValueOff(); } /// Return an iterator over all of this grid's values (tile and voxel). ValueAllIter beginValueAll() { return tree().beginValueAll(); } /// Return an iterator over all of this grid's values (tile and voxel). ValueAllCIter beginValueAll() const { return tree().cbeginValueAll(); } /// Return an iterator over all of this grid's values (tile and voxel). ValueAllCIter cbeginValueAll() const { return tree().cbeginValueAll(); } /// @} /// @name Tools /// @{ /// @brief Set all voxels within a given axis-aligned box to a constant value. /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box /// @param value the value to which to set voxels within the box /// @param active if true, mark voxels within the box as active, /// otherwise mark them as inactive /// @note This operation generates a sparse, but not always optimally sparse, /// representation of the filled box. Follow fill operations with a prune() /// operation for optimal sparseness. void sparseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); /// @brief Set all voxels within a given axis-aligned box to a constant value. /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box /// @param value the value to which to set voxels within the box /// @param active if true, mark voxels within the box as active, /// otherwise mark them as inactive /// @note This operation generates a sparse, but not always optimally sparse, /// representation of the filled box. Follow fill operations with a prune() /// operation for optimal sparseness. void fill(const CoordBBox& bbox, const ValueType& value, bool active = true); /// @brief Set all voxels within a given axis-aligned box to a constant value /// and ensure that those voxels are all represented at the leaf level. /// @param bbox inclusive coordinates of opposite corners of an axis-aligned box. /// @param value the value to which to set voxels within the box. /// @param active if true, mark voxels within the box as active, /// otherwise mark them as inactive. void denseFill(const CoordBBox& bbox, const ValueType& value, bool active = true); /// Reduce the memory footprint of this grid by increasing its sparseness. void pruneGrid(float tolerance = 0.0) override; /// @brief Clip this grid to the given index-space bounding box. /// @details Voxels that lie outside the bounding box are set to the background. /// @warning Clipping a level set will likely produce a grid that is /// no longer a valid level set. void clip(const CoordBBox&) override; /// @brief Efficiently merge another grid into this grid using one of several schemes. /// @details This operation is primarily intended to combine grids that are mostly /// non-overlapping (for example, intermediate grids from computations that are /// parallelized across disjoint regions of space). /// @warning This operation always empties the other grid. void merge(Grid& other, MergePolicy policy = MERGE_ACTIVE_STATES); /// @brief Union this grid's set of active values with the active values /// of the other grid, whose value type may be different. /// @details The resulting state of a value is active if the corresponding value /// was already active OR if it is active in the other grid. Also, a resulting /// value maps to a voxel if the corresponding value already mapped to a voxel /// OR if it is a voxel in the other grid. Thus, a resulting value can only /// map to a tile if the corresponding value already mapped to a tile /// AND if it is a tile value in the other grid. /// /// @note This operation modifies only active states, not values. /// Specifically, active tiles and voxels in this grid are not changed, and /// tiles or voxels that were inactive in this grid but active in the other grid /// are marked as active in this grid but left with their original values. template void topologyUnion(const Grid& other); /// @brief Intersect this grid's set of active values with the active values /// of the other grid, whose value type may be different. /// @details The resulting state of a value is active only if the corresponding /// value was already active AND if it is active in the other tree. Also, a /// resulting value maps to a voxel if the corresponding value /// already mapped to an active voxel in either of the two grids /// and it maps to an active tile or voxel in the other grid. /// /// @note This operation can delete branches of this grid that overlap with /// inactive tiles in the other grid. Also, because it can deactivate voxels, /// it can create leaf nodes with no active values. Thus, it is recommended /// to prune this grid after calling this method. template void topologyIntersection(const Grid& other); /// @brief Difference this grid's set of active values with the active values /// of the other grid, whose value type may be different. /// @details After this method is called, voxels in this grid will be active /// only if they were active to begin with and if the corresponding voxels /// in the other grid were inactive. /// /// @note This operation can delete branches of this grid that overlap with /// active tiles in the other grid. Also, because it can deactivate voxels, /// it can create leaf nodes with no active values. Thus, it is recommended /// to prune this grid after calling this method. template void topologyDifference(const Grid& other); /// @} /// @name Statistics /// @{ /// Return the number of active voxels. Index64 activeVoxelCount() const override { return tree().activeVoxelCount(); } /// Return the axis-aligned bounding box of all active voxels. CoordBBox evalActiveVoxelBoundingBox() const override; /// Return the dimensions of the axis-aligned bounding box of all active voxels. Coord evalActiveVoxelDim() const override; /// Return the minimum and maximum active values in this grid. OPENVDB_DEPRECATED_MESSAGE("Switch from grid->evalMinMax(minVal, maxVal) to \ tools::minMax(grid->tree()). Use threaded = false for serial execution") void evalMinMax(ValueType& minVal, ValueType& maxVal) const; /// Return the number of bytes of memory used by this grid. /// @todo Add transform().memUsage() Index64 memUsage() const override { return tree().memUsage(); } /// @} /// @name Tree /// @{ //@{ /// @brief Return a pointer to this grid's tree, which might be /// shared with other grids. The pointer is guaranteed to be non-null. TreePtrType treePtr() { return mTree; } ConstTreePtrType treePtr() const { return mTree; } ConstTreePtrType constTreePtr() const { return mTree; } TreeBase::ConstPtr constBaseTreePtr() const override { return mTree; } //@} /// @brief Return true if tree is not shared with another grid. /// @note This is a virtual function with ABI=8 bool isTreeUnique() const final; //@{ /// @brief Return a reference to this grid's tree, which might be /// shared with other grids. /// @note Calling setTree() on this grid invalidates all references /// previously returned by this method. TreeType& tree() { return *mTree; } const TreeType& tree() const { return *mTree; } const TreeType& constTree() const { return *mTree; } //@} /// @} /// @name Tree /// @{ /// @brief Associate the given tree with this grid, in place of its existing tree. /// @throw ValueError if the tree pointer is null /// @throw TypeError if the tree is not of type TreeType /// @note Invalidates all references previously returned by baseTree(), /// constBaseTree(), tree() or constTree(). void setTree(TreeBase::Ptr) override; /// @brief Associate a new, empty tree with this grid, in place of its existing tree. /// @note The new tree has the same background value as the existing tree. void newTree() override; /// @} /// @name I/O /// @{ /// @brief Read the grid topology from a stream. /// This will read only the grid structure, not the actual data buffers. void readTopology(std::istream&) override; /// @brief Write the grid topology to a stream. /// This will write only the grid structure, not the actual data buffers. void writeTopology(std::ostream&) const override; /// Read all data buffers for this grid. void readBuffers(std::istream&) override; /// Read all of this grid's data buffers that intersect the given index-space bounding box. void readBuffers(std::istream&, const CoordBBox&) override; /// @brief Read all of this grid's data buffers that are not yet resident in memory /// (because delayed loading is in effect). /// @details If this grid was read from a memory-mapped file, this operation /// disconnects the grid from the file. /// @sa io::File::open, io::MappedFile void readNonresidentBuffers() const override; /// Write out all data buffers for this grid. void writeBuffers(std::ostream&) const override; /// Output a human-readable description of this grid. void print(std::ostream& = std::cout, int verboseLevel = 1) const override; /// @} /// @brief Return @c true if grids of this type require multiple I/O passes /// to read and write data buffers. /// @sa HasMultiPassIO static inline bool hasMultiPassIO(); /// @name Registry /// @{ /// Return @c true if this grid type is registered. static bool isRegistered() { return GridBase::isRegistered(Grid::gridType()); } /// Register this grid type along with a factory function. static void registerGrid() { GridBase::registerGrid(Grid::gridType(), Grid::factory); } /// Remove this grid type from the registry. static void unregisterGrid() { GridBase::unregisterGrid(Grid::gridType()); } /// @} private: /// Deep copy metadata, but share tree and transform. Grid(TreePtrType tree, const MetaMap& meta, math::Transform::Ptr xform); /// Helper function for use with registerGrid() static GridBase::Ptr factory() { return Grid::create(); } TreePtrType mTree; }; // class Grid //////////////////////////////////////// /// @brief Cast a generic grid pointer to a pointer to a grid of a concrete class. /// /// Return a null pointer if the input pointer is null or if it /// points to a grid that is not of type @c GridType. /// /// @note Calling gridPtrCast(grid) is equivalent to calling /// GridBase::grid(grid). template inline typename GridType::Ptr gridPtrCast(const GridBase::Ptr& grid) { return GridBase::grid(grid); } /// @brief Cast a generic const grid pointer to a const pointer to a grid /// of a concrete class. /// /// Return a null pointer if the input pointer is null or if it /// points to a grid that is not of type @c GridType. /// /// @note Calling gridConstPtrCast(grid) is equivalent to calling /// GridBase::constGrid(grid). template inline typename GridType::ConstPtr gridConstPtrCast(const GridBase::ConstPtr& grid) { return GridBase::constGrid(grid); } //////////////////////////////////////// /// @{ /// @brief Return a pointer to a deep copy of the given grid, provided that /// the grid's concrete type is @c GridType. /// /// Return a null pointer if the input pointer is null or if it /// points to a grid that is not of type @c GridType. template inline typename GridType::Ptr deepCopyTypedGrid(const GridBase::ConstPtr& grid) { if (!grid || !grid->isType()) return typename GridType::Ptr(); return gridPtrCast(grid->deepCopyGrid()); } template inline typename GridType::Ptr deepCopyTypedGrid(const GridBase& grid) { if (!grid.isType()) return typename GridType::Ptr(); return gridPtrCast(grid.deepCopyGrid()); } /// @} //////////////////////////////////////// //@{ /// @brief This adapter allows code that is templated on a Tree type to /// accept either a Tree type or a Grid type. 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 tree::ValueAccessor; using ConstAccessorType = typename tree::ValueAccessor; using NonConstAccessorType = typename tree::ValueAccessor; static NonConstTreeType& tree(NonConstTreeType& t) { return t; } static NonConstTreeType& tree(NonConstGridType& g) { return g.tree(); } static const NonConstTreeType& tree(const NonConstTreeType& t) { return t; } static const NonConstTreeType& tree(const NonConstGridType& g) { return g.tree(); } static const NonConstTreeType& constTree(NonConstTreeType& t) { return t; } static const NonConstTreeType& constTree(NonConstGridType& g) { return g.constTree(); } static const NonConstTreeType& constTree(const NonConstTreeType& t) { return t; } static const NonConstTreeType& constTree(const NonConstGridType& g) { return g.constTree(); } }; /// Partial specialization for Grid 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 tree::ValueAccessor; using ConstAccessorType = typename tree::ValueAccessor; using NonConstAccessorType = typename tree::ValueAccessor; static NonConstTreeType& tree(NonConstTreeType& t) { return t; } static NonConstTreeType& tree(NonConstGridType& g) { return g.tree(); } static const NonConstTreeType& tree(const NonConstTreeType& t) { return t; } static const NonConstTreeType& tree(const NonConstGridType& g) { return g.tree(); } static const NonConstTreeType& constTree(NonConstTreeType& t) { return t; } static const NonConstTreeType& constTree(NonConstGridType& g) { return g.constTree(); } static const NonConstTreeType& constTree(const NonConstTreeType& t) { return t; } static const NonConstTreeType& constTree(const NonConstGridType& g) { return g.constTree(); } }; /// Partial specialization for const Grid 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 tree::ValueAccessor; using ConstAccessorType = typename tree::ValueAccessor; using NonConstAccessorType = typename tree::ValueAccessor; static NonConstTreeType& tree(NonConstTreeType& t) { return t; } static NonConstTreeType& tree(NonConstGridType& g) { return g.tree(); } static const NonConstTreeType& tree(const NonConstTreeType& t) { return t; } static const NonConstTreeType& tree(const NonConstGridType& g) { return g.tree(); } static const NonConstTreeType& constTree(NonConstTreeType& t) { return t; } static const NonConstTreeType& constTree(NonConstGridType& g) { return g.constTree(); } static const NonConstTreeType& constTree(const NonConstTreeType& t) { return t; } static const NonConstTreeType& constTree(const NonConstGridType& g) { return g.constTree(); } }; /// Partial specialization for ValueAccessor 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 tree::ValueAccessor; using ConstAccessorType = typename tree::ValueAccessor; using NonConstAccessorType = typename tree::ValueAccessor; 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(); } }; //@} //////////////////////////////////////// /// @brief Metafunction that specifies whether a given leaf node, tree, or grid type /// requires multiple passes to read and write voxel data /// @details Multi-pass I/O allows one to optimize the data layout of leaf nodes /// for certain access patterns during delayed loading. /// @sa io::MultiPass template struct HasMultiPassIO { static const bool value = std::is_base_of::value; }; // Partial specialization for Tree types template struct HasMultiPassIO> { // A tree is multi-pass if its (root node's) leaf node type is multi-pass. static const bool value = HasMultiPassIO::value; }; // Partial specialization for Grid types template struct HasMultiPassIO> { // A grid is multi-pass if its tree's leaf node type is multi-pass. static const bool value = HasMultiPassIO::value; }; //////////////////////////////////////// inline GridBase::GridBase(const MetaMap& meta, math::Transform::Ptr xform) : MetaMap(meta) , mTransform(xform) { if (!xform) OPENVDB_THROW(ValueError, "Transform pointer is null"); } template inline typename GridType::Ptr GridBase::grid(const GridBase::Ptr& grid) { // The string comparison on type names is slower than a dynamic pointer cast, but // it is safer when pointers cross DSO boundaries, as they do in many Houdini nodes. if (grid && grid->type() == GridType::gridType()) { return StaticPtrCast(grid); } return typename GridType::Ptr(); } template inline typename GridType::ConstPtr GridBase::grid(const GridBase::ConstPtr& grid) { return ConstPtrCast( GridBase::grid(ConstPtrCast(grid))); } template inline typename GridType::ConstPtr GridBase::constGrid(const GridBase::Ptr& grid) { return ConstPtrCast(GridBase::grid(grid)); } template inline typename GridType::ConstPtr GridBase::constGrid(const GridBase::ConstPtr& grid) { return ConstPtrCast( GridBase::grid(ConstPtrCast(grid))); } inline TreeBase::Ptr GridBase::baseTreePtr() { return ConstPtrCast(this->constBaseTreePtr()); } inline void GridBase::setTransform(math::Transform::Ptr xform) { if (!xform) OPENVDB_THROW(ValueError, "Transform pointer is null"); mTransform = xform; } //////////////////////////////////////// template inline Grid::Grid(): mTree(new TreeType) { } template inline Grid::Grid(const ValueType &background): mTree(new TreeType(background)) { } template inline Grid::Grid(TreePtrType tree): mTree(tree) { if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); } template inline Grid::Grid(TreePtrType tree, const MetaMap& meta, math::Transform::Ptr xform): GridBase(meta, xform), mTree(tree) { if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); } template inline Grid::Grid(const Grid& other): GridBase(other), mTree(StaticPtrCast(other.mTree->copy())) { } template template inline Grid::Grid(const Grid& other): GridBase(other), mTree(new TreeType(other.constTree())) { } template inline Grid::Grid(Grid& other, ShallowCopy): GridBase(other), mTree(other.mTree) { } template inline Grid::Grid(const GridBase& other): GridBase(other), mTree(new TreeType) { } //static template inline typename Grid::Ptr Grid::create() { return Grid::create(zeroVal()); } //static template inline typename Grid::Ptr Grid::create(const ValueType& background) { return Ptr(new Grid(background)); } //static template inline typename Grid::Ptr Grid::create(TreePtrType tree) { return Ptr(new Grid(tree)); } //static template inline typename Grid::Ptr Grid::create(const GridBase& other) { return Ptr(new Grid(other)); } //////////////////////////////////////// template inline typename Grid::ConstPtr Grid::copy() const { return ConstPtr{new Grid{*const_cast(this), ShallowCopy{}}}; } template inline typename Grid::ConstPtr Grid::copyReplacingMetadata(const MetaMap& meta) const { math::Transform::Ptr transformPtr = ConstPtrCast( this->constTransformPtr()); TreePtrType treePtr = ConstPtrCast(this->constTreePtr()); return ConstPtr{new Grid{treePtr, meta, transformPtr}}; } template inline typename Grid::ConstPtr Grid::copyReplacingTransform(math::Transform::Ptr xform) const { return this->copyReplacingMetadataAndTransform(*this, xform); } template inline typename Grid::ConstPtr Grid::copyReplacingMetadataAndTransform(const MetaMap& meta, math::Transform::Ptr xform) const { TreePtrType treePtr = ConstPtrCast(this->constTreePtr()); return ConstPtr{new Grid{treePtr, meta, xform}}; } template inline typename Grid::Ptr Grid::copy() { return Ptr{new Grid{*this, ShallowCopy{}}}; } template inline typename Grid::Ptr Grid::copyWithNewTree() const { Ptr result{new Grid{*const_cast(this), ShallowCopy{}}}; result->newTree(); return result; } template inline GridBase::Ptr Grid::copyGrid() { return this->copy(); } template inline GridBase::ConstPtr Grid::copyGrid() const { return this->copy(); } template inline GridBase::ConstPtr Grid::copyGridReplacingMetadata(const MetaMap& meta) const { return this->copyReplacingMetadata(meta); } template inline GridBase::ConstPtr Grid::copyGridReplacingTransform(math::Transform::Ptr xform) const { return this->copyReplacingTransform(xform); } template inline GridBase::ConstPtr Grid::copyGridReplacingMetadataAndTransform(const MetaMap& meta, math::Transform::Ptr xform) const { return this->copyReplacingMetadataAndTransform(meta, xform); } template inline GridBase::Ptr Grid::copyGridWithNewTree() const { return this->copyWithNewTree(); } //////////////////////////////////////// template inline bool Grid::isTreeUnique() const { return mTree.use_count() == 1; } template inline void Grid::setTree(TreeBase::Ptr tree) { if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); if (tree->type() != TreeType::treeType()) { OPENVDB_THROW(TypeError, "Cannot assign a tree of type " + tree->type() + " to a grid of type " + this->type()); } mTree = StaticPtrCast(tree); } template inline void Grid::newTree() { mTree.reset(new TreeType(this->background())); } //////////////////////////////////////// template inline void Grid::sparseFill(const CoordBBox& bbox, const ValueType& value, bool active) { tree().sparseFill(bbox, value, active); } template inline void Grid::fill(const CoordBBox& bbox, const ValueType& value, bool active) { this->sparseFill(bbox, value, active); } template inline void Grid::denseFill(const CoordBBox& bbox, const ValueType& value, bool active) { tree().denseFill(bbox, value, active); } template inline void Grid::pruneGrid(float tolerance) { const auto value = math::cwiseAdd(zeroVal(), tolerance); this->tree().prune(static_cast(value)); } template inline void Grid::clip(const CoordBBox& bbox) { tree().clip(bbox); } template inline void Grid::merge(Grid& other, MergePolicy policy) { tree().merge(other.tree(), policy); } template template inline void Grid::topologyUnion(const Grid& other) { tree().topologyUnion(other.tree()); } template template inline void Grid::topologyIntersection(const Grid& other) { tree().topologyIntersection(other.tree()); } template template inline void Grid::topologyDifference(const Grid& other) { tree().topologyDifference(other.tree()); } //////////////////////////////////////// template inline void Grid::evalMinMax(ValueType& minVal, ValueType& maxVal) const { OPENVDB_NO_DEPRECATION_WARNING_BEGIN tree().evalMinMax(minVal, maxVal); OPENVDB_NO_DEPRECATION_WARNING_END } template inline CoordBBox Grid::evalActiveVoxelBoundingBox() const { CoordBBox bbox; tree().evalActiveVoxelBoundingBox(bbox); return bbox; } template inline Coord Grid::evalActiveVoxelDim() const { Coord dim; const bool nonempty = tree().evalActiveVoxelDim(dim); return (nonempty ? dim : Coord()); } //////////////////////////////////////// /// @internal Consider using the stream tagging mechanism (see io::Archive) /// to specify the float precision, but note that the setting is per-grid. template inline void Grid::readTopology(std::istream& is) { tree().readTopology(is, saveFloatAsHalf()); } template inline void Grid::writeTopology(std::ostream& os) const { tree().writeTopology(os, saveFloatAsHalf()); } template inline void Grid::readBuffers(std::istream& is) { if (!hasMultiPassIO() || (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_MULTIPASS_IO)) { tree().readBuffers(is, saveFloatAsHalf()); } else { uint16_t numPasses = 1; is.read(reinterpret_cast(&numPasses), sizeof(uint16_t)); const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is); OPENVDB_ASSERT(bool(meta)); for (uint16_t passIndex = 0; passIndex < numPasses; ++passIndex) { uint32_t pass = (uint32_t(numPasses) << 16) | uint32_t(passIndex); meta->setPass(pass); tree().readBuffers(is, saveFloatAsHalf()); } } } /// @todo Refactor this and the readBuffers() above /// once support for ABI 2 compatibility is dropped. template inline void Grid::readBuffers(std::istream& is, const CoordBBox& bbox) { if (!hasMultiPassIO() || (io::getFormatVersion(is) < OPENVDB_FILE_VERSION_MULTIPASS_IO)) { tree().readBuffers(is, bbox, saveFloatAsHalf()); } else { uint16_t numPasses = 1; is.read(reinterpret_cast(&numPasses), sizeof(uint16_t)); const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(is); OPENVDB_ASSERT(bool(meta)); for (uint16_t passIndex = 0; passIndex < numPasses; ++passIndex) { uint32_t pass = (uint32_t(numPasses) << 16) | uint32_t(passIndex); meta->setPass(pass); tree().readBuffers(is, saveFloatAsHalf()); } // Cannot clip inside readBuffers() when using multiple passes, // so instead clip afterwards. tree().clip(bbox); } } template inline void Grid::readNonresidentBuffers() const { tree().readNonresidentBuffers(); } template inline void Grid::writeBuffers(std::ostream& os) const { if (!hasMultiPassIO()) { tree().writeBuffers(os, saveFloatAsHalf()); } else { // Determine how many leaf buffer passes are required for this grid const io::StreamMetadata::Ptr meta = io::getStreamMetadataPtr(os); OPENVDB_ASSERT(bool(meta)); uint16_t numPasses = 1; meta->setCountingPasses(true); meta->setPass(0); tree().writeBuffers(os, saveFloatAsHalf()); numPasses = static_cast(meta->pass()); os.write(reinterpret_cast(&numPasses), sizeof(uint16_t)); meta->setCountingPasses(false); // Save out the data blocks of the grid. for (uint16_t passIndex = 0; passIndex < numPasses; ++passIndex) { uint32_t pass = (uint32_t(numPasses) << 16) | uint32_t(passIndex); meta->setPass(pass); tree().writeBuffers(os, saveFloatAsHalf()); } } } //static template inline bool Grid::hasMultiPassIO() { return HasMultiPassIO::value; } template inline void Grid::print(std::ostream& os, int verboseLevel) const { tree().print(os, verboseLevel); if (metaCount() > 0) { os << "Additional metadata:" << std::endl; for (ConstMetaIterator it = beginMeta(), end = endMeta(); it != end; ++it) { os << " " << it->first; if (it->second) { const std::string value = it->second->str(); if (!value.empty()) os << ": " << value; } os << "\n"; } } os << "Transform:" << std::endl; transform().print(os, /*indent=*/" "); os << std::endl; } //////////////////////////////////////// template inline typename GridType::Ptr createGrid(const typename GridType::ValueType& background) { return GridType::create(background); } template inline typename GridType::Ptr createGrid() { return GridType::create(); } template inline typename Grid::Ptr createGrid(TreePtrType tree) { using TreeType = typename TreePtrType::element_type; return Grid::create(tree); } template typename GridType::Ptr createLevelSet(Real voxelSize, Real halfWidth) { using ValueType = typename GridType::ValueType; // GridType::ValueType is required to be a floating-point scalar. static_assert(std::is_floating_point::value, "level-set grids must be floating-point-valued"); typename GridType::Ptr grid = GridType::create( /*background=*/static_cast(voxelSize * halfWidth)); grid->setTransform(math::Transform::createLinearTransform(voxelSize)); grid->setGridClass(GRID_LEVEL_SET); return grid; } //////////////////////////////////////// template inline bool GridBase::apply(OpT& op) const { return GridTypeListT::template apply(std::ref(op), *this); } template inline bool GridBase::apply(OpT& op) { return GridTypeListT::template apply(std::ref(op), *this); } template inline bool GridBase::apply(const OpT& op) const { return GridTypeListT::template apply(std::ref(op), *this); } template inline bool GridBase::apply(const OpT& op) { return GridTypeListT::template apply(std::ref(op), *this); } } // namespace OPENVDB_VERSION_NAME } // namespace openvdb #endif // OPENVDB_GRID_HAS_BEEN_INCLUDED