rtiow: add bones of a scene file format based on toml.

This commit is contained in:
Bill Thiede 2023-02-15 14:40:25 -08:00
parent 9353ff675e
commit 37137ac9ca
8 changed files with 152 additions and 8 deletions

View File

@ -18,6 +18,7 @@ fn random_in_unit_disk() -> Vec3 {
} }
} }
#[derive(Debug)]
pub struct Camera { pub struct Camera {
origin: Vec3, origin: Vec3,
lower_left_corner: Vec3, lower_left_corner: Vec3,

View File

@ -16,6 +16,7 @@ pub mod material;
pub mod moving_sphere; pub mod moving_sphere;
pub mod noise; pub mod noise;
pub mod output; pub mod output;
pub mod parser;
pub mod ray; pub mod ray;
pub mod rect; pub mod rect;
pub mod renderer; pub mod renderer;

View File

@ -0,0 +1,94 @@
use crate::{
camera::Camera,
hitable::Hit,
hitable_list::HitableList,
material::Lambertian,
renderer::Scene,
sphere::Sphere,
texture::{EnvMap, Texture},
};
use serde::Deserialize;
use vec3::Vec3;
#[derive(Debug, Deserialize)]
pub struct Config {
scene: SceneConfig,
camera: CameraConfig,
spheres: Vec<SphereConfig>,
}
impl From<Config> for Scene {
fn from(c: Config) -> Scene {
let material = Lambertian::new([1., 0., 0.]);
let objects: Vec<Box<dyn Hit>> = c
.spheres
.iter()
.map(|sc| -> Box<dyn Hit> {
Box::new(Sphere::new(sc.center, sc.radius, material.clone()))
})
.collect();
let world: Box<dyn Hit> = Box::new(HitableList::new(objects));
let env_map: Option<EnvMap> = None;
let lookfrom = Vec3::new(0., 80., 80.);
let lookat = Vec3::new(0., 0., 0.);
let dist_to_focus = 10.0;
let aperture = 0.0;
let time_min = 0.;
let time_max = 1.;
let camera = Camera::new(
lookfrom,
lookat,
Vec3::new(0., 1., 0.),
45.,
c.scene.width as f32 / c.scene.height as f32,
aperture,
dist_to_focus,
time_min,
time_max,
);
let scene = Scene {
world,
camera,
env_map,
subsamples: c.scene.subsamples.unwrap_or(8),
adaptive_subsampling: c.scene.adaptive_subsampling,
num_threads: c.scene.num_threads,
width: c.scene.width,
height: c.scene.height,
global_illumination: c.scene.global_illumination.unwrap_or(true),
};
dbg!(&scene);
scene
}
}
#[derive(Debug, Deserialize)]
struct SceneConfig {
subsamples: Option<usize>,
adaptive_subsampling: Option<f32>,
num_threads: Option<usize>,
width: usize,
height: usize,
global_illumination: Option<bool>,
}
#[derive(Debug, Deserialize)]
struct SphereConfig {
center: [f32; 3],
radius: f32,
}
#[derive(Debug, Deserialize)]
pub struct CameraConfig {
lookfrom: [f32; 3],
lookat: [f32; 3],
fov: f32,
aspect: f32,
aperture: f32,
focus_dist: f32,
time_min: f32,
time_max: f32,
}

View File

@ -99,6 +99,9 @@ pub struct Opt {
/// Select scene to render. /// Select scene to render.
#[structopt(long = "model")] #[structopt(long = "model")]
pub model: Option<Model>, pub model: Option<Model>,
/// Toml config describing scene.
#[structopt(long = "config")]
pub config: Option<PathBuf>,
/// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof /// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof
#[structopt(long = "pprof", parse(from_os_str))] #[structopt(long = "pprof", parse(from_os_str))]
pub pprof: Option<PathBuf>, pub pprof: Option<PathBuf>,
@ -126,7 +129,7 @@ pub fn opt_hash(opt: &Opt) -> String {
} }
// TODO(wathiede): implement the skips and then the renderer could use json as an input file type. // TODO(wathiede): implement the skips and then the renderer could use json as an input file type.
#[derive(Serialize)] #[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct Scene { pub struct Scene {
#[serde(skip)] #[serde(skip)]

View File

@ -53,7 +53,6 @@ pub fn new(opt: &Opt) -> Scene {
let stl_cube = STL::parse( let stl_cube = STL::parse(
BufReader::new(Cursor::new(include_bytes!("../../stls/dragon.stl"))), BufReader::new(Cursor::new(include_bytes!("../../stls/dragon.stl"))),
//BufReader::new(Cursor::new(include_bytes!("../../stls/cube.stl"))),
false, false,
) )
.expect("failed to parse cube"); .expect("failed to parse cube");

View File

@ -7,8 +7,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
anyhow = "1.0.69"
log = "0.4.17" log = "0.4.17"
renderer = { path = "../renderer" } renderer = { path = "../renderer" }
stderrlog = "0.4.3" stderrlog = "0.4.3"
structopt = "0.2.18" structopt = "0.2.18"
strum = "0.24.1" strum = "0.24.1"
toml = "0.7.2"

View File

@ -0,0 +1,24 @@
[scene]
width = 768
height = 768
subsamples = 1000
[camera]
lookfrom = [0.0, 10.0, 0.0]
lookat = [0.0, 0.0, 0.0]
fov = 45.0
aspect = 1
aperture = 0.0
focus_dist = 10.0
time_min = 0.0
time_max = 1.0
[[spheres]]
center = [0.0, 0.0, 0.0]
radius = 10
[[spheres]]
center = [30.0, 0.0, 0.0]
radius = 10
[[spheres]]
center = [-30.0, 0.0, 0.0]
radius = 10

View File

@ -1,12 +1,16 @@
#![warn(unused_extern_crates)] #![warn(unused_extern_crates)]
use std::{fs, time::Instant}; use std::{fs, time::Instant};
use anyhow::Result;
#[cfg(feature = "profile")] #[cfg(feature = "profile")]
use cpuprofiler::PROFILER; use cpuprofiler::PROFILER;
use log::info; use log::info;
use structopt::StructOpt; use structopt::StructOpt;
use renderer::renderer::{render, Model, Opt}; use renderer::{
parser::Config,
renderer::{render, Model, Opt},
};
use strum::VariantNames; use strum::VariantNames;
#[cfg(not(feature = "profile"))] #[cfg(not(feature = "profile"))]
@ -35,7 +39,7 @@ impl MockProfiler {
#[cfg(not(feature = "profile"))] #[cfg(not(feature = "profile"))]
static PROFILER: MockProfiler = MockProfiler {}; static PROFILER: MockProfiler = MockProfiler {};
fn main() -> Result<(), std::io::Error> { fn main() -> Result<()> {
let start_time = Instant::now(); let start_time = Instant::now();
stderrlog::new() stderrlog::new()
.verbosity(3) .verbosity(3)
@ -43,12 +47,28 @@ fn main() -> Result<(), std::io::Error> {
.init() .init()
.unwrap(); .unwrap();
let opt = Opt::from_args(); let opt = Opt::from_args();
if opt.model.is_none() { if opt.model.is_none() && opt.config.is_none() {
eprintln!("--model should be one of {:?}", Model::VARIANTS); eprintln!(
"--config <path> or --model should be one of {:?}",
Model::VARIANTS
);
return Ok(());
}
if opt.model.is_some() && opt.config.is_some() {
eprintln!("only specify one of --config or --model");
return Ok(()); return Ok(());
} }
info!("{:#?}", opt); info!("{:#?}", opt);
let scene = opt.model.as_ref().unwrap().scene(&opt); let scene = match (&opt.model, &opt.config) {
(Some(model), None) => model.scene(&opt),
(None, Some(config)) => {
let s = std::fs::read_to_string(config)?;
let cfg: Config = toml::from_str(&s)?;
println!("{:#?}", cfg);
cfg.into()
}
_ => unreachable!(),
};
fs::create_dir_all(&opt.output)?; fs::create_dir_all(&opt.output)?;
if opt.pprof.is_some() && !cfg!(feature = "profile") { if opt.pprof.is_some() && !cfg!(feature = "profile") {
panic!("profiling disabled at compile time, but -pprof specified"); panic!("profiling disabled at compile time, but -pprof specified");
@ -68,5 +88,5 @@ fn main() -> Result<(), std::io::Error> {
let time_diff = Instant::now() - start_time; let time_diff = Instant::now() - start_time;
info!("Total runtime {} seconds", time_diff.as_secs_f32()); info!("Total runtime {} seconds", time_diff.as_secs_f32());
res Ok(res?)
} }