const std = @import("std"); const print = std.debug.print; const math = std.math; const Random = std.rand.Random; pub const Vec3 = packed struct { x: f32 = 0.0, y: f32 = 0.0, z: f32 = 0.0, pub fn add(u: Vec3, v: Vec3) Vec3 { return Vec3{ .x = u.x + v.x, .y = u.y + v.y, .z = u.z + v.z, }; } pub fn sub(u: Vec3, v: Vec3) Vec3 { return Vec3{ .x = u.x - v.x, .y = u.y - v.y, .z = u.z - v.z, }; } pub fn mul(self: Vec3, v: Vec3) Vec3 { return Vec3{ .x = self.x * v.x, .y = self.y * v.y, .z = self.z * v.z, }; } pub fn mul_s(self: Vec3, scalar: f32) Vec3 { return Vec3{ .x = self.x * scalar, .y = self.y * scalar, .z = self.z * scalar, }; } pub fn div(self: Vec3, scalar: f32) Vec3 { return Vec3{ .x = self.x / scalar, .y = self.y / scalar, .z = self.z / scalar, }; } pub fn length(self: Vec3) f32 { return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z); } pub fn length_squared(self: Vec3) f32 { return self.x * self.x + self.y * self.y + self.z * self.z; } pub fn dot(u: Vec3, v: Vec3) f32 { return u.x * v.x + u.y * v.y + u.z * v.z; } pub fn cross(u: Vec3, v: Vec3) Vec3 { return Vec3{ .x = u.y * v.z - u.z * v.y, .y = u.z * v.x - u.x * v.z, .z = u.x * v.y - u.y * v.x, }; } pub fn unit(self: Vec3) Vec3 { return self.div(self.length()); } pub fn nearZero(self: Vec3) bool { const t = 1e-8; return math.absFloat(self.x) < t and math.absFloat(self.y) < t and math.absFloat(self.z) < t; } pub fn reflect(self: Vec3, n: Vec3) Vec3 { return self.sub(n.mul_s(self.dot(n) * 2)); } pub fn refract(self: Vec3, n: Vec3, etai_over_etat: f32) Vec3 { const cos_theta = math.min(self.mul_s(-1).dot(n), 1.0); const r_out_perp = self.add(n.mul_s(cos_theta)).mul_s(etai_over_etat); const r_out_parallel = n.mul_s(-math.sqrt(math.absFloat(1.0 - r_out_perp.length_squared()))); return r_out_perp.add(r_out_parallel); } }; pub fn random(rng: *Random, min: f32, max: f32) Vec3 { const range = max - min; return Vec3{ .x = rng.*.float(f32) * range + min, .y = rng.*.float(f32) * range + min, .z = rng.*.float(f32) * range + min, }; } pub fn random_in_unit_sphere(rng: *Random) Vec3 { while (true) { const v = random(rng, -1, 1); if (v.length_squared() < 1) { return v; } } } pub fn random_unit_vector(rng: *Random) Vec3 { return random_in_unit_sphere(rng).unit(); } pub fn random_in_hemisphere(rng: *Random, normal: Vec3) Vec3 { const in_unit_sphere = random_in_unit_sphere(rng); if (in_unit_sphere.dot(normal) > 0) { // In the same hemisphere as the normal return in_unit_sphere; } else { return in_unit_sphere.mul_s(-1); } } pub const Point3 = Vec3; const assert = @import("std").debug.assert; fn assert_cmp(actual: f32, expected: f32) void { const epsilon: f32 = 1e-5; assert(math.fabs(actual - expected) < epsilon); } test "Vec3.length" { const v = Vec3{ .x = 1.0, .y = 1.0, .z = 1.0 }; assert_cmp(v.length(), 1.7320508); }