221 lines
5.4 KiB
Rust
221 lines
5.4 KiB
Rust
use std::fmt::Debug;
|
|
use std::sync::Arc;
|
|
|
|
use log::warn;
|
|
use serde::Deserialize;
|
|
|
|
use crate::{
|
|
camera::Camera,
|
|
objects::{
|
|
cube::Cube,
|
|
materials::{
|
|
dielectric::Dielectric,
|
|
lambertian::{Lambertian, Metal},
|
|
traits::Material,
|
|
},
|
|
quad::Quad,
|
|
sphere::Sphere,
|
|
traits::Hittable,
|
|
triangle::Triangle,
|
|
},
|
|
scenes::raw_camera::RawCamera,
|
|
vec3::Vec3,
|
|
};
|
|
|
|
pub struct Scene {
|
|
pub camera: Camera,
|
|
pub materials: Vec<Arc<dyn Material>>,
|
|
pub objects: Vec<Arc<dyn Hittable>>,
|
|
}
|
|
|
|
impl Debug for Scene {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.debug_struct("Scene")
|
|
.field("camera", &self.camera)
|
|
.field("materials", &self.materials)
|
|
.field("objects", &self.objects)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl Scene {
|
|
pub fn new(image_width: u32, image_height: u32) -> Self {
|
|
Self {
|
|
camera: Camera::new(image_width, image_height),
|
|
materials: vec![],
|
|
objects: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn camera(&mut self) -> &mut Camera {
|
|
&mut self.camera
|
|
}
|
|
|
|
pub fn add_hittable(&mut self, hittable: Arc<dyn Hittable>) {
|
|
self.objects.push(hittable);
|
|
}
|
|
|
|
pub fn render(&mut self) {
|
|
self.camera.render(&self.objects);
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct SceneDef {
|
|
pub camera: RawCamera,
|
|
pub materials: Vec<MaterialDef>,
|
|
pub objects: Vec<HittableDef>,
|
|
}
|
|
|
|
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),
|
|
materials: mats,
|
|
objects: objs,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(untagged)]
|
|
enum RawMaterial {
|
|
Ref(u32),
|
|
Direct(MaterialDef),
|
|
}
|
|
|
|
impl RawMaterial {
|
|
pub fn into_arc(self, materials: &Vec<Arc<dyn Material>>) -> Option<Arc<dyn Material>> {
|
|
match self {
|
|
RawMaterial::Ref(r) => {
|
|
if r as usize >= materials.len() {
|
|
warn!("Sphere specified nonexistent material {}; skipping...", r);
|
|
return None;
|
|
}
|
|
Some(materials.get(r as usize).unwrap().clone())
|
|
}
|
|
RawMaterial::Direct(m) => Some(m.into_arc()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct RawSphere {
|
|
pub center: Vec3,
|
|
pub radius: f32,
|
|
pub material: RawMaterial,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct RawTriangle {
|
|
pub p1: Vec3,
|
|
pub p2: Vec3,
|
|
pub p3: Vec3,
|
|
pub material: RawMaterial,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct RawQuad {
|
|
pub p1: Vec3,
|
|
pub p2: Vec3,
|
|
pub p3: Vec3,
|
|
pub p4: Vec3,
|
|
pub material: RawMaterial,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct RawCube {
|
|
pub p1: Vec3,
|
|
pub p2: Vec3,
|
|
pub p3: Vec3,
|
|
pub p4: Vec3,
|
|
pub p5: Vec3,
|
|
pub p6: Vec3,
|
|
pub p7: Vec3,
|
|
pub p8: Vec3,
|
|
pub material: RawMaterial,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(tag = "type", rename_all = "lowercase")]
|
|
enum HittableDef {
|
|
Sphere(RawSphere),
|
|
Triangle(RawTriangle),
|
|
Quad(RawQuad),
|
|
Cube(RawCube),
|
|
}
|
|
|
|
impl HittableDef {
|
|
fn into_arc(self, materials: &Vec<Arc<dyn Material>>) -> Option<Arc<dyn Hittable>> {
|
|
// THOUGHT: i think this can be done better; in the map/filter call up there?
|
|
match self {
|
|
HittableDef::Sphere(s) => {
|
|
let material = s.material.into_arc(materials);
|
|
if let Some(m) = material {
|
|
Some(Arc::new(Sphere::new(s.center, s.radius, m)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
HittableDef::Triangle(t) => {
|
|
let material = t.material.into_arc(materials);
|
|
if let Some(m) = material {
|
|
Some(Arc::new(Triangle::new(t.p1, t.p2, t.p3, m)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
HittableDef::Quad(q) => {
|
|
let material = q.material.into_arc(materials);
|
|
if let Some(m) = material {
|
|
Some(Arc::new(Quad::new(q.p1, q.p2, q.p3, q.p4, m)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
HittableDef::Cube(c) => {
|
|
let material = c.material.into_arc(materials);
|
|
if let Some(m) = material {
|
|
Some(Arc::new(Cube::new(
|
|
c.p1, c.p2, c.p3, c.p4, c.p5, c.p6, c.p7, c.p8, m,
|
|
)))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
#[serde(tag = "type", rename_all = "lowercase")]
|
|
enum MaterialDef {
|
|
Lambertian(Lambertian),
|
|
Metal(Metal),
|
|
Dielectric(Dielectric),
|
|
}
|
|
|
|
impl MaterialDef {
|
|
fn into_arc(self) -> Arc<dyn Material> {
|
|
match self {
|
|
MaterialDef::Lambertian(l) => Arc::new(l),
|
|
MaterialDef::Metal(m) => Arc::new(m),
|
|
MaterialDef::Dielectric(d) => Arc::new(d),
|
|
}
|
|
}
|
|
}
|