| 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 |