108 lines
3.4 KiB
Rust
108 lines
3.4 KiB
Rust
use crate::core::light::lookup_spectrum;
|
|
use crate::core::spectrum::spectrum_to_photometric;
|
|
use crate::core::texture::FloatTexture;
|
|
use crate::{Arena, FileLoc, ParameterDictionary};
|
|
use anyhow::Result;
|
|
use shared::core::geometry::{Frame, Point3f, VectorLike};
|
|
use shared::core::light::{Light, LightBase, LightType};
|
|
use shared::core::medium::{Medium, MediumInterface};
|
|
use shared::core::shape::Shape;
|
|
use shared::core::spectrum::Spectrum;
|
|
use shared::core::texture::SpectrumType;
|
|
use shared::lights::SpotLight;
|
|
use shared::spectra::RGBColorSpace;
|
|
use shared::utils::math::radians;
|
|
use shared::{Float, Ptr, Transform, PI};
|
|
|
|
trait CreateSpotLight {
|
|
fn new(
|
|
render_from_light: Transform,
|
|
_medium_interface: MediumInterface,
|
|
le: Spectrum,
|
|
scale: Float,
|
|
cos_falloff_start: Float,
|
|
total_width: Float,
|
|
arena: &Arena
|
|
) -> Self;
|
|
}
|
|
|
|
impl CreateSpotLight for SpotLight {
|
|
fn new(
|
|
render_from_light: Transform,
|
|
_medium_interface: MediumInterface,
|
|
le: Spectrum,
|
|
scale: Float,
|
|
cos_falloff_start: Float,
|
|
total_width: Float,
|
|
arena: &Arena
|
|
) -> Self {
|
|
let base = LightBase::new(
|
|
LightType::DeltaPosition,
|
|
render_from_light,
|
|
MediumInterface::empty(),
|
|
);
|
|
|
|
let i = lookup_spectrum(&le);
|
|
let iemit = arena.alloc_arc(i);
|
|
Self {
|
|
base,
|
|
iemit,
|
|
scale,
|
|
cos_falloff_end: radians(total_width).cos(),
|
|
cos_falloff_start: radians(cos_falloff_start).cos(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn create(
|
|
render_from_light: Transform,
|
|
medium: Option<Medium>,
|
|
parameters: &ParameterDictionary,
|
|
_loc: &FileLoc,
|
|
_shape: &Shape,
|
|
_alpha_tex: &FloatTexture,
|
|
colorspace: Option<&RGBColorSpace>,
|
|
arena: &Arena,
|
|
) -> Result<Light> {
|
|
let default_cs = crate::spectra::default_colorspace();
|
|
let cs = colorspace.unwrap_or(&default_cs);
|
|
let i = parameters
|
|
.get_one_spectrum(
|
|
"I",
|
|
Some(Spectrum::Dense(cs.illuminant)),
|
|
SpectrumType::Illuminant,
|
|
)
|
|
.expect("No spectrum");
|
|
let mut scale = parameters.get_one_float("scale", 1.)?;
|
|
let coneangle = parameters.get_one_float("coneangle", 30.)?;
|
|
let conedelta = parameters.get_one_float("conedelta", 5.)?;
|
|
let from = parameters.get_one_point3f("from", Point3f::zero())?;
|
|
let to = parameters.get_one_point3f("to", Point3f::new(0., 0., 1.))?;
|
|
let dir_to_z = Transform::from(Frame::from_z((to - from).normalize()));
|
|
let t = Transform::translate(from.into()) * dir_to_z.inverse();
|
|
let final_render = render_from_light * t;
|
|
scale /= spectrum_to_photometric(i);
|
|
|
|
let phi_v = parameters.get_one_float("power", -1.)?;
|
|
if phi_v > 0. {
|
|
let cos_falloff_end = radians(coneangle).cos();
|
|
let cos_falloff_start = radians(coneangle - conedelta).cos();
|
|
let k_e = 2. * PI * ((1. - cos_falloff_start) + (cos_falloff_start - cos_falloff_end) / 2.);
|
|
scale *= phi_v / k_e;
|
|
}
|
|
|
|
let mi = match medium {
|
|
Some(m) => {
|
|
let ptr = arena.alloc(m);
|
|
MediumInterface {
|
|
inside: ptr,
|
|
outside: ptr,
|
|
}
|
|
}
|
|
None => MediumInterface::default(),
|
|
};
|
|
|
|
let specific = SpotLight::new(final_render, mi, i, scale, coneangle, coneangle - conedelta, arena);
|
|
arena.alloc(specific);
|
|
Ok(Light::Spot(specific))
|
|
}
|