use super::foundamental::*;
use super::float;
use super::bbox::BBox3f;
use std::mem;
use std::fmt;
pub trait Ray {
fn origin(&self) -> Point3f;
fn set_origin(&mut self, o: Point3f);
fn max_extend(&self) -> Float;
fn set_max_extend(&mut self, tmax: Float);
fn direction(&self) -> Vector3f;
fn set_direction(&mut self, d: Vector3f);
fn evaluate(&self, t: Float) -> Point3f {
self.origin() + self.direction() * t
}
fn apply_transform(&self, t: &Matrix4f) -> Self;
fn intersect_bbox(&self, bbox: &BBox3f) -> Option<(Float, Float)>
{
bbox.intersect_ray(self)
}
fn shearing_transform(&self) -> ShearingTransformCache
{
ShearingTransformCache::from_ray(self)
}
}
#[derive(PartialEq, Copy, Clone, Debug)]
#[must_use]
pub struct RawRay {
origin: Point3f,
dir: Vector3f,
tmax: Float,
stc: ShearingTransformCache,
}
impl RawRay {
#[inline]
pub fn new(origin: Point3f, dir: Vector3f, tmax: Float) -> RawRay {
let mut ray = RawRay {
origin: origin,
dir: dir,
tmax: tmax,
stc: unsafe {mem::uninitialized()},
};
let stc = ShearingTransformCache::from_ray(&ray);
ray.stc = stc;
ray
}
#[inline]
pub fn from_od(origin: Point3f, dir: Vector3f) -> RawRay {
RawRay::new(origin, dir, float::infinity())
}
#[inline]
pub fn spawn(origin: Point3f, destination: Point3f) -> RawRay {
let dir_unormed = destination - origin;
let tmax = dir_unormed.magnitude();
RawRay::new(origin, dir_unormed/tmax, tmax)
}
#[inline]
fn reset_shearing_transform(&mut self) {
let stc = ShearingTransformCache::from_ray(self);
self.stc = stc;
}
}
impl Default for RawRay {
#[inline]
fn default() -> Self {
RawRay::new(
Point3::new(0.0 as Float, 0.0 as Float, 0.0 as Float),
Vector3::new(0.0 as Float, 0.0 as Float, 1.0 as Float),
float::infinity(),
)
}
}
impl Ray for RawRay {
#[inline]
fn origin(&self) -> Point3f {
self.origin
}
#[inline]
fn set_origin(&mut self, o: Point3f) {
self.origin = o;
self.reset_shearing_transform();
}
#[inline]
fn max_extend(&self) -> Float {
self.tmax
}
#[inline]
fn set_max_extend(&mut self, tmax: Float) {
self.tmax = tmax;
self.reset_shearing_transform();
}
#[inline]
fn direction(&self) -> Vector3f {
self.dir
}
#[inline]
fn set_direction(&mut self, d: Vector3f) {
self.dir = d;
self.reset_shearing_transform();
}
#[inline]
fn apply_transform(&self, t: &Matrix4f) -> RawRay
{
RawRay::new(
t.transform_point(self.origin),
t.transform_vector(self.dir),
self.tmax,
)
}
#[inline]
fn shearing_transform(&self) -> ShearingTransformCache {
self.stc
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct ShearingTransformCache {
perm: Permulation,
pub neg_o: Vector3f,
pub shear: Vector3f,
}
impl fmt::Debug for ShearingTransformCache {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
f.write_str("RST Cache")
}
}
#[derive(Copy, Clone, PartialEq)]
enum Permulation {
XZ,
YZ,
ZZ,
}
impl ShearingTransformCache {
pub fn from_ray<R: ?Sized + Ray>(ray: &R) -> ShearingTransformCache {
let o = ray.origin();
let neg_o = -o.to_vec();
let direction = ray.direction();
let absdx = direction.x.abs();
let absdy = direction.y.abs();
let absdz = direction.z.abs();
let (perm, direction) = if absdx > absdy && absdx > absdz {
(Permulation::XZ, Vector3f::new(
direction.y, direction.z, direction.x
))
} else if absdy > absdz {
(Permulation::YZ, Vector3f::new(
direction.z, direction.x, direction.y
))
} else {
(Permulation::ZZ, direction)
};
let shear = Vector3f::new(
-direction.x / direction.z,
-direction.y / direction.z,
(1.0 as Float) / direction.z);
ShearingTransformCache{
perm: perm,
neg_o: neg_o,
shear: shear,
}
}
pub fn apply(&self, p0: Point3f, p1: Point3f, p2: Point3f) -> (Point3f, Point3f, Point3f) {
let (mut p0t, mut p1t, mut p2t) = self.perm.perm(
p0 + self.neg_o, p1 + self.neg_o, p2 + self.neg_o
);
p0t.x += self.shear.x * p0t.z;
p0t.y += self.shear.y * p0t.z;
p1t.x += self.shear.x * p1t.z;
p1t.y += self.shear.y * p1t.z;
p2t.x += self.shear.x * p2t.z;
p2t.y += self.shear.y * p2t.z;
(p0t, p1t, p2t)
}
}
impl Permulation {
#[inline]
pub fn perm(self, p0t: Point3f, p1t: Point3f, p2t: Point3f) -> (Point3f, Point3f, Point3f) {
match self {
Permulation::XZ => (permxz(p0t), permxz(p1t), permxz(p2t)),
Permulation::YZ => (permyz(p0t), permyz(p1t), permyz(p2t)),
Permulation::ZZ => (p0t, p1t, p2t),
}
}
}
#[inline]
pub fn permxz(p0t: Point3f) -> Point3f {
Point3f::new(p0t.y, p0t.z, p0t.x)
}
#[inline]
pub fn permyz(p0t: Point3f) -> Point3f {
Point3f::new(p0t.z, p0t.x, p0t.y)
}
#[must_use]
#[derive(Clone)]
pub struct RayDifferential {
pub ray: RawRay,
pub diffs: Option<(RawRay, RawRay)>,
}
impl RayDifferential {
pub fn apply_transform(&self, t: &Matrix4f) -> Self
{
let mut diffs = self.diffs;
if let Some(diffs) = diffs.as_mut() {
diffs.0 = diffs.0.apply_transform(t);
diffs.1 = diffs.1.apply_transform(t);
}
RayDifferential{
ray: self.ray.apply_transform(t),
diffs: diffs,
}
}
pub fn scale_differentials(&mut self, s: Float) {
let origin = self.ray.origin();
let dir = self.ray.direction();
if let Some((ref mut rx, ref mut ry)) = self.diffs {
rx.origin = origin + (rx.origin - origin) * s;
ry.origin = origin + (ry.origin - origin) * s;
rx.dir = dir + (rx.dir - dir) * s;
ry.dir = dir + (ry.dir - dir) * s;
}
}
}
impl From<RawRay> for RayDifferential {
fn from(ray: RawRay) -> RayDifferential {
RayDifferential{
ray: ray,
diffs: None,
}
}
}