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