this repo has no description
1const math = @import("std").math;
2
3const zm = @import("zmath");
4
5const AABB = @import("../AABB.zig");
6const IntervalF32 = @import("../interval.zig").IntervalF32;
7const Ray = @import("../Ray.zig");
8const HitRecord = @import("../hittable.zig").HitRecord;
9const Material = @import("../material.zig").Material;
10
11const Sphere = @This();
12
13center: zm.Vec,
14radius: f32,
15mat: *Material,
16is_moving: bool = false,
17center_vec: zm.Vec = zm.f32x4s(0),
18bbox: AABB,
19
20pub fn init(center: zm.Vec, radius: f32, mat: *Material) Sphere {
21 const rvec = zm.f32x4s(radius);
22 return Sphere{
23 .center = center,
24 .radius = @max(0, radius),
25 .mat = mat,
26 .bbox = AABB.initP(center - rvec, center + rvec),
27 };
28}
29
30pub fn initMoving(center1: zm.Vec, center2: zm.Vec, radius: f32, mat: *Material) Sphere {
31 const rvec = zm.f32x4s(radius);
32 const box1 = AABB.initP(center1 - rvec, center1 + rvec);
33 const box2 = AABB.initP(center2 - rvec, center2 + rvec);
34
35 return Sphere{
36 .center = center1,
37 .radius = @max(0, radius),
38 .mat = mat,
39 .is_moving = true,
40 .center_vec = center2 - center1,
41 .bbox = AABB.initAB(&box1, &box2),
42 };
43}
44
45pub inline fn boundingBox(self: *Sphere) AABB {
46 return self.bbox;
47}
48
49pub fn hit(self: *const Sphere, r: *Ray, ray_t: IntervalF32) ?HitRecord {
50 const center = self.sphereCenter(r.tm);
51 const oc = r.orig - center;
52 const a = zm.lengthSq3(r.dir)[0];
53 const half_b = zm.dot3(oc, r.dir)[0];
54 const c = zm.dot3(oc, oc)[0] - self.radius * self.radius;
55
56 const discriminant = half_b * half_b - a * c;
57 if (discriminant < 0) return null;
58
59 const sqrtd = @sqrt(discriminant);
60
61 // Find the nearest root that lies in the acceptable range
62 var root = (-half_b - sqrtd) / a;
63 if (!ray_t.surrounds(root)) {
64 root = (-half_b + sqrtd) / a;
65 if (!ray_t.surrounds(root)) return null;
66 }
67
68 var rec = HitRecord{
69 .t = root,
70 .p = r.at(root),
71 .mat = self.mat,
72 };
73
74 const outward_normal = (rec.p - self.center) / zm.f32x4s(self.radius);
75 rec.setFaceNormal(r, outward_normal);
76 setSphereUV(&rec, outward_normal);
77
78 return rec;
79}
80
81pub inline fn sphereCenter(self: *const Sphere, time: f32) zm.Vec {
82 if (!self.is_moving) return self.center;
83 return self.center + zm.f32x4s(time) * self.center_vec;
84}
85
86fn setSphereUV(rec: *HitRecord, p: zm.Vec) void {
87 const theta = math.acos(-p[1]);
88 const phi = math.atan2(-p[2], p[0]) + math.pi;
89
90 rec.u = phi / (2 * math.pi);
91 rec.v = theta / phi;
92}