192 lines
5.9 KiB
Rust
192 lines
5.9 KiB
Rust
use std::iter;
|
|
|
|
use crate::{
|
|
accel::ray_code,
|
|
color::{rec709_to_xyz, Color},
|
|
lerp::lerp_slice,
|
|
math::Matrix4x4,
|
|
ray::{RayBatch, RayStack},
|
|
scene::{Assembly, InstanceType, Object},
|
|
shading::{SimpleSurfaceShader, SurfaceShader},
|
|
surface::SurfaceIntersection,
|
|
transform_stack::TransformStack,
|
|
};
|
|
|
|
pub struct Tracer<'a> {
|
|
ray_trace_count: u64,
|
|
ray_stack: RayStack,
|
|
inner: TracerInner<'a>,
|
|
}
|
|
|
|
impl<'a> Tracer<'a> {
|
|
pub fn from_assembly(assembly: &'a Assembly) -> Tracer<'a> {
|
|
Tracer {
|
|
ray_trace_count: 0,
|
|
ray_stack: RayStack::new(),
|
|
inner: TracerInner {
|
|
root: assembly,
|
|
xform_stack: TransformStack::new(),
|
|
isects: Vec::new(),
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn trace<'b>(&'b mut self, rays: &mut RayBatch) -> &'b [SurfaceIntersection] {
|
|
self.ray_trace_count += rays.len() as u64;
|
|
self.inner.trace(rays, &mut self.ray_stack)
|
|
}
|
|
|
|
pub fn rays_traced(&self) -> u64 {
|
|
self.ray_trace_count
|
|
}
|
|
}
|
|
|
|
struct TracerInner<'a> {
|
|
root: &'a Assembly<'a>,
|
|
xform_stack: TransformStack,
|
|
isects: Vec<SurfaceIntersection>,
|
|
}
|
|
|
|
impl<'a> TracerInner<'a> {
|
|
fn trace<'b>(
|
|
&'b mut self,
|
|
rays: &mut RayBatch,
|
|
ray_stack: &mut RayStack,
|
|
) -> &'b [SurfaceIntersection] {
|
|
ray_stack.clear();
|
|
|
|
// Ready the isects
|
|
self.isects.clear();
|
|
self.isects.reserve(rays.len());
|
|
self.isects
|
|
.extend(iter::repeat(SurfaceIntersection::Miss).take(rays.len()));
|
|
|
|
// Prep the accel part of the rays.
|
|
{
|
|
let ident = Matrix4x4::new();
|
|
for i in 0..rays.len() {
|
|
rays.update_local(i, &ident);
|
|
}
|
|
}
|
|
|
|
// Divide the rays into 8 different lanes by direction.
|
|
ray_stack.ensure_lane_count(8);
|
|
for i in 0..rays.len() {
|
|
ray_stack.push_ray_index(i, ray_code(rays.dir(i)));
|
|
}
|
|
ray_stack.push_lanes_to_tasks(&[0, 1, 2, 3, 4, 5, 6, 7]);
|
|
|
|
// Trace each of the 8 lanes separately.
|
|
while !ray_stack.is_empty() {
|
|
self.trace_assembly(self.root, rays, ray_stack);
|
|
}
|
|
|
|
&self.isects
|
|
}
|
|
|
|
fn trace_assembly<'b>(
|
|
&'b mut self,
|
|
assembly: &Assembly,
|
|
rays: &mut RayBatch,
|
|
ray_stack: &mut RayStack,
|
|
) {
|
|
assembly
|
|
.object_accel
|
|
.traverse(rays, ray_stack, |idx_range, rays, ray_stack| {
|
|
let inst = &assembly.instances[idx_range.start];
|
|
|
|
// Transform rays if needed
|
|
if let Some((xstart, xend)) = inst.transform_indices {
|
|
// Push transforms to stack
|
|
self.xform_stack.push(&assembly.xforms[xstart..xend]);
|
|
|
|
// Do transforms
|
|
// TODO: re-divide rays based on direction (maybe?).
|
|
let xforms = self.xform_stack.top();
|
|
ray_stack.do_next_task(|ray_idx| {
|
|
let t = rays.time(ray_idx);
|
|
rays.update_local(ray_idx, &lerp_slice(xforms, t));
|
|
});
|
|
ray_stack.duplicate_next_task();
|
|
}
|
|
|
|
// Trace rays
|
|
match inst.instance_type {
|
|
InstanceType::Object => {
|
|
self.trace_object(
|
|
&assembly.objects[inst.data_index],
|
|
inst.surface_shader_index
|
|
.map(|i| assembly.surface_shaders[i]),
|
|
rays,
|
|
ray_stack,
|
|
);
|
|
}
|
|
|
|
InstanceType::Assembly => {
|
|
self.trace_assembly(&assembly.assemblies[inst.data_index], rays, ray_stack);
|
|
}
|
|
}
|
|
|
|
// Un-transform rays if needed
|
|
if inst.transform_indices.is_some() {
|
|
// Pop transforms off stack
|
|
self.xform_stack.pop();
|
|
|
|
// Undo transforms
|
|
let xforms = self.xform_stack.top();
|
|
if !xforms.is_empty() {
|
|
ray_stack.pop_do_next_task(|ray_idx| {
|
|
let t = rays.time(ray_idx);
|
|
rays.update_local(ray_idx, &lerp_slice(xforms, t));
|
|
});
|
|
} else {
|
|
let ident = Matrix4x4::new();
|
|
ray_stack.pop_do_next_task(|ray_idx| {
|
|
rays.update_local(ray_idx, &ident);
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
fn trace_object<'b>(
|
|
&'b mut self,
|
|
obj: &Object,
|
|
surface_shader: Option<&dyn SurfaceShader>,
|
|
rays: &mut RayBatch,
|
|
ray_stack: &mut RayStack,
|
|
) {
|
|
match *obj {
|
|
Object::Surface(surface) => {
|
|
let unassigned_shader = SimpleSurfaceShader::Emit {
|
|
color: Color::new_xyz(rec709_to_xyz((1.0, 0.0, 1.0))),
|
|
};
|
|
let shader = surface_shader.unwrap_or(&unassigned_shader);
|
|
|
|
surface.intersect_rays(
|
|
rays,
|
|
ray_stack,
|
|
&mut self.isects,
|
|
shader,
|
|
self.xform_stack.top(),
|
|
);
|
|
}
|
|
|
|
Object::SurfaceLight(surface) => {
|
|
// Lights don't use shaders
|
|
let bogus_shader = SimpleSurfaceShader::Emit {
|
|
color: Color::new_xyz(rec709_to_xyz((1.0, 0.0, 1.0))),
|
|
};
|
|
|
|
surface.intersect_rays(
|
|
rays,
|
|
ray_stack,
|
|
&mut self.isects,
|
|
&bogus_shader,
|
|
self.xform_stack.top(),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|