1use std::f64::consts::PI;
6
7use super::functions::{
8 add, cross, dot, len, normalize, ray_aabb, ray_sphere, ray_triangle, reflect_ray, scale, sub,
9};
10
11#[derive(Debug, Clone, Copy)]
13pub struct RayHit {
14 pub t: f64,
16 pub point: [f64; 3],
18 pub normal: [f64; 3],
20 pub uv: [f64; 2],
22 pub prim_id: usize,
24}
25impl RayHit {
26 pub fn new(t: f64, point: [f64; 3], normal: [f64; 3], uv: [f64; 2], prim_id: usize) -> Self {
28 Self {
29 t,
30 point,
31 normal,
32 uv,
33 prim_id,
34 }
35 }
36 pub fn closer_than(&self, other: &RayHit) -> bool {
38 self.t < other.t
39 }
40}
41#[derive(Debug, Clone)]
43pub struct BatchRayResult {
44 pub hits: Vec<Option<RayHit>>,
46}
47#[derive(Debug, Clone)]
52pub struct RayTreeNode {
53 pub ray: Ray,
55 pub hit: Option<RayHit>,
57 pub weight: f64,
59 pub reflected_child: Option<usize>,
61 pub refracted_child: Option<usize>,
63}
64impl RayTreeNode {
65 pub fn new(ray: Ray, weight: f64) -> Self {
67 Self {
68 ray,
69 hit: None,
70 weight,
71 reflected_child: None,
72 refracted_child: None,
73 }
74 }
75}
76#[derive(Debug, Clone, Copy)]
78pub struct Capsule {
79 pub a: [f64; 3],
81 pub b: [f64; 3],
83 pub radius: f64,
85}
86impl Capsule {
87 pub fn new(a: [f64; 3], b: [f64; 3], radius: f64) -> Self {
89 Self { a, b, radius }
90 }
91 pub fn axis(&self) -> [f64; 3] {
93 sub(self.b, self.a)
94 }
95 pub fn length(&self) -> f64 {
97 len(self.axis())
98 }
99}
100#[derive(Debug, Clone, Copy)]
105pub struct Obb {
106 pub centre: [f64; 3],
108 pub axes: [[f64; 3]; 3],
110 pub half_extents: [f64; 3],
112}
113impl Obb {
114 pub fn new(centre: [f64; 3], axes: [[f64; 3]; 3], half_extents: [f64; 3]) -> Self {
116 Self {
117 centre,
118 axes,
119 half_extents,
120 }
121 }
122 pub fn from_aabb(aabb: &Aabb) -> Self {
124 Self::new(
125 aabb.centre(),
126 [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
127 aabb.half_extents(),
128 )
129 }
130}
131#[derive(Debug, Clone, Copy)]
133pub struct Sphere {
134 pub centre: [f64; 3],
136 pub radius: f64,
138}
139impl Sphere {
140 pub fn new(centre: [f64; 3], radius: f64) -> Self {
142 Self { centre, radius }
143 }
144 pub fn normal_at(&self, p: [f64; 3]) -> [f64; 3] {
146 normalize(sub(p, self.centre))
147 }
148 pub fn uv_at(&self, p: [f64; 3]) -> [f64; 2] {
152 let n = self.normal_at(p);
153 let u = 1.0 - (n[0].atan2(n[2]) + PI) / (2.0 * PI);
154 let v = (n[1].asin() + PI / 2.0) / PI;
155 [u, v]
156 }
157}
158pub struct Scene {
160 pub spheres: Vec<Sphere>,
162 pub vertices: Vec<[f64; 3]>,
164 pub triangles: Vec<(usize, usize, usize)>,
166}
167impl Scene {
168 pub fn new() -> Self {
170 Self {
171 spheres: Vec::new(),
172 vertices: Vec::new(),
173 triangles: Vec::new(),
174 }
175 }
176 pub fn add_sphere(&mut self, sphere: Sphere) {
178 self.spheres.push(sphere);
179 }
180 pub fn add_triangle(&mut self, v0: [f64; 3], v1: [f64; 3], v2: [f64; 3]) {
182 let base = self.vertices.len();
183 self.vertices.push(v0);
184 self.vertices.push(v1);
185 self.vertices.push(v2);
186 self.triangles.push((base, base + 1, base + 2));
187 }
188 pub fn cast(&self, ray: &Ray) -> Option<RayHit> {
190 let mut best: Option<RayHit> = None;
191 for sphere in &self.spheres {
192 if let Some(hit) = ray_sphere(ray, sphere) {
193 best = Some(match best {
194 None => hit,
195 Some(prev) => {
196 if hit.t < prev.t {
197 hit
198 } else {
199 prev
200 }
201 }
202 });
203 }
204 }
205 for &(i, j, k) in &self.triangles {
206 if let Some(hit) =
207 ray_triangle(ray, self.vertices[i], self.vertices[j], self.vertices[k], i)
208 {
209 best = Some(match best {
210 None => hit,
211 Some(prev) => {
212 if hit.t < prev.t {
213 hit
214 } else {
215 prev
216 }
217 }
218 });
219 }
220 }
221 best
222 }
223}
224pub struct Bvh {
226 pub nodes: Vec<BvhNode>,
228 pub prim_indices: Vec<usize>,
230 pub vertices: Vec<[f64; 3]>,
232 pub indices: Vec<(usize, usize, usize)>,
234}
235impl Bvh {
236 pub fn build(vertices: Vec<[f64; 3]>, indices: Vec<(usize, usize, usize)>) -> Self {
238 let n = indices.len();
239 let mut prim_indices: Vec<usize> = (0..n).collect();
240 let mut nodes = Vec::new();
241 Self::build_recursive(&vertices, &indices, &mut prim_indices, 0, n, &mut nodes);
242 Self {
243 nodes,
244 prim_indices,
245 vertices,
246 indices,
247 }
248 }
249 fn triangle_centroid(vertices: &[[f64; 3]], tri: (usize, usize, usize)) -> [f64; 3] {
250 let (i, j, k) = tri;
251 let v0 = vertices[i];
252 let v1 = vertices[j];
253 let v2 = vertices[k];
254 scale(add(add(v0, v1), v2), 1.0 / 3.0)
255 }
256 fn triangle_aabb(vertices: &[[f64; 3]], tri: (usize, usize, usize)) -> Aabb {
257 let (i, j, k) = tri;
258 let v0 = vertices[i];
259 let v1 = vertices[j];
260 let v2 = vertices[k];
261 let min = [
262 v0[0].min(v1[0]).min(v2[0]),
263 v0[1].min(v1[1]).min(v2[1]),
264 v0[2].min(v1[2]).min(v2[2]),
265 ];
266 let max = [
267 v0[0].max(v1[0]).max(v2[0]),
268 v0[1].max(v1[1]).max(v2[1]),
269 v0[2].max(v1[2]).max(v2[2]),
270 ];
271 Aabb::new(min, max)
272 }
273 fn build_recursive(
274 vertices: &[[f64; 3]],
275 indices: &[(usize, usize, usize)],
276 prim_indices: &mut Vec<usize>,
277 start: usize,
278 end: usize,
279 nodes: &mut Vec<BvhNode>,
280 ) -> usize {
281 let node_idx = nodes.len();
282 nodes.push(BvhNode::leaf(Aabb::new([0.0; 3], [0.0; 3]), 0, 0));
283 let mut aabb = Aabb::new([f64::INFINITY; 3], [f64::NEG_INFINITY; 3]);
284 for &pi in &prim_indices[start..end] {
285 let tri_bb = Self::triangle_aabb(vertices, indices[pi]);
286 aabb = Aabb::merge(&aabb, &tri_bb);
287 }
288 let n_prims = end - start;
289 if n_prims <= 2 {
290 nodes[node_idx] = BvhNode::leaf(aabb, start, n_prims);
291 return node_idx;
292 }
293 let d = sub(aabb.max, aabb.min);
294 let axis = if d[0] >= d[1] && d[0] >= d[2] {
295 0
296 } else if d[1] >= d[2] {
297 1
298 } else {
299 2
300 };
301 prim_indices[start..end].sort_by(|&a, &b| {
302 let ca = Self::triangle_centroid(vertices, indices[a])[axis];
303 let cb = Self::triangle_centroid(vertices, indices[b])[axis];
304 ca.partial_cmp(&cb).unwrap_or(std::cmp::Ordering::Equal)
305 });
306 let mid = (start + end) / 2;
307 let left = Self::build_recursive(vertices, indices, prim_indices, start, mid, nodes);
308 let right = Self::build_recursive(vertices, indices, prim_indices, mid, end, nodes);
309 nodes[node_idx] = BvhNode::internal(aabb, left, right);
310 node_idx
311 }
312 pub fn intersect(&self, ray: &Ray) -> Option<RayHit> {
314 let mut best: Option<RayHit> = None;
315 let mut stack = vec![0usize];
316 while let Some(node_idx) = stack.pop() {
317 let node = &self.nodes[node_idx];
318 if ray_aabb(ray, &node.aabb).is_none() {
319 continue;
320 }
321 if node.is_leaf() {
322 for i in node.prim_start..(node.prim_start + node.prim_count) {
323 let pi = self.prim_indices[i];
324 let (vi, vj, vk) = self.indices[pi];
325 if vi >= self.vertices.len()
326 || vj >= self.vertices.len()
327 || vk >= self.vertices.len()
328 {
329 continue;
330 }
331 if let Some(hit) = ray_triangle(
332 ray,
333 self.vertices[vi],
334 self.vertices[vj],
335 self.vertices[vk],
336 pi,
337 ) {
338 best = Some(match best {
339 None => hit,
340 Some(prev) => {
341 if hit.t < prev.t {
342 hit
343 } else {
344 prev
345 }
346 }
347 });
348 }
349 }
350 } else {
351 stack.push(node.left);
352 stack.push(node.right);
353 }
354 }
355 best
356 }
357}
358#[derive(Debug, Clone)]
363pub struct ConvexMesh {
364 pub vertices: Vec<[f64; 3]>,
366}
367impl ConvexMesh {
368 pub fn new(vertices: Vec<[f64; 3]>) -> Self {
370 Self { vertices }
371 }
372 pub fn support(&self, dir: [f64; 3]) -> [f64; 3] {
374 self.vertices
375 .iter()
376 .copied()
377 .fold(self.vertices[0], |best, v| {
378 if dot(v, dir) > dot(best, dir) {
379 v
380 } else {
381 best
382 }
383 })
384 }
385 pub fn aabb(&self) -> Aabb {
387 let mut min = [f64::INFINITY; 3];
388 let mut max = [f64::NEG_INFINITY; 3];
389 for &v in &self.vertices {
390 for i in 0..3 {
391 min[i] = min[i].min(v[i]);
392 max[i] = max[i].max(v[i]);
393 }
394 }
395 Aabb::new(min, max)
396 }
397}
398#[derive(Debug, Clone, Copy)]
400pub struct Aabb {
401 pub min: [f64; 3],
403 pub max: [f64; 3],
405}
406impl Aabb {
407 pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
409 Self { min, max }
410 }
411 pub fn centre(&self) -> [f64; 3] {
413 scale(add(self.min, self.max), 0.5)
414 }
415 pub fn half_extents(&self) -> [f64; 3] {
417 scale(sub(self.max, self.min), 0.5)
418 }
419 pub fn surface_area(&self) -> f64 {
421 let d = sub(self.max, self.min);
422 2.0 * (d[0] * d[1] + d[1] * d[2] + d[2] * d[0])
423 }
424 pub fn merge(a: &Aabb, b: &Aabb) -> Aabb {
426 Aabb::new(
427 [
428 a.min[0].min(b.min[0]),
429 a.min[1].min(b.min[1]),
430 a.min[2].min(b.min[2]),
431 ],
432 [
433 a.max[0].max(b.max[0]),
434 a.max[1].max(b.max[1]),
435 a.max[2].max(b.max[2]),
436 ],
437 )
438 }
439 pub fn expand(&self, p: [f64; 3]) -> Aabb {
441 Aabb::new(
442 [
443 self.min[0].min(p[0]),
444 self.min[1].min(p[1]),
445 self.min[2].min(p[2]),
446 ],
447 [
448 self.max[0].max(p[0]),
449 self.max[1].max(p[1]),
450 self.max[2].max(p[2]),
451 ],
452 )
453 }
454}
455pub struct RayTree {
459 pub nodes: Vec<RayTreeNode>,
461 pub max_depth: usize,
463}
464impl RayTree {
465 pub fn new(max_depth: usize) -> Self {
467 Self {
468 nodes: Vec::new(),
469 max_depth,
470 }
471 }
472 pub fn trace(&mut self, primary: Ray, spheres: &[Sphere]) {
476 self.nodes.clear();
477 self.nodes.push(RayTreeNode::new(primary, 1.0));
478 let mut i = 0;
479 while i < self.nodes.len() {
480 let depth = self.compute_depth(i);
481 if depth >= self.max_depth {
482 i += 1;
483 continue;
484 }
485 let ray = self.nodes[i].ray;
486 let weight = self.nodes[i].weight;
487 let mut best: Option<RayHit> = None;
488 for sphere in spheres {
489 if let Some(hit) = ray_sphere(&ray, sphere) {
490 best = Some(match best {
491 None => hit,
492 Some(prev) => {
493 if hit.t < prev.t {
494 hit
495 } else {
496 prev
497 }
498 }
499 });
500 }
501 }
502 self.nodes[i].hit = best;
503 if let Some(hit) = best
504 && weight > 0.01
505 {
506 let r_dir = reflect_ray(ray.direction, hit.normal);
507 let r_ray = ray.spawn_offset(hit.point, r_dir);
508 let child_idx = self.nodes.len();
509 self.nodes.push(RayTreeNode::new(r_ray, weight * 0.8));
510 self.nodes[i].reflected_child = Some(child_idx);
511 }
512 i += 1;
513 }
514 }
515 fn compute_depth(&self, target: usize) -> usize {
517 let mut depth = 0usize;
518 let mut current = target;
519 'outer: loop {
520 for (parent, node) in self.nodes.iter().enumerate() {
521 if node.reflected_child == Some(current) || node.refracted_child == Some(current) {
522 depth += 1;
523 current = parent;
524 continue 'outer;
525 }
526 }
527 break;
528 }
529 depth
530 }
531}
532#[derive(Debug, Clone, Copy)]
535pub struct RayDifferential {
536 pub ray: Ray,
538 pub rx: Ray,
540 pub ry: Ray,
542 pub pixel_spread: f64,
544}
545impl RayDifferential {
546 pub fn new(ray: Ray, rx: Ray, ry: Ray) -> Self {
548 Self {
549 ray,
550 rx,
551 ry,
552 pixel_spread: 1.0,
553 }
554 }
555 pub fn footprint_at(&self, t: f64) -> f64 {
559 let p = self.ray.at(t);
560 let px = self.rx.at(t);
561 let py = self.ry.at(t);
562 let dx = sub(px, p);
563 let dy = sub(py, p);
564 let c = cross(dx, dy);
565 len(c)
566 }
567 pub fn transfer_to_surface(&self, t: f64, normal: [f64; 3]) -> ([f64; 3], [f64; 3]) {
572 let n = normalize(normal);
573 let p = self.ray.at(t);
574 let px_world = self.rx.at(t);
575 let py_world = self.ry.at(t);
576 let transfer_point = |q: [f64; 3]| -> [f64; 3] {
577 let offset = dot(sub(q, p), n);
578 sub(q, scale(n, offset))
579 };
580 (transfer_point(px_world), transfer_point(py_world))
581 }
582}
583#[derive(Debug, Clone)]
585pub struct BvhNode {
586 pub aabb: Aabb,
588 pub left: usize,
590 pub right: usize,
592 pub prim_start: usize,
594 pub prim_count: usize,
596}
597impl BvhNode {
598 pub fn internal(aabb: Aabb, left: usize, right: usize) -> Self {
600 Self {
601 aabb,
602 left,
603 right,
604 prim_start: 0,
605 prim_count: 0,
606 }
607 }
608 pub fn leaf(aabb: Aabb, prim_start: usize, prim_count: usize) -> Self {
610 Self {
611 aabb,
612 left: usize::MAX,
613 right: usize::MAX,
614 prim_start,
615 prim_count,
616 }
617 }
618 pub fn is_leaf(&self) -> bool {
620 self.left == usize::MAX
621 }
622}
623pub struct Heightfield {
627 pub heights: Vec<f64>,
629 pub cols: usize,
631 pub rows: usize,
633 pub cell_size: f64,
635}
636impl Heightfield {
637 pub fn new(heights: Vec<f64>, cols: usize, rows: usize, cell_size: f64) -> Self {
639 Self {
640 heights,
641 cols,
642 rows,
643 cell_size,
644 }
645 }
646 pub fn height_at(&self, x: f64, z: f64) -> f64 {
648 let xi = (x / self.cell_size).max(0.0);
649 let zi = (z / self.cell_size).max(0.0);
650 let ix = (xi as usize).min(self.cols - 2);
651 let iz = (zi as usize).min(self.rows - 2);
652 let fx = xi - ix as f64;
653 let fz = zi - iz as f64;
654 let h00 = self.heights[iz * self.cols + ix];
655 let h10 = self.heights[iz * self.cols + ix + 1];
656 let h01 = self.heights[(iz + 1) * self.cols + ix];
657 let h11 = self.heights[(iz + 1) * self.cols + ix + 1];
658 h00 * (1.0 - fx) * (1.0 - fz)
659 + h10 * fx * (1.0 - fz)
660 + h01 * (1.0 - fx) * fz
661 + h11 * fx * fz
662 }
663 pub fn width(&self) -> f64 {
665 (self.cols - 1) as f64 * self.cell_size
666 }
667 pub fn depth(&self) -> f64 {
669 (self.rows - 1) as f64 * self.cell_size
670 }
671}
672#[derive(Debug, Clone, Copy)]
677pub struct Ray {
678 pub origin: [f64; 3],
680 pub direction: [f64; 3],
682 pub t_min: f64,
684 pub t_max: f64,
686}
687impl Ray {
688 pub fn new(origin: [f64; 3], direction: [f64; 3]) -> Self {
690 Self {
691 origin,
692 direction,
693 t_min: 0.0,
694 t_max: f64::INFINITY,
695 }
696 }
697 pub fn new_normalised(origin: [f64; 3], direction: [f64; 3]) -> Self {
699 Self::new(origin, normalize(direction))
700 }
701 pub fn at(&self, t: f64) -> [f64; 3] {
703 add(self.origin, scale(self.direction, t))
704 }
705 pub fn valid_t(&self, t: f64) -> bool {
707 t >= self.t_min && t <= self.t_max
708 }
709 pub fn spawn_offset(&self, origin: [f64; 3], direction: [f64; 3]) -> Ray {
711 let eps = 1e-6;
712 Ray::new(add(origin, scale(direction, eps)), normalize(direction))
713 }
714}