aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Spear/Scene/GameObject.hs320
1 files changed, 320 insertions, 0 deletions
diff --git a/Spear/Scene/GameObject.hs b/Spear/Scene/GameObject.hs
new file mode 100644
index 0000000..190d0a5
--- /dev/null
+++ b/Spear/Scene/GameObject.hs
@@ -0,0 +1,320 @@
1module Spear.Scene.GameObject
2(
3 GameObject
4, GameStyle(..)
5, Window(..)
6, AM.AnimationSpeed
7 -- * Construction
8, goNew
9 -- * Accessors
10, currentAnimation
11--, goAABB
12--, goAABBs
13, collisioners
14, goRPGtransform
15, numCollisioners
16, renderer
17, window
18 -- * Manipulation
19, goUpdate
20, setAnimation
21, setAnimationSpeed
22, setAxis
23, withCollisioners
24, setCollisioners
25, setWindow
26 -- * Rendering
27, goRender
28 -- * Collision
29, goCollide
30)
31where
32
33
34import Spear.GL
35import Spear.Math.AABB
36import qualified Spear.Math.Camera as Cam
37import Spear.Math.Collision as Col
38import qualified Spear.Math.Matrix3 as M3
39import qualified Spear.Math.Matrix4 as M4
40import Spear.Math.MatrixUtils
41import qualified Spear.Math.Spatial2 as S2
42import qualified Spear.Math.Spatial3 as S3
43import Spear.Math.Utils
44import Spear.Math.Vector
45import qualified Spear.Render.AnimatedModel as AM
46import Spear.Render.Program
47import Spear.Render.StaticModel as SM
48
49import Data.Fixed (mod')
50import Data.List (foldl')
51
52
53-- | Game style.
54data GameStyle
55 = RPG -- ^ RPG or RTS style game.
56 | PLT -- ^ Platformer or space invaders style game.
57
58
59data Window = Window
60 { projInv :: !M4.Matrix4
61 , viewInv :: !M4.Matrix4
62 , vpx :: !Float
63 , vpy :: !Float
64 , width :: !Float
65 , height :: !Float
66 }
67
68
69dummyWindow = Window M4.id M4.id 0 0 640 480
70
71
72-- | An object in the game scene.
73data GameObject = GameObject
74 { gameStyle :: !GameStyle
75 , renderer :: !(Either StaticModelRenderer AM.AnimatedModelRenderer)
76 , collisioners :: ![Collisioner2]
77 , transform :: !M3.Matrix3
78 , axis :: !Vector3
79 , angle :: !Float
80 , window :: !Window
81 }
82
83
84instance S2.Spatial2 GameObject where
85
86 move v go = go
87 { collisioners = fmap (Col.move v) $ collisioners go
88 , transform = M3.translv v * transform go
89 }
90
91 moveFwd s go =
92 let m = transform go
93 v = scale s $ M3.forward m
94 in go
95 { collisioners = fmap (Col.move v) $ collisioners go
96 , transform = M3.translv v * m
97 }
98
99 moveBack s go =
100 let m = transform go
101 v = scale (-s) $ M3.forward m
102 in go
103 { collisioners = fmap (Col.move v) $ collisioners go
104 , transform = M3.translv v * m
105 }
106
107 strafeLeft s go =
108 let m = transform go
109 v = scale (-s) $ M3.right m
110 in go
111 { collisioners = fmap (Col.move v) $ collisioners go
112 , transform = M3.translv v * m
113 }
114
115 strafeRight s go =
116 let m = transform go
117 v = scale s $ M3.right m
118 in go
119 { collisioners = fmap (Col.move v) $ collisioners go
120 , transform = M3.translv v * m
121 }
122
123 rotate a go =
124 go
125 { transform = transform go * M3.rot a
126 , angle = (angle go + a) `mod'` 360
127 }
128
129 setRotation a go =
130 go
131 { transform = M3.translation (transform go) * M3.rot a
132 , angle = a
133 }
134
135 pos go = M3.position . transform $ go
136
137 fwd go = M3.forward . transform $ go
138
139 up go = M3.up . transform $ go
140
141 right go = M3.right . transform $ go
142
143 transform go = Spear.Scene.GameObject.transform go
144
145 setTransform mat go = go { transform = mat }
146
147 setPos pos go =
148 let m = transform go
149 in go { transform = M3.transform (M3.right m) (M3.forward m) pos }
150
151 lookAt p go =
152 let position = S2.pos go
153 fwd = normalise $ p - position
154 r = perp fwd
155 toDeg = (*(180/pi))
156 viewI = viewInv . window $ go
157 p1 = viewToWorld2d position viewI
158 p2 = viewToWorld2d (position + fwd) viewI
159 f = normalise $ p2 - p1
160 in
161 go
162 { transform = M3.transform r fwd position
163 , angle = 180 -
164 if x f > 0
165 then toDeg . acos $ f `dot` unity2
166 else (+180) . toDeg . acos $ f `dot` (-unity2)
167 }
168
169
170-- | Create a new game object.
171goNew :: GameStyle
172 -> Either StaticModelResource AM.AnimatedModelResource
173 -> [Collisioner2]
174 -> M3.Matrix3 -- ^ Transform
175 -> Vector3 -- ^ Axis of rotation
176 -> GameObject
177
178goNew style (Left smr) cols transf axis = GameObject
179 style (Left $ SM.staticModelRenderer smr) cols transf axis 0 dummyWindow
180
181goNew style (Right amr) cols transf axis = GameObject
182 style (Right $ AM.animatedModelRenderer 1 amr) cols transf axis 0 dummyWindow
183
184
185goUpdate :: Float -> GameObject -> GameObject
186goUpdate dt go =
187 let rend = renderer go
188 rend' = case rend of
189 Left _ -> rend
190 Right amr -> Right $ AM.update dt amr
191 in go
192 { renderer = rend'
193 }
194
195
196-- | Get the game object's ith bounding box.
197--goAABB :: Int -> GameObject -> AABB2
198--goAABB i = getAABB . flip (!!) i . collisioners
199
200
201-- | Get the game object's bounding boxes.
202--goAABBs :: GameObject -> [AABB2]
203--goAABBs = fmap getAABB . collisioners
204
205
206-- | Get the game object's 3D transform.
207goRPGtransform :: GameObject -> M4.Matrix4
208goRPGtransform go =
209 let viewI = viewInv . window $ go
210 in rpgTransform 0 (angle go) (axis go) (S2.pos go) viewI
211
212
213-- | Get the game object's current animation.
214currentAnimation :: Enum a => GameObject -> a
215currentAnimation go = case renderer go of
216 Left _ -> toEnum 0
217 Right amr -> AM.currentAnimation amr
218
219
220-- | Return the game object's number of collisioners.
221numCollisioners :: GameObject -> Int
222numCollisioners = length . collisioners
223
224
225-- | Set the game object's current animation.
226setAnimation :: Enum a => a -> GameObject -> GameObject
227setAnimation a go = case renderer go of
228 Left _ -> go
229 Right amr -> go { renderer = Right $ AM.setAnimation a amr }
230
231
232-- | Set the game object's animation speed.
233setAnimationSpeed :: AM.AnimationSpeed -> GameObject -> GameObject
234setAnimationSpeed s go = case renderer go of
235 Left _ -> go
236 Right amr -> go { renderer = Right $ AM.setAnimationSpeed s amr }
237
238
239-- | Set the game object's axis of rotation.
240setAxis :: Vector3 -> GameObject -> GameObject
241setAxis ax go = go { axis = ax }
242
243
244-- | Set the game object's collisioners.
245setCollisioners :: [Collisioner2] -> GameObject -> GameObject
246setCollisioners cols go = go { collisioners = cols }
247
248
249-- | Set the game object's window.
250setWindow :: Window -> GameObject -> GameObject
251setWindow wnd go = go { window = wnd }
252
253
254-- | Manipulate the game object's collisioners.
255withCollisioners :: GameObject -> ([Collisioner2] -> [Collisioner2]) -> GameObject
256withCollisioners go f = go { collisioners = f $ collisioners go }
257
258
259-- | Render the game object.
260goRender :: StaticProgram -> AnimatedProgram -> Cam.Camera -> GameObject -> IO ()
261goRender sprog aprog cam go =
262 let spu = staticProgramUniforms sprog
263 apu = animatedProgramUniforms aprog
264 style = gameStyle go
265 axis' = axis go
266 a = angle go
267 proj = Cam.projection cam
268 view = M4.inverseTransform $ S3.transform cam
269 transf = S2.transform go
270 normal = fastNormalMatrix modelview
271 modelview = case style of
272 RPG -> view * goRPGtransform go
273 PLT -> view * pltTransform transf
274 in case renderer go of
275 Left smr ->
276 goRender' style a axis' sprog spu modelview proj normal
277 (SM.bind spu smr) (SM.render spu smr)
278 Right amr ->
279 goRender' style a axis' aprog apu modelview proj normal
280 (AM.bind apu amr) (AM.render apu amr)
281
282
283type Bind = IO ()
284
285type Render = IO ()
286
287
288goRender' :: (ProgramUniforms u, Program p)
289 => GameStyle
290 -> Float
291 -> Vector3
292 -> p
293 -> u
294 -> M4.Matrix4 -- Modelview
295 -> M4.Matrix4 -- Projection
296 -> M3.Matrix3 -- Normal matrix
297 -> Bind
298 -> Render
299 -> IO ()
300goRender' style a axis prog uniforms modelview proj normal bindRenderer render =
301 let
302 in do
303 useProgram . program $ prog
304 uniform (projLoc uniforms) proj
305 uniform (modelviewLoc uniforms) modelview
306 uniform (normalmatLoc uniforms) normal
307 bindRenderer
308 render
309
310
311-- | Return 'True' if the given game objects collide, 'False' otherwise.
312goCollide :: GameObject -> GameObject -> Bool
313goCollide go1 go2 =
314 let cols1 = collisioners go1
315 cols2 = collisioners go2
316 c1 = cols1 !! 0
317 c2 = cols2 !! 0
318 in
319 if length cols1 == 0 || length cols2 == 0 then False
320 else c1 `collide` c2 /= NoCollision \ No newline at end of file