215 lines
5.6 KiB
Rust
215 lines
5.6 KiB
Rust
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<Arc<dyn Hittable>>,
|
|
|
|
// 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<Arc<dyn Hittable>>,
|
|
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<Arc<dyn Hittable>> = 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<Arc<dyn Hittable>> {
|
|
&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<MaterialDef>,
|
|
pub objects: Vec<HittableDef>,
|
|
pub filename: String,
|
|
pub image_width: u32,
|
|
pub image_height: u32,
|
|
pub max_depth: u32,
|
|
}
|
|
|
|
impl<'de> Deserialize<'de> for Scene {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
let conc = SceneDef::deserialize(deserializer)?;
|
|
let mats: Vec<Arc<dyn Material>> = conc
|
|
.materials
|
|
.into_iter()
|
|
.map(MaterialDef::into_arc)
|
|
.collect();
|
|
let objs: Vec<Arc<dyn Hittable>> = 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,
|
|
})
|
|
}
|
|
}
|