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}