use crate::core::image::{Image, ImageIO, ImageMetadata}; use crate::core::light::{CreateLight, lookup_spectrum}; use crate::core::spectrum::spectrum_to_photometric; use crate::core::texture::FloatTexture; use crate::utils::sampling::PiecewiseConstant2D; use crate::utils::{Arena, FileLoc, ParameterDictionary, resolve_filename}; use shared::core::image::ImageBase; use shared::core::light::{Light, LightBase, LightType}; use shared::core::medium::{Medium, MediumInterface}; use shared::core::spectrum::Spectrum; use shared::core::texture::SpectrumType; use shared::lights::GoniometricLight; use shared::spectra::RGBColorSpace; use shared::utils::Transform; use shared::utils::containers::Array2D; impl CreateLight for GoniometricLight { fn new( render_from_light: Transform, medium_interface: MediumInterface, le: Spectrum, scale: shared::Float, shape: Option>, alpha: Option>, image: Option>, image_color_space: Option>, two_sided: Option, fov: Option, ) -> Self { let base = LightBase::new( LightType::DeltaPosition, render_from_light, medium_interface, ); let iemit = lookup_spectrum(le); let d = image.unwrap().get_sampling_distribution_uniform(); let distrib = PiecewiseConstant2D::new_with_data(d); Self { base, iemit, scale, image, distrib, } } fn create( arena: &mut Arena, render_from_light: Transform, medium: Medium, params: &ParameterDictionary, loc: &FileLoc, shape: &Shape, alpha_text: &FloatTexture, colorspace: Option<&RGBColorSpace>, ) -> Light { let i = params.get_one_spectrum( "I", colorspace.unwrap().illuminant, SpectrumType::Illuminant, ); let lemit_data = lookup_spectrum(&i_spectrum_def); let sc = params.get_one_float("scale", 1.); let filename = resolve_filename(params.get_one_string("filename", "")); let mut image: Option = None; if filename.is_empty() { println!( "{}: Both \"L\" and \"filename\" specified for DiffuseAreaLight.", loc ) } else { let im = Image::read(filename, None).expect("Could not load image"); let loaded_img: Image = im.image; let res = loaded_img.resolution(); let metadata: ImageMetadata = im.metadata; if loaded_img.has_any_infinite_pixels() { panic!( "{:?}: Image '{}' has NaN pixels. Not suitable for light.", loc, filename ); } if res.x() != res.y() { panic!( "{:?}: Image resolution ({}, {}) is non square. Unlikely that this is an equal area map.", loc, res.x(), res.y(), y() ); } let rgb_desc = loaded_img.get_channel_desc(&["R", "G", "B"]); let y_desc = loaded_img.get_channel_desc(&["Y"]); if let Ok(rgb) = rgb_desc { if y_desc.is_ok() { panic!( "{:?}: Image '{}' has both RGB and Y channels. Ambiguous.", loc, filename ); } let mut y_pixels = Vec::with_capacity((res.x * res.y) as usize); for y in 0..res.y { for x in 0..res.x { let r = loaded_img.get_channel(Point2i::new(x, y), 0); let g = loaded_img.get_channel(Point2i::new(x, y), 1); let b = loaded_img.get_channel(Point2i::new(x, y), 2); y_pixels.push((r + g + b) / 3.0); } } loaded_img = Some(Image::new( PixelFormat::F32, res, &["Y"], ColorEncoding::Linear, )); } else if y_desc.is_ok() { image = Some(loaded_img); } else { panic!( "{:?}: Image '{}' has neither RGB nor Y channels.", loc, filename ); } } scale /= spectrum_to_photometric(&lemit_data); let phi_v = params.get_one_float("power", -1.0); if phi_v > 0.0 { if let Some(ref img) = image { let mut sum_y = 0.0; let res = img.resolution(); for y in 0..res.y { for x in 0..res.x { sum_y += img.get_channel(Point2i::new(x, y), 0); } } let k_e = 4.0 * PI * sum_y / (res.x * res.y) as Float; scale *= phi_v / k_e; } } let swap_yz: [Float; 16] = [ 1., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0., 0., 1., ]; let t = Transform::from_flat(swap_yz); let final_render_from_light = render_from_light * t; let d: Array2D = image.get_sampling_distribution(); distrib = PiecewiseConstant2D::new_with_data(d); let specific = GoniometricLight::new( render_from_light, medium_interface, le, scale, None, None, Some(image), None, None, None, ); Light::Goniometric(specific) } }