obvhs/bvh2/
node.rs

1use bytemuck::{Pod, Zeroable};
2#[cfg(feature = "small_bvh2_node")]
3use glam::Vec3;
4
5use crate::aabb::Aabb;
6
7#[cfg(feature = "small_bvh2_node")]
8static_assertions::assert_eq_size!(Bvh2Node, Aabb);
9#[cfg(feature = "small_bvh2_node")]
10static_assertions::assert_eq_align!(Bvh2Node, Aabb);
11
12#[cfg(feature = "small_bvh2_node")]
13/// A node in the Bvh2, can be an inner node or leaf.
14#[derive(Default, Clone, Copy, Debug, Pod, Zeroable)]
15#[repr(C)]
16#[repr(align(16))]
17pub struct Bvh2Node {
18    /// The bounding box minimum coordinate for the primitive(s) contained in this node
19    pub min: Vec3,
20    /// Number of primitives contained in this node.
21    /// If prim_count is 0, this is a inner node.
22    /// If prim_count > 0 this node is a leaf node.
23    /// Note: CwBvh will clamp to max 3, Bvh2 will clamp to max 255
24    ///   partial rebuilds uses u32::MAX to temporarily designate a subtree root.
25    pub prim_count: u32,
26    /// The bounding box maximum coordinate for the primitive(s) contained in this node
27    pub max: Vec3,
28    /// The index of the first child Aabb or primitive.
29    /// If this node is an inner node the first child will be at `nodes[first_index]`, and the second at `nodes[first_index + 1]`.
30    /// If this node is a leaf node the first index typically indexes into a primitive_indices list that contains the actual index of the primitive.
31    /// The reason for this mapping is that if multiple primitives are contained in this node, they need to have their indices layed out contiguously.
32    /// To avoid this indirection we have two options:
33    /// 1. Layout the primitives in the order of the primitive_indices mapping so that this can index directly into the primitive list.
34    /// 2. Only allow one primitive per node and write back the original mapping to the bvh node list.
35    pub first_index: u32,
36}
37
38#[cfg(not(feature = "small_bvh2_node"))]
39/// A node in the Bvh2, can be an inner node or leaf.
40#[derive(Default, Clone, Copy, Debug, Pod, Zeroable)]
41#[repr(C)]
42#[repr(align(16))]
43pub struct Bvh2Node {
44    /// The bounding box for the primitive(s) contained in this node
45    pub aabb: Aabb,
46    /// Number of primitives contained in this node.
47    /// If prim_count is 0, this is a inner node.
48    /// If prim_count > 0 this node is a leaf node.
49    /// Note: CwBvh will clamp to max 3, Bvh2 will clamp to max 255
50    ///   partial rebuilds uses u32::MAX to temporarily designate a subtree root.
51    pub prim_count: u32,
52    /// The index of the first child Aabb or primitive.
53    /// If this node is an inner node the first child will be at `nodes[first_index]`, and the second at `nodes[first_index + 1]`.
54    /// If this node is a leaf node the first index typically indexes into a primitive_indices list that contains the actual index of the primitive.
55    /// The reason for this mapping is that if multiple primitives are contained in this node, they need to have their indices layed out contiguously.
56    /// To avoid this indirection we have two options:
57    /// 1. Layout the primitives in the order of the primitive_indices mapping so that this can index directly into the primitive list.
58    /// 2. Only allow one primitive per node and write back the original mapping to the bvh node list.
59    pub first_index: u32,
60    /// With the aabb, prim_count, and first_index, this struct was already padded out to 48 bytes. These meta fields
61    /// allow the user to access this otherwise unused space.
62    pub meta1: u32,
63    /// With the aabb, prim_count, and first_index, this struct was already padded out to 48 bytes. These meta fields
64    /// allow the user to access this otherwise unused space.
65    pub meta2: u32,
66}
67
68impl Bvh2Node {
69    #[cfg(feature = "small_bvh2_node")]
70    #[inline(always)]
71    pub fn new(aabb: Aabb, prim_count: u32, first_index: u32) -> Self {
72        let mut node: Bvh2Node = bytemuck::cast(aabb);
73        node.prim_count = prim_count;
74        node.first_index = first_index;
75        node
76    }
77
78    #[cfg(not(feature = "small_bvh2_node"))]
79    #[inline(always)]
80    pub fn new(aabb: Aabb, prim_count: u32, first_index: u32) -> Self {
81        Bvh2Node {
82            aabb,
83            prim_count,
84            first_index,
85            meta1: Default::default(),
86            meta2: Default::default(),
87        }
88    }
89
90    #[cfg(feature = "small_bvh2_node")]
91    #[inline(always)]
92    pub fn aabb(&self) -> &Aabb {
93        // Note(Lokathor): (from bytemuck::try_cast_ref()) everything with `align_of` and `size_of` will optimize away
94        // after monomorphization.
95        // Note(Griffin): checked asm and this appears to be correct. Test with:
96
97        // #[unsafe(no_mangle)] pub unsafe extern "C" fn aabb_shim(node: &Bvh2Node) -> &Aabb { node.aabb() }
98        // (use basic_bvh2 in black_box in basic_bvh2)
99        // cargo objdump --example basic_bvh2 --release --features small_bvh2_node -- -d --disassemble-symbols=aabb_shim -C
100
101        // 000000000003e260 <aabb_shim>:
102        // 3e260: 48 89 f8   movq	%rdi, %rax
103        // 3e263: c3         retq
104
105        bytemuck::cast_ref(self)
106    }
107
108    #[cfg(not(feature = "small_bvh2_node"))]
109    #[inline(always)]
110    pub fn aabb(&self) -> &Aabb {
111        &self.aabb
112    }
113
114    #[cfg(feature = "small_bvh2_node")]
115    #[inline(always)]
116    pub fn set_aabb(&mut self, aabb: Aabb) {
117        self.min = aabb.min.into();
118        self.max = aabb.max.into();
119    }
120
121    #[cfg(not(feature = "small_bvh2_node"))]
122    #[inline(always)]
123    pub fn set_aabb(&mut self, aabb: Aabb) {
124        self.aabb = aabb;
125    }
126
127    #[inline(always)]
128    /// Also returns true for invalid nodes. If that matters in the context you are using this also check
129    /// Bvh2::is_invalid (used internally for partial BVH rebuilds)
130    pub fn is_leaf(&self) -> bool {
131        self.prim_count != 0
132    }
133
134    #[inline(always)]
135    /// Used internally for partial BVH rebuilds. Does not usually need to be checked. Currently, a bvh will only
136    /// temporarily contain any invalid nodes.
137    pub fn valid(&self) -> bool {
138        (self.prim_count & 0b10000000000000000000000000000000) == 0
139    }
140
141    #[inline(always)]
142    /// Used internally for partial BVH rebuilds.
143    pub fn set_invalid(&mut self) {
144        self.prim_count |= 0b10000000000000000000000000000000
145    }
146
147    #[inline(always)]
148    /// Used internally for partial BVH rebuilds.
149    pub fn set_valid(&mut self) {
150        self.prim_count &= 0b01111111111111111111111111111111
151    }
152
153    #[inline(always)]
154    pub fn is_left_sibling(node_id: usize) -> bool {
155        node_id % 2 == 1
156    }
157    #[inline(always)]
158    pub fn get_sibling_id(node_id: usize) -> usize {
159        if Self::is_left_sibling(node_id) {
160            node_id + 1
161        } else {
162            node_id - 1
163        }
164    }
165    #[inline(always)]
166    pub fn get_left_sibling_id(node_id: usize) -> usize {
167        if Self::is_left_sibling(node_id) {
168            node_id
169        } else {
170            node_id - 1
171        }
172    }
173    #[inline(always)]
174    pub fn get_right_sibling_id(node_id: usize) -> usize {
175        if Self::is_left_sibling(node_id) {
176            node_id + 1
177        } else {
178            node_id
179        }
180    }
181
182    #[inline(always)]
183    pub fn is_left_sibling32(node_id: u32) -> bool {
184        node_id % 2 == 1
185    }
186    #[inline(always)]
187    pub fn get_sibling_id32(node_id: u32) -> u32 {
188        if Self::is_left_sibling32(node_id) {
189            node_id + 1
190        } else {
191            node_id - 1
192        }
193    }
194    #[inline(always)]
195    pub fn get_left_sibling_id32(node_id: u32) -> u32 {
196        if Self::is_left_sibling32(node_id) {
197            node_id
198        } else {
199            node_id - 1
200        }
201    }
202    #[inline(always)]
203    pub fn get_right_sibling_id32(node_id: u32) -> u32 {
204        if Self::is_left_sibling32(node_id) {
205            node_id + 1
206        } else {
207            node_id
208        }
209    }
210    #[inline(always)]
211    pub fn make_inner(&mut self, first_index: u32) {
212        self.prim_count = 0;
213        self.first_index = first_index;
214    }
215}