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, parameters: &ParameterDictionary, _loc: &FileLoc, _shape: &Shape, _alpha_tex: &FloatTexture, colorspace: Option<&RGBColorSpace>, arena: &Arena, ) -> Result { 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)) }