use geometry::prelude::*;
use super::*;
use std::sync::Arc;
use texturing::Texture;
use spectrum::*;
use lighting::{LightFlag, LightSample, LIGHT_AREA, SampleInfo, PathInfo};
use sample;
#[derive(Clone)]
pub struct ShapedPrimitive<S, M> {
pub shape: S,
pub material: M,
pub lighting_profile: Option<Arc<Texture<Texel=RGBSpectrumf>>>,
}
impl<S, M> ShapedPrimitive<S, M>
where S: Shape, M: Material
{
#[inline]
pub fn new(
shape: S, material: M,
lighting_profile: Option<Arc<Texture<Texel=RGBSpectrumf>>>
) -> ShapedPrimitive<S, M> {
ShapedPrimitive{
shape: shape, material: material, lighting_profile: lighting_profile,
}
}
}
impl<S, M> Composable for ShapedPrimitive<S, M>
where S: Shape, M: Material
{
#[inline]
fn bbox_parent(&self) -> BBox3f {
self.shape.bbox_local()
}
#[inline]
fn intersect_ray(&self, ray: &mut RawRay) -> Option<SurfaceInteraction> {
let r = self.shape.intersect_ray(ray);
if let Some((t, mut si)) = r {
ray.set_max_extend(t);
si.set_primitive(self);
Some(si)
} else {
None
}
}
#[inline]
fn can_intersect(&self, ray: &RawRay) -> bool {
self.shape.can_intersect(ray)
}
#[inline]
fn as_light(&self) -> &Light {
self
}
}
impl<S, M> Light for ShapedPrimitive<S, M>
where S: Shape, M: Material
{
#[inline]
fn flags(&self) -> LightFlag {
LIGHT_AREA
}
#[inline]
fn is_delta(&self) -> bool {
false
}
#[inline]
fn evaluate_path(&self, pos: Point3f, dir: Vector3f) -> RGBSpectrumf {
if let Some(ref lp) = self.lighting_profile {
let p = pos + dir;
let ray = RawRay::from_od(p, -dir);
if let Some((_t, si)) = self.shape.intersect_ray(&ray) {
let dxy = DxyInfo::from_duv(&si.duv);
return lp.evaluate(&si, &dxy);
}
}
RGBSpectrumf::black()
}
fn evaluate_sampled(
&self, pos: Point3f, sample: Point2f
) -> LightSample {
let (l_pos, l_norm, l_pdf) = self.shape.sample_wrt(pos, sample);
let mut ret = LightSample{
radiance: RGBSpectrumf::black(),
pdf: l_pdf,
pfrom: l_pos,
pto: pos,
};
if let Some(ref lp) = self.lighting_profile {
let ldir = pos - l_pos;
if ldir.dot(l_norm) > 0. as Float {
let ray = RawRay::from_od(pos, -ldir);
if let Some((_, si)) = self.shape.intersect_ray(&ray) {
let dxy = DxyInfo::from_duv(&si.duv);
ret.radiance = lp.evaluate(&si, &dxy);
}
}
}
ret
}
#[inline]
fn generate_path(&self, samples: SampleInfo) -> PathInfo {
let (pos, norm, pdfpos) = self.shape.sample(samples.pfilm);
let (u, v) = normal::get_basis_from(norm);
let dir = sample::sample_cosw_hemisphere(samples.plens);
let dir = dir.x * u + dir.y * v + dir.z * norm;
PathInfo{
ray: RawRay::from_od(pos, dir),
normal: norm,
pdfpos: pdfpos,
pdfdir: sample::pdf_cosw_hemisphere(dir.z),
radiance: self.evaluate_path(pos, dir),
}
}
#[inline]
fn pdf_path(&self, pos: Point3f, dir: Vector3f, norm: Vector3f) -> (Float, Float) {
(
self.shape.pdf(pos, norm),
sample::pdf_cosw_hemisphere(norm.dot(dir).abs())
)
}
fn pdf(&self, pos: Point3f, wi: Vector3f) -> Float {
self.shape.pdf_wrt(pos, wi)
}
fn power(&self) -> RGBSpectrumf {
if let Some(ref lp) = self.lighting_profile {
debug_assert!(self.shape.surface_area() >= 0. as Float);
lp.mean() * self.shape.surface_area() * float::pi()
} else {
RGBSpectrumf::black()
}
}
}
impl<S, M> Primitive for ShapedPrimitive<S, M>
where S: Shape, M: Material
{
#[inline]
fn get_material(&self) -> &Material {
&self.material
}
#[inline]
fn is_emissive(&self) -> bool {
self.lighting_profile.is_some()
}
}