diff options
author | Jeanne-Kamikaze <jeannekamikaze@gmail.com> | 2013-06-13 23:02:08 +0200 |
---|---|---|
committer | Jeanne-Kamikaze <jeannekamikaze@gmail.com> | 2013-06-13 23:02:08 +0200 |
commit | 04313774991dc503844ddd2c47529aca8280aa6c (patch) | |
tree | b02e35cd3b7d629e3e94fdb1b067f382207d5ce5 | |
parent | 13bcad5881dd97ab01e6ab8358da1320d42ea95b (diff) |
GameObject
-rw-r--r-- | Spear/Scene/GameObject.hs | 320 |
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 @@ | |||
1 | module 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 | ) | ||
31 | where | ||
32 | |||
33 | |||
34 | import Spear.GL | ||
35 | import Spear.Math.AABB | ||
36 | import qualified Spear.Math.Camera as Cam | ||
37 | import Spear.Math.Collision as Col | ||
38 | import qualified Spear.Math.Matrix3 as M3 | ||
39 | import qualified Spear.Math.Matrix4 as M4 | ||
40 | import Spear.Math.MatrixUtils | ||
41 | import qualified Spear.Math.Spatial2 as S2 | ||
42 | import qualified Spear.Math.Spatial3 as S3 | ||
43 | import Spear.Math.Utils | ||
44 | import Spear.Math.Vector | ||
45 | import qualified Spear.Render.AnimatedModel as AM | ||
46 | import Spear.Render.Program | ||
47 | import Spear.Render.StaticModel as SM | ||
48 | |||
49 | import Data.Fixed (mod') | ||
50 | import Data.List (foldl') | ||
51 | |||
52 | |||
53 | -- | Game style. | ||
54 | data GameStyle | ||
55 | = RPG -- ^ RPG or RTS style game. | ||
56 | | PLT -- ^ Platformer or space invaders style game. | ||
57 | |||
58 | |||
59 | data 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 | |||
69 | dummyWindow = Window M4.id M4.id 0 0 640 480 | ||
70 | |||
71 | |||
72 | -- | An object in the game scene. | ||
73 | data 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 | |||
84 | instance 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. | ||
171 | goNew :: GameStyle | ||
172 | -> Either StaticModelResource AM.AnimatedModelResource | ||
173 | -> [Collisioner2] | ||
174 | -> M3.Matrix3 -- ^ Transform | ||
175 | -> Vector3 -- ^ Axis of rotation | ||
176 | -> GameObject | ||
177 | |||
178 | goNew style (Left smr) cols transf axis = GameObject | ||
179 | style (Left $ SM.staticModelRenderer smr) cols transf axis 0 dummyWindow | ||
180 | |||
181 | goNew style (Right amr) cols transf axis = GameObject | ||
182 | style (Right $ AM.animatedModelRenderer 1 amr) cols transf axis 0 dummyWindow | ||
183 | |||
184 | |||
185 | goUpdate :: Float -> GameObject -> GameObject | ||
186 | goUpdate 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. | ||
207 | goRPGtransform :: GameObject -> M4.Matrix4 | ||
208 | goRPGtransform 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. | ||
214 | currentAnimation :: Enum a => GameObject -> a | ||
215 | currentAnimation 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. | ||
221 | numCollisioners :: GameObject -> Int | ||
222 | numCollisioners = length . collisioners | ||
223 | |||
224 | |||
225 | -- | Set the game object's current animation. | ||
226 | setAnimation :: Enum a => a -> GameObject -> GameObject | ||
227 | setAnimation 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. | ||
233 | setAnimationSpeed :: AM.AnimationSpeed -> GameObject -> GameObject | ||
234 | setAnimationSpeed 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. | ||
240 | setAxis :: Vector3 -> GameObject -> GameObject | ||
241 | setAxis ax go = go { axis = ax } | ||
242 | |||
243 | |||
244 | -- | Set the game object's collisioners. | ||
245 | setCollisioners :: [Collisioner2] -> GameObject -> GameObject | ||
246 | setCollisioners cols go = go { collisioners = cols } | ||
247 | |||
248 | |||
249 | -- | Set the game object's window. | ||
250 | setWindow :: Window -> GameObject -> GameObject | ||
251 | setWindow wnd go = go { window = wnd } | ||
252 | |||
253 | |||
254 | -- | Manipulate the game object's collisioners. | ||
255 | withCollisioners :: GameObject -> ([Collisioner2] -> [Collisioner2]) -> GameObject | ||
256 | withCollisioners go f = go { collisioners = f $ collisioners go } | ||
257 | |||
258 | |||
259 | -- | Render the game object. | ||
260 | goRender :: StaticProgram -> AnimatedProgram -> Cam.Camera -> GameObject -> IO () | ||
261 | goRender 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 | |||
283 | type Bind = IO () | ||
284 | |||
285 | type Render = IO () | ||
286 | |||
287 | |||
288 | goRender' :: (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 () | ||
300 | goRender' 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. | ||
312 | goCollide :: GameObject -> GameObject -> Bool | ||
313 | goCollide 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 | ||