summaryrefslogtreecommitdiff
path: root/racer-tracer/src/scene/yml.rs
blob: 3e6978666e6ad07dbda6e15b4b5eb640a4be5376 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::{
    collections::HashMap,
    path::{Path, PathBuf},
    sync::Arc,
};

use serde::Deserialize;

use crate::{
    error::TracerError,
    geometry::{sphere::Sphere, Hittable},
    material::{dialectric::Dialectric, lambertian::Lambertian, metal::Metal, SharedMaterial},
    scene::SceneLoader,
    vec3::{Color, Vec3},
};

use config::File;

pub struct YmlLoader {
    path: PathBuf,
}

impl YmlLoader {
    pub fn new(path: PathBuf) -> Self {
        Self { path }
    }
}

impl SceneLoader for YmlLoader {
    fn load(&self) -> Result<Vec<Box<dyn crate::geometry::Hittable>>, TracerError> {
        let datta = SceneData::from_file(PathBuf::from(&self.path))?;
        datta.try_into()
    }
}

#[derive(Debug, Deserialize)]
enum MaterialData {
    Lambertian { color: Color },
    Metal { color: Color, fuzz: f64 },
    Dialectric { refraction_index: f64 },
}

#[derive(Debug, Deserialize)]
enum GeometryData {
    Sphere {
        pos: Vec3,
        radius: f64,
        material: String,
    },
}

#[derive(Deserialize)]
struct SceneData {
    materials: HashMap<String, MaterialData>,
    geometry: Vec<GeometryData>,
}

impl SceneData {
    pub fn from_file<P: AsRef<Path>>(file: P) -> Result<Self, TracerError> {
        config::Config::builder()
            .add_source(File::from(file.as_ref()))
            .build()
            .map_err(|e| {
                TracerError::Configuration(
                    file.as_ref().to_string_lossy().into_owned(),
                    dbg!(e).to_string(),
                )
            })?
            .try_deserialize()
            .map_err(|e| {
                TracerError::Configuration(
                    file.as_ref().to_string_lossy().into_owned(),
                    dbg!(e).to_string(),
                )
            })
    }
}

impl TryInto<Vec<Box<dyn Hittable>>> for SceneData {
    type Error = TracerError;
    fn try_into(self) -> Result<Vec<Box<dyn Hittable>>, TracerError> {
        let mut materials: HashMap<String, SharedMaterial> = HashMap::new();
        self.materials
            .into_iter()
            .for_each(|(id, material)| match material {
                MaterialData::Lambertian { color } => {
                    materials.insert(id, Arc::new(Box::new(Lambertian::new(color))));
                }
                MaterialData::Metal { color, fuzz } => {
                    materials.insert(id, Arc::new(Box::new(Metal::new(color, fuzz))));
                }
                MaterialData::Dialectric { refraction_index } => {
                    materials.insert(id, Arc::new(Box::new(Dialectric::new(refraction_index))));
                }
            });

        let geometry: Vec<Box<dyn Hittable>> = self
            .geometry
            .into_iter()
            .map(|geo| match geo {
                GeometryData::Sphere {
                    pos,
                    radius,
                    material,
                } => materials
                    .get(&material)
                    .ok_or(TracerError::UnknownMaterial(material))
                    .map(|mat| {
                        Box::new(Sphere::new(pos, radius, Arc::clone(mat))) as Box<dyn Hittable>
                    }),
            })
            .collect::<Result<Vec<Box<dyn Hittable>>, TracerError>>()?;

        Ok(geometry)
    }
}