100 lines
3.1 KiB
Rust
100 lines
3.1 KiB
Rust
use std::f32::consts::PI;
|
|
use std::f32::MAX;
|
|
use std::f32::MIN;
|
|
|
|
use crate::aabb::AABB;
|
|
use crate::hitable::Hit;
|
|
use crate::hitable::HitRecord;
|
|
use crate::ray::Ray;
|
|
use crate::vec3::Vec3;
|
|
|
|
pub struct RotateY<H>
|
|
where
|
|
H: Hit,
|
|
{
|
|
hitable: H,
|
|
sin_theta: f32,
|
|
cos_theta: f32,
|
|
bbox: Option<AABB>,
|
|
}
|
|
|
|
impl<H> RotateY<H>
|
|
where
|
|
H: Hit,
|
|
{
|
|
pub fn new(hitable: H, angle: f32) -> RotateY<H> {
|
|
let radians = PI / 180. * angle;
|
|
let sin_theta = radians.sin();
|
|
let cos_theta = radians.cos();
|
|
let mut min = vec![MAX, MAX, MAX];
|
|
let mut max = vec![MIN, MIN, MIN];
|
|
let bbox = hitable.bounding_box(0., 1.).unwrap();
|
|
for i in 0..2 {
|
|
for j in 0..2 {
|
|
for k in 0..2 {
|
|
let x = i as f32 * bbox.max().x + (1 - i) as f32 * bbox.min().x;
|
|
let y = j as f32 * bbox.max().y + (1 - j) as f32 * bbox.min().y;
|
|
let z = k as f32 * bbox.max().z + (1 - k) as f32 * bbox.min().z;
|
|
let new_x = cos_theta * x + sin_theta * z;
|
|
let new_z = -sin_theta * x + cos_theta * z;
|
|
let tester = Vec3::new(new_x, y, new_z);
|
|
for c in 0..3 {
|
|
if tester[c] > max[c] {
|
|
max[c] = tester[c];
|
|
}
|
|
if tester[c] < min[c] {
|
|
min[c] = tester[c];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
RotateY {
|
|
hitable,
|
|
sin_theta,
|
|
cos_theta,
|
|
bbox: Some(AABB::new(
|
|
Vec3::new(min[0], min[1], min[2]),
|
|
Vec3::new(max[0], max[1], max[2]),
|
|
)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<H> Hit for RotateY<H>
|
|
where
|
|
H: Hit,
|
|
{
|
|
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
|
|
let origin = Vec3::new(
|
|
self.cos_theta * r.origin[0] - self.sin_theta * r.origin[2],
|
|
r.origin.y,
|
|
self.sin_theta * r.origin[0] + self.cos_theta * r.origin[2],
|
|
);
|
|
let direction = Vec3::new(
|
|
self.cos_theta * r.direction[0] - self.sin_theta * r.direction[2],
|
|
r.direction.y,
|
|
self.sin_theta * r.direction[0] + self.cos_theta * r.direction[2],
|
|
);
|
|
let rotated_r = Ray::new(origin, direction, r.time);
|
|
if let Some(rec) = self.hitable.hit(rotated_r, t_min, t_max) {
|
|
let p = Vec3::new(
|
|
self.cos_theta * rec.p[0] + self.sin_theta * rec.p[2],
|
|
rec.p[1],
|
|
-self.sin_theta * rec.p[0] + self.cos_theta * rec.p[2],
|
|
);
|
|
let normal = Vec3::new(
|
|
self.cos_theta * rec.normal[0] + self.sin_theta * rec.normal[2],
|
|
rec.normal[1],
|
|
-self.sin_theta * rec.normal[0] + self.cos_theta * rec.normal[2],
|
|
);
|
|
return Some(HitRecord { p, normal, ..rec });
|
|
}
|
|
None
|
|
}
|
|
|
|
fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
|
|
self.bbox.clone()
|
|
}
|
|
}
|