Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-27 09:40:39

0001 //------------------------------- -*- C++ -*- -------------------------------//
0002 // Copyright Celeritas contributors: see top-level COPYRIGHT file for details
0003 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0004 //---------------------------------------------------------------------------//
0005 //! \file orange/detail/BvhIntersectingVolFinder.hh
0006 //---------------------------------------------------------------------------//
0007 #pragma once
0008 
0009 #include "corecel/cont/IdStack.hh"
0010 #include "corecel/math/Algorithms.hh"
0011 #include "orange/OrangeTypes.hh"
0012 
0013 #include "BvhView.hh"
0014 #include "../BoundingBoxUtils.hh"
0015 #include "../univ/detail/Types.hh"
0016 
0017 #if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)
0018 #    if __GNUC__ < 9
0019 // We are using GCC (not Clang, not ICC)
0020 #        define CELER_GCC8_BUGGY_CONSTEXPR 1
0021 #    else
0022 #        define CELER_GCC8_BUGGY_CONSTEXPR 0
0023 #    endif
0024 #else
0025 #    define CELER_GCC8_BUGGY_CONSTEXPR 0
0026 #endif
0027 
0028 namespace celeritas
0029 {
0030 namespace detail
0031 {
0032 //---------------------------------------------------------------------------//
0033 /*!
0034  * Traverse the BVH to the find the volume that the ray intersects with first.
0035  *
0036  * Traversal is carried out using a depth first search. During traversal, the
0037  * minimum intersection is stored. The decision to traverse an edge is done by
0038  * calculating the distance to intersection with the precomputed edge bounding
0039  * box. The edge bounding box is the bounding box created by clipping an
0040  * infinite bounding box with all bounding planes between the root node and the
0041  * current edge (inclusive). If a ray's intersection with the edge bbox is
0042  * found to be nearer than the current minimum intersection, traversal proceeds
0043  * down that edge. The minimum intersection is modified when a nearer
0044  * minimumium intersection with a actual volume if found (NOT a nearer
0045  * intersection with an edge bbox).
0046  *
0047  * \todo move to top-level orange directory out of detail namespace
0048  */
0049 class BvhIntersectingVolFinder
0050 {
0051   public:
0052     //!@{
0053     //! \name Type aliases
0054     using Storage = NativeCRef<BvhTreeData>;
0055 
0056     struct Ray
0057     {
0058         Real3 const& pos;
0059         Real3 const& dir;
0060     };
0061     //!@}
0062 
0063     // Construct from a vector of bounding boxes and storage for LocalVolumeIds
0064     inline CELER_FUNCTION BvhIntersectingVolFinder(BvhTreeRecord const& tree,
0065                                                    Storage const& storage);
0066 
0067     // Calculate the minimum intersection, with supplied maximum search
0068     // distance
0069     template<class F>
0070     inline CELER_FUNCTION Intersection operator()(
0071         Ray ray, F&& visit_vol, real_type max_search_dist) const;
0072 
0073   private:
0074     //// DATA ////
0075     BvhView view_;
0076 
0077     //// HELPER FUNCTIONS ////
0078 
0079     // Determine if the intersection with an edge/vol bbox is less than
0080     // min_dist
0081     inline CELER_FUNCTION bool visit_bbox(
0082         FastBBox const& bbox, Ray ray, real_type min_dist) const;
0083 
0084     // Calculate the current min intersection, which may/may not be on this
0085     // leaf
0086     template<class F>
0087     inline CELER_FUNCTION Intersection visit_leaf(BvhNodeId leaf_node_id,
0088                                                   Intersection intersection,
0089                                                   F&& visit_vol) const;
0090 
0091     // Calculate the current min intersection, which may/may not be in inf_vols
0092     template<class F>
0093     inline CELER_FUNCTION Intersection visit_inf_vols(
0094         Intersection intersection, F&& visit_vol) const;
0095 };
0096 
0097 //---------------------------------------------------------------------------//
0098 // INLINE DEFINITIONS
0099 //---------------------------------------------------------------------------//
0100 /*!
0101  * Construct from vector a of bounding boxes and storage.
0102  */
0103 CELER_FUNCTION BvhIntersectingVolFinder::BvhIntersectingVolFinder(
0104     BvhTreeRecord const& tree, BvhIntersectingVolFinder::Storage const& storage)
0105     : view_(tree, storage)
0106 {
0107     CELER_EXPECT(tree);
0108 }
0109 
0110 //---------------------------------------------------------------------------//
0111 /*!
0112  * Calculate the minimum intersection, with supplied maximum search distance.
0113  *
0114  * If no intersection is found within max_search_dist, an empty Intersection
0115  * object is returned. The visit_vol argument should be of the form:
0116  *
0117  * detail::Intersection(*)(LocalVolumeId id, real_type max_search_dist)
0118  *
0119  * Other information required by the functor should be handled through
0120  * lambda capture.
0121  */
0122 template<class F>
0123 CELER_FUNCTION auto BvhIntersectingVolFinder::operator()(
0124     BvhIntersectingVolFinder::Ray ray,
0125     F&& visit_vol,
0126     real_type max_search_dist) const -> Intersection
0127 {
0128     using Side = BvhInternalNode::Side;
0129 
0130     Intersection intersection{OnLocalSurface{}, max_search_dist};
0131 
0132     // Stack of deferred nodes
0133     using StackT = IdStack<BvhNodeId, max_bvh_depth - 1>;
0134     BvhNodeId stack_spill_[StackT::spill_extent];
0135     StackT stack{stack_spill_};
0136 #if !CELER_GCC8_BUGGY_CONSTEXPR
0137     // This static_assert does not compile with gcc 8.5
0138     static_assert(stack.capacity() == max_bvh_depth);
0139 #endif
0140     stack.push(BvhNodeId{0});
0141 
0142     while (!stack.empty())
0143     {
0144         if (!view_.is_internal(stack.top()))
0145         {
0146             intersection
0147                 = this->visit_leaf(stack.top(), intersection, visit_vol);
0148             stack.pop();
0149             continue;
0150         }
0151 
0152         auto const& node = view_.inner_node(stack.top());
0153         stack.pop();
0154         int ax = to_int(node.axis());
0155 
0156         // Guess the better edge to traverse first: unrolled with loads for
0157         // GPU performance
0158         FastBBox first_bbox = node.bbox(Side::left);
0159         BvhNodeId first_child = node.child(Side::left);
0160         FastBBox second_bbox = node.bbox(Side::right);
0161         BvhNodeId second_child = node.child(Side::right);
0162 
0163         if (ray.pos[ax] > node.bbox(Side::right).lower()[ax])
0164         {
0165             trivial_swap(first_bbox, second_bbox);
0166             trivial_swap(first_child, second_child);
0167         }
0168 
0169         // Choose the next node on the basis of which edges are hits
0170         if (this->visit_bbox(second_bbox, ray, intersection.distance))
0171         {
0172             stack.push(second_child);
0173         }
0174         if (this->visit_bbox(first_bbox, ray, intersection.distance))
0175         {
0176             stack.push(first_child);
0177         }
0178     }
0179 
0180     return this->visit_inf_vols(intersection, visit_vol);
0181 }
0182 //---------------------------------------------------------------------------//
0183 // HELPER FUNCTIONS
0184 //---------------------------------------------------------------------------//
0185 /*!
0186  * Determine if the intersection with an edge/vol bbox is less than min_dist.
0187  */
0188 CELER_FUNCTION bool BvhIntersectingVolFinder::visit_bbox(
0189     FastBBox const& bbox, Ray ray, real_type min_dist) const
0190 {
0191     return intersects_segment(bbox, ray.pos, ray.dir, min_dist);
0192 }
0193 
0194 //---------------------------------------------------------------------------//
0195 /*!
0196  * Calculate the current min intersection, which may/may not be on this leaf.
0197  */
0198 template<class F>
0199 CELER_FUNCTION auto BvhIntersectingVolFinder::visit_leaf(
0200     BvhNodeId leaf_node_id, Intersection min_intersection, F&& visit_vol) const
0201     -> Intersection
0202 {
0203     for (auto id : view_.leaf_vol_ids(leaf_node_id))
0204     {
0205         auto intersection = visit_vol(id, min_intersection.distance);
0206         if (intersection)
0207         {
0208             CELER_ASSERT(intersection.distance <= min_intersection.distance);
0209             min_intersection = intersection;
0210         }
0211     }
0212     return min_intersection;
0213 }
0214 
0215 //---------------------------------------------------------------------------//
0216 /*!
0217  * Calculate the current min intersection, which may/may not be in inf_vols.
0218  */
0219 template<class F>
0220 CELER_FUNCTION auto BvhIntersectingVolFinder::visit_inf_vols(
0221     Intersection min_intersection, F&& visit_vol) const -> Intersection
0222 {
0223     for (auto id : view_.inf_vol_ids())
0224     {
0225         auto intersection = visit_vol(id, min_intersection.distance);
0226         if (intersection)
0227         {
0228             CELER_ASSERT(intersection.distance <= min_intersection.distance);
0229             min_intersection = intersection;
0230         }
0231     }
0232     return min_intersection;
0233 }
0234 
0235 //---------------------------------------------------------------------------//
0236 }  // namespace detail
0237 }  // namespace celeritas