tinybvh_rs/layouts/bvh.rs
1//! "Wald" BVH layout
2//!
3//! Main BVH layout used to construct other layouts.
4//!
5//! This layout is split into:
6//! - [`BVHData`]: Read-only operations
7//! - [`BVH`]: Read-write operations
8//! - Read operations mixing layout and referenced data, such as vertices
9//! - Build, refit
10
11use crate::{ffi, layouts::impl_bvh_deref, Error};
12use std::{fmt::Debug, marker::PhantomData};
13
14/// "Traditional" 32-bytes BVH node layout, as proposed by Ingo Wald.
15///
16/// Node layout used by [`BVH`].
17#[repr(C)]
18#[derive(Clone, Copy, Default, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
19pub struct Node {
20 /// AABB min position.
21 pub min: [f32; 3],
22 /// If the node is a leaf, this is the start index of the primitive.
23 /// Otherwise, this is the start index of the first child node.
24 pub left_first: u32,
25 /// AABB max position.
26 pub max: [f32; 3],
27 /// If the node is a leaf, number of triangles in the node.
28 /// `0` otherwise.
29 pub tri_count: u32,
30}
31
32impl Node {
33 /// Returns `true` if the node is a leaf.
34 pub fn is_leaf(&self) -> bool {
35 self.tri_count > 0
36 }
37}
38
39/// Read-only BVH data.
40pub struct BVHData {
41 pub(crate) inner: cxx::UniquePtr<ffi::BVH>,
42 pub(crate) max_primitives_per_leaf: Option<u32>,
43 /// Primitive slice len, **not** primitive count.
44 pub(crate) primitives_len: u32,
45}
46
47impl BVHData {
48 fn new() -> Self {
49 Self {
50 inner: ffi::BVH_new(),
51 max_primitives_per_leaf: None,
52 primitives_len: 0,
53 }
54 }
55
56 /// Move this instance into a writable BVH.
57 ///
58 /// Note: `primitives` slice must have a length multiple of 3.
59 ///
60 /// # Example
61 ///
62 /// ```rust
63 /// # use tinybvh_rs::bvh;
64 ///
65 /// let triangles = vec![[-1.0, 1.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 0.0, 0.0]];
66 /// # let bvh = bvh::BVH::new(triangles.as_slice().into()).unwrap();
67 /// let data = bvh.data();
68 /// let mut bvh = data.bvh(triangles.as_slice().into()).unwrap();
69 /// bvh.refit();
70 /// ```
71 pub fn bvh<'a>(self, primitives: crate::Positions<'a>) -> Result<BVH<'a>, Error> {
72 BVH {
73 bvh: self,
74 _phantom: PhantomData,
75 }
76 .bind(primitives)
77 }
78
79 /// Number of primitives for a given node.
80 pub fn primitive_count(&self, id: u32) -> u32 {
81 self.inner.PrimCount(id) as u32
82 }
83
84 /// SAH cost for a subtree.
85 pub fn sah_cost(&self, id: u32) -> f32 {
86 self.inner.SAHCost(id)
87 }
88
89 /// BVH nodes.
90 ///
91 /// Useful to upload to the BVH to the GPU.
92 pub fn nodes(&self) -> &[Node] {
93 ffi::BVH_nodes(&self.inner)
94 }
95
96 /// BVH indices.
97 ///
98 /// Map from primitive index to first vertex index.
99 ///
100 /// # Example
101 ///
102 /// ```rust
103 /// # use tinybvh_rs::bvh;
104 /// # let primitives = vec![[0.0; 4], [0.0; 4], [0.0; 4]];
105 /// # let bvh = bvh::BVH::new(primitives.as_slice().into()).unwrap();
106 /// # let node = bvh.nodes()[0];
107 /// for i in 0..node.tri_count {
108 /// let vertex_start = bvh.indices()[(node.left_first + i) as usize] as usize * 3;
109 /// let vertex = [
110 /// primitives[vertex_start],
111 /// primitives[vertex_start + 1],
112 /// primitives[vertex_start + 2]
113 /// ];
114 /// println!("Vertex {:?}", vertex);
115 /// }
116 /// ```
117 pub fn indices(&self) -> &[u32] {
118 ffi::BVH_indices(&self.inner)
119 }
120
121 /// Returns `true` if the BVH can be refitted.
122 ///
123 /// A BVH is refittable if it was built without spatial splits (i.e., not an SBVH).
124 pub fn refittable(&self) -> bool {
125 ffi::BVH_refittable(&self.inner)
126 }
127}
128
129/// Read-write BVH data.
130///
131/// The BVH is bound to the positions until moved into a [`BVHData`].
132/// More information on the [`BVH::bind`] method.
133///
134/// # Examples
135///
136/// ```rust
137/// use tinybvh_rs::bvh;
138///
139/// let triangles = vec![[-1.0, 1.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 0.0, 0.0]];
140/// let bvh = bvh::BVH::new(triangles.as_slice().into());
141/// ```
142pub struct BVH<'a> {
143 pub(crate) bvh: BVHData,
144 _phantom: PhantomData<&'a [f32; 4]>,
145}
146impl_bvh_deref!(BVH<'a>, BVHData);
147
148impl<'a> BVH<'a> {
149 /// Create a new BVH and build it using [`BVH::build`].
150 pub fn new(primitives: crate::Positions<'a>) -> Result<Self, Error> {
151 Self {
152 bvh: BVHData::new(),
153 _phantom: PhantomData,
154 }
155 .build(primitives)
156 }
157
158 /// Create a new BVH and build it using [`BVH::build_hq`].
159 pub fn new_hq(primitives: crate::Positions<'a>) -> Result<Self, Error> {
160 Self {
161 bvh: BVHData::new(),
162 _phantom: PhantomData,
163 }
164 .build_hq(primitives)
165 }
166
167 /// Build the BVH and bind it to `primitives`.
168 ///
169 /// More information on the tinybvh repository (`BVH::Build()` method).
170 ///
171 /// # Examples
172 ///
173 /// ```rust
174 /// use tinybvh_rs::bvh;
175 ///
176 /// let triangles = vec![[-1.0, 1.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 0.0, 0.0]];
177 /// # let bvh = bvh::BVH::new(triangles.as_slice().into()).unwrap();
178 /// let bvh = bvh.build(triangles.as_slice().into());
179 /// ```
180 pub fn build<'b>(self, primitives: crate::Positions<'b>) -> Result<BVH<'b>, Error> {
181 Error::validate_triangulated(primitives.len())?;
182 let slice = primitives.into();
183 let mut bvh = self.bvh;
184 bvh.inner.pin_mut().Build(&slice);
185 Ok(BVH::build_internal(bvh, primitives))
186 }
187
188 /// Build the BVH and bind it to `primitives`.
189 ///
190 /// More information on the tinybvh repository (`BVH::BuildHQ()` method).
191 pub fn build_hq<'b>(self, primitives: crate::Positions<'b>) -> Result<BVH<'b>, Error> {
192 Error::validate_triangulated(primitives.len())?;
193 let slice = primitives.into();
194 let mut bvh = self.bvh;
195 bvh.inner.pin_mut().BuildHQ(&slice);
196 Ok(BVH::build_internal(bvh, primitives))
197 }
198
199 /// Update the primitives reference.
200 ///
201 /// Note: At the opposite of [`BVH::build`], no rebuild is performed.
202 ///
203 /// # Examples
204 ///
205 /// ```rust
206 /// # use tinybvh_rs::{bvh, Intersector, Ray};
207 ///
208 /// let mut triangles = vec![
209 /// [-1.0, 1.0, 0.0, 0.0],
210 /// [1.0, 1.0, 0.0, 0.0],
211 /// [-1.0, 0.0, 0.0, 0.0],
212 /// ];
213 /// let bvh = bvh::BVH::new(triangles.as_slice().into()).unwrap();
214 ///
215 /// let mut other_triangles = vec![
216 /// [-2.0, 2.0, 0.0, 0.0],
217 /// [2.0, 2.0, 0.0, 0.0],
218 /// [-2.0, 0.0, 0.0, 0.0]
219 /// ];
220 /// let bvh = bvh.bind(other_triangles.as_slice().into()).unwrap();
221 ///
222 /// let mut ray = Ray::new([0.0; 3], [0.0, 0.0, -1.0]);
223 /// bvh.intersect(&mut ray);
224 /// ```
225 pub fn bind<'b>(self, primitives: crate::Positions<'b>) -> Result<BVH<'b>, Error> {
226 let mut bvh = self.bvh;
227 Error::validate_primitives_len(bvh.primitives_len, primitives.len() as u32)?;
228
229 let slice = primitives.into();
230 ffi::BVH_setPrimitives(bvh.inner.pin_mut(), &slice);
231 Ok(BVH {
232 bvh,
233 _phantom: PhantomData,
234 })
235 }
236
237 /// Remove unused nodes and reduce BVH size.
238 ///
239 /// More information on the tinybvh repository (`BVH::Compact()` method).
240 pub fn compact(&mut self) {
241 self.bvh.inner.pin_mut().Compact();
242 }
243
244 /// Split BVH leaves into a at most `max_primitives` primitives.
245 ///
246 /// More information on the tinybvh repository (`BVH::SplitLeafs()` method).
247 pub fn split_leaves(&mut self, max_primitives: u32) {
248 self.bvh.inner.pin_mut().SplitLeafs(max_primitives);
249
250 let max_prim = self.bvh.max_primitives_per_leaf.unwrap_or(u32::MAX);
251 self.bvh.max_primitives_per_leaf = Some(u32::min(max_prim, max_primitives));
252 }
253
254 /// Refits the BVH.
255 ///
256 /// Note: This library diverges from tinybvh here that asserts if it's not refittable.
257 /// tinybvh_rs skips refitting instead.
258 ///
259 /// More information on the tinybvh repository (`BVH::Refit()` method).
260 pub fn refit(&mut self) {
261 if self.refittable() {
262 self.bvh.inner.pin_mut().Refit(0);
263 }
264 }
265
266 /// Move the BVH into read-only state.
267 ///
268 /// Allows to relax the lifetime bound, for instance to modify
269 /// the positions before a refit:
270 ///
271 /// ```rust
272 /// # use tinybvh_rs::bvh;
273 /// let data = {
274 /// let mut triangles = vec![[-1.0, 1.0, 0.0, 0.0], [1.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 0.0, 0.0]];
275 /// let bvh = bvh::BVH::new(triangles.as_slice().into()).unwrap();
276 /// bvh.data()
277 /// }; // `triangles` is dropped by now
278 /// println!("Is leaf: {}", data.nodes()[0].is_leaf()); // false
279 /// ```
280 pub fn data(self) -> BVHData {
281 self.bvh
282 }
283
284 fn build_internal<'b>(mut bvh: BVHData, primitives: crate::Positions<'b>) -> BVH<'b> {
285 bvh.max_primitives_per_leaf = None;
286 bvh.primitives_len = primitives.len() as u32;
287 BVH {
288 bvh,
289 _phantom: PhantomData,
290 }
291 }
292}
293
294impl crate::Intersector for BVH<'_> {
295 fn intersect(&self, ray: &mut crate::Ray) -> u32 {
296 self.bvh.inner.Intersect(ray) as u32
297 }
298}