Moved source files to src directory

This commit is contained in:
Matthias Schiffer 2010-03-09 03:49:15 +01:00
parent 2bb8561836
commit 7327695ca3
14 changed files with 3 additions and 6 deletions

158
src/Bindings/GLPng.hsc Normal file
View file

@ -0,0 +1,158 @@
{-# LANGUAGE ForeignFunctionInterface, DeriveDataTypeable #-}
module Bindings.GLPng ( PngInfo(..)
, Mipmap(..)
, Trans (..)
, pngBind
) where
import Data.Generics
import Foreign.C.String (CString, withCString)
import Foreign.C.Types
import Foreign.Marshal.Alloc (alloca)
import Foreign.Ptr
import Foreign.Storable
import Graphics.Rendering.OpenGL.GL.Texturing.Parameters (Repetition(..), Clamping(..), TextureFilter(..), MinificationFilter, MagnificationFilter)
#include <GL/gl.h>
#include <GL/glpng.h>
data PngInfo = PngInfo
{ pngWidth :: !CUInt
, pngHeight :: !CUInt
, pngDepth :: !CUInt
, pngAlpha :: !CUInt
} deriving (Eq, Ord, Show, Typeable)
instance Storable PngInfo where
sizeOf _ = (#size pngInfo)
alignment _ = alignment (undefined :: CUInt)
peek pi = do
w <- (#peek pngInfo, Width) pi
h <- (#peek pngInfo, Height) pi
d <- (#peek pngInfo, Depth) pi
a <- (#peek pngInfo, Alpha) pi
return (PngInfo w h d a)
poke pi (PngInfo w h d a) = do
(#poke pngInfo, Width) pi w
(#poke pngInfo, Height) pi h
(#poke pngInfo, Depth) pi d
(#poke pngInfo, Alpha) pi a
png_NoMipmap :: CInt
png_NoMipmap = (#const PNG_NOMIPMAP)
png_BuildMipmap :: CInt
png_BuildMipmap = (#const PNG_BUILDMIPMAP)
png_SimpleMipmap :: CInt
png_SimpleMipmap = (#const PNG_SIMPLEMIPMAP)
data Mipmap = NoMipmap | BuildMipmap | SimpleMipmap
deriving (Eq, Show)
marshalMipmap :: Mipmap -> CInt
marshalMipmap m
| m == NoMipmap = png_NoMipmap
| m == BuildMipmap = png_BuildMipmap
| m == SimpleMipmap = png_SimpleMipmap
png_Alpha :: CInt
png_Alpha = (#const PNG_ALPHA)
png_Solid :: CInt
png_Solid = (#const PNG_SOLID)
data Trans = Alpha | Solid
deriving (Eq, Show)
marshalTrans :: Trans -> CInt
marshalTrans t
| t == Alpha = png_Alpha
| t == Solid = png_Solid
magToMin :: MagnificationFilter -> MinificationFilter
magToMin magFilter = (magFilter, Nothing)
gl_NEAREST :: CInt
gl_NEAREST = (#const GL_NEAREST)
gl_LINEAR :: CInt
gl_LINEAR = (#const GL_LINEAR)
gl_NEAREST_MIPMAP_NEAREST :: CInt
gl_NEAREST_MIPMAP_NEAREST = (#const GL_NEAREST_MIPMAP_NEAREST)
gl_LINEAR_MIPMAP_NEAREST :: CInt
gl_LINEAR_MIPMAP_NEAREST = (#const GL_LINEAR_MIPMAP_NEAREST)
gl_NEAREST_MIPMAP_LINEAR :: CInt
gl_NEAREST_MIPMAP_LINEAR = (#const GL_NEAREST_MIPMAP_LINEAR)
gl_LINEAR_MIPMAP_LINEAR :: CInt
gl_LINEAR_MIPMAP_LINEAR = (#const GL_LINEAR_MIPMAP_LINEAR)
marshalMinificationFilter :: MinificationFilter -> CInt
marshalMinificationFilter x = fromIntegral $ case x of
(Nearest, Nothing ) -> gl_NEAREST
(Linear', Nothing ) -> gl_LINEAR
(Nearest, Just Nearest) -> gl_NEAREST_MIPMAP_NEAREST
(Linear', Just Nearest) -> gl_LINEAR_MIPMAP_NEAREST
(Nearest, Just Linear') -> gl_NEAREST_MIPMAP_LINEAR
(Linear', Just Linear') -> gl_LINEAR_MIPMAP_LINEAR
marshalMagnificationFilter :: MagnificationFilter -> CInt
marshalMagnificationFilter = marshalMinificationFilter . magToMin
gl_CLAMP :: CInt
gl_CLAMP = (#const GL_CLAMP)
gl_REPEAT :: CInt
gl_REPEAT = (#const GL_REPEAT)
gl_CLAMP_TO_EDGE :: CInt
gl_CLAMP_TO_EDGE = (#const GL_CLAMP_TO_EDGE)
gl_CLAMP_TO_BORDER :: CInt
gl_CLAMP_TO_BORDER = (#const GL_CLAMP_TO_BORDER)
gl_MIRRORED_REPEAT :: CInt
gl_MIRRORED_REPEAT = (#const GL_MIRRORED_REPEAT)
marshalTextureWrapMode :: (Repetition, Clamping) -> CInt
marshalTextureWrapMode x = fromIntegral $ case x of
(Repeated, Clamp) -> gl_CLAMP
(Repeated, Repeat) -> gl_REPEAT
(Repeated, ClampToEdge) -> gl_CLAMP_TO_EDGE
(Repeated, ClampToBorder) -> gl_CLAMP_TO_BORDER
(Mirrored, Repeat) -> gl_MIRRORED_REPEAT
_ -> error ("marshalTextureWrapMode: illegal value " ++ show x)
foreign import ccall unsafe "GL/glpng.h pngBind"
rawPngBind :: CString -> CInt -> CInt -> Ptr PngInfo -> CInt -> CInt -> CInt -> IO CUInt
pngBind :: String -> Mipmap -> Trans -> (Repetition, Clamping) -> MinificationFilter -> MagnificationFilter -> IO (CUInt, PngInfo)
pngBind name mipmap trans wrapst minfilter magfilter = alloca $ \infop -> withCString name $ \cname -> do
ret <- rawPngBind cname (marshalMipmap mipmap) (marshalTrans trans) infop (marshalTextureWrapMode wrapst)
(marshalMinificationFilter minfilter) (marshalMagnificationFilter magfilter)
info <- peek infop
return (ret, info)

265
src/Bindings/GLX.hsc Normal file
View file

@ -0,0 +1,265 @@
{-# LANGUAGE ForeignFunctionInterface, DeriveDataTypeable, GeneralizedNewtypeDeriving #-}
module Bindings.GLX ( createColormap
, createWindow
, setClassHint
, chooseFBConfig
, getVisualFromFBConfig
, VisualInfo(..)
, SetWindowAttributes(..)
, nullSetWindowAttributes
, renderType
, rgbaBit
, drawableType
, windowBit
, xRenderable
, doublebuffer
, depthSize
, stencilSize
, createContext
, makeCurrent
, destroyContext
, swapBuffers
, Context(..)
, Drawable
) where
import Data.Generics
import Data.Int
import Data.Word
import Foreign.C.String (withCString)
import Foreign.C.Types
import Foreign.Ptr
import Foreign.Marshal.Alloc (alloca, allocaBytes)
import Foreign.Marshal.Array (peekArray, withArray0)
import Foreign.Storable
import Graphics.X11.Types (AttributeMask, Colormap, ColormapAlloc, Cursor, EventMask, Pixmap, Window, WindowClass, VisualID, XID)
import Graphics.X11.Xlib.Extras (none, xFree, ClassHint, resName, resClass, TextProperty)
import Graphics.X11.Xlib.Types (Dimension, Display, Pixel, Position)
#include <GL/glx.h>
type Drawable = XID
newtype FBConfig = FBConfig (Ptr FBConfig)
deriving (Eq, Ord, Show, Typeable, Data, Storable)
newtype Context = Context (Ptr Context)
deriving (Eq, Ord, Show, Typeable, Data, Storable)
newtype Visual = Visual (Ptr Visual)
deriving (Eq, Ord, Show, Typeable, Data, Storable)
data VisualInfo = VisualInfo
{ viVisual :: !Visual
, viVisualid :: !VisualID
, viScreen :: !CInt
, viDepth :: !CInt
, viClass :: !CInt
, viRedMask :: !CULong
, viGreenMask :: !CULong
, viBlueMask :: !CULong
, viColormapSize :: !CInt
, viBitsPerRgb :: !CInt
} deriving (Eq, Ord, Show, Typeable)
instance Storable VisualInfo where
sizeOf _ = (#size XVisualInfo)
alignment _ = alignment (undefined :: CULong)
peek vi = do
visual <- (#peek XVisualInfo, visual) vi
visualid <- (#peek XVisualInfo, visualid) vi
screen <- (#peek XVisualInfo, screen) vi
depth <- (#peek XVisualInfo, depth) vi
viclass <- (#peek XVisualInfo, class) vi
red_mask <- (#peek XVisualInfo, red_mask) vi
green_mask <- (#peek XVisualInfo, green_mask) vi
blue_mask <- (#peek XVisualInfo, blue_mask) vi
colormap_size <- (#peek XVisualInfo, colormap_size) vi
bits_per_rgb <- (#peek XVisualInfo, bits_per_rgb) vi
return (VisualInfo visual visualid screen depth viclass red_mask green_mask blue_mask colormap_size bits_per_rgb)
poke vi (VisualInfo visual visualid screen depth viclass red_mask green_mask blue_mask colormap_size bits_per_rgb) = do
(#poke XVisualInfo, visual) vi visual
(#poke XVisualInfo, visualid) vi visualid
(#poke XVisualInfo, screen) vi screen
(#poke XVisualInfo, depth) vi depth
(#poke XVisualInfo, class) vi viclass
(#poke XVisualInfo, red_mask) vi red_mask
(#poke XVisualInfo, green_mask) vi green_mask
(#poke XVisualInfo, blue_mask) vi blue_mask
(#poke XVisualInfo, colormap_size) vi colormap_size
(#poke XVisualInfo, bits_per_rgb) vi bits_per_rgb
data SetWindowAttributes = SetWindowAttributes
{ swaBackgroundPixmap :: !Pixmap
, swaBackgroundPixel :: !Pixel
, swaBorderPixmap :: !Pixmap
, swaBitGravity :: !CInt
, swaWinGravity :: !CInt
, swaBackingStore :: !CInt
, swaBackingPlanes :: !CULong
, swaBackingPixel :: !CULong
, swaSaveUnder :: !Bool
, swaEventMask :: !EventMask
, swaDoNotPropagateMask :: !CULong
, swaOverrideRedirect :: !Bool
, swaColormap :: !Colormap
, swaCursor :: !Cursor
} deriving (Eq, Ord, Show, Typeable)
instance Storable SetWindowAttributes where
sizeOf _ = (#size XSetWindowAttributes)
alignment _ = alignment (undefined :: CULong)
peek swa = do
background_pixmap <- (#peek XSetWindowAttributes, background_pixmap) swa
background_pixel <- (#peek XSetWindowAttributes, background_pixel) swa
border_pixmap <- (#peek XSetWindowAttributes, border_pixmap) swa
bit_gravity <- (#peek XSetWindowAttributes, bit_gravity) swa
win_gravity <- (#peek XSetWindowAttributes, win_gravity) swa
backing_store <- (#peek XSetWindowAttributes, backing_store) swa
backing_planes <- (#peek XSetWindowAttributes, backing_planes) swa
backing_pixel <- (#peek XSetWindowAttributes, backing_pixel) swa
save_under <- (#peek XSetWindowAttributes, save_under) swa
event_mask <- (#peek XSetWindowAttributes, event_mask) swa
do_not_propagate_mask <- (#peek XSetWindowAttributes, do_not_propagate_mask) swa
override_redirect <- (#peek XSetWindowAttributes, override_redirect) swa
colormap <- (#peek XSetWindowAttributes, colormap) swa
cursor <- (#peek XSetWindowAttributes, cursor) swa
return (SetWindowAttributes
background_pixmap
background_pixel
border_pixmap
bit_gravity
win_gravity
backing_store
backing_planes
backing_pixel
save_under
event_mask
do_not_propagate_mask
override_redirect
colormap
cursor)
poke swa (SetWindowAttributes
background_pixmap
background_pixel
border_pixmap
bit_gravity
win_gravity
backing_store
backing_planes
backing_pixel
save_under
event_mask
do_not_propagate_mask
override_redirect
colormap
cursor) = do
(#poke XSetWindowAttributes, background_pixmap) swa background_pixmap
(#poke XSetWindowAttributes, background_pixel) swa background_pixel
(#poke XSetWindowAttributes, border_pixmap) swa border_pixmap
(#poke XSetWindowAttributes, bit_gravity) swa bit_gravity
(#poke XSetWindowAttributes, win_gravity) swa win_gravity
(#poke XSetWindowAttributes, backing_store) swa backing_store
(#poke XSetWindowAttributes, backing_planes) swa backing_planes
(#poke XSetWindowAttributes, backing_pixel) swa backing_pixel
(#poke XSetWindowAttributes, save_under) swa save_under
(#poke XSetWindowAttributes, event_mask) swa event_mask
(#poke XSetWindowAttributes, do_not_propagate_mask) swa do_not_propagate_mask
(#poke XSetWindowAttributes, override_redirect) swa override_redirect
(#poke XSetWindowAttributes, colormap) swa colormap
(#poke XSetWindowAttributes, cursor) swa cursor
nullSetWindowAttributes :: SetWindowAttributes
nullSetWindowAttributes = (SetWindowAttributes 0 0 0 0 0 0 0 0 False 0 0 False 0 0)
foreign import ccall unsafe "GL/glx.h XCreateColormap"
createColormap :: Display -> Window -> Visual -> ColormapAlloc -> IO Colormap
foreign import ccall unsafe "GL/glx.h XCreateWindow"
createWindow :: Display -> Window -> Position -> Position ->
Dimension -> Dimension -> CInt -> CInt -> WindowClass ->
Visual -> AttributeMask -> Ptr SetWindowAttributes -> IO Window
foreign import ccall unsafe "GL/glx.h XSetClassHint"
xSetClassHint :: Display -> Window -> Ptr ClassHint -> IO ()
setClassHint :: Display -> Window -> ClassHint -> IO ()
setClassHint disp wnd hint = allocaBytes (#size XClassHint) $ \p ->
withCString (resName hint) $ \res_name ->
withCString (resClass hint) $ \res_class -> do
(#poke XClassHint, res_name) p res_name
(#poke XClassHint, res_class) p res_class
xSetClassHint disp wnd p
foreign import ccall unsafe "GL/glx.h glXChooseFBConfig"
glXChooseFBConfig :: Display -> CInt -> Ptr CInt -> Ptr CInt -> IO (Ptr FBConfig)
chooseFBConfig :: Display -> CInt -> [(CInt, CInt)] -> IO [FBConfig]
chooseFBConfig disp sc attr = alloca $ \n -> withArray0 (fromIntegral none) (concatMap (\(a,b) -> [a,b]) attr) $ \attrp -> do
configs <- glXChooseFBConfig disp sc attrp n
nelements <- peek n
configlist <- peekArray (fromIntegral nelements) configs
xFree configs
return configlist
renderType :: CInt
renderType = (#const GLX_RENDER_TYPE)
rgbaBit :: CInt
rgbaBit = (#const GLX_RGBA_BIT)
drawableType :: CInt
drawableType = (#const GLX_DRAWABLE_TYPE)
windowBit :: CInt
windowBit = (#const GLX_WINDOW_BIT)
xRenderable :: CInt
xRenderable = (#const GLX_X_RENDERABLE)
doublebuffer :: CInt
doublebuffer = (#const GLX_DOUBLEBUFFER)
depthSize :: CInt
depthSize = (#const GLX_DEPTH_SIZE)
stencilSize :: CInt
stencilSize = (#const GLX_STENCIL_SIZE)
foreign import ccall unsafe "GL/glx.h glXGetVisualFromFBConfig"
glXGetVisualFromFBConfig :: Display -> FBConfig -> IO (Ptr VisualInfo)
getVisualFromFBConfig :: Display -> FBConfig -> IO (VisualInfo)
getVisualFromFBConfig disp config = do
viptr <- glXGetVisualFromFBConfig disp config
vi <- peek viptr
xFree viptr
return vi
foreign import ccall unsafe "GL/glx.h glXCreateContext"
createContext :: Display -> Ptr VisualInfo -> Context -> Bool -> IO Context
foreign import ccall unsafe "GL/glx.h glXMakeCurrent"
makeCurrent :: Display -> Drawable -> Context -> IO Bool
foreign import ccall unsafe "GL/glx.h glXDestroyContext"
destroyContext :: Display -> Context -> IO ()
foreign import ccall unsafe "GL/glx.h glXSwapBuffers"
swapBuffers :: Display -> Drawable -> IO ()

19
src/CPUPlayer.hs Normal file
View file

@ -0,0 +1,19 @@
{-# LANGUAGE DeriveDataTypeable #-}
module CPUPlayer ( CPUPlayer(..)
) where
import Data.Fixed
import Data.Ratio ((%))
import Data.Typeable
import GLDriver
import Player
data CPUPlayer = CPUPlayer Micro
deriving (Typeable, Show)
instance Player CPUPlayer where
playerUpdate (CPUPlayer angle) _ = (CPUPlayer (if (angle+0.1) > 180 then angle-359.9 else angle+0.1), Just angle, True, Just (-angle), False)

36
src/DefaultPlayer.hs Normal file
View file

@ -0,0 +1,36 @@
{-# LANGUAGE DeriveDataTypeable, PatternGuards #-}
module DefaultPlayer ( DefaultPlayer(..)
) where
import qualified Data.Set as S
import Data.Fixed
import Data.Ratio ((%))
import Data.Typeable
import Game (Tank(..))
import GLDriver
import Player
data DefaultPlayer = DefaultPlayer (S.Set Key) Float Float Bool
deriving (Typeable, Show)
instance Player DefaultPlayer where
playerUpdate (DefaultPlayer keys aimx aimy shoot) tank =
let x = (if (S.member KeyLeft keys) then (-1) else 0) + (if (S.member KeyRight keys) then 1 else 0)
y = (if (S.member KeyDown keys) then (-1) else 0) + (if (S.member KeyUp keys) then 1 else 0)
ax = aimx - (fromRational . toRational . tankX $ tank)
ay = aimy - (fromRational . toRational . tankY $ tank)
move = (x /= 0 || y /= 0)
angle = if move then Just $ fromRational $ round ((atan2 y x)*1000000*180/pi)%1000000 else Nothing
aangle = if (ax /= 0 || ay /= 0) then Just $ fromRational $ round ((atan2 ay ax)*1000000*180/pi)%1000000 else Nothing
in (DefaultPlayer keys aimx aimy False, angle, move, aangle, shoot)
handleEvent (DefaultPlayer keys aimx aimy shoot) ev
| Just (KeyPressEvent key) <- fromEvent ev = DefaultPlayer (S.insert key keys) aimx aimy shoot
| Just (KeyReleaseEvent key) <- fromEvent ev = DefaultPlayer (S.delete key keys) aimx aimy shoot
| Just (MouseMotionEvent x y) <- fromEvent ev = DefaultPlayer keys x y shoot
| Just (MousePressEvent x y) <- fromEvent ev = DefaultPlayer keys x y True
| otherwise = DefaultPlayer keys aimx aimy shoot

67
src/GLDriver.hs Normal file
View file

@ -0,0 +1,67 @@
{-# LANGUAGE ExistentialQuantification, DeriveDataTypeable #-}
module GLDriver ( Driver(..)
, SomeDriver(..)
, Event
, SomeEvent(..)
, fromEvent
, QuitEvent(..)
, Key(..)
, KeyPressEvent(..)
, KeyReleaseEvent(..)
, MouseMotionEvent(..)
, MousePressEvent(..)
) where
import Data.Typeable
class Driver a where
initialized :: a -> Bool
initGL :: a -> IO a
deinitGL :: a -> IO ()
swapBuffers :: a -> IO ()
nextEvent :: a -> IO (a, Maybe SomeEvent)
data SomeDriver = forall d. Driver d => SomeDriver d
instance Driver SomeDriver where
initialized (SomeDriver d) = initialized d
initGL (SomeDriver d) = initGL d >>= return . SomeDriver
deinitGL (SomeDriver d) = deinitGL d
swapBuffers (SomeDriver d) = swapBuffers d
nextEvent (SomeDriver d) = nextEvent d >>= \(gl, ev) -> return (SomeDriver gl, ev)
class (Typeable a, Show a) => Event a
data SomeEvent = forall a. Event a => SomeEvent a
instance Show SomeEvent where
show (SomeEvent a) = show a
fromEvent :: Event a => SomeEvent -> Maybe a
fromEvent (SomeEvent a) = cast a
data QuitEvent = QuitEvent deriving (Typeable, Show)
instance Event QuitEvent
data Key = KeyLeft | KeyRight | KeyUp | KeyDown
deriving (Eq, Ord, Show)
data KeyPressEvent = KeyPressEvent Key deriving (Typeable, Show)
instance Event KeyPressEvent
data KeyReleaseEvent = KeyReleaseEvent Key deriving (Typeable, Show)
instance Event KeyReleaseEvent
data MouseMotionEvent = MouseMotionEvent Float Float deriving (Typeable, Show)
instance Event MouseMotionEvent
data MousePressEvent = MousePressEvent Float Float deriving (Typeable, Show)
instance Event MousePressEvent

214
src/GLX.hs Normal file
View file

@ -0,0 +1,214 @@
module GLX ( glxDriver
) where
import GLDriver
import Bindings.GLX
import Control.Monad (when, unless)
import Data.Bits ((.|.))
import Data.Maybe (isJust)
import Data.Ratio
import Graphics.Rendering.OpenGL.GL (($=), GLdouble, GLfloat, Vector3(..), Capability(..))
import Graphics.Rendering.OpenGL.GL.CoordTrans (matrixMode, MatrixMode(..), viewport, Position(..), Size(..), loadIdentity, ortho, translate)
import Graphics.X11.Types
import Graphics.X11.Xlib.Atom (internAtom)
import Graphics.X11.Xlib.Display (defaultScreen, openDisplay, rootWindow)
import Graphics.X11.Xlib.Event (XEventPtr, allocaXEvent, nextEvent, get_Window, get_EventType, pending)
import Graphics.X11.Xlib.Extras (ClassHint(..), getEvent, ev_event_type, ev_width, ev_height, ev_keycode, ev_data, ev_x, ev_y, getWindowAttributes, wa_width, wa_height)
import Graphics.X11.Xlib.Misc (keycodeToKeysym, setWMProtocols)
import Graphics.X11.Xlib.Types
import Graphics.X11.Xlib.Window (destroyWindow, mapWindow, storeName)
import Foreign.Marshal.Utils (with)
import Foreign.Ptr
import Foreign.Storable
data GLX = GLX
{ glxDisplay :: !Display
, glxWindow :: !Window
, glxContext :: !Context
, glxDeleteWindow :: !Atom
, glxScale :: !Rational
, glxLevelWidth :: !Int
, glxLevelHeight :: !Int
}
glxDriver :: Int -> Int -> GLX
glxDriver w h = GLX
{ glxDisplay = Display nullPtr
, glxWindow = 0
, glxContext = Context nullPtr
, glxDeleteWindow = 0
, glxScale = 1
, glxLevelWidth = w
, glxLevelHeight = h
}
instance Driver GLX where
initialized glx = ((glxContext glx) /= (Context nullPtr))
initGL glx = do
when (initialized glx) $ fail "GLX already initialized"
disp <- openDisplay ""
delwnd <- internAtom disp "WM_DELETE_WINDOW" False
fbconfigs <- chooseFBConfig disp (fromIntegral . defaultScreen $ disp)
[ (renderType, rgbaBit)
, (drawableType, windowBit)
, (doublebuffer, 1)
, (xRenderable, 1)
, (depthSize, 1)
, (stencilSize, 1)
]
visualinfo <- getVisualFromFBConfig disp (head fbconfigs)
rootwindow <- rootWindow disp (fromIntegral $ viScreen visualinfo)
cmap <- createColormap disp rootwindow (viVisual visualinfo) allocNone
let swa = nullSetWindowAttributes {swaColormap = cmap, swaEventMask = structureNotifyMask .|. keyPressMask .|. keyReleaseMask .|. pointerMotionMask .|. buttonPressMask}
wnd <- with swa $ \swaptr ->
createWindow disp rootwindow 0 0 800 600 0 (fromIntegral $ viDepth visualinfo) inputOutput (viVisual visualinfo) (cWBorderPixel.|.cWColormap.|.cWEventMask) swaptr
setClassHint disp wnd (ClassHint "HTanks" "htanks")
setWMProtocols disp wnd [delwnd]
storeName disp wnd "HTanks"
mapWindow disp wnd
waitForMapNotify disp wnd
ctx <- with visualinfo $ \vi -> createContext disp vi (Context nullPtr) True
makeCurrent disp wnd ctx
wa <- getWindowAttributes disp wnd
s <- resize (glxLevelWidth glx) (glxLevelHeight glx) (fromIntegral . wa_width $ wa) (fromIntegral . wa_height $ wa)
return glx
{ glxDisplay = disp
, glxWindow = wnd
, glxContext = ctx
, glxDeleteWindow = delwnd
, glxScale = s
}
deinitGL glx = do
destroyWindow (glxDisplay glx) (glxWindow glx)
destroyContext (glxDisplay glx) (glxContext glx)
swapBuffers glx = Bindings.GLX.swapBuffers (glxDisplay glx) (glxWindow glx)
nextEvent glx = allocaXEvent $ nextEvent' glx
nextEvent' :: GLX -> XEventPtr -> IO (GLX, Maybe SomeEvent)
nextEvent' glx xevent = do
p <- pending $ glxDisplay glx
if (p > 0) then do
Graphics.X11.Xlib.Event.nextEvent (glxDisplay glx) xevent
(newglx, ev) <- handleEvent glx xevent
if isJust ev then
return (newglx, ev)
else
nextEvent' newglx xevent
else
return (glx, Nothing)
handleEvent :: GLX -> XEventPtr -> IO (GLX, Maybe SomeEvent)
handleEvent glx xevent = do
event <- getEvent xevent
let evtype = ev_event_type event
case () of
_ | evtype == configureNotify -> do
s <- resize (glxLevelWidth glx) (glxLevelHeight glx) (fromIntegral . ev_width $ event) (fromIntegral . ev_height $ event)
return (glx {glxScale = s}, Nothing)
| evtype == keyPress -> do
keysym <- keycodeToKeysym (glxDisplay glx) (ev_keycode event) 0
case () of
_ | keysym == xK_Escape -> return (glx, Just $ SomeEvent QuitEvent)
| keysym == xK_Up -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyUp)
| keysym == xK_Down -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyDown)
| keysym == xK_Left -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyLeft)
| keysym == xK_Right -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyRight)
| keysym == xK_w -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyUp)
| keysym == xK_s -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyDown)
| keysym == xK_a -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyLeft)
| keysym == xK_d -> return (glx, Just $ SomeEvent $ KeyPressEvent KeyRight)
| otherwise -> return (glx, Nothing)
| evtype == keyRelease -> do
keysym <- keycodeToKeysym (glxDisplay glx) (ev_keycode event) 0
case () of
_ | keysym == xK_Up -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyUp)
| keysym == xK_Down -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyDown)
| keysym == xK_Left -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyLeft)
| keysym == xK_Right -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyRight)
| keysym == xK_w -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyUp)
| keysym == xK_s -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyDown)
| keysym == xK_a -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyLeft)
| keysym == xK_d -> return (glx, Just $ SomeEvent $ KeyReleaseEvent KeyRight)
| otherwise -> return (glx, Nothing)
| evtype == clientMessage -> do
if ((glxDeleteWindow glx) == (fromIntegral . head . ev_data $ event))
then
return (glx, Just $ SomeEvent QuitEvent)
else
return (glx, Nothing)
| evtype == motionNotify -> do
(x, y) <- windowToGameCoords glx (ev_x event) (ev_y event)
wa <- getWindowAttributes (glxDisplay glx) (glxWindow glx)
return (glx, Just $ SomeEvent $ MouseMotionEvent x y)
| evtype == buttonPress -> do
(x, y) <- windowToGameCoords glx (ev_x event) (ev_y event)
wa <- getWindowAttributes (glxDisplay glx) (glxWindow glx)
return (glx, Just $ SomeEvent $ MousePressEvent x y)
| otherwise -> return (glx, Nothing)
windowToGameCoords :: Integral a => GLX -> a -> a -> IO (Float, Float)
windowToGameCoords glx x y = getWindowAttributes (glxDisplay glx) (glxWindow glx) >>= \wa ->
let w = fromIntegral . wa_width $ wa
h = fromIntegral . wa_height $ wa
in return (((-w/2 + wx)/s + lw/2), ((h/2 - wy)/s + lh/2))
where s = fromRational . glxScale $ glx
lw = fromIntegral . glxLevelWidth $ glx
lh = fromIntegral . glxLevelHeight $ glx
wx = fromIntegral x
wy = fromIntegral y
resize :: Int -> Int -> Int -> Int -> IO Rational
resize lw lh w h = do
let aspect = (fromIntegral w)%(fromIntegral h)
s = (max ((fromIntegral lw)/aspect) (fromIntegral lh))/2
sf = fromRational s
aspectf = fromRational aspect
matrixMode $= Projection
loadIdentity
ortho (-sf*aspectf) (sf*aspectf) (-sf) sf (-1) 1
translate $ Vector3 (-(fromIntegral lw)/2) (-(fromIntegral lh)/2) (0 :: GLfloat)
matrixMode $= Modelview 0
viewport $= ((Position 0 0), (Size (fromIntegral w) (fromIntegral h)))
return $ (fromIntegral h)/(2*s)
waitForMapNotify :: Display -> Window -> IO ()
waitForMapNotify disp wnd = allocaXEvent waitForMapNotify'
where
waitForMapNotify' event = do
Graphics.X11.Xlib.Event.nextEvent disp event
window <- get_Window event
eventType <- get_EventType event
unless (window == wnd && eventType == mapNotify) $
waitForMapNotify' event

52
src/Game.hs Normal file
View file

@ -0,0 +1,52 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Game ( Tank(..)
, Shoot(..)
, GameState(..)
, Game
, runGame
) where
import Level
import Texture
import Control.Monad
import Control.Monad.State
import Data.Fixed
import qualified Data.Map as M
data Tank = Tank
{ tankX :: !Micro
, tankY :: !Micro
, tankDir :: !Micro
, tankAim :: !Micro
, tankSpeed :: !Micro
, tankTurnspeed :: !Micro
, tankMoving :: !Bool
, tankShootSpeed :: !Micro
, tankShootBounces :: !Int
, tankShootsLeft :: !Int
} deriving Show
data Shoot = Shoot
{ shootX :: !Micro
, shootY :: !Micro
, shootDir :: !Micro
, shootSpeed :: !Micro
, shootBouncesLeft :: !Int
, shootTank :: !Int
} deriving Show
data GameState = GameState
{ level :: !Level
, tanks :: ![Tank]
, shoots :: ![Shoot]
, textures :: !(M.Map Texture TextureObject)
} deriving (Show)
newtype Game a = Game (StateT GameState IO a)
deriving (Monad, MonadIO, MonadState GameState)
runGame :: GameState -> Game a -> IO (a, GameState)
runGame st (Game a) = runStateT a st

199
src/HTanks.hs Normal file
View file

@ -0,0 +1,199 @@
{-# LANGUAGE PatternGuards, GeneralizedNewtypeDeriving #-}
import Game
import Level
import Render
import Player
import CPUPlayer
import DefaultPlayer
import GLDriver
import GLX
import Control.Concurrent (threadDelay)
import Control.Monad.State
import Data.Fixed
import Data.Maybe
import qualified Data.Map as M
import Data.Ratio
import qualified Data.Set as S
import Data.Time.Clock
data MainState = MainState
{ run :: !Bool
, driver :: !SomeDriver
, time :: !UTCTime
, players :: ![SomePlayer]
}
newtype MainT m a = MainT (StateT MainState m a)
deriving (Monad, MonadState MainState, MonadIO, MonadTrans)
type Main = MainT Game
runMain :: MainState -> Main a -> Game (a, MainState)
runMain st (MainT a) = runStateT a st
main :: IO ()
main = do
let theLevel = testLevel
gl <- initGL $ glxDriver (levelWidth theLevel) (levelHeight theLevel)
when (initialized gl) $ do
currentTime <- getCurrentTime
let mainState = MainState {run = True, driver = SomeDriver gl, time = currentTime, players =
[ SomePlayer $ DefaultPlayer S.empty 0 0 False
, SomePlayer $ CPUPlayer 0
]}
gameState = GameState {level = theLevel, tanks = [ Tank 5.0 5.0 0 0 2 270 False 3 2 5
, Tank 5.0 3.5 0 0 2 270 False 3 2 5
], shoots = [], textures = M.empty}
runGame gameState $ do
setup
runMain mainState mainLoop
deinitGL gl
minFrameTime :: NominalDiffTime
minFrameTime = 0.01
mainLoop :: Main ()
mainLoop = do
gl <- gets driver
t <- gets time
handleEvents
lift render
liftIO $ swapBuffers gl
rtime <- liftIO getCurrentTime
let drender = diffUTCTime rtime t
when (drender < minFrameTime) $
liftIO $ threadDelay $ truncate $ 1e6*(minFrameTime - drender)
currenttime <- liftIO getCurrentTime
let d = round $ 1e3*(diffUTCTime currenttime t)
replicateM_ d simulationStep
let newtime = addUTCTime ((1e-3)*(fromIntegral d)) t
modify $ \state -> state {time = newtime}
runnext <- gets run
when runnext mainLoop
updateAngle :: Micro -> State Tank ()
updateAngle angle = do
oldangle <- gets tankDir
tspeed <- gets tankTurnspeed >>= return . (/1000)
let diff = angle - oldangle
let diff360 = if (diff > 180)
then (diff-360)
else if (diff <= -180)
then (diff+360)
else diff
let (diff180, angle180) = if (diff360 > 90)
then (diff360-180, oldangle+180)
else if (diff360 <= -90)
then (diff360+180, oldangle-180)
else (diff360, oldangle)
let turn = if (diff180 > tspeed)
then tspeed
else if (diff180 < -tspeed)
then (-tspeed)
else diff180
let newangle = angle180 + turn
let newangle180 = if (newangle > 180)
then (newangle-360)
else if (newangle <= -180)
then (newangle+360)
else newangle
modify $ \tank -> tank {tankDir = newangle180}
updateTank :: Maybe Micro -> Bool -> Maybe Micro -> State Tank ()
updateTank angle move aangle = do
when (isJust angle) $
updateAngle $ fromJust angle
when (isJust aangle) $
modify $ \tank -> tank {tankAim = fromJust aangle}
when move $ do
tdir <- gets tankDir
tspeed <- gets tankSpeed
moved <- gets tankMoving
when (isNothing angle || (isJust angle && (tdir == fromJust angle)) || moved) $ do
let anglej = (fromRational . toRational $ tdir)*pi/180
x = tspeed * fromRational (round ((cos anglej)*1000)%1000000)
y = tspeed * fromRational (round ((sin anglej)*1000)%1000000)
modify $ \tank -> tank {tankX = x + tankX tank, tankY = y + tankY tank, tankMoving = True}
when (not move) $ do
modify $ \tank -> tank {tankMoving = False}
updateShoot :: State Shoot ()
updateShoot = do
angle <- gets shootDir >>= return . (/180) . (*pi) . fromRational . toRational
speed <- gets shootSpeed
let dx = speed * fromRational (round ((cos angle)*1000)%1000000)
dy = speed * fromRational (round ((sin angle)*1000)%1000000)
modify $ \shoot -> shoot {shootX = dx + shootX shoot, shootY = dy + shootY shoot}
simulationStep :: Main ()
simulationStep = do
oldplayers <- gets players
oldtanks <- lift $ gets tanks
let (p, t, s) = unzip3 $ map updateTank' $ zip oldplayers oldtanks
ts = zip3 t s [0..]
shootingtanks = map (\(tank, _, n) -> (tank, n)) $ filter (\(tank, shoot, _) -> shoot && (tankShootsLeft tank) > 0) $ ts
newtanks = map (\(tank, shoot, _) -> if shoot then tank {tankShootsLeft = (tankShootsLeft tank) - 1} else tank) $ ts
newshoots = map (\(tank, n) -> Shoot
{ shootX = tankX tank
, shootY = tankY tank
, shootDir = tankAim tank
, shootSpeed = tankShootSpeed tank
, shootBouncesLeft = tankShootBounces tank
, shootTank = n
}) shootingtanks
modify $ \state -> state {players = p}
lift $ modify $ \state -> state {tanks = newtanks, shoots = map (execState updateShoot) (shoots state ++ newshoots)}
where
updateTank' (player, tank) = let (p, angle, move, aangle, shoot) = playerUpdate player tank
t = execState (updateTank angle move aangle) tank
in (p, t, shoot)
handleEvents :: Main ()
handleEvents = do
(newgl, event) <- gets driver >>= liftIO . nextEvent
modify $ \state -> state {driver = newgl}
when (isJust event) $ do
Main.handleEvent $ fromJust event
modify $ \state -> state {players = map (\p -> Player.handleEvent p $ fromJust event) $ players state}
handleEvents
handleEvent :: SomeEvent -> Main ()
handleEvent ev
| Just QuitEvent <- fromEvent ev = modify $ \state -> state {run = False}
| otherwise = return ()

18
src/Level.hs Normal file
View file

@ -0,0 +1,18 @@
module Level ( Level(..)
, testLevel
) where
import Data.List
data Level = Level
{ levelWidth :: !Int
, levelHeight :: !Int
} deriving (Show)
testLevel :: Level
testLevel = Level
{ levelWidth = 10
, levelHeight = 10
}

27
src/Player.hs Normal file
View file

@ -0,0 +1,27 @@
{-# LANGUAGE ExistentialQuantification, DeriveDataTypeable #-}
module Player ( Player(..)
, SomePlayer(..)
) where
import Data.Fixed
import Data.Typeable
import Game (Tank(..))
import GLDriver (SomeEvent)
class Player a where
playerUpdate :: a -> Tank -> (a, Maybe Micro, Bool, Maybe Micro, Bool)
handleEvent :: a -> SomeEvent -> a
handleEvent player _ = player
data SomePlayer = forall a. Player a => SomePlayer a
instance Player SomePlayer where
playerUpdate (SomePlayer player) tank =
let (p, angle, move, aangle, shoot) = playerUpdate player tank
in (SomePlayer p, angle, move, aangle, shoot)
handleEvent (SomePlayer player) event = SomePlayer $ handleEvent player event

167
src/Render.hs Normal file
View file

@ -0,0 +1,167 @@
module Render ( setup
, render
) where
import Paths_htanks
import Game
import Level
import Texture
import Control.Monad.State
import Data.Fixed
import Data.Maybe
import Data.Ratio
import qualified Data.Map as M
import Bindings.GLPng
import Graphics.Rendering.OpenGL.GL (($=), GLfloat, GLdouble, Capability(..), Vector3(..))
import Graphics.Rendering.OpenGL.GL.BeginEnd (renderPrimitive, PrimitiveMode(..))
import Graphics.Rendering.OpenGL.GL.CoordTrans (matrixMode, MatrixMode(..), viewport, Position(..), Size(..), loadIdentity, preservingMatrix, ortho, translate, rotate)
import Graphics.Rendering.OpenGL.GL.Framebuffer (clear, ClearBuffer(..))
import Graphics.Rendering.OpenGL.GL.PerFragment (blend, blendFunc, BlendingFactor(..))
import Graphics.Rendering.OpenGL.GL.Texturing.Application (texture)
import Graphics.Rendering.OpenGL.GL.Texturing.Objects (textureBinding, TextureObject(..))
import Graphics.Rendering.OpenGL.GL.Texturing.Parameters (Repetition(..), Clamping(..), TextureFilter(..), MinificationFilter, MagnificationFilter)
import Graphics.Rendering.OpenGL.GL.Texturing.Specification (TextureTarget(..))
import Graphics.Rendering.OpenGL.GL.VertexSpec
texturePath :: Texture -> IO FilePath
texturePath t = getDataFileName $ path t
where
path TextureWood = "tex/Wood.png"
path TextureTank = "tex/Tank.png"
path TextureCannon = "tex/Cannon.png"
path TextureBullet = "tex/Bullet.png"
getTexture :: Texture -> Game TextureObject
getTexture t = do
ts <- gets textures
let tobj = M.lookup t ts
if (isJust tobj)
then
return $ fromJust tobj
else do
path <- liftIO $ texturePath t
tex <- liftIO $ pngBind path BuildMipmap Alpha (Repeated, Repeat) (Linear', Just Linear') Linear' >>= return . TextureObject . fromIntegral . fst
modify $ \state -> state {textures = M.insert t tex ts}
return tex
setup :: Game ()
setup = do
liftIO $ do
blend $= Enabled
blendFunc $= (SrcAlpha, OneMinusSrcAlpha)
-- cache textures
getTexture TextureWood
getTexture TextureTank
getTexture TextureCannon
getTexture TextureBullet
return ()
render :: Game ()
render = do
tanklist <- gets tanks
shootlist <- gets shoots
textureWood <- getTexture TextureWood
textureTank <- getTexture TextureTank
textureCannon <- getTexture TextureCannon
textureBullet <- getTexture TextureBullet
(lw, lh) <- gets level >>= \l -> return (fromIntegral . levelWidth $ l :: GLfloat, fromIntegral . levelHeight $ l :: GLfloat)
liftIO $ do
clear [ColorBuffer]
texture Texture2D $= Enabled
textureBinding Texture2D $= Just textureWood
renderPrimitive Quads $ do
texCoord $ TexCoord2 (0 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 0 lh
texCoord $ TexCoord2 lw 0
vertex $ Vertex2 lw lh
texCoord $ TexCoord2 lw lh
vertex $ Vertex2 lw 0
texCoord $ TexCoord2 0 lh
vertex $ Vertex2 (0 :: GLfloat) (0 :: GLfloat)
forM_ tanklist $ \tank -> preservingMatrix $ do
let x = fromReal . tankX $ tank
y = fromReal . tankY $ tank
rotDir = fromReal . tankDir $ tank
rotAim = fromReal . tankAim $ tank
translate $ Vector3 x y (0 :: GLfloat)
rotate rotDir $ Vector3 0 0 (1 :: GLfloat)
textureBinding Texture2D $= Just textureTank
renderPrimitive Quads $ do
texCoord $ TexCoord2 (0 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 (-0.5 :: GLfloat) (-0.5 :: GLfloat)
texCoord $ TexCoord2 (0 :: GLfloat) (1 :: GLfloat)
vertex $ Vertex2 (-0.5 :: GLfloat) (0.5 :: GLfloat)
texCoord $ TexCoord2 (1 :: GLfloat) (1 :: GLfloat)
vertex $ Vertex2 (0.5 :: GLfloat) (0.5 :: GLfloat)
texCoord $ TexCoord2 (1 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 (0.5 :: GLfloat) (-0.5 :: GLfloat)
rotate (rotAim-rotDir) $ Vector3 0 0 (1 :: GLfloat)
textureBinding Texture2D $= Just textureCannon
renderPrimitive Quads $ do
texCoord $ TexCoord2 (0 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 (-0.5 :: GLfloat) (-0.5 :: GLfloat)
texCoord $ TexCoord2 (0 :: GLfloat) (1 :: GLfloat)
vertex $ Vertex2 (-0.5 :: GLfloat) (0.5 :: GLfloat)
texCoord $ TexCoord2 (1 :: GLfloat) (1 :: GLfloat)
vertex $ Vertex2 (0.5 :: GLfloat) (0.5 :: GLfloat)
texCoord $ TexCoord2 (1 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 (0.5 :: GLfloat) (-0.5 :: GLfloat)
forM_ shootlist $ \shoot -> preservingMatrix $ do
let x = fromReal . shootX $ shoot
y = fromReal . shootY $ shoot
rotDir = fromReal . shootDir $ shoot
translate $ Vector3 x y (0 :: GLfloat)
rotate rotDir $ Vector3 0 0 (1 :: GLfloat)
textureBinding Texture2D $= Just textureBullet
renderPrimitive Quads $ do
texCoord $ TexCoord2 (0 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 (-0.2 :: GLfloat) (-0.2 :: GLfloat)
texCoord $ TexCoord2 (0 :: GLfloat) (1 :: GLfloat)
vertex $ Vertex2 (-0.2 :: GLfloat) (0.2 :: GLfloat)
texCoord $ TexCoord2 (1 :: GLfloat) (1 :: GLfloat)
vertex $ Vertex2 (0.2 :: GLfloat) (0.2 :: GLfloat)
texCoord $ TexCoord2 (1 :: GLfloat) (0 :: GLfloat)
vertex $ Vertex2 (0.2 :: GLfloat) (-0.2 :: GLfloat)
fromReal :: (Real a, Fractional b) => a -> b
fromReal = fromRational . toRational

8
src/Texture.hs Normal file
View file

@ -0,0 +1,8 @@
module Texture ( Texture(..)
, TextureObject
) where
import Graphics.Rendering.OpenGL.GL.Texturing.Objects (TextureObject)
data Texture = TextureWood | TextureTank | TextureCannon | TextureBullet
deriving (Eq, Ord, Show)