| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @file Dense.h | ||
| 5 | /// | ||
| 6 | /// @brief This file defines a simple dense grid and efficient | ||
| 7 | /// converters to and from VDB grids. | ||
| 8 | |||
| 9 | #ifndef OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED | ||
| 10 | #define OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED | ||
| 11 | |||
| 12 | #include <openvdb/Types.h> | ||
| 13 | #include <openvdb/Grid.h> | ||
| 14 | #include <openvdb/tree/ValueAccessor.h> | ||
| 15 | #include <openvdb/Exceptions.h> | ||
| 16 | #include <openvdb/util/Formats.h> | ||
| 17 | #include "Prune.h" | ||
| 18 | #include <tbb/parallel_for.h> | ||
| 19 | #include <iostream> | ||
| 20 | #include <memory> | ||
| 21 | #include <string> | ||
| 22 | #include <utility> // for std::pair | ||
| 23 | #include <vector> | ||
| 24 | |||
| 25 | namespace openvdb { | ||
| 26 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 27 | namespace OPENVDB_VERSION_NAME { | ||
| 28 | namespace tools { | ||
| 29 | |||
| 30 | /// @brief Populate a dense grid with the values of voxels from a sparse grid, | ||
| 31 | /// where the sparse grid intersects the dense grid. | ||
| 32 | /// @param sparse an OpenVDB grid or tree from which to copy values | ||
| 33 | /// @param dense the dense grid into which to copy values | ||
| 34 | /// @param serial if false, process voxels in parallel | ||
| 35 | template<typename DenseT, typename GridOrTreeT> | ||
| 36 | void | ||
| 37 | copyToDense( | ||
| 38 | const GridOrTreeT& sparse, | ||
| 39 | DenseT& dense, | ||
| 40 | bool serial = false); | ||
| 41 | |||
| 42 | |||
| 43 | /// @brief Populate a sparse grid with the values of all of the voxels of a dense grid. | ||
| 44 | /// @param dense the dense grid from which to copy values | ||
| 45 | /// @param sparse an OpenVDB grid or tree into which to copy values | ||
| 46 | /// @param tolerance values in the dense grid that are within this tolerance of the sparse | ||
| 47 | /// grid's background value become inactive background voxels or tiles in the sparse grid | ||
| 48 | /// @param serial if false, process voxels in parallel | ||
| 49 | template<typename DenseT, typename GridOrTreeT> | ||
| 50 | void | ||
| 51 | copyFromDense( | ||
| 52 | const DenseT& dense, | ||
| 53 | GridOrTreeT& sparse, | ||
| 54 | const typename GridOrTreeT::ValueType& tolerance, | ||
| 55 | bool serial = false); | ||
| 56 | |||
| 57 | |||
| 58 | //////////////////////////////////////// | ||
| 59 | |||
| 60 | /// We currently support the following two 3D memory layouts for dense | ||
| 61 | /// volumes: XYZ, i.e. x is the fastest moving index, and ZYX, i.e. z | ||
| 62 | /// is the fastest moving index. The ZYX memory layout leads to nested | ||
| 63 | /// for-loops of the order x, y, z, which we find to be the most | ||
| 64 | /// intuitive. Hence, ZYX is the layout used throughout VDB. However, | ||
| 65 | /// other data structures, e.g. Houdini and Maya, employ the XYZ | ||
| 66 | /// layout. Clearly a dense volume with the ZYX layout converts more | ||
| 67 | /// efficiently to a VDB, but we support both for convenience. | ||
| 68 | enum MemoryLayout { LayoutXYZ, LayoutZYX }; | ||
| 69 | |||
| 70 | /// @brief Base class for Dense which is defined below. | ||
| 71 | /// @note The constructor of this class is protected to prevent direct | ||
| 72 | /// instantiation. | ||
| 73 | template<typename ValueT, MemoryLayout Layout> class DenseBase; | ||
| 74 | |||
| 75 | /// @brief Partial template specialization of DenseBase. | ||
| 76 | /// @note ZYX is the memory-layout in VDB. It leads to nested | ||
| 77 | /// for-loops of the order x, y, z which we find to be the most intuitive. | ||
| 78 | template<typename ValueT> | ||
| 79 | class DenseBase<ValueT, LayoutZYX> | ||
| 80 | { | ||
| 81 | public: | ||
| 82 | /// @brief Return the linear offset into this grid's value array given by | ||
| 83 | /// unsigned coordinates (i, j, k), i.e., coordinates relative to | ||
| 84 | /// the origin of this grid's bounding box. | ||
| 85 | /// | ||
| 86 | /// @warning The input coordinates are assume to be relative to | ||
| 87 | /// the grid's origin, i.e. minimum of its index bounding box! | ||
| 88 | 947241 | inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i*mX + j*mY + k; } | |
| 89 | |||
| 90 | /// @brief Return the local coordinate corresponding to the specified linear offset. | ||
| 91 | /// | ||
| 92 | /// @warning The returned coordinate is relative to the origin of this | ||
| 93 | /// grid's bounding box so add dense.origin() to get absolute coordinates. | ||
| 94 | inline Coord offsetToLocalCoord(size_t n) const | ||
| 95 | { | ||
| 96 | 13260 | const size_t x = n / mX; | |
| 97 | n -= mX*x; | ||
| 98 | 13260 | const size_t y = n / mY; | |
| 99 |
2/4✓ Branch 1 taken 6630 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6630 times.
✗ Branch 5 not taken.
|
13260 | return Coord(Coord::ValueType(x), Coord::ValueType(y), Coord::ValueType(n - mY*y)); |
| 100 | } | ||
| 101 | |||
| 102 | /// @brief Return the stride of the array in the x direction ( = dimY*dimZ). | ||
| 103 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 104 | 3753 | inline size_t xStride() const { return mX; } | |
| 105 | |||
| 106 | /// @brief Return the stride of the array in the y direction ( = dimZ). | ||
| 107 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 108 | 1630 | inline size_t yStride() const { return mY; } | |
| 109 | |||
| 110 | /// @brief Return the stride of the array in the z direction ( = 1). | ||
| 111 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 112 | static size_t zStride() { return 1; } | ||
| 113 | |||
| 114 | protected: | ||
| 115 | /// Protected constructor so as to prevent direct instantiation | ||
| 116 | 48 | DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[2]), mX(mY*bbox.dim()[1]) {} | |
| 117 | |||
| 118 | const CoordBBox mBBox;//signed coordinates of the domain represented by the grid | ||
| 119 | const size_t mY, mX;//strides in the y and x direction | ||
| 120 | };// end of DenseBase<ValueT, LayoutZYX> | ||
| 121 | |||
| 122 | /// @brief Partial template specialization of DenseBase. | ||
| 123 | /// @note This is the memory-layout employed in Houdini and Maya. It leads | ||
| 124 | /// to nested for-loops of the order z, y, x. | ||
| 125 | template<typename ValueT> | ||
| 126 | class DenseBase<ValueT, LayoutXYZ> | ||
| 127 | { | ||
| 128 | public: | ||
| 129 | /// @brief Return the linear offset into this grid's value array given by | ||
| 130 | /// unsigned coordinates (i, j, k), i.e., coordinates relative to | ||
| 131 | /// the origin of this grid's bounding box. | ||
| 132 | /// | ||
| 133 | /// @warning The input coordinates are assume to be relative to | ||
| 134 | /// the grid's origin, i.e. minimum of its index bounding box! | ||
| 135 | 662791 | inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i + j*mY + k*mZ; } | |
| 136 | |||
| 137 | /// @brief Return the index coordinate corresponding to the specified linear offset. | ||
| 138 | /// | ||
| 139 | /// @warning The returned coordinate is relative to the origin of this | ||
| 140 | /// grid's bounding box so add dense.origin() to get absolute coordinates. | ||
| 141 | inline Coord offsetToLocalCoord(size_t n) const | ||
| 142 | { | ||
| 143 | 13260 | const size_t z = n / mZ; | |
| 144 | n -= mZ*z; | ||
| 145 | 13260 | const size_t y = n / mY; | |
| 146 |
2/4✓ Branch 1 taken 6630 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6630 times.
✗ Branch 5 not taken.
|
13260 | return Coord(Coord::ValueType(n - mY*y), Coord::ValueType(y), Coord::ValueType(z)); |
| 147 | } | ||
| 148 | |||
| 149 | /// @brief Return the stride of the array in the x direction ( = 1). | ||
| 150 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 151 | static size_t xStride() { return 1; } | ||
| 152 | |||
| 153 | /// @brief Return the stride of the array in the y direction ( = dimX). | ||
| 154 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 155 | 3794 | inline size_t yStride() const { return mY; } | |
| 156 | |||
| 157 | /// @brief Return the stride of the array in the y direction ( = dimX*dimY). | ||
| 158 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 159 | 1666 | inline size_t zStride() const { return mZ; } | |
| 160 | |||
| 161 | protected: | ||
| 162 | /// Protected constructor so as to prevent direct instantiation | ||
| 163 | 29 | DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[0]), mZ(mY*bbox.dim()[1]) {} | |
| 164 | |||
| 165 | const CoordBBox mBBox;//signed coordinates of the domain represented by the grid | ||
| 166 | const size_t mY, mZ;//strides in the y and z direction | ||
| 167 | };// end of DenseBase<ValueT, LayoutXYZ> | ||
| 168 | |||
| 169 | /// @brief Dense is a simple dense grid API used by the CopyToDense and | ||
| 170 | /// CopyFromDense classes defined below. | ||
| 171 | /// @details Use the Dense class to efficiently produce a dense in-memory | ||
| 172 | /// representation of an OpenVDB grid. However, be aware that a dense grid | ||
| 173 | /// could have a memory footprint that is orders of magnitude larger than | ||
| 174 | /// the sparse grid from which it originates. | ||
| 175 | /// | ||
| 176 | /// @note This class can be used as a simple wrapper for existing dense grid | ||
| 177 | /// classes if they provide access to the raw data array. | ||
| 178 | /// @note This implementation allows for the 3D memory layout to be | ||
| 179 | /// defined by the MemoryLayout template parameter (see above for definition). | ||
| 180 | /// The default memory layout is ZYX since that's the layout used by OpenVDB grids. | ||
| 181 | template<typename ValueT, MemoryLayout Layout = LayoutZYX> | ||
| 182 |
6/12✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
|
12 | class Dense : public DenseBase<ValueT, Layout> |
| 183 | { | ||
| 184 | public: | ||
| 185 | using ValueType = ValueT; | ||
| 186 | using BaseT = DenseBase<ValueT, Layout>; | ||
| 187 | using Ptr = SharedPtr<Dense>; | ||
| 188 | using ConstPtr = SharedPtr<const Dense>; | ||
| 189 | |||
| 190 | /// @brief Construct a dense grid with a given range of coordinates. | ||
| 191 | /// | ||
| 192 | /// @param bbox the bounding box of the (signed) coordinate range of this grid | ||
| 193 | /// @throw ValueError if the bounding box is empty. | ||
| 194 | /// @note The min and max coordinates of the bounding box are inclusive. | ||
| 195 |
2/2✓ Branch 2 taken 10 times.
✓ Branch 3 taken 2 times.
|
24 | Dense(const CoordBBox& bbox) : BaseT(bbox) { this->init(); } |
| 196 | |||
| 197 | /// @brief Construct a dense grid with a given range of coordinates and initial value | ||
| 198 | /// | ||
| 199 | /// @param bbox the bounding box of the (signed) coordinate range of this grid | ||
| 200 | /// @param value the initial value of the grid. | ||
| 201 | /// @throw ValueError if the bounding box is empty. | ||
| 202 | /// @note The min and max coordinates of the bounding box are inclusive. | ||
| 203 | 46 | Dense(const CoordBBox& bbox, const ValueT& value) : BaseT(bbox) | |
| 204 | { | ||
| 205 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
46 | this->init(); |
| 206 | 46 | this->fill(value); | |
| 207 | 46 | } | |
| 208 | |||
| 209 | /// @brief Construct a dense grid that wraps an external array. | ||
| 210 | /// | ||
| 211 | /// @param bbox the bounding box of the (signed) coordinate range of this grid | ||
| 212 | /// @param data a raw C-style array whose size is commensurate with | ||
| 213 | /// the coordinate domain of @a bbox | ||
| 214 | /// | ||
| 215 | /// @note The data array is assumed to have a stride of one in the @e z direction. | ||
| 216 | /// @throw ValueError if the bounding box is empty. | ||
| 217 | /// @note The min and max coordinates of the bounding box are inclusive. | ||
| 218 | Dense(const CoordBBox& bbox, ValueT* data) : BaseT(bbox), mData(data) | ||
| 219 | { | ||
| 220 | if (BaseT::mBBox.empty()) { | ||
| 221 | OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | /// @brief Construct a dense grid with a given origin and dimensions. | ||
| 226 | /// | ||
| 227 | /// @param dim the desired dimensions of the grid | ||
| 228 | /// @param min the signed coordinates of the first voxel in the dense grid | ||
| 229 | /// @throw ValueError if any of the dimensions are zero. | ||
| 230 | /// @note The @a min coordinate is inclusive, and the max coordinate will be | ||
| 231 | /// @a min + @a dim - 1. | ||
| 232 | 8 | Dense(const Coord& dim, const Coord& min = Coord(0)) | |
| 233 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
8 | : BaseT(CoordBBox(min, min+dim.offsetBy(-1))) |
| 234 | { | ||
| 235 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | this->init(); |
| 236 | 8 | } | |
| 237 | |||
| 238 | /// @brief Return the memory layout for this grid (see above for definitions). | ||
| 239 | static MemoryLayout memoryLayout() { return Layout; } | ||
| 240 | |||
| 241 | /// @brief Return a raw pointer to this grid's value array. | ||
| 242 | /// @note This method is required by CopyToDense. | ||
| 243 | 4210 | inline ValueT* data() { return mData; } | |
| 244 | |||
| 245 | /// @brief Return a raw pointer to this grid's value array. | ||
| 246 | /// @note This method is required by CopyFromDense. | ||
| 247 | 1184 | inline const ValueT* data() const { return mData; } | |
| 248 | |||
| 249 | /// @brief Return the bounding box of the signed index domain of this grid. | ||
| 250 | /// @note This method is required by both CopyToDense and CopyFromDense. | ||
| 251 | 12 | inline const CoordBBox& bbox() const { return BaseT::mBBox; } | |
| 252 | |||
| 253 | /// Return the grid's origin in index coordinates. | ||
| 254 | inline const Coord& origin() const { return BaseT::mBBox.min(); } | ||
| 255 | |||
| 256 | /// @brief Return the number of voxels contained in this grid. | ||
| 257 | 41 | inline Index64 valueCount() const { return BaseT::mBBox.volume(); } | |
| 258 | |||
| 259 | /// @brief Set the value of the voxel at the given array offset. | ||
| 260 | inline void setValue(size_t offset, const ValueT& value) { mData[offset] = value; } | ||
| 261 | |||
| 262 | /// @brief Return a const reference to the value of the voxel at the given array offset. | ||
| 263 | const ValueT& getValue(size_t offset) const { return mData[offset]; } | ||
| 264 | |||
| 265 | /// @brief Return a non-const reference to the value of the voxel at the given array offset. | ||
| 266 |
2/4✓ Branch 1 taken 6630 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6630 times.
✗ Branch 5 not taken.
|
13260 | ValueT& getValue(size_t offset) { return mData[offset]; } |
| 267 | |||
| 268 | /// @brief Set the value of the voxel at unsigned index coordinates (i, j, k). | ||
| 269 | /// @note This is somewhat slower than using an array offset. | ||
| 270 | inline void setValue(size_t i, size_t j, size_t k, const ValueT& value) | ||
| 271 | { | ||
| 272 | 2 | mData[BaseT::coordToOffset(i,j,k)] = value; | |
| 273 | } | ||
| 274 | |||
| 275 | /// @brief Return a const reference to the value of the voxel | ||
| 276 | /// at unsigned index coordinates (i, j, k). | ||
| 277 | /// @note This is somewhat slower than using an array offset. | ||
| 278 | inline const ValueT& getValue(size_t i, size_t j, size_t k) const | ||
| 279 | { | ||
| 280 | return mData[BaseT::coordToOffset(i,j,k)]; | ||
| 281 | } | ||
| 282 | |||
| 283 | /// @brief Return a non-const reference to the value of the voxel | ||
| 284 | /// at unsigned index coordinates (i, j, k). | ||
| 285 | /// @note This is somewhat slower than using an array offset. | ||
| 286 | inline ValueT& getValue(size_t i, size_t j, size_t k) | ||
| 287 | { | ||
| 288 | return mData[BaseT::coordToOffset(i,j,k)]; | ||
| 289 | } | ||
| 290 | |||
| 291 | /// @brief Set the value of the voxel at the given signed coordinates. | ||
| 292 | /// @note This is slower than using either an array offset or unsigned index coordinates. | ||
| 293 | inline void setValue(const Coord& xyz, const ValueT& value) | ||
| 294 | { | ||
| 295 |
1/2✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
510 | mData[this->coordToOffset(xyz)] = value; |
| 296 | } | ||
| 297 | |||
| 298 | /// @brief Return a const reference to the value of the voxel at the given signed coordinates. | ||
| 299 | /// @note This is slower than using either an array offset or unsigned index coordinates. | ||
| 300 | inline const ValueT& getValue(const Coord& xyz) const | ||
| 301 | { | ||
| 302 |
2/4✓ Branch 2 taken 352947 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 352947 times.
✗ Branch 7 not taken.
|
1522975 | return mData[this->coordToOffset(xyz)]; |
| 303 | } | ||
| 304 | |||
| 305 | /// @brief Return a non-const reference to the value of the voxel | ||
| 306 | /// at the given signed coordinates. | ||
| 307 | /// @note This is slower than using either an array offset or unsigned index coordinates. | ||
| 308 | inline ValueT& getValue(const Coord& xyz) | ||
| 309 | { | ||
| 310 |
29/43✓ Branch 5 taken 1260 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 3600 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 500 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 1 times.
✓ Branch 18 taken 1260 times.
✗ Branch 19 not taken.
✓ Branch 21 taken 1 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 3600 times.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1000 times.
✓ Branch 29 taken 1 times.
✗ Branch 30 not taken.
✓ Branch 32 taken 1000 times.
✓ Branch 33 taken 1 times.
✗ Branch 34 not taken.
✓ Branch 36 taken 1000 times.
✓ Branch 37 taken 1 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 1000 times.
✓ Branch 41 taken 105 times.
✗ Branch 42 not taken.
✓ Branch 44 taken 1000 times.
✓ Branch 45 taken 219 times.
✗ Branch 46 not taken.
✓ Branch 48 taken 1000 times.
✗ Branch 49 not taken.
✓ Branch 52 taken 6630 times.
✗ Branch 53 not taken.
✓ Branch 55 taken 221 times.
✓ Branch 56 taken 6409 times.
✓ Branch 58 taken 221 times.
✓ Branch 59 taken 6409 times.
✓ Branch 62 taken 6630 times.
✗ Branch 63 not taken.
✓ Branch 65 taken 221 times.
✓ Branch 66 taken 6409 times.
✓ Branch 68 taken 221 times.
✓ Branch 69 taken 6409 times.
|
63567 | return mData[this->coordToOffset(xyz)]; |
| 311 | } | ||
| 312 | |||
| 313 | /// @brief Fill this grid with a constant value. | ||
| 314 | 82 | inline void fill(const ValueT& value) | |
| 315 | { | ||
| 316 | size_t size = this->valueCount(); | ||
| 317 | 82 | ValueT* a = mData; | |
| 318 |
2/2✓ Branch 0 taken 14614926 times.
✓ Branch 1 taken 41 times.
|
29229934 | while(size--) *a++ = value; |
| 319 | 82 | } | |
| 320 | |||
| 321 | /// @brief Return the linear offset into this grid's value array given by | ||
| 322 | /// the specified signed coordinates, i.e., coordinates in the space of | ||
| 323 | /// this grid's bounding box. | ||
| 324 | /// | ||
| 325 | /// @note This method reflects the fact that we assume the same | ||
| 326 | /// layout of values as an OpenVDB grid, i.e., the fastest coordinate is @e z. | ||
| 327 |
1/2✓ Branch 0 taken 1610030 times.
✗ Branch 1 not taken.
|
3220060 | inline size_t coordToOffset(const Coord& xyz) const |
| 328 | { | ||
| 329 | ✗ | assert(BaseT::mBBox.isInside(xyz)); | |
| 330 | 3220060 | return BaseT::coordToOffset(size_t(xyz[0]-BaseT::mBBox.min()[0]), | |
| 331 | 3220060 | size_t(xyz[1]-BaseT::mBBox.min()[1]), | |
| 332 | 3220060 | size_t(xyz[2]-BaseT::mBBox.min()[2])); | |
| 333 | } | ||
| 334 | |||
| 335 | /// @brief Return the global coordinate corresponding to the specified linear offset. | ||
| 336 | inline Coord offsetToCoord(size_t n) const | ||
| 337 | { | ||
| 338 | return this->offsetToLocalCoord(n) + BaseT::mBBox.min(); | ||
| 339 | } | ||
| 340 | |||
| 341 | /// @brief Return the memory footprint of this Dense grid in bytes. | ||
| 342 | inline Index64 memUsage() const | ||
| 343 | { | ||
| 344 | return sizeof(*this) + BaseT::mBBox.volume() * sizeof(ValueType); | ||
| 345 | } | ||
| 346 | |||
| 347 | /// @brief Output a human-readable description of this grid to the | ||
| 348 | /// specified stream. | ||
| 349 | void print(const std::string& name = "", std::ostream& os = std::cout) const | ||
| 350 | { | ||
| 351 | const Coord dim = BaseT::mBBox.dim(); | ||
| 352 | os << "Dense Grid"; | ||
| 353 | if (!name.empty()) os << " \"" << name << "\""; | ||
| 354 | util::printBytes(os, this->memUsage(), ":\n Memory footprint: "); | ||
| 355 | os << " Dimensions of grid : " << dim[0] << " x " << dim[1] << " x " << dim[2] << "\n"; | ||
| 356 | os << " Number of voxels: " << util::formattedInt(this->valueCount()) << "\n"; | ||
| 357 | os << " Bounding box of voxels: " << BaseT::mBBox << "\n"; | ||
| 358 | os << " Memory layout: " << (Layout == LayoutZYX ? "ZYX (" : "XYZ (dis") | ||
| 359 | << "similar to VDB)\n"; | ||
| 360 | } | ||
| 361 | |||
| 362 | private: | ||
| 363 | /// @brief Private method to initialize the dense value array. | ||
| 364 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 2 times.
|
78 | void init() |
| 365 | { | ||
| 366 | if (BaseT::mBBox.empty()) { | ||
| 367 |
2/6✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
16 | OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); |
| 368 | } | ||
| 369 |
1/2✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
|
74 | mArray.reset(new ValueT[BaseT::mBBox.volume()]); |
| 370 | 74 | mData = mArray.get(); | |
| 371 | 74 | } | |
| 372 | |||
| 373 | std::unique_ptr<ValueT[]> mArray; | ||
| 374 | ValueT* mData;//raw c-style pointer to values | ||
| 375 | };// end of Dense | ||
| 376 | |||
| 377 | //////////////////////////////////////// | ||
| 378 | |||
| 379 | |||
| 380 | /// @brief Copy an OpenVDB tree into an existing dense grid. | ||
| 381 | /// | ||
| 382 | /// @note Only voxels that intersect the dense grid's bounding box are copied | ||
| 383 | /// from the OpenVDB tree. But both active and inactive voxels are copied, | ||
| 384 | /// so all existing values in the dense grid are overwritten, regardless of | ||
| 385 | /// the OpenVDB tree's topology. | ||
| 386 | template<typename _TreeT, typename _DenseT = Dense<typename _TreeT::ValueType> > | ||
| 387 | class CopyToDense | ||
| 388 | { | ||
| 389 | public: | ||
| 390 | using DenseT = _DenseT; | ||
| 391 | using TreeT = _TreeT; | ||
| 392 | using ValueT = typename TreeT::ValueType; | ||
| 393 | |||
| 394 | 8 | CopyToDense(const TreeT& tree, DenseT& dense) | |
| 395 | 8 | : mRoot(&(tree.root())), mDense(&dense) {} | |
| 396 | |||
| 397 | 16 | void copy(bool serial = false) const | |
| 398 | { | ||
| 399 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
16 | if (serial) { |
| 400 | 4 | mRoot->copyToDense(mDense->bbox(), *mDense); | |
| 401 | } else { | ||
| 402 | 12 | tbb::parallel_for(mDense->bbox(), *this); | |
| 403 | } | ||
| 404 | 16 | } | |
| 405 | |||
| 406 | /// @brief Public method called by tbb::parallel_for | ||
| 407 | void operator()(const CoordBBox& bbox) const | ||
| 408 | { | ||
| 409 |
2/4✓ Branch 2 taken 249 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 262 times.
✗ Branch 7 not taken.
|
816 | mRoot->copyToDense(bbox, *mDense); |
| 410 | } | ||
| 411 | |||
| 412 | private: | ||
| 413 | const typename TreeT::RootNodeType* mRoot; | ||
| 414 | DenseT* mDense; | ||
| 415 | };// CopyToDense | ||
| 416 | |||
| 417 | |||
| 418 | // Convenient wrapper function for the CopyToDense class | ||
| 419 | template<typename DenseT, typename GridOrTreeT> | ||
| 420 | void | ||
| 421 | 16 | copyToDense(const GridOrTreeT& sparse, DenseT& dense, bool serial) | |
| 422 | { | ||
| 423 | using Adapter = TreeAdapter<GridOrTreeT>; | ||
| 424 | using TreeT = typename Adapter::TreeType; | ||
| 425 | |||
| 426 | CopyToDense<TreeT, DenseT> op(Adapter::constTree(sparse), dense); | ||
| 427 | 16 | op.copy(serial); | |
| 428 | 16 | } | |
| 429 | |||
| 430 | |||
| 431 | //////////////////////////////////////// | ||
| 432 | |||
| 433 | |||
| 434 | /// @brief Copy the values from a dense grid into an OpenVDB tree. | ||
| 435 | /// | ||
| 436 | /// @details Values in the dense grid that are within a tolerance of | ||
| 437 | /// the background value are truncated to inactive background voxels or tiles. | ||
| 438 | /// This allows the tree to form a sparse representation of the dense grid. | ||
| 439 | /// | ||
| 440 | /// @note Since this class allocates leaf nodes concurrently it is recommended | ||
| 441 | /// to use a scalable implementation of @c new like the one provided by TBB, | ||
| 442 | /// rather than the mutex-protected standard library @c new. | ||
| 443 | template<typename _TreeT, typename _DenseT = Dense<typename _TreeT::ValueType> > | ||
| 444 | ✗ | class CopyFromDense | |
| 445 | { | ||
| 446 | public: | ||
| 447 | using DenseT = _DenseT; | ||
| 448 | using TreeT = _TreeT; | ||
| 449 | using ValueT = typename TreeT::ValueType; | ||
| 450 | using LeafT = typename TreeT::LeafNodeType; | ||
| 451 | using AccessorT = tree::ValueAccessor<TreeT>; | ||
| 452 | |||
| 453 | 48 | CopyFromDense(const DenseT& dense, TreeT& tree, const ValueT& tolerance) | |
| 454 | : mDense(&dense), | ||
| 455 | mTree(&tree), | ||
| 456 | mBlocks(nullptr), | ||
| 457 | mTolerance(tolerance), | ||
| 458 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
|
48 | mAccessor(tree.empty() ? nullptr : new AccessorT(tree)) |
| 459 | { | ||
| 460 | 48 | } | |
| 461 | 516 | CopyFromDense(const CopyFromDense& other) | |
| 462 | 516 | : mDense(other.mDense), | |
| 463 | 516 | mTree(other.mTree), | |
| 464 | 516 | mBlocks(other.mBlocks), | |
| 465 | 516 | mTolerance(other.mTolerance), | |
| 466 |
3/4✓ Branch 0 taken 20 times.
✓ Branch 1 taken 238 times.
✓ Branch 4 taken 20 times.
✗ Branch 5 not taken.
|
516 | mAccessor(other.mAccessor.get() == nullptr ? nullptr : new AccessorT(*mTree)) |
| 467 | { | ||
| 468 | 516 | } | |
| 469 | |||
| 470 | /// @brief Copy values from the dense grid to the sparse tree. | ||
| 471 | 48 | void copy(bool serial = false) | |
| 472 | { | ||
| 473 | 48 | mBlocks = new std::vector<Block>(); | |
| 474 | 48 | const CoordBBox& bbox = mDense->bbox(); | |
| 475 | // Pre-process: Construct a list of blocks aligned with (potential) leaf nodes | ||
| 476 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 24 times.
|
192 | for (CoordBBox sub=bbox; sub.min()[0] <= bbox.max()[0]; sub.min()[0] = sub.max()[0] + 1) { |
| 477 |
2/2✓ Branch 0 taken 248 times.
✓ Branch 1 taken 72 times.
|
640 | for (sub.min()[1] = bbox.min()[1]; sub.min()[1] <= bbox.max()[1]; |
| 478 | 496 | sub.min()[1] = sub.max()[1] + 1) | |
| 479 | { | ||
| 480 |
2/2✓ Branch 0 taken 1184 times.
✓ Branch 1 taken 248 times.
|
2864 | for (sub.min()[2] = bbox.min()[2]; sub.min()[2] <= bbox.max()[2]; |
| 481 | 2368 | sub.min()[2] = sub.max()[2] + 1) | |
| 482 | { | ||
| 483 | 2368 | sub.max() = Coord::minComponent(bbox.max(), | |
| 484 | (sub.min()&(~(LeafT::DIM-1u))).offsetBy(LeafT::DIM-1u)); | ||
| 485 | 2368 | mBlocks->push_back(Block(sub)); | |
| 486 | } | ||
| 487 | } | ||
| 488 | } | ||
| 489 | |||
| 490 | // Multi-threaded process: Convert dense grid into leaf nodes and tiles | ||
| 491 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
|
48 | if (serial) { |
| 492 | 12 | (*this)(tbb::blocked_range<size_t>(0, mBlocks->size())); | |
| 493 | } else { | ||
| 494 | 36 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mBlocks->size()), *this); | |
| 495 | } | ||
| 496 | |||
| 497 | // Post-process: Insert leaf nodes and tiles into the tree, and prune the tiles only! | ||
| 498 | 48 | tree::ValueAccessor<TreeT> acc(*mTree); | |
| 499 |
2/2✓ Branch 0 taken 1184 times.
✓ Branch 1 taken 24 times.
|
2416 | for (size_t m=0, size = mBlocks->size(); m<size; ++m) { |
| 500 |
2/2✓ Branch 0 taken 852 times.
✓ Branch 1 taken 332 times.
|
2368 | Block& block = (*mBlocks)[m]; |
| 501 |
2/2✓ Branch 0 taken 852 times.
✓ Branch 1 taken 332 times.
|
2368 | if (block.leaf) { |
| 502 |
1/2✓ Branch 1 taken 852 times.
✗ Branch 2 not taken.
|
1704 | acc.addLeaf(block.leaf); |
| 503 |
2/2✓ Branch 0 taken 62 times.
✓ Branch 1 taken 270 times.
|
664 | } else if (block.tile.second) {//only background tiles are inactive |
| 504 |
1/2✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
|
124 | acc.addTile(1, block.bbox.min(), block.tile.first, true);//leaf tile |
| 505 | } | ||
| 506 | } | ||
| 507 |
1/2✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
|
96 | delete mBlocks; |
| 508 | 48 | mBlocks = nullptr; | |
| 509 | |||
| 510 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
48 | tools::pruneTiles(*mTree, mTolerance);//multi-threaded |
| 511 | 48 | } | |
| 512 | |||
| 513 | /// @brief Public method called by tbb::parallel_for | ||
| 514 | /// @warning Never call this method directly! | ||
| 515 | 1438 | void operator()(const tbb::blocked_range<size_t> &r) const | |
| 516 | { | ||
| 517 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 719 times.
|
1438 | assert(mBlocks); |
| 518 | 1438 | LeafT* leaf = new LeafT(); | |
| 519 | |||
| 520 |
2/2✓ Branch 0 taken 1184 times.
✓ Branch 1 taken 719 times.
|
3806 | for (size_t m=r.begin(), n=0, end = r.end(); m != end; ++m, ++n) { |
| 521 | |||
| 522 |
2/2✓ Branch 0 taken 1160 times.
✓ Branch 1 taken 24 times.
|
2368 | Block& block = (*mBlocks)[m]; |
| 523 |
2/2✓ Branch 0 taken 1160 times.
✓ Branch 1 taken 24 times.
|
2368 | const CoordBBox &bbox = block.bbox; |
| 524 | |||
| 525 |
2/2✓ Branch 0 taken 1160 times.
✓ Branch 1 taken 24 times.
|
2368 | if (mAccessor.get() == nullptr) {//i.e. empty target tree |
| 526 |
1/2✓ Branch 0 taken 54 times.
✗ Branch 1 not taken.
|
2320 | leaf->fill(mTree->background(), false); |
| 527 | } else {//account for existing leaf nodes in the target tree | ||
| 528 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 8 times.
|
48 | if (const LeafT* target = mAccessor->probeConstLeaf(bbox.min())) { |
| 529 | 32 | (*leaf) = (*target); | |
| 530 | } else { | ||
| 531 | 16 | ValueT value = zeroVal<ValueT>(); | |
| 532 | 16 | bool state = mAccessor->probeValue(bbox.min(), value); | |
| 533 | 16 | leaf->fill(value, state); | |
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | 2368 | leaf->copyFromDense(bbox, *mDense, mTree->background(), mTolerance); | |
| 538 | |||
| 539 |
2/2✓ Branch 1 taken 852 times.
✓ Branch 2 taken 332 times.
|
2368 | if (!leaf->isConstant(block.tile.first, block.tile.second, mTolerance)) { |
| 540 | 1704 | leaf->setOrigin(bbox.min() & (~(LeafT::DIM - 1))); | |
| 541 | 1704 | block.leaf = leaf; | |
| 542 | 1704 | leaf = new LeafT(); | |
| 543 | } | ||
| 544 | }// loop over blocks | ||
| 545 | |||
| 546 |
1/2✓ Branch 0 taken 719 times.
✗ Branch 1 not taken.
|
2768 | delete leaf; |
| 547 | 1438 | } | |
| 548 | |||
| 549 | private: | ||
| 550 | struct Block { | ||
| 551 | CoordBBox bbox; | ||
| 552 | LeafT* leaf; | ||
| 553 | std::pair<ValueT, bool> tile; | ||
| 554 | 1184 | Block(const CoordBBox& b) : bbox(b), leaf(nullptr) {} | |
| 555 | }; | ||
| 556 | |||
| 557 | const DenseT* mDense; | ||
| 558 | TreeT* mTree; | ||
| 559 | std::vector<Block>* mBlocks; | ||
| 560 | ValueT mTolerance; | ||
| 561 | std::unique_ptr<AccessorT> mAccessor; | ||
| 562 | };// CopyFromDense | ||
| 563 | |||
| 564 | |||
| 565 | // Convenient wrapper function for the CopyFromDense class | ||
| 566 | template<typename DenseT, typename GridOrTreeT> | ||
| 567 | void | ||
| 568 | 48 | copyFromDense(const DenseT& dense, GridOrTreeT& sparse, | |
| 569 | const typename GridOrTreeT::ValueType& tolerance, bool serial) | ||
| 570 | { | ||
| 571 | using Adapter = TreeAdapter<GridOrTreeT>; | ||
| 572 | using TreeT = typename Adapter::TreeType; | ||
| 573 | |||
| 574 | 48 | CopyFromDense<TreeT, DenseT> op(dense, Adapter::tree(sparse), tolerance); | |
| 575 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
48 | op.copy(serial); |
| 576 | 48 | } | |
| 577 | |||
| 578 | } // namespace tools | ||
| 579 | } // namespace OPENVDB_VERSION_NAME | ||
| 580 | } // namespace openvdb | ||
| 581 | |||
| 582 | #endif // OPENVDB_TOOLS_DENSE_HAS_BEEN_INCLUDED | ||
| 583 |