vect-0.4.0: Data/Vect/Float/Util/Projective.hs
{-# OPTIONS_GHC -DFlt=Float -DVECT_Float #-}
-- | Classic 4x4 projective matrices. Our convention is that they are intended for multiplication on
-- the /right/, that is, they are of the form
--
-- > _____
-- > [ | | 0 ]
-- > [ | 3x3 | 0 ]
-- > [ |_____| 0 ]
-- > [ p q r 1 ]
--
-- Please note that by default, OpenGL stores the matrices (in memory) by columns, while we
-- store them by rows; but OpenGL also use the opposite convention (so the OpenGL projective matrices
-- are intended for multiplication on the /left/). So in effect, they are the same when stored in the memory,
-- say with @poke :: Ptr Mat4 -> Mat4 -> IO ()@.
module Data.Vect.Flt.Util.Projective where
import Data.Vect.Flt.Base
import Data.Vect.Flt.Util.Dim3
import qualified Data.Vect.Flt.Util.Dim4 as Dim4
class ExtendProjective v e | v->e where
extendProj :: v -> e
extendProjWith :: Flt -> v -> e
extendProj = extendProjWith 1
instance ExtendProjective Vec2 Vec4 where
extendProj (Vec2 x y) = Vec4 x y 0 1
extendProjWith w (Vec2 x y) = Vec4 x y 0 w
instance ExtendProjective Vec3 Vec4 where
extendProj (Vec3 x y z) = Vec4 x y z 1
extendProjWith w (Vec3 x y z) = Vec4 x y z w
instance ExtendProjective Vec4 Vec4 where
extendProj = id
extendProjWith w (Vec4 x y z w') = let s = w/w' in Vec4 (s*x) (s*y) (s*z) w
instance ExtendProjective Mat2 Mat4 where
extendProj (Mat2 r1 r2) = Mat4 (extendZero r1) (extendZero r2) (Dim4.vec4Z) (Vec4 0 0 0 1)
extendProjWith w (Mat2 r1 r2) = Mat4 (extendZero r1) (extendZero r2) (Dim4.vec4Z) (Vec4 0 0 0 w)
instance ExtendProjective Mat3 Mat4 where
extendProj (Mat3 r1 r2 r3) = Mat4 (extendZero r1) (extendZero r2) (extendZero r3) (Vec4 0 0 0 1)
extendProjWith w (Mat3 r1 r2 r3) = Mat4 (extendZero r1) (extendZero r2) (extendZero r3) (Vec4 0 0 0 w)
rotMatrixProj :: Flt -> Normal3 -> Mat4
rotMatrixProj angle axis = extendProj $ rotMatrix3' axis angle
rotMatrixProj' :: {- ' CPP is sensitive to primes -} Flt -> Vec3 -> Mat4
rotMatrixProj' angle axis = extendProj $ rotMatrix3 axis angle
translMatrixProj :: Vec3 -> Mat4
translMatrixProj v = Mat4 Dim4.vec4X Dim4.vec4Y Dim4.vec4Z (extendProj v)
-- | we assume that the bottom-right corner is 1.
translWithProj :: Vec3 -> Mat4 -> Mat4
translWithProj v mat@(Mat4 r1 r2 r3 r4) = Mat4 r1 r2 r3 (extendProjWith 0 v &+ r4)
scaleMatrixProj :: Vec3 -> Mat4
scaleMatrixProj v = diag $ extendProj v
scaleMatrixUniformProj :: Flt -> Mat4
scaleMatrixUniformProj s = diag (Vec4 s s s 1)
class ProjectiveAction v where
actProj :: v -> Mat4 -> v
instance ProjectiveAction Vec3 where
actProj v m = trim $ (extendProj v) .* m
instance ProjectiveAction Vec4 where
actProj v m = v .* m
-- | When acting on unit vectors, we ignore the translation part.
instance ProjectiveAction Normal3 where
actProj (Normal3 v) m = Normal3 (v .* (trim m :: Mat3))
-- | Inverts a projective 4x4 matrix, assuming that the top-left 3x3 part is /orthogonal/,
-- and the bottom-right corner is 1.
invertProj :: Mat4 -> Mat4
invertProj mat@(Mat4 u v w t) =
translWithProj t' $ extendProj $ transpose $ (trim mat :: Mat3)
where
t' = Vec3 (- u &. t) (- v &. t) (- w &. t)