use std::sync::Arc;
use rayon::prelude::*;
use rayon::current_num_threads;
use nalgebra::coordinates::XYZW;
use ::utils::clamp;
use ::pixel::Pixel;
use ::mesh::{Mesh, Vertex};
use ::render::geometry::{FaceWinding, ClipVertex, ScreenVertex};
use ::render::framebuffer::FrameBuffer;
use ::render::uniform::Barycentric;
pub struct Pipeline<U, P> where P: Pixel, U: Send + Sync {
framebuffer: FrameBuffer<P>,
uniforms: U,
}
pub struct VertexShader<'a, V, U: 'a, P: 'static> where V: Send + Sync,
U: Send + Sync,
P: Pixel {
mesh: Arc<Mesh<V>>,
uniforms: &'a U,
framebuffer: &'a mut FrameBuffer<P>,
}
pub struct FragmentShader<'a, V, U: 'a, K, P: 'static> where V: Send + Sync,
U: Send + Sync,
K: Send + Sync + Barycentric,
P: Pixel {
mesh: Arc<Mesh<V>>,
uniforms: &'a U,
framebuffer: &'a mut FrameBuffer<P>,
screen_vertices: Arc<Vec<ScreenVertex<K>>>,
cull_faces: Option<FaceWinding>,
blend_func: Arc<Box<Fn(P, P) -> P + Send + Sync>>,
}
impl<U, P> Pipeline<U, P> where U: Send + Sync,
P: Pixel {
pub fn new(framebuffer: FrameBuffer<P>, uniforms: U) -> Pipeline<U, P> {
assert!(framebuffer.width() > 0, "Framebuffer must have a non-zero width");
assert!(framebuffer.height() > 0, "Framebuffer must have a non-zero height");
Pipeline {
framebuffer: framebuffer,
uniforms: uniforms,
}
}
pub fn render_mesh<V>(&mut self, mesh: Arc<Mesh<V>>) -> VertexShader<V, U, P> where V: Send + Sync {
VertexShader {
mesh: mesh,
uniforms: &self.uniforms,
framebuffer: &mut self.framebuffer,
}
}
pub fn uniforms(&self) -> &U { &self.uniforms }
pub fn uniforms_mut(&mut self) -> &mut U { &mut self.uniforms }
pub fn framebuffer(&self) -> &FrameBuffer<P> { &self.framebuffer }
pub fn framebuffer_mut(&mut self) -> &mut FrameBuffer<P> { &mut self.framebuffer }
}
impl<'a, V, U: 'a, P: 'static> VertexShader<'a, V, U, P> where V: Send + Sync,
U: Send + Sync,
P: Pixel {
pub fn duplicate<'b>(&'b mut self) -> VertexShader<'b, V, U, P> where 'a: 'b {
VertexShader {
mesh: self.mesh.clone(),
uniforms: self.uniforms,
framebuffer: self.framebuffer
}
}
pub fn run<S, K>(self, vertex_shader: S) -> FragmentShader<'a, V, U, K, P> where S: Fn(&Vertex<V>, &U) -> ClipVertex<K> + Sync,
K: Send + Sync + Barycentric {
let VertexShader {
mesh,
uniforms,
framebuffer
} = self;
let viewport = framebuffer.viewport();
let vertices_per_thread = mesh.indices.len() / current_num_threads();
let screen_vertices = mesh.vertices.par_iter()
.with_min_len(vertices_per_thread)
.map(|vertex| {
vertex_shader(vertex, &*uniforms)
.normalize(viewport)
}).collect();
FragmentShader {
mesh: mesh,
uniforms: uniforms,
framebuffer: framebuffer,
screen_vertices: Arc::new(screen_vertices),
cull_faces: None,
blend_func: Arc::new(Box::new(|s, _| s)),
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum Fragment<P> where P: Sized + Pixel {
Discard,
Color(P)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineStyle {
Thin,
ThinAA,
}
impl Default for LineStyle {
fn default() -> LineStyle { LineStyle::ThinAA }
}
impl<'a, V, U: 'a, K, P: 'static> FragmentShader<'a, V, U, K, P> where V: Send + Sync,
U: Send + Sync,
K: Send + Sync + Barycentric,
P: Pixel {
pub fn duplicate<'b>(&'b mut self) -> FragmentShader<'b, V, U, K, P> where 'a: 'b {
FragmentShader {
mesh: self.mesh.clone(),
uniforms: self.uniforms,
framebuffer: self.framebuffer,
screen_vertices: self.screen_vertices.clone(),
cull_faces: self.cull_faces.clone(),
blend_func: self.blend_func.clone()
}
}
#[inline(always)]
pub fn cull_faces(&mut self, cull: Option<FaceWinding>) {
self.cull_faces = cull;
}
#[inline(always)]
pub fn set_blend_function<F>(&mut self, f: F) where F: Fn(P, P) -> P + Send + Sync + 'static {
self.blend_func = Arc::new(Box::new(f));
}
pub fn points<S>(self, fragment_shader: S) where S: Fn(&ScreenVertex<K>, &U) -> Fragment<P> + Send + Sync {
let FragmentShader {
mesh,
uniforms,
framebuffer,
screen_vertices,
blend_func,
..
} = self;
let blend_func = &*blend_func;
let empty_framebuffer = framebuffer.empty_clone();
let points_per_thread = mesh.indices.len() / current_num_threads();
let bb = (framebuffer.width() as f32,
framebuffer.height() as f32);
let partial_framebuffers = mesh.indices.par_iter().cloned().with_min_len(points_per_thread).fold(
|| { empty_framebuffer.empty_clone() }, |mut framebuffer: FrameBuffer<P>, index| {
let ref vertex = screen_vertices[index as usize];
let XYZW { x, y, z, .. } = *vertex.position;
if 0.0 <= x && x < bb.0 && 0.0 <= y && y < bb.1 && z > 0.0 {
let px = x as u32;
let py = y as u32;
if framebuffer.check_coordinate(px, py) {
let (fc, fd) = unsafe { framebuffer.pixel_depth_mut(px, py) };
if z < *fd {
match fragment_shader(vertex, &uniforms) {
Fragment::Color(c) => {
*fc = (*blend_func)(c, *fc);
*fd = z;
}
Fragment::Discard => ()
};
}
}
}
framebuffer
});
partial_framebuffers.reduce_with(|mut a, b| {
b.merge_into(&mut a, &blend_func);
a
}).map(|final_framebuffer| {
final_framebuffer.merge_into(framebuffer, &blend_func);
});
}
pub fn lines<S>(self, fragment_shader: S, style: LineStyle) where S: Fn(&ScreenVertex<K>, &U) -> Fragment<P> + Send + Sync {
let FragmentShader {
mesh,
uniforms,
framebuffer,
screen_vertices,
blend_func,
..
} = self;
let blend_func = &*blend_func;
let bb = (framebuffer.width() - 1,
framebuffer.height() - 1);
let empty_framebuffer = framebuffer.empty_clone();
let lines_per_thread = mesh.indices.len() / (2 * current_num_threads());
let partial_framebuffers = mesh.indices.par_chunks(2).with_min_len(lines_per_thread).filter(|line| line.len() == 2).fold(
|| { empty_framebuffer.empty_clone() }, |mut framebuffer: FrameBuffer<P>, line| {
let ref a = screen_vertices[line[0] as usize];
let ref b = screen_vertices[line[1] as usize];
let (x1, y1) = (a.position.x, a.position.y);
let (x2, y2) = (b.position.x, b.position.y);
let d = (x1 - x2).hypot(y1 - y2);
{
let plot_fragment = |x, y, alpha| {
if x >= 0 && y >= 0 {
let x = x as u32;
let y = y as u32;
if x <= bb.0 && y <= bb.1 {
let d1 = (x1 - x as f32).hypot(y1 - y as f32);
let t = d1 / d;
let position = a.position * (1.0 - t) + b.position * t;
if position.z > 0.0 {
if framebuffer.check_coordinate(x, y) {
let (fc, fd) = unsafe { framebuffer.pixel_depth_mut(x, y) };
if position.z < *fd {
let fragment = fragment_shader(&ScreenVertex {
position: position,
uniforms: Barycentric::interpolate((1.0 - t), &a.uniforms,
t, &b.uniforms,
0.0, &b.uniforms),
}, &uniforms);
match fragment {
Fragment::Color(c) => {
*fc = (*blend_func)(c.mul_alpha(alpha as f32), *fc);
*fd = 0.0;
}
Fragment::Discard => ()
};
}
}
}
}
}
};
match style {
LineStyle::Thin => {
::render::line::draw_line_bresenham(x1 as i64, y1 as i64, x2 as i64, y2 as i64, plot_fragment);
}
LineStyle::ThinAA => {
::render::line::draw_line_xiaolin_wu(x1 as f64, y1 as f64, x2 as f64, y2 as f64, plot_fragment);
}
}
}
framebuffer
}
);
partial_framebuffers.reduce_with(|mut a, b| {
b.merge_into(&mut a, &blend_func);
a
}).map(|final_framebuffer| {
final_framebuffer.merge_into(framebuffer, &blend_func);
});
}
pub fn wireframe<S>(self, fragment_shader: S, style: LineStyle) where S: Fn(&ScreenVertex<K>, &U) -> Fragment<P> + Send + Sync {
let FragmentShader {
mesh,
uniforms,
framebuffer,
screen_vertices,
blend_func,
..
} = self;
let blend_func = &*blend_func;
let bb = (framebuffer.width() - 1,
framebuffer.height() - 1);
let empty_framebuffer = framebuffer.empty_clone();
let triangles_per_thread = mesh.indices.len() / (3 * current_num_threads());
let partial_framebuffers = mesh.indices.par_chunks(3).with_min_len(triangles_per_thread).filter(|triangle| triangle.len() == 3).fold(
|| { empty_framebuffer.empty_clone() }, |mut framebuffer: FrameBuffer<P>, triangle| {
let ref a = screen_vertices[triangle[0] as usize];
let ref b = screen_vertices[triangle[1] as usize];
let ref c = screen_vertices[triangle[2] as usize];
for &(a, b) in &[(a, b), (b, c), (c, a)] {
let (x1, y1) = (a.position.x, a.position.y);
let (x2, y2) = (b.position.x, b.position.y);
let d = (x1 - x2).hypot(y1 - y2);
let plot_fragment = |x, y, alpha| {
if x >= 0 && y >= 0 {
let x = x as u32;
let y = y as u32;
if x <= bb.0 && y <= bb.1 {
let d1 = (x1 - x as f32).hypot(y1 - y as f32);
let t = d1 / d;
let position = a.position * (1.0 - t) + b.position * t;
if position.z > 0.0 {
if framebuffer.check_coordinate(x, y) {
let (fc, fd) = unsafe { framebuffer.pixel_depth_mut(x, y) };
if position.z < *fd {
let fragment = fragment_shader(&ScreenVertex {
position: position,
uniforms: Barycentric::interpolate((1.0 - t), &a.uniforms,
t, &b.uniforms,
0.0, &b.uniforms),
}, &uniforms);
match fragment {
Fragment::Color(c) => {
*fc = (*blend_func)(c.mul_alpha(alpha as f32), *fc);
*fd = 0.0;
}
Fragment::Discard => ()
};
}
}
}
}
}
};
match style {
LineStyle::Thin => {
::render::line::draw_line_bresenham(x1 as i64, y1 as i64, x2 as i64, y2 as i64, plot_fragment);
}
LineStyle::ThinAA => {
::render::line::draw_line_xiaolin_wu(x1 as f64, y1 as f64, x2 as f64, y2 as f64, plot_fragment);
}
}
}
framebuffer
});
partial_framebuffers.reduce_with(|mut a, b| {
b.merge_into(&mut a, &blend_func);
a
}).map(|final_framebuffer| {
final_framebuffer.merge_into(framebuffer, &blend_func);
});
}
pub fn triangles<S>(self, fragment_shader: S) where S: Fn(&ScreenVertex<K>, &U) -> Fragment<P> + Send + Sync {
let FragmentShader {
mesh,
uniforms,
framebuffer,
screen_vertices,
cull_faces,
blend_func
} = self;
let blend_func = &*blend_func;
let bb = (framebuffer.width() - 1,
framebuffer.height() - 1);
let empty_framebuffer = framebuffer.empty_clone();
let triangles_per_thread = mesh.indices.len() / (3 * current_num_threads());
let partial_framebuffers = mesh.indices.par_chunks(3).with_min_len(triangles_per_thread).filter(|triangle| {
if triangle.len() == 3 {
if let Some(winding) = cull_faces {
let ref a = screen_vertices[triangle[0] as usize];
let ref b = screen_vertices[triangle[1] as usize];
let ref c = screen_vertices[triangle[2] as usize];
let (x1, y1) = (a.position.x, a.position.y);
let (x2, y2) = (b.position.x, b.position.y);
let (x3, y3) = (c.position.x, c.position.y);
let area2 = -x2 * y1 + 2.0 * x3 * y1 + x1 * y2 - x3 * y2 + 2.0 * x1 * y3 + x2 * y3;
if winding == if area2.is_sign_negative() { FaceWinding::Clockwise } else { FaceWinding::CounterClockwise } {
true
} else {
false
}
} else {
true
}
} else {
false
}
}).fold(|| { empty_framebuffer.empty_clone() }, |mut framebuffer: FrameBuffer<P>, triangle| {
let ref a = screen_vertices[triangle[0] as usize];
let ref b = screen_vertices[triangle[1] as usize];
let ref c = screen_vertices[triangle[2] as usize];
let (x1, y1) = (a.position.x, a.position.y);
let (x2, y2) = (b.position.x, b.position.y);
let (x3, y3) = (c.position.x, c.position.y);
let min_x: u32 = clamp(x1.min(x2).min(x3).floor() as u32, 0, bb.0);
let max_x: u32 = clamp(x1.max(x2).max(x3).ceil() as u32, 0, bb.0);
let min_y: u32 = clamp(y1.min(y2).min(y3).floor() as u32, 0, bb.1);
let max_y: u32 = clamp(y1.max(y2).max(y3).ceil() as u32, 0, bb.1);
let mut py = min_y;
while py <= max_y {
let mut px = min_x;
while px <= max_x {
let (x, y) = (px as f32 + 0.5,
py as f32 + 0.5);
let det = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
let u = ((y2 - y3) * (x - x3) + (x3 - x2) * (y - y3)) / det;
let v = ((y3 - y1) * (x - x3) + (x1 - x3) * (y - y3)) / det;
let w = 1.0 - u - v;
if u >= 0.0 && v >= 0.0 && w >= 0.0 {
let position = a.position * u + b.position * v + c.position * w;
if position.z > 0.0 {
let (fc, fd) = unsafe { framebuffer.pixel_depth_mut(px, py) };
if position.z < *fd {
let fragment = fragment_shader(&ScreenVertex {
position: position,
uniforms: Barycentric::interpolate(u, &a.uniforms,
v, &b.uniforms,
w, &c.uniforms),
}, &uniforms);
match fragment {
Fragment::Color(c) => {
*fc = (*blend_func)(c, *fc);
*fd = position.z;
}
Fragment::Discard => ()
};
}
}
}
px += 1;
}
py += 1;
}
framebuffer
});
partial_framebuffers.reduce_with(|mut a, b| {
b.merge_into(&mut a, &blend_func);
a
}).map(|final_framebuffer| {
final_framebuffer.merge_into(framebuffer, &blend_func);
});
}
}