diff options
author | Joe Crayne <joe@jerkface.net> | 2019-06-03 19:16:56 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-06-03 19:16:56 -0400 |
commit | f13eedc6482098581c359518e5db1dc7ec572eaf (patch) | |
tree | d5bce3bf46c367adc902e13e3d0eb9dc2e44357b | |
parent | 5ef4e1f8f206bb6b1bfe94b421d8b72b912af477 (diff) |
Factor Camera module out of MeshSketch.
-rw-r--r-- | Camera.hs | 79 | ||||
-rw-r--r-- | MeshSketch.hs | 48 | ||||
-rw-r--r-- | lambda-gtk.cabal | 2 |
3 files changed, 81 insertions, 48 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 | |||
diff --git a/MeshSketch.hs b/MeshSketch.hs index c63420e..1019d72 100644 --- a/MeshSketch.hs +++ b/MeshSketch.hs | |||
@@ -67,6 +67,7 @@ import qualified VectorRing as Vector | |||
67 | import RingBuffer | 67 | import RingBuffer |
68 | import MaskableStream (AttributeKey,(@<-)) | 68 | import MaskableStream (AttributeKey,(@<-)) |
69 | import SmallRing | 69 | import SmallRing |
70 | import Camera | ||
70 | 71 | ||
71 | 72 | ||
72 | prettyDebug :: GL.DebugMessage -> String | 73 | prettyDebug :: GL.DebugMessage -> String |
@@ -118,21 +119,6 @@ data State = State | |||
118 | , stAngle :: IORef Int | 119 | , stAngle :: IORef Int |
119 | } | 120 | } |
120 | 121 | ||
121 | data Camera = Camera | ||
122 | { camHeightAngle :: Float | ||
123 | , camTarget :: Vector Float -- 3-vector | ||
124 | , camDirection :: Vector Float -- 3-vector | ||
125 | , camDistance :: Float | ||
126 | , camWidth :: Float | ||
127 | , camHeight :: Float | ||
128 | , camUp :: Vector Float -- 3-vector | ||
129 | , camWorldToScreen :: Maybe (Matrix Float) -- 4×4 | ||
130 | , camScreenToWorld :: Maybe (Matrix Float) -- 4×4 | ||
131 | } | ||
132 | |||
133 | camPos :: Camera -> Vector Float | ||
134 | camPos c = camTarget c - scale (camDistance c) (camDirection c) | ||
135 | |||
136 | initCamera :: Camera | 122 | initCamera :: Camera |
137 | initCamera = Camera | 123 | initCamera = Camera |
138 | { camHeightAngle = pi/6 | 124 | { camHeightAngle = pi/6 |
@@ -147,16 +133,6 @@ initCamera = Camera | |||
147 | } | 133 | } |
148 | where d = realToFrac $ norm_2 $ fromList [2::Float,2,10] | 134 | where d = realToFrac $ norm_2 $ fromList [2::Float,2,10] |
149 | 135 | ||
150 | viewProjection :: Camera -> (Camera,(Matrix Float,Vector Float)) | ||
151 | viewProjection c | ||
152 | | Just m <- camWorldToScreen c = (c,(m,pos)) | ||
153 | | otherwise = (c { camWorldToScreen = Just m' }, (m',pos)) | ||
154 | where | ||
155 | m' = proj <> cam | ||
156 | cam = lookat pos (camTarget c) (camUp c) | ||
157 | pos = camPos c | ||
158 | proj = perspective 0.1 100 (camHeightAngle c) (camWidth c / camHeight c) | ||
159 | |||
160 | realToFracVector :: ( Real a | 136 | realToFracVector :: ( Real a |
161 | , Fractional b | 137 | , Fractional b |
162 | , Storable a | 138 | , Storable a |
@@ -164,18 +140,6 @@ realToFracVector :: ( Real a | |||
164 | ) => Vector a -> Vector b | 140 | ) => Vector a -> Vector b |
165 | realToFracVector v = Math.fromList $ map realToFrac $ Math.toList v | 141 | realToFracVector v = Math.fromList $ map realToFrac $ Math.toList v |
166 | 142 | ||
167 | realToFracMatrix :: (Real a, Fractional t, Element t, Element a) => Matrix a -> Matrix t | ||
168 | realToFracMatrix m = fromLists $ map realToFrac <$> toLists m | ||
169 | |||
170 | invFloat :: Matrix Float -> Matrix Float | ||
171 | invFloat m = realToFracMatrix $ inv (realToFracMatrix m :: Matrix Double) | ||
172 | |||
173 | projectionView :: Camera -> (Camera,Matrix Float) | ||
174 | projectionView c | ||
175 | | Just m <- camScreenToWorld c = (c,m) | ||
176 | | Just w <- camWorldToScreen c = projectionView c{ camScreenToWorld = Just $ invFloat w } | ||
177 | | otherwise = projectionView $ fst $ viewProjection c | ||
178 | |||
179 | addOBJToObjectArray :: GLStorage -> String -> [(GPUMesh, Maybe Text)] -> Map Text (ObjMaterial,TextureData) -> IO [LC.Object] | 143 | addOBJToObjectArray :: GLStorage -> String -> [(GPUMesh, Maybe Text)] -> Map Text (ObjMaterial,TextureData) -> IO [LC.Object] |
180 | addOBJToObjectArray storage slotName objMesh mtlLib = forM objMesh $ \(mesh,mat) -> do | 144 | addOBJToObjectArray storage slotName objMesh mtlLib = forM objMesh $ \(mesh,mat) -> do |
181 | obj <- LC.addMeshToObjectArray storage slotName ["diffuseTexture","diffuseColor"] mesh | 145 | obj <- LC.addMeshToObjectArray storage slotName ["diffuseTexture","diffuseColor"] mesh |
@@ -468,16 +432,6 @@ onResize glarea realized w h = do | |||
468 | } | 432 | } |
469 | LC.setScreenSize (stStorage realized) wd ht | 433 | LC.setScreenSize (stStorage realized) wd ht |
470 | 434 | ||
471 | unit :: (Linear t c, Fractional t, Normed (c t)) => c t -> c t | ||
472 | unit v = scale (1/realToFrac (norm_2 v)) v | ||
473 | |||
474 | -- | Compute the height of a pixel at the given 3d point. | ||
475 | pixelDelta :: Camera -> Vector Float -> Float | ||
476 | pixelDelta cam x = realToFrac $ frustumHeight eyeToPoint / realToFrac (camHeight cam) | ||
477 | where | ||
478 | eyeToPoint = norm_2 (x - camPos cam) | ||
479 | frustumHeight d = 2 * d * tan (realToFrac $ camHeightAngle cam / 2) | ||
480 | |||
481 | -- This computes a point in world coordinates on the view screen if | 435 | -- This computes a point in world coordinates on the view screen if |
482 | -- we assume the camera is located at the origin. | 436 | -- we assume the camera is located at the origin. |
483 | computeDirection :: Camera -> Double -> Double -> Vector Float | 437 | computeDirection :: Camera -> Double -> Double -> Vector Float |
diff --git a/lambda-gtk.cabal b/lambda-gtk.cabal index e7e6859..a0c9fc3 100644 --- a/lambda-gtk.cabal +++ b/lambda-gtk.cabal | |||
@@ -50,7 +50,7 @@ executable meshsketch | |||
50 | other-modules: InfinitePlane LambdaCubeWidget GLWidget LambdaCube.Gtk TimeKeeper | 50 | other-modules: InfinitePlane LambdaCubeWidget GLWidget LambdaCube.Gtk TimeKeeper |
51 | LoadMesh MtlParser Matrix LambdaCube.GL.HMatrix | 51 | LoadMesh MtlParser Matrix LambdaCube.GL.HMatrix |
52 | Animator MeshSketch CubeMap AttributeData GPURing MaskableStream | 52 | Animator MeshSketch CubeMap AttributeData GPURing MaskableStream |
53 | RingBuffer SmallRing VectorRing | 53 | RingBuffer SmallRing VectorRing Camera |
54 | extensions: NondecreasingIndentation | 54 | extensions: NondecreasingIndentation |
55 | other-extensions: OverloadedLabels, OverloadedLists, OverloadedStrings | 55 | other-extensions: OverloadedLabels, OverloadedLists, OverloadedStrings |
56 | build-depends: base, containers >=0.5 && <0.6, bytestring >=0.10 && <0.11, | 56 | build-depends: base, containers >=0.5 && <0.6, bytestring >=0.10 && <0.11, |