SoccerFunGL-0.5.3: SoccerFun/UI/GL.hs
{-# LANGUAGE UnicodeSyntax #-}
-- | Usage: Hit /q/ to abort the match
module SoccerFun.UI.GL where
import qualified Graphics.UI.GLUT as GL
import Graphics.UI.GLUT (($=), get)
import Data.IORef
import Control.Monad
import Unsafe.Coerce
import System.Exit
import Prelude.Unicode
import SoccerFun.MatchControl
import SoccerFun.Types
import SoccerFun.Geometry
import SoccerFun.Referee.Ivanov
import SoccerFun.RefereeAction
import System.Random
import SoccerFun.MatchGame
import SoccerFun.Player
import SoccerFun.Team (Team)
import SoccerFun.Tape
import Data.Maybe
import SoccerFun.Field
import SoccerFun.Ball
gameLength ∷ TimeUnit
gameLength = 1
initialise ∷ IO (String,[String])
initialise = GL.getArgsAndInitialize
runMatch ∷ (Home → Field → Team) → (Home → Field → Team) → IO ()
runMatch t1 t2 = do
initialise
playTape =<< liftM recordMatch (setupMatch t1 t2)
-- GL.initialDisplayCapabilities $= [GL.With GL.DisplayDouble, GL.With GL.DisplaySamples]
-- GL.multisample $= GL.Enabled
playTape ∷ Tape → IO ()
playTape (Tape tape) = do
GL.initialDisplayMode $= [GL.DoubleBuffered, GL.Multisampling]
p ← get GL.displayModePossible
when (not p) $ do
GL.initialDisplayMode $= [GL.DoubleBuffered]
p ← get GL.displayModePossible
when (not p) $ GL.initialDisplayMode $= []
window ← GL.createWindow "SoccerFun"
GL.clearColor $= (GL.Color4 0 0.5 0 0 ∷ GL.Color4 GL.GLclampf) -- green
aspect ← newIORef 1
registerCallbacks window aspect =<< newIORef tape
GL.cursor $= GL.LeftArrow
GL.mainLoop
GL.exit
registerCallbacks ∷ GL.Window → IORef GL.GLdouble → IORef [Step] → IO ()
registerCallbacks window aspect tape = do
GL.displayCallback $= display
GL.reshapeCallback $= Just reshape
GL.keyboardMouseCallback $= Just inputCallback
gameLoop
-- GL.keyboardMouseCallback $= Just inputCallback
where
inputCallback ∷ GL.Key → GL.KeyState → GL.Modifiers → GL.Position → IO ()
inputCallback (GL.Char 'q') _ _ _ = exitSuccess
inputCallback _ _ _ _ = return ()
gameLoop = do
((refereeActions,playerWithActions),match) : tape' ← readIORef tape
when (null tape') GL.exit
writeIORef tape tape'
GL.postRedisplay $ Just window
let actions = mapMaybe logRefereeAction refereeActions
when (not $ GameOver `elem` refereeActions) $ GL.addTimerCallback 30 gameLoop
mapM_ putStrLn actions
reshape s@(GL.Size w h) = do
writeIORef aspect newAspect
GL.viewport $= (GL.Position 0 0, s)
GL.matrixMode $= GL.Projection
GL.loadIdentity
GL.perspective 0 newAspect (-1) 1
GL.matrixMode $= GL.Modelview 0
where newAspect = fromIntegral w / fromIntegral (max 1 h)
display = do
m@Match {theField = field, team1 = t1, team2 = t2, theBall = ball, score = score, playingTime = time} ← liftM (snd ∘ head) $ readIORef tape
GL.clear [GL.ColorBuffer]
GL.loadIdentity
a ← readIORef aspect
if a < 1 then GL.ortho2D (-1) 1 (-1/a) (1/a) else GL.ortho2D (-1*a) (1*a) (-1) 1
let zoom = convertFloat $ 2.1 / flength field
GL.scale zoom zoom 1
GL.translate $ vector2 (-flength field/2,-fwidth field/2)
renderStatus m
GL.lineWidth $= 3
renderField field
GL.lineWidth $= 2
renderBall ball m
colorRGB (1,0,0) -- red
mapM_ renderPlayer t1
colorRGB (0,0,1) -- blue
mapM_ renderPlayer t2
GL.swapBuffers
renderStatus Match {theField = field, team1 = t1, team2 = t2, theBall = ball, score = score, playingTime = time} = do
colorRGB (0,0,0) -- black
GL.preservingMatrix $ do
GL.translate $ vector2 (0, fwidth field + 2)
GL.lineWidth $= 2
drawStatus $ show (fst score) ++ ":" ++ show (snd score) ++ " " ++ show (round $ (1 - time) * 90) ++ ":00"
renderField field@Field {flength = l, fwidth = w} = do
colorRGB (0.8,1,0.8) -- green-white
GL.renderPrimitive GL.LineLoop $ do -- side line
vertex2 (0,0)
vertex2 (l,0)
vertex2 (l,w)
vertex2 (0,w)
GL.renderPrimitive GL.LineStrip $ do -- middle line
vertex2 (l/2,0)
vertex2 (l/2,w)
GL.renderPrimitive GL.LineStrip $ do -- west penalty area
vertex2 (0, w/2 + radiusPenaltyArea)
vertex2 (penaltyAreaDepth, w/2 + radiusPenaltyArea)
vertex2 (penaltyAreaDepth, w/2 - radiusPenaltyArea)
vertex2 (0, w/2 - radiusPenaltyArea)
GL.renderPrimitive GL.LineStrip $ do -- east penalty area
vertex2 (l, w/2 + radiusPenaltyArea)
vertex2 (l - penaltyAreaDepth, w/2 + radiusPenaltyArea)
vertex2 (l - penaltyAreaDepth, w/2 - radiusPenaltyArea)
vertex2 (l, w/2 - radiusPenaltyArea)
GL.preservingMatrix $ do -- centre circle and centre spot
GL.translate $ vector2 (l/2,w/2)
GL.renderPrimitive GL.LineLoop $ circle radiusCentreCircle 23
GL.renderPrimitive GL.Polygon $ circle radiusCentreSpot 7
GL.preservingMatrix $ do
GL.translate $ vector2 (penaltySpotDepth, w/2)
GL.renderPrimitive GL.Polygon $ circle radiusPenaltySpot 7
GL.preservingMatrix $ do
GL.translate $ vector2 (l - penaltySpotDepth, w/2)
GL.renderPrimitive GL.Polygon $ circle radiusPenaltySpot 7
colorRGB (1,1,1) -- white
mapM_ renderPole [(x,y) | let (n,s) = goalPoles field, y ← [n,s], x ← [0,l]] -- goal poles
renderPole pos = GL.preservingMatrix $ do
colorRGB (0.3,0.3,0.3)
GL.translate $ vector2 pos
GL.renderPrimitive GL.Polygon (circle goalPoleWidth 7)
renderBall ball match = do
let p = case ball of
GainedBy pid → toPosition3D $ pos $ fromJust $ lookupPlayer pid match
Free Ball {ballPos = p} → p
colorRGB (1,1,1) -- white
drawAt3D p $ GL.renderPrimitive GL.Polygon (circle radiusBall 7)
renderPlayer Player {pos = p, playerID = id} = drawAt p $ do
square
drawString $ show $ playerNo id
colorRGB ∷ (GL.GLfloat,GL.GLfloat,GL.GLfloat) → IO ()
colorRGB (r,g,b) = GL.color $ GL.Color3 r g b
--dot3D ∷ Position3D → IO ()
--dot3D pos3D = dot $ pxy pos3D
drawAt3D ∷ Position3D → IO () → IO ()
drawAt3D pos = drawAt (pxy pos)
drawAt ∷ Position → IO () → IO ()
drawAt pos draw = GL.preservingMatrix $ do
GL.translate $ vector2 (px pos, py pos)
draw
square ∷ IO ()
square = GL.preservingMatrix $ do
GL.renderPrimitive GL.LineLoop $ do
vertex2 (-0.7,-0.7)
vertex2 (0.7,-0.7)
vertex2 (0.7,0.7)
vertex2 (-0.7,0.7)
vector2 ∷ (Float,Float) → GL.Vector3 GL.GLfloat
vector2 (x,y) = GL.Vector3 (convertFloat x) (convertFloat y) 0
vertex2 ∷ (Float,Float) → IO ()
vertex2 (x,y) = GL.vertex $ GL.Vertex2 (convertFloat x) (convertFloat y)
circle r = oval r r
oval r1 r2 step = mapM_ vertex2 vs where
is = take (truncate step + 1) [0, i' .. ]
i' = 2 * pi / step
vs = [ (r1 * cos i, r2 * sin i) | i <- is ]
{-
drawPort pos = GL.preservingMatrix $ do
GL.translate $ vector pos
GL.renderPrimitive GL.Polygon (circle 0.15 0.15 10)
drawNode label = do
GL.renderPrimitive GL.LineLoop (circle 1 1 20)
drawString label
-}
drawString label = GL.preservingMatrix $ do
GL.translate $ GL.Vector3 (-0.3) (-0.3) (0 ∷ GL.GLfloat)
GL.scale 0.007 0.007 (0 ∷ GL.GLdouble)
GL.renderString GL.MonoRoman label
drawStatus label = GL.preservingMatrix $ do
GL.translate $ GL.Vector3 (-0.3) (-0.3) (0 ∷ GL.GLfloat)
GL.scale 0.03 0.03 (0 ∷ GL.GLdouble)
GL.renderString GL.MonoRoman label
convertDouble ∷ Double → GL.GLdouble
convertDouble = unsafeCoerce
convertFloat ∷ Float → GL.GLfloat
convertFloat = unsafeCoerce