summaryrefslogtreecommitdiff
path: root/Camera.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Camera.hs')
-rw-r--r--Camera.hs79
1 files changed, 79 insertions, 0 deletions
diff --git a/Camera.hs b/Camera.hs
new file mode 100644
index 0000000..6bf4dd8
--- /dev/null
+++ b/Camera.hs
@@ -0,0 +1,79 @@
1module Camera where
2
3import qualified Data.Vector.Generic as G
4import Numeric.LinearAlgebra as Math hiding ((<>))
5
6import Matrix
7import LambdaCube.GL.HMatrix ()
8
9data Camera = Camera
10 { camHeightAngle :: Float
11 , camTarget :: Vector Float -- 3-vector
12 , camDirection :: Vector Float -- 3-vector
13 , camDistance :: Float
14 , camWidth :: Float
15 , camHeight :: Float
16 , camUp :: Vector Float -- 3-vector
17 , camWorldToScreen :: Maybe (Matrix Float) -- 4×4
18 , camScreenToWorld :: Maybe (Matrix Float) -- 4×4
19 }
20
21-- | Compute the height of a pixel at the given 3d point.
22pixelDelta :: Camera -> Vector Float -> Float
23pixelDelta cam x = realToFrac $ frustumHeight eyeToPoint / realToFrac (camHeight cam)
24 where
25 eyeToPoint = norm_2 (x - camPos cam)
26 frustumHeight d = 2 * d * tan (realToFrac $ camHeightAngle cam / 2)
27
28camPos :: Camera -> Vector Float
29camPos c = camTarget c - scale (camDistance c) (camDirection c)
30
31camWorldCoordinates :: Camera -> Double -> Double -> Maybe (Vector Float) -> Vector Float
32camWorldCoordinates cam h k mplane = case mplane of
33 -- Write on the plane.
34 Just plane -> let n̂ = G.init plane
35 c = plane!3
36 a = (c - dot p n̂) / dot d̂ n̂
37 in p + scale a d̂
38
39 -- Write on the camDistance sphere.
40 Nothing -> p + scale (camDistance cam) d̂
41 where
42 q0 = fromList [ 2 * realToFrac h/camWidth cam - 1
43 , 1 - 2 * realToFrac k/camHeight cam
44 , 1
45 , 1
46 ] :: Vector Float
47 pv = snd $ projectionView cam
48 q1 = pv #> q0
49 q2 = scale (1 /(q1!3)) $ G.init q1
50 p = camPos cam
51 d = q2 - p
52 d̂ = unit d
53
54projectionView :: Camera -> (Camera,Matrix Float)
55projectionView c
56 | Just m <- camScreenToWorld c = (c,m)
57 | Just w <- camWorldToScreen c = projectionView c{ camScreenToWorld = Just $ invFloat w }
58 | otherwise = projectionView $ fst $ viewProjection c
59
60viewProjection :: Camera -> (Camera,(Matrix Float,Vector Float))
61viewProjection c
62 | Just m <- camWorldToScreen c = (c,(m,pos))
63 | otherwise = (c { camWorldToScreen = Just m' }, (m',pos))
64 where
65 m' = proj <> cam
66 cam = lookat pos (camTarget c) (camUp c)
67 pos = camPos c
68 proj = perspective 0.1 100 (camHeightAngle c) (camWidth c / camHeight c)
69
70invFloat :: Matrix Float -> Matrix Float
71invFloat m = realToFracMatrix $ inv (realToFracMatrix m :: Matrix Double)
72
73realToFracMatrix :: (Real a, Fractional t, Element t, Element a) => Matrix a -> Matrix t
74realToFracMatrix m = fromLists $ map realToFrac <$> toLists m
75
76unit :: (Linear t c, Fractional t, Normed (c t)) => c t -> c t
77unit v = scale (1/realToFrac (norm_2 v)) v
78
79