85 lines
2.1 KiB
Rust
85 lines
2.1 KiB
Rust
use core::f32::math::sqrt;
|
|
use std::f32::consts::PI;
|
|
use std::fmt::{self, Debug};
|
|
use std::sync::Arc;
|
|
|
|
use crate::Vec3;
|
|
use crate::objects::hit::Hit;
|
|
use crate::objects::materials::traits::Material;
|
|
use crate::objects::traits::Hittable;
|
|
use crate::ray::Ray;
|
|
|
|
pub struct Sphere {
|
|
center: Vec3,
|
|
radius: f32,
|
|
material: Arc<dyn Material>,
|
|
}
|
|
|
|
impl Debug for Sphere {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("Sphere")
|
|
.field("center", &self.center)
|
|
.field("radius", &self.radius)
|
|
.field("material", &self.material)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl Sphere {
|
|
pub fn new(center: Vec3, r: f32, mat: Arc<dyn Material>) -> Self {
|
|
Self {
|
|
center,
|
|
radius: r,
|
|
material: mat,
|
|
}
|
|
}
|
|
|
|
pub fn xyz(x: f32, y: f32, z: f32, r: f32, mat: Arc<dyn Material>) -> Self {
|
|
Self {
|
|
center: Vec3::new(x, y, z),
|
|
radius: r,
|
|
material: mat,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Hittable for Sphere {
|
|
fn hit(&self, r: &Ray) -> Option<Hit> {
|
|
let oc = self.center - r.origin();
|
|
let a = r.dir().length_squared();
|
|
let h = r.dir().dot(&oc);
|
|
let c = oc.length_squared() - self.radius * self.radius;
|
|
let d = h * h - a * c;
|
|
|
|
if d < 0. {
|
|
None
|
|
} else {
|
|
let tl = (h - sqrt(d)) / a;
|
|
let tr = (h + sqrt(d)) / a;
|
|
let t = if tl > 0.001 { tl } else { tr };
|
|
let p = r.at(t);
|
|
let out_n = (p - self.center) / self.radius;
|
|
let uv = self.to_uv(&p);
|
|
Some(Hit::new(
|
|
t,
|
|
p,
|
|
(p - self.center).get_unit(),
|
|
self.material.clone(),
|
|
out_n.dot(r.dir()) < 0.,
|
|
*uv.x(),
|
|
*uv.y(),
|
|
))
|
|
}
|
|
}
|
|
|
|
fn to_uv(&self, point: &Vec3) -> Vec3 {
|
|
let p = *point - self.center;
|
|
// TODO: add rotated texture support
|
|
Vec3::new(
|
|
0.5 + p.y().atan2(*p.x()) / (2. * PI),
|
|
1. - (p.z() / self.radius).acos() / PI,
|
|
0.0,
|
|
)
|
|
}
|
|
}
|