OpenVDB  0.104.0
GridTransformer.h
Go to the documentation of this file.
1 
2 //
3 // Copyright (c) 2012 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 //
30 //
32 
33 #ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
34 #define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
35 
36 #include <cmath>
37 #include <boost/bind.hpp>
38 #include <boost/function.hpp>
39 #include <boost/shared_ptr.hpp>
40 #include <tbb/blocked_range.h>
41 #include <tbb/parallel_reduce.h>
42 #include <openvdb/Grid.h>
43 #include <openvdb/Platform.h> // for round()
44 #include <openvdb/Types.h>
45 #include <openvdb/math/Math.h> // for isApproxEqual()
46 #include <openvdb/util/NullInterrupter.h>
47 #include "Interpolation.h"
48 
49 namespace openvdb {
51 namespace OPENVDB_VERSION_NAME {
52 namespace tools {
53 
77 template<typename Sampler, typename Interrupter, typename GridType>
78 inline void
79 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter);
80 
102 template<typename Sampler, typename GridType>
103 inline void
104 resampleToMatch(const GridType& inGrid, GridType& outGrid);
105 
106 
108 
109 
110 namespace internal {
111 
115 template<typename Sampler, typename TreeT>
116 class TileSampler: public Sampler
117 {
118 public:
119  typedef typename TreeT::ValueType ValueT;
120 
124  TileSampler(const CoordBBox& b, const ValueT& tileVal, bool on):
125  mBBox(b.min().asVec3d(), b.max().asVec3d()), mVal(tileVal), mActive(on), mEmpty(false)
126  {
127  mBBox.expand(-this->radius()); // shrink the bounding box by the sample radius
128  mEmpty = mBBox.empty();
129  }
130 
131  bool sample(const TreeT& inTree, const Vec3R& inCoord, ValueT& result) const
132  {
133  if (!mEmpty && mBBox.isInside(inCoord)) { result = mVal; return mActive; }
134  return Sampler::sample(inTree, inCoord, result);
135  }
136 
137 protected:
140  bool mActive, mEmpty;
141 };
142 
143 
146 template<typename TreeT>
147 struct TileSampler<PointSampler, TreeT>: public PointSampler {
148  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
149 };
150 
153 template<typename TreeT>
155  TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {}
156 };
157 
158 } // namespace internal
159 
160 
162 
163 
182 {
183 public:
184  typedef boost::shared_ptr<GridResampler> Ptr;
185  typedef boost::function<bool (void)> InterruptFunc;
186 
187  GridResampler(): mThreaded(true), mTransformTiles(true) {}
188  virtual ~GridResampler() {}
189 
191  void setThreaded(bool b) { mThreaded = b; }
193  bool threaded() const { return mThreaded; }
195  void setTransformTiles(bool b) { mTransformTiles = b; }
197  bool transformTiles() const { return mTransformTiles; }
198 
202  template<typename InterrupterType> void setInterrupter(InterrupterType&);
203 
209  OPENVDB_DEPRECATED void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
210 
211  template<typename Sampler, typename GridT, typename Transformer>
212  void transformGrid(const Transformer&,
213  const GridT& inGrid, GridT& outGrid) const;
214 
215 protected:
216  template<typename Sampler, typename GridT, typename Transformer>
217  void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const;
218 
219  bool interrupt() const { return mInterrupt && mInterrupt(); }
220 
221 private:
222  template<typename Sampler, typename InTreeT, typename OutTreeT, typename Transformer>
223  static void transformBBox(const Transformer&, const CoordBBox& inBBox,
224  const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&,
225  const Sampler& = Sampler());
226 
227  template<typename Sampler, typename TreeT, typename Transformer>
228  class RangeProcessor;
229 
230  bool mThreaded, mTransformTiles;
231  InterruptFunc mInterrupt;
232 };
233 
234 
236 
237 
257 {
258 public:
259  typedef boost::shared_ptr<GridTransformer> Ptr;
260 
261  GridTransformer(const Mat4R& xform);
263  const Vec3R& pivot,
264  const Vec3R& scale,
265  const Vec3R& rotate,
266  const Vec3R& translate,
267  const std::string& xformOrder = "tsr",
268  const std::string& rotationOrder = "zyx");
269  virtual ~GridTransformer() {}
270 
271  const Mat4R& getTransform() const { return mTransform; }
272 
273  template<class Sampler, class GridT>
274  void transformGrid(const GridT& inGrid, GridT& outGrid) const;
275 
276 private:
277  struct MatrixTransform;
278 
279  inline void init(const Vec3R& pivot, const Vec3R& scale,
280  const Vec3R& rotate, const Vec3R& translate,
281  const std::string& xformOrder, const std::string& rotOrder);
282 
283  Vec3R mPivot;
284  Vec3i mMipLevels;
285  Mat4R mTransform, mPreScaleTransform, mPostScaleTransform;
286 };
287 
288 
290 
291 
292 namespace local_util {
293 
297 template<typename T>
298 inline bool
300  math::Vec3<T>& rotate, math::Vec3<T>& translate)
301 {
302  if (!math::isAffine(m)) return false;
303 
304  // this is the translation in world space
305  translate = m.getTranslation();
306  // Extract translation.
307  math::Mat3<T> temp = m.getMat3();
308 
309  scale.init(
310  (math::Vec3<T>(1, 0, 0) * temp).length(),
311  (math::Vec3<T>(0, 1, 0) * temp).length(),
312  (math::Vec3<T>(0, 0, 1) * temp).length());
313  // Extract scale.
314  temp *= math::scale<math::Mat3<T> >(scale).inverse();
315 
316  rotate = math::eulerAngles(temp, math::XYZ_ROTATION);
317 
318  if (!rotate.eq(math::Vec3<T>::zero()) && !scale.eq(math::Vec3<T>(scale[0]))) {
319  // No unique decomposition if scale is nonuniform and rotation is nonzero.
320  return false;
321  }
322  return true;
323 }
324 
325 } // namespace local_util
326 
327 
329 
330 
335 {
336  MatrixTransform(): mat(Mat4R::identity()), invMat(Mat4R::identity()) {}
337  MatrixTransform(const Mat4R& xform): mat(xform), invMat(xform.inverse()) {}
338 
339  bool isAffine() const { return math::isAffine(mat); }
340 
341  Vec3R transform(const Vec3R& pos) const { return mat.transformH(pos); }
342 
343  Vec3R invTransform(const Vec3R& pos) const { return invMat.transformH(pos); }
344 
345  Mat4R mat, invMat;
346 };
347 
348 
350 
351 
357 {
358 public:
361  ABTransform(const math::Transform& aXform, const math::Transform& bXform):
362  mAXform(aXform),
363  mBXform(bXform),
364  mIsAffine(mAXform.isLinear() && mBXform.isLinear()),
365  mIsIdentity(mIsAffine && mAXform == mBXform)
366  {}
367 
368  bool isAffine() const { return mIsAffine; }
369 
370  bool isIdentity() const { return mIsIdentity; }
371 
372  openvdb::Vec3R transform(const openvdb::Vec3R& pos) const
373  {
374  return mBXform.worldToIndex(mAXform.indexToWorld(pos));
375  }
376 
377  openvdb::Vec3R invTransform(const openvdb::Vec3R& pos) const
378  {
379  return mAXform.worldToIndex(mBXform.indexToWorld(pos));
380  }
381 
382  const math::Transform& getA() const { return mAXform; }
383  const math::Transform& getB() const { return mBXform; }
384 
385 private:
386  const math::Transform &mAXform, &mBXform;
387  const bool mIsAffine;
388  const bool mIsIdentity;
389 };
390 
391 
392 template<typename Sampler, typename Interrupter, typename GridType>
393 inline void
394 resampleToMatch(const GridType& inGrid, GridType& outGrid, Interrupter& interrupter)
395 {
396  ABTransform xform(inGrid.transform(), outGrid.transform());
397 
398  if (Sampler::consistent() && xform.isIdentity()) {
399  // If the transform of the input and output are identical, the
400  // output tree is simply a deep copy of the input tree.
401  outGrid.setTree(inGrid.tree().copy());
402  } else if (xform.isAffine()) {
403  // If the input and output transforms are both affine, create an
404  // input to output transform (in:index-to-world * out:world-to-index)
405  // and use the fast GridTransformer API.
406  Mat4R mat = xform.getA().baseMap()->getAffineMap()->getMat4() *
407  ( xform.getB().baseMap()->getAffineMap()->getMat4().inverse() );
408 
409  GridTransformer transformer(mat);
410  transformer.setInterrupter(interrupter);
411 
412  // Transform the input grid and store the result in the output grid.
413  transformer.transformGrid<Sampler>(inGrid, outGrid);
414  } else {
415  // If either the input or the output transform is non-affine,
416  // use the slower GridResampler API.
417  GridResampler resampler;
418  resampler.setInterrupter(interrupter);
419 
420  resampler.transformGrid<Sampler>(xform, inGrid, outGrid);
421  }
422 }
423 
424 
425 template<typename Sampler, typename GridType>
426 inline void
427 resampleToMatch(const GridType& inGrid, GridType& outGrid)
428 {
429  util::NullInterrupter interrupter;
430  resampleToMatch<Sampler>(inGrid, outGrid, interrupter);
431 }
432 
433 
435 
436 
437 inline
438 GridTransformer::GridTransformer(const Mat4R& xform):
439  mPivot(0, 0, 0),
440  mMipLevels(0, 0, 0),
441  mTransform(xform),
442  mPreScaleTransform(Mat4R::identity()),
443  mPostScaleTransform(Mat4R::identity())
444 {
445  Vec3R scale, rotate, translate;
446  if (local_util::decompose(mTransform, scale, rotate, translate)) {
447  // If the transform can be decomposed into affine components,
448  // use them to set up a mipmapping-like scheme for downsampling.
449  init(mPivot, scale, rotate, translate, "srt", "zyx");
450  }
451 }
452 
453 
454 inline
456  const Vec3R& pivot, const Vec3R& scale,
457  const Vec3R& rotate, const Vec3R& translate,
458  const std::string& xformOrder, const std::string& rotOrder):
459  mPivot(0, 0, 0),
460  mMipLevels(0, 0, 0),
461  mPreScaleTransform(Mat4R::identity()),
462  mPostScaleTransform(Mat4R::identity())
463 {
464  init(pivot, scale, rotate, translate, xformOrder, rotOrder);
465 }
466 
467 
469 
470 
471 inline void
472 GridTransformer::init(
473  const Vec3R& pivot, const Vec3R& scale,
474  const Vec3R& rotate, const Vec3R& translate,
475  const std::string& xformOrder, const std::string& rotOrder)
476 {
477  if (xformOrder.size() != 3) {
478  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
479  }
480  if (rotOrder.size() != 3) {
481  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
482  }
483 
484  mPivot = pivot;
485 
486  // Scaling is handled via a mipmapping-like scheme of successive
487  // halvings of the tree resolution, until the remaining scale
488  // factor is greater than or equal to 1/2.
489  Vec3R scaleRemainder = scale;
490  for (int i = 0; i < 3; ++i) {
491  double s = std::fabs(scale(i));
492  if (s < 0.5) {
493  mMipLevels(i) = int(std::floor(-std::log(s)/std::log(2.0)));
494  scaleRemainder(i) = scale(i) * (1 << mMipLevels(i));
495  }
496  }
497 
498  // Build pre-scale and post-scale transform matrices based on
499  // the user-specified order of operations.
500  // Note that we iterate over the transform order string in reverse order
501  // (e.g., "t", "r", "s", given "srt"). This is because math::Mat matrices
502  // postmultiply row vectors rather than premultiplying column vectors.
503  mTransform = mPreScaleTransform = mPostScaleTransform = Mat4R::identity();
504  Mat4R* remainder = &mPostScaleTransform;
505  int rpos, spos, tpos;
506  rpos = spos = tpos = 3;
507  for (int ix = 2; ix >= 0; --ix) { // reverse iteration
508  switch (xformOrder[ix]) {
509 
510  case 'r':
511  rpos = ix;
512  mTransform.preTranslate(pivot);
513  remainder->preTranslate(pivot);
514 
515  int xpos, ypos, zpos;
516  xpos = ypos = zpos = 3;
517  for (int ir = 2; ir >= 0; --ir) {
518  switch (rotOrder[ir]) {
519  case 'x':
520  xpos = ir;
521  mTransform.preRotate(math::X_AXIS, rotate.x());
522  remainder->preRotate(math::X_AXIS, rotate.x());
523  break;
524  case 'y':
525  ypos = ir;
526  mTransform.preRotate(math::Y_AXIS, rotate.y());
527  remainder->preRotate(math::Y_AXIS, rotate.y());
528  break;
529  case 'z':
530  zpos = ir;
531  mTransform.preRotate(math::Z_AXIS, rotate.z());
532  remainder->preRotate(math::Z_AXIS, rotate.z());
533  break;
534  }
535  }
536  // Reject rotation order strings that don't contain exactly one
537  // instance of "x", "y" and "z".
538  if (xpos > 2 || ypos > 2 || zpos > 2) {
539  OPENVDB_THROW(ValueError, "invalid rotation order (" + rotOrder + ")");
540  }
541 
542  mTransform.preTranslate(-pivot);
543  remainder->preTranslate(-pivot);
544  break;
545 
546  case 's':
547  spos = ix;
548  mTransform.preTranslate(pivot);
549  mTransform.preScale(scale);
550  mTransform.preTranslate(-pivot);
551 
552  remainder->preTranslate(pivot);
553  remainder->preScale(scaleRemainder);
554  remainder->preTranslate(-pivot);
555  remainder = &mPreScaleTransform;
556  break;
557 
558  case 't':
559  tpos = ix;
560  mTransform.preTranslate(translate);
561  remainder->preTranslate(translate);
562  break;
563  }
564  }
565  // Reject transform order strings that don't contain exactly one
566  // instance of "t", "r" and "s".
567  if (tpos > 2 || rpos > 2 || spos > 2) {
568  OPENVDB_THROW(ValueError, "invalid transform order (" + xformOrder + ")");
569  }
570 }
571 
572 
574 
575 
576 template<typename InterrupterType>
577 void
578 GridResampler::setInterrupter(InterrupterType& interrupter)
579 {
580  mInterrupt = boost::bind(&InterrupterType::wasInterrupted,
581  /*this=*/&interrupter, /*percent=*/-1);
582 }
583 
584 
585 template<typename Sampler, typename GridT, typename Transformer>
586 void
587 GridResampler::transformGrid(const Transformer& xform,
588  const GridT& inGrid, GridT& outGrid) const
589 {
590  outGrid.setBackground(inGrid.background());
591  applyTransform<Sampler>(xform, inGrid, outGrid);
592 }
593 
594 
595 template<class Sampler, class GridT>
596 void
597 GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const
598 {
599  outGrid.setBackground(inGrid.background());
600 
601  if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) {
602  // Skip the mipmapping step.
603  const MatrixTransform xform(mTransform);
604  applyTransform<Sampler>(xform, inGrid, outGrid);
605 
606  } else {
607  bool firstPass = true;
608  const typename GridT::ValueType background = inGrid.background();
609  typename GridT::Ptr tempGrid = GridT::create(background);
610 
611  if (!mPreScaleTransform.eq(Mat4R::identity())) {
612  firstPass = false;
613  // Apply the pre-scale transform to the input grid
614  // and store the result in a temporary grid.
615  const MatrixTransform xform(mPreScaleTransform);
616  applyTransform<Sampler>(xform, inGrid, *tempGrid);
617  }
618 
619  // While the scale factor along one or more axes is less than 1/2,
620  // scale the grid by half along those axes.
621  Vec3i count = mMipLevels; // # of halvings remaining per axis
622  while (count != Vec3i::zero()) {
623  MatrixTransform xform;
624  xform.mat.setTranslation(mPivot);
625  xform.mat.preScale(Vec3R(
626  count.x() ? .5 : 1, count.y() ? .5 : 1, count.z() ? .5 : 1));
627  xform.mat.preTranslate(-mPivot);
628  xform.invMat = xform.mat.inverse();
629 
630  if (firstPass) {
631  firstPass = false;
632  // Scale the input grid and store the result in a temporary grid.
633  applyTransform<Sampler>(xform, inGrid, *tempGrid);
634  } else {
635  // Scale the temporary grid and store the result in a transient grid,
636  // then swap the two and discard the transient grid.
637  typename GridT::Ptr destGrid = GridT::create(background);
638  applyTransform<Sampler>(xform, *tempGrid, *destGrid);
639  tempGrid.swap(destGrid);
640  }
641  // (3, 2, 1) -> (2, 1, 0) -> (1, 0, 0) -> (0, 0, 0), etc.
642  count = math::maxComponent(count - 1, Vec3i::zero());
643  }
644 
645  // Apply the post-scale transform and store the result in the output grid.
646  if (!mPostScaleTransform.eq(Mat4R::identity())) {
647  const MatrixTransform xform(mPostScaleTransform);
648  applyTransform<Sampler>(xform, *tempGrid, outGrid);
649  } else {
650  outGrid.setTree(tempGrid->treePtr());
651  }
652  }
653 }
654 
655 
657 
658 
659 template<class Sampler, class TreeT, typename Transformer>
660 class GridResampler::RangeProcessor
661 {
662 public:
663  typedef typename TreeT::LeafCIter LeafIterT;
664  typedef typename TreeT::ValueAllCIter TileIterT;
665  typedef typename tree::IteratorRange<LeafIterT> LeafRange;
666  typedef typename tree::IteratorRange<TileIterT> TileRange;
667  typedef typename tree::ValueAccessor<const TreeT> InTreeAccessor;
668  typedef typename tree::ValueAccessor<TreeT> OutTreeAccessor;
669 
670  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inT, TreeT& outT):
671  mIsRoot(true), mXform(xform), mBBox(b),
672  mInTree(inT), mOutTree(&outT), mInAcc(mInTree), mOutAcc(*mOutTree)
673  {}
674 
675  RangeProcessor(const Transformer& xform, const CoordBBox& b, const TreeT& inTree):
676  mIsRoot(false), mXform(xform), mBBox(b),
677  mInTree(inTree), mOutTree(new TreeT(inTree.getBackground())),
678  mInAcc(mInTree), mOutAcc(*mOutTree)
679  {}
680 
681  ~RangeProcessor() { if (!mIsRoot) delete mOutTree; }
682 
684  RangeProcessor(RangeProcessor& other, tbb::split):
685  mIsRoot(false),
686  mXform(other.mXform),
687  mBBox(other.mBBox),
688  mInTree(other.mInTree),
689  mOutTree(new TreeT(mInTree.getBackground())),
690  mInAcc(mInTree),
691  mOutAcc(*mOutTree),
692  mInterrupt(other.mInterrupt)
693  {}
694 
695  void setInterrupt(const InterruptFunc& f) { mInterrupt = f; }
696 
698  void operator()(LeafRange& r)
699  {
700  for ( ; r; ++r) {
701  if (interrupt()) break;
702  LeafIterT i = r.iterator();
703  CoordBBox bbox(i->getOrigin(), i->getOrigin() + Coord(i->dim()));
704  if (!mBBox.empty()) {
705  // Intersect the leaf node's bounding box with mBBox.
706  bbox = CoordBBox(
707  Coord::maxComponent(bbox.min(), mBBox.min()),
708  Coord::minComponent(bbox.max(), mBBox.max()));
709  }
710  if (!bbox.empty()) {
711  transformBBox<Sampler>(mXform, bbox, mInAcc, mOutAcc, mInterrupt);
712  }
713  }
714  }
715 
717  void operator()(TileRange& r)
718  {
719  for ( ; r; ++r) {
720  if (interrupt()) break;
721 
722  TileIterT i = r.iterator();
723  // Skip voxels and background tiles.
724  if (!i.isTileValue()) continue;
725  if (!i.isValueOn() && math::isApproxEqual(*i, mOutTree->getBackground())) continue;
726 
727  CoordBBox bbox;
728  i.getBoundingBox(bbox);
729  if (!mBBox.empty()) {
730  // Intersect the tile's bounding box with mBBox.
731  bbox = CoordBBox(
732  Coord::maxComponent(bbox.min(), mBBox.min()),
733  Coord::minComponent(bbox.max(), mBBox.max()));
734  }
735  if (!bbox.empty()) {
740  internal::TileSampler<Sampler, InTreeAccessor>
741  sampler(bbox, i.getValue(), i.isValueOn());
742  transformBBox(mXform, bbox, mInAcc, mOutAcc, mInterrupt, sampler);
743  }
744  }
745  }
746 
748  void join(RangeProcessor& other)
749  {
750  if (!interrupt()) mOutTree->merge(*other.mOutTree);
751  }
752 
753 private:
754  bool interrupt() const { return mInterrupt && mInterrupt(); }
755 
756  const bool mIsRoot; // true if mOutTree is the top-level tree
757  Transformer mXform;
758  CoordBBox mBBox;
759  const TreeT& mInTree;
760  TreeT* mOutTree;
761  InTreeAccessor mInAcc;
762  OutTreeAccessor mOutAcc;
763  InterruptFunc mInterrupt;
764 };
765 
766 
768 
769 
770 template<class Sampler, class GridT, typename Transformer>
771 void
772 GridResampler::applyTransform(const Transformer& xform,
773  const GridT& inGrid, GridT& outGrid) const
774 {
775  typedef typename GridT::TreeType TreeT;
776  const TreeT& inTree = inGrid.tree();
777  TreeT& outTree = outGrid.tree();
778 
779  typedef RangeProcessor<Sampler, TreeT, Transformer> RangeProc;
780 
781  const GridClass gridClass = inGrid.getGridClass();
782 
783  if (gridClass != GRID_LEVEL_SET && mTransformTiles) {
784  // Independently transform the tiles of the input grid.
785  // Note: Tiles in level sets can only be background tiles, and they
786  // are handled more efficiently with a signed flood fill (see below).
787 
788  RangeProc proc(xform, CoordBBox(), inTree, outTree);
789  proc.setInterrupt(mInterrupt);
790 
791  typename RangeProc::TileIterT tileIter = inTree.cbeginValueAll();
792  tileIter.setMaxDepth(tileIter.getLeafDepth() - 1); // skip leaf nodes
793  typename RangeProc::TileRange tileRange(tileIter);
794 
795  if (mThreaded) {
796  tbb::parallel_reduce(tileRange, proc);
797  } else {
798  proc(tileRange);
799  }
800  }
801 
802  CoordBBox clipBBox;
803  if (gridClass == GRID_LEVEL_SET) {
804  // Inactive voxels in level sets can only be background voxels, and they
805  // are handled more efficiently with a signed flood fill (see below).
806  clipBBox = inGrid.evalActiveVoxelBoundingBox();
807  }
808 
809  // Independently transform the leaf nodes of the input grid.
810 
811  RangeProc proc(xform, clipBBox, inTree, outTree);
812  proc.setInterrupt(mInterrupt);
813 
814  typename RangeProc::LeafRange leafRange(inTree.cbeginLeaf());
815 
816  if (mThreaded) {
817  tbb::parallel_reduce(leafRange, proc);
818  } else {
819  proc(leafRange);
820  }
821 
822  // If the grid is a level set, mark inactive voxels as inside or outside.
823  if (gridClass == GRID_LEVEL_SET) {
824  outTree.pruneInactive();
825  outTree.signedFloodFill();
826  }
827 }
828 
829 
831 
832 
833 //static
834 template<class Sampler, class InTreeT, class OutTreeT, class Transformer>
835 void
836 GridResampler::transformBBox(
837  const Transformer& xform,
838  const CoordBBox& bbox,
839  const InTreeT& inTree,
840  OutTreeT& outTree,
841  const InterruptFunc& interrupt,
842  const Sampler& sampler)
843 {
844  typedef typename OutTreeT::ValueType ValueT;
845  typedef math::Vec4<Real> Vec4R;
846 
847  // Transform the corners of the input tree's bounding box
848  // and compute the enclosing bounding box in the output tree.
849  Vec3R
850  inRMin(bbox.min().x(), bbox.min().y(), bbox.min().z()),
851  inRMax(bbox.max().x(), bbox.max().y(), bbox.max().z()),
852  outRMin = math::minComponent(xform.transform(inRMin), xform.transform(inRMax)),
853  outRMax = math::maxComponent(xform.transform(inRMin), xform.transform(inRMax));
854  for (int i = 0; i < 8; ++i) {
855  Vec3R corner(
856  i & 1 ? inRMax.x() : inRMin.x(),
857  i & 2 ? inRMax.y() : inRMin.y(),
858  i & 4 ? inRMax.z() : inRMin.z());
859  outRMin = math::minComponent(outRMin, xform.transform(corner));
860  outRMax = math::maxComponent(outRMax, xform.transform(corner));
861  }
862  Vec3i
863  outMin = local_util::floorVec3(outRMin) - Sampler::radius(),
864  outMax = local_util::ceilVec3(outRMax) + Sampler::radius();
865 
866  if (!xform.isAffine()) {
867  // If the transform is not affine, back-project each output voxel
868  // into the input tree.
869  Vec3R xyz, inXYZ;
870  Coord outXYZ;
871  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
872  for (x = outMin.x(); x <= outMax.x(); ++x) {
873  if (interrupt && interrupt()) break;
874  xyz.x() = x;
875  for (y = outMin.y(); y <= outMax.y(); ++y) {
876  if (interrupt && interrupt()) break;
877  xyz.y() = y;
878  for (z = outMin.z(); z <= outMax.z(); ++z) {
879  xyz.z() = z;
880  inXYZ = xform.invTransform(xyz);
881  ValueT result;
882  if (sampler.sample(inTree, inXYZ, result)) {
883  outTree.setValueOn(outXYZ, result);
884  } else {
885  // Note: Don't overwrite existing active values with inactive values.
886  if (!outTree.isValueOn(outXYZ)) {
887  outTree.setValueOff(outXYZ, result);
888  }
889  }
890  }
891  }
892  }
893  } else { // affine
894  // Compute step sizes in the input tree that correspond to
895  // unit steps in x, y and z in the output tree.
896  const Vec3R
897  translation = xform.invTransform(Vec3R(0, 0, 0)),
898  deltaX = xform.invTransform(Vec3R(1, 0, 0)) - translation,
899  deltaY = xform.invTransform(Vec3R(0, 1, 0)) - translation,
900  deltaZ = xform.invTransform(Vec3R(0, 0, 1)) - translation;
901 
902 #if defined(__ICC)
903 
904 
905 
906  const Vec3R dummy = deltaX;
907 #endif
908 
909  // Step by whole voxels through the output tree, sampling the
910  // corresponding fractional voxels of the input tree.
911  Vec3R inStartX = xform.invTransform(Vec3R(outMin));
912  Coord outXYZ;
913  int &x = outXYZ.x(), &y = outXYZ.y(), &z = outXYZ.z();
914  for (x = outMin.x(); x <= outMax.x(); ++x, inStartX += deltaX) {
915  if (interrupt && interrupt()) break;
916  Vec3R inStartY = inStartX;
917  for (y = outMin.y(); y <= outMax.y(); ++y, inStartY += deltaY) {
918  if (interrupt && interrupt()) break;
919  Vec3R inXYZ = inStartY;
920  for (z = outMin.z(); z <= outMax.z(); ++z, inXYZ += deltaZ) {
921  ValueT result;
922  if (sampler.sample(inTree, inXYZ, result)) {
923  outTree.setValueOn(outXYZ, result);
924  } else {
925  // Note: Don't overwrite existing active values with inactive values.
926  if (!outTree.isValueOn(outXYZ)) {
927  outTree.setValueOff(outXYZ, result);
928  }
929  }
930  }
931  }
932  }
933  }
934 } // GridResampler::transformBBox()
935 
936 } // namespace tools
937 } // namespace OPENVDB_VERSION_NAME
938 } // namespace openvdb
939 
940 #endif // OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED
941 
942 // Copyright (c) 2012 DreamWorks Animation LLC
943 // All rights reserved. This software is distributed under the
944 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )