WIP refactor for new scene data layout.
All rendering code is commented out for now. It will need to be largely rewritten after this refactor is done, most likely. This commit gets the basic new scene parsing working, with the basic new data structures.
This commit is contained in:
parent
7f9f576093
commit
2998bbe7ce
148
src/main.rs
148
src/main.rs
|
@ -31,13 +31,13 @@ mod math;
|
|||
mod mis;
|
||||
mod parse;
|
||||
mod ray;
|
||||
mod renderer;
|
||||
// mod renderer;
|
||||
mod sampling;
|
||||
mod scene;
|
||||
mod shading;
|
||||
mod surface;
|
||||
mod timer;
|
||||
mod tracer;
|
||||
// mod tracer;
|
||||
mod transform_stack;
|
||||
|
||||
use std::{fs::File, io, io::Read, mem, path::Path, str::FromStr};
|
||||
|
@ -51,7 +51,7 @@ use crate::{
|
|||
accel::BVH4Node,
|
||||
bbox::BBox,
|
||||
parse::{parse_scene, DataTree},
|
||||
renderer::LightPath,
|
||||
// renderer::LightPath,
|
||||
surface::SurfaceIntersection,
|
||||
timer::Timer,
|
||||
};
|
||||
|
@ -163,7 +163,7 @@ fn main() {
|
|||
"SurfaceIntersection size: {} bytes",
|
||||
mem::size_of::<SurfaceIntersection>()
|
||||
);
|
||||
println!("LightPath size: {} bytes", mem::size_of::<LightPath>());
|
||||
// println!("LightPath size: {} bytes", mem::size_of::<LightPath>());
|
||||
println!("BBox size: {} bytes", mem::size_of::<BBox>());
|
||||
// println!("BVHNode size: {} bytes", mem::size_of::<BVHNode>());
|
||||
println!("BVH4Node size: {} bytes", mem::size_of::<BVH4Node>());
|
||||
|
@ -247,7 +247,7 @@ fn main() {
|
|||
}
|
||||
|
||||
let arena = Arena::new().with_block_size((1 << 20) * 4);
|
||||
let mut r = parse_scene(&arena, child).unwrap_or_else(|e| {
|
||||
let mut scene = parse_scene(&arena, child).unwrap_or_else(|e| {
|
||||
e.print(&psy_contents);
|
||||
panic!("Parse error.");
|
||||
});
|
||||
|
@ -256,7 +256,7 @@ fn main() {
|
|||
if !args.is_present("serialized_output") {
|
||||
println!("\tOverriding scene spp: {}", spp);
|
||||
}
|
||||
r.spp = usize::from_str(spp).unwrap();
|
||||
// r.spp = usize::from_str(spp).unwrap();
|
||||
}
|
||||
|
||||
let max_samples_per_bucket =
|
||||
|
@ -279,78 +279,82 @@ fn main() {
|
|||
if !args.is_present("serialized_output") {
|
||||
println!("Rendering scene with {} threads...", thread_count);
|
||||
}
|
||||
let (mut image, rstats) = r.render(
|
||||
max_samples_per_bucket,
|
||||
crop,
|
||||
thread_count,
|
||||
args.is_present("serialized_output"),
|
||||
);
|
||||
// Print render stats
|
||||
if !args.is_present("serialized_output") {
|
||||
let rtime = t.tick();
|
||||
let ntime = rtime as f64 / rstats.total_time;
|
||||
println!("\tRendered scene in {:.3}s", rtime);
|
||||
println!(
|
||||
"\t\tTrace: {:.3}s",
|
||||
ntime * rstats.trace_time
|
||||
);
|
||||
println!("\t\t\tRays traced: {}", rstats.ray_count);
|
||||
println!(
|
||||
"\t\t\tRays/sec: {}",
|
||||
(rstats.ray_count as f64 / (ntime * rstats.trace_time) as f64) as u64
|
||||
);
|
||||
println!("\t\t\tRay/node tests: {}", rstats.accel_node_visits);
|
||||
println!(
|
||||
"\t\tInitial ray generation: {:.3}s",
|
||||
ntime * rstats.initial_ray_generation_time
|
||||
);
|
||||
println!(
|
||||
"\t\tRay generation: {:.3}s",
|
||||
ntime * rstats.ray_generation_time
|
||||
);
|
||||
println!(
|
||||
"\t\tSample writing: {:.3}s",
|
||||
ntime * rstats.sample_writing_time
|
||||
);
|
||||
}
|
||||
|
||||
// Write to disk
|
||||
if !args.is_present("serialized_output") {
|
||||
println!("Writing image to disk into '{}'...", r.output_file);
|
||||
if r.output_file.ends_with(".png") {
|
||||
image
|
||||
.write_png(Path::new(&r.output_file))
|
||||
.expect("Failed to write png...");
|
||||
} else if r.output_file.ends_with(".exr") {
|
||||
image.write_exr(Path::new(&r.output_file));
|
||||
} else {
|
||||
panic!("Unknown output file extension.");
|
||||
}
|
||||
println!("\tWrote image in {:.3}s", t.tick());
|
||||
}
|
||||
println!("{:#?}", scene);
|
||||
|
||||
// Print memory stats if stats are wanted.
|
||||
if args.is_present("stats") {
|
||||
// let arena_stats = arena.stats();
|
||||
// let mib_occupied = arena_stats.0 as f64 / 1_048_576.0;
|
||||
// let mib_allocated = arena_stats.1 as f64 / 1_048_576.0;
|
||||
println!("Didn't really render, because all that code is disabled! Done!");
|
||||
// let (mut image, rstats) = r.render(
|
||||
// max_samples_per_bucket,
|
||||
// crop,
|
||||
// thread_count,
|
||||
// args.is_present("serialized_output"),
|
||||
// );
|
||||
// // Print render stats
|
||||
// if !args.is_present("serialized_output") {
|
||||
// let rtime = t.tick();
|
||||
// let ntime = rtime as f64 / rstats.total_time;
|
||||
// println!("\tRendered scene in {:.3}s", rtime);
|
||||
// println!(
|
||||
// "\t\tTrace: {:.3}s",
|
||||
// ntime * rstats.trace_time
|
||||
// );
|
||||
// println!("\t\t\tRays traced: {}", rstats.ray_count);
|
||||
// println!(
|
||||
// "\t\t\tRays/sec: {}",
|
||||
// (rstats.ray_count as f64 / (ntime * rstats.trace_time) as f64) as u64
|
||||
// );
|
||||
// println!("\t\t\tRay/node tests: {}", rstats.accel_node_visits);
|
||||
// println!(
|
||||
// "\t\tInitial ray generation: {:.3}s",
|
||||
// ntime * rstats.initial_ray_generation_time
|
||||
// );
|
||||
// println!(
|
||||
// "\t\tRay generation: {:.3}s",
|
||||
// ntime * rstats.ray_generation_time
|
||||
// );
|
||||
// println!(
|
||||
// "\t\tSample writing: {:.3}s",
|
||||
// ntime * rstats.sample_writing_time
|
||||
// );
|
||||
// }
|
||||
|
||||
// println!("MemArena stats:");
|
||||
// // Write to disk
|
||||
// if !args.is_present("serialized_output") {
|
||||
// println!("Writing image to disk into '{}'...", r.output_file);
|
||||
// if r.output_file.ends_with(".png") {
|
||||
// image
|
||||
// .write_png(Path::new(&r.output_file))
|
||||
// .expect("Failed to write png...");
|
||||
// } else if r.output_file.ends_with(".exr") {
|
||||
// image.write_exr(Path::new(&r.output_file));
|
||||
// } else {
|
||||
// panic!("Unknown output file extension.");
|
||||
// }
|
||||
// println!("\tWrote image in {:.3}s", t.tick());
|
||||
// }
|
||||
|
||||
// if mib_occupied >= 1.0 {
|
||||
// println!("\tOccupied: {:.1} MiB", mib_occupied);
|
||||
// } else {
|
||||
// println!("\tOccupied: {:.4} MiB", mib_occupied);
|
||||
// }
|
||||
// // Print memory stats if stats are wanted.
|
||||
// if args.is_present("stats") {
|
||||
// let arena_stats = arena.stats();
|
||||
// let mib_occupied = arena_stats.0 as f64 / 1_048_576.0;
|
||||
// let mib_allocated = arena_stats.1 as f64 / 1_048_576.0;
|
||||
|
||||
// if mib_allocated >= 1.0 {
|
||||
// println!("\tUsed: {:.1} MiB", mib_allocated);
|
||||
// } else {
|
||||
// println!("\tUsed: {:.4} MiB", mib_allocated);
|
||||
// }
|
||||
// println!("MemArena stats:");
|
||||
|
||||
// println!("\tTotal blocks: {}", arena_stats.2);
|
||||
}
|
||||
// if mib_occupied >= 1.0 {
|
||||
// println!("\tOccupied: {:.1} MiB", mib_occupied);
|
||||
// } else {
|
||||
// println!("\tOccupied: {:.4} MiB", mib_occupied);
|
||||
// }
|
||||
|
||||
// if mib_allocated >= 1.0 {
|
||||
// println!("\tUsed: {:.1} MiB", mib_allocated);
|
||||
// } else {
|
||||
// println!("\tUsed: {:.4} MiB", mib_allocated);
|
||||
// }
|
||||
|
||||
// println!("\tTotal blocks: {}", arena_stats.2);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,13 @@ impl<'a> DataTree<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ident(&'a self) -> Option<&'a str> {
|
||||
match *self {
|
||||
DataTree::Internal { ident, .. } => ident,
|
||||
DataTree::Leaf { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn byte_offset(&'a self) -> usize {
|
||||
match *self {
|
||||
DataTree::Internal { byte_offset, .. } | DataTree::Leaf { byte_offset, .. } => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{f32, result::Result};
|
||||
use std::{collections::HashMap, f32, result::Result};
|
||||
|
||||
use nom::{combinator::all_consuming, sequence::tuple, IResult};
|
||||
|
||||
|
@ -11,15 +11,17 @@ use crate::{
|
|||
color::{rec709_e_to_xyz, Color},
|
||||
light::WorldLightSource,
|
||||
math::Matrix4x4,
|
||||
renderer::Renderer,
|
||||
// renderer::Renderer,
|
||||
scene::Scene,
|
||||
scene::World,
|
||||
shading::SurfaceShader,
|
||||
};
|
||||
|
||||
use super::{
|
||||
basics::{ws_f32, ws_u32},
|
||||
psy_assembly::parse_assembly,
|
||||
psy_light::parse_distant_disk_light,
|
||||
psy_surface_shader::parse_surface_shader,
|
||||
DataTree,
|
||||
};
|
||||
|
||||
|
@ -31,6 +33,7 @@ pub enum PsyParseError {
|
|||
UnknownVariant(usize, &'static str), // Error message
|
||||
ExpectedInternalNode(usize, &'static str), // Error message
|
||||
ExpectedLeafNode(usize, &'static str), // Error message
|
||||
ExpectedIdent(usize, &'static str), // Error message
|
||||
MissingNode(usize, &'static str), // Error message
|
||||
IncorrectLeafData(usize, &'static str), // Error message
|
||||
WrongNodeCount(usize, &'static str, usize), // Error message, sections found
|
||||
|
@ -64,6 +67,11 @@ impl PsyParseError {
|
|||
println!("Line {}: {}", line, error);
|
||||
}
|
||||
|
||||
PsyParseError::ExpectedIdent(offset, error) => {
|
||||
let line = line_count_to_byte_offset(psy_content, offset);
|
||||
println!("Line {}: {}", line, error);
|
||||
}
|
||||
|
||||
PsyParseError::MissingNode(offset, error) => {
|
||||
let line = line_count_to_byte_offset(psy_content, offset);
|
||||
println!("Line {}: {}", line, error);
|
||||
|
@ -95,7 +103,7 @@ fn line_count_to_byte_offset(text: &str, offset: usize) -> usize {
|
|||
pub fn parse_scene<'a>(
|
||||
arena: &'a Arena,
|
||||
tree: &'a DataTree,
|
||||
) -> Result<Renderer<'a>, PsyParseError> {
|
||||
) -> Result<Scene<'a>, PsyParseError> {
|
||||
// Verify we have the right number of each section
|
||||
if tree.iter_children_with_type("Output").count() != 1 {
|
||||
let count = tree.iter_children_with_type("Output").count();
|
||||
|
@ -132,6 +140,14 @@ pub fn parse_scene<'a>(
|
|||
count,
|
||||
));
|
||||
}
|
||||
if tree.iter_children_with_type("Shaders").count() != 1 {
|
||||
let count = tree.iter_children_with_type("Shaders").count();
|
||||
return Err(PsyParseError::WrongNodeCount(
|
||||
tree.byte_offset(),
|
||||
"Scene should have precisely one Shaders section.",
|
||||
count,
|
||||
));
|
||||
}
|
||||
if tree.iter_children_with_type("Assembly").count() != 1 {
|
||||
let count = tree.iter_children_with_type("Assembly").count();
|
||||
return Err(PsyParseError::WrongNodeCount(
|
||||
|
@ -161,6 +177,9 @@ pub fn parse_scene<'a>(
|
|||
// Parse world
|
||||
let world = parse_world(arena, tree.iter_children_with_type("World").nth(0).unwrap())?;
|
||||
|
||||
// Parse shaders
|
||||
let shaders = parse_shaders(tree.iter_children_with_type("Shaders").nth(0).unwrap())?;
|
||||
|
||||
// Parse root scene assembly
|
||||
let assembly = parse_assembly(
|
||||
arena,
|
||||
|
@ -178,25 +197,25 @@ pub fn parse_scene<'a>(
|
|||
None
|
||||
};
|
||||
let scene = Scene {
|
||||
name: scene_name,
|
||||
camera: camera,
|
||||
world: world,
|
||||
root: assembly,
|
||||
shaders: shaders,
|
||||
root_assembly: assembly,
|
||||
};
|
||||
|
||||
// Put renderer together
|
||||
let renderer = Renderer {
|
||||
output_file: output_info.clone(),
|
||||
resolution: (
|
||||
(render_settings.0).0 as usize,
|
||||
(render_settings.0).1 as usize,
|
||||
),
|
||||
spp: render_settings.1 as usize,
|
||||
seed: render_settings.2,
|
||||
scene: scene,
|
||||
};
|
||||
// // Put renderer together
|
||||
// let renderer = Renderer {
|
||||
// output_file: output_info.clone(),
|
||||
// resolution: (
|
||||
// (render_settings.0).0 as usize,
|
||||
// (render_settings.0).1 as usize,
|
||||
// ),
|
||||
// spp: render_settings.1 as usize,
|
||||
// seed: render_settings.2,
|
||||
// scene: scene,
|
||||
// };
|
||||
|
||||
return Ok(renderer);
|
||||
return Ok(scene);
|
||||
}
|
||||
|
||||
fn parse_output_info(tree: &DataTree) -> Result<String, PsyParseError> {
|
||||
|
@ -553,6 +572,44 @@ fn parse_world<'a>(arena: &'a Arena, tree: &'a DataTree) -> Result<World<'a>, Ps
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_shaders<'a>(
|
||||
tree: &'a DataTree,
|
||||
) -> Result<HashMap<String, Box<dyn SurfaceShader>>, PsyParseError> {
|
||||
if tree.is_internal() {
|
||||
let mut shaders = HashMap::new();
|
||||
|
||||
for shader_item in tree.iter_children() {
|
||||
match shader_item {
|
||||
DataTree::Internal {
|
||||
type_name,
|
||||
ident,
|
||||
children,
|
||||
byte_offset,
|
||||
} if type_name == &"SurfaceShader" => {
|
||||
if let Some(name) = ident {
|
||||
shaders.insert(name.to_string(), parse_surface_shader(shader_item)?);
|
||||
} else {
|
||||
// TODO: error.
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
// TODO: an error.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the list of shaders.
|
||||
return Ok(shaders);
|
||||
} else {
|
||||
return Err(PsyParseError::ExpectedInternalNode(
|
||||
tree.byte_offset(),
|
||||
"Shaders section should be an internal \
|
||||
node.",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> {
|
||||
if let IResult::Ok((leftover, ns)) = all_consuming(tuple((
|
||||
ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32, ws_f32,
|
||||
|
|
|
@ -4,13 +4,12 @@ use std::result::Result;
|
|||
|
||||
use kioku::Arena;
|
||||
|
||||
use crate::scene::{Assembly, AssemblyBuilder, Object};
|
||||
use crate::scene::{Assembly, Object, ObjectData};
|
||||
|
||||
use super::{
|
||||
psy::{parse_matrix, PsyParseError},
|
||||
psy_light::{parse_rectangle_light, parse_sphere_light},
|
||||
psy_mesh_surface::parse_mesh_surface,
|
||||
psy_surface_shader::parse_surface_shader,
|
||||
DataTree,
|
||||
};
|
||||
|
||||
|
@ -18,171 +17,93 @@ pub fn parse_assembly<'a>(
|
|||
arena: &'a Arena,
|
||||
tree: &'a DataTree,
|
||||
) -> Result<Assembly<'a>, PsyParseError> {
|
||||
let mut builder = AssemblyBuilder::new(arena);
|
||||
|
||||
if tree.is_internal() {
|
||||
for child in tree.iter_children() {
|
||||
match child.type_name() {
|
||||
// Sub-Assembly
|
||||
"Assembly" => {
|
||||
if let DataTree::Internal {
|
||||
ident: Some(ident), ..
|
||||
} = *child
|
||||
{
|
||||
builder.add_assembly(ident, parse_assembly(arena, child)?);
|
||||
} else {
|
||||
return Err(PsyParseError::UnknownError(child.byte_offset()));
|
||||
}
|
||||
}
|
||||
|
||||
// Instance
|
||||
"Instance" => {
|
||||
// Pre-conditions
|
||||
if !child.is_internal() {
|
||||
return Err(PsyParseError::UnknownError(child.byte_offset()));
|
||||
}
|
||||
|
||||
// Get data name
|
||||
let name = {
|
||||
if child.iter_leaf_children_with_type("Data").count() != 1 {
|
||||
return Err(PsyParseError::UnknownError(child.byte_offset()));
|
||||
}
|
||||
child.iter_leaf_children_with_type("Data").nth(0).unwrap().1
|
||||
};
|
||||
|
||||
// Get surface shader binding, if any.
|
||||
let surface_shader_name = if child
|
||||
.iter_leaf_children_with_type("SurfaceShaderBind")
|
||||
.count()
|
||||
> 0
|
||||
{
|
||||
Some(
|
||||
child
|
||||
.iter_leaf_children_with_type("SurfaceShaderBind")
|
||||
.nth(0)
|
||||
.unwrap()
|
||||
.1,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Get xforms
|
||||
let mut xforms = Vec::new();
|
||||
for (_, contents, _) in child.iter_leaf_children_with_type("Transform") {
|
||||
xforms.push(parse_matrix(contents)?);
|
||||
}
|
||||
|
||||
// Add instance
|
||||
if builder.name_exists(name) {
|
||||
builder.add_instance(name, surface_shader_name, Some(&xforms));
|
||||
} else {
|
||||
return Err(PsyParseError::InstancedMissingData(
|
||||
child.iter_leaf_children_with_type("Data").nth(0).unwrap().2,
|
||||
"Attempted to add \
|
||||
instance for data with \
|
||||
a name that doesn't \
|
||||
exist.",
|
||||
name.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// SurfaceShader
|
||||
"SurfaceShader" => {
|
||||
if let DataTree::Internal {
|
||||
ident: Some(ident), ..
|
||||
} = *child
|
||||
{
|
||||
builder.add_surface_shader(ident, parse_surface_shader(arena, child)?);
|
||||
} else {
|
||||
// TODO: error condition of some kind, because no ident
|
||||
panic!(
|
||||
"SurfaceShader encountered that was a leaf, but SurfaceShaders cannot \
|
||||
be a leaf: {}",
|
||||
child.byte_offset()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// MeshSurface
|
||||
"MeshSurface" => {
|
||||
if let DataTree::Internal {
|
||||
ident: Some(ident), ..
|
||||
} = *child
|
||||
{
|
||||
builder.add_object(
|
||||
ident,
|
||||
Object::Surface(arena.alloc(parse_mesh_surface(arena, child)?)),
|
||||
);
|
||||
} else {
|
||||
// TODO: error condition of some kind, because no ident
|
||||
panic!(
|
||||
"MeshSurface encountered that was a leaf, but MeshSurfaces cannot \
|
||||
be a leaf: {}",
|
||||
child.byte_offset()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Sphere Light
|
||||
"SphereLight" => {
|
||||
if let DataTree::Internal {
|
||||
ident: Some(ident), ..
|
||||
} = *child
|
||||
{
|
||||
builder.add_object(
|
||||
ident,
|
||||
Object::SurfaceLight(arena.alloc(parse_sphere_light(arena, child)?)),
|
||||
);
|
||||
} else {
|
||||
// No ident
|
||||
return Err(PsyParseError::UnknownError(child.byte_offset()));
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle Light
|
||||
"RectangleLight" => {
|
||||
if let DataTree::Internal {
|
||||
ident: Some(ident), ..
|
||||
} = *child
|
||||
{
|
||||
builder.add_object(
|
||||
ident,
|
||||
Object::SurfaceLight(arena.alloc(parse_rectangle_light(arena, child)?)),
|
||||
);
|
||||
} else {
|
||||
// No ident
|
||||
return Err(PsyParseError::UnknownError(child.byte_offset()));
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
// TODO: some kind of error, because not a known type name
|
||||
} // // Bilinear Patch
|
||||
// "BilinearPatch" => {
|
||||
// assembly->add_object(child.name, parse_bilinear_patch(child));
|
||||
// }
|
||||
//
|
||||
// // Bicubic Patch
|
||||
// else if (child.type == "BicubicPatch") {
|
||||
// assembly->add_object(child.name, parse_bicubic_patch(child));
|
||||
// }
|
||||
//
|
||||
// // Subdivision surface
|
||||
// else if (child.type == "SubdivisionSurface") {
|
||||
// assembly->add_object(child.name, parse_subdivision_surface(child));
|
||||
// }
|
||||
//
|
||||
// // Sphere
|
||||
// else if (child.type == "Sphere") {
|
||||
// assembly->add_object(child.name, parse_sphere(child));
|
||||
// }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !tree.is_internal() {
|
||||
return Err(PsyParseError::UnknownError(tree.byte_offset()));
|
||||
}
|
||||
|
||||
return Ok(builder.build());
|
||||
let mut assembly = Assembly::new();
|
||||
for object in tree.iter_children() {
|
||||
if object.type_name() == "Object" {
|
||||
// Get object identifier.
|
||||
let object_ident = if let Some(ident) = object.ident() {
|
||||
ident
|
||||
} else {
|
||||
return Err(PsyParseError::ExpectedIdent(
|
||||
object.byte_offset(),
|
||||
"\'Object\' types must have an identifier, but the identifier is missing.",
|
||||
));
|
||||
};
|
||||
|
||||
// Collect instances.
|
||||
let mut instance_xform_idxs = Vec::new();
|
||||
for instance in object.iter_children_with_type("Instance") {
|
||||
if !instance.is_internal() {
|
||||
// TODO: error.
|
||||
}
|
||||
|
||||
let xforms_start_idx = assembly.xforms.len();
|
||||
for (_, contents, _) in instance.iter_leaf_children_with_type("Transform") {
|
||||
assembly.xforms.push(parse_matrix(contents)?);
|
||||
}
|
||||
instance_xform_idxs.push(xforms_start_idx..assembly.xforms.len());
|
||||
}
|
||||
|
||||
// Get object data.
|
||||
let object_data = {
|
||||
let obj_data_tree = {
|
||||
if object
|
||||
.iter_children()
|
||||
.filter(|d| d.type_name() != "Instance")
|
||||
.count()
|
||||
!= 1
|
||||
{
|
||||
// TODO: error.
|
||||
}
|
||||
object
|
||||
.iter_children()
|
||||
.filter(|d| d.type_name() != "Instance")
|
||||
.nth(0)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
match obj_data_tree.type_name() {
|
||||
// Sub-Assembly
|
||||
"Assembly" => {
|
||||
ObjectData::Assembly(Box::new(parse_assembly(arena, obj_data_tree)?))
|
||||
}
|
||||
|
||||
"MeshSurface" => {
|
||||
ObjectData::Surface(Box::new(parse_mesh_surface(arena, obj_data_tree)?))
|
||||
}
|
||||
|
||||
"SphereLight" => {
|
||||
ObjectData::Light(Box::new(parse_sphere_light(arena, obj_data_tree)?))
|
||||
}
|
||||
|
||||
"RectangleLight" => {
|
||||
ObjectData::Light(Box::new(parse_rectangle_light(arena, obj_data_tree)?))
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(PsyParseError::UnknownVariant(
|
||||
tree.byte_offset(),
|
||||
"Unknown data type for Object.",
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
assembly.objects.insert(
|
||||
object_ident.to_string(),
|
||||
Object {
|
||||
data: object_data,
|
||||
instance_xform_idxs: instance_xform_idxs,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// TODO: error.
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(assembly);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@ use std::result::Result;
|
|||
|
||||
use nom::{combinator::all_consuming, IResult};
|
||||
|
||||
use kioku::Arena;
|
||||
|
||||
use crate::shading::{SimpleSurfaceShader, SurfaceShader};
|
||||
|
||||
use super::{
|
||||
|
@ -21,10 +19,7 @@ use super::{
|
|||
// accel: BVH,
|
||||
// }
|
||||
|
||||
pub fn parse_surface_shader<'a>(
|
||||
arena: &'a Arena,
|
||||
tree: &'a DataTree,
|
||||
) -> Result<&'a dyn SurfaceShader, PsyParseError> {
|
||||
pub fn parse_surface_shader(tree: &DataTree) -> Result<Box<dyn SurfaceShader>, PsyParseError> {
|
||||
let type_name = if let Some((_, text, _)) = tree.iter_leaf_children_with_type("Type").nth(0) {
|
||||
text.trim()
|
||||
} else {
|
||||
|
@ -52,7 +47,7 @@ pub fn parse_surface_shader<'a>(
|
|||
));
|
||||
};
|
||||
|
||||
arena.alloc(SimpleSurfaceShader::Lambert { color: color })
|
||||
Box::new(SimpleSurfaceShader::Lambert { color: color })
|
||||
}
|
||||
|
||||
"GGX" => {
|
||||
|
@ -105,7 +100,7 @@ pub fn parse_surface_shader<'a>(
|
|||
));
|
||||
};
|
||||
|
||||
arena.alloc(SimpleSurfaceShader::GGX {
|
||||
Box::new(SimpleSurfaceShader::GGX {
|
||||
color: color,
|
||||
roughness: roughness,
|
||||
fresnel: fresnel,
|
||||
|
@ -129,7 +124,7 @@ pub fn parse_surface_shader<'a>(
|
|||
));
|
||||
};
|
||||
|
||||
arena.alloc(SimpleSurfaceShader::Emit { color: color })
|
||||
Box::new(SimpleSurfaceShader::Emit { color: color })
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
|
|
|
@ -1,417 +1,34 @@
|
|||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, ops::Range};
|
||||
|
||||
use kioku::Arena;
|
||||
use crate::{light::SurfaceLight, math::Matrix4x4, surface::Surface};
|
||||
|
||||
use crate::{
|
||||
accel::BVH4,
|
||||
accel::{LightAccel, LightTree},
|
||||
bbox::{transform_bbox_slice_from, BBox},
|
||||
boundable::Boundable,
|
||||
color::SpectralSample,
|
||||
lerp::lerp_slice,
|
||||
light::SurfaceLight,
|
||||
math::{Matrix4x4, Normal, Point},
|
||||
shading::SurfaceShader,
|
||||
surface::{Surface, SurfaceIntersection},
|
||||
transform_stack::TransformStack,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct Assembly<'a> {
|
||||
// Instance list
|
||||
pub instances: &'a [Instance],
|
||||
pub light_instances: &'a [Instance],
|
||||
pub xforms: &'a [Matrix4x4],
|
||||
|
||||
// Surface shader list
|
||||
pub surface_shaders: &'a [&'a dyn SurfaceShader],
|
||||
|
||||
// Object list
|
||||
pub objects: &'a [Object<'a>],
|
||||
|
||||
// Assembly list
|
||||
pub assemblies: &'a [Assembly<'a>],
|
||||
|
||||
// Object accel
|
||||
pub object_accel: BVH4<'a>,
|
||||
|
||||
// Light accel
|
||||
pub light_accel: LightTree<'a>,
|
||||
pub objects: HashMap<String, Object<'a>>, // Name, Object.
|
||||
pub xforms: Vec<Matrix4x4>,
|
||||
}
|
||||
|
||||
// TODO: actually fix this clippy warning, rather than `allow`ing it.
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl<'a> Assembly<'a> {
|
||||
// Returns (light_color, (sample_point, normal, point_err), pdf, selection_pdf)
|
||||
pub fn sample_lights(
|
||||
&self,
|
||||
xform_stack: &mut TransformStack,
|
||||
n: f32,
|
||||
uvw: (f32, f32, f32),
|
||||
wavelength: f32,
|
||||
time: f32,
|
||||
intr: &SurfaceIntersection,
|
||||
) -> Option<(SpectralSample, (Point, Normal, f32), f32, f32)> {
|
||||
if let SurfaceIntersection::Hit {
|
||||
intersection_data: idata,
|
||||
closure,
|
||||
} = *intr
|
||||
{
|
||||
let sel_xform = if !xform_stack.top().is_empty() {
|
||||
lerp_slice(xform_stack.top(), time)
|
||||
} else {
|
||||
Matrix4x4::new()
|
||||
};
|
||||
if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel.select(
|
||||
idata.incoming * sel_xform,
|
||||
idata.pos * sel_xform,
|
||||
idata.nor * sel_xform,
|
||||
idata.nor_g * sel_xform,
|
||||
&closure,
|
||||
time,
|
||||
n,
|
||||
) {
|
||||
let inst = self.light_instances[light_i];
|
||||
match inst.instance_type {
|
||||
InstanceType::Object => {
|
||||
match self.objects[inst.data_index] {
|
||||
Object::SurfaceLight(light) => {
|
||||
// Get the world-to-object space transform of the light
|
||||
let xform = if let Some((a, b)) = inst.transform_indices {
|
||||
let pxforms = xform_stack.top();
|
||||
let xform = lerp_slice(&self.xforms[a..b], time);
|
||||
if !pxforms.is_empty() {
|
||||
lerp_slice(pxforms, time) * xform
|
||||
} else {
|
||||
xform
|
||||
}
|
||||
} else {
|
||||
let pxforms = xform_stack.top();
|
||||
if !pxforms.is_empty() {
|
||||
lerp_slice(pxforms, time)
|
||||
} else {
|
||||
Matrix4x4::new()
|
||||
}
|
||||
};
|
||||
|
||||
// Sample the light
|
||||
let (color, sample_geo, pdf) = light.sample_from_point(
|
||||
&xform, idata.pos, uvw.0, uvw.1, wavelength, time,
|
||||
);
|
||||
return Some((color, sample_geo, pdf, sel_pdf));
|
||||
}
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
InstanceType::Assembly => {
|
||||
// Push the world-to-object space transforms of the assembly onto
|
||||
// the transform stack.
|
||||
if let Some((a, b)) = inst.transform_indices {
|
||||
xform_stack.push(&self.xforms[a..b]);
|
||||
}
|
||||
|
||||
// Sample sub-assembly lights
|
||||
let sample = self.assemblies[inst.data_index].sample_lights(
|
||||
xform_stack,
|
||||
whittled_n,
|
||||
uvw,
|
||||
wavelength,
|
||||
time,
|
||||
intr,
|
||||
);
|
||||
|
||||
// Pop the assembly's transforms off the transform stack.
|
||||
if inst.transform_indices.is_some() {
|
||||
xform_stack.pop();
|
||||
}
|
||||
|
||||
// Return sample
|
||||
return sample.map(|(ss, v, pdf, spdf)| (ss, v, pdf, spdf * sel_pdf));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
pub fn new() -> Assembly<'a> {
|
||||
Assembly {
|
||||
objects: HashMap::new(),
|
||||
xforms: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Boundable for Assembly<'a> {
|
||||
fn bounds(&self) -> &[BBox] {
|
||||
self.object_accel.bounds()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AssemblyBuilder<'a> {
|
||||
arena: &'a Arena,
|
||||
pub struct Object<'a> {
|
||||
pub data: ObjectData<'a>,
|
||||
|
||||
// Instance list
|
||||
instances: Vec<Instance>,
|
||||
xforms: Vec<Matrix4x4>,
|
||||
|
||||
// Shader list
|
||||
surface_shaders: Vec<&'a dyn SurfaceShader>,
|
||||
surface_shader_map: HashMap<String, usize>, // map Name -> Index
|
||||
|
||||
// Object list
|
||||
objects: Vec<Object<'a>>,
|
||||
object_map: HashMap<String, usize>, // map Name -> Index
|
||||
|
||||
// Assembly list
|
||||
assemblies: Vec<Assembly<'a>>,
|
||||
assembly_map: HashMap<String, usize>, // map Name -> Index
|
||||
// One range per instance, indexing into the assembly's xforms array.
|
||||
pub instance_xform_idxs: Vec<Range<usize>>,
|
||||
}
|
||||
|
||||
impl<'a> AssemblyBuilder<'a> {
|
||||
pub fn new(arena: &'a Arena) -> AssemblyBuilder<'a> {
|
||||
AssemblyBuilder {
|
||||
arena: arena,
|
||||
instances: Vec::new(),
|
||||
xforms: Vec::new(),
|
||||
surface_shaders: Vec::new(),
|
||||
surface_shader_map: HashMap::new(),
|
||||
objects: Vec::new(),
|
||||
object_map: HashMap::new(),
|
||||
assemblies: Vec::new(),
|
||||
assembly_map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_surface_shader(&mut self, name: &str, shader: &'a dyn SurfaceShader) {
|
||||
// Make sure the name hasn't already been used.
|
||||
if self.surface_shader_map.contains_key(name) {
|
||||
panic!("Attempted to add surface shader to assembly with a name that already exists.");
|
||||
}
|
||||
|
||||
// Add shader
|
||||
self.surface_shader_map
|
||||
.insert(name.to_string(), self.surface_shaders.len());
|
||||
self.surface_shaders.push(shader);
|
||||
}
|
||||
|
||||
pub fn add_object(&mut self, name: &str, obj: Object<'a>) {
|
||||
// Make sure the name hasn't already been used.
|
||||
if self.name_exists(name) {
|
||||
panic!("Attempted to add object to assembly with a name that already exists.");
|
||||
}
|
||||
|
||||
// Add object
|
||||
self.object_map.insert(name.to_string(), self.objects.len());
|
||||
self.objects.push(obj);
|
||||
}
|
||||
|
||||
pub fn add_assembly(&mut self, name: &str, asmb: Assembly<'a>) {
|
||||
// Make sure the name hasn't already been used.
|
||||
if self.name_exists(name) {
|
||||
panic!(
|
||||
"Attempted to add assembly to another assembly with a name that already \
|
||||
exists."
|
||||
);
|
||||
}
|
||||
|
||||
// Add assembly
|
||||
self.assembly_map
|
||||
.insert(name.to_string(), self.assemblies.len());
|
||||
self.assemblies.push(asmb);
|
||||
}
|
||||
|
||||
pub fn add_instance(
|
||||
&mut self,
|
||||
name: &str,
|
||||
surface_shader_name: Option<&str>,
|
||||
xforms: Option<&[Matrix4x4]>,
|
||||
) {
|
||||
// Make sure name exists
|
||||
if !self.name_exists(name) {
|
||||
panic!("Attempted to add instance with a name that doesn't exist.");
|
||||
}
|
||||
|
||||
// Map zero-length transforms to None
|
||||
let xforms = if let Some(xf) = xforms {
|
||||
if !xf.is_empty() {
|
||||
Some(xf)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Create instance
|
||||
let instance = if self.object_map.contains_key(name) {
|
||||
Instance {
|
||||
instance_type: InstanceType::Object,
|
||||
data_index: self.object_map[name],
|
||||
surface_shader_index: surface_shader_name.map(|name| {
|
||||
*self
|
||||
.surface_shader_map
|
||||
.get(name)
|
||||
.unwrap_or_else(|| panic!("Unknown surface shader '{}'.", name))
|
||||
}),
|
||||
id: self.instances.len(),
|
||||
transform_indices: xforms
|
||||
.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())),
|
||||
}
|
||||
} else {
|
||||
Instance {
|
||||
instance_type: InstanceType::Assembly,
|
||||
data_index: self.assembly_map[name],
|
||||
surface_shader_index: surface_shader_name.map(|name| {
|
||||
*self
|
||||
.surface_shader_map
|
||||
.get(name)
|
||||
.unwrap_or_else(|| panic!("Unknown surface shader '{}'.", name))
|
||||
}),
|
||||
id: self.instances.len(),
|
||||
transform_indices: xforms
|
||||
.map(|xf| (self.xforms.len(), self.xforms.len() + xf.len())),
|
||||
}
|
||||
};
|
||||
|
||||
self.instances.push(instance);
|
||||
|
||||
// Store transforms
|
||||
if let Some(xf) = xforms {
|
||||
self.xforms.extend(xf);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_exists(&self, name: &str) -> bool {
|
||||
self.object_map.contains_key(name) || self.assembly_map.contains_key(name)
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Assembly<'a> {
|
||||
// Calculate instance bounds, used for building object accel and light accel.
|
||||
let (bis, bbs) = self.instance_bounds();
|
||||
|
||||
// Build object accel
|
||||
let object_accel = BVH4::from_objects(self.arena, &mut self.instances[..], 1, |inst| {
|
||||
&bbs[bis[inst.id]..bis[inst.id + 1]]
|
||||
});
|
||||
|
||||
// Get list of instances that are for light sources or assemblies that contain light
|
||||
// sources.
|
||||
let mut light_instances: Vec<_> = self
|
||||
.instances
|
||||
.iter()
|
||||
.filter(|inst| match inst.instance_type {
|
||||
InstanceType::Object => {
|
||||
if let Object::SurfaceLight(_) = self.objects[inst.data_index] {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
InstanceType::Assembly => {
|
||||
self.assemblies[inst.data_index]
|
||||
.light_accel
|
||||
.approximate_energy()
|
||||
> 0.0
|
||||
}
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
// Build light accel
|
||||
let light_accel = LightTree::from_objects(self.arena, &mut light_instances[..], |inst| {
|
||||
let bounds = &bbs[bis[inst.id]..bis[inst.id + 1]];
|
||||
let energy = match inst.instance_type {
|
||||
InstanceType::Object => {
|
||||
if let Object::SurfaceLight(light) = self.objects[inst.data_index] {
|
||||
light.approximate_energy()
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
InstanceType::Assembly => self.assemblies[inst.data_index]
|
||||
.light_accel
|
||||
.approximate_energy(),
|
||||
};
|
||||
(bounds, energy)
|
||||
});
|
||||
|
||||
Assembly {
|
||||
instances: self.arena.copy_slice(&self.instances),
|
||||
light_instances: self.arena.copy_slice(&light_instances),
|
||||
xforms: self.arena.copy_slice(&self.xforms),
|
||||
surface_shaders: self.arena.copy_slice(&self.surface_shaders),
|
||||
objects: self.arena.copy_slice(&self.objects),
|
||||
assemblies: self.arena.copy_slice(&self.assemblies),
|
||||
object_accel: object_accel,
|
||||
light_accel: light_accel,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a pair of vectors with the bounds of all instances.
|
||||
/// This is used for building the assembly's BVH4.
|
||||
fn instance_bounds(&self) -> (Vec<usize>, Vec<BBox>) {
|
||||
let mut indices = vec![0];
|
||||
let mut bounds = Vec::new();
|
||||
|
||||
for inst in &self.instances {
|
||||
let mut bbs = Vec::new();
|
||||
let mut bbs2 = Vec::new();
|
||||
|
||||
// Get bounding boxes
|
||||
match inst.instance_type {
|
||||
InstanceType::Object => {
|
||||
// Push bounds onto bbs
|
||||
let obj = &self.objects[inst.data_index];
|
||||
match *obj {
|
||||
Object::Surface(s) => bbs.extend(s.bounds()),
|
||||
Object::SurfaceLight(l) => bbs.extend(l.bounds()),
|
||||
}
|
||||
}
|
||||
|
||||
InstanceType::Assembly => {
|
||||
// Push bounds onto bbs
|
||||
let asmb = &self.assemblies[inst.data_index];
|
||||
bbs.extend(asmb.bounds());
|
||||
}
|
||||
}
|
||||
|
||||
// Transform the bounding boxes, if necessary
|
||||
if let Some((xstart, xend)) = inst.transform_indices {
|
||||
let xf = &self.xforms[xstart..xend];
|
||||
transform_bbox_slice_from(&bbs, xf, &mut bbs2);
|
||||
} else {
|
||||
bbs2.clear();
|
||||
bbs2.extend(bbs);
|
||||
}
|
||||
|
||||
// Push transformed bounds onto vec
|
||||
bounds.extend(bbs2);
|
||||
indices.push(bounds.len());
|
||||
}
|
||||
|
||||
(indices, bounds)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Object<'a> {
|
||||
Surface(&'a dyn Surface),
|
||||
SurfaceLight(&'a dyn SurfaceLight),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Instance {
|
||||
pub instance_type: InstanceType,
|
||||
pub data_index: usize,
|
||||
pub surface_shader_index: Option<usize>,
|
||||
pub id: usize,
|
||||
pub transform_indices: Option<(usize, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum InstanceType {
|
||||
Object,
|
||||
Assembly,
|
||||
#[derive(Debug)]
|
||||
pub enum ObjectData<'a> {
|
||||
Empty,
|
||||
Surface(Box<dyn Surface + 'a>),
|
||||
Light(Box<dyn SurfaceLight + 'a>),
|
||||
Assembly(Box<Assembly<'a>>),
|
||||
}
|
||||
|
|
144
src/scene/mod.rs
144
src/scene/mod.rs
|
@ -1,151 +1,19 @@
|
|||
mod assembly;
|
||||
mod world;
|
||||
|
||||
use crate::{
|
||||
accel::LightAccel,
|
||||
algorithm::weighted_choice,
|
||||
camera::Camera,
|
||||
color::SpectralSample,
|
||||
math::{Normal, Point, Vector},
|
||||
surface::SurfaceIntersection,
|
||||
transform_stack::TransformStack,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{camera::Camera, shading::SurfaceShader};
|
||||
|
||||
pub use self::{
|
||||
assembly::{Assembly, AssemblyBuilder, InstanceType, Object},
|
||||
assembly::{Assembly, Object, ObjectData},
|
||||
world::World,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scene<'a> {
|
||||
pub name: Option<String>,
|
||||
pub camera: Camera<'a>,
|
||||
pub world: World<'a>,
|
||||
pub root: Assembly<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Scene<'a> {
|
||||
pub fn sample_lights(
|
||||
&self,
|
||||
xform_stack: &mut TransformStack,
|
||||
n: f32,
|
||||
uvw: (f32, f32, f32),
|
||||
wavelength: f32,
|
||||
time: f32,
|
||||
intr: &SurfaceIntersection,
|
||||
) -> SceneLightSample {
|
||||
// TODO: this just selects between world lights and local lights
|
||||
// with a 50/50 chance. We should do something more sophisticated
|
||||
// than this, accounting for the estimated impact of the lights
|
||||
// on the point being lit.
|
||||
|
||||
// Calculate relative probabilities of traversing into world lights
|
||||
// or local lights.
|
||||
let wl_energy = if self
|
||||
.world
|
||||
.lights
|
||||
.iter()
|
||||
.fold(0.0, |energy, light| energy + light.approximate_energy())
|
||||
<= 0.0
|
||||
{
|
||||
0.0
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let ll_energy = if self.root.light_accel.approximate_energy() <= 0.0 {
|
||||
0.0
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let tot_energy = wl_energy + ll_energy;
|
||||
|
||||
// Decide either world or local lights, and select and sample a light.
|
||||
if tot_energy <= 0.0 {
|
||||
return SceneLightSample::None;
|
||||
} else {
|
||||
let wl_prob = wl_energy / tot_energy;
|
||||
|
||||
if n < wl_prob {
|
||||
// World lights
|
||||
let n = n / wl_prob;
|
||||
let (i, p) = weighted_choice(self.world.lights, n, |l| l.approximate_energy());
|
||||
let (ss, sv, pdf) =
|
||||
self.world.lights[i].sample_from_point(uvw.0, uvw.1, wavelength, time);
|
||||
return SceneLightSample::Distant {
|
||||
color: ss,
|
||||
direction: sv,
|
||||
pdf: pdf,
|
||||
selection_pdf: p * wl_prob,
|
||||
};
|
||||
} else {
|
||||
// Local lights
|
||||
let n = (n - wl_prob) / (1.0 - wl_prob);
|
||||
|
||||
if let Some((ss, sgeo, pdf, spdf)) =
|
||||
self.root
|
||||
.sample_lights(xform_stack, n, uvw, wavelength, time, intr)
|
||||
{
|
||||
return SceneLightSample::Surface {
|
||||
color: ss,
|
||||
sample_geo: sgeo,
|
||||
pdf: pdf,
|
||||
selection_pdf: spdf * (1.0 - wl_prob),
|
||||
};
|
||||
} else {
|
||||
return SceneLightSample::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum SceneLightSample {
|
||||
None,
|
||||
Distant {
|
||||
color: SpectralSample,
|
||||
direction: Vector,
|
||||
pdf: f32,
|
||||
selection_pdf: f32,
|
||||
},
|
||||
Surface {
|
||||
color: SpectralSample,
|
||||
sample_geo: (Point, Normal, f32),
|
||||
pdf: f32,
|
||||
selection_pdf: f32,
|
||||
},
|
||||
}
|
||||
|
||||
impl SceneLightSample {
|
||||
pub fn is_none(&self) -> bool {
|
||||
if let SceneLightSample::None = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(&self) -> SpectralSample {
|
||||
match *self {
|
||||
SceneLightSample::None => panic!(),
|
||||
SceneLightSample::Distant { color, .. } => color,
|
||||
SceneLightSample::Surface { color, .. } => color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pdf(&self) -> f32 {
|
||||
match *self {
|
||||
SceneLightSample::None => panic!(),
|
||||
SceneLightSample::Distant { pdf, .. } => pdf,
|
||||
SceneLightSample::Surface { pdf, .. } => pdf,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn selection_pdf(&self) -> f32 {
|
||||
match *self {
|
||||
SceneLightSample::None => panic!(),
|
||||
SceneLightSample::Distant { selection_pdf, .. } => selection_pdf,
|
||||
SceneLightSample::Surface { selection_pdf, .. } => selection_pdf,
|
||||
}
|
||||
}
|
||||
pub shaders: HashMap<String, Box<dyn SurfaceShader>>, // Name, Shader
|
||||
pub root_assembly: Assembly<'a>,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user