use crate::{ matrices::Matrix4x4, tuples::{cross, Tuple}, }; /// Create a matrix representing a eye at `from` looking at `to`, with an `up` /// as the up vector. pub fn view_transform(from: Tuple, to: Tuple, up: Tuple) -> Matrix4x4 { let forward = (to - from).normalize(); let left = cross(forward, up.normalize()); let true_up = cross(left, forward); Matrix4x4::new( [left.x, left.y, left.z, 0.], [true_up.x, true_up.y, true_up.z, 0.], [-forward.x, -forward.y, -forward.z, 0.], [0., 0., 0., 1.], ) * Matrix4x4::translation(-from.x, -from.y, -from.z) } #[cfg(test)] mod tests { use crate::{ matrices::{identity, scaling, translation, Matrix4x4}, transformations::view_transform, tuples::{point, vector}, }; #[test] fn default_orientation() { // The transformation matrix for the default orientation. let from = point(0., 0., 0.); let to = point(0., 0., -1.); let up = vector(0., 1., 0.); let t = view_transform(from, to, up); assert_eq!(t, identity()); } #[test] fn looking_positive_z() { // A view transformation matrix looking in positive z direction. let from = point(0., 0., 0.); let to = point(0., 0., 1.); let up = vector(0., 1., 0.); let t = view_transform(from, to, up); assert_eq!(t, scaling(-1., 1., -1.)); } #[test] fn transformation_moves_world() { // The view transformation moves the world. let from = point(0., 0., 8.); let to = point(0., 0., 0.); let up = vector(0., 1., 0.); let t = view_transform(from, to, up); assert_eq!(t, translation(0., 0., -8.)); } #[test] fn arbitrary_view() { // An arbitrary view transformation. let from = point(1., 3., 2.); let to = point(4., -2., 8.); let up = vector(1., 1., 0.); let t = view_transform(from, to, up); assert_eq!( t, Matrix4x4::new( [-0.50709, 0.50709, 0.67612, -2.36643], [0.76772, 0.60609, 0.12122, -2.82843], [-0.35857, 0.59761, -0.71714, 0.], [0., 0., 0., 1.], ) ); } }