HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GridTransformer.h
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (c) 2012-2017 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
29 ///////////////////////////////////////////////////////////////////////////
30 
31 /// @file GridTransformer.h
32 /// @author Peter Cucka
33 
34 #ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
35 #define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
36 
37 #include <openvdb/Grid.h>
38 #include <openvdb/Types.h>
39 #include <openvdb/math/Math.h> // for isApproxEqual()
41 #include "ChangeBackground.h"
42 #include "Interpolation.h"
43 #include "LevelSetRebuild.h" // for doLevelSetRebuild()
44 #include "SignedFloodFill.h" // for signedFloodFill
45 #include "Prune.h" // for pruneLevelSet
46 #include <tbb/blocked_range.h>
47 #include <tbb/parallel_reduce.h>
48 #include <cmath>
49 #include <functional>
50 
51 namespace openvdb {
53 namespace OPENVDB_VERSION_NAME {
54 namespace tools {
55 
56 /// @brief Resample an input grid into an output grid of the same type such that,
57 /// after resampling, the input and output grids coincide (apart from sampling
58 /// artifacts), but the output grid's transform is unchanged.
59 /// @details Specifically, this function resamples the input grid into the output
60 /// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
61 /// or QuadraticSampler.
62 /// @param inGrid the grid to be resampled
63 /// @param outGrid the grid into which to write the resampled voxel data
64 /// @param interrupter an object adhering to the util::NullInterrupter interface
65 /// @par Example:
66 /// @code
67 /// // Create an input grid with the default identity transform
68 /// // and populate it with a level-set sphere.
69 /// FloatGrid::ConstPtr src = tools::makeSphere(...);
70 /// // Create an output grid and give it a uniform-scale transform.
71 /// FloatGrid::Ptr dest = FloatGrid::create();
72 /// const float voxelSize = 0.5;
73 /// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
74 /// // Resample the input grid into the output grid, reproducing
75 /// // the level-set sphere at a smaller voxel size.
76 /// MyInterrupter interrupter = ...;
77 /// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest, interrupter);
78 /// @endcode
79 template<typename Sampler, typename Interrupter, typename GridType>
80 inline void
81 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
82 
83 /// @brief Resample an input grid into an output grid of the same type such that,
84 /// after resampling, the input and output grids coincide (apart from sampling
85 /// artifacts), but the output grid's transform is unchanged.
86 /// @details Specifically, this function resamples the input grid into the output
87 /// grid's index space, using a sampling kernel like PointSampler, BoxSampler,
88 /// or QuadraticSampler.
89 /// @param inGrid the grid to be resampled
90 /// @param outGrid the grid into which to write the resampled voxel data
91 /// @par Example:
92 /// @code
93 /// // Create an input grid with the default identity transform
94 /// // and populate it with a level-set sphere.
95 /// FloatGrid::ConstPtr src = tools::makeSphere(...);
96 /// // Create an output grid and give it a uniform-scale transform.
97 /// FloatGrid::Ptr dest = FloatGrid::create();
98 /// const float voxelSize = 0.5;
99 /// dest->setTransform(math::Transform::createLinearTransform(voxelSize));
100 /// // Resample the input grid into the output grid, reproducing
101 /// // the level-set sphere at a smaller voxel size.
102 /// tools::resampleToMatch<tools::QuadraticSampler>(*src, *dest);
103 /// @endcode
104 template<typename Sampler, typename GridType>
105 inline void
106 resampleToMatch(const GridType& inGrid, GridType& outGrid);
107 
108 
109 ////////////////////////////////////////
110 
111 
112 namespace internal {
113 
114 /// @brief A TileSampler wraps a grid sampler of another type (BoxSampler,
115 /// QuadraticSampler, etc.), and for samples that fall within a given tile
116 /// of the grid, it returns a cached tile value instead of accessing the grid.
117 template<typename Sampler, typename TreeT>
118 class TileSampler: public Sampler
119 {
120 public:
121  using ValueT = typename TreeT::ValueType;
122 
123  /// @param b the index-space bounding box of a particular grid tile
124  /// @param tileVal the tile's value
125  /// @param on the tile's active state
126  TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
127  mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
128  {
129  mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
130  mEmpty = mBBox.empty();
131  }
132 
133  bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
134  {
135  if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
136  return Sampler::sample(inTree, inCoord, result);
137  }
138 
139 protected:
143 };
144 
145 
146 /// @brief For point sampling, tree traversal is less expensive than testing
147 /// bounding box membership.
148 template<typename TreeT>
149 class TileSampler<PointSampler, TreeT>: public PointSampler {
150 public:
151  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
152 };
153 
154 /// @brief For point sampling, tree traversal is less expensive than testing
155 /// bounding box membership.
156 template<typename TreeT>
158 public:
159  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
160 };
161 
162 } // namespace internal
163 
164 
165 ////////////////////////////////////////
166 
167 
168 /// A GridResampler applies a geometric transformation to an
169 /// input grid using one of several sampling schemes, and stores
170 /// the result in an output grid.
171 ///
172 /// Usage:
173 /// @code
174 /// GridResampler resampler();
175 /// resampler.transformGrid<BoxSampler>(xform, inGrid, outGrid);
176 /// @endcode
177 /// where @c xform is a functor that implements the following methods:
178 /// @code
179 /// bool isAffine() const
180 /// openvdb::Vec3d transform(const openvdb::Vec3d&) const
181 /// openvdb::Vec3d invTransform(const openvdb::Vec3d&) const
182 /// @endcode
183 /// @note When the transform is affine and can be expressed as a 4 x 4 matrix,
184 /// a GridTransformer is much more efficient than a GridResampler.
186 {
187 public:
189  using InterruptFunc = std::function<bool (void)>;
190 
191  GridResampler(): mThreaded(true), mTransformTiles(true) {}
192  virtual ~GridResampler() {}
193 
194  /// Enable or disable threading. (Threading is enabled by default.)
195  void setThreaded(bool b) { mThreaded = b; }
196  /// Return @c true if threading is enabled.
197  bool threaded() const { return mThreaded; }
198  /// Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
199  void setTransformTiles(bool b) { mTransformTiles = b; }
200  /// Return @c true if tile processing is enabled.
201  bool transformTiles() const { return mTransformTiles; }
202 
203  /// @brief Allow processing to be aborted by providing an interrupter object.
204  /// The interrupter will be queried periodically during processing.
205  /// @see util/NullInterrupter.h for interrupter interface requirements.
206  template<typename InterrupterType> void setInterrupter(InterrupterType&);
207 
208  template<typename Sampler, typename GridT, typename Transformer>
209  void transformGrid(const Transformer&,
210  const GridT& inGrid, GridT& outGrid) const;
211 
212 protected:
213  template<typename Sampler, typename GridT, typename Transformer>
214  void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
215 
216  bool interrupt() const { return mInterrupt && mInterrupt(); }
217 
218 private:
219  template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
220  static void transformBBox(const Transformer&, const CoordBBox& inBBox,
221  const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
222  const Sampler& = Sampler());
223 
224  template<typename Sampler, typename TreeT, typename Transformer>
225  class RangeProcessor;
226 
227  bool mThreaded, mTransformTiles;
228  InterruptFunc mInterrupt;
229 };
230 
231 
232 ////////////////////////////////////////
233 
234 
235 /// @brief A GridTransformer applies a geometric transformation to an
236 /// input grid using one of several sampling schemes, and stores
237 /// the result in an output grid.
238 ///
239 /// @note GridTransformer is optimized for affine transformations.
240 ///
241 /// Usage:
242 /// @code
243 /// Mat4R xform = ...;
244 /// GridTransformer transformer(xform);
245 /// transformer.transformGrid<BoxSampler>(inGrid, outGrid);
246 /// @endcode
247 /// or
248 /// @code
249 /// Vec3R pivot = ..., scale = ..., rotate = ..., translate = ...;
250 /// GridTransformer transformer(pivot, scale, rotate, translate);
251 /// transformer.transformGrid<QuadraticSampler>(inGrid, outGrid);
252 /// @endcode
254 {
255 public:
257 
258  GridTransformer(const Mat4R& xform);
260  const Vec3R& pivot,
261  const Vec3R& scale,
262  const Vec3R& rotate,
263  const Vec3R& translate,
264  const std::string& xformOrder = "tsr",
265  const std::string& rotationOrder = "zyx");
266  ~GridTransformer() override = default;
267 
268  const Mat4R& getTransform() const { return mTransform; }
269 
270  template<class Sampler, class GridT>
271  void transformGrid(const GridT& inGrid, GridT& outGrid) const;
272 
273 private:
274  struct MatrixTransform;
275 
276  inline void init(const Vec3R& pivot, const Vec3R& scale,
277  const Vec3R& rotate, const Vec3R& translate,
278  const std::string& xformOrder, const std::string& rotOrder);
279 
280  Vec3R mPivot;
281  Vec3i mMipLevels;
282  Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
283 };
284 
285 
286 ////////////////////////////////////////
287 
288 
289 namespace local_util {
290 
291 /// @brief Decompose an affine transform into scale, rotation and translation components.
292 /// @return @c false if the given matrix is not affine or cannot otherwise be decomposed.
293 template<typename T>
294 inline bool
296  math::Vec3<T>& rotate, math::Vec3<T>& translate)
297 {
298  if (!math::isAffine(m)) return false;
299 
300  // This is the translation in world space
301  translate = m.getTranslation();
302  // Extract translation.
303  const math::Mat3<T> xform = m.getMat3();
304 
305  const math::Vec3<T> unsignedScale(
306  (math::Vec3<T>(1, 0, 0) * xform).length(),
307  (math::Vec3<T>(0, 1, 0) * xform).length(),
308  (math::Vec3<T>(0, 0, 1) * xform).length());
309 
310  const bool hasUniformScale = unsignedScale.eq(math::Vec3<T>(unsignedScale[0]));
311 
312  bool hasRotation = false;
313  bool validDecomposition = false;
314 
315  T minAngle = std::numeric_limits<T>::max();
316 
317  // If the transformation matrix contains a reflection,
318  // test different negative scales to find a decomposition
319  // that favors the optimal resampling algorithm.
320  for (size_t n = 0; n < 8; ++n) {
321 
322  const math::Vec3<T> signedScale(
323  n & 0x1 ? -unsignedScale.x() : unsignedScale.x(),
324  n & 0x2 ? -unsignedScale.y() : unsignedScale.y(),
325  n & 0x4 ? -unsignedScale.z() : unsignedScale.z());
326 
327  // Extract scale and potentially reflection.
328  const math::Mat3<T> mat = xform * math::scale<math::Mat3<T> >(signedScale).inverse();
329  if (mat.det() < T(0.0)) continue; // Skip if mat contains a reflection.
330 
331  const math::Vec3<T> tmpAngle = math::eulerAngles(mat, math::XYZ_ROTATION);
332 
333  const math::Mat3<T> rebuild =
334  math::rotation<math::Mat3<T> >(math::Vec3<T>(1, 0, 0), tmpAngle.x()) *
335  math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 1, 0), tmpAngle.y()) *
336  math::rotation<math::Mat3<T> >(math::Vec3<T>(0, 0, 1), tmpAngle.z()) *
337  math::scale<math::Mat3<T> >(signedScale);
338 
339  if (xform.eq(rebuild)) {
340 
341  const T maxAngle = std::max(std::abs(tmpAngle[0]),
342  std::max(std::abs(tmpAngle[1]), std::abs(tmpAngle[2])));
343 
344  if (!(minAngle < maxAngle)) { // Update if less or equal.
345 
346  minAngle = maxAngle;
347  rotate = tmpAngle;
348  scale = signedScale;
349 
350  hasRotation = !rotate.eq(math::Vec3<T>::zero());
351  validDecomposition = true;
352 
353  if (hasUniformScale || !hasRotation) {
354  // Current decomposition is optimal.
355  break;
356  }
357  }
358  }
359  }
360 
361  if (!validDecomposition || (hasRotation && !hasUniformScale)) {
362  // The decomposition is invalid if the transformation matrix contains shear.
363  // No unique decomposition if scale is nonuniform and rotation is nonzero.
364  return false;
365  }
366 
367  return true;
368 }
369 
370 } // namespace local_util
371 
372 
373 ////////////////////////////////////////
374 
375 
376 /// This class implements the Transformer functor interface (specifically,
377 /// the isAffine(), transform() and invTransform() methods) for a transform
378 /// that is expressed as a 4 x 4 matrix.
380 {
381  MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
382  MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
383 
384  bool isAffine() const { return math::isAffine(mat); }
385 
386  Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
387 
388  Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
389 
391 };
392 
393 
394 ////////////////////////////////////////
395 
396 
397 /// @brief This class implements the Transformer functor interface (specifically,
398 /// the isAffine(), transform() and invTransform() methods) for a transform
399 /// that maps an A grid into a B grid's index space such that, after resampling,
400 /// A's index space and transform match B's index space and transform.
402 {
403 public:
404  /// @param aXform the A grid's transform
405  /// @param bXform the B grid's transform
406  ABTransform(const math::Transform& aXform, const math::Transform& bXform):
407  mAXform(aXform),
408  mBXform(bXform),
409  mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
410  mIsIdentity(mIsAffine && mAXform == mBXform)
411  {}
412 
413  bool isAffine() const { return mIsAffine; }
414 
415  bool isIdentity() const { return mIsIdentity; }
416 
418  {
419  return mBXform.worldToIndex(mAXform.indexToWorld(pos));
420  }
421 
423  {
424  return mAXform.worldToIndex(mBXform.indexToWorld(pos));
425  }
426 
427  const math::Transform& getA() const { return mAXform; }
428  const math::Transform& getB() const { return mBXform; }
429 
430 private:
431  const math::Transform &mAXform, &mBXform;
432  const bool mIsAffine;
433  const bool mIsIdentity;
434 };
435 
436 
437 /// The normal entry points for resampling are the resampleToMatch() functions,
438 /// which correctly handle level set grids under scaling and shearing.
439 /// doResampleToMatch() is mainly for internal use but is typically faster
440 /// for level sets, and correct provided that no scaling or shearing is needed.
441 ///
442 /// @warning Do not use this function to scale or shear a level set grid.
443 template<typename Sampler, typename Interrupter, typename GridType>
444 inline void
445 doResampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
446 {
447  ABTransform xform(inGrid.transform(), outGrid.transform());
448 
449  if (Sampler::consistent() && xform.isIdentity()) {
450  // If the transforms of the input and output are identical, the
451  // output tree is simply a deep copy of the input tree.
452  outGrid.setTree(inGrid.tree().copy());
453  } else if (xform.isAffine()) {
454  // If the input and output transforms are both affine, create an
455  // input to output transform (in:index-to-world * out:world-to-index)
456  // and use the fast GridTransformer API.
457  Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
458  ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
459 
460  GridTransformer transformer(mat);
461  transformer.setInterrupter(interrupter);
462 
463  // Transform the input grid and store the result in the output grid.
464  transformer.transformGrid<Sampler>(inGrid, outGrid);
465  } else {
466  // If either the input or the output transform is non-affine,
467  // use the slower GridResampler API.
468  GridResampler resampler;
469  resampler.setInterrupter(interrupter);
470 
471  resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
472  }
473 }
474 
475 
476 template<typename Sampler, typename Interrupter, typename GridType>
477 inline void
478 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
479 {
480  if (inGrid.getGridClass() == GRID_LEVEL_SET) {
481  // If the input grid is a level set, resample it using the level set rebuild tool.
482 
483  if (inGrid.constTransform() == outGrid.constTransform()) {
484  // If the transforms of the input and output grids are identical,
485  // the output tree is simply a deep copy of the input tree.
486  outGrid.setTree(inGrid.tree().copy());
487  return;
488  }
489 
490  // If the output grid is a level set, resample the input grid to have the output grid's
491  // background value. Otherwise, preserve the input grid's background value.
492  using ValueT = typename GridType::ValueType;
493  const ValueT halfWidth = ((outGrid.getGridClass() == openvdb::GRID_LEVEL_SET)
494  ? ValueT(outGrid.background() * (1.0 / outGrid.voxelSize()[0]))
495  : ValueT(inGrid.background() * (1.0 / inGrid.voxelSize()[0])));
496 
497  typename GridType::Ptr tempGrid;
498  try {
499  tempGrid = doLevelSetRebuild(inGrid, /*iso=*/zeroVal<ValueT>(),
500  /*exWidth=*/halfWidth, /*inWidth=*/halfWidth,
501  &outGrid.constTransform(), &interrupter);
502  } catch (TypeError&) {
503  // The input grid is classified as a level set, but it has a value type
504  // that is not supported by the level set rebuild tool. Fall back to
505  // using the generic resampler.
506  tempGrid.reset();
507  }
508  if (tempGrid) {
509  outGrid.setTree(tempGrid->treePtr());
510  return;
511  }
512  }
513 
514  // If the input grid is not a level set, use the generic resampler.
515  doResampleToMatch<Sampler>(inGrid, outGrid, interrupter);
516 }
517 
518 
519 template<typename Sampler, typename GridType>
520 inline void
521 resampleToMatch(const GridType& inGrid, GridType& outGrid)
522 {
523  util::NullInterrupter interrupter;
524  resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
525 }
526 
527 
528 ////////////////////////////////////////
529 
530 
531 inline
533  mPivot(0, 0, 0),
534  mMipLevels(0, 0, 0),
535  mTransform(xform),
536  mPreScaleTransform(Mat4R::identity()),
537  mPostScaleTransform(Mat4R::identity())
538 {
539  Vec3R scale, rotate, translate;
540  if (local_util::decompose(mTransform, scale, rotate, translate)) {
541  // If the transform can be decomposed into affine components,
542  // use them to set up a mipmapping-like scheme for downsampling.
543  init(mPivot, scale, rotate, translate, "srt", "zyx");
544  }
545 }
546 
547 
548 inline
550  const Vec3R& pivot, const Vec3R& scale,
551  const Vec3R& rotate, const Vec3R& translate,
552  const std::string& xformOrder, const std::string& rotOrder):
553  mPivot(0, 0, 0),
554  mMipLevels(0, 0, 0),
555  mPreScaleTransform(Mat4R::identity()),
556  mPostScaleTransform(Mat4R::identity())
557 {
558  init(pivot, scale, rotate, translate, xformOrder, rotOrder);
559 }
560 
561 
562 ////////////////////////////////////////
563 
564 
565 inline void
566 GridTransformer::init(
567  const Vec3R& pivot, const Vec3R& scale,
568  const Vec3R& rotate, const Vec3R& translate,
569  const std::string& xformOrder, const std::string& rotOrder)
570 {
571  if (xformOrder.size() != 3) {
572  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
573  }
574  if (rotOrder.size() != 3) {
575  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
576  }
577 
578  mPivot = pivot;
579 
580  // Scaling is handled via a mipmapping-like scheme of successive
581  // halvings of the tree resolution, until the remaining scale
582  // factor is greater than or equal to 1/2.
583  Vec3R scaleRemainder = scale;
584  for (int i = 0; i < 3; ++i) {
585  double s = std::fabs(scale(i));
586  if (s < 0.5) {
587  mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
588  scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
589  }
590  }
591 
592  // Build pre-scale and post-scale transform matrices based on
593  // the user-specified order of operations.
594  // Note that we iterate over the transform order string in reverse order
595  // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
596  // postmultiply row vectors rather than premultiplying column vectors.
597  mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
598  Mat4R* remainder = &mPostScaleTransform;
599  int rpos, spos, tpos;
600  rpos = spos = tpos = 3;
601  for (int ix = 2; ix >= 0; --ix) { // reverse iteration
602  switch (xformOrder[ix]) {
603 
604  case 'r':
605  rpos = ix;
606  mTransform.preTranslate(pivot);
607  remainder->preTranslate(pivot);
608 
609  int xpos, ypos, zpos;
610  xpos = ypos = zpos = 3;
611  for (int ir = 2; ir >= 0; --ir) {
612  switch (rotOrder[ir]) {
613  case 'x':
614  xpos = ir;
615  mTransform.preRotate(math::X_AXIS, rotate.x());
616  remainder->preRotate(math::X_AXIS, rotate.x());
617  break;
618  case 'y':
619  ypos = ir;
620  mTransform.preRotate(math::Y_AXIS, rotate.y());
621  remainder->preRotate(math::Y_AXIS, rotate.y());
622  break;
623  case 'z':
624  zpos = ir;
625  mTransform.preRotate(math::Z_AXIS, rotate.z());
626  remainder->preRotate(math::Z_AXIS, rotate.z());
627  break;
628  }
629  }
630  // Reject rotation order strings that don't contain exactly one
631  // instance of "x", "y" and "z".
632  if (xpos > 2 || ypos > 2 || zpos > 2) {
633  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
634  }
635 
636  mTransform.preTranslate(-pivot);
637  remainder->preTranslate(-pivot);
638  break;
639 
640  case 's':
641  spos = ix;
642  mTransform.preTranslate(pivot);
643  mTransform.preScale(scale);
644  mTransform.preTranslate(-pivot);
645 
646  remainder->preTranslate(pivot);
647  remainder->preScale(scaleRemainder);
648  remainder->preTranslate(-pivot);
649  remainder = &mPreScaleTransform;
650  break;
651 
652  case 't':
653  tpos = ix;
654  mTransform.preTranslate(translate);
655  remainder->preTranslate(translate);
656  break;
657  }
658  }
659  // Reject transform order strings that don't contain exactly one
660  // instance of "t", "r" and "s".
661  if (tpos > 2 || rpos > 2 || spos > 2) {
662  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
663  }
664 }
665 
666 
667 ////////////////////////////////////////
668 
669 
670 template<typename InterrupterType>
671 void
672 GridResampler::setInterrupter(InterrupterType& interrupter)
673 {
674  mInterrupt = std::bind(&InterrupterType::wasInterrupted,
675  /*this=*/&interrupter, /*percent=*/-1);
676 }
677 
678 
679 template<typename Sampler, typename GridT, typename Transformer>
680 void
681 GridResampler::transformGrid(const Transformer& xform,
682  const GridT& inGrid, GridT& outGrid) const
683 {
684  tools::changeBackground(outGrid.tree(), inGrid.background());
685  applyTransform<Sampler>(xform, inGrid, outGrid);
686 }
687 
688 
689 template<class Sampler, class GridT>
690 void
691 GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
692 {
693  tools::changeBackground(outGrid.tree(), inGrid.background());
694 
695  if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
696  // Skip the mipmapping step.
697  const MatrixTransform xform(mTransform);
698  applyTransform<Sampler>(xform, inGrid, outGrid);
699 
700  } else {
701  bool firstPass = true;
702  const typename GridT::ValueType background = inGrid.background();
703  typename GridT::Ptr tempGrid = GridT::create(background);
704 
705  if (!mPreScaleTransform.eq(Mat4R::identity())) {
706  firstPass = false;
707  // Apply the pre-scale transform to the input grid
708  // and store the result in a temporary grid.
709  const MatrixTransform xform(mPreScaleTransform);
710  applyTransform<Sampler>(xform, inGrid, *tempGrid);
711  }
712 
713  // While the scale factor along one or more axes is less than 1/2,
714  // scale the grid by half along those axes.
715  Vec3i count = mMipLevels; // # of halvings remaining per axis
716  while (count != Vec3i::zero()) {
717  MatrixTransform xform;
718  xform.mat.setTranslation(mPivot);
719  xform.mat.preScale(Vec3R(
720  count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
721  xform.mat.preTranslate(-mPivot);
722  xform.invMat = xform.mat.inverse();
723 
724  if (firstPass) {
725  firstPass = false;
726  // Scale the input grid and store the result in a temporary grid.
727  applyTransform<Sampler>(xform, inGrid, *tempGrid);
728  } else {
729  // Scale the temporary grid and store the result in a transient grid,
730  // then swap the two and discard the transient grid.
731  typename GridT::Ptr destGrid = GridT::create(background);
732  applyTransform<Sampler>(xform, *tempGrid, *destGrid);
733  tempGrid.swap(destGrid);
734  }
735  // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
736  count = math::maxComponent(count - 1, Vec3i::zero());
737  }
738 
739  // Apply the post-scale transform and store the result in the output grid.
740  if (!mPostScaleTransform.eq(Mat4R::identity())) {
741  const MatrixTransform xform(mPostScaleTransform);
742  applyTransform<Sampler>(xform, *tempGrid, outGrid);
743  } else {
744  outGrid.setTree(tempGrid->treePtr());
745  }
746  }
747 }
748 
749 
750 ////////////////////////////////////////
751 
752 
753 template<class Sampler, class TreeT, typename Transformer>
754 class GridResampler::RangeProcessor
755 {
756 public:
757  using LeafIterT = typename TreeT::LeafCIter;
758  using TileIterT = typename TreeT::ValueAllCIter;
759  using LeafRange = typename tree::IteratorRange<LeafIterT>;
760  using TileRange = typename tree::IteratorRange<TileIterT>;
761  using InTreeAccessor = typename tree::ValueAccessor<const TreeT>;
762  using OutTreeAccessor = typename tree::ValueAccessor<TreeT>;
763 
764  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
765  mIsRoot(true), mXform(xform), mBBox(b),
766  mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
767  {}
768 
769  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
770  mIsRoot(false), mXform(xform), mBBox(b),
771  mInTree(inTree), mOutTree(new TreeT(inTree.background())),
772  mInAcc(mInTree), mOutAcc(*mOutTree)
773  {}
774 
775  ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
776 
777  /// Splitting constructor: don't copy the original processor's output tree
778  RangeProcessor(RangeProcessor& other, tbb::split):
779  mIsRoot(false),
780  mXform(other.mXform),
781  mBBox(other.mBBox),
782  mInTree(other.mInTree),
783  mOutTree(new TreeT(mInTree.background())),
784  mInAcc(mInTree),
785  mOutAcc(*mOutTree),
786  mInterrupt(other.mInterrupt)
787  {}
788 
789  void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
790 
791  /// Transform each leaf node in the given range.
792  void operator()(LeafRange& r)
793  {
794  for ( ; r; ++r) {
795  if (interrupt()) break;
796  LeafIterT i = r.iterator();
797  CoordBBox bbox(i->origin(), i->origin() + Coord(i->dim()));
798  if (!mBBox.empty()) {
799  // Intersect the leaf node's bounding box with mBBox.
800  bbox = CoordBBox(
801  Coord::maxComponent(bbox.min(), mBBox.min()),
802  Coord::minComponent(bbox.max(), mBBox.max()));
803  }
804  if (!bbox.empty()) {
805  transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
806  }
807  }
808  }
809 
810  /// Transform each non-background tile in the given range.
811  void operator()(TileRange& r)
812  {
813  for ( ; r; ++r) {
814  if (interrupt()) break;
815 
816  TileIterT i = r.iterator();
817  // Skip voxels and background tiles.
818  if (!i.isTileValue()) continue;
819  if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->background())) continue;
820 
821  CoordBBox bbox;
822  i.getBoundingBox(bbox);
823  if (!mBBox.empty()) {
824  // Intersect the tile's bounding box with mBBox.
825  bbox = CoordBBox(
826  Coord::maxComponent(bbox.min(), mBBox.min()),
827  Coord::minComponent(bbox.max(), mBBox.max()));
828  }
829  if (!bbox.empty()) {
830  /// @todo This samples the tile voxel-by-voxel, which is much too slow.
831  /// Instead, compute the largest axis-aligned bounding box that is
832  /// contained in the transformed tile (adjusted for the sampler radius)
833  /// and fill it with the tile value. Then transform the remaining voxels.
834  internal::TileSampler<Sampler, InTreeAccessor>
835  sampler(bbox, i.getValue(), i.isValueOn());
836  transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
837  }
838  }
839  }
840 
841  /// Merge another processor's output tree into this processor's tree.
842  void join(RangeProcessor& other)
843  {
844  if (!interrupt()) mOutTree->merge(*other.mOutTree);
845  }
846 
847 private:
848  bool interrupt() const { return mInterrupt && mInterrupt(); }
849 
850  const bool mIsRoot; // true if mOutTree is the top-level tree
851  Transformer mXform;
852  CoordBBox mBBox;
853  const TreeT& mInTree;
854  TreeT* mOutTree;
855  InTreeAccessor mInAcc;
856  OutTreeAccessor mOutAcc;
857  InterruptFunc mInterrupt;
858 };
859 
860 
861 ////////////////////////////////////////
862 
863 
864 template<class Sampler, class GridT, typename Transformer>
865 void
866 GridResampler::applyTransform(const Transformer& xform,
867  const GridT& inGrid, GridT& outGrid) const
868 {
869  using TreeT = typename GridT::TreeType;
870  const TreeT& inTree = inGrid.tree();
871  TreeT& outTree = outGrid.tree();
872 
873  using RangeProc = RangeProcessor<Sampler, TreeT, Transformer>;
874 
875  const GridClass gridClass = inGrid.getGridClass();
876 
877  if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
878  // Independently transform the tiles of the input grid.
879  // Note: Tiles in level sets can only be background tiles, and they
880  // are handled more efficiently with a signed flood fill (see below).
881 
882  RangeProc proc(xform, CoordBBox(), inTree, outTree);
883  proc.setInterrupt(mInterrupt);
884 
885  typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
886  tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
887  typename RangeProc::TileRange tileRange(tileIter);
888 
889  if (mThreaded) {
890  tbb::parallel_reduce(tileRange, proc);
891  } else {
892  proc(tileRange);
893  }
894  }
895 
896  CoordBBox clipBBox;
897  if (gridClass == GRID_LEVEL_SET) {
898  // Inactive voxels in level sets can only be background voxels, and they
899  // are handled more efficiently with a signed flood fill (see below).
900  clipBBox = inGrid.evalActiveVoxelBoundingBox();
901  }
902 
903  // Independently transform the leaf nodes of the input grid.
904 
905  RangeProc proc(xform, clipBBox, inTree, outTree);
906  proc.setInterrupt(mInterrupt);
907 
908  typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
909 
910  if (mThreaded) {
911  tbb::parallel_reduce(leafRange, proc);
912  } else {
913  proc(leafRange);
914  }
915 
916  // If the grid is a level set, mark inactive voxels as inside or outside.
917  if (gridClass == GRID_LEVEL_SET) {
918  tools::pruneLevelSet(outTree);
919  tools::signedFloodFill(outTree);
920  }
921 }
922 
923 
924 ////////////////////////////////////////
925 
926 
927 //static
928 template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
929 void
930 GridResampler::transformBBox(
931  const Transformer& xform,
932  const CoordBBox& bbox,
933  const InTreeT& inTree,
934  OutTreeT& outTree,
935  const InterruptFunc& interrupt,
936  const Sampler& sampler)
937 {
938  using ValueT = typename OutTreeT::ValueType;
939 
940  // Transform the corners of the input tree's bounding box
941  // and compute the enclosing bounding box in the output tree.
942  Vec3R
943  inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
944  inRMax(bbox.max().x(), bbox.max().y(), bbox.max().z()),
945  outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
946  outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
947  for (int i = 0; i < 8; ++i) {
948  Vec3R corner(
949  i & 1 ? inRMax.x() : inRMin.x(),
950  i & 2 ? inRMax.y() : inRMin.y(),
951  i & 4 ? inRMax.z() : inRMin.z());
952  outRMin = math::minComponent(outRMin, xform.transform(corner));
953  outRMax = math::maxComponent(outRMax, xform.transform(corner));
954  }
955  Vec3i
956  outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
957  outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
958 
959  if (!xform.isAffine()) {
960  // If the transform is not affine, back-project each output voxel
961  // into the input tree.
962  Vec3R xyz, inXYZ;
963  Coord outXYZ;
964  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
965  for (x = outMin.x(); x <= outMax.x(); ++x) {
966  if (interrupt && interrupt()) break;
967  xyz.x() = x;
968  for (y = outMin.y(); y <= outMax.y(); ++y) {
969  if (interrupt && interrupt()) break;
970  xyz.y() = y;
971  for (z = outMin.z(); z <= outMax.z(); ++z) {
972  xyz.z() = z;
973  inXYZ = xform.invTransform(xyz);
974  ValueT result;
975  if (sampler.sample(inTree, inXYZ, result)) {
976  outTree.setValueOn(outXYZ, result);
977  } else {
978  // Note: Don't overwrite existing active values with inactive values.
979  if (!outTree.isValueOn(outXYZ)) {
980  outTree.setValueOff(outXYZ, result);
981  }
982  }
983  }
984  }
985  }
986  } else { // affine
987  // Compute step sizes in the input tree that correspond to
988  // unit steps in x, y and z in the output tree.
989  const Vec3R
990  translation = xform.invTransform(Vec3R(0, 0, 0)),
991  deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
992  deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
993  deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
994 
995 #if defined(__ICC)
996  /// @todo The following line is a workaround for bad code generation
997  /// in opt-icc11.1_64 (but not debug or gcc) builds. It should be
998  /// removed once the problem has been addressed at its source.
999  const Vec3R dummy = deltaX;
1000 #endif
1001 
1002  // Step by whole voxels through the output tree, sampling the
1003  // corresponding fractional voxels of the input tree.
1004  Vec3R inStartX = xform.invTransform(Vec3R(outMin));
1005  Coord outXYZ;
1006  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
1007  for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
1008  if (interrupt && interrupt()) break;
1009  Vec3R inStartY = inStartX;
1010  for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
1011  if (interrupt && interrupt()) break;
1012  Vec3R inXYZ = inStartY;
1013  for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
1014  ValueT result;
1015  if (sampler.sample(inTree, inXYZ, result)) {
1016  outTree.setValueOn(outXYZ, result);
1017  } else {
1018  // Note: Don't overwrite existing active values with inactive values.
1019  if (!outTree.isValueOn(outXYZ)) {
1020  outTree.setValueOff(outXYZ, result);
1021  }
1022  }
1023  }
1024  }
1025  }
1026  }
1027 } // GridResampler::transformBBox()
1028 
1029 } // namespace tools
1030 } // namespace OPENVDB_VERSION_NAME
1031 } // namespace openvdb
1032 
1033 #endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
1034 
1035 // Copyright (c) 2012-2017 DreamWorks Animation LLC
1036 // All rights reserved. This software is distributed under the
1037 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
bool transformTiles() const
Return true if tile processing is enabled.
void setThreaded(bool b)
Enable or disable threading. (Threading is enabled by default.)
Vec2< T > minComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise minimum of the two vectors.
Definition: Vec2.h:553
TileSampler(const CoordBBox &, const typename TreeT::ValueType &, bool)
void transformGrid(const GridT &inGrid, GridT &outGrid) const
Vec3d indexToWorld(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:135
math::Vec3< Real > Vec3R
Definition: Types.h:75
bool sample(const TreeT &inTree, const Vec3R &inCoord, ValueT &result) const
const hboost::disable_if_c< VecTraits< T >::IsVec, T >::type & min(const T &a, const T &b)
Definition: Composite.h:128
Vec3< T0 > transformH(const Vec3< T0 > &p) const
Transform a Vec3 by post-multiplication, doing homogenous divison.
Definition: Mat4.h:1080
static bool sample(const TreeT &inTree, const Vec3R &inCoord, typename TreeT::ValueType &result)
Sample inTree at the floating-point index coordinate inCoord and store the result in result...
GLsizei const GLchar *const * string
Definition: glcorearb.h:813
png_infop png_color_16p * background
Definition: png.h:2326
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:847
void changeBackground(TreeOrLeafManagerT &tree, const typename TreeOrLeafManagerT::ValueType &background, bool threaded=true, size_t grainSize=32)
Replace the background value in all the nodes of a tree.
openvdb::Vec3R transform(const openvdb::Vec3R &pos) const
hboost::enable_if< hboost::is_floating_point< typename GridType::ValueType >, typename GridType::Ptr >::type doLevelSetRebuild(const GridType &grid, typename GridType::ValueType iso, typename GridType::ValueType exWidth, typename GridType::ValueType inWidth, const math::Transform *xform, InterruptT *interrupter)
void expand(ElementType padding)
Pad this bounding box.
Definition: BBox.h:356
ABTransform(const math::Transform &aXform, const math::Transform &bXform)
GLint y
Definition: glcorearb.h:102
GLuint sampler
Definition: glcorearb.h:1655
Dummy NOOP interrupter class defining interface.
png_uint_32 i
Definition: png.h:2877
void setTranslation(const Vec3< T > &t)
Definition: Mat4.h:368
void preRotate(Axis axis, T angle)
Left multiplies by a rotation clock-wiseabout the given axis into this matrix.
Definition: Mat4.h:852
Vec3< typename MatType::value_type > eulerAngles(const MatType &mat, RotationOrder rotationOrder, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the Euler angles composing the given rotation matrix.
Definition: Mat.h:330
const hboost::disable_if_c< VecTraits< T >::IsVec, T >::type & max(const T &a, const T &b)
Definition: Composite.h:132
std::shared_ptr< T > SharedPtr
Definition: Types.h:130
SYS_API double log(double x)
GA_API const UT_StringHolder scale
bool decompose(const math::Mat4< T > &m, math::Vec3< T > &scale, math::Vec3< T > &rotate, math::Vec3< T > &translate)
Decompose an affine transform into scale, rotation and translation components.
GLdouble n
Definition: glcorearb.h:2007
GLfloat f
Definition: glcorearb.h:1925
IMATH_INTERNAL_NAMESPACE_HEADER_ENTER T abs(T a)
Definition: ImathFun.h:55
Calculate an axis-aligned bounding box in index space from a bounding sphere in world space...
Definition: Transform.h:66
void preScale(const Vec3< T0 > &v)
Definition: Mat4.h:790
#define OPENVDB_VERSION_NAME
Definition: version.h:43
Vec3< T > getTranslation() const
Return the translation component.
Definition: Mat4.h:363
void applyTransform(const Transformer &, const GridT &inGrid, GridT &outGrid) const
void transformGrid(const Transformer &, const GridT &inGrid, GridT &outGrid) const
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
Efficient multi-threaded replacement of the background values in tree.
bool eq(const Mat3 &m, T eps=1.0e-8) const
Test if "this" is equivalent to m with tolerance of eps value.
Definition: Mat3.h:355
openvdb::Vec3R invTransform(const openvdb::Vec3R &pos) const
Defined various multi-threaded utility functions for trees.
This class implements the Transformer functor interface (specifically, the isAffine(), transform() and invTransform() methods) for a transform that maps an A grid into a B grid's index space such that, after resampling, A's index space and transform match B's index space and transform.
void preTranslate(const Vec3< T0 > &tr)
Left multiples by the specified translation, i.e. Trans * (*this)
Definition: Mat4.h:757
bool threaded() const
Return true if threading is enabled.
bool empty() const
Return true if the BBox is empty, i.e. has no (positive) volume.
Definition: BBox.h:231
A TileSampler wraps a grid sampler of another type (BoxSampler, QuadraticSampler, etc...
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1221
GLint GLsizei count
Definition: glcorearb.h:404
bool isApproxEqual(const Type &a, const Type &b)
Return true if a is equal to b to within the default floating-point comparison tolerance.
Definition: Math.h:370
int floor(T x)
Definition: ImathFun.h:150
Propagates the sign of distance values from the active voxels in the narrow band to the inactive valu...
Provises a unified interface for sampling, i.e. interpolation.
Definition: Interpolation.h:90
void resampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
Resample an input grid into an output grid of the same type such that, after resampling, the input and output grids coincide (apart from sampling artifacts), but the output grid's transform is unchanged.
MatType scale(const Vec3< typename MatType::value_type > &s)
Return a matrix that scales by s.
Definition: Mat.h:610
typedef int
Definition: png.h:1175
bool eq(const Vec3< T > &v, T eps=static_cast< T >(1.0e-7)) const
Test if "this" vector is equivalent to vector v with tolerance of eps.
Definition: Vec3.h:157
bool eq(const Mat4 &m, T eps=1.0e-8) const
Test if "this" is equivalent to m with tolerance of eps value.
Definition: Mat4.h:387
void signedFloodFill(TreeOrLeafManagerT &tree, bool threaded=true, size_t grainSize=1, Index minLevel=0)
Set the values of all inactive voxels and tiles of a narrow-band level set from the signs of the acti...
T & x()
Reference to the component, e.g. v.x() = 4.5f;.
Definition: Vec3.h:109
A GridTransformer applies a geometric transformation to an input grid using one of several sampling s...
TileSampler(const CoordBBox &b, const ValueT &tileVal, bool on)
GLint GLenum GLint x
Definition: glcorearb.h:408
Vec2< T > maxComponent(const Vec2< T > &v1, const Vec2< T > &v2)
Return component-wise maximum of the two vectors.
Definition: Vec2.h:562
static const Mat4< Real > & identity()
Predefined constant for identity matrix.
Definition: Mat4.h:152
GA_API const UT_StringHolder pivot
void setTransformTiles(bool b)
Enable or disable processing of tiles. (Enabled by default, except for level set grids.)
void setInterrupter(InterrupterType &)
Allow processing to be aborted by providing an interrupter object. The interrupter will be queried pe...
void doResampleToMatch(const GridType &inGrid, GridType &outGrid, Interrupter &interrupter)
GLboolean r
Definition: glcorearb.h:1221
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:71
Vec3d worldToIndex(const Vec3d &xyz) const
Apply this transformation to the given coordinates.
Definition: Transform.h:137
void pruneLevelSet(TreeT &tree, bool threaded=true, size_t grainSize=1)
Reduce the memory footprint of a tree by replacing nodes whose values are all inactive with inactive ...
Definition: Prune.h:429
bool wasInterrupted(T *i, int percent=-1)
math::Mat4< Real > Mat4R
Definition: Types.h:102
bool isAffine(const Mat4< T > &m)
Definition: Mat4.h:1363
Mat4 inverse(T tolerance=0) const
Definition: Mat4.h:539
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:101
bool isInside(const Vec3T &xyz) const
Return true if point (x, y, z) is inside this bounding box.
Definition: BBox.h:302
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:794
MatType rotation(const Quat< typename MatType::value_type > &q, typename MatType::value_type eps=static_cast< typename MatType::value_type >(1.0e-8))
Return the rotation matrix specified by the given quaternion.
Definition: Mat.h:169