use super::*;
use cgmath::Quaternion;
use sample;
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct PointLight {
pub posw: Point3f,
pub intensity: RGBSpectrumf,
}
impl PointLight {
pub fn new(pos: Point3f, intensity: RGBSpectrumf) -> PointLight {
PointLight{ posw: pos, intensity: intensity,}
}
}
impl Light for PointLight {
#[inline]
fn flags(&self) -> LightFlag {
LIGHT_DPOS
}
#[inline]
fn is_delta(&self) -> bool {
true
}
#[inline]
fn evaluate_sampled(&self, pos: Point3f, _sample: Point2f) -> LightSample {
let pfrom = self.posw;
let pto = pos;
let radiance = self.intensity/(pto-pfrom).magnitude2();
LightSample {
radiance: radiance,
pdf: 1.0 as Float,
pto: pto,
pfrom: pfrom,
}
}
#[inline]
fn generate_path(&self, samples: SampleInfo) -> PathInfo {
let dir = sample::sample_uniform_sphere(samples.pfilm);
let ray = RawRay::from_od(self.posw, dir);
PathInfo{
ray: ray,
normal: dir,
pdfpos: 1. as Float,
pdfdir: sample::pdf_uniform_sphere(),
radiance: self.intensity,
}
}
#[inline]
fn pdf_path(&self, _pos: Point3f, _dir: Vector3f, _normal: Vector3f) -> (Float, Float) {
(0. as Float, sample::pdf_uniform_sphere())
}
fn power(&self) -> RGBSpectrumf {
self.intensity * (float::pi() * 4.0 as Float)
}
}
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
pub struct SpotLight {
posw: Point3f,
pub intensity: RGBSpectrumf,
cost: Float,
cosf: Float,
local_parent: Matrix4f,
parent_local: Matrix4f,
}
impl SpotLight {
#[inline]
pub fn new(pos: Point3f, towards: Vector3f, intensity: RGBSpectrumf, total_angle: Float, start_falloff_angle: Float) -> SpotLight {
assert!(total_angle>start_falloff_angle);
assert!(start_falloff_angle> 0.0 as Float);
assert!(total_angle < (float::pi() * 2.0 as Float));
let towards = towards.normalize();
let rotation: Matrix4f = Quaternion::from_arc(towards, Vector3f::new(0.0 as Float, 0.0 as Float, 1.0 as Float), None).into();
let translation = Matrix4f::from_translation(pos - Point3f::new(0.0 as Float, 0.0 as Float, 0.0 as Float));
let parent_local = rotation * translation;
let local_parent = parent_local.invert().expect("invalid inversion");
SpotLight{
posw: pos,
intensity: intensity,
cost: total_angle.cos(),
cosf: start_falloff_angle.cos(),
local_parent: local_parent,
parent_local: parent_local,
}
}
#[inline]
pub fn set_angles(&mut self, total_angle: Float, start_falloff_angle: Float) {
assert!(total_angle>start_falloff_angle);
assert!(start_falloff_angle> 0.0 as Float);
assert!(total_angle < (float::pi() * 2.0 as Float));
self.cost = total_angle.cos();
self.cosf = start_falloff_angle.cos();
}
#[inline]
pub fn set_orientations(&mut self, pos: Point3f, towards: Vector3f) {
let towards = towards.normalize();
let rotation: Matrix4f = Quaternion::from_arc(towards, Vector3f::new(0.0 as Float, 0.0 as Float, 1.0 as Float), None).into();
let translation = Matrix4f::from_translation(pos - Point3f::new(0.0 as Float, 0.0 as Float, 0.0 as Float));
self.parent_local = rotation * translation;
self.local_parent = self.parent_local.invert().expect("invalid inversion");
self.posw = pos;
}
#[inline]
fn falloff(&self, dir: Vector3f) -> Float {
let cos_theta = self.parent_local.transform_vector(dir).z;
if cos_theta < self.cost {
0.0 as Float
} else if cos_theta > self.cosf {
1.0 as Float
} else {
let delta = (cos_theta - self.cost) / (self.cosf - self.cost);
let delta2 = delta * delta;
delta2 * delta2
}
}
}
impl Light for SpotLight {
#[inline]
fn flags(&self) -> LightFlag {
LIGHT_DPOS
}
#[inline]
fn is_delta(&self) -> bool {
true
}
#[inline]
fn evaluate_sampled(&self, pos: Point3f, _sample: Point2f) -> LightSample {
let pfrom = self.posw;
let pto = pos;
let dir = pto - pfrom;
let mag2 = dir.magnitude2();
let radiance = self.intensity * self.falloff(dir/mag2.sqrt())/mag2;
LightSample {
radiance: radiance,
pdf: 1.0 as Float,
pto: pto,
pfrom: pfrom,
}
}
#[inline]
fn generate_path(&self, samples: SampleInfo) -> PathInfo {
let dir = sample::sample_uniform_cone(samples.pfilm, self.cost);
let ray = RawRay::from_od(self.posw, dir);
PathInfo{
ray: ray,
normal: dir,
pdfpos: 1. as Float,
pdfdir: sample::pdf_uniform_cone(self.cost),
radiance: self.intensity * self.falloff(dir),
}
}
#[inline]
fn pdf_path(&self, _pos: Point3f, dir: Vector3f, _normal: Vector3f) -> (Float, Float) {
let costheta = normal::cos_theta(dir);
let pdfdir = if costheta >= self.cost {
sample::pdf_uniform_cone(self.cost)
} else {
0. as Float
};
(0. as Float, pdfdir)
}
#[inline]
fn power(&self) -> RGBSpectrumf {
self.intensity * (float::pi() * 2.0 as Float) * (
1.0 as Float - 0.5 as Float * (self.cosf - self.cost)
)
}
}