Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
bvh.h
Go to the documentation of this file.
1#ifndef BVH_V2_BVH_H
2#define BVH_V2_BVH_H
3
4#include "bvh/v2/node.h"
5
6#include <cstddef>
7#include <iterator>
8#include <vector>
9#include <stack>
10#include <utility>
11#include <tuple>
12#include <algorithm>
13
14namespace bvh::v2 {
15
16template <typename Node>
17struct Bvh {
18 using Index = typename Node::Index;
19 using Scalar = typename Node::Scalar;
21
22 std::vector<Node> nodes;
23 std::vector<size_t> prim_ids;
24
25 Bvh() = default;
26 Bvh(Bvh&&) = default;
27
28 Bvh& operator = (Bvh&&) = default;
29
30 //bool operator == (const Bvh& other) const = default;
31 //bool operator != (const Bvh& other) const = default;
32 bool operator == (const Bvh& other) const {
33 return other.nodes == nodes && other.prim_ids == prim_ids;
34 }
35 bool operator != (const Bvh& other) const {
36 return other.nodes != nodes || other.prim_ids != prim_ids;
37 }
38
39 /// Returns whether the node located at the given index is the left child of its parent.
40 static BVH_ALWAYS_INLINE bool is_left_sibling(size_t node_id) { return node_id % 2 == 1; }
41
42 /// Returns the index of a sibling of a node.
43 static BVH_ALWAYS_INLINE size_t get_sibling_id(size_t node_id) {
44 return is_left_sibling(node_id) ? node_id + 1 : node_id - 1;
45 }
46
47 /// Returns the index of the left sibling of the node. This effectively returns the given index
48 /// unchanged if the node is the left sibling, or the other sibling index otherwise.
49 static BVH_ALWAYS_INLINE size_t get_left_sibling_id(size_t node_id) {
50 return is_left_sibling(node_id) ? node_id : node_id - 1;
51 }
52
53 /// Returns the index of the right sibling of the node. This effectively returns the given index
54 /// unchanged if the node is the right sibling, or the other sibling index otherwise.
55 static BVH_ALWAYS_INLINE size_t get_right_sibling_id(size_t node_id) {
56 return is_left_sibling(node_id) ? node_id + 1 : node_id;
57 }
58
59 /// Returns the root node of this BVH.
60 BVH_ALWAYS_INLINE const Node& get_root() const { return nodes[0]; }
61
62 /// Extracts the BVH rooted at the given node index.
63 inline Bvh extract_bvh(size_t root_id) const;
64
65 /// Traverses the BVH from the given index in `start` using the provided stack. Every leaf
66 /// encountered on the way is processed using the given `LeafFn` function, and every pair of
67 /// nodes is processed with the function in `HitFn`, which returns a triplet of booleans
68 /// indicating whether the first child should be processed, whether the second child should be
69 /// processed, and whether to traverse the second child first instead of the other way around.
70 template <bool IsAnyHit, typename Stack, typename LeafFn, typename InnerFn>
71 inline void traverse_top_down(Index start, Stack&, LeafFn&&, InnerFn&&) const;
72
73 /// Intersects the BVH with a single ray, using the given function to intersect the contents
74 /// of a leaf. The algorithm starts at the node index `start` and uses the given stack object.
75 /// When `IsAnyHit` is true, the function stops at the first intersection (useful for shadow
76 /// rays), otherwise it finds the closest intersection. When `IsRobust` is true, a slower but
77 /// numerically robust ray-box test is used, otherwise a fast, but less precise test is used.
78 template <bool IsAnyHit, bool IsRobust, typename Stack, typename LeafFn, typename InnerFn = IgnoreArgs>
79 inline void intersect(const Ray& ray, Index start, Stack&, LeafFn&&, InnerFn&& = {}) const;
80
81 /// Traverses this BVH from the bottom to the top, using the given function objects to process
82 /// leaves and inner nodes.
83 template <typename LeafFn = IgnoreArgs, typename InnerFn = IgnoreArgs>
84 inline void traverse_bottom_up(LeafFn&& = {}, InnerFn&& = {});
85
86 /// Refits the BVH, using the given function object to recompute the bounding box of the leaves.
87 template <typename LeafFn = IgnoreArgs>
88 inline void refit(LeafFn&& = {});
89
90 inline void serialize(OutputStream&) const;
91 static inline Bvh deserialize(InputStream&);
92};
93
94template <typename Node>
95Bvh<Node> Bvh<Node>::extract_bvh(size_t root_id) const {
96 assert(root_id != 0);
97
98 Bvh bvh;
99 bvh.nodes.emplace_back();
100
101 std::stack<std::pair<size_t, size_t>> stack;
102 stack.emplace(root_id, 0);
103 while (!stack.empty()) {
104 auto [src_id, dst_id] = stack.top();
105 stack.pop();
106 const auto& src_node = nodes[src_id];
107 auto& dst_node = bvh.nodes[dst_id];
108 dst_node = src_node;
109 if (src_node.is_leaf()) {
110 dst_node.index.set_first_id(bvh.prim_ids.size());
111 std::copy_n(
112 prim_ids.begin() + src_node.index.first_id(),
113 src_node.index.prim_count(),
114 std::back_inserter(bvh.prim_ids));
115 } else {
116 dst_node.index.set_first_id(bvh.nodes.size());
117 stack.emplace(src_node.index.first_id() + 0, bvh.nodes.size() + 0);
118 stack.emplace(src_node.index.first_id() + 1, bvh.nodes.size() + 1);
119 // Note: This may invalidate `dst_node` so has to happen after any access to it.
120 bvh.nodes.emplace_back();
121 bvh.nodes.emplace_back();
122 }
123 }
124 return bvh;
125}
126
127template <typename Node>
128template <bool IsAnyHit, typename Stack, typename LeafFn, typename InnerFn>
129void Bvh<Node>::traverse_top_down(Index start, Stack& stack, LeafFn&& leaf_fn, InnerFn&& inner_fn) const
130{
131 stack.push(start);
132restart:
133 while (!stack.is_empty()) {
134 auto top = stack.pop();
135 while (top.prim_count() == 0) {
136 auto& left = nodes[top.first_id()];
137 auto& right = nodes[top.first_id() + 1];
138 auto [hit_left, hit_right, should_swap] = inner_fn(left, right);
139
140 if (hit_left) {
141 auto near_index = left.index;
142 if (hit_right) {
143 auto far_index = right.index;
144 if (should_swap)
145 std::swap(near_index, far_index);
146 stack.push(far_index);
147 }
148 top = near_index;
149 } else if (hit_right)
150 top = right.index;
151 else [[unlikely]]
152 goto restart;
153 }
154
155 [[maybe_unused]] auto was_hit = leaf_fn(top.first_id(), top.first_id() + top.prim_count());
156 if constexpr (IsAnyHit) {
157 if (was_hit) return;
158 }
159 }
160}
161
162template <typename Node>
163template <bool IsAnyHit, bool IsRobust, typename Stack, typename LeafFn, typename InnerFn>
164void Bvh<Node>::intersect(const Ray& ray, Index start, Stack& stack, LeafFn&& leaf_fn, InnerFn&& inner_fn) const {
165 auto inv_dir = ray.template get_inv_dir<!IsRobust>();
166 auto inv_org = -inv_dir * ray.org;
167 auto inv_dir_pad = ray.pad_inv_dir(inv_dir);
168 auto octant = ray.get_octant();
169
170 traverse_top_down<IsAnyHit>(start, stack, leaf_fn, [&] (const Node& left, const Node& right) {
171 inner_fn(left, right);
172 std::pair<Scalar, Scalar> intr_left, intr_right;
173 if constexpr (IsRobust) {
174 intr_left = left .intersect_robust(ray, inv_dir, inv_dir_pad, octant);
175 intr_right = right.intersect_robust(ray, inv_dir, inv_dir_pad, octant);
176 } else {
177 intr_left = left .intersect_fast(ray, inv_dir, inv_org, octant);
178 intr_right = right.intersect_fast(ray, inv_dir, inv_org, octant);
179 }
180 return std::make_tuple(
181 intr_left.first <= intr_left.second,
182 intr_right.first <= intr_right.second,
183 !IsAnyHit && intr_left.first > intr_right.first);
184 });
185}
186
187template <typename Node>
188template <typename LeafFn, typename InnerFn>
189void Bvh<Node>::traverse_bottom_up(LeafFn&& leaf_fn, InnerFn&& inner_fn) {
190 std::vector<size_t> parents(nodes.size(), 0);
191 for (size_t i = 0; i < nodes.size(); ++i) {
192 if (nodes[i].is_leaf())
193 continue;
194 parents[nodes[i].index.first_id()] = i;
195 parents[nodes[i].index.first_id() + 1] = i;
196 }
197 std::vector<bool> seen(nodes.size(), false);
198 for (size_t i = nodes.size(); i-- > 0;) {
199 if (!nodes[i].is_leaf())
200 continue;
201 leaf_fn(nodes[i]);
202 seen[i] = true;
203 for (size_t j = parents[i];; j = parents[j]) {
204 auto& node = nodes[j];
205 if (seen[j] || !seen[node.index.first_id()] || !seen[node.index.first_id() + 1])
206 break;
207 inner_fn(nodes[j]);
208 seen[j] = true;
209 }
210 }
211}
212
213template <typename Node>
214template <typename LeafFn>
215void Bvh<Node>::refit(LeafFn&& leaf_fn) {
216 traverse_bottom_up(leaf_fn, [&] (Node& node) {
217 const auto& left = nodes[node.index.first_id()];
218 const auto& right = nodes[node.index.first_id() + 1];
219 node.set_bbox(left.get_bbox().extend(right.get_bbox()));
220 });
221}
222
223template <typename Node>
225 stream.write(nodes.size());
226 stream.write(prim_ids.size());
227 for (auto&& node : nodes)
228 node.serialize(stream);
229 for (auto&& prim_id : prim_ids)
230 stream.write(prim_id);
231}
232
233template <typename Node>
235 Bvh bvh;
236 bvh.nodes.resize(stream.read<size_t>());
237 bvh.prim_ids.resize(stream.read<size_t>());
238 for (auto& node : bvh.nodes)
239 node = Node::deserialize(stream);
240 for (auto& prim_id : bvh.prim_ids)
241 prim_id = stream.read<size_t>();
242 return bvh;
243}
244
245} // namespace bvh::v2
246
247#endif
Stream of data that can be used to deserialize data structures.
Definition stream.h:10
T read(T &&default_val={})
Definition stream.h:13
Stream of data that can be used to serialize data structures.
Definition stream.h:25
bool write(const T &data)
Definition stream.h:28
Definition bbox.h:9
Definition bbox.h:9
#define BVH_ALWAYS_INLINE
Definition platform.h:19
bool operator==(const Bvh &other) const
Definition bvh.h:32
bool operator!=(const Bvh &other) const
Definition bvh.h:35
std::vector< Node > nodes
Definition bvh.h:22
void refit(LeafFn &&={})
Refits the BVH, using the given function object to recompute the bounding box of the leaves.
Definition bvh.h:215
static BVH_ALWAYS_INLINE bool is_left_sibling(size_t node_id)
Returns whether the node located at the given index is the left child of its parent.
Definition bvh.h:40
void intersect(const Ray &ray, Index start, Stack &, LeafFn &&, InnerFn &&={}) const
Intersects the BVH with a single ray, using the given function to intersect the contents of a leaf.
Definition bvh.h:164
void serialize(OutputStream &) const
Definition bvh.h:224
static Bvh deserialize(InputStream &)
Definition bvh.h:234
Bvh extract_bvh(size_t root_id) const
Extracts the BVH rooted at the given node index.
Definition bvh.h:95
Bvh & operator=(Bvh &&)=default
typename Node::Scalar Scalar
Definition bvh.h:19
static BVH_ALWAYS_INLINE size_t get_sibling_id(size_t node_id)
Returns the index of a sibling of a node.
Definition bvh.h:43
void traverse_top_down(Index start, Stack &, LeafFn &&, InnerFn &&) const
Traverses the BVH from the given index in start using the provided stack.
Definition bvh.h:129
static BVH_ALWAYS_INLINE size_t get_left_sibling_id(size_t node_id)
Returns the index of the left sibling of the node.
Definition bvh.h:49
static BVH_ALWAYS_INLINE size_t get_right_sibling_id(size_t node_id)
Returns the index of the right sibling of the node.
Definition bvh.h:55
std::vector< size_t > prim_ids
Definition bvh.h:23
typename Node::Index Index
Definition bvh.h:18
Bvh(Bvh &&)=default
void traverse_bottom_up(LeafFn &&={}, InnerFn &&={})
Traverses this BVH from the bottom to the top, using the given function objects to process leaves and...
Definition bvh.h:189
BVH_ALWAYS_INLINE const Node & get_root() const
Returns the root node of this BVH.
Definition bvh.h:60
Bvh()=default
BVH_ALWAYS_INLINE Type first_id() const
Definition index.h:57
Binary BVH node, containing its bounds and an index into its children or the primitives it contains.
Definition node.h:23
static BVH_ALWAYS_INLINE Node deserialize(InputStream &stream)
Definition node.h:102
bvh::v2::Index< IndexBits, PrimCountBits > Index
Definition node.h:25
BVH_ALWAYS_INLINE std::pair< T, T > intersect_robust(const Ray< T, Dim > &ray, const Vec< T, Dim > &inv_dir, const Vec< T, Dim > &inv_dir_pad, const Octant &octant) const
Robust ray-node intersection routine. See "Robust BVH Ray Traversal", by T. Ize.
Definition node.h:74
BVH_ALWAYS_INLINE void set_bbox(const BBox< T, Dim > &bbox)
Definition node.h:58
Index index
Index to the children of an inner node, or to the primitives for a leaf node.
Definition node.h:37
BVH_ALWAYS_INLINE std::pair< T, T > intersect_fast(const Ray< T, Dim > &ray, const Vec< T, Dim > &inv_dir, const Vec< T, Dim > &inv_org, const Octant &octant) const
Definition node.h:85
static BVH_ALWAYS_INLINE Vec< T, N > pad_inv_dir(const Vec< T, N > &inv_dir)
Definition ray.h:46
Vec< T, N > org
Definition ray.h:17
BVH_ALWAYS_INLINE Octant get_octant() const
Definition ray.h:36