use std::sync::Arc; use rand::RngExt; use serde::Deserialize; use crate::{ camera::Camera, objects::{ materials::{ dielectric::Dielectric, lambertian::{Lambertian, Metal}, traits::Material, }, sphere::Sphere, traits::Hittable, }, scenes::{hittable_def::HittableDef, material_def::MaterialDef, raw_camera::RawCamera}, vec3::Vec3, }; #[derive(Debug)] pub struct Scene { camera: Camera, objects: Vec>, // image filename: String, image_width: u32, image_height: u32, // raytracing // TODO: think about organisation of these vars, also in Camera max_depth: u32, } impl Scene { pub fn new( camera: Camera, objects: Vec>, filename: String, image_width: u32, image_height: u32, max_depth: u32, ) -> Self { Self { camera, objects, filename, image_width, image_height, max_depth, } } pub fn random() -> Self { let ground = Lambertian::rgb(-2.5, 0.5, 0.5, 1.); let mut world: Vec> = vec![Arc::new(Sphere::xyz( 0., -1000., 0., 1000., Arc::new(ground), ))]; let mut rng = rand::rng(); let point = Vec3::new(4., 0.2, 0.); for a in -11..11 { for b in -11..11 { let mat = rng.random_range((0.)..1.); let center = Vec3::new( a as f32 + 0.9 * rng.random_range((0.)..1.), 0.2, b as f32 + 0.9 * rng.random_range((0.)..1.), ); if (center - point).length() > 0.9 { if mat < 0.8 { // diffuse world.push(Arc::new(Sphere::new( center, 0.2, Arc::new(Lambertian::new(Vec3::random() * Vec3::random(), 1.)), ))); } else if mat < 0.95 { // metal world.push(Arc::new(Sphere::new( center, 0.2, Arc::new(Metal::rgb( rng.random_range(0.5..1.), rng.random_range(0.5..1.), rng.random_range(0.5..1.), 1., rng.random_range((0.)..0.5), )), ))); } else { // glass world.push(Arc::new(Sphere::new( center, 0.2, Arc::new(Dielectric::new(1.5)), ))); } } } } world.push(Arc::new(Sphere::xyz( 0., 1., 0., 1., Arc::new(Dielectric::new(1.5)), ))); world.push(Arc::new(Sphere::xyz( -4., 1., 0., 1., Arc::new(Lambertian::rgb(0.4, 0.2, 0.1, 1.)), ))); world.push(Arc::new(Sphere::xyz( 4., 1., 0., 1., Arc::new(Metal::rgb(0.7, 0.6, 0.5, 1., 0.)), ))); let mut c = Camera::new(); c.set_fov(20.); c.set_anti_alias_rate(2); c.set_vup(Vec3::new(0., 1., 0.)); c.set_look_from(Vec3::new(13., 2., 3.)); c.set_look_at(Vec3::new(0., 0., 0.)); c.add_defocus_blur(0.6, 10.); Self { camera: c, objects: world, filename: "random.png".to_string(), image_width: 1920, image_height: 1080, max_depth: 50, } } pub fn get_camera(&self) -> &Camera { &self.camera } pub fn get_image_width(&self) -> u32 { self.image_width } pub fn get_image_height(&self) -> u32 { self.image_height } pub fn get_objects(&self) -> &Vec> { &self.objects } pub fn get_max_depth(&self) -> u32 { self.max_depth } pub fn get_filename(&self) -> &String { &self.filename } pub fn init(&mut self) { self.camera .init(self.get_image_width(), self.get_image_height()); } } #[derive(Deserialize)] struct SceneDef { pub camera: RawCamera, pub materials: Vec, pub objects: Vec, pub filename: String, pub image_width: u32, pub image_height: u32, pub max_depth: u32, } impl<'de> Deserialize<'de> for Scene { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let conc = SceneDef::deserialize(deserializer)?; let mats: Vec> = conc .materials .into_iter() .map(MaterialDef::into_arc) .collect(); let objs: Vec> = conc .objects .into_iter() .filter_map(|h| h.into_arc(&mats)) .collect(); Ok(Self { camera: Camera::from(conc.camera), objects: objs, filename: conc.filename, image_width: conc.image_width, image_height: conc.image_height, max_depth: conc.max_depth, }) } }