psychopath/src/surface/triangle_mesh.rs
Nathan Vegdahl 011405e131 Implemented robust ray origin calculation for bounced rays.
We take a small performance hit for this, but given that it's
making things meaningfully more correct I feel like it's more
than worth it.
2017-06-19 22:28:44 -07:00

146 lines
5.6 KiB
Rust

#![allow(dead_code)]
use mem_arena::MemArena;
use accel::BVH;
use bbox::BBox;
use boundable::Boundable;
use color::XYZ;
use fp_utils::fp_gamma;
use lerp::{lerp, lerp_slice, lerp_slice_with};
use math::{Point, Matrix4x4, cross};
use ray::{Ray, AccelRay};
use shading::surface_closure::{SurfaceClosureUnion, GTRClosure, LambertClosure};
use super::{Surface, SurfaceIntersection, SurfaceIntersectionData};
use super::triangle;
#[derive(Copy, Clone, Debug)]
pub struct TriangleMesh<'a> {
time_samples: usize,
geo: &'a [(Point, Point, Point)],
indices: &'a [usize],
accel: BVH<'a>,
}
impl<'a> TriangleMesh<'a> {
pub fn from_triangles<'b>(
arena: &'b MemArena,
time_samples: usize,
triangles: Vec<(Point, Point, Point)>,
) -> TriangleMesh<'b> {
assert!(triangles.len() % time_samples == 0);
let mut indices: Vec<usize> = (0..(triangles.len() / time_samples))
.map(|n| n * time_samples)
.collect();
let bounds = {
let mut bounds = Vec::new();
for tri in triangles.iter() {
let minimum = tri.0.min(tri.1.min(tri.2));
let maximum = tri.0.max(tri.1.max(tri.2));
bounds.push(BBox::from_points(minimum, maximum));
}
bounds
};
let accel = BVH::from_objects(arena, &mut indices[..], 3, |tri_i| {
&bounds[*tri_i..(*tri_i + time_samples)]
});
TriangleMesh {
time_samples: time_samples,
geo: arena.copy_slice(&triangles),
indices: arena.copy_slice(&indices),
accel: accel,
}
}
}
impl<'a> Boundable for TriangleMesh<'a> {
fn bounds<'b>(&'b self) -> &'b [BBox] {
self.accel.bounds()
}
}
impl<'a> Surface for TriangleMesh<'a> {
fn intersect_rays(
&self,
accel_rays: &mut [AccelRay],
wrays: &[Ray],
isects: &mut [SurfaceIntersection],
space: &[Matrix4x4],
) {
self.accel
.traverse(
&mut accel_rays[..], &self.indices, |tri_i, rs| {
for r in rs {
let wr = &wrays[r.id as usize];
let tri = lerp_slice_with(
&self.geo[*tri_i..(*tri_i + self.time_samples)],
wr.time,
|a, b, t| (lerp(a.0, b.0, t), lerp(a.1, b.1, t), lerp(a.2, b.2, t)),
);
// TODO: when there's no transforms, we don't have to
// transform the triangles at all.
let mat_space = if space.len() > 0 {
lerp_slice(space, wr.time)
} else {
Matrix4x4::new()
};
let mat_inv = mat_space.inverse();
let tri = (tri.0 * mat_inv, tri.1 * mat_inv, tri.2 * mat_inv);
if let Some((t, b0, b1, b2)) = triangle::intersect_ray(wr, tri) {
if t < r.max_t {
if r.is_occlusion() {
isects[r.id as usize] = SurfaceIntersection::Occlude;
r.mark_done();
} else {
// Calculate intersection point and error magnitudes
let pos = ((tri.0.into_vector() * b0)
+ (tri.1.into_vector() * b1)
+ (tri.2.into_vector() * b2)).into_point();
let pos_err = ((tri.0.into_vector().abs() * b0)
+ (tri.1.into_vector().abs() * b1)
+ (tri.2.into_vector().abs() * b2))
* fp_gamma(7);
// Fill in intersection data
isects[r.id as usize] = SurfaceIntersection::Hit {
intersection_data: SurfaceIntersectionData {
incoming: wr.dir,
t: t,
pos: pos,
pos_err: pos_err,
nor: cross(tri.0 - tri.1, tri.0 - tri.2)
.into_normal(), // TODO
nor_g: cross(tri.0 - tri.1, tri.0 - tri.2)
.into_normal(),
uv: (0.0, 0.0), // TODO
local_space: mat_space,
},
// TODO: get surface closure from surface shader.
closure: SurfaceClosureUnion::LambertClosure(
LambertClosure::new(XYZ::new(0.8, 0.8, 0.8))
),
// closure:
// SurfaceClosureUnion::GTRClosure(
// GTRClosure::new(XYZ::new(0.8, 0.8, 0.8),
// 0.1,
// 2.0,
// 1.0)),
};
r.max_t = t;
}
}
}
}
}
);
}
}