Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright Contributors to the OpenVDB Project | ||
2 | // SPDX-License-Identifier: MPL-2.0 | ||
3 | |||
4 | /// @file MultiResGrid.h | ||
5 | /// | ||
6 | /// @author Ken Museth | ||
7 | /// | ||
8 | /// @warning This class is fairly new and as such has not seen a lot of | ||
9 | /// use in production. Please report any issues or request for new | ||
10 | /// features directly to ken.museth@dreamworks.com. | ||
11 | /// | ||
12 | /// @brief Multi-resolution grid that contains LoD sequences of trees | ||
13 | /// with powers of two refinements. | ||
14 | /// | ||
15 | /// @note While this class can arguably be used to implement a sparse | ||
16 | /// Multi-Grid solver it is currently intended as a means to | ||
17 | /// efficiently compute LoD levels for applications like rendering | ||
18 | /// | ||
19 | /// @note Prolongation means interpolation from coarse -> fine | ||
20 | /// @note Restriction means interpolation (or remapping) from fine -> coarse | ||
21 | /// | ||
22 | /// @todo Add option to define the level of the input grid (currenlty | ||
23 | /// 0) so as to allow for super-sampling. | ||
24 | |||
25 | #ifndef OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED | ||
26 | #define OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED | ||
27 | |||
28 | #include <openvdb/Grid.h> | ||
29 | #include <openvdb/math/FiniteDifference.h> | ||
30 | #include <openvdb/math/Math.h> | ||
31 | #include <openvdb/math/Operators.h> | ||
32 | #include <openvdb/math/Stencils.h> | ||
33 | #include <openvdb/Metadata.h> | ||
34 | #include <openvdb/tree/LeafManager.h> | ||
35 | #include <openvdb/tree/NodeManager.h> | ||
36 | #include "Interpolation.h" | ||
37 | #include "Morphology.h" | ||
38 | #include "Prune.h" | ||
39 | #include "SignedFloodFill.h" | ||
40 | #include "ValueTransformer.h" | ||
41 | #include <openvdb/openvdb.h> | ||
42 | |||
43 | #include <tbb/blocked_range.h> | ||
44 | #include <tbb/enumerable_thread_specific.h> | ||
45 | #include <tbb/parallel_for.h> | ||
46 | |||
47 | #include <iostream> | ||
48 | #include <sstream> | ||
49 | #include <string> | ||
50 | #include <vector> | ||
51 | |||
52 | |||
53 | namespace openvdb { | ||
54 | OPENVDB_USE_VERSION_NAMESPACE | ||
55 | namespace OPENVDB_VERSION_NAME { | ||
56 | namespace tools { | ||
57 | |||
58 | template<typename TreeType> | ||
59 | class MultiResGrid: public MetaMap | ||
60 | { | ||
61 | public: | ||
62 | using Ptr = SharedPtr<MultiResGrid>; | ||
63 | using ConstPtr = SharedPtr<const MultiResGrid>; | ||
64 | |||
65 | using ValueType = typename TreeType::ValueType; | ||
66 | using ValueOnCIter = typename TreeType::ValueOnCIter; | ||
67 | using ValueOnIter = typename TreeType::ValueOnIter; | ||
68 | using TreePtr = typename TreeType::Ptr; | ||
69 | using ConstTreePtr = typename TreeType::ConstPtr; | ||
70 | using GridPtr = typename Grid<TreeType>::Ptr; | ||
71 | using ConstGridPtr = typename Grid<TreeType>::ConstPtr; | ||
72 | |||
73 | ////////////////////////////////////////////////////////////////////// | ||
74 | |||
75 | /// @brief Constructor of empty grids | ||
76 | /// @param levels The number of trees in this MultiResGrid | ||
77 | /// @param background Background value | ||
78 | /// @param voxelSize Size of a (uniform voxel). Defaults to one. | ||
79 | /// @note The multiple grids are all empty. | ||
80 | MultiResGrid(size_t levels, ValueType background, double voxelSize = 1.0); | ||
81 | |||
82 | /// @brief Given an initial high-resolution grid this constructor | ||
83 | /// generates all the coarser grids by means of restriction. | ||
84 | /// @param levels The number of trees in this MultiResGrid | ||
85 | /// @param grid High-resolution input grid | ||
86 | /// @param useInjection Use restriction by injection, vs | ||
87 | /// full-weighting. It defaults to false and should rarely be used. | ||
88 | /// @note This constructor will perform a deep copy of the input | ||
89 | /// grid and use it as the highest level grid. | ||
90 | MultiResGrid(size_t levels, const Grid<TreeType> &grid, bool useInjection = false); | ||
91 | |||
92 | /// @brief Given an initial high-resolution grid this constructor | ||
93 | /// generates all the coarser grids by means of restriction. | ||
94 | /// @param levels The number of trees in this MultiResGrid | ||
95 | /// @param grid High-resolution input grid | ||
96 | /// @param useInjection Use restriction by injection, vs | ||
97 | /// full-weighting. It defaults to false and should rarely be used. | ||
98 | /// @note This constructor will steal the input grid and use it | ||
99 | /// as the highest level grid. On output the grid is empty. | ||
100 | MultiResGrid(size_t levels, GridPtr grid, bool useInjection = false); | ||
101 | |||
102 | ////////////////////////////////////////////////////////////////////// | ||
103 | |||
104 | /// @brief Return the number of levels, i.e. trees, in this MultiResGrid | ||
105 | /// @note level 0 is the finest level and numLevels()-1 is the coarsest | ||
106 | /// level. | ||
107 | ✗ | size_t numLevels() const { return mTrees.size(); } | |
108 | |||
109 | /// @brief Return the level of the finest grid (always 0) | ||
110 | ✗ | static size_t finestLevel() { return 0; } | |
111 | |||
112 | /// @brief Return the level of the coarsest grid, i.e. numLevels()-1 | ||
113 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | size_t coarsestLevel() const { return mTrees.size()-1; } |
114 | |||
115 | ////////////////////////////////////////////////////////////////////// | ||
116 | |||
117 | /// @brief Return a reference to the tree at the specified level | ||
118 | /// @param level The level of the tree to be returned | ||
119 | /// @note Level 0 is by definition the finest tree. | ||
120 | TreeType& tree(size_t level); | ||
121 | |||
122 | /// @brief Return a const reference to the tree at the specified level | ||
123 | /// @param level The level of the tree to be returned | ||
124 | /// @note Level 0 is by definition the finest tree. | ||
125 | const TreeType& constTree(size_t level) const; | ||
126 | |||
127 | /// @brief Return a shared pointer to the tree at the specified level | ||
128 | /// @param level The level of the tree to be returned | ||
129 | /// @note Level 0 is by definition the finest tree. | ||
130 | TreePtr treePtr(size_t level); | ||
131 | |||
132 | /// @brief Return a const shared pointer to the tree at the specified level | ||
133 | /// @param level The level of the tree to be returned | ||
134 | /// @note Level 0 is by definition the finest tree. | ||
135 | ConstTreePtr constTreePtr(size_t level) const; | ||
136 | |||
137 | /// @brief Return a reference to the tree at the finest level | ||
138 | ✗ | TreeType& finestTree() { return *mTrees.front(); } | |
139 | |||
140 | /// @brief Return a const reference to the tree at the finest level | ||
141 | ✗ | const TreeType& finestConstTree() const { return *mTrees.front(); } | |
142 | |||
143 | /// @brief Return a shared pointer to the tree at the finest level | ||
144 | ✗ | TreePtr finestTreePtr() { return mTrees.front(); } | |
145 | |||
146 | /// @brief Return a const shared pointer to the tree at the finest level | ||
147 | ✗ | ConstTreePtr finestConstTreePtr() const { return mTrees.front(); } | |
148 | |||
149 | /// @brief Return a reference to the tree at the coarsest level | ||
150 | ✗ | TreeType& coarsestTree() { return *mTrees.back(); } | |
151 | |||
152 | /// @brief Return a const reference to the tree at the coarsest level | ||
153 | ✗ | const TreeType& coarsestConstTree() const { return *mTrees.back(); } | |
154 | |||
155 | /// @brief Return a shared pointer to the tree at the coarsest level | ||
156 | ✗ | TreePtr coarsestTreePtr() { return mTrees.back(); } | |
157 | |||
158 | /// @brief Return a const shared pointer to the tree at the coarsest level | ||
159 | ✗ | ConstTreePtr coarsestConstTreePtr() const { return mTrees.back(); } | |
160 | |||
161 | ////////////////////////////////////////////////////////////////////// | ||
162 | |||
163 | /// @brief Return a shared pointer to the grid at the specified integer level | ||
164 | /// @param level Integer level of the grid to be returned | ||
165 | /// @note Level 0 is by definition the finest grid. | ||
166 | GridPtr grid(size_t level); | ||
167 | |||
168 | /// @brief Return a const shared pointer to the grid at the specified level | ||
169 | /// @param level The level of the grid to be returned | ||
170 | /// @note Level 0 is by definition the finest grid. | ||
171 | ConstGridPtr grid(size_t level) const; | ||
172 | |||
173 | /// @brief Return a shared pointer to a new grid at the specified | ||
174 | /// floating-point level. | ||
175 | /// @param level Floating-point level of the grid to be returned | ||
176 | /// @param grainSize Grain size for the multi-threading | ||
177 | /// @details Interpolation of the specified order is performed | ||
178 | /// between the bracketing integer levels. | ||
179 | /// @note Level 0 is by definition the finest grid. | ||
180 | template<Index Order> | ||
181 | GridPtr createGrid(float level, size_t grainSize = 1) const; | ||
182 | |||
183 | /// @brief Return a shared pointer to a vector of all the base | ||
184 | /// grids in this instance of the MultiResGrid. | ||
185 | /// @brief This method is useful for I/O | ||
186 | GridPtrVecPtr grids(); | ||
187 | |||
188 | /// @brief Return a const shared pointer to a vector of all the base | ||
189 | /// grids in this instance of the MultiResGrid. | ||
190 | /// @brief This method is useful for I/O | ||
191 | GridCPtrVecPtr grids() const; | ||
192 | |||
193 | ////////////////////////////////////////////////////////////////////// | ||
194 | |||
195 | //@{ | ||
196 | /// @brief Return a reference to the finest grid's transform, which might be | ||
197 | /// shared with other grids. | ||
198 | /// @note Calling setTransform() on this grid invalidates all references | ||
199 | /// previously returned by this method. | ||
200 | /// @warning The transform is relative to the finest level (=0) grid! | ||
201 | ✗ | math::Transform& transform() { return *mTransform; } | |
202 | ✗ | const math::Transform& transform() const { return *mTransform; } | |
203 | ✗ | const math::Transform& constTransform() const { return *mTransform; } | |
204 | //@} | ||
205 | |||
206 | ////////////////////////////////////////////////////////////////////// | ||
207 | |||
208 | //@{ | ||
209 | /// @brief Return the floating-point index coordinate at out_level given | ||
210 | /// the index coordinate in_xyz at in_level. | ||
211 | static Vec3R xyz(const Coord& in_ijk, size_t in_level, size_t out_level); | ||
212 | static Vec3R xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level); | ||
213 | static Vec3R xyz(const Vec3R& in_xyz, double in_level, double out_level); | ||
214 | //@} | ||
215 | |||
216 | ////////////////////////////////////////////////////////////////////// | ||
217 | |||
218 | |||
219 | |||
220 | //@{ | ||
221 | /// @brief Return the value at the specified coordinate position using | ||
222 | /// interpolation of the specified order into the tree at the out_level. | ||
223 | /// | ||
224 | /// @details First in_ijk is mapped from index space at in_level to | ||
225 | /// out_level, and then a value is interpolated from the tree at out_level. | ||
226 | /// | ||
227 | /// @param in_ijk Index coordinate position relative to tree at in_level | ||
228 | /// @param in_level Integer level of the input coordinate in_ijk | ||
229 | /// @param out_level Integer level of the interpolated value | ||
230 | template<Index Order> | ||
231 | ValueType sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const; | ||
232 | template<Index Order> | ||
233 | ValueType sampleValue(const Vec3R& in_ijk, size_t in_level, size_t out_level) const; | ||
234 | //@} | ||
235 | |||
236 | /// @brief Return the value at the specified integer coordinate position | ||
237 | /// and level using interpolation of the specified order. | ||
238 | /// @param ijk Integer coordinate position relative to the highest level (=0) grid | ||
239 | /// @param level Floating-point level from which to interpolate the value. | ||
240 | /// @brief Non-integer values of the level will use linear-interpolation | ||
241 | /// between the neighboring integer levels. | ||
242 | template<Index Order> | ||
243 | ValueType sampleValue(const Coord& ijk, double level) const; | ||
244 | |||
245 | /// @brief Return the value at the specified floating-point coordinate position | ||
246 | /// and level using interpolation of the specified order. | ||
247 | /// @param xyz Floating-point coordinate position relative to the highest level grid | ||
248 | /// @param level Floating-point level from which to interpolate | ||
249 | /// the value. | ||
250 | /// @brief Non-integer values of the level will use linear-interpolation | ||
251 | /// between the neighboring integer levels. | ||
252 | template<Index Order> | ||
253 | ValueType sampleValue(const Vec3R& xyz, double level) const; | ||
254 | |||
255 | ////////////////////////////////////////////////////////////////////// | ||
256 | |||
257 | /// @brief Return the value at coordinate location in @a level tree | ||
258 | /// from the coarser tree at @a level+1 using trilinear interpolation | ||
259 | /// @param coords input coords relative to the fine tree at level | ||
260 | /// @param level The fine level to receive values from the coarser | ||
261 | /// level-1 | ||
262 | /// @note Prolongation means to interpolation from coarse -> fine | ||
263 | ValueType prolongateVoxel(const Coord& coords, const size_t level) const; | ||
264 | |||
265 | |||
266 | /// (coarse->fine) Populates all the active voxel values in a fine (@a level) tree | ||
267 | /// from the coarse (@a level+1) tree using linear interpolation | ||
268 | /// This transforms multiple values of the tree in parallel | ||
269 | void prolongateActiveVoxels(size_t destlevel, size_t grainSize = 1); | ||
270 | |||
271 | ////////////////////////////////////////////////////////////////////// | ||
272 | |||
273 | /// Populate a coordinate location in @a level (coarse) tree | ||
274 | /// from the @a level-1 (fine) tree using trilinear interpolation | ||
275 | /// input coords are relative to the mTree[level] (coarse) | ||
276 | /// @note Restriction means remapping from fine -> coarse | ||
277 | ValueType restrictVoxel(Coord ijk, const size_t level, bool useInjection = false) const; | ||
278 | |||
279 | /// (fine->coarse) Populates all the active voxel values in the coarse (@a level) tree | ||
280 | /// from the fine (@a level-1) tree using trilinear interpolation. | ||
281 | /// For cell-centered data, this is equivalent to an average | ||
282 | /// For vertex-centered data this is equivalent to transferring the data | ||
283 | /// from the fine vertex directly above the coarse vertex. | ||
284 | /// This transforms multiple values of the tree in parallel | ||
285 | void restrictActiveVoxels(size_t destlevel, size_t grainSize = 1); | ||
286 | |||
287 | /// Output a human-readable description of this MultiResGrid | ||
288 | void print(std::ostream& = std::cout, int verboseLevel = 1) const; | ||
289 | |||
290 | /// @brief Return a string with the name of this MultiResGrid | ||
291 | 8 | std::string getName() const | |
292 | { | ||
293 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
16 | if (Metadata::ConstPtr meta = (*this)[GridBase::META_GRID_NAME]) return meta->str(); |
294 | ✗ | return ""; | |
295 | } | ||
296 | |||
297 | /// @brief Set the name of this MultiResGrid | ||
298 | ✗ | void setName(const std::string& name) | |
299 | { | ||
300 | ✗ | this->removeMeta(GridBase::META_GRID_NAME); | |
301 | ✗ | this->insertMeta(GridBase::META_GRID_NAME, StringMetadata(name)); | |
302 | } | ||
303 | |||
304 | /// Return the class of volumetric data (level set, fog volume, etc.) stored in this grid. | ||
305 | 2 | GridClass getGridClass() const | |
306 | { | ||
307 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
|
4 | typename StringMetadata::ConstPtr s = |
308 | this->getMetadata<StringMetadata>(GridBase::META_GRID_CLASS); | ||
309 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
4 | return s ? GridBase::stringToGridClass(s->value()) : GRID_UNKNOWN; |
310 | } | ||
311 | |||
312 | /// Specify the class of volumetric data (level set, fog volume, etc.) stored in this grid. | ||
313 | ✗ | void setGridClass(GridClass cls) | |
314 | { | ||
315 | ✗ | this->insertMeta(GridBase::META_GRID_CLASS, StringMetadata(GridBase::gridClassToString(cls))); | |
316 | } | ||
317 | |||
318 | /// Remove the setting specifying the class of this grid's volumetric data. | ||
319 | ✗ | void clearGridClass() { this->removeMeta(GridBase::META_GRID_CLASS); } | |
320 | |||
321 | private: | ||
322 | |||
323 | MultiResGrid(const MultiResGrid& other);//disallow copy construction | ||
324 | MultiResGrid& operator=(const MultiResGrid& other);//disallow copy assignment | ||
325 | |||
326 | // For optimal performance we disable registration of the ValueAccessor | ||
327 | using Accessor = tree::ValueAccessor<TreeType, false>; | ||
328 | using ConstAccessor = tree::ValueAccessor<const TreeType, false>; | ||
329 | |||
330 | void topDownRestrict(bool useInjection); | ||
331 | |||
332 | inline void initMeta(); | ||
333 | |||
334 | // Private struct that concurrently creates a mask of active voxel | ||
335 | // in a coarse tree from the active voxels in a fine tree | ||
336 | struct MaskOp; | ||
337 | |||
338 | /// Private struct that performs multi-threaded restriction | ||
339 | struct RestrictOp; | ||
340 | |||
341 | /// Private struct that performs multi-threaded prolongation | ||
342 | struct ProlongateOp; | ||
343 | |||
344 | // Private struct that performs multi-threaded computation of grids a fraction levels | ||
345 | template<Index Order> | ||
346 | struct FractionOp; | ||
347 | |||
348 | /// Private template struct that performs the actual multi-threading | ||
349 | template<typename OpType> struct CookOp; | ||
350 | |||
351 | // Array of shared pointer to trees, level 0 has the highest resolution. | ||
352 | std::vector<TreePtr> mTrees; | ||
353 | // Shared pointer to a transform associated with the finest level grid | ||
354 | typename math::Transform::Ptr mTransform; | ||
355 | };// MultiResGrid | ||
356 | |||
357 | template<typename TreeType> | ||
358 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | MultiResGrid<TreeType>:: |
359 | MultiResGrid(size_t levels, ValueType background, double voxelSize) | ||
360 | : mTrees(levels) | ||
361 |
2/4✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
2 | , mTransform(math::Transform::createLinearTransform( voxelSize )) |
362 | { | ||
363 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | this->initMeta(); |
364 |
3/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
10 | for (size_t i=0; i<levels; ++i) mTrees[i] = TreePtr(new TreeType(background)); |
365 | } | ||
366 | |||
367 | template<typename TreeType> | ||
368 | 2 | MultiResGrid<TreeType>:: | |
369 | MultiResGrid(size_t levels, const Grid<TreeType> &grid, bool useInjection) | ||
370 | : MetaMap(grid) | ||
371 | , mTrees(levels) | ||
372 |
2/4✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
|
2 | , mTransform( grid.transform().copy() ) |
373 | { | ||
374 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | this->initMeta(); |
375 |
3/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
|
2 | mTrees[0].reset( new TreeType( grid.tree() ) );// deep copy input tree |
376 | mTrees[0]->voxelizeActiveTiles(); | ||
377 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
2 | this->topDownRestrict(useInjection); |
378 | } | ||
379 | |||
380 | template<typename TreeType> | ||
381 | ✗ | MultiResGrid<TreeType>:: | |
382 | MultiResGrid(size_t levels, GridPtr grid, bool useInjection) | ||
383 | : MetaMap(*grid) | ||
384 | , mTrees(levels) | ||
385 | ✗ | , mTransform( grid->transform().copy() ) | |
386 | { | ||
387 | ✗ | this->initMeta(); | |
388 | ✗ | mTrees[0] = grid->treePtr();// steal tree from input grid | |
389 | mTrees[0]->voxelizeActiveTiles(); | ||
390 | ✗ | grid->newTree(); | |
391 | ✗ | this->topDownRestrict(useInjection); | |
392 | } | ||
393 | |||
394 | template<typename TreeType> | ||
395 | ✗ | inline TreeType& MultiResGrid<TreeType>:: | |
396 | tree(size_t level) | ||
397 | { | ||
398 | ✗ | assert( level < mTrees.size() ); | |
399 | ✗ | return *mTrees[level]; | |
400 | } | ||
401 | |||
402 | template<typename TreeType> | ||
403 | ✗ | inline const TreeType& MultiResGrid<TreeType>:: | |
404 | constTree(size_t level) const | ||
405 | { | ||
406 | ✗ | assert( level < mTrees.size() ); | |
407 | ✗ | return *mTrees[level]; | |
408 | } | ||
409 | |||
410 | template<typename TreeType> | ||
411 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
8 | inline typename TreeType::Ptr MultiResGrid<TreeType>:: |
412 | treePtr(size_t level) | ||
413 | { | ||
414 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
8 | assert( level < mTrees.size() ); |
415 | 8 | return mTrees[level]; | |
416 | } | ||
417 | |||
418 | template<typename TreeType> | ||
419 | ✗ | inline typename TreeType::ConstPtr MultiResGrid<TreeType>:: | |
420 | constTreePtr(size_t level) const | ||
421 | { | ||
422 | ✗ | assert( level < mTrees.size() ); | |
423 | ✗ | return mTrees[level]; | |
424 | } | ||
425 | |||
426 | template<typename TreeType> | ||
427 | 8 | typename Grid<TreeType>::Ptr MultiResGrid<TreeType>:: | |
428 | grid(size_t level) | ||
429 | { | ||
430 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
16 | typename Grid<TreeType>::Ptr grid = Grid<TreeType>::create(this->treePtr(level)); |
431 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | math::Transform::Ptr xform = mTransform->copy(); |
432 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
|
8 | if (level>0) xform->preScale( Real(1 << level) ); |
433 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
16 | grid->setTransform( xform ); |
434 |
3/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 4 times.
✗ Branch 8 not taken.
|
16 | grid->insertMeta( *this->copyMeta() ); |
435 |
2/6✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
8 | grid->insertMeta( "MultiResGrid_Level", Int64Metadata(level)); |
436 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
8 | std::stringstream ss; |
437 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
16 | ss << this->getName() << "_level_" << level; |
438 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
16 | grid->setName( ss.str() ); |
439 | 8 | return grid; | |
440 | } | ||
441 | |||
442 | template<typename TreeType> | ||
443 | ✗ | inline typename Grid<TreeType>::ConstPtr MultiResGrid<TreeType>:: | |
444 | grid(size_t level) const | ||
445 | { | ||
446 | ✗ | return const_cast<MultiResGrid*>(this)->grid(level); | |
447 | } | ||
448 | |||
449 | template<typename TreeType> | ||
450 | template<Index Order> | ||
451 | typename Grid<TreeType>::Ptr MultiResGrid<TreeType>:: | ||
452 | createGrid(float level, size_t grainSize) const | ||
453 | { | ||
454 | assert( level >= 0.0f && level <= float(mTrees.size()-1) ); | ||
455 | |||
456 | typename Grid<TreeType>::Ptr grid(new Grid<TreeType>(this->constTree(0).background())); | ||
457 | math::Transform::Ptr xform = mTransform->copy(); | ||
458 | xform->preScale( math::Pow(2.0f, level) ); | ||
459 | grid->setTransform( xform ); | ||
460 | grid->insertMeta( *(this->copyMeta()) ); | ||
461 | grid->insertMeta( "MultiResGrid_Level", FloatMetadata(level) ); | ||
462 | std::stringstream ss; | ||
463 | ss << this->getName() << "_level_" << level; | ||
464 | grid->setName( ss.str() ); | ||
465 | |||
466 | if ( size_t(floorf(level)) == size_t(ceilf(level)) ) { | ||
467 | grid->setTree( this->constTree( size_t(floorf(level))).copy() ); | ||
468 | } else { | ||
469 | FractionOp<Order> tmp(*this, grid->tree(), level, grainSize); | ||
470 | if ( grid->getGridClass() == GRID_LEVEL_SET ) { | ||
471 | signedFloodFill( grid->tree() ); | ||
472 | pruneLevelSet( grid->tree() );//only creates inactive tiles | ||
473 | } | ||
474 | } | ||
475 | |||
476 | return grid; | ||
477 | } | ||
478 | |||
479 | template<typename TreeType> | ||
480 | 2 | GridPtrVecPtr MultiResGrid<TreeType>:: | |
481 | grids() | ||
482 | { | ||
483 | 2 | GridPtrVecPtr grids( new GridPtrVec ); | |
484 |
4/10✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 4 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
10 | for (size_t level=0; level<mTrees.size(); ++level) grids->push_back(this->grid(level)); |
485 | 2 | return grids; | |
486 | } | ||
487 | |||
488 | template<typename TreeType> | ||
489 | ✗ | GridCPtrVecPtr MultiResGrid<TreeType>:: | |
490 | grids() const | ||
491 | { | ||
492 | ✗ | GridCPtrVecPtr grids( new GridCPtrVec ); | |
493 | ✗ | for (size_t level=0; level<mTrees.size(); ++level) grids->push_back(this->grid(level)); | |
494 | ✗ | return grids; | |
495 | } | ||
496 | |||
497 | template<typename TreeType> | ||
498 | 26 | Vec3R MultiResGrid<TreeType>:: | |
499 | xyz(const Coord& in_ijk, size_t in_level, size_t out_level) | ||
500 | { | ||
501 | 26 | return Vec3R( in_ijk.data() ) * Real(1 << in_level) / Real(1 << out_level); | |
502 | } | ||
503 | |||
504 | template<typename TreeType> | ||
505 | 4 | Vec3R MultiResGrid<TreeType>:: | |
506 | xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level) | ||
507 | { | ||
508 | 4 | return in_xyz * Real(1 << in_level) / Real(1 << out_level); | |
509 | } | ||
510 | |||
511 | template<typename TreeType> | ||
512 | ✗ | Vec3R MultiResGrid<TreeType>:: | |
513 | xyz(const Vec3R& in_xyz, double in_level, double out_level) | ||
514 | { | ||
515 | ✗ | return in_xyz * math::Pow(2.0, in_level - out_level); | |
516 | |||
517 | } | ||
518 | |||
519 | template<typename TreeType> | ||
520 | template<Index Order> | ||
521 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
26 | typename TreeType::ValueType MultiResGrid<TreeType>:: |
522 | sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const | ||
523 | { | ||
524 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
26 | assert( in_level >= 0 && in_level < mTrees.size() ); |
525 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
26 | assert( out_level >= 0 && out_level < mTrees.size() ); |
526 | const ConstAccessor acc(*mTrees[out_level]);// has disabled registration! | ||
527 | 26 | return tools::Sampler<Order>::sample( acc, this->xyz(in_ijk, in_level, out_level) ); | |
528 | } | ||
529 | |||
530 | template<typename TreeType> | ||
531 | template<Index Order> | ||
532 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | typename TreeType::ValueType MultiResGrid<TreeType>:: |
533 | sampleValue(const Vec3R& in_xyz, size_t in_level, size_t out_level) const | ||
534 | { | ||
535 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | assert( in_level >= 0 && in_level < mTrees.size() ); |
536 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | assert( out_level >= 0 && out_level < mTrees.size() ); |
537 | const ConstAccessor acc(*mTrees[out_level]);// has disabled registration! | ||
538 | 2 | return tools::Sampler<Order>::sample( acc, this->xyz(in_xyz, in_level, out_level) ); | |
539 | } | ||
540 | |||
541 | template<typename TreeType> | ||
542 | template<Index Order> | ||
543 | 12 | typename TreeType::ValueType MultiResGrid<TreeType>:: | |
544 | sampleValue(const Coord& ijk, double level) const | ||
545 | { | ||
546 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
12 | assert( level >= 0.0 && level <= double(mTrees.size()-1) ); |
547 | 12 | const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level)); | |
548 | 12 | const ValueType v0 = this->template sampleValue<Order>( ijk, 0, level0 ); | |
549 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
|
12 | if ( level0 == level1 ) return v0; |
550 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | assert( level1 - level0 == 1 ); |
551 | 2 | const ValueType v1 = this->template sampleValue<Order>( ijk, 0, level1 ); | |
552 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
553 | 2 | const ValueType a = ValueType(level1 - level); | |
554 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
555 | 2 | return a * v0 + (ValueType(1) - a) * v1; | |
556 | } | ||
557 | |||
558 | template<typename TreeType> | ||
559 | template<Index Order> | ||
560 | 1 | typename TreeType::ValueType MultiResGrid<TreeType>:: | |
561 | sampleValue(const Vec3R& xyz, double level) const | ||
562 | { | ||
563 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | assert( level >= 0.0 && level <= double(mTrees.size()-1) ); |
564 | 1 | const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level)); | |
565 | 1 | const ValueType v0 = this->template sampleValue<Order>( xyz, 0, level0 ); | |
566 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if ( level0 == level1 ) return v0; |
567 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | assert( level1 - level0 == 1 ); |
568 | 1 | const ValueType v1 = this->template sampleValue<Order>( xyz, 0, level1 ); | |
569 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
570 | 1 | const ValueType a = ValueType(level1 - level); | |
571 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
572 | 1 | return a * v0 + (ValueType(1) - a) * v1; | |
573 | } | ||
574 | |||
575 | template<typename TreeType> | ||
576 | 2 | typename TreeType::ValueType MultiResGrid<TreeType>:: | |
577 | prolongateVoxel(const Coord& ijk, const size_t level) const | ||
578 | { | ||
579 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
2 | assert( level+1 < mTrees.size() ); |
580 | const ConstAccessor acc(*mTrees[level + 1]);// has disabled registration! | ||
581 | 2 | return ProlongateOp::run(ijk, acc); | |
582 | } | ||
583 | |||
584 | template<typename TreeType> | ||
585 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | void MultiResGrid<TreeType>:: |
586 | prolongateActiveVoxels(size_t destlevel, size_t grainSize) | ||
587 | { | ||
588 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | assert( destlevel < mTrees.size()-1 ); |
589 | TreeType &fineTree = *mTrees[ destlevel ]; | ||
590 | 6 | const TreeType &coarseTree = *mTrees[ destlevel+1 ]; | |
591 | 6 | CookOp<ProlongateOp> tmp( coarseTree, fineTree, grainSize ); | |
592 | } | ||
593 | |||
594 | template<typename TreeType> | ||
595 | ✗ | typename TreeType::ValueType MultiResGrid<TreeType>:: | |
596 | restrictVoxel(Coord ijk, const size_t destlevel, bool useInjection) const | ||
597 | { | ||
598 | ✗ | assert( destlevel > 0 && destlevel < mTrees.size() ); | |
599 | ✗ | const TreeType &fineTree = *mTrees[ destlevel-1 ]; | |
600 | ✗ | if ( useInjection ) return fineTree.getValue(ijk<<1); | |
601 | const ConstAccessor acc( fineTree );// has disabled registration! | ||
602 | ✗ | return RestrictOp::run( ijk, acc); | |
603 | } | ||
604 | |||
605 | template<typename TreeType> | ||
606 | 6 | void MultiResGrid<TreeType>:: | |
607 | restrictActiveVoxels(size_t destlevel, size_t grainSize) | ||
608 | { | ||
609 |
2/4✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
6 | assert( destlevel > 0 && destlevel < mTrees.size() ); |
610 | 6 | const TreeType &fineTree = *mTrees[ destlevel-1 ]; | |
611 | TreeType &coarseTree = *mTrees[ destlevel ]; | ||
612 | 6 | CookOp<RestrictOp> tmp( fineTree, coarseTree, grainSize ); | |
613 | } | ||
614 | |||
615 | template<typename TreeType> | ||
616 | ✗ | void MultiResGrid<TreeType>:: | |
617 | print(std::ostream& os, int verboseLevel) const | ||
618 | { | ||
619 | ✗ | os << "MultiResGrid with " << mTrees.size() << " levels\n"; | |
620 | ✗ | for (size_t i=0; i<mTrees.size(); ++i) { | |
621 | ✗ | os << "Level " << i << ": "; | |
622 | ✗ | mTrees[i]->print(os, verboseLevel); | |
623 | } | ||
624 | |||
625 | ✗ | if ( MetaMap::metaCount() > 0) { | |
626 | os << "Additional metadata:" << std::endl; | ||
627 | ✗ | for (ConstMetaIterator it = beginMeta(), end = endMeta(); it != end; ++it) { | |
628 | os << " " << it->first; | ||
629 | ✗ | if (it->second) { | |
630 | ✗ | const std::string value = it->second->str(); | |
631 | ✗ | if (!value.empty()) os << ": " << value; | |
632 | } | ||
633 | ✗ | os << "\n"; | |
634 | } | ||
635 | } | ||
636 | |||
637 | os << "Transform:" << std::endl; | ||
638 | ✗ | transform().print(os, /*indent=*/" "); | |
639 | os << std::endl; | ||
640 | } | ||
641 | |||
642 | template<typename TreeType> | ||
643 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
4 | void MultiResGrid<TreeType>:: |
644 | initMeta() | ||
645 | { | ||
646 | const size_t levels = this->numLevels(); | ||
647 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
4 | if (levels < 2) { |
648 | ✗ | OPENVDB_THROW(ValueError, "MultiResGrid: at least two levels are required"); | |
649 | } | ||
650 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
|
4 | this->insertMeta("MultiResGrid_Levels", Int64Metadata( levels ) ); |
651 | 4 | } | |
652 | |||
653 | template<typename TreeType> | ||
654 | 2 | void MultiResGrid<TreeType>:: | |
655 | topDownRestrict(bool useInjection) | ||
656 | { | ||
657 | 2 | const bool isLevelSet = this->getGridClass() == GRID_LEVEL_SET; | |
658 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
8 | for (size_t n=1; n<mTrees.size(); ++n) { |
659 | 6 | const TreeType &fineTree = *mTrees[n-1]; | |
660 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
12 | mTrees[n] = TreePtr(new TreeType( fineTree.background() ) );// empty tree |
661 | TreeType &coarseTree = *mTrees[n]; | ||
662 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | if (useInjection) {// Restriction by injection |
663 | ✗ | for (ValueOnCIter it = fineTree.cbeginValueOn(); it; ++it) { | |
664 | ✗ | const Coord ijk = it.getCoord(); | |
665 | ✗ | if ( (ijk[0] & 1) || (ijk[1] & 1) || (ijk[2] & 1) ) continue; | |
666 | ✗ | coarseTree.setValue( ijk >> 1, *it ); | |
667 | } | ||
668 | } else {// Restriction by full-weighting | ||
669 | 6 | MaskOp tmp(fineTree, coarseTree, 128); | |
670 | 6 | this->restrictActiveVoxels(n, 64); | |
671 | } | ||
672 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
6 | if ( isLevelSet ) { |
673 | 6 | tools::signedFloodFill( coarseTree ); | |
674 | 6 | tools::pruneLevelSet( coarseTree );//only creates inactive tiles | |
675 | } | ||
676 | }// loop over grid levels | ||
677 | } | ||
678 | |||
679 | template<typename TreeType> | ||
680 | struct MultiResGrid<TreeType>::MaskOp | ||
681 | { | ||
682 | using MaskT = typename TreeType::template ValueConverter<ValueMask>::Type; | ||
683 | using PoolType = tbb::enumerable_thread_specific<TreeType>; | ||
684 | using ManagerT = tree::LeafManager<const MaskT>; | ||
685 | using RangeT = typename ManagerT::LeafRange; | ||
686 | using VoxelIterT = typename ManagerT::LeafNodeType::ValueOnCIter; | ||
687 | |||
688 | 6 | MaskOp(const TreeType& fineTree, TreeType& coarseTree, size_t grainSize = 1) | |
689 |
1/2✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
6 | : mPool(new PoolType( coarseTree ) )// empty coarse tree acts as examplar |
690 | { | ||
691 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | assert( coarseTree.empty() ); |
692 | |||
693 | // Create Mask of restruction performed on fineTree | ||
694 | 12 | MaskT mask(fineTree, false, true, TopologyCopy() ); | |
695 | |||
696 | // Multi-threaded dilation which also linearizes the tree to leaf nodes | ||
697 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | tools::dilateActiveValues(mask, 1, NN_FACE_EDGE_VERTEX, EXPAND_TILES); |
698 | |||
699 | // Restriction by injection using thread-local storage of coarse tree masks | ||
700 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
12 | ManagerT leafs( mask ); |
701 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | tbb::parallel_for(leafs.leafRange( grainSize ), *this); |
702 | |||
703 | // multithreaded union of thread-local coarse tree masks with the coarse tree | ||
704 | using IterT = typename PoolType::const_iterator; | ||
705 |
3/4✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 3 times.
|
18 | for (IterT it=mPool->begin(); it!=mPool->end(); ++it) coarseTree.topologyUnion( *it ); |
706 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
6 | delete mPool; |
707 | } | ||
708 | 168 | void operator()(const RangeT& range) const | |
709 | { | ||
710 | 168 | Accessor coarseAcc( mPool->local() );// disabled registration | |
711 |
2/2✓ Branch 1 taken 6179 times.
✓ Branch 2 taken 84 times.
|
12526 | for (typename RangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { |
712 |
2/2✓ Branch 0 taken 1419478 times.
✓ Branch 1 taken 6179 times.
|
2851314 | for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { |
713 |
1/2✓ Branch 1 taken 1419478 times.
✗ Branch 2 not taken.
|
2838956 | Coord ijk = voxelIter.getCoord(); |
714 |
6/6✓ Branch 0 taken 709754 times.
✓ Branch 1 taken 709724 times.
✓ Branch 2 taken 354782 times.
✓ Branch 3 taken 354972 times.
✓ Branch 4 taken 177756 times.
✓ Branch 5 taken 177026 times.
|
2838956 | if ( (ijk[2] & 1) || (ijk[1] & 1) || (ijk[0] & 1) ) continue;//no overlap |
715 |
1/2✓ Branch 1 taken 177026 times.
✗ Branch 2 not taken.
|
354052 | coarseAcc.setValueOn( ijk >> 1 );//injection from fine to coarse level |
716 | }//loop over active voxels in the fine tree | ||
717 | }// loop over leaf nodes in the fine tree | ||
718 | } | ||
719 | PoolType* mPool; | ||
720 | };// MaskOp | ||
721 | |||
722 | template<typename TreeType> | ||
723 | template<Index Order> | ||
724 | struct MultiResGrid<TreeType>::FractionOp | ||
725 | { | ||
726 | using MaskT = typename TreeType::template ValueConverter<ValueMask>::Type; | ||
727 | using PoolType = tbb::enumerable_thread_specific<MaskT>; | ||
728 | using PoolIterT = typename PoolType::iterator; | ||
729 | using Manager1 = tree::LeafManager<const TreeType>; | ||
730 | using Manager2 = tree::LeafManager<TreeType>; | ||
731 | using Range1 = typename Manager1::LeafRange; | ||
732 | using Range2 = typename Manager2::LeafRange; | ||
733 | |||
734 | FractionOp(const MultiResGrid& parent, | ||
735 | TreeType& midTree, | ||
736 | float level, | ||
737 | size_t grainSize = 1) | ||
738 | : mLevel( level ) | ||
739 | , mPool(nullptr) | ||
740 | , mTree0( &*(parent.mTrees[size_t(floorf(level))]) )//high-resolution | ||
741 | , mTree1( &*(parent.mTrees[size_t(ceilf(level))]) ) //low-resolution | ||
742 | { | ||
743 | assert( midTree.empty() ); | ||
744 | assert( mTree0 != mTree1 ); | ||
745 | |||
746 | // Create a pool of thread-local masks | ||
747 | MaskT examplar( false ); | ||
748 | mPool = new PoolType( examplar ); | ||
749 | |||
750 | {// create mask from re-mapping coarse tree to mid-level tree | ||
751 | tree::LeafManager<const TreeType> manager( *mTree1 ); | ||
752 | tbb::parallel_for( manager.leafRange(grainSize), *this ); | ||
753 | } | ||
754 | |||
755 | // Multi-threaded dilation of mask | ||
756 | tbb::parallel_for(tbb::blocked_range<PoolIterT>(mPool->begin(),mPool->end(),1), *this); | ||
757 | |||
758 | // Union thread-local coarse tree masks into the coarse tree | ||
759 | for (PoolIterT it=mPool->begin(); it!=mPool->end(); ++it) midTree.topologyUnion( *it ); | ||
760 | delete mPool; | ||
761 | |||
762 | {// Interpolate values into the static mid level tree | ||
763 | Manager2 manager( midTree ); | ||
764 | tbb::parallel_for(manager.leafRange(grainSize), *this); | ||
765 | } | ||
766 | } | ||
767 | void operator()(const Range1& range) const | ||
768 | { | ||
769 | using VoxelIter = typename Manager1::LeafNodeType::ValueOnCIter; | ||
770 | // Let mLevel = level + frac, where | ||
771 | // level is integer part of mLevel and frac is the fractional part | ||
772 | // low-res voxel size in world units = dx1 = 2^(level + 1) | ||
773 | // mid-res voxel size in world units = dx = 2^(mLevel) = 2^(level + frac) | ||
774 | // low-res index -> world: ijk * dx1 | ||
775 | // world -> mid-res index: world / dx | ||
776 | // low-res index -> mid-res index: (ijk * dx1) / dx = ijk * scale where | ||
777 | // scale = dx1/dx = 2^(level+1)/2^(level+frac) = 2^(1-frac) | ||
778 | const float scale = math::Pow(2.0f, 1.0f - math::FractionalPart(mLevel)); | ||
779 | tree::ValueAccessor<MaskT, false> acc( mPool->local() );// disabled registration | ||
780 | for (typename Range1::Iterator leafIter = range.begin(); leafIter; ++leafIter) { | ||
781 | for (VoxelIter voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { | ||
782 | Coord ijk = voxelIter.getCoord(); | ||
783 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
784 | const auto value0 = ijk[0] * scale; | ||
785 | const auto value1 = ijk[1] * scale; | ||
786 | const auto value2 = ijk[2] * scale; | ||
787 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
788 | ijk[0] = int(math::Round(value0)); | ||
789 | ijk[1] = int(math::Round(value1)); | ||
790 | ijk[2] = int(math::Round(value2)); | ||
791 | |||
792 | acc.setValueOn( ijk ); | ||
793 | }//loop over active voxels in the fine tree | ||
794 | }// loop over leaf nodes in the fine tree | ||
795 | } | ||
796 | void operator()(const tbb::blocked_range<PoolIterT>& range) const | ||
797 | { | ||
798 | for (PoolIterT it=range.begin(); it!=range.end(); ++it) { | ||
799 | tools::dilateActiveValues(*it, 1, tools::NN_FACE_EDGE_VERTEX, tools::IGNORE_TILES); | ||
800 | } | ||
801 | } | ||
802 | void operator()(const Range2 &r) const | ||
803 | { | ||
804 | using VoxelIter = typename TreeType::LeafNodeType::ValueOnIter; | ||
805 | // Let mLevel = level + frac, where | ||
806 | // level is integer part of mLevel and frac is the fractional part | ||
807 | // high-res voxel size in world units = dx0 = 2^(level) | ||
808 | // low-res voxel size in world units = dx1 = 2^(level+1) | ||
809 | // mid-res voxel size in world units = dx = 2^(mLevel) = 2^(level + frac) | ||
810 | // mid-res index -> world: ijk * dx | ||
811 | // world -> high-res index: world / dx0 | ||
812 | // world -> low-res index: world / dx1 | ||
813 | // mid-res index -> high-res index: (ijk * dx) / dx0 = ijk * scale0 where | ||
814 | // scale0 = dx/dx0 = 2^(level+frac)/2^(level) = 2^(frac) | ||
815 | // mid-res index -> low-res index: (ijk * dx) / dx1 = ijk * scale1 where | ||
816 | // scale1 = dx/dx1 = 2^(level+frac)/2^(level+1) = 2^(frac-1) | ||
817 | const float b = math::FractionalPart(mLevel), a = 1.0f - b; | ||
818 | const float scale0 = math::Pow( 2.0f, b ); | ||
819 | const float scale1 = math::Pow( 2.0f,-a ); | ||
820 | ConstAccessor acc0( *mTree0 ), acc1( *mTree1 ); | ||
821 | for (typename Range2::Iterator leafIter = r.begin(); leafIter; ++leafIter) { | ||
822 | for (VoxelIter voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { | ||
823 | const Vec3R xyz = Vec3R( voxelIter.getCoord().data() );// mid level coord | ||
824 | const ValueType v0 = tools::Sampler<Order>::sample( acc0, xyz * scale0 ); | ||
825 | const ValueType v1 = tools::Sampler<Order>::sample( acc1, xyz * scale1 ); | ||
826 | OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN | ||
827 | const auto value0 = a*v0; | ||
828 | const auto value1 = b*v1; | ||
829 | OPENVDB_NO_TYPE_CONVERSION_WARNING_END | ||
830 | voxelIter.setValue( ValueType(value0 + value1) ); | ||
831 | } | ||
832 | } | ||
833 | } | ||
834 | const float mLevel; | ||
835 | PoolType* mPool; | ||
836 | const TreeType *mTree0, *mTree1; | ||
837 | };// FractionOp | ||
838 | |||
839 | |||
840 | template<typename TreeType> | ||
841 | template<typename OperatorType> | ||
842 | struct MultiResGrid<TreeType>::CookOp | ||
843 | { | ||
844 | using ManagerT = tree::LeafManager<TreeType>; | ||
845 | using RangeT = typename ManagerT::LeafRange; | ||
846 | |||
847 | 12 | CookOp(const TreeType& srcTree, TreeType& dstTree, size_t grainSize): acc(srcTree) | |
848 | { | ||
849 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
12 | ManagerT leafs(dstTree); |
850 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
12 | tbb::parallel_for(leafs.leafRange(grainSize), *this); |
851 | } | ||
852 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 71 times.
|
142 | CookOp(const CookOp &other): acc(other.acc.tree()) {} |
853 | |||
854 | 362 | void operator()(const RangeT& range) const | |
855 | { | ||
856 |
2/2✓ Branch 1 taken 1418 times.
✓ Branch 2 taken 181 times.
|
3198 | for (auto leafIt = range.begin(); leafIt; ++leafIt) { |
857 | auto& phi = leafIt.buffer(0); | ||
858 |
2/2✓ Branch 0 taken 218605 times.
✓ Branch 1 taken 1418 times.
|
440046 | for (auto voxelIt = leafIt->beginValueOn(); voxelIt; ++voxelIt) { |
859 | 437210 | phi.setValue(voxelIt.pos(), OperatorType::run(voxelIt.getCoord(), acc)); | |
860 | } | ||
861 | } | ||
862 | } | ||
863 | |||
864 | const ConstAccessor acc; | ||
865 | };// CookOp | ||
866 | |||
867 | |||
868 | template<typename TreeType> | ||
869 | struct MultiResGrid<TreeType>::RestrictOp | ||
870 | { | ||
871 | /// @brief Static method that performs restriction by full weighting | ||
872 | /// @param ijk Coordinate location on the coarse tree | ||
873 | /// @param acc ValueAccessor to the fine tree | ||
874 | 354052 | static ValueType run(Coord ijk, const ConstAccessor &acc) | |
875 | { | ||
876 | ijk <<= 1; | ||
877 | // Overlapping grid point | ||
878 | 354052 | ValueType v = 8*acc.getValue(ijk); | |
879 | // neighbors in one axial direction | ||
880 | 354052 | v += 4*(acc.getValue(ijk.offsetBy(-1, 0, 0)) + acc.getValue(ijk.offsetBy( 1, 0, 0)) +// x | |
881 | 354052 | acc.getValue(ijk.offsetBy( 0,-1, 0)) + acc.getValue(ijk.offsetBy( 0, 1, 0)) +// y | |
882 | 354052 | acc.getValue(ijk.offsetBy( 0, 0,-1)) + acc.getValue(ijk.offsetBy( 0, 0, 1)));// z | |
883 | // neighbors in two axial directions | ||
884 | 354052 | v += 2*(acc.getValue(ijk.offsetBy(-1,-1, 0)) + acc.getValue(ijk.offsetBy(-1, 1, 0)) +// xy | |
885 | 354052 | acc.getValue(ijk.offsetBy( 1,-1, 0)) + acc.getValue(ijk.offsetBy( 1, 1, 0)) +// xy | |
886 | 354052 | acc.getValue(ijk.offsetBy(-1, 0,-1)) + acc.getValue(ijk.offsetBy(-1, 0, 1)) +// xz | |
887 | 354052 | acc.getValue(ijk.offsetBy( 1, 0,-1)) + acc.getValue(ijk.offsetBy( 1, 0, 1)) +// xz | |
888 | 354052 | acc.getValue(ijk.offsetBy( 0,-1,-1)) + acc.getValue(ijk.offsetBy( 0,-1, 1)) +// yz | |
889 | 354052 | acc.getValue(ijk.offsetBy( 0, 1,-1)) + acc.getValue(ijk.offsetBy( 0, 1, 1)));// yz | |
890 | // neighbors in three axial directions | ||
891 |
2/2✓ Branch 0 taken 354052 times.
✓ Branch 1 taken 177026 times.
|
1062156 | for (int i=-1; i<=1; i+=2) { |
892 |
2/2✓ Branch 0 taken 708104 times.
✓ Branch 1 taken 354052 times.
|
2124312 | for (int j=-1; j<=1; j+=2) { |
893 |
2/2✓ Branch 0 taken 1416208 times.
✓ Branch 1 taken 708104 times.
|
4248624 | for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k));// xyz |
894 | } | ||
895 | } | ||
896 | 354052 | v *= ValueType(1.0f/64.0f); | |
897 | 354052 | return v; | |
898 | } | ||
899 | };// RestrictOp | ||
900 | |||
901 | template<typename TreeType> | ||
902 | struct MultiResGrid<TreeType>::ProlongateOp | ||
903 | { | ||
904 | /// @brief Interpolate values from a coarse grid (acc) into the index space (ijk) of a fine grid | ||
905 | /// @param ijk Coordinate location on the fine tree | ||
906 | /// @param acc ValueAccessor to the coarse tree | ||
907 | 41580 | static ValueType run(const Coord& ijk, const ConstAccessor &acc) | |
908 | { | ||
909 | 41580 | switch ( (ijk[0] & 1) | ((ijk[1] & 1) << 1) | ((ijk[2] & 1) << 2) ) { | |
910 | 5768 | case 0:// all even | |
911 | 5768 | return acc.getValue(ijk>>1); | |
912 | 5372 | case 1:// x is odd | |
913 | 5372 | return ValueType(0.5)*(acc.getValue(ijk.offsetBy(-1,0,0)>>1) + | |
914 | 5372 | acc.getValue(ijk.offsetBy( 1,0,0)>>1)); | |
915 | 5372 | case 2:// y is odd | |
916 | 5372 | return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,-1,0)>>1) + | |
917 | 5372 | acc.getValue(ijk.offsetBy(0, 1,0)>>1)); | |
918 | 5008 | case 3:// x&y are odd | |
919 | 5008 | return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,-1,0)>>1) + | |
920 | 10016 | acc.getValue(ijk.offsetBy(-1, 1,0)>>1) + | |
921 | 5008 | acc.getValue(ijk.offsetBy( 1,-1,0)>>1) + | |
922 | 5008 | acc.getValue(ijk.offsetBy( 1, 1,0)>>1)); | |
923 | 5372 | case 4:// z is odd | |
924 | 5372 | return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,0,-1)>>1) + | |
925 | 5372 | acc.getValue(ijk.offsetBy(0,0, 1)>>1)); | |
926 | 5008 | case 5:// x&z are odd | |
927 | 5008 | return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,0,-1)>>1) + | |
928 | 10016 | acc.getValue(ijk.offsetBy(-1,0, 1)>>1) + | |
929 | 5008 | acc.getValue(ijk.offsetBy( 1,0,-1)>>1) + | |
930 | 5008 | acc.getValue(ijk.offsetBy( 1,0, 1)>>1)); | |
931 | 5008 | case 6:// y&z are odd | |
932 | 5008 | return ValueType(0.25)*(acc.getValue(ijk.offsetBy(0,-1,-1)>>1) + | |
933 | 10016 | acc.getValue(ijk.offsetBy(0,-1, 1)>>1) + | |
934 | 5008 | acc.getValue(ijk.offsetBy(0, 1,-1)>>1) + | |
935 | 5008 | acc.getValue(ijk.offsetBy(0, 1, 1)>>1)); | |
936 | } | ||
937 | // all are odd | ||
938 | ValueType v = zeroVal<ValueType>(); | ||
939 | 14016 | for (int i=-1; i<=1; i+=2) { | |
940 | 28032 | for (int j=-1; j<=1; j+=2) { | |
941 | 56064 | for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k)>>1);// xyz | |
942 | } | ||
943 | } | ||
944 | 4672 | return ValueType(0.125) * v; | |
945 | } | ||
946 | };// ProlongateOp | ||
947 | |||
948 | |||
949 | //////////////////////////////////////// | ||
950 | |||
951 | |||
952 | // Explicit Template Instantiation | ||
953 | |||
954 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
955 | |||
956 | #ifdef OPENVDB_INSTANTIATE_MULTIRESGRID | ||
957 | #include <openvdb/util/ExplicitInstantiation.h> | ||
958 | #endif | ||
959 | |||
960 | OPENVDB_INSTANTIATE_CLASS MultiResGrid<FloatTree>; | ||
961 | OPENVDB_INSTANTIATE_CLASS MultiResGrid<DoubleTree>; | ||
962 | |||
963 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
964 | |||
965 | |||
966 | } // namespace tools | ||
967 | } // namespace OPENVDB_VERSION_NAME | ||
968 | } // namespace openvdb | ||
969 | |||
970 | #endif // OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED | ||
971 |