HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RayIntersector.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: Apache-2.0
3 ///
4 /// @file RayIntersector.h
5 ///
6 /// @author Ken Museth
7 ///
8 /// @brief Accelerated intersection of a ray with a narrow-band level
9 /// set or a generic (e.g. density) volume. This will of course be
10 /// useful for respectively surface and volume rendering.
11 ///
12 /// @details This file defines two main classes,
13 /// LevelSetRayIntersector and VolumeRayIntersector, as well as the
14 /// three support classes LevelSetHDDA, VolumeHDDA and LinearSearchImpl.
15 /// The LevelSetRayIntersector is templated on the LinearSearchImpl class
16 /// and calls instances of the LevelSetHDDA class. The reason to split
17 /// level set ray intersection into three classes is twofold. First
18 /// LevelSetRayIntersector defines the public API for client code and
19 /// LinearSearchImpl defines the actual algorithm used for the
20 /// ray level-set intersection. In other words this design will allow
21 /// for the public API to be fixed while the intersection algorithm
22 /// can change without resolving to (slow) virtual methods. Second,
23 /// LevelSetHDDA, which implements a hierarchical Differential Digital
24 /// Analyzer, relies on partial template specialization, so it has to
25 /// be a standalone class (as opposed to a member class of
26 /// LevelSetRayIntersector). The VolumeRayIntersector is conceptually
27 /// much simpler than the LevelSetRayIntersector, and hence it only
28 /// depends on VolumeHDDA that implements the hierarchical
29 /// Differential Digital Analyzer.
30 
31 
32 #ifndef OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED
33 #define OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED
34 
35 #include <openvdb/math/DDA.h>
36 #include <openvdb/math/Math.h>
37 #include <openvdb/math/Ray.h>
38 #include <openvdb/math/Stencils.h>
39 #include <openvdb/Grid.h>
40 #include <openvdb/Types.h>
41 #include <openvdb/util/Assert.h>
42 #include "Morphology.h"
43 #include <iostream>
44 #include <type_traits>
45 
46 
47 namespace openvdb {
49 namespace OPENVDB_VERSION_NAME {
50 namespace tools {
51 
52 // Helper class that implements the actual search of the zero-crossing
53 // of the level set along the direction of a ray. This particular
54 // implementation uses iterative linear search.
55 template<typename GridT, int Iterations = 0, typename RealT = double>
57 
58 
59 ///////////////////////////////////// LevelSetRayIntersector /////////////////////////////////////
60 
61 
62 /// @brief This class provides the public API for intersecting a ray
63 /// with a narrow-band level set.
64 ///
65 /// @details It wraps a SearchImplT with a simple public API and
66 /// performs the actual hierarchical tree node and voxel traversal.
67 ///
68 /// @warning Use the (default) copy-constructor to make sure each
69 /// computational thread has their own instance of this class. This is
70 /// important since the SearchImplT contains a ValueAccessor that is
71 /// not thread-safe. However copying is very efficient.
72 ///
73 /// @see tools/RayTracer.h for examples of intended usage.
74 ///
75 /// @todo Add TrilinearSearchImpl, as an alternative to LinearSearchImpl,
76 /// that performs analytical 3D trilinear intersection tests, i.e., solves
77 /// cubic equations. This is slower but also more accurate than the 1D
78 /// linear interpolation in LinearSearchImpl.
79 template<typename GridT,
80  typename SearchImplT = LinearSearchImpl<GridT>,
81  int NodeLevel = GridT::TreeType::RootNodeType::ChildNodeType::LEVEL,
82  typename RayT = math::Ray<Real> >
84 {
85 public:
86  using GridType = GridT;
87  using RayType = RayT;
88  using RealType = typename RayT::RealType;
89  using Vec3Type = typename RayT::Vec3T;
90  using ValueT = typename GridT::ValueType;
91  using TreeT = typename GridT::TreeType;
92 
93  static_assert(NodeLevel >= -1 && NodeLevel < int(TreeT::DEPTH)-1, "NodeLevel out of range");
95  "level set grids must have scalar, floating-point value types");
96 
97  /// @brief Constructor
98  /// @param grid level set grid to intersect rays against.
99  /// @param isoValue optional iso-value for the ray-intersection.
100  LevelSetRayIntersector(const GridT& grid, const ValueT& isoValue = zeroVal<ValueT>())
101  : mTester(grid, isoValue)
102  {
103  if (!grid.hasUniformVoxels() ) {
104  OPENVDB_THROW(RuntimeError,
105  "LevelSetRayIntersector only supports uniform voxels!");
106  }
107  if (grid.getGridClass() != GRID_LEVEL_SET) {
108  OPENVDB_THROW(RuntimeError,
109  "LevelSetRayIntersector only supports level sets!"
110  "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)");
111  }
112  }
113 
114  /// @brief Return the iso-value used for ray-intersections
115  const ValueT& getIsoValue() const { return mTester.getIsoValue(); }
116 
117  /// @brief Return @c true if the index-space ray intersects the level set.
118  /// @param iRay ray represented in index space.
119  bool intersectsIS(const RayType& iRay) const
120  {
121  if (!mTester.setIndexRay(iRay)) return false;//missed bbox
123  }
124 
125  /// @brief Return @c true if the index-space ray intersects the level set
126  /// @param iRay ray represented in index space.
127  /// @param iTime if an intersection was found it is assigned the time of the
128  /// intersection along the index ray.
129  bool intersectsIS(const RayType& iRay, RealType &iTime) const
130  {
131  if (!mTester.setIndexRay(iRay)) return false;//missed bbox
132  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
133  iTime = mTester.getIndexTime();
134  return true;
135  }
136 
137  /// @brief Return @c true if the index-space ray intersects the level set.
138  /// @param iRay ray represented in index space.
139  /// @param xyz if an intersection was found it is assigned the
140  /// intersection point in index space, otherwise it is unchanged.
141  bool intersectsIS(const RayType& iRay, Vec3Type& xyz) const
142  {
143  if (!mTester.setIndexRay(iRay)) return false;//missed bbox
144  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
145  mTester.getIndexPos(xyz);
146  return true;
147  }
148 
149  /// @brief Return @c true if the index-space ray intersects the level set.
150  /// @param iRay ray represented in index space.
151  /// @param xyz if an intersection was found it is assigned the
152  /// intersection point in index space, otherwise it is unchanged.
153  /// @param iTime if an intersection was found it is assigned the time of the
154  /// intersection along the index ray.
155  bool intersectsIS(const RayType& iRay, Vec3Type& xyz, RealType &iTime) const
156  {
157  if (!mTester.setIndexRay(iRay)) return false;//missed bbox
158  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
159  mTester.getIndexPos(xyz);
160  iTime = mTester.getIndexTime();
161  return true;
162  }
163 
164  /// @brief Return @c true if the world-space ray intersects the level set.
165  /// @param wRay ray represented in world space.
166  bool intersectsWS(const RayType& wRay) const
167  {
168  if (!mTester.setWorldRay(wRay)) return false;//missed bbox
170  }
171 
172  /// @brief Return @c true if the world-space ray intersects the level set.
173  /// @param wRay ray represented in world space.
174  /// @param wTime if an intersection was found it is assigned the time of the
175  /// intersection along the world ray.
176  bool intersectsWS(const RayType& wRay, RealType &wTime) const
177  {
178  if (!mTester.setWorldRay(wRay)) return false;//missed bbox
179  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
180  wTime = mTester.getWorldTime();
181  return true;
182  }
183 
184  /// @brief Return @c true if the world-space ray intersects the level set.
185  /// @param wRay ray represented in world space.
186  /// @param world if an intersection was found it is assigned the
187  /// intersection point in world space, otherwise it is unchanged
188  bool intersectsWS(const RayType& wRay, Vec3Type& world) const
189  {
190  if (!mTester.setWorldRay(wRay)) return false;//missed bbox
191  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
192  mTester.getWorldPos(world);
193  return true;
194  }
195 
196  /// @brief Return @c true if the world-space ray intersects the level set.
197  /// @param wRay ray represented in world space.
198  /// @param world if an intersection was found it is assigned the
199  /// intersection point in world space, otherwise it is unchanged.
200  /// @param wTime if an intersection was found it is assigned the time of the
201  /// intersection along the world ray.
202  bool intersectsWS(const RayType& wRay, Vec3Type& world, RealType &wTime) const
203  {
204  if (!mTester.setWorldRay(wRay)) return false;//missed bbox
205  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
206  mTester.getWorldPos(world);
207  wTime = mTester.getWorldTime();
208  return true;
209  }
210 
211  /// @brief Return @c true if the world-space ray intersects the level set.
212  /// @param wRay ray represented in world space.
213  /// @param world if an intersection was found it is assigned the
214  /// intersection point in world space, otherwise it is unchanged.
215  /// @param normal if an intersection was found it is assigned the normal
216  /// of the level set surface in world space, otherwise it is unchanged.
217  bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal) const
218  {
219  if (!mTester.setWorldRay(wRay)) return false;//missed bbox
220  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
221  mTester.getWorldPosAndNml(world, normal);
222  return true;
223  }
224 
225  /// @brief Return @c true if the world-space ray intersects the level set.
226  /// @param wRay ray represented in world space.
227  /// @param world if an intersection was found it is assigned the
228  /// intersection point in world space, otherwise it is unchanged.
229  /// @param normal if an intersection was found it is assigned the normal
230  /// of the level set surface in world space, otherwise it is unchanged.
231  /// @param wTime if an intersection was found it is assigned the time of the
232  /// intersection along the world ray.
233  bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal, RealType &wTime) const
234  {
235  if (!mTester.setWorldRay(wRay)) return false;//missed bbox
236  if (!math::LevelSetHDDA<TreeT, NodeLevel>::test(mTester)) return false;//missed level set
237  mTester.getWorldPosAndNml(world, normal);
238  wTime = mTester.getWorldTime();
239  return true;
240  }
241 
242 private:
243 
244  mutable SearchImplT mTester;
245 
246 };// LevelSetRayIntersector
247 
248 
249 ////////////////////////////////////// VolumeRayIntersector //////////////////////////////////////
250 
251 
252 /// @brief This class provides the public API for intersecting a ray
253 /// with a generic (e.g. density) volume.
254 /// @details Internally it performs the actual hierarchical tree node traversal.
255 /// @warning Use the (default) copy-constructor to make sure each
256 /// computational thread has their own instance of this class. This is
257 /// important since it contains a ValueAccessor that is
258 /// not thread-safe and a CoordBBox of the active voxels that should
259 /// not be re-computed for each thread. However copying is very efficient.
260 /// @par Example:
261 /// @code
262 /// // Create an instance for the master thread
263 /// VolumeRayIntersector inter(grid);
264 /// // For each additional thread use the copy constructor. This
265 /// // amortizes the overhead of computing the bbox of the active voxels!
266 /// VolumeRayIntersector inter2(inter);
267 /// // Before each ray-traversal set the index ray.
268 /// iter.setIndexRay(ray);
269 /// // or world ray
270 /// iter.setWorldRay(ray);
271 /// // Now you can begin the ray-marching using consecutive calls to VolumeRayIntersector::march
272 /// double t0=0, t1=0;// note the entry and exit times are with respect to the INDEX ray
273 /// while ( inter.march(t0, t1) ) {
274 /// // perform line-integration between t0 and t1
275 /// }}
276 /// @endcode
277 template<typename GridT,
278  int NodeLevel = GridT::TreeType::RootNodeType::ChildNodeType::LEVEL,
279  typename RayT = math::Ray<Real> >
281 {
282 public:
283  using GridType = GridT;
284  using RayType = RayT;
285  using RealType = typename RayT::RealType;
286  using RootType = typename GridT::TreeType::RootNodeType;
288 
289  static_assert(NodeLevel >= 0 && NodeLevel < int(TreeT::DEPTH)-1, "NodeLevel out of range");
290 
291  /// @brief Grid constructor
292  /// @param grid Generic grid to intersect rays against.
293  /// @param dilationCount The number of voxel dilations performed
294  /// on (a boolean copy of) the input grid. This allows the
295  /// intersector to account for the size of interpolation kernels
296  /// in client code.
297  /// @throw RuntimeError if the voxels of the grid are not uniform
298  /// or the grid is empty.
299  VolumeRayIntersector(const GridT& grid, int dilationCount = 0)
300  : mIsMaster(true)
301  , mTree(new TreeT(grid.tree(), false, TopologyCopy()))
302  , mGrid(&grid)
303  , mAccessor(*mTree)
304  {
305  if (!grid.hasUniformVoxels() ) {
306  OPENVDB_THROW(RuntimeError,
307  "VolumeRayIntersector only supports uniform voxels!");
308  }
309  if ( grid.empty() ) {
310  OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids");
311  }
312 
313  // Dilate active voxels to better account for the size of interpolation kernels
315 
316  mTree->root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false);
317 
318  mBBox.max().offset(1);//padding so the bbox of a node becomes (origin,origin + node_dim)
319  }
320 
321  /// @brief Grid and BBox constructor
322  /// @param grid Generic grid to intersect rays against.
323  /// @param bbox The axis-aligned bounding-box in the index space of the grid.
324  /// @warning It is assumed that bbox = (min, min + dim) where min denotes
325  /// to the smallest grid coordinates and dim are the integer length of the bbox.
326  /// @throw RuntimeError if the voxels of the grid are not uniform
327  /// or the grid is empty.
329  : mIsMaster(true)
330  , mTree(new TreeT(grid.tree(), false, TopologyCopy()))
331  , mGrid(&grid)
332  , mAccessor(*mTree)
333  , mBBox(bbox)
334  {
335  if (!grid.hasUniformVoxels() ) {
336  OPENVDB_THROW(RuntimeError,
337  "VolumeRayIntersector only supports uniform voxels!");
338  }
339  if ( grid.empty() ) {
340  OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids");
341  }
342  }
343 
344  /// @brief Shallow copy constructor
345  /// @warning This copy constructor creates shallow copies of data
346  /// members of the instance passed as the argument. For
347  /// performance reasons we are not using shared pointers (their
348  /// mutex-lock impairs multi-threading).
350  : mIsMaster(false)
351  , mTree(other.mTree)//shallow copy
352  , mGrid(other.mGrid)//shallow copy
353  , mAccessor(*mTree)//initialize new (vs deep copy)
354  , mRay(other.mRay)//deep copy
355  , mTmax(other.mTmax)//deep copy
356  , mBBox(other.mBBox)//deep copy
357  {
358  }
359 
360  /// @brief Destructor
361  ~VolumeRayIntersector() { if (mIsMaster) delete mTree; }
362 
363  /// @brief Return @c false if the index ray misses the bbox of the grid.
364  /// @param iRay Ray represented in index space.
365  /// @warning Call this method (or setWorldRay) before the ray
366  /// traversal starts and use the return value to decide if further
367  /// marching is required.
368  inline bool setIndexRay(const RayT& iRay)
369  {
370  mRay = iRay;
371  const bool hit = mRay.clip(mBBox);
372  if (hit) mTmax = mRay.t1();
373  return hit;
374  }
375 
376  /// @brief Return @c false if the world ray misses the bbox of the grid.
377  /// @param wRay Ray represented in world space.
378  /// @warning Call this method (or setIndexRay) before the ray
379  /// traversal starts and use the return value to decide if further
380  /// marching is required.
381  /// @details Since hit times are computed with respect to the ray
382  /// represented in index space of the current grid, it is
383  /// recommended that either the client code uses getIndexPos to
384  /// compute index position from hit times or alternatively keeps
385  /// an instance of the index ray and instead uses setIndexRay to
386  /// initialize the ray.
387  inline bool setWorldRay(const RayT& wRay)
388  {
389  return this->setIndexRay(wRay.worldToIndex(*mGrid));
390  }
391 
392  inline typename RayT::TimeSpan march()
393  {
394  const typename RayT::TimeSpan t = mHDDA.march(mRay, mAccessor);
395  if (t.t1>0) mRay.setTimes(t.t1 + math::Delta<RealType>::value(), mTmax);
396  return t;
397  }
398 
399  /// @brief Return @c true if the ray intersects active values,
400  /// i.e. either active voxels or tiles. Only when a hit is
401  /// detected are t0 and t1 updated with the corresponding entry
402  /// and exit times along the INDEX ray!
403  /// @note Note that t0 and t1 are only resolved at the node level
404  /// (e.g. a LeafNode with active voxels) as opposed to the individual
405  /// active voxels.
406  /// @param t0 If the return value > 0 this is the time of the
407  /// first hit of an active tile or leaf.
408  /// @param t1 If the return value > t0 this is the time of the
409  /// first hit (> t0) of an inactive tile or exit point of the
410  /// BBOX for the leaf nodes.
411  /// @warning t0 and t1 are computed with respect to the ray represented in
412  /// index space of the current grid, not world space!
413  inline bool march(RealType& t0, RealType& t1)
414  {
415  const typename RayT::TimeSpan t = this->march();
416  t.get(t0, t1);
417  return t.valid();
418  }
419 
420  /// @brief Generates a list of hits along the ray.
421  ///
422  /// @param list List of hits represented as time spans.
423  ///
424  /// @note ListType is a list of RayType::TimeSpan and is required to
425  /// have the two methods: clear() and push_back(). Thus, it could
426  /// be std::vector<typename RayType::TimeSpan> or
427  /// std::deque<typename RayType::TimeSpan>.
428  template <typename ListType>
429  inline void hits(ListType& list)
430  {
431  mHDDA.hits(mRay, mAccessor, list);
432  }
433 
434  /// @brief Return the floating-point index position along the
435  /// current index ray at the specified time.
436  inline Vec3R getIndexPos(RealType time) const { return mRay(time); }
437 
438  /// @brief Return the floating-point world position along the
439  /// current index ray at the specified time.
440  inline Vec3R getWorldPos(RealType time) const { return mGrid->indexToWorld(mRay(time)); }
441 
443  {
444  return time*mGrid->transform().baseMap()->applyJacobian(mRay.dir()).length();
445  }
446 
447  /// @brief Return a const reference to the input grid.
448  const GridT& grid() const { return *mGrid; }
449 
450  /// @brief Return a const reference to the (potentially dilated)
451  /// bool tree used to accelerate the ray marching.
452  const TreeT& tree() const { return *mTree; }
453 
454  /// @brief Return a const reference to the BBOX of the grid
455  const math::CoordBBox& bbox() const { return mBBox; }
456 
457  /// @brief Print bbox, statistics, memory usage and other information.
458  /// @param os a stream to which to write textual information
459  /// @param verboseLevel 1: print bbox only; 2: include boolean tree
460  /// statistics; 3: include memory usage
461  void print(std::ostream& os = std::cout, int verboseLevel = 1)
462  {
463  if (verboseLevel>0) {
464  os << "BBox: " << mBBox << std::endl;
465  if (verboseLevel==2) {
466  mTree->print(os, 1);
467  } else if (verboseLevel>2) {
468  mTree->print(os, 2);
469  }
470  }
471  }
472 
473 private:
474  using AccessorT = typename tree::ValueAccessor<const TreeT,/*IsSafe=*/false>;
475 
476  const bool mIsMaster;
477  TreeT* mTree;
478  const GridT* mGrid;
479  AccessorT mAccessor;
480  RayT mRay;
481  RealType mTmax;
482  math::CoordBBox mBBox;
484 
485 };// VolumeRayIntersector
486 
487 
488 //////////////////////////////////////// LinearSearchImpl ////////////////////////////////////////
489 
490 
491 /// @brief Implements linear iterative search for an iso-value of
492 /// the level set along the direction of the ray.
493 ///
494 /// @note Since this class is used internally in
495 /// LevelSetRayIntersector (define above) and LevelSetHDDA (defined below)
496 /// client code should never interact directly with its API. This also
497 /// explains why we are not concerned with the fact that several of
498 /// its methods are unsafe to call unless roots were already detected.
499 ///
500 /// @details It is approximate due to the limited number of iterations
501 /// which can can be defined with a template parameter. However the default value
502 /// has proven surprisingly accurate and fast. In fact more iterations
503 /// are not guaranteed to give significantly better results.
504 ///
505 /// @warning Since the root-searching algorithm is approximate
506 /// (first-order) it is possible to miss intersections if the
507 /// iso-value is too close to the inside or outside of the narrow
508 /// band (typically a distance less than a voxel unit).
509 ///
510 /// @warning Since this class internally stores a ValueAccessor it is NOT thread-safe,
511 /// so make sure to give each thread its own instance. This of course also means that
512 /// the cost of allocating an instance should (if possible) be amortized over
513 /// as many ray intersections as possible.
514 template<typename GridT, int Iterations, typename RealT>
515 class LinearSearchImpl
516 {
517 public:
520  using ValueT = typename GridT::ValueType;
521  using AccessorT = typename GridT::ConstAccessor;
523 
524  /// @brief Constructor from a grid.
525  /// @throw RunTimeError if the grid is empty.
526  /// @throw ValueError if the isoValue is not inside the narrow-band.
527  LinearSearchImpl(const GridT& grid, const ValueT& isoValue = zeroVal<ValueT>())
528  : mStencil(grid),
529  mIsoValue(isoValue),
530  mMinValue(isoValue - ValueT(2 * grid.voxelSize()[0])),
531  mMaxValue(isoValue + ValueT(2 * grid.voxelSize()[0]))
532  {
533  if ( grid.empty() ) {
534  OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids");
535  }
536  if (mIsoValue<= -grid.background() ||
537  mIsoValue>= grid.background() ){
538  OPENVDB_THROW(ValueError, "The iso-value must be inside the narrow-band!");
539  }
540  grid.tree().root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false);
541  }
542 
543  /// @brief Return the iso-value used for ray-intersections
544  const ValueT& getIsoValue() const { return mIsoValue; }
545 
546  /// @brief Return @c false if the ray misses the bbox of the grid.
547  /// @param iRay Ray represented in index space.
548  /// @warning Call this method before the ray traversal starts.
549  inline bool setIndexRay(const RayT& iRay)
550  {
551  mRay = iRay;
552  return mRay.clip(mBBox);//did it hit the bbox
553  }
554 
555  /// @brief Return @c false if the ray misses the bbox of the grid.
556  /// @param wRay Ray represented in world space.
557  /// @warning Call this method before the ray traversal starts.
558  inline bool setWorldRay(const RayT& wRay)
559  {
560  mRay = wRay.worldToIndex(mStencil.grid());
561  return mRay.clip(mBBox);//did it hit the bbox
562  }
563 
564  /// @brief Get the intersection point in index space.
565  /// @param xyz The position in index space of the intersection.
566  inline void getIndexPos(VecT& xyz) const { xyz = mRay(mTime); }
567 
568  /// @brief Get the intersection point in world space.
569  /// @param xyz The position in world space of the intersection.
570  inline void getWorldPos(VecT& xyz) const { xyz = mStencil.grid().indexToWorld(mRay(mTime)); }
571 
572  /// @brief Get the intersection point and normal in world space
573  /// @param xyz The position in world space of the intersection.
574  /// @param nml The surface normal in world space of the intersection.
575  inline void getWorldPosAndNml(VecT& xyz, VecT& nml)
576  {
577  this->getIndexPos(xyz);
578  mStencil.moveTo(xyz);
579  nml = mStencil.gradient(xyz);
580  nml.normalize();
581  xyz = mStencil.grid().indexToWorld(xyz);
582  }
583 
584  /// @brief Return the time of intersection along the index ray.
585  inline RealT getIndexTime() const { return mTime; }
586 
587  /// @brief Return the time of intersection along the world ray.
588  inline RealT getWorldTime() const
589  {
590  return mTime*mStencil.grid().transform().baseMap()->applyJacobian(mRay.dir()).length();
591  }
592 
593 private:
594 
595  /// @brief Initiate the local voxel intersection test.
596  /// @warning Make sure to call this method before the local voxel intersection test.
597  inline void init(RealT t0)
598  {
599  mT[0] = t0;
600  mV[0] = static_cast<ValueT>(this->interpValue(t0));
601  }
602 
603  inline void setRange(RealT t0, RealT t1) { mRay.setTimes(t0, t1); }
604 
605  /// @brief Return a const reference to the ray.
606  inline const RayT& ray() const { return mRay; }
607 
608  /// @brief Return true if a node of the specified type exists at ijk.
609  template <typename NodeT>
610  inline bool hasNode(const Coord& ijk)
611  {
612  return mStencil.accessor().template probeConstNode<NodeT>(ijk) != nullptr;
613  }
614 
615  /// @brief Return @c true if an intersection is detected.
616  /// @param ijk Grid coordinate of the node origin or voxel being tested.
617  /// @param time Time along the index ray being tested.
618  /// @warning Only if an intersection is detected is it safe to
619  /// call getIndexPos, getWorldPos and getWorldPosAndNml!
620  inline bool operator()(const Coord& ijk, RealT time)
621  {
622  ValueT V;
623  if (mStencil.accessor().probeValue(ijk, V) &&//within narrow band
624  V>mMinValue && V<mMaxValue) {// and close to iso-value?
625  mT[1] = time;
626  mV[1] = static_cast<ValueT>(this->interpValue(time));
627  if (math::ZeroCrossing(mV[0], mV[1])) {
628  mTime = this->interpTime();
630  for (int n=0; Iterations>0 && n<Iterations; ++n) {//resolved at compile-time
631  V = static_cast<ValueT>(this->interpValue(mTime));
632  const int m = math::ZeroCrossing(mV[0], V) ? 1 : 0;
633  mV[m] = V;
634  mT[m] = mTime;
635  mTime = this->interpTime();
636  }
638  return true;
639  }
640  mT[0] = mT[1];
641  mV[0] = mV[1];
642  }
643  return false;
644  }
645 
646  inline RealT interpTime()
647  {
648  OPENVDB_ASSERT( math::isApproxLarger(mT[1], mT[0], RealT(1e-6) ) );
649  return mT[0]+(mT[1]-mT[0])*mV[0]/(mV[0]-mV[1]);
650  }
651 
652  inline RealT interpValue(RealT time)
653  {
654  const VecT pos = mRay(time);
655  mStencil.moveTo(pos);
656  return mStencil.interpolation(pos) - mIsoValue;
657  }
658 
659  template<typename, int> friend struct math::LevelSetHDDA;
660 
661  RayT mRay;
662  StencilT mStencil;
663  RealT mTime;//time of intersection
664  ValueT mV[2];
665  RealT mT[2];
666  const ValueT mIsoValue, mMinValue, mMaxValue;
667  math::CoordBBox mBBox;
668 };// LinearSearchImpl
669 
670 } // namespace tools
671 } // namespace OPENVDB_VERSION_NAME
672 } // namespace openvdb
673 
674 #endif // OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED
bool setIndexRay(const RayT &iRay)
Return false if the ray misses the bbox of the grid.
LinearSearchImpl(const GridT &grid, const ValueT &isoValue=zeroVal< ValueT >())
Constructor from a grid.
void getWorldPos(VecT &xyz) const
Get the intersection point in world space.
void setTimes(RealT t0=math::Delta< RealT >::value(), RealT t1=std::numeric_limits< RealT >::max())
Definition: Ray.h:77
bool clip(const Vec3T &center, RealT radius)
Return true if this ray intersects the specified sphere.
Definition: Ray.h:218
bool intersectsWS(const RayType &wRay) const
Return true if the world-space ray intersects the level set.
bool ZeroCrossing(const Type &a, const Type &b)
Return true if the interval [a, b] includes zero, i.e., if either a or b is zero or if they have diff...
Definition: Math.h:824
bool normalize(T eps=T(1.0e-7))
this = normalized this
Definition: Vec3.h:363
bool setIndexRay(const RayT &iRay)
Return false if the index ray misses the bbox of the grid.
This class provides the public API for intersecting a ray with a generic (e.g. density) volume...
RealT getWorldTime() const
Return the time of intersection along the world ray.
const ValueT & getIsoValue() const
Return the iso-value used for ray-intersections.
GT_API const UT_StringHolder time
const math::CoordBBox & bbox() const
Return a const reference to the BBOX of the grid.
GLsizei const GLfloat * value
Definition: glcorearb.h:824
Ray worldToIndex(const GridType &grid) const
Return a new ray in the index space of the specified grid, assuming the existing ray is represented i...
Definition: Ray.h:172
bool intersectsIS(const RayType &iRay) const
Return true if the index-space ray intersects the level set.
static bool test(TesterT &tester)
Definition: DDA.h:151
GLuint GLsizei GLsizei * length
Definition: glcorearb.h:795
RootNodeType & root()
Return this tree's root node.
Definition: Tree.h:303
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h:246
const GridT & grid() const
Return a const reference to the input grid.
The Value Accessor Implementation and API methods. The majoirty of the API matches the API of a compa...
Definition: ValueAccessor.h:68
tree::Tree< typename RootType::template ValueConverter< bool >::Type > TreeT
bool setWorldRay(const RayT &wRay)
Return false if the ray misses the bbox of the grid.
math::Vec3< ValueType > gradient(const math::Vec3< ValueType > &xyz) const
Return the gradient in world space of the trilinear interpolation kernel.
Definition: Stencils.h:372
const AccessorType & accessor() const
Return a const reference to the ValueAccessor associated with this Stencil.
Definition: Stencils.h:207
void dilateActiveValues(TreeOrLeafManagerT &tree, const int iterations=1, const NearestNeighbors nn=NN_FACE, const TilePolicy mode=PRESERVE_TILES, const bool threaded=true)
Topologically dilate all active values (i.e. both voxels and tiles) in a tree using one of three near...
Definition: Morphology.h:1057
#define OPENVDB_ASSERT(X)
Definition: Assert.h:41
Vec3R getWorldPos(RealType time) const
Return the floating-point world position along the current index ray at the specified time...
VolumeRayIntersector(const GridT &grid, const math::CoordBBox &bbox)
Grid and BBox constructor.
GLdouble n
Definition: glcorearb.h:2008
bool probeValue(const Coord &xyz, ValueType &value) const
Return the active state of the value at a given coordinate as well as its value.
bool intersectsWS(const RayType &wRay, Vec3Type &world) const
Return true if the world-space ray intersects the level set.
void print(std::ostream &os=std::cout, int verboseLevel=1) const override
Print statistics, memory usage and other information about this tree.
Definition: Tree.h:2024
bool intersectsIS(const RayType &iRay, Vec3Type &xyz) const
Return true if the index-space ray intersects the level set.
General-purpose arithmetic and comparison routines, most of which accept arbitrary value types (or at...
const TreeT & tree() const
Return a const reference to the (potentially dilated) bool tree used to accelerate the ray marching...
Delta for small floating-point offsets.
Definition: Math.h:167
void getIndexPos(VecT &xyz) const
Get the intersection point in index space.
Axis-aligned bounding box of signed integer coordinates.
Definition: Coord.h:251
bool intersectsIS(const RayType &iRay, Vec3Type &xyz, RealType &iTime) const
Return true if the index-space ray intersects the level set.
bool intersectsWS(const RayType &wRay, Vec3Type &world, Vec3Type &normal, RealType &wTime) const
Return true if the world-space ray intersects the level set.
bool intersectsIS(const RayType &iRay, RealType &iTime) const
Return true if the index-space ray intersects the level set.
void moveTo(const Coord &ijk)
Initialize the stencil buffer with the values of voxel (i, j, k) and its neighbors.
Definition: Stencils.h:47
const ValueT & getIsoValue() const
Return the iso-value used for ray-intersections.
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN
Definition: Platform.h:163
GLdouble t
Definition: glad.h:2397
bool intersectsWS(const RayType &wRay, Vec3Type &world, Vec3Type &normal) const
Return true if the world-space ray intersects the level set.
void hits(ListType &list)
Generates a list of hits along the ray.
Implements linear iterative search for an iso-value of the level set along the direction of the ray...
void print(std::ostream &os=std::cout, int verboseLevel=1)
Print bbox, statistics, memory usage and other information.
ValueType interpolation(const math::Vec3< ValueType > &xyz) const
Return the trilinear interpolation at the normalized position.
Definition: Stencils.h:338
Helper class that implements Hierarchical Digital Differential Analyzers and is specialized for ray i...
Definition: DDA.h:145
bool intersectsWS(const RayType &wRay, Vec3Type &world, RealType &wTime) const
Return true if the world-space ray intersects the level set.
const GridType & grid() const
Return a const reference to the grid from which this stencil was constructed.
Definition: Stencils.h:203
const Vec3T & dir() const
Definition: Ray.h:100
This class provides the public API for intersecting a ray with a narrow-band level set...
bool march(RealType &t0, RealType &t1)
Return true if the ray intersects active values, i.e. either active voxels or tiles. Only when a hit is detected are t0 and t1 updated with the corresponding entry and exit times along the INDEX ray!
RealT getIndexTime() const
Return the time of intersection along the index ray.
Implementation of morphological dilation and erosion.
typename GridT::TreeType::RootNodeType RootType
bool intersectsWS(const RayType &wRay, RealType &wTime) const
Return true if the world-space ray intersects the level set.
#define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END
Definition: Platform.h:164
Tag dispatch class that distinguishes topology copy constructors from deep copy constructors.
Definition: Types.h:750
void getWorldPosAndNml(VecT &xyz, VecT &nml)
Get the intersection point and normal in world space.
Vec3R getIndexPos(RealType time) const
Return the floating-point index position along the current index ray at the specified time...
Digital Differential Analyzers specialized for VDB.
VolumeRayIntersector(const VolumeRayIntersector &other)
Shallow copy constructor.
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h:119
VolumeRayIntersector(const GridT &grid, int dilationCount=0)
Grid constructor.
bool ValueType
Definition: NanoVDB.h:5729
LevelSetRayIntersector(const GridT &grid, const ValueT &isoValue=zeroVal< ValueT >())
Constructor.
bool setWorldRay(const RayT &wRay)
Return false if the world ray misses the bbox of the grid.
Helper class that implements Hierarchical Digital Differential Analyzers for ray intersections agains...
Definition: DDA.h:188
#define OPENVDB_THROW(exception, message)
Definition: Exceptions.h:74
bool isApproxLarger(const Type &a, const Type &b, const Type &tolerance)
Return true if a is larger than b to within the given tolerance, i.e., if b - a < tolerance...
Definition: Math.h:459