aboutsummaryrefslogtreecommitdiff
path: root/Demos/Pong/Main.hs
blob: ac0feab67b3700e17ee29a79a6265cea0859485b (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
module Main where

import           Data.Maybe                              (mapMaybe)
import           Graphics.Rendering.OpenGL.GL            (($=))
import qualified Graphics.Rendering.OpenGL.GL            as GL
import           Graphics.Rendering.OpenGL.GL.VertexSpec (currentColor)
import           Pong
import           Spear.App
import           Spear.Game
import           Spear.Math.AABB
import           Spear.Math.Spatial
import           Spear.Math.Spatial2
import           Spear.Math.Vector
import           Spear.Window

data GameState = GameState
  { window :: Window,
    world  :: [GameObject]
  }

app = App step render resize

main =
  withWindow (900, 600) (2, 0) (Just "Pong") initGame $
    loop app

initGame :: Window -> Game () GameState
initGame window = return $ GameState window newWorld

step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool
step elapsed dt inputEvents = do
  gs <- getGameState
  let events = translateEvents inputEvents
  modifyGameState $ \gs ->
    gs
      { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gs)
      }
  return (not $ exitRequested inputEvents)

render :: Game GameState ()
render = getGameState >>= \gs -> gameIO . render' $ world gs

render' :: [GameObject] -> IO ()
render' world = do
  -- Clear the background to a different colour than the playable area to make
  -- the latter distinguishable.
  GL.clearColor $= GL.Color4 0.2 0.2 0.2 0.0
  GL.clear [GL.ColorBuffer]
  GL.matrixMode $= GL.Modelview 0
  GL.loadIdentity
  renderBackground
  -- Draw objects.
  GL.currentColor $= GL.Color4 1.0 1.0 1.0 1.0
  mapM_ renderGO world

renderBackground :: IO ()
renderBackground =
  let pmin = 0 :: Float
      pmax = 1 :: Float
  in do
    GL.currentColor $= GL.Color4 0.7 0.5 0.7 1.0
    GL.renderPrimitive GL.TriangleStrip $ do
        GL.vertex (GL.Vertex2 pmin pmax)
        GL.vertex (GL.Vertex2 pmin pmin)
        GL.vertex (GL.Vertex2 pmax pmax)
        GL.vertex (GL.Vertex2 pmax pmin)

renderGO :: GameObject -> IO ()
renderGO go = do
  let (AABB2 (Vector2 xmin' ymin') (Vector2 xmax' ymax')) = aabb go
      (Vector2 xcenter ycenter) = position go
      (xmin, ymin, xmax, ymax) = (f2d xmin', f2d ymin', f2d xmax', f2d ymax')
  GL.preservingMatrix $ do
    GL.translate (GL.Vector3 (f2d xcenter) (f2d ycenter) 0)
    GL.renderPrimitive GL.TriangleStrip $ do
      GL.vertex (GL.Vertex2 xmin ymax)
      GL.vertex (GL.Vertex2 xmin ymin)
      GL.vertex (GL.Vertex2 xmax ymax)
      GL.vertex (GL.Vertex2 xmax ymin)

resize :: WindowEvent -> Game s ()
resize (ResizeEvent w h) =
  let r = fromIntegral w / fromIntegral h
      pad    = if r > 1 then (r-1) / 2 else (1/r - 1) / 2
      left   = if r > 1 then -pad else 0
      right  = if r > 1 then 1 + pad else 1
      bottom = if r > 1 then 0 else -pad
      top    = if r > 1 then 1 else 1 + pad
  in gameIO $ do
    GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral w) (fromIntegral h))
    GL.matrixMode $= GL.Projection
    GL.loadIdentity
    GL.ortho left right bottom top (-1) 1
    GL.matrixMode $= GL.Modelview 0

translateEvents = mapMaybe translateEvents'
  where translateEvents' (KeyDown KEY_LEFT)  = Just MoveLeft
        translateEvents' (KeyDown KEY_RIGHT) = Just MoveRight
        translateEvents' (KeyUp KEY_LEFT)    = Just StopLeft
        translateEvents' (KeyUp KEY_RIGHT)   = Just StopRight
        translateEvents' _                   = Nothing

exitRequested = elem (KeyDown KEY_ESC)

f2d :: Float -> GL.GLdouble
f2d = realToFrac