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 |