1 #[cfg(feature = "arbitrary")] 2 use quickcheck::{Arbitrary, Gen}; 3 use rand::distributions::{Distribution, Standard}; 4 use rand::Rng; 5 6 #[cfg(feature = "serde-serialize")] 7 use serde::{Deserialize, Deserializer, Serialize, Serializer}; 8 use std::fmt; 9 use std::mem; 10 11 use alga::general::RealField; 12 13 use crate::base::dimension::U3; 14 use crate::base::helper; 15 use crate::base::storage::Storage; 16 use crate::base::{Matrix4, Scalar, Vector, Vector3}; 17 18 use crate::geometry::{Point3, Projective3}; 19 20 /// A 3D perspective projection stored as an homogeneous 4x4 matrix. 21 pub struct Perspective3<N: Scalar> { 22 matrix: Matrix4<N>, 23 } 24 25 impl<N: RealField> Copy for Perspective3<N> {} 26 27 impl<N: RealField> Clone for Perspective3<N> { 28 #[inline] clone(&self) -> Self29 fn clone(&self) -> Self { 30 Self::from_matrix_unchecked(self.matrix.clone()) 31 } 32 } 33 34 impl<N: RealField> fmt::Debug for Perspective3<N> { fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>35 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 36 self.matrix.fmt(f) 37 } 38 } 39 40 impl<N: RealField> PartialEq for Perspective3<N> { 41 #[inline] eq(&self, right: &Self) -> bool42 fn eq(&self, right: &Self) -> bool { 43 self.matrix == right.matrix 44 } 45 } 46 47 #[cfg(feature = "serde-serialize")] 48 impl<N: RealField + Serialize> Serialize for Perspective3<N> { serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer49 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 50 where S: Serializer { 51 self.matrix.serialize(serializer) 52 } 53 } 54 55 #[cfg(feature = "serde-serialize")] 56 impl<'a, N: RealField + Deserialize<'a>> Deserialize<'a> for Perspective3<N> { deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: Deserializer<'a>57 fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> 58 where Des: Deserializer<'a> { 59 let matrix = Matrix4::<N>::deserialize(deserializer)?; 60 61 Ok(Self::from_matrix_unchecked(matrix)) 62 } 63 } 64 65 impl<N: RealField> Perspective3<N> { 66 /// Creates a new perspective matrix from the aspect ratio, y field of view, and near/far planes. new(aspect: N, fovy: N, znear: N, zfar: N) -> Self67 pub fn new(aspect: N, fovy: N, znear: N, zfar: N) -> Self { 68 assert!( 69 !relative_eq!(zfar - znear, N::zero()), 70 "The near-plane and far-plane must not be superimposed." 71 ); 72 assert!( 73 !relative_eq!(aspect, N::zero()), 74 "The apsect ratio must not be zero." 75 ); 76 77 let matrix = Matrix4::identity(); 78 let mut res = Self::from_matrix_unchecked(matrix); 79 80 res.set_fovy(fovy); 81 res.set_aspect(aspect); 82 res.set_znear_and_zfar(znear, zfar); 83 84 res.matrix[(3, 3)] = N::zero(); 85 res.matrix[(3, 2)] = -N::one(); 86 87 res 88 } 89 90 /// Wraps the given matrix to interpret it as a 3D perspective matrix. 91 /// 92 /// It is not checked whether or not the given matrix actually represents an orthographic 93 /// projection. 94 #[inline] from_matrix_unchecked(matrix: Matrix4<N>) -> Self95 pub fn from_matrix_unchecked(matrix: Matrix4<N>) -> Self { 96 Self { matrix: matrix } 97 } 98 99 /// Retrieves the inverse of the underlying homogeneous matrix. 100 #[inline] inverse(&self) -> Matrix4<N>101 pub fn inverse(&self) -> Matrix4<N> { 102 let mut res = self.to_homogeneous(); 103 104 res[(0, 0)] = N::one() / self.matrix[(0, 0)]; 105 res[(1, 1)] = N::one() / self.matrix[(1, 1)]; 106 res[(2, 2)] = N::zero(); 107 108 let m23 = self.matrix[(2, 3)]; 109 let m32 = self.matrix[(3, 2)]; 110 111 res[(2, 3)] = N::one() / m32; 112 res[(3, 2)] = N::one() / m23; 113 res[(3, 3)] = -self.matrix[(2, 2)] / (m23 * m32); 114 115 res 116 } 117 118 /// Computes the corresponding homogeneous matrix. 119 #[inline] to_homogeneous(&self) -> Matrix4<N>120 pub fn to_homogeneous(&self) -> Matrix4<N> { 121 self.matrix.clone_owned() 122 } 123 124 /// A reference to the underlying homogeneous transformation matrix. 125 #[inline] as_matrix(&self) -> &Matrix4<N>126 pub fn as_matrix(&self) -> &Matrix4<N> { 127 &self.matrix 128 } 129 130 /// A reference to this transformation seen as a `Projective3`. 131 #[inline] as_projective(&self) -> &Projective3<N>132 pub fn as_projective(&self) -> &Projective3<N> { 133 unsafe { mem::transmute(self) } 134 } 135 136 /// This transformation seen as a `Projective3`. 137 #[inline] to_projective(&self) -> Projective3<N>138 pub fn to_projective(&self) -> Projective3<N> { 139 Projective3::from_matrix_unchecked(self.matrix) 140 } 141 142 /// Retrieves the underlying homogeneous matrix. 143 #[inline] into_inner(self) -> Matrix4<N>144 pub fn into_inner(self) -> Matrix4<N> { 145 self.matrix 146 } 147 148 /// Retrieves the underlying homogeneous matrix. 149 /// Deprecated: Use [Perspective3::into_inner] instead. 150 #[deprecated(note="use `.into_inner()` instead")] 151 #[inline] unwrap(self) -> Matrix4<N>152 pub fn unwrap(self) -> Matrix4<N> { 153 self.matrix 154 } 155 156 /// Gets the `width / height` aspect ratio of the view frustum. 157 #[inline] aspect(&self) -> N158 pub fn aspect(&self) -> N { 159 self.matrix[(1, 1)] / self.matrix[(0, 0)] 160 } 161 162 /// Gets the y field of view of the view frustum. 163 #[inline] fovy(&self) -> N164 pub fn fovy(&self) -> N { 165 (N::one() / self.matrix[(1, 1)]).atan() * crate::convert(2.0) 166 } 167 168 /// Gets the near plane offset of the view frustum. 169 #[inline] znear(&self) -> N170 pub fn znear(&self) -> N { 171 let ratio = (-self.matrix[(2, 2)] + N::one()) / (-self.matrix[(2, 2)] - N::one()); 172 173 self.matrix[(2, 3)] / (ratio * crate::convert(2.0)) - self.matrix[(2, 3)] / crate::convert(2.0) 174 } 175 176 /// Gets the far plane offset of the view frustum. 177 #[inline] zfar(&self) -> N178 pub fn zfar(&self) -> N { 179 let ratio = (-self.matrix[(2, 2)] + N::one()) / (-self.matrix[(2, 2)] - N::one()); 180 181 (self.matrix[(2, 3)] - ratio * self.matrix[(2, 3)]) / crate::convert(2.0) 182 } 183 184 // FIXME: add a method to retrieve znear and zfar simultaneously? 185 186 // FIXME: when we get specialization, specialize the Mul impl instead. 187 /// Projects a point. Faster than matrix multiplication. 188 #[inline] project_point(&self, p: &Point3<N>) -> Point3<N>189 pub fn project_point(&self, p: &Point3<N>) -> Point3<N> { 190 let inverse_denom = -N::one() / p[2]; 191 Point3::new( 192 self.matrix[(0, 0)] * p[0] * inverse_denom, 193 self.matrix[(1, 1)] * p[1] * inverse_denom, 194 (self.matrix[(2, 2)] * p[2] + self.matrix[(2, 3)]) * inverse_denom, 195 ) 196 } 197 198 /// Un-projects a point. Faster than multiplication by the matrix inverse. 199 #[inline] unproject_point(&self, p: &Point3<N>) -> Point3<N>200 pub fn unproject_point(&self, p: &Point3<N>) -> Point3<N> { 201 let inverse_denom = self.matrix[(2, 3)] / (p[2] + self.matrix[(2, 2)]); 202 203 Point3::new( 204 p[0] * inverse_denom / self.matrix[(0, 0)], 205 p[1] * inverse_denom / self.matrix[(1, 1)], 206 -inverse_denom, 207 ) 208 } 209 210 // FIXME: when we get specialization, specialize the Mul impl instead. 211 /// Projects a vector. Faster than matrix multiplication. 212 #[inline] project_vector<SB>(&self, p: &Vector<N, U3, SB>) -> Vector3<N> where SB: Storage<N, U3>213 pub fn project_vector<SB>(&self, p: &Vector<N, U3, SB>) -> Vector3<N> 214 where SB: Storage<N, U3> { 215 let inverse_denom = -N::one() / p[2]; 216 Vector3::new( 217 self.matrix[(0, 0)] * p[0] * inverse_denom, 218 self.matrix[(1, 1)] * p[1] * inverse_denom, 219 self.matrix[(2, 2)], 220 ) 221 } 222 223 /// Updates this perspective matrix with a new `width / height` aspect ratio of the view 224 /// frustum. 225 #[inline] set_aspect(&mut self, aspect: N)226 pub fn set_aspect(&mut self, aspect: N) { 227 assert!( 228 !relative_eq!(aspect, N::zero()), 229 "The aspect ratio must not be zero." 230 ); 231 self.matrix[(0, 0)] = self.matrix[(1, 1)] / aspect; 232 } 233 234 /// Updates this perspective with a new y field of view of the view frustum. 235 #[inline] set_fovy(&mut self, fovy: N)236 pub fn set_fovy(&mut self, fovy: N) { 237 let old_m22 = self.matrix[(1, 1)]; 238 self.matrix[(1, 1)] = N::one() / (fovy / crate::convert(2.0)).tan(); 239 self.matrix[(0, 0)] = self.matrix[(0, 0)] * (self.matrix[(1, 1)] / old_m22); 240 } 241 242 /// Updates this perspective matrix with a new near plane offset of the view frustum. 243 #[inline] set_znear(&mut self, znear: N)244 pub fn set_znear(&mut self, znear: N) { 245 let zfar = self.zfar(); 246 self.set_znear_and_zfar(znear, zfar); 247 } 248 249 /// Updates this perspective matrix with a new far plane offset of the view frustum. 250 #[inline] set_zfar(&mut self, zfar: N)251 pub fn set_zfar(&mut self, zfar: N) { 252 let znear = self.znear(); 253 self.set_znear_and_zfar(znear, zfar); 254 } 255 256 /// Updates this perspective matrix with new near and far plane offsets of the view frustum. 257 #[inline] set_znear_and_zfar(&mut self, znear: N, zfar: N)258 pub fn set_znear_and_zfar(&mut self, znear: N, zfar: N) { 259 self.matrix[(2, 2)] = (zfar + znear) / (znear - zfar); 260 self.matrix[(2, 3)] = zfar * znear * crate::convert(2.0) / (znear - zfar); 261 } 262 } 263 264 impl<N: RealField> Distribution<Perspective3<N>> for Standard 265 where Standard: Distribution<N> 266 { sample<'a, R: Rng + ?Sized>(&self, r: &'a mut R) -> Perspective3<N>267 fn sample<'a, R: Rng + ?Sized>(&self, r: &'a mut R) -> Perspective3<N> { 268 let znear = r.gen(); 269 let zfar = helper::reject_rand(r, |&x: &N| !(x - znear).is_zero()); 270 let aspect = helper::reject_rand(r, |&x: &N| !x.is_zero()); 271 272 Perspective3::new(aspect, r.gen(), znear, zfar) 273 } 274 } 275 276 #[cfg(feature = "arbitrary")] 277 impl<N: RealField + Arbitrary> Arbitrary for Perspective3<N> { arbitrary<G: Gen>(g: &mut G) -> Self278 fn arbitrary<G: Gen>(g: &mut G) -> Self { 279 let znear = Arbitrary::arbitrary(g); 280 let zfar = helper::reject(g, |&x: &N| !(x - znear).is_zero()); 281 let aspect = helper::reject(g, |&x: &N| !x.is_zero()); 282 283 Self::new(aspect, Arbitrary::arbitrary(g), znear, zfar) 284 } 285 } 286 287 impl<N: RealField> From<Perspective3<N>> for Matrix4<N> { 288 #[inline] from(orth: Perspective3<N>) -> Self289 fn from(orth: Perspective3<N>) -> Self { 290 orth.into_inner() 291 } 292 } 293