use mikktspace::{generate_tangents, Geometry}; use nalgebra::{Point2, Point3, Vector3}; pub type Face = [u32; 3]; #[derive(Debug)] struct Vertex { position: Point3, normal: Vector3, tex_coord: Point2, } #[derive(Debug, PartialEq)] struct Result { tangent: [f32; 3], bi_tangent: [f32; 3], mag_s: f32, mag_t: f32, bi_tangent_preserves_orientation: bool, face: usize, vert: usize, } impl Result { fn new( tangent: [f32; 3], bi_tangent: [f32; 3], mag_s: f32, mag_t: f32, bi_tangent_preserves_orientation: bool, face: usize, vert: usize, ) -> Self { Self { tangent, bi_tangent, mag_s, mag_t, bi_tangent_preserves_orientation, face, vert, } } } struct Mesh { faces: Vec, vertices: Vec, } struct Context { mesh: Mesh, results: Vec, } fn vertex(mesh: &Mesh, face: usize, vert: usize) -> &Vertex { let vs: &[u32; 3] = &mesh.faces[face]; &mesh.vertices[vs[vert] as usize] } impl Geometry for Context { fn num_faces(&self) -> usize { self.mesh.faces.len() } fn num_vertices_of_face(&self, _face: usize) -> usize { 3 } fn position(&self, face: usize, vert: usize) -> [f32; 3] { vertex(&self.mesh, face, vert).position.coords.into() } fn normal(&self, face: usize, vert: usize) -> [f32; 3] { vertex(&self.mesh, face, vert).normal.into() } fn tex_coord(&self, face: usize, vert: usize) -> [f32; 2] { vertex(&self.mesh, face, vert).tex_coord.coords.into() } fn set_tangent( &mut self, tangent: [f32; 3], bi_tangent: [f32; 3], mag_s: f32, mag_t: f32, bi_tangent_preserves_orientation: bool, face: usize, vert: usize, ) { self.results.push(Result { tangent, bi_tangent, mag_s, mag_t, bi_tangent_preserves_orientation, face, vert, }) } } struct ControlPoint { uv: [f32; 2], dir: [f32; 3], } impl ControlPoint { fn new(uv: [f32; 2], dir: [f32; 3]) -> Self { Self { uv, dir } } } fn make_cube() -> Mesh { let mut faces = Vec::new(); let mut ctl_pts = Vec::new(); let mut vertices = Vec::new(); // +x plane { let base = ctl_pts.len() as u32; faces.push([base, base + 1, base + 4]); faces.push([base + 1, base + 2, base + 4]); faces.push([base + 2, base + 3, base + 4]); faces.push([base + 3, base, base + 4]); ctl_pts.push(ControlPoint::new([0.0, 0.0], [1.0, -1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [1.0, -1.0, -1.0])); ctl_pts.push(ControlPoint::new([1.0, 1.0], [1.0, 1.0, -1.0])); ctl_pts.push(ControlPoint::new([1.0, 0.0], [1.0, 1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.5, 0.5], [1.0, 0.0, 0.0])); } // -x plane { let base = ctl_pts.len() as u32; faces.push([base, base + 1, base + 4]); faces.push([base + 1, base + 2, base + 4]); faces.push([base + 2, base + 3, base + 4]); faces.push([base + 3, base, base + 4]); ctl_pts.push(ControlPoint::new([1.0, 0.0], [-1.0, 1.0, 1.0])); ctl_pts.push(ControlPoint::new([1.0, 1.0], [-1.0, 1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [-1.0, -1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 0.0], [-1.0, -1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.5, 0.5], [-1.0, 0.0, 0.0])); } // +y plane { let base = ctl_pts.len() as u32; faces.push([base, base + 1, base + 4]); faces.push([base + 1, base + 2, base + 4]); faces.push([base + 2, base + 3, base + 4]); faces.push([base + 3, base, base + 4]); ctl_pts.push(ControlPoint::new([0.0, 0.0], [1.0, 1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [1.0, 1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [-1.0, 1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 0.0], [-1.0, 1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.0, 0.5], [0.0, 1.0, 0.0])); } // -y plane { let base = ctl_pts.len() as u32; faces.push([base, base + 1, base + 4]); faces.push([base + 1, base + 2, base + 4]); faces.push([base + 2, base + 3, base + 4]); faces.push([base + 3, base, base + 4]); ctl_pts.push(ControlPoint::new([0.0, 0.0], [-1.0, -1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [-1.0, -1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [1.0, -1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 0.0], [1.0, -1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.0, 0.5], [0.0, -1.0, 0.0])); } // +z plane { let base = ctl_pts.len() as u32; faces.push([base, base + 1, base + 4]); faces.push([base + 1, base + 2, base + 4]); faces.push([base + 2, base + 3, base + 4]); faces.push([base + 3, base, base + 4]); ctl_pts.push(ControlPoint::new([0.0, 0.0], [-1.0, 1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [-1.0, -1.0, 1.0])); ctl_pts.push(ControlPoint::new([1.0, 1.0], [1.0, -1.0, 1.0])); ctl_pts.push(ControlPoint::new([1.0, 0.0], [1.0, 1.0, 1.0])); ctl_pts.push(ControlPoint::new([0.5, 0.5], [0.0, 0.0, 1.0])); } // -z plane { let base = ctl_pts.len() as u32; faces.push([base, base + 1, base + 4]); faces.push([base + 1, base + 2, base + 4]); faces.push([base + 2, base + 3, base + 4]); faces.push([base + 3, base, base + 4]); ctl_pts.push(ControlPoint::new([1.0, 0.0], [1.0, 1.0, -1.0])); ctl_pts.push(ControlPoint::new([1.0, 1.0], [1.0, -1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 1.0], [-1.0, -1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.0, 0.0], [-1.0, 1.0, -1.0])); ctl_pts.push(ControlPoint::new([0.5, 0.5], [0.0, 0.0, -1.0])); } for pt in ctl_pts { let p: Point3 = pt.dir.into(); let n: Vector3 = p.coords.normalize(); let t: Point2 = pt.uv.into(); vertices.push(Vertex { position: (p / 2.0).into(), normal: n.into(), tex_coord: t.into(), }); } Mesh { faces, vertices } } #[test] fn cube_tangents_should_equal_reference_values() { let mut context = Context { mesh: make_cube(), results: Vec::new(), }; let ret = generate_tangents(&mut context); assert_eq!(true, ret); let expected_results: Vec = vec![ Result::new( [0.40824825, 0.81649655, 0.40824825], [0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 0, 0, ), Result::new( [0.40824825, 0.81649655, -0.40824825], [-0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 0, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, false, 0, 2, ), Result::new( [0.40824825, 0.81649655, -0.40824825], [-0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 1, 0, ), Result::new( [-0.40824825, 0.81649655, 0.40824825], [-0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 1, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, false, 1, 2, ), Result::new( [-0.40824825, 0.81649655, 0.40824825], [-0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 2, 0, ), Result::new( [-0.40824825, 0.81649655, -0.40824825], [0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 2, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, false, 2, 2, ), Result::new( [-0.40824825, 0.81649655, -0.40824825], [0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 3, 0, ), Result::new( [0.40824825, 0.81649655, 0.40824825], [0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 3, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, false, 3, 2, ), Result::new( [0.40824825, 0.81649655, -0.40824825], [-0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 4, 0, ), Result::new( [0.40824825, 0.81649655, 0.40824825], [0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 4, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, true, 4, 2, ), Result::new( [0.40824825, 0.81649655, 0.40824825], [0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 5, 0, ), Result::new( [-0.40824825, 0.81649655, -0.40824825], [0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 5, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, true, 5, 2, ), Result::new( [-0.40824825, 0.81649655, -0.40824825], [0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 6, 0, ), Result::new( [-0.40824825, 0.81649655, 0.40824825], [-0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 6, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, true, 6, 2, ), Result::new( [-0.40824825, 0.81649655, 0.40824825], [-0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 7, 0, ), Result::new( [0.40824825, 0.81649655, -0.40824825], [-0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 7, 1, ), Result::new( [0.00000000, 1.00000000, 0.00000000], [0.00000000, 0.00000000, -1.00000000], 1.00000000, 1.00000000, true, 7, 2, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 8, 0, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 8, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 8, 2, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 9, 0, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 9, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 9, 2, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 10, 0, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 10, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 10, 2, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 11, 0, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 11, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 11, 2, ), Result::new( [-0.40824825, 0.81649655, 0.40824825], [-0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 12, 0, ), Result::new( [-0.40824825, 0.81649655, -0.40824825], [0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, true, 12, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 12, 2, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 13, 0, ), Result::new( [0.40824825, 0.81649655, -0.40824825], [-0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 13, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 13, 2, ), Result::new( [0.40824825, 0.81649655, -0.40824825], [-0.40824825, 0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 14, 0, ), Result::new( [0.40824825, 0.81649655, 0.40824825], [0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 14, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 14, 2, ), Result::new( [0.40824825, 0.81649655, 0.40824825], [0.40824825, -0.40824825, -0.81649655], 1.00000000, 1.00000000, false, 15, 0, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 15, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, 1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 15, 2, ), Result::new( [0.81649655, 0.40824825, 0.40824825], [-0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, false, 16, 0, ), Result::new( [0.81649655, -0.40824825, 0.40824825], [0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, false, 16, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 16, 2, ), Result::new( [0.81649655, -0.40824825, 0.40824825], [0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, false, 17, 0, ), Result::new( [0.81649655, 0.40824825, -0.40824825], [-0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, false, 17, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 17, 2, ), Result::new( [0.81649655, 0.40824825, -0.40824825], [-0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, false, 18, 0, ), Result::new( [0.81649655, -0.40824825, -0.40824825], [0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, false, 18, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 18, 2, ), Result::new( [0.81649655, -0.40824825, -0.40824825], [0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, false, 19, 0, ), Result::new( [0.81649655, 0.40824825, 0.40824825], [-0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, false, 19, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, false, 19, 2, ), Result::new( [0.81649655, -0.40824825, 0.40824825], [0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, true, 20, 0, ), Result::new( [0.81649655, 0.40824825, 0.40824825], [-0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, true, 20, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, true, 20, 2, ), Result::new( [0.81649655, 0.40824825, 0.40824825], [-0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, true, 21, 0, ), Result::new( [0.81649655, -0.40824825, -0.40824825], [0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, true, 21, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, true, 21, 2, ), Result::new( [0.81649655, -0.40824825, -0.40824825], [0.40824825, -0.81649655, 0.40824825], 1.00000000, 1.00000000, true, 22, 0, ), Result::new( [0.81649655, 0.40824825, -0.40824825], [-0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, true, 22, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, true, 22, 2, ), Result::new( [0.81649655, 0.40824825, -0.40824825], [-0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, true, 23, 0, ), Result::new( [0.81649655, -0.40824825, 0.40824825], [0.40824825, -0.81649655, -0.40824825], 1.00000000, 1.00000000, true, 23, 1, ), Result::new( [1.00000000, 0.00000000, 0.00000000], [0.00000000, -1.00000000, 0.00000000], 1.00000000, 1.00000000, true, 23, 2, ), ]; assert_eq!(expected_results, context.results); }