use super::*;
#[cfg(test)]
mod cone_tests;
pub struct Cone {
height: Float,
radius: Float,
material: Material,
}
impl Cone {
pub fn new(height: Float, radius: Float, material: Material) -> Box<Self> {
assert!(height > 0.0);
assert!(radius > 0.0);
Box::new(Self {
height,
radius,
material,
})
}
}
impl Object for Cone {
fn hit(&self, r: &Ray, t_min: Float, t_max: Float) -> Option<Hit> {
let xo = r.origin;
let wi = r.dir;
let dx = EFloat::from(wi.x); let dy = EFloat::from(wi.y);
let dz = EFloat::from(wi.z); let ox = EFloat::from(xo.x);
let oy = EFloat::from(xo.y); let oz = EFloat::from(xo.z);
let tan_theta = EFloat::from(self.radius) / EFloat::from(self.height);
let tan2_theta = tan_theta * tan_theta;
let oy_height = oy - EFloat::from(self.height);
let a = dx * dx - tan2_theta * dy * dy + dz * dz;
let b = EFloat::from(2.0) * (dx * ox - tan2_theta * dy * oy_height + dz * oz);
let c = ox * ox - tan2_theta * oy_height * oy_height + oz * oz;
let (t0, t1) = EFloat::quadratic(a, b, c)?;
if t0.high >= t_max || t1.low <= t_min {
return None;
}
let mut t = if t0.low > t_min {
t0
} else {
if t1.high >= t_max {
return None;
}
t1
};
let mut xi = r.at(t.value);
if xi.y < 0.0 || xi.y > self.height {
t = t1;
xi = r.at(t.value);
if t.high >= t_max || xi.y < 0.0 || xi.y > self.height {
return None;
}
}
let err = Vec3::new(
50.0 * (ox + dx * t).abs_error(),
50.0 * (oy + dy * t).abs_error(),
50.0 * (oz + dz * t).abs_error(),
);
let u = ((-xi.z).atan2(xi.x) + crate::PI) / (2.0 * crate::PI);
let v = xi.y / self.height;
let uv = Vec2::new(u, v);
let radius = (xi.x * xi.x + xi.z * xi.z).sqrt();
let ni = Normal::new(xi.x, radius * tan_theta.value, xi.z);
let ni = ni.normalize();
Hit::new(t.value, &self.material, wi, xi, err, ni, ni, uv)
}
}