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.
numLevels()107     size_t numLevels() const { return mTrees.size(); }
108 
109     /// @brief Return the level of the finest grid (always 0)
finestLevel()110     static size_t finestLevel() { return 0; }
111 
112     /// @brief Return the level of the coarsest grid, i.e. numLevels()-1
coarsestLevel()113     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
finestTree()138     TreeType& finestTree() { return *mTrees.front(); }
139 
140     /// @brief Return a const reference to the tree at the finest level
finestConstTree()141     const TreeType& finestConstTree() const { return *mTrees.front(); }
142 
143     /// @brief Return a shared pointer to the tree at the finest level
finestTreePtr()144     TreePtr finestTreePtr() { return mTrees.front(); }
145 
146     /// @brief Return a const shared pointer to the tree at the finest level
finestConstTreePtr()147     ConstTreePtr finestConstTreePtr() const { return mTrees.front(); }
148 
149     /// @brief Return a reference to the tree at the coarsest level
coarsestTree()150     TreeType& coarsestTree() { return *mTrees.back(); }
151 
152     /// @brief Return a const reference to the tree at the coarsest level
coarsestConstTree()153     const TreeType& coarsestConstTree() const { return *mTrees.back(); }
154 
155     /// @brief Return a shared pointer to the tree at the coarsest level
coarsestTreePtr()156     TreePtr coarsestTreePtr() { return mTrees.back(); }
157 
158     /// @brief Return a const shared pointer to the tree at the coarsest level
coarsestConstTreePtr()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!
transform()201     math::Transform& transform() { return *mTransform; }
transform()202     const math::Transform& transform() const { return *mTransform; }
constTransform()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
getName()291     std::string getName() const
292     {
293         if (Metadata::ConstPtr meta = (*this)[GridBase::META_GRID_NAME]) return meta->str();
294         return "";
295     }
296 
297     /// @brief Set the name of this MultiResGrid
setName(const std::string & name)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.
getGridClass()305     GridClass getGridClass() const
306     {
307         typename StringMetadata::ConstPtr s =
308             this->getMetadata<StringMetadata>(GridBase::META_GRID_CLASS);
309         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.
setGridClass(GridClass cls)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.
clearGridClass()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 MultiResGrid<TreeType>::
MultiResGrid(size_t levels,ValueType background,double voxelSize)359 MultiResGrid(size_t levels, ValueType background, double voxelSize)
360     : mTrees(levels)
361     , mTransform(math::Transform::createLinearTransform( voxelSize ))
362 {
363     this->initMeta();
364     for (size_t i=0; i<levels; ++i) mTrees[i] = TreePtr(new TreeType(background));
365 }
366 
367 template<typename TreeType>
368 MultiResGrid<TreeType>::
MultiResGrid(size_t levels,const Grid<TreeType> & grid,bool useInjection)369 MultiResGrid(size_t levels, const Grid<TreeType> &grid, bool useInjection)
370     : MetaMap(grid)
371     , mTrees(levels)
372     , mTransform( grid.transform().copy() )
373 {
374     this->initMeta();
375     mTrees[0].reset( new TreeType( grid.tree() ) );// deep copy input tree
376     mTrees[0]->voxelizeActiveTiles();
377     this->topDownRestrict(useInjection);
378 }
379 
380 template<typename TreeType>
381 MultiResGrid<TreeType>::
MultiResGrid(size_t levels,GridPtr grid,bool useInjection)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>::
tree(size_t level)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>::
constTree(size_t level)404 constTree(size_t level) const
405 {
406     assert( level < mTrees.size() );
407     return *mTrees[level];
408 }
409 
410 template<typename TreeType>
411 inline typename TreeType::Ptr MultiResGrid<TreeType>::
treePtr(size_t level)412 treePtr(size_t level)
413 {
414     assert( level < mTrees.size() );
415     return mTrees[level];
416 }
417 
418 template<typename TreeType>
419 inline typename TreeType::ConstPtr MultiResGrid<TreeType>::
constTreePtr(size_t level)420 constTreePtr(size_t level) const
421 {
422     assert( level < mTrees.size() );
423     return mTrees[level];
424 }
425 
426 template<typename TreeType>
427 typename Grid<TreeType>::Ptr MultiResGrid<TreeType>::
grid(size_t level)428 grid(size_t level)
429 {
430     typename Grid<TreeType>::Ptr grid = Grid<TreeType>::create(this->treePtr(level));
431     math::Transform::Ptr xform = mTransform->copy();
432     if (level>0) xform->preScale( Real(1 << level) );
433     grid->setTransform( xform );
434     grid->insertMeta( *this->copyMeta() );
435     grid->insertMeta( "MultiResGrid_Level", Int64Metadata(level));
436     std::stringstream ss;
437     ss << this->getName() << "_level_" << level;
438     grid->setName( ss.str() );
439     return grid;
440 }
441 
442 template<typename TreeType>
443 inline typename Grid<TreeType>::ConstPtr MultiResGrid<TreeType>::
grid(size_t level)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>::
createGrid(float level,size_t grainSize)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 GridPtrVecPtr MultiResGrid<TreeType>::
grids()481 grids()
482 {
483     GridPtrVecPtr grids( new GridPtrVec );
484     for (size_t level=0; level<mTrees.size(); ++level) grids->push_back(this->grid(level));
485     return grids;
486 }
487 
488 template<typename TreeType>
489 GridCPtrVecPtr MultiResGrid<TreeType>::
grids()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 Vec3R MultiResGrid<TreeType>::
xyz(const Coord & in_ijk,size_t in_level,size_t out_level)499 xyz(const Coord& in_ijk, size_t in_level, size_t out_level)
500 {
501     return Vec3R( in_ijk.data() ) * Real(1 << in_level) / Real(1 << out_level);
502 }
503 
504 template<typename TreeType>
505 Vec3R MultiResGrid<TreeType>::
xyz(const Vec3R & in_xyz,size_t in_level,size_t out_level)506 xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level)
507 {
508     return in_xyz * Real(1 << in_level) / Real(1 << out_level);
509 }
510 
511 template<typename TreeType>
512 Vec3R MultiResGrid<TreeType>::
xyz(const Vec3R & in_xyz,double in_level,double out_level)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 typename TreeType::ValueType MultiResGrid<TreeType>::
sampleValue(const Coord & in_ijk,size_t in_level,size_t out_level)522 sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const
523 {
524     assert( in_level  >= 0 && in_level  < mTrees.size() );
525     assert( out_level >= 0 && out_level < mTrees.size() );
526     const ConstAccessor acc(*mTrees[out_level]);// has disabled registration!
527     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 typename TreeType::ValueType MultiResGrid<TreeType>::
sampleValue(const Vec3R & in_xyz,size_t in_level,size_t out_level)533 sampleValue(const Vec3R& in_xyz, size_t in_level, size_t out_level) const
534 {
535     assert( in_level  >= 0 && in_level  < mTrees.size() );
536     assert( out_level >= 0 && out_level < mTrees.size() );
537     const ConstAccessor acc(*mTrees[out_level]);// has disabled registration!
538     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 typename TreeType::ValueType MultiResGrid<TreeType>::
sampleValue(const Coord & ijk,double level)544 sampleValue(const Coord& ijk, double level) const
545 {
546     assert( level >= 0.0 && level <= double(mTrees.size()-1) );
547     const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level));
548     const ValueType v0 = this->template sampleValue<Order>( ijk, 0, level0 );
549     if ( level0 == level1 ) return v0;
550     assert( level1 - level0 == 1 );
551     const ValueType v1 = this->template sampleValue<Order>( ijk, 0, level1 );
552     OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
553     const ValueType a = ValueType(level1 - level);
554     OPENVDB_NO_TYPE_CONVERSION_WARNING_END
555     return a * v0 + (ValueType(1) - a) * v1;
556 }
557 
558 template<typename TreeType>
559 template<Index Order>
560 typename TreeType::ValueType MultiResGrid<TreeType>::
sampleValue(const Vec3R & xyz,double level)561 sampleValue(const Vec3R& xyz, double level) const
562 {
563     assert( level >= 0.0 && level <= double(mTrees.size()-1) );
564     const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level));
565     const ValueType v0 = this->template sampleValue<Order>( xyz, 0, level0 );
566     if ( level0 == level1 ) return v0;
567     assert( level1 - level0 == 1 );
568     const ValueType v1 = this->template sampleValue<Order>( xyz, 0, level1 );
569     OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN
570     const ValueType a = ValueType(level1 - level);
571     OPENVDB_NO_TYPE_CONVERSION_WARNING_END
572     return a * v0 + (ValueType(1) - a) * v1;
573 }
574 
575 template<typename TreeType>
576 typename TreeType::ValueType MultiResGrid<TreeType>::
prolongateVoxel(const Coord & ijk,const size_t level)577 prolongateVoxel(const Coord& ijk, const size_t level) const
578 {
579     assert( level+1 < mTrees.size() );
580     const ConstAccessor acc(*mTrees[level + 1]);// has disabled registration!
581     return ProlongateOp::run(ijk, acc);
582 }
583 
584 template<typename TreeType>
585 void MultiResGrid<TreeType>::
prolongateActiveVoxels(size_t destlevel,size_t grainSize)586 prolongateActiveVoxels(size_t destlevel, size_t grainSize)
587 {
588     assert( destlevel < mTrees.size()-1 );
589     TreeType &fineTree = *mTrees[ destlevel ];
590     const TreeType &coarseTree = *mTrees[ destlevel+1 ];
591     CookOp<ProlongateOp> tmp( coarseTree, fineTree, grainSize );
592 }
593 
594 template<typename TreeType>
595 typename TreeType::ValueType MultiResGrid<TreeType>::
restrictVoxel(Coord ijk,const size_t destlevel,bool useInjection)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 void MultiResGrid<TreeType>::
restrictActiveVoxels(size_t destlevel,size_t grainSize)607 restrictActiveVoxels(size_t destlevel, size_t grainSize)
608 {
609     assert( destlevel > 0 && destlevel < mTrees.size() );
610     const TreeType &fineTree = *mTrees[ destlevel-1 ];
611     TreeType &coarseTree = *mTrees[ destlevel ];
612     CookOp<RestrictOp> tmp( fineTree, coarseTree, grainSize );
613 }
614 
615 template<typename TreeType>
616 void MultiResGrid<TreeType>::
print(std::ostream & os,int verboseLevel)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 void MultiResGrid<TreeType>::
initMeta()644 initMeta()
645 {
646     const size_t levels = this->numLevels();
647     if (levels < 2) {
648         OPENVDB_THROW(ValueError, "MultiResGrid: at least two levels are required");
649     }
650     this->insertMeta("MultiResGrid_Levels", Int64Metadata( levels ) );
651 }
652 
653 template<typename TreeType>
654 void MultiResGrid<TreeType>::
topDownRestrict(bool useInjection)655 topDownRestrict(bool useInjection)
656 {
657     const bool isLevelSet = this->getGridClass() == GRID_LEVEL_SET;
658     for (size_t n=1; n<mTrees.size(); ++n) {
659         const TreeType &fineTree = *mTrees[n-1];
660         mTrees[n] = TreePtr(new TreeType( fineTree.background() ) );// empty tree
661         TreeType &coarseTree = *mTrees[n];
662         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             MaskOp tmp(fineTree, coarseTree, 128);
670             this->restrictActiveVoxels(n, 64);
671         }
672         if ( isLevelSet ) {
673             tools::signedFloodFill( coarseTree );
674             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     MaskOp(const TreeType& fineTree, TreeType& coarseTree, size_t grainSize = 1)
689         : mPool(new PoolType( coarseTree ) )// empty coarse tree acts as examplar
690     {
691         assert( coarseTree.empty() );
692 
693         // Create Mask of restruction performed on fineTree
694         MaskT mask(fineTree, false, true, TopologyCopy() );
695 
696         // Multi-threaded dilation which also linearizes the tree to leaf nodes
697         tools::dilateActiveValues(mask, 1, NN_FACE_EDGE_VERTEX, EXPAND_TILES);
698 
699         // Restriction by injection using thread-local storage of coarse tree masks
700         ManagerT leafs( mask );
701         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         for (IterT it=mPool->begin(); it!=mPool->end(); ++it) coarseTree.topologyUnion( *it );
706         delete mPool;
707     }
708     void operator()(const RangeT& range) const
709     {
710         Accessor coarseAcc( mPool->local() );// disabled registration
711         for (typename RangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) {
712             for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) {
713                 Coord ijk = voxelIter.getCoord();
714                 if ( (ijk[2] & 1) || (ijk[1] & 1) || (ijk[0] & 1) ) continue;//no overlap
715                 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     CookOp(const TreeType& srcTree, TreeType& dstTree, size_t grainSize): acc(srcTree)
848     {
849         ManagerT leafs(dstTree);
850         tbb::parallel_for(leafs.leafRange(grainSize), *this);
851     }
852     CookOp(const CookOp &other): acc(other.acc.tree()) {}
853 
854     void operator()(const RangeT& range) const
855     {
856         for (auto leafIt = range.begin(); leafIt; ++leafIt) {
857             auto& phi = leafIt.buffer(0);
858             for (auto voxelIt = leafIt->beginValueOn(); voxelIt; ++voxelIt) {
859                 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     static ValueType run(Coord ijk, const ConstAccessor &acc)
875     {
876         ijk <<= 1;
877         // Overlapping grid point
878         ValueType v = 8*acc.getValue(ijk);
879         // neighbors in one axial direction
880         v += 4*(acc.getValue(ijk.offsetBy(-1, 0, 0)) + acc.getValue(ijk.offsetBy( 1, 0, 0)) +// x
881                 acc.getValue(ijk.offsetBy( 0,-1, 0)) + acc.getValue(ijk.offsetBy( 0, 1, 0)) +// y
882                 acc.getValue(ijk.offsetBy( 0, 0,-1)) + acc.getValue(ijk.offsetBy( 0, 0, 1)));// z
883         // neighbors in two axial directions
884         v += 2*(acc.getValue(ijk.offsetBy(-1,-1, 0)) + acc.getValue(ijk.offsetBy(-1, 1, 0)) +// xy
885                 acc.getValue(ijk.offsetBy( 1,-1, 0)) + acc.getValue(ijk.offsetBy( 1, 1, 0)) +// xy
886                 acc.getValue(ijk.offsetBy(-1, 0,-1)) + acc.getValue(ijk.offsetBy(-1, 0, 1)) +// xz
887                 acc.getValue(ijk.offsetBy( 1, 0,-1)) + acc.getValue(ijk.offsetBy( 1, 0, 1)) +// xz
888                 acc.getValue(ijk.offsetBy( 0,-1,-1)) + acc.getValue(ijk.offsetBy( 0,-1, 1)) +// yz
889                 acc.getValue(ijk.offsetBy( 0, 1,-1)) + acc.getValue(ijk.offsetBy( 0, 1, 1)));// yz
890         // neighbors in three axial directions
891         for (int i=-1; i<=1; i+=2) {
892             for (int j=-1; j<=1; j+=2) {
893                 for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k));// xyz
894             }
895         }
896         v *= ValueType(1.0f/64.0f);
897         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     static ValueType run(const Coord& ijk, const ConstAccessor &acc)
908     {
909         switch ( (ijk[0] & 1) | ((ijk[1] & 1) << 1) | ((ijk[2] & 1) << 2) ) {
910         case 0:// all even
911             return acc.getValue(ijk>>1);
912         case 1:// x is odd
913             return ValueType(0.5)*(acc.getValue(ijk.offsetBy(-1,0,0)>>1) +
914                                    acc.getValue(ijk.offsetBy( 1,0,0)>>1));
915         case 2:// y is odd
916             return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,-1,0)>>1) +
917                                    acc.getValue(ijk.offsetBy(0, 1,0)>>1));
918         case 3:// x&y are odd
919             return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,-1,0)>>1) +
920                                     acc.getValue(ijk.offsetBy(-1, 1,0)>>1) +
921                                     acc.getValue(ijk.offsetBy( 1,-1,0)>>1) +
922                                     acc.getValue(ijk.offsetBy( 1, 1,0)>>1));
923         case 4:// z is odd
924             return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,0,-1)>>1) +
925                                    acc.getValue(ijk.offsetBy(0,0, 1)>>1));
926         case 5:// x&z are odd
927             return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,0,-1)>>1) +
928                                     acc.getValue(ijk.offsetBy(-1,0, 1)>>1) +
929                                     acc.getValue(ijk.offsetBy( 1,0,-1)>>1) +
930                                     acc.getValue(ijk.offsetBy( 1,0, 1)>>1));
931         case 6:// y&z are odd
932             return ValueType(0.25)*(acc.getValue(ijk.offsetBy(0,-1,-1)>>1) +
933                                     acc.getValue(ijk.offsetBy(0,-1, 1)>>1) +
934                                     acc.getValue(ijk.offsetBy(0, 1,-1)>>1) +
935                                     acc.getValue(ijk.offsetBy(0, 1, 1)>>1));
936         }
937         // all are odd
938         ValueType v = zeroVal<ValueType>();
939         for (int i=-1; i<=1; i+=2) {
940             for (int j=-1; j<=1; j+=2) {
941                 for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k)>>1);// xyz
942             }
943         }
944         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