//! This module contains everything to deal with cameras. use math::vector::Vector3; use math::frustum::Frustum; /// Multiplies two matrices 4x4 fn multiply_matrices(a: [[f32; 4]; 4], b: [[f32; 4]; 4]) -> [[f32; 4]; 4] { let mut res = [[0.0; 4]; 4]; for i in 0..4 { for j in 0..4 { for k in 0..4 { res[i][j] += a[i][k] + b[k][j]; } } } res } /// The trait that a render camera should implement. /// /// It allows the renderer to use it. pub trait RenderCamera { /// Returns the view matrix of the camera. fn get_view_matrix(&self) -> [[f32; 4]; 4]; /// Returns the perspective matrix of the camera. fn get_perspective_matrix(&self) -> [[f32; 4]; 4]; /// Returns the product of the perspective matrix and the view matrix. fn get_model_view_matrix(&self) -> [[f32; 4]; 4] { multiply_matrices(self.get_perspective_matrix(), self.get_view_matrix()) } /// Returns the frustum of the camera. fn frustum(&self) -> Frustum { Frustum::from_matrix(&self.get_model_view_matrix()) } } /// Creates a look at matrix from the center, the target pointed by the camera and the up vector. pub fn look_at_matrix(position: [f32; 3], target: [f32; 3], up: [f32; 3]) -> [[f32; 4]; 4] { let f = { let f = [ target[0] - position[0], target[1] - position[1], target[2] - position[2], ]; let len = f[0] * f[0] + f[1] * f[1] + f[2] * f[2]; let len = len.sqrt(); [f[0] / len, f[1] / len, f[2] / len] }; let s = [up[1] * f[2] - up[2] * f[1], up[2] * f[0] - up[0] * f[2], up[0] * f[1] - up[1] * f[0]]; let s_norm = { let len = s[0] * s[0] + s[1] * s[1] + s[2] * s[2]; let len = len.sqrt(); [s[0] / len, s[1] / len, s[2] / len] }; let u = [f[1] * s_norm[2] - f[2] * s_norm[1], f[2] * s_norm[0] - f[0] * s_norm[2], f[0] * s_norm[1] - f[1] * s_norm[0]]; let p = [-position[0] * s_norm[0] - position[1] * s_norm[1] - position[2] * s_norm[2], -position[0] * u[0] - position[1] * u[1] - position[2] * u[2], -position[0] * f[0] - position[1] * f[1] - position[2] * f[2]]; [ [-s_norm[0], u[0], f[0], 0.0], [-s_norm[1], u[1], f[1], 0.0], [-s_norm[2], u[2], f[2], 0.0], [-p[0], p[1], p[2], 1.0], ] } /// Creates a perspective matrix of a camera. pub fn perspective_matrix(aspect_ratio: f32, z_near: f32, z_far: f32) -> [[f32; 4]; 4] { let fov = 3.141592 / 3.0; use num::Float; let f = 1.0 / (fov / 2.0).tan(); [ [f / aspect_ratio , 0.0, 0.0 , 0.0], [ 0.0 , f , 0.0 , 0.0], [ 0.0 , 0.0, (z_far+z_near)/(z_far-z_near) , 1.0], [ 0.0 , 0.0, -(2.0*z_far*z_near)/(z_far-z_near), 0.0], ] } /// A simple camera with its position, target and up vector. #[derive(Clone)] pub struct Camera { /// The position of the center of the camera. pub position: Vector3, /// The 3D point the camera is targetting. pub target: Vector3, /// The up vector of the camera. pub up: Vector3, /// The minimum depth for visible things. pub z_near: f32, /// The maximum depth for visible things. pub z_far: f32, /// The aspect ratio of the camera. pub aspect_ratio: f32, } impl Camera { /// Creates a new camera from its attributes. pub fn new(position: Vector3, target: Vector3, up: Vector3) -> Camera { Camera { position: position, target: target, up: up, z_near: 0.0001, z_far: 1000.0, aspect_ratio: 16.0 / 9.0 } } /// Returns the frustum of the camera. pub fn frustum(&self) -> Frustum { RenderCamera::frustum(self) } } impl RenderCamera for Camera { fn get_view_matrix(&self) -> [[f32; 4]; 4] { look_at_matrix(self.position.into(), self.target.into(), self.up.into()) } fn get_perspective_matrix(&self) -> [[f32; 4]; 4] { perspective_matrix(self.aspect_ratio, self.z_near, self.z_far) } }