Refactoring
This commit is contained in:
parent
226ff88874
commit
3226e9c965
10 changed files with 316 additions and 95 deletions
|
|
@ -8,6 +8,7 @@ use core::ops::{
|
|||
Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
|
||||
};
|
||||
use num_traits::{AsPrimitive, FloatConst, Num, Signed, Zero};
|
||||
use core::fmt;
|
||||
|
||||
pub trait MulAdd<M = Self, A = Self> {
|
||||
type Output;
|
||||
|
|
@ -35,6 +36,45 @@ pub struct Point<T, const N: usize>(pub [T; N]);
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Normal<T, const N: usize>(pub [T; N]);
|
||||
|
||||
impl<T: fmt::Display, const N: usize> fmt::Display for Vector<T, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Vector(")?;
|
||||
for (i, item) in (&self.0).into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", item)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, const N: usize> fmt::Display for Point<T, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Point(")?;
|
||||
for (i, item) in (&self.0).into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", item)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display, const N: usize> fmt::Display for Normal<T, N> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Normal(")?;
|
||||
for (i, item) in (&self.0).into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
write!(f, "{}", item)?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_tuple_core {
|
||||
($Struct:ident) => {
|
||||
|
|
|
|||
|
|
@ -194,3 +194,10 @@ pub enum Material {
|
|||
Mix(MixMaterial),
|
||||
}
|
||||
|
||||
|
||||
// TODO: THIS IS A HACK JUST FOR TESTING
|
||||
impl PartialEq for Material {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
core::mem::discriminant(self) == core::mem::discriminant(other)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
use crate::core::scene::BasicScene;
|
||||
use crate::globals::get_options;
|
||||
use crate::integrators::pipeline::render;
|
||||
use crate::Arena;
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use log::warn;
|
||||
use shared::core::camera::CameraTrait;
|
||||
use shared::core::geometry::{Point2f, Vector2f};
|
||||
use shared::core::interaction::InteractionTrait;
|
||||
use shared::core::primitive::PrimitiveTrait;
|
||||
use shared::core::sampler::CameraSample;
|
||||
use shared::spectra::{SampledWavelengths, LAMBDA_MAX, LAMBDA_MIN};
|
||||
use shared::Float;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
||||
let media = scene.create_media();
|
||||
let textures = scene.create_textures(arena);
|
||||
let lights = scene.create_lights()
|
||||
let (lights, _) = scene.create_lights(&textures, arena);
|
||||
let (named_materials, materials) = scene.create_materials(&textures, arena)?;
|
||||
let (aggregate, area_lights) =
|
||||
scene.create_aggregate(&textures, &named_materials, &materials, arena);
|
||||
|
|
@ -15,6 +24,90 @@ fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
|||
let film = camera.get_film();
|
||||
warn!("Creating integrator");
|
||||
let sampler = scene.get_sampler()?;
|
||||
let integrator = scene.create_integrator(camera, sampler, aggregate, lights, arena);
|
||||
let integrator = scene.create_integrator(camera.clone(), sampler.clone(), aggregate.clone(), lights, arena);
|
||||
let mut have_scattering = false;
|
||||
for sh in scene.shapes.lock().iter() {
|
||||
if !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty() {
|
||||
have_scattering = true;
|
||||
}
|
||||
}
|
||||
for sh in scene.animated_shapes.lock().iter() {
|
||||
if !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty() {
|
||||
have_scattering = true;
|
||||
}
|
||||
}
|
||||
|
||||
if get_options().pixel_material.is_some() {
|
||||
let lambda =
|
||||
SampledWavelengths::sample_uniform(0.5, LAMBDA_MIN as Float, LAMBDA_MAX as Float);
|
||||
let cs = CameraSample {
|
||||
p_film: Point2f::from(get_options().pixel_material.unwrap()) + Vector2f::new(0.5, 0.5),
|
||||
time: 0.5,
|
||||
p_lens: Point2f::new(0.5, 0.5),
|
||||
filter_weight: 1.,
|
||||
};
|
||||
|
||||
let Some(cr) = camera.generate_ray_differential(cs, &lambda) else {
|
||||
bail!("Unable to generate ray for pixel")
|
||||
};
|
||||
|
||||
let mut depth = 1;
|
||||
let mut ray = cr.ray;
|
||||
loop {
|
||||
if let Some(isect) = aggregate.intersect(&ray, Some(Float::INFINITY)) {
|
||||
let intr = isect.intr;
|
||||
if intr.material.is_null() {
|
||||
log::warn!("Ignoring material")
|
||||
} else {
|
||||
let world_from_render = camera.base().camera_transform.world_from_render;
|
||||
log::debug!("Intersection depth {}\n", depth);
|
||||
log::debug!(
|
||||
"World-space p: {}\n",
|
||||
world_from_render.apply_to_point(intr.p())
|
||||
);
|
||||
log::debug!(
|
||||
"World-space n: {}\n",
|
||||
world_from_render.apply_to_normal(intr.n())
|
||||
);
|
||||
log::debug!(
|
||||
"World-space ns: {}\n",
|
||||
world_from_render.apply_to_normal(intr.shading.n)
|
||||
);
|
||||
log::debug!("Distance from camera: {}\n", intr.p().distance(cr.ray.o));
|
||||
|
||||
let mut is_named = false;
|
||||
for (name, mtl) in &named_materials {
|
||||
if *mtl == unsafe { *intr.material.as_ref() } {
|
||||
log::debug!("Named material: {}\n\n", name);
|
||||
is_named = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if !is_named {
|
||||
// log::warn!("{}\n\n", intr.material.as_ref().to_str());
|
||||
// }
|
||||
//
|
||||
|
||||
depth += 1;
|
||||
ray = intr.spawn_ray(ray.d);
|
||||
}
|
||||
} else {
|
||||
if depth == 1 {
|
||||
bail!("No geometry visible from pixel")
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(
|
||||
&integrator,
|
||||
&integrator.base,
|
||||
&camera,
|
||||
sampler.as_ref(),
|
||||
arena,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use super::entities::*;
|
|||
use super::BasicScene;
|
||||
use crate::spectra::get_colorspace_device;
|
||||
use crate::utils::error::FileLoc;
|
||||
use crate::utils::normalize_utf8;
|
||||
use crate::utils::parameters::{ParameterDictionary, ParsedParameterVector};
|
||||
use crate::utils::parser::{ParserError, ParserTarget};
|
||||
use crate::Arena;
|
||||
|
|
@ -17,9 +16,14 @@ use shared::Float;
|
|||
use std::collections::{HashMap, HashSet};
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::sync::Arc;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
const MAX_TRANSFORMS: usize = 2;
|
||||
|
||||
fn normalize_utf8(input: &str) -> String {
|
||||
input.nfc().collect::<String>()
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
struct TransformSet {
|
||||
t: [Transform; MAX_TRANSFORMS],
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ use shared::core::shape::Shape;
|
|||
use shared::core::texture::{GPUFloatTexture, SpectrumType};
|
||||
use shared::lights::sampler::LightSampler;
|
||||
use shared::spectra::RGBColorSpace;
|
||||
use shared::textures::FloatConstantTexture;
|
||||
use shared::{Ptr, Transform};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -508,52 +509,165 @@ impl BasicScene {
|
|||
Ok((named_materials, materials))
|
||||
}
|
||||
|
||||
pub fn create_lights(&self, camera_transform: &CameraTransform, arena: &Arena) -> Vec<Light> {
|
||||
let state = self.light_state.lock();
|
||||
pub fn create_lights(
|
||||
&self,
|
||||
textures: &NamedTextures,
|
||||
arena: &Arena,
|
||||
) -> (Vec<Arc<Light>>, HashMap<usize, Vec<Arc<Light>>>) {
|
||||
let shape_entities = self.shapes.lock();
|
||||
let light_state = self.light_state.lock();
|
||||
let material_state = self.material_state.lock();
|
||||
|
||||
state
|
||||
.lights
|
||||
let mut all_lights: Vec<Arc<Light>> = Vec::new();
|
||||
let mut shape_index_to_area_lights: HashMap<usize, Vec<Arc<Light>>> = HashMap::new();
|
||||
|
||||
for (i, entity) in shape_entities.iter().enumerate() {
|
||||
let light_idx = match entity.light_index {
|
||||
Some(idx) => idx,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let material_name = match &entity.material {
|
||||
MaterialRef::Name(name) => {
|
||||
match material_state
|
||||
.named_materials
|
||||
.iter()
|
||||
.filter_map(|entity| {
|
||||
let render_from_light = entity.transformed_base.render_from_object.start_transform;
|
||||
.find(|(n, _)| n == name)
|
||||
{
|
||||
Some((_, mtl_entity)) => {
|
||||
match mtl_entity.parameters.get_one_string("type", "") {
|
||||
Ok(t) if !t.is_empty() => t,
|
||||
_ => {
|
||||
log::error!(
|
||||
"{}: named material '{}' missing type",
|
||||
entity.base.loc,
|
||||
name
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
log::error!(
|
||||
"{}: no named material '{}' defined.",
|
||||
entity.base.loc,
|
||||
name
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
MaterialRef::Index(idx) => match material_state.materials.get(*idx) {
|
||||
Some(mtl_entity) => mtl_entity.name.clone(),
|
||||
None => {
|
||||
log::error!("{}: material index {} out of bounds", entity.base.loc, idx);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
MaterialRef::None => String::new(),
|
||||
};
|
||||
|
||||
let medium = self
|
||||
.get_medium(&entity.medium, &entity.transformed_base.base.loc)
|
||||
.map(|m| *m);
|
||||
if material_name == "interface" || material_name == "none" || material_name.is_empty() {
|
||||
log::warn!(
|
||||
"{}: Ignoring area light specification for shape with \"interface\" material.",
|
||||
entity.base.loc
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
match crate::core::light::create_light(
|
||||
&entity.transformed_base.base.name,
|
||||
render_from_light,
|
||||
medium,
|
||||
&entity.transformed_base.base.parameters,
|
||||
&entity.transformed_base.base.loc,
|
||||
camera_transform.clone(),
|
||||
let shape_objects = match Shape::create(
|
||||
&entity.base.name,
|
||||
*entity.render_from_object,
|
||||
*entity.object_from_render,
|
||||
entity.reverse_orientation,
|
||||
entity.base.parameters.clone(),
|
||||
&textures.float_textures,
|
||||
entity.base.loc.clone(),
|
||||
arena,
|
||||
) {
|
||||
Ok(light) => Some(light),
|
||||
Ok(shapes) => shapes,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"{}: failed to create light: {}",
|
||||
entity.transformed_base.base.loc,
|
||||
e
|
||||
);
|
||||
None
|
||||
log::error!("{}: failed to create shape: {}", entity.base.loc, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
if shape_objects.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let alpha_tex = Self::get_alpha_texture(
|
||||
&entity.base.parameters,
|
||||
&entity.base.loc,
|
||||
&textures.float_textures,
|
||||
);
|
||||
let default_alpha = Arc::new(FloatTexture::default());
|
||||
let alpha_ref = alpha_tex.as_ref().unwrap_or(&default_alpha);
|
||||
|
||||
let inside = self.get_medium(&entity.inside_medium, &entity.base.loc);
|
||||
let outside = self.get_medium(&entity.outside_medium, &entity.base.loc);
|
||||
let medium_interface = MediumInterface {
|
||||
inside: inside
|
||||
.as_ref()
|
||||
.map(|m| Ptr::from(m.as_ref()))
|
||||
.unwrap_or(Ptr::null()),
|
||||
outside: outside
|
||||
.as_ref()
|
||||
.map(|m| Ptr::from(m.as_ref()))
|
||||
.unwrap_or(Ptr::null()),
|
||||
};
|
||||
|
||||
let al_entity = &light_state.area_lights[light_idx];
|
||||
|
||||
let film_cs = self.film_colorspace.lock();
|
||||
let colorspace_ref = al_entity
|
||||
.parameters
|
||||
.color_space
|
||||
.as_ref()
|
||||
.or(film_cs.as_ref());
|
||||
|
||||
let mut shape_lights: Vec<Arc<Light>> = Vec::new();
|
||||
|
||||
for shape in &shape_objects {
|
||||
let cs = colorspace_ref.map(|cs| cs.as_ref());
|
||||
match crate::core::light::create_area_light(
|
||||
*entity.render_from_object,
|
||||
None,
|
||||
&al_entity.parameters,
|
||||
&al_entity.loc,
|
||||
shape,
|
||||
alpha_ref,
|
||||
cs,
|
||||
arena,
|
||||
) {
|
||||
Ok(light) => {
|
||||
let light_arc = Arc::new(light);
|
||||
all_lights.push(Arc::clone(&light_arc));
|
||||
shape_lights.push(light_arc);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{}: failed to create area light: {}", al_entity.loc, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !shape_lights.is_empty() {
|
||||
shape_index_to_area_lights.insert(i, shape_lights);
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Finished area lights");
|
||||
|
||||
(all_lights, shape_index_to_area_lights)
|
||||
}
|
||||
|
||||
/// Create area lights for shapes that reference one. Produces a map from
|
||||
/// shape index to a vec of lights.
|
||||
/// Must be called after shapes are loaded but before upload_shapes.
|
||||
pub fn create_area_lights(
|
||||
&self,
|
||||
loaded_shapes: &[Vec<Shape>],
|
||||
shape_entities: &[ShapeSceneEntity],
|
||||
textures: &NamedTextures,
|
||||
arena: &Arena,
|
||||
) -> HashMap<usize, Vec<Light>> {
|
||||
let shape_entities = &self.shapes.lock();
|
||||
let light_state = self.light_state.lock();
|
||||
let mut shape_lights: HashMap<usize, Vec<Light>> = HashMap::new();
|
||||
|
||||
|
|
@ -681,7 +795,7 @@ impl BasicScene {
|
|||
sampler: Arc<Sampler>,
|
||||
aggregate: Arc<Primitive>,
|
||||
lights: Vec<Arc<Light>>,
|
||||
arena: &Arena
|
||||
arena: &Arena,
|
||||
) -> PathIntegrator {
|
||||
let integrator = &self.integrator.lock().clone().unwrap();
|
||||
PathIntegrator::create(
|
||||
|
|
@ -691,7 +805,7 @@ impl BasicScene {
|
|||
aggregate,
|
||||
lights,
|
||||
PathConfig::SIMPLE,
|
||||
arena
|
||||
arena,
|
||||
)
|
||||
.expect("Integrator creation has failed")
|
||||
}
|
||||
|
|
@ -1114,12 +1228,23 @@ impl BasicScene {
|
|||
textures: &HashMap<String, Arc<FloatTexture>>,
|
||||
) -> Option<Arc<FloatTexture>> {
|
||||
let name = params.get_texture("alpha");
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
if !name.is_empty() {
|
||||
match textures.get(&name) {
|
||||
Some(tex) => Some(tex.clone()),
|
||||
None => panic!("{:?}: Alpha texture '{}' not found", loc, name),
|
||||
None => panic!(
|
||||
"{}: couldn't find float texture '{}' for \"alpha\" parameter.",
|
||||
loc, name
|
||||
),
|
||||
}
|
||||
} else {
|
||||
let alpha = params.get_one_float("alpha", 1.0).unwrap();
|
||||
if alpha < 1.0 {
|
||||
Some(Arc::new(FloatTexture::Constant(FloatConstantTexture::new(
|
||||
alpha,
|
||||
))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl CreateIntegrator for PathIntegrator {
|
|||
) -> Result<PathIntegrator> {
|
||||
let _max_depth = parameters.get_one_int("maxdepth", 5)?;
|
||||
let _regularize = parameters.get_one_bool("regularize", false)?;
|
||||
let light_sampler = create_light_sampler("bvh", &lights, arena);
|
||||
let light_sampler = create_light_sampler("power", &lights, arena);
|
||||
let integrator = PathIntegrator::new(aggregate, lights, camera, light_sampler, config);
|
||||
Ok(integrator)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ pub fn render<T>(
|
|||
_base: &IntegratorBase,
|
||||
camera: &Camera,
|
||||
sampler_prototype: &Sampler,
|
||||
arena: Arc<Arena>,
|
||||
arena: &Arena,
|
||||
) where
|
||||
T: RayIntegratorTrait + Sync,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ pub mod mipmap;
|
|||
pub mod parallel;
|
||||
pub mod parameters;
|
||||
pub mod parser;
|
||||
pub mod strings;
|
||||
pub mod upload;
|
||||
|
||||
pub use error::FileLoc;
|
||||
|
|
@ -16,7 +15,6 @@ pub use file::{read_float_file, resolve_filename};
|
|||
pub use parameters::{
|
||||
ParameterDictionary, ParsedParameter, ParsedParameterVector, TextureParameterDictionary,
|
||||
};
|
||||
pub use strings::*;
|
||||
pub use mipmap::{MIPMap, MIPMapFilterOptions};
|
||||
pub use upload::{Upload, ArenaUpload};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,6 @@
|
|||
use crossbeam_channel::{Receiver, bounded};
|
||||
use rayon::prelude::*;
|
||||
|
||||
pub fn init_parallel(n_threads: usize) {
|
||||
let threads = if n_threads == 0 {
|
||||
num_cpus::get()
|
||||
} else {
|
||||
n_threads
|
||||
};
|
||||
|
||||
if let Err(e) = rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(threads)
|
||||
.build_global()
|
||||
{
|
||||
eprintln!("Warning: Rayon thread pool already initialized: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_system_cores() -> usize {
|
||||
num_cpus::get()
|
||||
}
|
||||
|
||||
pub fn max_concurrency() -> usize {
|
||||
rayon::current_num_threads()
|
||||
}
|
||||
|
||||
pub fn parallel_for<F>(start: i64, end: i64, func: F)
|
||||
where
|
||||
F: Fn(i64) + Sync + Send,
|
||||
{
|
||||
(start..end).into_par_iter().for_each(|i| func(i));
|
||||
}
|
||||
|
||||
pub fn parallel_for_2d<F>(start_x: i64, end_x: i64, start_y: i64, end_y: i64, func: F)
|
||||
where
|
||||
F: Fn(i64, i64) + Sync + Send,
|
||||
{
|
||||
(start_y..end_y).into_par_iter().for_each(|y| {
|
||||
(start_x..end_x).into_par_iter().for_each(|x| {
|
||||
func(x, y);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AsyncJob<T> {
|
||||
receiver: Receiver<T>,
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
pub fn normalize_utf8(input: &str) -> String {
|
||||
input.nfc().collect::<String>()
|
||||
}
|
||||
Loading…
Reference in a new issue