29 bvh::bvh(std::span<const bvh_primitive> primitives)
39 void bvh::build(std::span<const bvh_primitive> primitives)
41 if (primitives.empty())
48 m_primitive_indices.resize(primitives.size());
49 std::iota(m_primitive_indices.begin(), m_primitive_indices.end(), 0);
52 m_nodes.resize(primitives.size() << 1);
56 auto& root = m_nodes.front();
57 root.size =
static_cast<std::uint32_t
>(primitives.size());
59 update_bounds(root, primitives);
62 subdivide(root, primitives);
69 const auto& vertex_positions = mesh.vertices().attributes().at<
math::fvec3>(
"position");
72 std::vector<bvh_primitive> primitives(mesh.faces().size());
77 auto& primitive = primitives[face->index()];
78 primitive.centroid = {};
83 const auto& vertex_position = vertex_positions[loop->vertex()->index()];
85 primitive.centroid += vertex_position;
86 primitive.bounds.extend(vertex_position);
89 primitive.centroid /=
static_cast<float>(face->loops().size());
98 m_primitive_indices.clear();
103 void bvh::update_bounds(
bvh_node& node,
const std::span<const bvh_primitive>& primitives)
106 for (std::uint32_t i = 0; i < node.
size; ++i)
108 const auto& primitive = primitives[m_primitive_indices[node.
offset + i]];
109 node.
bounds.extend(primitive.bounds);
113 void bvh::subdivide(bvh_node& node,
const std::span<const bvh_primitive>& primitives)
121 const auto extents = node.bounds.size();
122 std::uint8_t split_axis = 0;
123 if (extents.y() > extents.x())
127 if (extents.z() > extents[split_axis])
133 const float split_coord = node.bounds.min[split_axis] + extents[split_axis] * 0.5f;
135 std::uint32_t
i = node.offset;
136 std::uint32_t
j = (node.size) ? i + node.size - 1 : i;
139 const auto& primitive = primitives[m_primitive_indices[
i]];
141 if (primitive.centroid[split_axis] < split_coord)
148 std::swap(m_primitive_indices[i], m_primitive_indices[j]);
157 const std::uint32_t left_size =
i - node.offset;
158 if (!left_size || left_size == node.size)
163 const std::uint32_t left_index = m_node_count++;
164 auto& left_child = m_nodes[left_index];
165 left_child.offset = node.offset;
166 left_child.size = left_size;
167 update_bounds(left_child, primitives);
169 const std::uint32_t right_index = m_node_count++;
170 auto& right_child = m_nodes[right_index];
171 right_child.offset =
i;
172 right_child.size = node.size - left_size;
173 update_bounds(right_child, primitives);
175 node.offset = left_index;
177 subdivide(left_child, primitives);
178 subdivide(right_child, primitives);
191 for (std::uint32_t i = 0;
i < node.size; ++
i)
193 f(m_primitive_indices[node.offset + i]);
199 visit(m_nodes[node.offset], ray, f);
200 visit(m_nodes[node.offset + 1], ray, f);
Portion of a shell bounded by loops.
Connected boundary of a single face.
Boundary representation (B-rep) of a mesh.
void clear()
Clears the BVH.
void build(std::span< const bvh_primitive > primitives)
Constructs a BVH from a set of primitives.
constexpr bvh() noexcept=default
Constructs an empty BVH.
void visit(const geom::ray< float, 3 > &ray, const visitor_type &f) const
Visits the primitive indices of all BVH nodes that intersect a ray.
constexpr std::optional< T > intersection(const ray< T, N > &ray, const hyperplane< T, N > &hyperplane) noexcept
Ray-hyperplane intersection test.
Single node in a bounding volume hierarchy.
std::uint32_t offset
Offset to the first child node (non-leaf) or primitive (leaf).
std::uint32_t size
Number of primitives in the node.
geom::box< float > bounds
Node bounds.
Half of a line proceeding from an initial point.
static constexpr vector infinity() noexcept
Returns a vector of infinities, where every element is equal to infinity.