aboutsummaryrefslogtreecommitdiff
path: root/Spear/Math/Spatial3.hs
blob: 896d5ae1f3bf6080b20ff7ee0facb2ca52ebd43a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
module Spear.Math.Spatial3
(
    Spatial3(..)
,   Obj3
,   move
,   moveFwd
,   moveBack
,   moveLeft
,   moveRight
,   rotate
,   pitch
,   yaw
,   roll
,   pos
,   fwd
,   up
,   right
,   transform
,   setTransform
,   setPos
,   lookAt
,   Spear.Math.Spatial3.orbit
,   fromVectors
,   fromTransform
)
where

import Spear.Math.Vector
import qualified Spear.Math.Matrix4 as M

type Matrix4 = M.Matrix4

class Spatial3 s where

      -- | Gets the spatial's Obj3.
      getObj3 :: s -> Obj3

      -- | Set the spatial's Obj3.
      setObj3 :: s -> Obj3 -> s

-- | Move the spatial.
move :: Spatial3 s => Vector3 -> s -> s
move d s = let o = getObj3 s in setObj3 s $ o { p = p o + d }

-- | Move the spatial forwards.
moveFwd :: Spatial3 s => Float -> s -> s
moveFwd a s = let o = getObj3 s in setObj3 s $ o { p = p o + scale a (f o) }

-- | Move the spatial backwards.
moveBack :: Spatial3 s => Float -> s -> s
moveBack a s = let o = getObj3 s in setObj3 s $ o { p = p o + scale (-a) (f o) }

-- | Make the spatial strafe left.
moveLeft :: Spatial3 s => Float -> s -> s
moveLeft a s = let o = getObj3 s in setObj3 s $ o { p = p o + scale (-a) (r o) }

-- | Make the spatial Strafe right.
moveRight :: Spatial3 s => Float -> s -> s
moveRight a s = let o = getObj3 s in setObj3 s $ o { p = p o + scale a (r o) }

-- | Rotate the spatial about the given axis.
rotate :: Spatial3 s => Vector3 -> Float -> s -> s
rotate axis a s =
       let t = transform s
           axis' = M.inverseTransform t `M.muld` axis
       in setTransform (t * M.axisAngle axis' a) s

-- | Rotate the spatial about its local X axis.
pitch :: Spatial3 s => Float -> s -> s
pitch a s =
      let o  = getObj3 s
          a' = toRAD a
          sa = sin a'
          ca = cos a'
          f' = normalise $ scale ca (f o) + scale sa (u o)
          u' = normalise $ r o `cross` f'
      in  setObj3 s $ o { u = u', f = f' }

-- | Rotate the spatial about its local Y axis.
yaw :: Spatial3 s => Float -> s -> s
yaw a s =
    let o  = getObj3 s
        a' = toRAD a
        sa = sin a'
        ca = cos a'
        r' = normalise $ scale ca (r o) + scale sa (f o)
        f' = normalise $ u o `cross` r'
    in  setObj3 s $ o { r = r', f = f' }

-- | Rotate the spatial about its local Z axis.
roll :: Spatial3 s => Float -> s -> s
roll a s =
     let o  = getObj3 s
         a' = toRAD a
         sa = sin a'
         ca = cos a'
         u' = normalise $ scale ca (u o) - scale sa (r o)
         r' = normalise $ f o `cross` u'
     in  setObj3 s $ o { r = r', u = u' }

-- | Get the spatial's position.
pos :: Spatial3 s => s -> Vector3
pos = p . getObj3

-- | Get the spatial's forward vector.
fwd :: Spatial3 s => s -> Vector3
fwd = f . getObj3

-- | Get the spatial's up vector.
up :: Spatial3 s => s -> Vector3
up = u . getObj3

-- | Get the spatial's right vector.
right :: Spatial3 s => s -> Vector3
right = r . getObj3

-- | Get the spatial's transform.
transform :: Spatial3 s => s -> Matrix4
transform s = let o = getObj3 s in M.transform (r o) (u o) (scale (-1) $ f o) (p o)

-- | Set the spatial's transform.
setTransform :: Spatial3 s => Matrix4 -> s -> s
setTransform t s =
             let o = Obj3 (M.right t) (M.up t) (scale (-1) $ M.forward t) (M.position t)
             in setObj3 s o

-- | Set the spatial's position.
setPos :: Spatial3 s => Vector3 -> s -> s
setPos pos s = setObj3 s $ (getObj3 s) { p = pos }

-- | Make the spatial look at the given point.
lookAt :: Spatial3 s => Vector3 -> s -> s
lookAt pt s =
       let position = pos s
           fwd      = normalise $ pt - position
           r        = fwd `cross` unity3
           u        = r `cross` fwd
       in setTransform (M.transform r u (-fwd) position) s

-- | Make the spatial orbit around the given point
orbit :: Spatial3 s
      => Vector3 -- ^ Target point
      -> Float   -- ^ Horizontal angle
      -> Float   -- ^ Vertical angle
      -> Float   -- ^ Orbit radius.
      -> s
      -> s

orbit pt anglex angley radius s =
      let ax = anglex * pi / 180
          ay = angley * pi / 180
          sx = sin ax
          sy = sin ay
          cx = cos ax
          cy = cos ay
          px = (x pt) + radius*cy*sx
          py = (y pt) + radius*sy
          pz = (z pt) + radius*cx*cy
      in setPos (vec3 px py pz) s

-- | An object in 3D space.
data Obj3 = Obj3
     { r :: Vector3
     , u :: Vector3
     , f :: Vector3
     , p :: Vector3
     } deriving Show

instance Spatial3 Obj3 where
         getObj3 = id
         setObj3 _ o' = o'

fromVectors :: Right3 -> Up3 -> Forward3 -> Position3 -> Obj3
fromVectors = Obj3

fromTransform :: Matrix4 -> Obj3
fromTransform m = Obj3 (M.right m) (M.up m) (M.forward m) (M.position m)

toRAD = (*pi) . (/180)