///|
/// Cone geometry tapered along the Y-axis.
/// Defined by base center, base radius, apex point (tip), and height.
pub(all) struct Cone {
base_center : Vec3
base_radius : Double
apex : Vec3
height : Double
material : Material
} derive(Debug)
pub fn Cone::new(base_center~ : Vec3, base_radius~ : Double, height~ : Double, material~ : Material) -> Cone {
let apex = { x: base_center.x, y: base_center.y + height, z: base_center.z }
{ base_center, base_radius, apex, height, material }
}
pub fn Cone::hit_cone(self : Cone, r : Ray, t_min~ : Double, t_max~ : Double) -> HitRecord? {
let direction = { x: 0.0, y: 1.0, z: 0.0 }
let apex = self.apex
let base = self.base_center
let r_cone = self.base_radius
let h_cone = self.height
let cos_a2 = h_cone * h_cone / (h_cone * h_cone + r_cone * r_cone)
let v = direction
let w = r.orig - apex
let dv = r.dir.dot(v)
let wv = w.dot(v)
let a = dv * dv - cos_a2 * r.dir.length_squared()
let b = 2.0 * (dv * wv - cos_a2 * r.dir.dot(w))
let c_side = wv * wv - cos_a2 * w.length_squared()
let disc = b * b - 4.0 * a * c_side
let mut closest_t = t_max
let mut found = false
let mut hit_normal = { x: 0.0, y: 0.0, z: 0.0 }
if disc >= 0.0 && a.abs() > 1.0e-8 {
let sqrt_disc = disc.sqrt()
let t0 = (-b - sqrt_disc) / (2.0 * a)
let t1 = (-b + sqrt_disc) / (2.0 * a)
let check_t = fn(t_val : Double) -> Unit {
if t_val < t_min || t_val > t_max {
return
}
let p = r.at(t_val)
let y_proj = (p - apex).dot(v)
if y_proj >= 0.0 && y_proj <= h_cone {
let cone_normal = {
x: (p.x - apex.x) / r_cone,
y: r_cone / h_cone,
z: (p.z - apex.z) / r_cone,
}.normalize()
if t_val < closest_t {
closest_t = t_val
hit_normal = cone_normal
found = true
}
}
}
check_t(t0)
check_t(t1)
}
let base_denom = r.dir.dot(v)
if base_denom.abs() > 1.0e-8 {
let t_base = (base.y - r.orig.y) / base_denom
if t_base >= t_min && t_base <= t_max {
let p = r.at(t_base)
if (p.x - base.x) * (p.x - base.x) + (p.z - base.z) * (p.z - base.z) <= r_cone * r_cone {
if t_base < closest_t {
closest_t = t_base
hit_normal = { x: 0.0, y: -1.0, z: 0.0 }
found = true
}
}
}
}
if !found {
return None
}
let p = r.at(closest_t)
let rec = { p, normal: hit_normal, t: closest_t, front_face: false, material: self.material }
Some(rec.set_face_normal(r, hit_normal))
}