use euc::{buffer::Buffer2d, rasterizer, Pipeline, Target}; use std::path::Path; use vek::*; struct Teapot<'a> { mvp: Mat4, positions: &'a [Vec4], normals: &'a [Vec3], light_dir: Vec3, } impl<'a> Pipeline for Teapot<'a> { type Vertex = u32; // Vertex index type VsOut = Vec3; // Normal type Pixel = u32; // BGRA #[inline(always)] fn vert(&self, v_index: &Self::Vertex) -> ([f32; 4], Self::VsOut) { let v_index = *v_index as usize; // Find vertex position ( // Calculate vertex position in camera space (self.mvp * self.positions[v_index]).into_array(), // Find vertex normal self.normals[v_index], ) } #[inline(always)] fn frag(&self, norm: &Self::VsOut) -> Self::Pixel { let ambient = 0.2; let diffuse = norm.dot(self.light_dir).max(0.0) * 0.5; let specular = self .light_dir .reflected(Vec3::from(self.mvp * Vec4::from(*norm)).normalized()) .dot(-Vec3::unit_z()) .powf(20.0); let light = ambient + diffuse + specular; let color = (Rgba::new(1.0, 0.7, 0.1, 1.0) * light).clamped(Rgba::zero(), Rgba::one()); let bytes = (color * 255.0).map(|e| e as u8).into_array(); (bytes[2] as u32) << 0 | (bytes[1] as u32) << 8 | (bytes[0] as u32) << 16 | (bytes[3] as u32) << 24 } } struct Wireframe<'a> { mvp: &'a Mat4, positions: &'a [Vec4], normals: &'a [Vec3], } impl<'a> Pipeline for Wireframe<'a> { type Vertex = u32; // Vertex index type VsOut = (); type Pixel = u32; // BGRA #[inline] fn vert(&self, v_index: &Self::Vertex) -> ([f32; 4], Self::VsOut) { let v_index = *v_index as usize; // Offset position to avoid z fighting let offset = 0.002 * self.normals[v_index]; let v_pos = self.positions[v_index] + offset; ((*self.mvp * Vec4::from_point(v_pos)).into_array(), ()) } #[inline] fn frag(&self, _: &Self::VsOut) -> Self::Pixel { 120 } } const W: usize = 800; const H: usize = 600; fn main() { let mut color = Buffer2d::new([W, H], 0); let mut depth = Buffer2d::new([W, H], 1.0); let mut win = minifb::Window::new("Teapot", W, H, minifb::WindowOptions::default()).unwrap(); let obj = tobj::load_obj(&Path::new("examples/data/teapot.obj"), false).unwrap(); let indices = &obj.0[0].mesh.indices; let wf_indices: Vec<_> = indices .chunks(3) .flat_map(|sl| vec![sl[0], sl[1], sl[1], sl[2], sl[2], sl[0]].into_iter()) .collect(); let positions = obj.0[0] .mesh .positions .chunks(3) .map(|sl| Vec4::from_point(Vec3::from_slice(sl) + Vec3::new(0.0, -0.5, 0.0))) .collect::>(); let normals = obj.0[0] .mesh .normals .chunks(3) .map(|sl| Vec3::from_slice(sl)) .collect::>(); for i in 0.. { let mvp = Mat4::perspective_fov_rh_no(1.3, W as f32, H as f32, 0.01, 100.0) * Mat4::translation_3d(Vec3::new(0.0, 0.0, -1.5)) * Mat4::::scaling_3d(0.8) * Mat4::rotation_x((i as f32 * 0.002).sin() * 8.0) * Mat4::rotation_y((i as f32 * 0.004).cos() * 4.0) * Mat4::rotation_z((i as f32 * 0.008).sin() * 2.0); color.clear(0); depth.clear(1.0); Teapot { mvp: mvp, positions: &positions, normals: &normals, light_dir: Vec3::new(1.0, 1.0, 1.0).normalized(), } .draw::, _>(indices, &mut color, Some(&mut depth)); Wireframe { mvp: &mvp, positions: &positions, normals: &normals, } .draw::, _>(&wf_indices, &mut color, Some(&mut depth)); if win.is_open() { win.update_with_buffer(color.as_ref(), W, H).unwrap(); } else { break; } } }