use geometry::prelude::*;
use super::Shape;
use std;
use serde;
use serde::{Serialize, Deserialize};
use serde::ser::{Serializer, SerializeStruct};
use serde::de::{Deserializer, MapAccess, SeqAccess, Visitor};
#[derive(Copy, Clone, PartialEq)]
pub struct Sphere {
pub radius: Float,
pub zmin: Float,
pub zmax: Float,
pub phimax: Float,
thetamin: Float,
thetamax: Float,
}
impl Serialize for Sphere {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut state = s.serialize_struct("Sphere", 4)?;
state.serialize_field("radius", &self.radius)?;
state.serialize_field("zmin", &self.zmin)?;
state.serialize_field("zmax", &self.zmax)?;
state.serialize_field("phimax", &self.phimax)?;
state.end()
}
}
impl<'de> Deserialize<'de> for Sphere {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: Deserializer<'de>
{
#[derive(Deserialize)]
#[serde(field_identifier, rename_all = "lowercase")]
enum Field { Radius, Zmin, Zmax, Phimax }
struct SamplerVisitor;
impl<'de> Visitor<'de> for SamplerVisitor {
type Value = Sphere;
fn expecting(&self, fmter: &mut std::fmt::Formatter) -> std::fmt::Result {
fmter.write_str("struct Sphere")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where V: SeqAccess<'de>
{
let radius = seq.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
let zmin = seq.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
let zmax = seq.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
let phimax = seq.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
Ok(Sphere::new(radius, zmin, zmax, phimax))
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where V: MapAccess<'de>
{
let mut radius = None;
let mut zmin = None;
let mut zmax = None;
let mut phimax = None;
while let Some(key) = map.next_key()? {
match key {
Field::Radius => {
if radius.is_some() {
return Err(serde::de::Error::duplicate_field("radius"));
}
radius = Some(map.next_value()?);
}
Field::Zmin => {
if zmin.is_some() {
return Err(serde::de::Error::duplicate_field("zmin"));
}
zmin = Some(map.next_value()?);
}
Field::Zmax => {
if zmax.is_some() {
return Err(serde::de::Error::duplicate_field("zmax"));
}
zmax = Some(map.next_value()?);
}
Field::Phimax => {
if phimax.is_some() {
return Err(serde::de::Error::duplicate_field("phimax"));
}
phimax = Some(map.next_value()?);
}
}
}
let radius = radius.ok_or_else(||
serde::de::Error::missing_field("radius")
)?;
let zmin = zmin.ok_or_else(||
serde::de::Error::missing_field("zmin")
)?;
let zmax = zmax.ok_or_else(||
serde::de::Error::missing_field("znear")
)?;
let phimax = phimax.ok_or_else(||
serde::de::Error::missing_field("zfar")
)?;
Ok(Sphere::new(
radius, zmin, zmax, phimax
))
}
}
const FIELDS: &[&str] = &["transform", "screen", "znear", "zfar", "fov", "lens", "film"];
deserializer.deserialize_struct("Sphere", FIELDS, SamplerVisitor)
}
}
impl Sphere {
pub fn new(radius: Float, mut zmin: Float, mut zmax: Float, mut phimax: Float) -> Sphere {
assert!(radius>(0.0 as Float), "Sphere radius should be positive");
assert!(zmin<zmax, "zmin should be lower than zmax");
if zmin < -radius { zmin = -radius; }
if zmax > radius { zmax = radius; }
if phimax < (0.0 as Float) { phimax = 0.0 as Float; }
let twopi = float::pi() * (2.0 as Float);
if phimax > twopi { phimax = twopi; }
// TODO: double check
let thetamin = (zmin/radius).acos();
let thetamax = (zmax/radius).acos();
Sphere {
radius: radius,
zmin: zmin,
zmax: zmax,
thetamin: thetamin,
thetamax: thetamax,
phimax: phimax,
}
}
/// Constructs a full sphere
#[inline]
pub fn full(radius: Float) -> Sphere {
Sphere::new(radius, -radius, radius, float::pi() * (2.0 as Float))
}
/// returns the local space bounding box
#[inline]
pub fn bounding(&self) -> BBox3f {
BBox3f::new(
Point3f::new(-self.radius, -self.radius, self.zmin),
Point3f::new(self.radius, self.radius, self.zmax)
)
}
// /// test intersection in local frame, returns `t` when first hit
// #[inline]
// pub fn intersect_ray(&self, ray: &RawRay) -> Option<Float>
// {
// if let Some(t) = Sphere::intersect_ray_full(self.radius, ray) {
// let p = ray.evaluate(t);
// // TODO: refine sphere intersection
// let mut phi = p.y.atan2(p.x);
// if phi < (0.0 as Float) { phi += (2.0 as Float) * float::pi(); }
// if p.z < self.zmin || p.z > self.zmax || phi > self.phimax {
// None
// } else {
// Some(t)
// }
// } else {
// None
// }
// }
/// test intersection against the full sphere
pub fn intersect_ray_full(radius: Float, ray: &RawRay) -> Option<Float>
{
let origin = ray.origin().to_vec();
let direction = ray.direction();
let a = direction.magnitude2();
let b = (direction.mul_element_wise(origin) * (2.0 as Float)).sum();
let c = origin.magnitude2() - radius * radius;
let delta = b* b - (4.0 as Float) * a * c;
if delta < (0.0 as Float) { return None; }
let invert_2a = (1.0 as Float) / ((2.0 as Float) * a);
let d1 = delta.sqrt() * invert_2a;
let d0 = -b * invert_2a;
let(t0, t1) = if invert_2a > 0.0 as Float {
(d0-d1, d0+d1)
} else {
(d0+d1, d0-d1)
};
let tmax = ray.max_extend();
if t0 > tmax || t1 < (0.0 as Float) { return None; }
if t0 > (0.0 as Float) {
Some(t0)
} else if t1 > tmax {
None
} else {
Some(t1)
}
}
}
impl Shape for Sphere {
#[inline]
fn bbox_local(&self) -> BBox3f {
self.bounding()
}
#[inline]
fn intersect_ray(&self, ray: &RawRay) -> Option<(Float, SurfaceInteraction)> {
if let Some(t) = Sphere::intersect_ray_full(self.radius, &ray) {
let mut p = ray.evaluate(t).to_vec();
// refine sphere intersection
p = p* self.radius / p.magnitude();
if p.x == 0.0 as Float && p.y == 0.0 as Float {
p.x = 1e-5 as Float * self.radius;
}
let p = Point3f::from_vec(p);
let mut phi = p.y.atan2(p.x);
if phi < (0.0 as Float) { phi += (2.0 as Float) * float::pi(); }
// TODO: refine test against clipping
if p.z < self.zmin || p.z > self.zmax || phi > self.phimax {
None
} else {
let phimax = self.phimax;
let thetamax = self.thetamax;
let thetamin = self.thetamin;
let thetadelta = thetamax - thetamin;
let u = phi / phimax;
let theta = (p.z / self.radius).acos();
let v = (theta - thetamin) / thetadelta;
let inv_z_radius = (1.0 as Float) / (p.x * p.x + p.y * p.y).sqrt();
let cos_phi = p.x * inv_z_radius;
let sin_phi = p.y * inv_z_radius;
let dpdu = Vector3f::new(-phimax * p.y, phimax * p.x, 0.0 as Float);
let dpdv = thetadelta * Vector3f::new(p.z * cos_phi, p.z * sin_phi, -self.radius * theta.sin());
let (dndu, dndv) = {
let dppduu = - phimax * phimax * Vector3f::new(p.x, p.y, 0.0 as Float);
let dppduv = thetadelta * p.z * phimax * Vector3f::new(-sin_phi, cos_phi, 0.0 as Float);
let dppdvv = -thetadelta * thetadelta * Vector3f::new(p.x, p.y, p.z);
let e = dpdu.dot(dpdu);
let f = dpdu.dot(dpdv);
let g = dpdv.dot(dpdv);
let n = dpdu.cross(dpdv).normalize();
let ee = n.dot(dppduu);
let ff = n.dot(dppduv);
let gg = n.dot(dppdvv);
let inv = (1.0 as Float) / (e * g - f * f);
(
(ff*f - ee*g) * inv * dpdu + (ee*f - ff*e) * inv * dpdv,
(gg*f - ff*g) * inv * dpdu + (ff*f - gg*e) * inv * dpdv
)
};
Some((
t, SurfaceInteraction::new(
p,
// FIXME: wrong
Vector3f::zero(),
-ray.direction(), Point2f::new(u, v),
DuvInfo{
dpdu: dpdu,
dpdv: dpdv,
dndu: dndu,
dndv: dndv,
},
)
))
}
} else {
None
}
}
#[inline]
fn surface_area(&self) -> Float {
self.phimax * self.radius * (self.zmax - self.zmin)
}
fn sample(&self, sample: Point2f) -> (Point3f, Vector3f, Float) {
// sample.x scaled to [0, phimax]
let phi = sample.x * self.phimax;
// sample.y scaled to [thetamin, thetamax]
let theta = sample.y * (self.thetamax - self.thetamin) + self.thetamin;
let dir = Sphericalf::new(theta, phi).to_vec();
let pos = Point3f::from_vec(dir*self.radius);
(pos, dir, 1. as Float / self.surface_area())
// use sample::sample_uniform_sphere;
// let dir = sample_uniform_sphere(sample);
// let pos = Point3f::from_vec(dir*self.radius);
// (pos, dir, 1. as Float / self.surface_area())
}
}