aboutsummaryrefslogtreecommitdiff
path: root/src/hittable/sphere.rs
blob: f27636657d6c65b54a9042d7b980e3e80b4b5606 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::f64::consts;
use std::sync::Arc;

use crate::ray::Ray;
use crate::vec3::Point3;
use crate::{
    hittable::{HitRecord, Hittable, AABB},
    material::Material,
    vec3::Vec3,
};

pub struct Sphere {
    pub center: Point3,
    pub radius: f64,
    pub material: Arc<dyn Material>,
}

impl Sphere {
    pub fn get_sphere_uv(p: &Point3, u: &mut f64, v: &mut f64) {
        let theta = (-p.y).acos();
        let phi = -p.z.atan2(p.x) + consts::PI;

        *u = phi / (2.0 * consts::PI);
        *v = theta / consts::PI;
    }
}

impl Hittable for Sphere {
    fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
        let oc = &ray.origin - &self.center;
        let a = ray.direction.length_squared();
        let half_b = oc.dot(&ray.direction);
        let c = oc.length_squared() - self.radius * self.radius;
        let discriminant = half_b * half_b - a * c;
        if discriminant < 0.0 {
            return None;
        }

        let sqrtd = discriminant.sqrt();
        // Find the nearest root that lies within acceptable range
        let mut root = (-half_b - sqrtd) / a;
        if root < t_min || t_max < root {
            root = (-half_b + sqrtd) / a;
            if root < t_min || t_max < root {
                return None;
            }
        }

        let mut hit_record = HitRecord::new();
        hit_record.t = root;
        hit_record.p = ray.at(hit_record.t);
        let outward_normal = (&hit_record.p - &self.center) / self.radius;
        hit_record.set_face_normal(ray, &outward_normal);
        Self::get_sphere_uv(&outward_normal, &mut hit_record.u, &mut hit_record.v);
        hit_record.material = Some(self.material.clone());
        Some(hit_record)
    }

    fn bounding_box(&self, _: f64, _: f64) -> Option<AABB> {
        Some(AABB {
            minimum: &self.center
                - Vec3 {
                    x: self.radius,
                    y: self.radius,
                    z: self.radius,
                },
            maximum: &self.center
                + Vec3 {
                    x: self.radius,
                    y: self.radius,
                    z: self.radius,
                },
        })
    }
}