Added DistantDiskLight (a.k.a. sun light) parsing and data structures.

Also created a proper World struct in the process, to store all
infinite-extent type stuff.

Note that I goofed and did a new rustfmt pass but forgot to
commit before making these changes, so there's a lot of
formatting changes in this too.  *sigh*
This commit is contained in:
Nathan Vegdahl 2017-02-12 20:29:08 -08:00
parent 746b3b0c1f
commit 3cbb816d4b
15 changed files with 329 additions and 111 deletions

View File

@ -191,8 +191,9 @@ pub fn merge_slices_to<T: Lerp + Copy, F>(slice1: &[T],
if slice1.len() == 0 || slice2.len() == 0 { if slice1.len() == 0 || slice2.len() == 0 {
return; return;
} else if slice1.len() == slice2.len() { } else if slice1.len() == slice2.len() {
for (xfo, (xf1, xf2)) in Iterator::zip(slice_out.iter_mut(), for (xfo, (xf1, xf2)) in
Iterator::zip(slice1.iter(), slice2.iter())) { Iterator::zip(slice_out.iter_mut(),
Iterator::zip(slice1.iter(), slice2.iter())) {
*xfo = merge(xf1, xf2); *xfo = merge(xf1, xf2);
} }
} else if slice1.len() > slice2.len() { } else if slice1.len() > slice2.len() {
@ -216,14 +217,12 @@ mod tests {
use super::*; use super::*;
fn quick_select_ints(list: &mut [i32], i: usize) { fn quick_select_ints(list: &mut [i32], i: usize) {
quick_select(list, i, |a, b| { quick_select(list, i, |a, b| if a < b {
if a < b { Ordering::Less
Ordering::Less } else if a == b {
} else if a == b { Ordering::Equal
Ordering::Equal } else {
} else { Ordering::Greater
Ordering::Greater
}
}); });
} }

View File

@ -48,13 +48,14 @@ impl Assembly {
} else { } else {
Matrix4x4::new() Matrix4x4::new()
}; };
if let Some((light_i, sel_pdf, whittled_n)) = self.light_accel if let Some((light_i, sel_pdf, whittled_n)) =
.select(incoming * sel_xform, self.light_accel
pos * sel_xform, .select(incoming * sel_xform,
nor * sel_xform, pos * sel_xform,
closure.as_surface_closure(), nor * sel_xform,
time, closure.as_surface_closure(),
n) { time,
n) {
let inst = self.light_instances[light_i]; let inst = self.light_instances[light_i];
match inst.instance_type { match inst.instance_type {
@ -239,19 +240,17 @@ impl AssemblyBuilder {
// sources. // sources.
let mut light_instances: Vec<_> = self.instances let mut light_instances: Vec<_> = self.instances
.iter() .iter()
.filter(|inst| { .filter(|inst| match inst.instance_type {
match inst.instance_type { InstanceType::Object => {
InstanceType::Object => { if let Object::Light(_) = self.objects[inst.data_index] {
if let Object::Light(_) = self.objects[inst.data_index] { true
true } else {
} else { false
false
}
} }
}
InstanceType::Assembly => { InstanceType::Assembly => {
self.assemblies[inst.data_index].light_accel.approximate_energy() > 0.0 self.assemblies[inst.data_index].light_accel.approximate_energy() > 0.0
}
} }
}) })
.map(|&a| a) .map(|&a| a)

View File

@ -0,0 +1,82 @@
use std::f64::consts::PI as PI_64;
use color::{XYZ, SpectralSample, Color};
use lerp::lerp_slice;
use math::{Vector, coordinate_system_from_vector};
use sampling::{uniform_sample_cone, uniform_sample_cone_pdf};
use super::WorldLightSource;
// TODO: handle case where radius = 0.0.
#[derive(Debug)]
pub struct DistantDiskLight {
radii: Vec<f32>,
directions: Vec<Vector>,
colors: Vec<XYZ>,
}
impl DistantDiskLight {
pub fn new(radii: Vec<f32>, directions: Vec<Vector>, colors: Vec<XYZ>) -> DistantDiskLight {
DistantDiskLight {
radii: radii,
directions: directions,
colors: colors,
}
}
}
impl WorldLightSource for DistantDiskLight {
fn sample(&self, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32) {
// Calculate time interpolated values
let radius: f64 = lerp_slice(&self.radii, time) as f64;
let direction = lerp_slice(&self.directions, time);
let col = lerp_slice(&self.colors, time);
let solid_angle_inv = 1.0 / (2.0 * PI_64 * (1.0 - radius.cos()));
// Create a coordinate system from the vector pointing at the center of
// of the light.
let (z, x, y) = coordinate_system_from_vector(-direction);
let (x, y, z) = (x.normalized(), y.normalized(), z.normalized());
// Calculate the radius in terms of cosine
let cos_theta_max: f64 = radius.cos();
// Sample the cone subtended by the light.
let sample = uniform_sample_cone(u, v, cos_theta_max).normalized();
// Calculate the final values and return everything.
let shadow_vec = (x * sample.x()) + (y * sample.y()) + (z * sample.z());
let pdf = uniform_sample_cone_pdf(cos_theta_max);
let spectral_sample = (col * solid_angle_inv as f32).to_spectral_sample(wavelength);
return (spectral_sample, shadow_vec, pdf as f32);
}
fn sample_pdf(&self, sample_dir: Vector, wavelength: f32, time: f32) -> f32 {
// We're not using these, silence warnings
let _ = (sample_dir, wavelength);
let radius: f64 = lerp_slice(&self.radii, time) as f64;
return uniform_sample_cone_pdf(radius.cos()) as f32;
}
fn outgoing(&self, dir: Vector, wavelength: f32, time: f32) -> SpectralSample {
// We're not using this, silence warning
let _ = dir;
let radius = lerp_slice(&self.radii, time) as f64;
let col = lerp_slice(&self.colors, time);
let solid_angle = 2.0 * PI_64 * (1.0 - radius.cos());
(col / solid_angle as f32).to_spectral_sample(wavelength)
}
fn is_delta(&self) -> bool {
false
}
fn approximate_energy(&self) -> f32 {
let color: XYZ = self.colors.iter().fold(XYZ::new(0.0, 0.0, 0.0), |a, &b| a + b) /
self.colors.len() as f32;
color.y
}
}

View File

@ -1,3 +1,4 @@
mod distant_disk_light;
mod rectangle_light; mod rectangle_light;
mod sphere_light; mod sphere_light;
@ -7,14 +8,17 @@ use boundable::Boundable;
use color::SpectralSample; use color::SpectralSample;
use math::{Vector, Point, Matrix4x4}; use math::{Vector, Point, Matrix4x4};
pub use self::distant_disk_light::DistantDiskLight;
pub use self::rectangle_light::RectangleLight; pub use self::rectangle_light::RectangleLight;
pub use self::sphere_light::SphereLight; pub use self::sphere_light::SphereLight;
/// A finite light source that can be bounded in space.
pub trait LightSource: Boundable + Debug + Sync { pub trait LightSource: Boundable + Debug + Sync {
/// Samples the light source for a given point to be illuminated. /// Samples the light source for a given point to be illuminated.
/// ///
/// - arr: The point to be illuminated. /// - space: The world-to-object space transform of the light.
/// - arr: The point to be illuminated (in world space).
/// - u: Random parameter U. /// - u: Random parameter U.
/// - v: Random parameter V. /// - v: Random parameter V.
/// - wavelength: The wavelength of light to sample at. /// - wavelength: The wavelength of light to sample at.
@ -77,6 +81,54 @@ pub trait LightSource: Boundable + Debug + Sync {
fn is_delta(&self) -> bool; fn is_delta(&self) -> bool;
/// Returns an approximation of the total energy emitted by the light
/// source. Note that this does not need to be exact: it is used for
/// importance sampling.
fn approximate_energy(&self) -> f32;
}
/// An infinite light source that cannot be bounded in space. E.g.
/// a sun light source.
pub trait WorldLightSource: Debug + Sync {
/// Samples the light source for a given point to be illuminated.
///
/// - u: Random parameter U.
/// - v: Random parameter V.
/// - wavelength: The wavelength of light to sample at.
/// - time: The time to sample at.
///
/// Returns: The light arriving from the shadow-testing direction, the
/// vector to use for shadow testing, and the pdf of the sample.
fn sample(&self, u: f32, v: f32, wavelength: f32, time: f32) -> (SpectralSample, Vector, f32);
/// Calculates the pdf of sampling the given sample_dir. This is used
/// primarily to calculate probabilities for multiple importance sampling.
///
/// NOTE: this function CAN assume that sample_dir is a valid sample for
/// the light source (i.e. hits/lies on the light source). No guarantees
/// are made about the correctness of the return value if it isn't valid.
fn sample_pdf(&self, sample_dir: Vector, wavelength: f32, time: f32) -> f32;
/// Returns the color emitted in the given direction from the
/// given parameters on the light.
///
/// - dir: The direction of the outgoing light.
/// - wavelength: The hero wavelength of light to sample at.
/// - time: The time to sample at.
fn outgoing(&self, dir: Vector, wavelength: f32, time: f32) -> SpectralSample;
/// Returns whether the light has a delta distribution.
///
/// If a light has no chance of a ray hitting it through random process
/// then it is a delta light source. For example, point light sources,
/// lights that only emit in a single direction, etc.
fn is_delta(&self) -> bool;
/// Returns an approximation of the total energy emitted by the light /// Returns an approximation of the total energy emitted by the light
/// source. Note that this does not need to be exact: it is used for /// source. Note that this does not need to be exact: it is used for
/// importance sampling. /// importance sampling.

View File

@ -9,6 +9,7 @@ use sampling::{uniform_sample_cone, uniform_sample_cone_pdf, uniform_sample_sphe
use super::LightSource; use super::LightSource;
// TODO: handle case where radius = 0.0.
#[derive(Debug)] #[derive(Debug)]
pub struct SphereLight { pub struct SphereLight {
@ -57,7 +58,7 @@ impl LightSource for SphereLight {
// Create a coordinate system from the vector between the // Create a coordinate system from the vector between the
// point and the center of the light // point and the center of the light
let z = pos - arr; let z = pos - arr;
let d2: f64 = z.length2() as f64; // Distance from center of sphere squared let d2: f64 = z.length2() as f64; // Distance from center of sphere squared
let d = d2.sqrt(); // Distance from center of sphere let d = d2.sqrt(); // Distance from center of sphere
let (z, x, y) = coordinate_system_from_vector(z); let (z, x, y) = coordinate_system_from_vector(z);
let (x, y, z) = (x.normalized(), y.normalized(), z.normalized()); let (x, y, z) = (x.normalized(), y.normalized(), z.normalized());
@ -125,7 +126,7 @@ impl LightSource for SphereLight {
let pos = Point::new(0.0, 0.0, 0.0); let pos = Point::new(0.0, 0.0, 0.0);
let radius: f64 = lerp_slice(&self.radii, time) as f64; let radius: f64 = lerp_slice(&self.radii, time) as f64;
let d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared let d2: f64 = (pos - arr).length2() as f64; // Distance from center of sphere squared
let d: f64 = d2.sqrt(); // Distance from center of sphere let d: f64 = d2.sqrt(); // Distance from center of sphere
if d > radius { if d > radius {

View File

@ -41,6 +41,7 @@ mod timer;
mod tracer; mod tracer;
mod transform_stack; mod transform_stack;
mod triangle; mod triangle;
mod world;
use std::fs::File; use std::fs::File;
use std::io; use std::io;

View File

@ -48,8 +48,8 @@ pub fn fast_pow2(p: f32) -> f32 {
let z: f32 = clipp - w as f32 + offset; let z: f32 = clipp - w as f32 + offset;
let i: u32 = ((1 << 23) as f32 * let i: u32 = ((1 << 23) as f32 *
(clipp + 121.2740575 + 27.7280233 / (4.84252568 - z) - (clipp + 121.2740575 + 27.7280233 / (4.84252568 - z) - 1.49012907 * z)) as
1.49012907 * z)) as u32; u32;
unsafe { transmute_copy::<u32, f32>(&i) } unsafe { transmute_copy::<u32, f32>(&i) }
} }

View File

@ -64,8 +64,8 @@ pub fn free_sah_split<'a, T, F>(seed: u32, objects: &mut [T], bounder: &F) -> (u
// Build SAH bins // Build SAH bins
let sah_bins = { let sah_bins = {
let mut sah_bins = let mut sah_bins = [[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1];
[[(BBox::new(), BBox::new(), 0, 0); SAH_BIN_COUNT - 1]; SPLIT_PLANE_COUNT]; SPLIT_PLANE_COUNT];
for obj in objects.iter() { for obj in objects.iter() {
let tb = lerp_slice(bounder(obj), 0.5); let tb = lerp_slice(bounder(obj), 0.5);
let centroid = tb.center().into_vector(); let centroid = tb.center().into_vector();

View File

@ -287,10 +287,10 @@ fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
} }
if let (Token::CloseInner, text4) = next_token(text_remaining) { if let (Token::CloseInner, text4) = next_token(text_remaining) {
return Ok(Some((DataTree::Internal { return Ok(Some((DataTree::Internal {
type_name: type_name, type_name: type_name,
ident: Some(n), ident: Some(n),
children: children, children: children,
}, },
text4))); text4)));
} else { } else {
return Err(ParseError::MissingCloseInternal(text_remaining.0)); return Err(ParseError::MissingCloseInternal(text_remaining.0));
@ -311,10 +311,10 @@ fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
if let (Token::CloseInner, text3) = next_token(text_remaining) { if let (Token::CloseInner, text3) = next_token(text_remaining) {
return Ok(Some((DataTree::Internal { return Ok(Some((DataTree::Internal {
type_name: type_name, type_name: type_name,
ident: None, ident: None,
children: children, children: children,
}, },
text3))); text3)));
} else { } else {
return Err(ParseError::MissingCloseInternal(text_remaining.0)); return Err(ParseError::MissingCloseInternal(text_remaining.0));
@ -326,9 +326,9 @@ fn parse_node<'a>(source_text: (usize, &'a str)) -> ParseResult<'a> {
let (contents, text3) = parse_leaf_content(text2); let (contents, text3) = parse_leaf_content(text2);
if let (Token::CloseLeaf, text4) = next_token(text3) { if let (Token::CloseLeaf, text4) = next_token(text3) {
return Ok(Some((DataTree::Leaf { return Ok(Some((DataTree::Leaf {
type_name: type_name, type_name: type_name,
contents: contents, contents: contents,
}, },
text4))); text4)));
} else { } else {
return Err(ParseError::MissingCloseLeaf(text3.0)); return Err(ParseError::MissingCloseLeaf(text3.0));

View File

@ -9,11 +9,14 @@ use camera::Camera;
use color::{XYZ, rec709e_to_xyz}; use color::{XYZ, rec709e_to_xyz};
use math::Matrix4x4; use math::Matrix4x4;
use renderer::Renderer; use renderer::Renderer;
use world::World;
use scene::Scene; use scene::Scene;
use light::WorldLightSource;
use super::basics::{ws_u32, ws_f32}; use super::basics::{ws_u32, ws_f32};
use super::DataTree; use super::DataTree;
use super::psy_assembly::parse_assembly; use super::psy_assembly::parse_assembly;
use super::psy_light::parse_distant_disk_light;
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@ -24,7 +27,6 @@ pub enum PsyParseError {
/// Takes in a DataTree representing a Scene node and returns /// Takes in a DataTree representing a Scene node and returns
/// a renderer.
pub fn parse_scene(tree: &DataTree) -> Result<Renderer, PsyParseError> { pub fn parse_scene(tree: &DataTree) -> Result<Renderer, PsyParseError> {
// Verify we have the right number of each section // Verify we have the right number of each section
if tree.iter_children_with_type("Output").count() != 1 { if tree.iter_children_with_type("Output").count() != 1 {
@ -79,8 +81,8 @@ pub fn parse_scene(tree: &DataTree) -> Result<Renderer, PsyParseError> {
}; };
let scene = Scene { let scene = Scene {
name: scene_name, name: scene_name,
background_color: world,
camera: camera, camera: camera,
world: world,
root: assembly, root: assembly,
}; };
@ -266,9 +268,10 @@ fn parse_camera(tree: &DataTree) -> Result<Camera, PsyParseError> {
fn parse_world(tree: &DataTree) -> Result<XYZ, PsyParseError> { fn parse_world(tree: &DataTree) -> Result<World, PsyParseError> {
if tree.is_internal() { if tree.is_internal() {
let background_color; let background_color;
let mut lights: Vec<Box<WorldLightSource>> = Vec::new();
// Parse background shader // Parse background shader
let bgs = { let bgs = {
@ -281,9 +284,10 @@ fn parse_world(tree: &DataTree) -> Result<XYZ, PsyParseError> {
if bgs.iter_children_with_type("Type").count() != 1 { if bgs.iter_children_with_type("Type").count() != 1 {
return Err(PsyParseError::UnknownError); return Err(PsyParseError::UnknownError);
} }
if let &DataTree::Leaf { contents, .. } = bgs.iter_children_with_type("Type") if let &DataTree::Leaf { contents, .. } =
.nth(0) bgs.iter_children_with_type("Type")
.unwrap() { .nth(0)
.unwrap() {
contents.trim() contents.trim()
} else { } else {
return Err(PsyParseError::UnknownError); return Err(PsyParseError::UnknownError);
@ -291,12 +295,12 @@ fn parse_world(tree: &DataTree) -> Result<XYZ, PsyParseError> {
}; };
match bgs_type { match bgs_type {
"Color" => { "Color" => {
if let Some(&DataTree::Leaf { contents, .. }) = bgs.iter_children_with_type("Color") if let Some(&DataTree::Leaf { contents, .. }) =
.nth(0) { bgs.iter_children_with_type("Color")
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, .nth(0) {
ws_f32, if let IResult::Done(_, color) =
ws_f32))(contents.trim() closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.trim()
.as_bytes()) { .as_bytes()) {
// TODO: proper color space management, not just assuming // TODO: proper color space management, not just assuming
// rec.709. // rec.709.
background_color = XYZ::from_tuple(rec709e_to_xyz(color)); background_color = XYZ::from_tuple(rec709e_to_xyz(color));
@ -311,7 +315,22 @@ fn parse_world(tree: &DataTree) -> Result<XYZ, PsyParseError> {
_ => return Err(PsyParseError::UnknownError), _ => return Err(PsyParseError::UnknownError),
} }
return Ok(background_color); // Parse light sources
for child in tree.iter_children() {
match child {
&DataTree::Internal { type_name, .. } if type_name == "DistantDiskLight" => {
lights.push(Box::new(parse_distant_disk_light(&child)?));
}
_ => {}
}
}
// Build and return the world
return Ok(World {
background_color: background_color,
lights: lights,
});
} else { } else {
return Err(PsyParseError::UnknownError); return Err(PsyParseError::UnknownError);
} }
@ -321,23 +340,24 @@ fn parse_world(tree: &DataTree) -> Result<XYZ, PsyParseError> {
pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> { pub fn parse_matrix(contents: &str) -> Result<Matrix4x4, PsyParseError> {
if let IResult::Done(_, ns) = closure!(terminated!(tuple!(ws_f32, if let IResult::Done(_, ns) =
ws_f32, closure!(terminated!(tuple!(ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32, ws_f32,
ws_f32), ws_f32,
nom::eof))(contents.as_bytes()) { ws_f32),
nom::eof))(contents.as_bytes()) {
return Ok(Matrix4x4::new_from_values(ns.0, return Ok(Matrix4x4::new_from_values(ns.0,
ns.4, ns.4,
ns.8, ns.8,

View File

@ -4,14 +4,70 @@ use std::result::Result;
use nom::IResult; use nom::IResult;
use math::Vector;
use color::{XYZ, rec709e_to_xyz}; use color::{XYZ, rec709e_to_xyz};
use light::{SphereLight, RectangleLight}; use light::{DistantDiskLight, SphereLight, RectangleLight};
use super::basics::ws_f32; use super::basics::ws_f32;
use super::DataTree; use super::DataTree;
use super::psy::PsyParseError; use super::psy::PsyParseError;
pub fn parse_distant_disk_light(tree: &DataTree) -> Result<DistantDiskLight, PsyParseError> {
if let &DataTree::Internal { ref children, .. } = tree {
let mut radii = Vec::new();
let mut directions = Vec::new();
let mut colors = Vec::new();
// Parse
for child in children.iter() {
match child {
// Radius
&DataTree::Leaf { type_name, contents } if type_name == "Radius" => {
if let IResult::Done(_, radius) = ws_f32(contents.as_bytes()) {
radii.push(radius);
} else {
// Found radius, but its contents is not in the right format
return Err(PsyParseError::UnknownError);
}
}
// Direction
&DataTree::Leaf { type_name, contents } if type_name == "Direction" => {
if let IResult::Done(_, direction) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) {
directions.push(Vector::new(direction.0, direction.1, direction.2));
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError);
}
}
// Color
&DataTree::Leaf { type_name, contents } if type_name == "Color" => {
if let IResult::Done(_, color) =
closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) {
// TODO: handle color space conversions properly.
// Probably will need a special color type with its
// own parser...?
colors.push(XYZ::from_tuple(rec709e_to_xyz(color)));
} else {
// Found color, but its contents is not in the right format
return Err(PsyParseError::UnknownError);
}
}
_ => {}
}
}
return Ok(DistantDiskLight::new(radii, directions, colors));
} else {
return Err(PsyParseError::UnknownError);
}
}
pub fn parse_sphere_light(tree: &DataTree) -> Result<SphereLight, PsyParseError> { pub fn parse_sphere_light(tree: &DataTree) -> Result<SphereLight, PsyParseError> {
if let &DataTree::Internal { ref children, .. } = tree { if let &DataTree::Internal { ref children, .. } = tree {
let mut radii = Vec::new(); let mut radii = Vec::new();
@ -32,9 +88,8 @@ pub fn parse_sphere_light(tree: &DataTree) -> Result<SphereLight, PsyParseError>
// Color // Color
&DataTree::Leaf { type_name, contents } if type_name == "Color" => { &DataTree::Leaf { type_name, contents } if type_name == "Color" => {
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, if let IResult::Done(_, color) =
ws_f32, closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) {
ws_f32))(contents.as_bytes()) {
// TODO: handle color space conversions properly. // TODO: handle color space conversions properly.
// Probably will need a special color type with its // Probably will need a special color type with its
// own parser...? // own parser...?
@ -66,7 +121,7 @@ pub fn parse_rectangle_light(tree: &DataTree) -> Result<RectangleLight, PsyParse
// Dimensions // Dimensions
&DataTree::Leaf { type_name, contents } if type_name == "Dimensions" => { &DataTree::Leaf { type_name, contents } if type_name == "Dimensions" => {
if let IResult::Done(_, radius) = if let IResult::Done(_, radius) =
closure!(tuple!(ws_f32, ws_f32))(contents.as_bytes()) { closure!(tuple!(ws_f32, ws_f32))(contents.as_bytes()) {
dimensions.push(radius); dimensions.push(radius);
} else { } else {
// Found dimensions, but its contents is not in the right format // Found dimensions, but its contents is not in the right format
@ -76,9 +131,8 @@ pub fn parse_rectangle_light(tree: &DataTree) -> Result<RectangleLight, PsyParse
// Color // Color
&DataTree::Leaf { type_name, contents } if type_name == "Color" => { &DataTree::Leaf { type_name, contents } if type_name == "Color" => {
if let IResult::Done(_, color) = closure!(tuple!(ws_f32, if let IResult::Done(_, color) =
ws_f32, closure!(tuple!(ws_f32, ws_f32, ws_f32))(contents.as_bytes()) {
ws_f32))(contents.as_bytes()) {
// TODO: handle color space conversions properly. // TODO: handle color space conversions properly.
// Probably will need a special color type with its // Probably will need a special color type with its
// own parser...? // own parser...?

View File

@ -35,9 +35,8 @@ pub fn parse_mesh_surface(tree: &DataTree) -> Result<TriangleMesh, PsyParseError
// Collect verts for this time sample // Collect verts for this time sample
let mut vert_count = 0; let mut vert_count = 0;
while let IResult::Done(remaining, vert) = closure!(tuple!(ws_f32, while let IResult::Done(remaining, vert) =
ws_f32, closure!(tuple!(ws_f32, ws_f32, ws_f32))(raw_text) {
ws_f32))(raw_text) {
raw_text = remaining; raw_text = remaining;
verts.push(Point::new(vert.0, vert.1, vert.2)); verts.push(Point::new(vert.0, vert.1, vert.2));

View File

@ -113,9 +113,10 @@ impl Renderer {
(halton::sample(0, offset + si as u32), (halton::sample(0, offset + si as u32),
halton::sample(1, offset + si as u32)), halton::sample(1, offset + si as u32)),
halton::sample(2, offset + si as u32), halton::sample(2, offset + si as u32),
map_0_1_to_wavelength( map_0_1_to_wavelength(halton::sample(3,
halton::sample(3, offset + si as u32) offset +
), si as
u32)),
offset + si as u32); offset + si as u32);
paths.push(path); paths.push(path);
rays.push(ray); rays.push(ray);
@ -249,17 +250,17 @@ impl LightPath {
lds_offset: u32) lds_offset: u32)
-> (LightPath, Ray) { -> (LightPath, Ray) {
(LightPath { (LightPath {
pixel_co: pixel_co, pixel_co: pixel_co,
lds_offset: lds_offset, lds_offset: lds_offset,
dim_offset: 6, dim_offset: 6,
round: 0, round: 0,
time: time, time: time,
wavelength: wavelength, wavelength: wavelength,
interaction: surface::SurfaceIntersection::Miss, interaction: surface::SurfaceIntersection::Miss,
light_attenuation: SpectralSample::from_value(1.0, wavelength), light_attenuation: SpectralSample::from_value(1.0, wavelength),
pending_color_addition: SpectralSample::new(wavelength), pending_color_addition: SpectralSample::new(wavelength),
color: SpectralSample::new(wavelength), color: SpectralSample::new(wavelength),
}, },
scene.camera.generate_ray(image_plane_co.0, scene.camera.generate_ray(image_plane_co.0,
image_plane_co.1, image_plane_co.1,
@ -297,13 +298,14 @@ impl LightPath {
let light_n = self.next_lds_samp(); let light_n = self.next_lds_samp();
let light_uvw = (self.next_lds_samp(), self.next_lds_samp(), self.next_lds_samp()); let light_uvw = (self.next_lds_samp(), self.next_lds_samp(), self.next_lds_samp());
xform_stack.clear(); xform_stack.clear();
if let Some((light_color, shadow_vec, light_pdf, light_sel_pdf)) = scene.root if let Some((light_color, shadow_vec, light_pdf, light_sel_pdf)) =
.sample_lights(xform_stack, scene.root
light_n, .sample_lights(xform_stack,
light_uvw, light_n,
self.wavelength, light_uvw,
self.time, self.wavelength,
isect) { self.time,
isect) {
// Calculate and store the light that will be contributed // Calculate and store the light that will be contributed
// to the film plane if the light is not in shadow. // to the film plane if the light is not in shadow.
self.pending_color_addition = { self.pending_color_addition = {
@ -327,7 +329,7 @@ impl LightPath {
} }
} else { } else {
// Didn't hit anything, so background color // Didn't hit anything, so background color
self.color += scene.background_color.to_spectral_sample(self.wavelength) * self.color += scene.world.background_color.to_spectral_sample(self.wavelength) *
self.light_attenuation; self.light_attenuation;
return false; return false;
} }

View File

@ -1,12 +1,13 @@
use assembly::Assembly; use assembly::Assembly;
use camera::Camera; use camera::Camera;
use color::XYZ; use world::World;
#[derive(Debug)] #[derive(Debug)]
pub struct Scene { pub struct Scene {
pub name: Option<String>, pub name: Option<String>,
pub background_color: XYZ,
pub camera: Camera, pub camera: Camera,
pub world: World,
pub root: Assembly, pub root: Assembly,
} }

8
src/world.rs Normal file
View File

@ -0,0 +1,8 @@
use color::XYZ;
use light::WorldLightSource;
#[derive(Debug)]
pub struct World {
pub background_color: XYZ,
pub lights: Vec<Box<WorldLightSource>>,
}