diff options
Diffstat (limited to 'Camera.hs')
-rw-r--r-- | Camera.hs | 79 |
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 @@ | |||
1 | module Camera where | ||
2 | |||
3 | import qualified Data.Vector.Generic as G | ||
4 | import Numeric.LinearAlgebra as Math hiding ((<>)) | ||
5 | |||
6 | import Matrix | ||
7 | import LambdaCube.GL.HMatrix () | ||
8 | |||
9 | data 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. | ||
22 | pixelDelta :: Camera -> Vector Float -> Float | ||
23 | pixelDelta 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 | |||
28 | camPos :: Camera -> Vector Float | ||
29 | camPos c = camTarget c - scale (camDistance c) (camDirection c) | ||
30 | |||
31 | camWorldCoordinates :: Camera -> Double -> Double -> Maybe (Vector Float) -> Vector Float | ||
32 | camWorldCoordinates 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 | |||
54 | projectionView :: Camera -> (Camera,Matrix Float) | ||
55 | projectionView 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 | |||
60 | viewProjection :: Camera -> (Camera,(Matrix Float,Vector Float)) | ||
61 | viewProjection 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 | |||
70 | invFloat :: Matrix Float -> Matrix Float | ||
71 | invFloat m = realToFracMatrix $ inv (realToFracMatrix m :: Matrix Double) | ||
72 | |||
73 | realToFracMatrix :: (Real a, Fractional t, Element t, Element a) => Matrix a -> Matrix t | ||
74 | realToFracMatrix m = fromLists $ map realToFrac <$> toLists m | ||
75 | |||
76 | unit :: (Linear t c, Fractional t, Normed (c t)) => c t -> c t | ||
77 | unit v = scale (1/realToFrac (norm_2 v)) v | ||
78 | |||
79 | |||