I'm trying to learn Rust and have been building a raytracer as a beginner project to get my hands dirty with the language. I've built my raytracer following Peter Shirley's Ray Tracing in One Weekend The book is written in C++, I've tried my best to port it to Rust, and admittedly the code is probably not written in a Rust-like manner.
I've got the raytracer working, and I'm currently trying to implement a scene-saving system, where you save a scene as a JSON file, with various information about the scene's camera, objects, etc.
I'm running into a problem when it comes to using serde
to deserialize JSON of a scene so it can be loaded into the program. I serealize in a simple Python script, where the output looks like this, saved to scene.json
:
{
"aspect_ratio": 1.5,
"image_width": 1200,
"image_height": 800,
"samples_per_pixel": 500,
"camera": {
"origin": {
"vec": [
13.0,
2.0,
3.0
]
},
"lower_left_corner": {
"vec": [
3.0112371,
-1.1992972,
2.6938112
]
},
"horizontal": {
"vec": [
1.5556582,
0,
-5.055889
]
},
"vertical": {
"vec": [
-0.49034908,
3.4890225,
-0.15087664
]
},
"u": {
"vec": [
0.29408586,
0,
-0.955779
]
},
"v": {
"vec": [
-0.13904539,
0.9893614,
-0.042783197
]
},
"w": {
"vec": [
0.9456108,
0.14547859,
0.29095718
]
},
"radius": 0.5
},
"world": [
{
"center": {
"vec": [
0,
-1000,
0
]
},
"r": 1000,
"material": {
"type": "lambertian",
"albedo": [
0.5,
0.5,
0.5
]
}
},
{
"center": {
"vec": [
0.3712137,
0.2,
1.5109301
]
},
"r": 0.2,
"material": {
"type": "metal",
"albedo": [
0.66978514,
0.9735459,
0.52093863
],
"fuzziness": 0.045185298
}
},
.
.
.
]
}
Currently there's only sphere's in my raytracer, so the only thing that differs between two Renderable
s in "world"
is "material"
, which I have LambertianMaterial
, Metal
, and Dielectric
defined as so:
pub struct LambertianMaterial {
albedo: Color
}
pub struct Metal {
albedo: Color,
fuzziness: f32
}
pub struct Dielectric {
// index of refraction (')
ir: f32
}
Deseralizing metadata like the "camera"
and "aspect_ratio"
, "image_width
, etc, is perfectly fine. My issue is with the "world"
field, as its a list that contains various spheres that all use the Renderable
trait, which I have defined as:
pub trait Renderable: fmt::Display {
fn hit (&self, ray: &Ray, t_min: f32, t_max: f32) -> (bool, HitRecord);
}
Within the raytracer these spheres are rendered via looping through another struct that contains a vec
of Renderable
s, called RenderableList
, defined as:
pub struct RenderableList {
objects: Vec<Rc<dyn Renderable>>
}
However, trying to deserialize a RenderableList
from JSON using serde is proving difficult. Simply slapping a #[derive(Debug, Deserialize)]
on top of the struct definition obviously doesn't solve it.
I tried reading through this post on serializing Arc<Mutex>, but it's more on the serialization side, and not the deserialization.
I tried reading through the docs for serde about implementing a deserializer, but I think that's more complicated and overkill than what I actually need.
My initial stab at this was to do the following:
#[derive(Deserialize, Debug)]
struct Scene {
aspect_ratio: f32,
image_width: i32,
image_height: i32,
samples_per_pixel: i32,
camera: Camera,
world: RenderableList,
}
fn load_scene<P: AsRef<Path>>(path: P) -> Result<Scene, Box<dyn Error>> {
// Open the file in read-only mode with buffer.
let file = File::open(path)?;
let reader = BufReader::new(file);
// Read the JSON contents of the file as an instance of `Scene`.
let s = serde_json::from_reader(reader)?;
// Return the `Scene`.
Ok(s)
}
But as stated previously, this raises errors when trying to make RenderableList
deserializable with #[derive(Debug, Deserialize)]
. Specifically, I get:
the trait bound `Rc<dyn Renderable>: Deserialize<'_>` is not satisfied
the following other types implement trait `Deserialize<'de>`:
&'a Path
&'a [u8]
&'a str
()
(T0, T1)
(T0, T1, T2)
(T0, T1, T2, T3)
(T0, T1, T2, T3, T4)
and 131 others
required for `Vec<Rc<dyn Renderable>>` to implement `Deserialize<'_>`
Would anyone happen to know how to work around this?