File indexing completed on 2025-09-17 09:13:46
0001 #ifndef BVH_V2_BVH_H
0002 #define BVH_V2_BVH_H
0003
0004 #include "bvh/v2/node.h"
0005
0006 #include <cstddef>
0007 #include <iterator>
0008 #include <vector>
0009 #include <stack>
0010 #include <utility>
0011 #include <tuple>
0012 #include <algorithm>
0013
0014 namespace bvh::v2 {
0015
0016 template <typename Node>
0017 struct Bvh {
0018 using Index = typename Node::Index;
0019 using Scalar = typename Node::Scalar;
0020 using Ray = bvh::v2::Ray<Scalar, Node::dimension>;
0021
0022 std::vector<Node> nodes;
0023 std::vector<size_t> prim_ids;
0024
0025 Bvh() = default;
0026 Bvh(Bvh&&) = default;
0027
0028 Bvh& operator = (Bvh&&) = default;
0029
0030
0031
0032 bool operator == (const Bvh& other) const {
0033 return other.nodes == nodes && other.prim_ids == prim_ids;
0034 }
0035 bool operator != (const Bvh& other) const {
0036 return other.nodes != nodes || other.prim_ids != prim_ids;
0037 }
0038
0039
0040 static BVH_ALWAYS_INLINE bool is_left_sibling(size_t node_id) { return node_id % 2 == 1; }
0041
0042
0043 static BVH_ALWAYS_INLINE size_t get_sibling_id(size_t node_id) {
0044 return is_left_sibling(node_id) ? node_id + 1 : node_id - 1;
0045 }
0046
0047
0048
0049 static BVH_ALWAYS_INLINE size_t get_left_sibling_id(size_t node_id) {
0050 return is_left_sibling(node_id) ? node_id : node_id - 1;
0051 }
0052
0053
0054
0055 static BVH_ALWAYS_INLINE size_t get_right_sibling_id(size_t node_id) {
0056 return is_left_sibling(node_id) ? node_id + 1 : node_id;
0057 }
0058
0059
0060 BVH_ALWAYS_INLINE const Node& get_root() const { return nodes[0]; }
0061
0062
0063 inline Bvh extract_bvh(size_t root_id) const;
0064
0065
0066
0067
0068
0069
0070 template <bool IsAnyHit, typename Stack, typename LeafFn, typename InnerFn>
0071 inline void traverse_top_down(Index start, Stack&, LeafFn&&, InnerFn&&) const;
0072
0073
0074
0075
0076
0077
0078 template <bool IsAnyHit, bool IsRobust, typename Stack, typename LeafFn, typename InnerFn = IgnoreArgs>
0079 inline void intersect(const Ray& ray, Index start, Stack&, LeafFn&&, InnerFn&& = {}) const;
0080
0081
0082
0083 template <typename LeafFn = IgnoreArgs, typename InnerFn = IgnoreArgs>
0084 inline void traverse_bottom_up(LeafFn&& = {}, InnerFn&& = {});
0085
0086
0087 template <typename LeafFn = IgnoreArgs>
0088 inline void refit(LeafFn&& = {});
0089
0090 inline void serialize(OutputStream&) const;
0091 static inline Bvh deserialize(InputStream&);
0092 };
0093
0094 template <typename Node>
0095 Bvh<Node> Bvh<Node>::extract_bvh(size_t root_id) const {
0096 assert(root_id != 0);
0097
0098 Bvh bvh;
0099 bvh.nodes.emplace_back();
0100
0101 std::stack<std::pair<size_t, size_t>> stack;
0102 stack.emplace(root_id, 0);
0103 while (!stack.empty()) {
0104 auto [src_id, dst_id] = stack.top();
0105 stack.pop();
0106 const auto& src_node = nodes[src_id];
0107 auto& dst_node = bvh.nodes[dst_id];
0108 dst_node = src_node;
0109 if (src_node.is_leaf()) {
0110 dst_node.index.set_first_id(bvh.prim_ids.size());
0111 std::copy_n(
0112 prim_ids.begin() + src_node.index.first_id(),
0113 src_node.index.prim_count(),
0114 std::back_inserter(bvh.prim_ids));
0115 } else {
0116 dst_node.index.set_first_id(bvh.nodes.size());
0117 stack.emplace(src_node.index.first_id() + 0, bvh.nodes.size() + 0);
0118 stack.emplace(src_node.index.first_id() + 1, bvh.nodes.size() + 1);
0119
0120 bvh.nodes.emplace_back();
0121 bvh.nodes.emplace_back();
0122 }
0123 }
0124 return bvh;
0125 }
0126
0127 template <typename Node>
0128 template <bool IsAnyHit, typename Stack, typename LeafFn, typename InnerFn>
0129 void Bvh<Node>::traverse_top_down(Index start, Stack& stack, LeafFn&& leaf_fn, InnerFn&& inner_fn) const
0130 {
0131 stack.push(start);
0132 restart:
0133 while (!stack.is_empty()) {
0134 auto top = stack.pop();
0135 while (top.prim_count() == 0) {
0136 auto& left = nodes[top.first_id()];
0137 auto& right = nodes[top.first_id() + 1];
0138 auto [hit_left, hit_right, should_swap] = inner_fn(left, right);
0139
0140 if (hit_left) {
0141 auto near_index = left.index;
0142 if (hit_right) {
0143 auto far_index = right.index;
0144 if (should_swap)
0145 std::swap(near_index, far_index);
0146 stack.push(far_index);
0147 }
0148 top = near_index;
0149 } else if (hit_right) {
0150 top = right.index;
0151 }
0152 else [[unlikely]] {
0153 goto restart;
0154 }
0155 }
0156
0157 [[maybe_unused]] auto was_hit = leaf_fn(top.first_id(), top.first_id() + top.prim_count());
0158 if constexpr (IsAnyHit) {
0159 if (was_hit) return;
0160 }
0161 }
0162 }
0163
0164 template <typename Node>
0165 template <bool IsAnyHit, bool IsRobust, typename Stack, typename LeafFn, typename InnerFn>
0166 void Bvh<Node>::intersect(const Ray& ray, Index start, Stack& stack, LeafFn&& leaf_fn, InnerFn&& inner_fn) const {
0167 auto inv_dir = ray.template get_inv_dir<!IsRobust>();
0168 auto inv_dir_pad_or_inv_org = IsRobust ? ray.pad_inv_dir(inv_dir) : -inv_dir * ray.org;
0169 auto octant = ray.get_octant();
0170
0171 traverse_top_down<IsAnyHit>(start, stack, leaf_fn, [&] (const Node& left, const Node& right) {
0172 inner_fn(left, right);
0173 std::pair<Scalar, Scalar> intr_left, intr_right;
0174 if constexpr (IsRobust) {
0175 intr_left = left .intersect_robust(ray, inv_dir, inv_dir_pad_or_inv_org, octant);
0176 intr_right = right.intersect_robust(ray, inv_dir, inv_dir_pad_or_inv_org, octant);
0177 } else {
0178 intr_left = left .intersect_fast(ray, inv_dir, inv_dir_pad_or_inv_org, octant);
0179 intr_right = right.intersect_fast(ray, inv_dir, inv_dir_pad_or_inv_org, octant);
0180 }
0181 return std::make_tuple(
0182 intr_left.first <= intr_left.second,
0183 intr_right.first <= intr_right.second,
0184 !IsAnyHit && intr_left.first > intr_right.first);
0185 });
0186 }
0187
0188 template <typename Node>
0189 template <typename LeafFn, typename InnerFn>
0190 void Bvh<Node>::traverse_bottom_up(LeafFn&& leaf_fn, InnerFn&& inner_fn) {
0191 std::vector<size_t> parents(nodes.size(), 0);
0192 for (size_t i = 0; i < nodes.size(); ++i) {
0193 if (nodes[i].is_leaf())
0194 continue;
0195 parents[nodes[i].index.first_id()] = i;
0196 parents[nodes[i].index.first_id() + 1] = i;
0197 }
0198 std::vector<bool> seen(nodes.size(), false);
0199 for (size_t i = nodes.size(); i-- > 0;) {
0200 if (!nodes[i].is_leaf())
0201 continue;
0202 leaf_fn(nodes[i]);
0203 seen[i] = true;
0204 for (size_t j = parents[i];; j = parents[j]) {
0205 auto& node = nodes[j];
0206 if (seen[j] || !seen[node.index.first_id()] || !seen[node.index.first_id() + 1])
0207 break;
0208 inner_fn(nodes[j]);
0209 seen[j] = true;
0210 }
0211 }
0212 }
0213
0214 template <typename Node>
0215 template <typename LeafFn>
0216 void Bvh<Node>::refit(LeafFn&& leaf_fn) {
0217 traverse_bottom_up(leaf_fn, [&] (Node& node) {
0218 const auto& left = nodes[node.index.first_id()];
0219 const auto& right = nodes[node.index.first_id() + 1];
0220 node.set_bbox(left.get_bbox().extend(right.get_bbox()));
0221 });
0222 }
0223
0224 template <typename Node>
0225 void Bvh<Node>::serialize(OutputStream& stream) const {
0226 stream.write(nodes.size());
0227 stream.write(prim_ids.size());
0228 for (auto&& node : nodes)
0229 node.serialize(stream);
0230 for (auto&& prim_id : prim_ids)
0231 stream.write(prim_id);
0232 }
0233
0234 template <typename Node>
0235 Bvh<Node> Bvh<Node>::deserialize(InputStream& stream) {
0236 Bvh bvh;
0237 bvh.nodes.resize(stream.read<size_t>());
0238 bvh.prim_ids.resize(stream.read<size_t>());
0239 for (auto& node : bvh.nodes)
0240 node = Node::deserialize(stream);
0241 for (auto& prim_id : bvh.prim_ids)
0242 prim_id = stream.read<size_t>();
0243 return bvh;
0244 }
0245
0246 }
0247
0248 #endif