use super::*;
#[cfg(test)]
mod cylinder_tests;
pub struct Cylinder {
radius: Float,
height: Float,
material: Material,
}
impl Cylinder {
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 Cylinder {
fn hit(&self, r: &Ray, t_min: Float, t_max: Float) -> Option<Hit> {
let xo = r.origin;
let wi = r.dir;
if wi.x == 0.0 && wi.z == 0.0 {
return None;
}
let dx = EFloat::from(wi.x); let dz = EFloat::from(wi.z);
let ox = EFloat::from(xo.x); let oz = EFloat::from(xo.z);
let radius2 = EFloat::from(self.radius) * EFloat::from(self.radius);
let a = dx * dx + dz * dz;
let b = EFloat::from(2.0) * (dx * ox + dz * oz);
let c = ox * ox + oz * oz - radius2;
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 hit_radius2 = xi.x * xi.x + xi.z * xi.z;
let xi = Point::new(
xi.x * radius2.value / hit_radius2,
xi.y,
xi.z * radius2.value / hit_radius2,
);
let ni = Normal::new(xi.x, 0.0, xi.z) / self.radius;
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 err = efloat::gamma(3) * Vec3::new(xi.x, 0.0, xi.z).abs();
Hit::new(t.value, &self.material, r.dir, xi, err, ni, ni, uv)
}
}