Skip to main content

euv_engine/raytracing/scene/
struct.rs

1use super::*;
2
3/// A 3D ray carrying the parameters needed for traversal and depth tracking.
4#[derive(Clone, Data, Debug, PartialEq)]
5pub struct Ray {
6    /// The world-space origin of the ray.
7    #[get(type(copy))]
8    #[get_mut(pub(crate))]
9    #[set(pub(crate))]
10    pub origin: Vector3D,
11    /// The unit direction of the ray.
12    #[get(type(copy))]
13    #[get_mut(pub(crate))]
14    pub direction: Vector3D,
15    /// The minimum acceptable `t` value for valid intersections.
16    #[get(type(copy))]
17    #[get_mut(pub(crate))]
18    #[set(pub(crate))]
19    pub t_min: f64,
20    /// The maximum `t` value before the ray escapes the scene.
21    #[get(type(copy))]
22    #[get_mut(pub(crate))]
23    #[set(pub(crate))]
24    pub t_max: f64,
25    /// The current recursion depth (used to terminate recursive bounces).
26    #[get(type(copy))]
27    #[get_mut(pub(crate))]
28    #[set(pub(crate))]
29    pub depth: u32,
30}
31
32/// The result of a ray-object intersection.
33#[derive(Clone, Data, Debug, PartialEq)]
34pub struct Hit {
35    /// The ray parameter at the hit point.
36    #[get(type(copy))]
37    #[get_mut(pub(crate))]
38    #[set(pub(crate))]
39    pub t: f64,
40    /// The world-space hit position.
41    #[get(type(copy))]
42    #[get_mut(pub(crate))]
43    #[set(pub(crate))]
44    pub position: Vector3D,
45    /// The outward unit normal at the hit point.
46    #[get(type(copy))]
47    #[get_mut(pub(crate))]
48    #[set(pub(crate))]
49    pub normal: Vector3D,
50    /// The material of the hit surface.
51    #[get(pub(crate), type(clone))]
52    #[get_mut(pub(crate))]
53    #[set(pub(crate))]
54    pub(crate) material: Material,
55}
56
57/// A ray-traceable geometric surface with an attached material.
58#[derive(Clone, Data, Debug, PartialEq)]
59pub struct Occluder {
60    /// The geometric shape represented by this occluder.
61    #[get(type(copy))]
62    #[get_mut(pub(crate))]
63    #[set(pub(crate))]
64    pub kind: OccluderKind,
65    /// For spheres: the center. For AABBs: the minimum corner. Unused by
66    /// triangles, whose vertices live in `vertices`.
67    #[get(type(copy))]
68    #[get_mut(pub(crate))]
69    #[set(pub(crate))]
70    pub center: Vector3D,
71    /// For spheres: `.x` is the radius. For AABBs: the maximum corner.
72    /// Unused by triangles, whose vertices live in `vertices`.
73    #[get(type(copy))]
74    #[get_mut(pub(crate))]
75    #[set(pub(crate))]
76    pub extent: Vector3D,
77    /// For triangles: the three corners in winding order. Unused by spheres
78    /// and AABBs, which are fully described by `center` and `extent`.
79    #[get(type(copy))]
80    #[get_mut(pub(crate))]
81    #[set(pub(crate))]
82    pub vertices: [Vector3D; 3],
83    /// The surface material.
84    #[get(pub(crate))]
85    #[get_mut(pub(crate))]
86    #[set(pub(crate))]
87    pub(crate) material: Material,
88}
89
90/// One node of the flat bounding volume hierarchy owned by
91/// [`RayTraceScene`].
92///
93/// The hierarchy is stored as a preorder array: a node is followed
94/// immediately by its left child at `index + 1`, and `escape` holds the
95/// index of the first node after this node's entire subtree. A node with
96/// `count == 0` is internal, and traversal either descends into
97/// `index + 1` or jumps to `escape` when the ray misses the bounds. A node
98/// with `count > 0` is a leaf whose `start`/`count` slice of the scene's
99/// `indices` array lists the primitives it owns, reordered but never
100/// renumbered: every entry stays the occluder's original index.
101#[derive(Clone, Copy, Data, Debug, PartialEq)]
102pub(crate) struct RayTraceNode {
103    /// The minimum corner of the axis-aligned bounds covering the subtree.
104    #[get(pub(crate), type(copy))]
105    #[get_mut(pub(crate))]
106    #[set(pub(crate))]
107    pub(crate) lower: Vector3D,
108    /// The maximum corner of the axis-aligned bounds covering the subtree.
109    #[get(pub(crate), type(copy))]
110    #[get_mut(pub(crate))]
111    #[set(pub(crate))]
112    pub(crate) upper: Vector3D,
113    /// The first entry of this leaf's primitive slice in the scene's
114    /// `indices` array; unused when `count == 0`.
115    #[get(pub(crate), type(copy))]
116    #[get_mut(pub(crate))]
117    #[set(pub(crate))]
118    pub(crate) start: usize,
119    /// The number of primitives in this leaf; zero marks an internal node.
120    #[get(pub(crate), type(copy))]
121    #[get_mut(pub(crate))]
122    #[set(pub(crate))]
123    pub(crate) count: usize,
124    /// The index of the first node after this subtree, so a miss can skip
125    /// the whole subtree in one step.
126    #[get(pub(crate), type(copy))]
127    #[get_mut(pub(crate))]
128    #[set(pub(crate))]
129    pub(crate) escape: usize,
130}
131
132/// The per-primitive bounds and centroids a BVH build reads while
133/// splitting.
134///
135/// Holding them apart from [`RayTraceScene`] keeps the build scratch out
136/// of the stored scene: once the hierarchy is built the three arrays are
137/// dropped, so nothing a query touches carries build-time state.
138#[derive(Debug)]
139pub(crate) struct BvhBuild {
140    /// The minimum corner of each primitive's world-space bounds.
141    pub(crate) lower: Vec<Vector3D>,
142    /// The maximum corner of each primitive's world-space bounds.
143    pub(crate) upper: Vec<Vector3D>,
144    /// The midpoint of each primitive's bounds, used to bin and partition.
145    pub(crate) centroids: Vec<Vector3D>,
146}
147
148/// A half-open slice of the permuted primitive index array that one BVH
149/// node owns.
150///
151/// The named start and count fields are shared by measurement, partition,
152/// and split helpers.
153#[derive(Clone, Copy, Debug)]
154pub(crate) struct BvhRange {
155    /// The first entry of the range in the primitive index array.
156    pub(crate) start: usize,
157    /// The number of entries in the range.
158    pub(crate) count: usize,
159}
160
161/// The single node one `build_bvh_subtree` call is responsible for
162/// emitting.
163///
164/// The bounds, the primitive range and the nesting level describe which
165/// subtree is being built and at what depth, which is distinct from the
166/// mutable arrays every step shares.
167#[derive(Clone, Copy, Debug)]
168pub(crate) struct BvhSubtree {
169    /// The `(min, max)` corners covering this subtree.
170    pub(crate) bounds: (Vector3D, Vector3D),
171    /// The primitives this subtree owns.
172    pub(crate) range: BvhRange,
173    /// The nesting level of this subtree, counting from zero at the root.
174    pub(crate) depth: usize,
175}
176
177/// One centroid axis and the position on it where a range is cut.
178///
179/// The surface-area search chooses a split and the fallback median split
180/// picks one too, so both hand the same pair of values to the partition
181/// step instead of it re-reading two loose arguments.
182#[derive(Clone, Copy, Debug)]
183pub(crate) struct SahSplit {
184    /// `0` for `x`, `1` for `y`, `2` for `z`.
185    pub(crate) axis: usize,
186    /// The split position on that axis.
187    pub(crate) boundary: f64,
188}
189
190/// The mutable build state that every BVH construction step shares.
191///
192/// The node array under construction and the primitive index array it
193/// permutes have to be threaded through every helper, including both sides
194/// of every recursive call. Holding them behind one borrowed context keeps
195/// each helper's remaining parameters to the decisions that helper alone
196/// makes. Constructing the context does not allocate additional storage.
197#[derive(Debug)]
198pub(crate) struct BvhBuildContext<'a> {
199    /// The preorder node array under construction.
200    pub(crate) nodes: &'a mut Vec<RayTraceNode>,
201    /// The primitive index array, partitioned in place.
202    pub(crate) indices: &'a mut [usize],
203    /// The per-primitive bounds and centroids the split search reads.
204    pub(crate) build: &'a BvhBuild,
205}
206
207/// An owned ray-tracing scene with a precomputed bounding volume
208/// hierarchy and shadow data.
209///
210/// `RayTraceScene` bundles the occluder list, the flat BVH over their
211/// bounds, and the `(center, radius)` shadow bounding spheres consumed by
212/// [`soft_shadow_factor`], computing both exactly once at construction.
213/// Tracing through [`RayTraceScene::trace`] and querying through
214/// [`RayTraceScene::closest_hit`] perform no heap allocation per ray or
215/// per bounce, making the scene the single canonical entry point for
216/// tracing many rays against a static scene.
217#[derive(Clone, Debug, Getter, PartialEq)]
218pub struct RayTraceScene {
219    /// All occluding surfaces in the scene.
220    #[get(pub(crate))]
221    pub(crate) occluders: Vec<Occluder>,
222    /// The flat preorder BVH over the occluders' bounds. Shared verbatim
223    /// with the GPU encoders, so the layout is part of the internal
224    /// contract rather than an implementation detail.
225    #[get(pub(crate))]
226    pub(crate) nodes: Vec<RayTraceNode>,
227    /// The reordered primitive indexes addressed by the leaves' `start` and
228    /// `count` fields. Every value stays the occluder's original index.
229    #[get(pub(crate))]
230    pub(crate) indices: Vec<usize>,
231    /// Precomputed `(center, radius)` shadow bounding spheres, one per
232    /// occluder, in the same order as `occluders`.
233    #[get(pub(crate))]
234    pub(crate) shadow_points: Vec<(Vector3D, f64)>,
235}