packages feed

Hate (empty) → 0.1.4.2

raw patch · 35 files changed

+2018/−0 lines, 35 filesdep +GLFW-bdep +GLUtildep +Hatesetup-changed

Dependencies added: GLFW-b, GLUtil, Hate, JuicyPixels, JuicyPixels-util, OpenGL, base, bytestring, lens, mtl, multimap, random, stm, transformers, vect, vect-opengl, vector

Files

+ Hate.cabal view
@@ -0,0 +1,133 @@+name:                Hate+-- PVP summary:      +-+------- breaking API changes+--                   | | +----- non-breaking API additions+--                   | | | +--- code changes with no API change++version:             0.1.4.2+synopsis:            A small 2D game framework.+description:         A small 2D game framework. +stability:           experimental+homepage:            http://github.com/bananu7/Hate+license:             MIT+license-file:        LICENSE+author:              Bartek Banachewicz+maintainer:          bananu7@o2.pl+category:            Graphics+build-type:          Simple+cabal-version:       >=1.18++library+  -- Modules exported by the library.+  exposed-modules:    Hate+                      Hate.Graphics+                      Hate.Graphics.Shapes+                      Hate.Math+                      Hate.Math.Types+                      Hate.Common.Scheduler+                      Hate.Events+                      Hate.Events.Types+                      +  other-modules:      Hate.Common+                      Hate.Common.Types+                      Hate.Common.Instances+                      Hate.Math.Transformable.Class+                      Hate.Math.Util+                      Hate.Graphics.Pipeline+                      Hate.Graphics.Pipeline.Util+                      Hate.Graphics.Shader+                      Hate.Graphics.Types+                      Hate.Graphics.Rendering+                      Hate.Graphics.Sprite+                      Hate.Graphics.Backend+                      Hate.Graphics.Backend.Util+                      Hate.Graphics.Backend.Compat+                      Hate.Graphics.Backend.Compat.Shaders+                      Hate.Graphics.Backend.Compat.Types+                      Hate.Graphics.Backend.Modern+                      Hate.Graphics.Backend.Modern.Shaders+                      Hate.Graphics.Backend.Modern.Types+                       ++  hs-source-dirs:      .,src+  -- Modules included in this library but not exported.+  -- other-modules:       +  +  -- Other library packages from which modules are imported.+  build-depends:       base >= 4.6 && < 4.9,+                       GLFW-b >= 1.4 && < 1.4.7,+                       GLUtil >= 0.7,+                       OpenGL > 2.9.1,+                       transformers,+                       mtl,+                       vect,+                       vect-opengl,+                       vector,+                       JuicyPixels,+                       JuicyPixels-util,+                       bytestring,+                       multimap,+                       stm > 2.4++  default-language:    Haskell2010+  ghc-options:         -Wall -ferror-spans++executable sample_shapes+  main-is:          sample_shapes.hs+  hs-source-dirs:   samples +  build-depends:    base >= 4.6 && < 4.9,+                    GLFW-b >= 1.4 && < 1.4.7,+                    GLUtil >= 0.7,+                    OpenGL > 2.9.1,+                    transformers, mtl, vect, vect-opengl, vector, JuicyPixels, JuicyPixels-util,+                    Hate+  default-language: Haskell2010++executable sample_scheduler+  main-is:          sample_scheduler.hs+  hs-source-dirs:   samples +  build-depends:    base >= 4.6 && < 4.9,+                    GLFW-b >= 1.4 && < 1.4.7,+                    GLUtil >= 0.7,+                    OpenGL > 2.9.1,+                    transformers, mtl, vect, vect-opengl, vector, JuicyPixels, JuicyPixels-util,+                    Hate,+                    lens+  default-language: Haskell2010++executable sample_sprite+  main-is:          sample_sprite.hs+  hs-source-dirs:   samples +  build-depends:    base >= 4.6 && < 4.9,+                    GLFW-b >= 1.4 && < 1.4.7,+                    GLUtil >= 0.7,+                    OpenGL > 2.9.1,+                    transformers, mtl, vect, vect-opengl, vector, JuicyPixels, JuicyPixels-util,+                    Hate,+                    random, lens+  default-language: Haskell2010++executable sample_spritesheet+  main-is:          sample_spritesheet.hs+  hs-source-dirs:   samples +  build-depends:    base >= 4.6 && < 4.9,+                    GLFW-b >= 1.4 && < 1.4.7,+                    GLUtil >= 0.7,+                    OpenGL > 2.9.1,+                    transformers, mtl, vect, vect-opengl, vector, JuicyPixels, JuicyPixels-util,+                    Hate,+                    random, lens+  default-language: Haskell2010++executable sample_asteroids+  main-is:          sample_asteroids.hs+  hs-source-dirs:   samples +  build-depends:    base >= 4.6 && < 4.9,+                    GLFW-b >= 1.4 && < 1.4.7,+                    GLUtil >= 0.7,+                    OpenGL > 2.9.1,+                    transformers, mtl, vect, vect-opengl, vector, JuicyPixels, JuicyPixels-util,+                    Hate,+                    random, lens+  default-language: Haskell2010++
+ LICENSE view
@@ -0,0 +1,20 @@+The MIT License (MIT)++Copyright (c) 2014 Bartek Banachewicz++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of+the Software, and to permit persons to whom the Software is furnished to do so,+subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ samples/sample_asteroids.hs view
@@ -0,0 +1,133 @@+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+import Hate
+import Hate.Graphics
+import Vec2Lens (x,y)
+
+import Control.Applicative
+import Control.Lens
+import System.Random
+
+-- sample 4
+
+data EntityState = EntityState {
+    _pos :: Vec2,
+    _vel :: Vec2,
+    _rot :: Float,
+    _rotVel :: Float
+}
+makeLenses ''EntityState
+
+data Asteroid = Asteroid {
+    _asteroidEntity :: EntityState,
+    _size :: Int
+}
+makeLenses ''Asteroid
+makeFields ''Asteroid
+
+data Player = Player {
+    _playerEntity :: EntityState
+}
+makeLenses ''Player
+makeFields ''Player
+
+data SampleState = SampleState {
+    _playerSprite :: Sprite,
+    _smallAsteroidSprite :: Sprite,
+    _mediumAsteroidSprite :: Sprite,
+    _bigAsteroidSprite :: Sprite,
+    _asteroids :: [Asteroid],
+    _player :: Player
+}
+makeLenses ''SampleState
+
+data VisualRepresentation = SmallAsteroidSprite | MediumAsteroidSprite | BigAsteroidSprite | PlayerSprite
+class ToVisual a where
+    toVisual :: a -> VisualRepresentation
+
+instance ToVisual Player where
+    toVisual (Player _) = PlayerSprite
+
+instance ToVisual Asteroid where
+    toVisual (Asteroid _ sz) | sz < 2 = SmallAsteroidSprite
+                             | sz < 4 = MediumAsteroidSprite
+                             | otherwise = BigAsteroidSprite
+
+randomAsteroids :: Int -> IO [Asteroid] 
+randomAsteroids n = replicateM n randomAsteroid
+
+randomAsteroid :: IO Asteroid
+randomAsteroid = do
+    px <- getStdRandom $ randomR (0,800)
+    py <- getStdRandom $ randomR (0,800)
+    vx <- getStdRandom $ randomR (-2, 2)
+    vy <- getStdRandom $ randomR (-2, 2)
+    vr <- getStdRandom $ randomR (-0.1, 0.1)
+    sz <- getStdRandom $ randomR (1, 5)
+
+    let e = EntityState (Vec2 px py) (Vec2 vx vy) 0 vr
+    return $ Asteroid e sz
+
+initialPlayer :: Player
+initialPlayer = Player $ EntityState (Vec2 200 200) (Vec2 0 0) 0 0
+
+sampleLoad :: LoadFn SampleState
+sampleLoad = SampleState <$> loadSprite "samples/asteroids/ship.png" 
+                         <*> loadSprite "samples/asteroids/asteroid_small.png" 
+                         <*> loadSprite "samples/asteroids/asteroid_medium.png" 
+                         <*> loadSprite "samples/asteroids/asteroid_big.png" 
+                         <*> randomAsteroids 10
+                         <*> pure initialPlayer
+
+sampleDraw :: DrawFn SampleState
+sampleDraw s = (map draw (s ^. asteroids)) ++ [draw (s ^. player)]
+    where
+        draw :: (HasEntity a EntityState, ToVisual a) => a -> DrawRequest
+        draw a = (entityToTransform a) . sprite Middle . visualToSprite . toVisual $ a
+
+        visualToSprite SmallAsteroidSprite = s ^. smallAsteroidSprite
+        visualToSprite MediumAsteroidSprite = s ^. mediumAsteroidSprite
+        visualToSprite BigAsteroidSprite = s ^. bigAsteroidSprite
+        visualToSprite PlayerSprite = s ^. playerSprite
+
+        entityToTransform :: HasEntity a EntityState => a -> DrawRequest -> DrawRequest
+        entityToTransform a = let e = a ^. entity in ((rotated (e ^. rot)) . (translate (e ^. pos)))
+
+sampleUpdate :: UpdateFn SampleState
+sampleUpdate _ = do
+    asteroids . traversed %= updateEntity
+    player %= updateEntity
+    steerShip
+    where
+        updateEntity :: HasEntity a EntityState => a -> a
+        updateEntity a = a & (entity . pos +~ a ^. entity . vel)
+                           & (entity . rot +~ a ^. entity . rotVel)
+                           -- damping:
+                           & (entity . vel *~ 0.99)
+                           & (entity . rotVel *~ 0.99)
+                           & screenWarp
+
+        screenWarp a = a & (if a ^. entity . pos . x > 1024 then entity . pos . x -~ 1024 else id)
+                         & (if a ^. entity . pos . y > 768  then entity . pos . y -~ 768 else id)
+                         & (if a ^. entity . pos . x < 0 then entity . pos . x +~ 1024 else id)
+                         & (if a ^. entity . pos . y < 0 then entity . pos . y +~ 768 else id)
+
+
+        steerShip = do
+            r <- use $ player . entity . rot
+            whenKeyPressed Key'Up $ player . entity . vel += (rotateVec r (Vec2 0 (negate 1)))
+            whenKeyPressed Key'Left $ player . entity . rotVel -= 0.01
+            whenKeyPressed Key'Right $ player . entity . rotVel += 0.01
+
+config :: Config
+config = 
+    Config
+        { windowTitle = "Sample - Asteroids"
+        , windowSize  = (1024, 768)
+        }
+
+main :: IO ()
+main = runApp config sampleLoad sampleUpdate sampleDraw
+ samples/sample_scheduler.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE FlexibleContexts #-}++import Hate+import Hate.Graphics.Shapes+import Hate.Common.Scheduler++import Control.Lens++data SampleState = SampleState {+    _radius :: Float,+    _sched :: Scheduler SampleState,+    _firstRun :: Bool+}+makeLenses ''SampleState++sampleLoad :: LoadFn SampleState+sampleLoad = return $ SampleState 0 emptyScheduler True++sampleDraw :: DrawFn SampleState+sampleDraw p = [translate (Vec2 150 150) $ circle (p ^. radius)]++schedule' evt = sched %= (flip schedule) evt++every' t evt = every t evt >>= schedule'+after' t evt = after t evt >>= schedule'++sampleUpdate :: UpdateFn SampleState+sampleUpdate _ = do+    -- repeated action+    use firstRun >>= \p -> when p $ do+        every' 1 $ radius += 50+        firstRun .= False+        +    use sched >>= process >>= assign sched++    -- queue a waiting action+    whenKeyPressed Key'Space $ after' 2 $ radius += 20++    -- continuous action+    radius *= 0.95++config :: Config+config =+    Config+        { windowTitle = "Sample - Scheduler"+        , windowSize  = (1024, 768)+        }++main :: IO ()+main = runApp config sampleLoad sampleUpdate sampleDraw
+ samples/sample_shapes.hs view
@@ -0,0 +1,38 @@+import Hate+import Hate.Graphics.Shapes++data SampleState = SampleState {+    r :: Int,+    p :: Vec2+}++sampleLoad :: LoadFn SampleState+sampleLoad = return $ SampleState 0 (Vec2 150 150)++green :: Float -> Vec4+green e = Vec4 0.0 e 0.0 1.0++sampleDraw :: DrawFn SampleState+sampleDraw (SampleState r p) =+    [ translate p $ colored (green $ (fromIntegral r) / 150.0) $ circle (fromIntegral r)+    , line p (Vec2 300 300)+    ]++sampleUpdate :: UpdateFn SampleState+sampleUpdate events = do+    modify $ \s -> if r s > 150 then s { r = 0 }+                                else s { r = r s + 1 }++    forM_ events $ \e -> case e of+        EventCursorPos x y -> modify $ \s -> s { p = Vec2 x y }+        _ -> return ()++config :: Config+config =+    Config+        { windowTitle = "Sample - Shapes"+        , windowSize  = (1280, 768)+        }++main :: IO ()+main = runApp config sampleLoad sampleUpdate sampleDraw
+ samples/sample_sprite.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+import Hate
+import Hate.Graphics
+import Vec2Lens (x,y)
+
+import Control.Applicative
+import Control.Lens
+import System.Random
+
+-- sample 4
+
+data Sehe = Sehe {
+    _pos :: Vec2,
+    _vel :: Vec2
+}
+makeLenses ''Sehe
+
+data SampleState = SampleState {
+    _seheSprite :: Sprite,
+    _sehes :: [Sehe]
+}
+makeLenses ''SampleState
+
+generateSehes :: IO [Sehe] 
+generateSehes = replicateM 100 generateSehe
+
+generateSehe :: IO Sehe
+generateSehe = do
+    px <- getStdRandom $ randomR (0,300)
+    py <- getStdRandom $ randomR (0,300)
+    vx <- getStdRandom $ randomR (-5, 5)
+    vy <- getStdRandom $ randomR (-5, 5)
+
+    return $ Sehe (Vec2 px py) (Vec2 vx vy)
+
+sampleLoad :: LoadFn SampleState
+sampleLoad = SampleState <$> loadSprite "samples/image.png" 
+                         <*> generateSehes
+
+sampleDraw :: DrawFn SampleState
+sampleDraw s = map (\(Sehe p v) -> translate p $ sprite TopLeft (s ^. seheSprite)) $ s ^. sehes
+
+moveSehe :: Sehe -> Sehe
+moveSehe = updatePos . updateVel
+    where
+        updateVel :: Sehe -> Sehe
+        updateVel s = s & (if bounceX then vel . x %~ negate else id)
+                        & (if bounceY then vel . y %~ negate else id)
+            where
+                bounceX = outOfBounds (s ^. pos . x) (0, 1024 - 128)
+                bounceY = outOfBounds (s ^. pos . y) (0, 786 - 128)
+                outOfBounds v (lo, hi) = v < lo || v > hi
+
+        updatePos :: Sehe -> Sehe
+        updatePos (Sehe p v) = Sehe (p + v) v
+
+sampleUpdate :: UpdateFn SampleState
+sampleUpdate _ = sehes . traverse %= moveSehe
+
+config :: Config
+config = 
+    Config
+        { windowTitle = "Sample - Sprite"
+        , windowSize  = (1024, 768)
+        }
+
+main :: IO ()
+main = runApp config sampleLoad sampleUpdate sampleDraw
+ samples/sample_spritesheet.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+import Hate
+import Hate.Graphics
+
+import Control.Applicative
+import Control.Lens
+import System.Random
+
+-- sample 4
+
+data Koala = Koala {
+    _pos :: Vec2,
+    _num :: Int
+}
+makeLenses ''Koala
+
+data SampleState = SampleState {
+    _koalaSpriteSheet :: SpriteSheet,
+    _koalas :: [Koala]
+}
+makeLenses ''SampleState
+
+generateKoalas :: IO [Koala] 
+generateKoalas = mapM generateKoala [0..24]
+
+generateKoala :: Int -> IO Koala
+generateKoala i = do
+    let px = 128 * (fromIntegral $ i `div` 5)
+    let py = 128 * (fromIntegral $ i `mod` 5)
+    n <- getStdRandom $ randomR (0 :: Int,3)
+
+    return $ Koala (Vec2 px py) n
+
+sampleLoad :: LoadFn SampleState
+sampleLoad = SampleState <$> loadSpriteSheet "samples/nooble.png" (2,2)
+                         <*> generateKoalas
+
+sampleDraw :: DrawFn SampleState
+sampleDraw s = map (\(Koala p n) -> translate p $ spriteSheet TopLeft (n `div` 10) (s ^. koalaSpriteSheet)) $ s ^. koalas
+
+sampleUpdate :: UpdateFn SampleState
+sampleUpdate _ = koalas . traverse . num %= \n -> if n > 39 then 0 else n+1
+
+config :: Config
+config = 
+    Config
+        { windowTitle = "Sample - Sprite"
+        , windowSize  = (1024, 768)
+        }
+
+main :: IO ()
+main = runApp config sampleLoad sampleUpdate sampleDraw
+ src/Hate.hs view
@@ -0,0 +1,16 @@+module Hate +    ( module Hate.Common+    , module Hate.Math+    , module Hate.Events.Types+    , module Control.Monad.State+    , module Control.Monad.Reader+    , module Graphics.UI.GLFW+    ) where++import Control.Monad.State+import Control.Monad.Reader (ask)+import Graphics.UI.GLFW (Key(..))++import Hate.Common (LoadFn, UpdateFn, DrawFn, whenKeyPressed, runApp, Config(..))+import Hate.Math+import Hate.Events.Types
+ src/Hate/Common.hs view
@@ -0,0 +1,246 @@+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RecordWildCards #-}++{-|+Module      : HateCommon+Description : Core and common parts of the Hate framework.+License     : MIT+Maintainer  : bananu7@o2.pl+Stability   : experimental+Portability : requires OpenGL and GLFW build+-}++module Hate.Common +    ( module Hate.Common.Types+    , module Hate.Common.Instances+    , Hate.Common.Types.Hate(..)    +    , runApp+    , getKey+    , whenKeyPressed+    ) where++import Hate.Util +import Hate.Common.Types+import Hate.Common.Instances()+import Hate.Events++import Hate.Graphics.Rendering+import Hate.Graphics.Backend++import Control.Monad.State+import Control.Applicative++import System.Exit+import System.IO++import Graphics.Rendering.OpenGL(($=))+import qualified Graphics.Rendering.OpenGL as GL+import qualified Graphics.UI.GLFW as G++import Data.Maybe++import Control.Concurrent (threadDelay)++-- Assuming this doesn't change, for now+-- this actually controls update rate, as the display+-- rate is vsynced+desiredFPS = 60.0+desiredSPF = 1.0 / desiredFPS++stderrErrorCallback :: G.ErrorCallback+stderrErrorCallback _ = hPutStrLn stderr+ +--keyCallback :: G.KeyCallback+--keyCallback win key _ action _ =+--    when (key == G.Key'Escape && action == G.KeyState'Pressed) $+--        G.setWindowShouldClose win True        ++choose :: [IO (Maybe a)] -> IO (Maybe a)+choose [] = return Nothing+choose (x:xs) = do+    a <- x+    if isJust a+        then return a -- short-circuit+        else choose xs -- eventually fall to Nothing++data GlContextDescriptor = GlContextDescriptor {+    majVersion :: Int,+    minVersion :: Int,+    forwardCompat :: Bool+} deriving Show++hateInitWindow :: String -> (Int, Int) -> IO G.Window+hateInitWindow titl wSize = do+    G.setErrorCallback (Just stderrErrorCallback)+    successfulInit <- G.init+    -- if init failed, we exit the program+    bool successfulInit (hateFailureExit "GLFW Init failed") $ do+        mw <- choose+            [ tryOpenWindow (GlContextDescriptor 4 5 True) wSize titl+            , tryOpenWindow (GlContextDescriptor 4 5 False) wSize titl+            , tryOpenWindow (GlContextDescriptor 4 4 True) wSize titl+            , tryOpenWindow (GlContextDescriptor 3 3 True) wSize titl+            , tryOpenWindow (GlContextDescriptor 3 3 False) wSize titl+            ]++        case mw of+            Nothing -> G.terminate >> (hateFailureExit "Window creation failed")+            Just win -> do+                G.makeContextCurrent mw+                G.swapInterval 1 --vsync+                hateInitGL+                return win++tryOpenWindow :: GlContextDescriptor -> (Int, Int) -> String -> IO (Maybe G.Window)+tryOpenWindow cd (width, height) titl = do+    putStrLn $ "Opening Window (" ++ show cd ++ ")"++    G.windowHint (G.WindowHint'ContextVersionMajor (majVersion cd))+    G.windowHint (G.WindowHint'ContextVersionMinor (minVersion cd))+    G.windowHint (G.WindowHint'OpenGLForwardCompat (forwardCompat cd))+    G.windowHint (G.WindowHint'OpenGLProfile G.OpenGLProfile'Core)+    G.windowHint (G.WindowHint'OpenGLDebugContext True)+    G.createWindow width height titl Nothing Nothing++hateInitGL :: IO ()+hateInitGL = do+    GL.blend $= GL.Enabled+    GL.blendFunc $= (GL.SrcAlpha, GL.OneMinusSrcAlpha)++hateFailureExit :: String -> IO a+hateFailureExit errMsg = do+    hPutStrLn stderr errMsg+    exitFailure++hateSuccessfulExit :: G.Window -> IO b+hateSuccessfulExit win = do+    G.destroyWindow win+    G.terminate+    exitSuccess++-- this function is so generic because it used to work with universally+-- quantified Renderer. Now that libraryState uses RendererI it's not strictly+-- necessary, but I don't mind leaving it here either.+updateRendererState :: (forall r. Renderer r => (r -> IO (a, r))) -> HateInner us a+updateRendererState mutator = do+    g <- gets libraryState+    case g of+        (LibraryState{ graphicsState = gs, ..}) -> do+            (ret, ngs) <- liftIO $ mutator gs+            modify $ \g -> g { libraryState = LibraryState { graphicsState = ngs, .. }}+            return $ ret++runHateDraw :: (forall r. Renderer r => StateT r IO a) -> HateInner us a+runHateDraw m = updateRendererState (runStateT m)++hateLoop :: HateInner us ()+hateLoop = do+    gs <- get+    let w = window gs++    shouldClose <- (liftIO . G.windowShouldClose) w+    unless shouldClose $ do +        liftIO $ do+            (width, height) <- G.getFramebufferSize w+            --let ratio = fromIntegral width / fromIntegral height+    +            GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral width) (fromIntegral height))+            GL.clear [GL.ColorBuffer]++        -- call user drawing function+        let drawRequests = drawFn gs $ userState gs+        runHateDraw $ render drawRequests++        -- update the game state in constant intervals+        Just t <- liftIO G.getTime+        let tDiff = t - (lastUpdateTime gs)++        evts <- reverse <$> fetchEvents+        let groupedEvts = groupEventsByTime (lastUpdateTime gs) desiredSPF evts++        when (tDiff > desiredSPF) $ if length groupedEvts > 0+            then mapM_ hateUpdate groupedEvts+            else hateUpdate []+++        when (tDiff < desiredSPF) $ liftIO $+            threadDelay (floor $ 1000000 * (desiredSPF - tDiff))++        liftIO $ do +            G.swapBuffers w+            G.pollEvents+        hateLoop++groupEventsByTime :: Time -> Time -> [TimedEvent] -> [[TimedEvent]]+groupEventsByTime _ _ [] = []+groupEventsByTime lastTime frameLength evts = current : groupEventsByTime (lastTime + frameLength) frameLength next+    where+        (current, next) = span inCurrentFrame evts+        inCurrentFrame = ((lastTime + frameLength) >) . fst++hateUpdate :: [TimedEvent] -> HateInner us ()+hateUpdate evts = do+    handleInternalEvents . map snd $ evts++    -- uncomment to print all the events out    +    -- liftIO $ mapM print evts++    let allowedEvts = filter allowedEvent . map snd $ evts++    gs <- get+    runHate $ (updateFn gs) allowedEvts+    modify $ \x -> x { lastUpdateTime = lastUpdateTime x + desiredSPF }++handleInternalEvents :: [Event] -> HateInner us ()+handleInternalEvents = mapM_ handleEvent+    where+        handleEvent e = case e of+            EventWindowSize xs ys -> do+                liftIO $ GL.viewport $= (GL.Position 0 0, GL.Size (fromIntegral xs) (fromIntegral ys))+                runHateDraw $ updateScreenSize (xs, ys)+            _ -> return ()++getKey :: G.Key -> Hate us Bool+getKey k = UnsafeHate $ do+    gs <- get+    stt <- liftIO $ G.getKey (window gs) k +    return $ keystateToBool stt+    where keystateToBool s+            | s == G.KeyState'Released = False+            | otherwise = True++whenKeyPressed :: G.Key -> Hate us () -> Hate us ()+whenKeyPressed k action = do+    b <- getKey k+    if b then action +         else return ()++-- This function is called after the window has been created+pickRenderer :: (Int, Int) -> IO RendererI+pickRenderer ws = do+    (G.Version vMaj vMin _) <- G.getVersion+    if vMaj >= 4+        then initialRendererStateModern ws+        else initialRendererStateCompat ws++initialLibraryState :: Config -> IO LibraryState+initialLibraryState cfg = LibraryState <$> pickRenderer (windowSize cfg)+                                       <*> initialEventsState++runApp :: Config -> LoadFn us -> UpdateFn us -> DrawFn us -> IO ()+runApp config ldFn upFn drFn = do+    win <- hateInitWindow (windowTitle config) (windowSize config)+    libState <- initialLibraryState config++    setCallbacks (eventsState libState) win++    print $! "Loading user state"+    initialUserState <- ldFn++    time <- fromJust <$> G.getTime+    evalStateT hateLoop $ HateState { userState = initialUserState, window = win, drawFn = drFn, updateFn = upFn, libraryState = libState, lastUpdateTime = time }+    hateSuccessfulExit win    
+ src/Hate/Common/Instances.hs view
@@ -0,0 +1,13 @@+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}++module Hate.Common.Instances() where++import Hate.Common.Types+import Control.Monad.State++instance MonadState us (Hate us) where+    get = UnsafeHate $ gets userState++    put s = UnsafeHate $ do+            gs <- get+            put $ gs { userState = s }
+ src/Hate/Common/Scheduler.hs view
@@ -0,0 +1,97 @@+{-# LANGUAGE RankNTypes #-}
+
+{-|
+Module      : Hate.Common.Scheduler
+Description : Event scheduler for Hate programs
+License     : MIT
+Maintainer  : bananu7@o2.pl
+Stability   : experimental
+
+Scheduler allows you to use FRP-ish style of delaying and
+scheduling actions.
+
+-}
+
+module Hate.Common.Scheduler 
+    ( every
+    , after
+    , process
+    , emptyScheduler
+    , schedule
+    , Scheduler
+    )
+where
+
+import Hate.Common.Types
+
+import Data.MultiMap
+import Data.Maybe (catMaybes)
+import Control.Monad.State
+
+type Time = Double
+type EventAction us = Hate us ()
+
+data ScheduledEvent us = OneTimeEvent Time (EventAction us) | RepetitiveEvent Time Time (EventAction us)
+instance Show (ScheduledEvent us) where
+    show (OneTimeEvent t _) = "OneTimeEvent (t = " ++ show t ++ ")"
+    show (RepetitiveEvent t i _) = "RepetitiveEvent (t = " ++ show t ++ ", interval = " ++ show i ++ ")"
+
+fire :: ScheduledEvent us -> Hate us ()
+fire (OneTimeEvent _ a) = a
+fire (RepetitiveEvent _ _ a) = a
+
+-- |This represents a control structure for your events.
+-- You can use multiple schedulers and update them basing on the
+-- state of your application.
+type Scheduler us = MultiMap Time (ScheduledEvent us)
+instance (Show a, Show b) => Show (MultiMap a b) where
+    show x = show . toMap $ x
+
+-- |Schedules an action to run every @t@ seconds.
+every :: Time -> Hate us () -> Hate us (ScheduledEvent us)
+every interval action = do
+    t <- UnsafeHate $ gets lastUpdateTime
+    return $ RepetitiveEvent t interval action
+
+-- |Schedules an action to run once, after @t@ seconds.
+after :: Time -> Hate us () -> Hate us (ScheduledEvent us)
+after t a = do
+    currentTime <- UnsafeHate $ gets lastUpdateTime
+    return $ OneTimeEvent (t + currentTime) a
+
+-- |This is a main way to update the scheduler and run appropriate events.
+-- Typically you'll want to be running it every update.
+process :: Scheduler us -> Hate us (Scheduler us)
+process sched = do
+    currTime <- UnsafeHate $ gets lastUpdateTime
+    --UnsafeHate . liftIO $ putStrLn ("currenttime " ++ show currTime)
+
+    let maybeEvents = findMinWithValues sched
+    case maybeEvents of 
+        Just (t, nearestEvents) ->
+            if currTime > t then do
+                mapM_ fire nearestEvents
+
+                let sched' = delete t sched
+                let updatedEvents = catMaybes . Prelude.map (updateEvent currTime) $ nearestEvents
+
+                let sched'' = Prelude.foldr (flip schedule) sched' updatedEvents
+
+                process sched'' -- recursive call to inspect the next batch
+            else return sched -- no events to fire yet
+        Nothing -> return sched -- no events scheduled at all
+
+-- |This is provided in case the underlying implementation should change.
+emptyScheduler :: Scheduler us
+emptyScheduler = empty
+
+-- |Adds a new event to scheduler and returns an updated one.
+schedule :: Scheduler us -> ScheduledEvent us -> Scheduler us
+schedule sched evt = case evt of
+    OneTimeEvent time _ -> insert time evt sched
+    RepetitiveEvent time _ _ -> insert time evt sched
+
+
+updateEvent :: Time -> ScheduledEvent us -> Maybe (ScheduledEvent us)
+updateEvent _ (OneTimeEvent _ _) = Nothing
+updateEvent _ (RepetitiveEvent lastFire interval act) = Just $ RepetitiveEvent (lastFire+interval) interval act
+ src/Hate/Common/Types.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ExistentialQuantification #-}++module Hate.Common.Types+    ( Config(..)+    , LibraryState(..)+    , HateState(..)+    , HateInner+    , Hate(..)+    , LoadFn+    , UpdateFn+    , DrawFn+    )+where++import Control.Monad.State+import Control.Applicative+import qualified Graphics.UI.GLFW as G++import Hate.Graphics.Types(DrawRequest)+import Hate.Graphics.Rendering+import Hate.Events.Types (Event, EventsState)++data LibraryState = LibraryState {+    graphicsState :: RendererI,+    eventsState :: EventsState+}++{-| Configuration object to pass to `runApp` -}+data Config+    = Config+        { windowTitle :: String+        , windowSize :: (Int, Int)+        } deriving (Eq, Show)++data HateState us = HateState { +    userState :: us,+    libraryState :: LibraryState,+    window :: G.Window,+    drawFn :: DrawFn us,+    updateFn :: UpdateFn us,+    lastUpdateTime :: Double+}++-- |HateInner is the actual implementation backend+type HateInner us a = StateT (HateState us) IO a++-- |Hate Monad restricts user operations+newtype Hate us a = UnsafeHate { runHate :: HateInner us a }+    deriving (Functor, Applicative, Monad, MonadIO)++{- |This is one of the three functions that the user has to+provide in order to use the framework. It's a regular IO+function, so it's not limited in any way. It has to produce+initial state of the user's program. -}+type LoadFn userStateType = IO userStateType++type UpdateFn us = [Event] -> Hate us ()++type DrawFn us = us -> [DrawRequest]
+ src/Hate/Events.hs view
@@ -0,0 +1,106 @@+module Hate.Events
+    ( initialEventsState
+    , setCallbacks
+    , fetchEvents
+    , allowedEvent
+    , module Hate.Events.Types
+    )
+where
+
+import qualified Graphics.UI.GLFW as GLFW
+
+import Control.Concurrent.STM    (TQueue, atomically, newTQueueIO, tryReadTQueue, writeTQueue)
+import Hate.Events.Types
+import Hate.Common.Types
+
+import Control.Monad.IO.Class (liftIO)
+import Control.Monad.State.Class (gets)
+import Control.Applicative
+import Data.Maybe
+
+import GHC.Float (double2Float)
+
+initialEventsState :: IO EventsState
+initialEventsState = newTQueueIO :: IO (TQueue TimedEvent)
+
+{- The code has been borrowed from GLFW-b-demo; thanks @bsl -}
+
+-- I assume only one window can be used by the framework
+
+time = fromJust <$> GLFW.getTime
+
+writeWithTime :: TQueue TimedEvent -> Event -> IO ()
+writeWithTime tc e = time >>= \t -> atomically . writeTQueue tc $ (t, e)
+
+errorCallback           :: TQueue TimedEvent -> GLFW.Error -> String                                                            -> IO ()
+windowPosCallback       :: TQueue TimedEvent -> GLFW.Window -> Int -> Int                                                       -> IO ()
+windowSizeCallback      :: TQueue TimedEvent -> GLFW.Window -> Int -> Int                                                       -> IO ()
+windowCloseCallback     :: TQueue TimedEvent -> GLFW.Window                                                                     -> IO ()
+windowRefreshCallback   :: TQueue TimedEvent -> GLFW.Window                                                                     -> IO ()
+windowFocusCallback     :: TQueue TimedEvent -> GLFW.Window -> GLFW.FocusState                                                  -> IO ()
+windowIconifyCallback   :: TQueue TimedEvent -> GLFW.Window -> GLFW.IconifyState                                                -> IO ()
+framebufferSizeCallback :: TQueue TimedEvent -> GLFW.Window -> Int -> Int                                                       -> IO ()
+mouseButtonCallback     :: TQueue TimedEvent -> GLFW.Window -> GLFW.MouseButton   -> GLFW.MouseButtonState -> GLFW.ModifierKeys -> IO ()
+cursorPosCallback       :: TQueue TimedEvent -> GLFW.Window -> Double -> Double                                                 -> IO ()
+cursorEnterCallback     :: TQueue TimedEvent -> GLFW.Window -> GLFW.CursorState                                                 -> IO ()
+scrollCallback          :: TQueue TimedEvent -> GLFW.Window -> Double -> Double                                                 -> IO ()
+keyCallback             :: TQueue TimedEvent -> GLFW.Window -> GLFW.Key -> Int -> GLFW.KeyState -> GLFW.ModifierKeys            -> IO ()
+charCallback            :: TQueue TimedEvent -> GLFW.Window -> Char                                                             -> IO ()
+
+errorCallback           tc e s            = writeWithTime tc $ EventError           e s
+windowPosCallback       tc _ x y          = writeWithTime tc $ EventWindowPos       x y
+windowSizeCallback      tc _ w h          = writeWithTime tc $ EventWindowSize      w h
+windowCloseCallback     tc _              = writeWithTime tc $ EventWindowClose
+windowRefreshCallback   tc _              = writeWithTime tc $ EventWindowRefresh
+windowFocusCallback     tc _ fa           = writeWithTime tc $ EventWindowFocus     fa
+windowIconifyCallback   tc _ ia           = writeWithTime tc $ EventWindowIconify   ia
+framebufferSizeCallback tc _ w h          = writeWithTime tc $ EventFramebufferSize w h
+mouseButtonCallback     tc _ mb mba mk    = writeWithTime tc $ EventMouseButton     mb mba mk
+cursorPosCallback       tc _ x y          = writeWithTime tc $ EventCursorPos       (double2Float x) (double2Float y)
+cursorEnterCallback     tc _ ca           = writeWithTime tc $ EventCursorEnter     ca
+scrollCallback          tc _ x y          = writeWithTime tc $ EventScroll          x y
+keyCallback             tc _ k sc ka mk   = writeWithTime tc $ EventKey             k sc ka mk
+charCallback            tc _ c            = writeWithTime tc $ EventChar            c
+
+setErrorCallback :: TQueue TimedEvent -> IO ()
+setErrorCallback eventsChan = GLFW.setErrorCallback $ Just $ errorCallback eventsChan
+
+setCallbacks :: EventsState -> GLFW.Window -> IO ()
+setCallbacks eventsChan win = do
+    GLFW.setWindowPosCallback       win $ Just $ windowPosCallback       eventsChan
+    GLFW.setWindowSizeCallback      win $ Just $ windowSizeCallback      eventsChan
+    GLFW.setWindowCloseCallback     win $ Just $ windowCloseCallback     eventsChan
+    GLFW.setWindowRefreshCallback   win $ Just $ windowRefreshCallback   eventsChan
+    GLFW.setWindowFocusCallback     win $ Just $ windowFocusCallback     eventsChan
+    GLFW.setWindowIconifyCallback   win $ Just $ windowIconifyCallback   eventsChan
+    GLFW.setFramebufferSizeCallback win $ Just $ framebufferSizeCallback eventsChan
+    GLFW.setMouseButtonCallback     win $ Just $ mouseButtonCallback     eventsChan
+    GLFW.setCursorPosCallback       win $ Just $ cursorPosCallback       eventsChan
+    GLFW.setCursorEnterCallback     win $ Just $ cursorEnterCallback     eventsChan
+    GLFW.setScrollCallback          win $ Just $ scrollCallback          eventsChan
+    GLFW.setKeyCallback             win $ Just $ keyCallback             eventsChan
+    GLFW.setCharCallback            win $ Just $ charCallback            eventsChan
+
+fetchEvents :: HateInner us [TimedEvent]
+fetchEvents = fetchEvents' []
+    where 
+        fetchEvents' :: [TimedEvent] -> HateInner us [TimedEvent]
+        fetchEvents' xs = do
+            tc <- gets (eventsState . libraryState)
+            me <- liftIO $ atomically $ tryReadTQueue tc
+            case me of
+                Just e -> fetchEvents' (e:xs)
+                Nothing -> return xs
+
+-- | Some events aren't meant to impact the user, and should be handled
+-- internally by framework instead.
+
+allowedEvent :: Event -> Bool
+allowedEvent (EventWindowClose)       = True
+allowedEvent (EventWindowFocus _)     = True
+allowedEvent (EventMouseButton _ _ _) = True
+allowedEvent (EventCursorPos _ _)     = True
+allowedEvent (EventScroll _ _)        = True
+allowedEvent (EventKey _ _ _ _)       = True
+allowedEvent (EventChar _)            = True
+allowedEvent _ = False
+ src/Hate/Events/Types.hs view
@@ -0,0 +1,26 @@+module Hate.Events.Types where
+
+import Control.Concurrent.STM (TQueue)
+import qualified Graphics.UI.GLFW as GLFW
+
+type EventsState = TQueue TimedEvent
+type Time = Double
+
+data Event =
+    EventError           !GLFW.Error !String
+  | EventWindowPos       !Int !Int
+  | EventWindowSize      !Int !Int
+  | EventWindowClose
+  | EventWindowRefresh
+  | EventWindowFocus     !GLFW.FocusState
+  | EventWindowIconify   !GLFW.IconifyState
+  | EventFramebufferSize !Int !Int
+  | EventMouseButton     !GLFW.MouseButton !GLFW.MouseButtonState !GLFW.ModifierKeys
+  | EventCursorPos       !Float !Float
+  | EventCursorEnter     !GLFW.CursorState
+  | EventScroll          !Double !Double
+  | EventKey             !GLFW.Key !Int !GLFW.KeyState !GLFW.ModifierKeys
+  | EventChar            !Char
+  deriving Show
+
+type TimedEvent = (Time, Event)
+ src/Hate/Graphics.hs view
@@ -0,0 +1,27 @@+{-|+Module      : Hate.Graphics+Description : Hate graphics +License     : MIT+Maintainer  : bananu7@o2.pl+Stability   : experimental+Portability : To whatever has OpenGL in a reasonable version.++This module contains graphics+-}++module Hate.Graphics+    ( module Hate.Graphics.Pipeline+    , module Hate.Graphics.Pipeline.Util+    , module Hate.Graphics.Types+    , module Hate.Graphics.Rendering+    , module Hate.Graphics.Sprite+    , module Hate.Graphics.Shapes+    )+where++import Hate.Graphics.Pipeline+import Hate.Graphics.Pipeline.Util+import Hate.Graphics.Types+import Hate.Graphics.Rendering+import Hate.Graphics.Sprite+import Hate.Graphics.Shapes
+ src/Hate/Graphics/Backend.hs view
@@ -0,0 +1,32 @@+module Hate.Graphics.Backend 
+    ( RendererI(..)
+    , initialRendererStateModern
+    , initialRendererStateCompat
+    )
+where
+
+import qualified Hate.Graphics.Backend.Modern as Modern
+import qualified Hate.Graphics.Backend.Compat as Compat
+
+import Hate.Graphics.Rendering
+
+import Control.Monad.State
+
+instance Renderer RendererI where
+    contextRequirements (RendererImpl a) = contextRequirements a
+    --initialRendererState s = fmap RendererImpl $ initialRendererState s
+    updateScreenSize s = do
+        (RendererImpl a) <- get
+        a' <- execStateT (updateScreenSize s) a
+        put $ RendererImpl a'
+
+    render x = do
+        (RendererImpl a) <- get
+        a' <- execStateT (render x) a
+        put $ RendererImpl a'
+
+initialRendererStateModern :: (Int, Int) -> IO RendererI
+initialRendererStateCompat :: (Int, Int) -> IO RendererI
+
+initialRendererStateModern s = fmap RendererImpl $ Modern.initialGraphicsState s
+initialRendererStateCompat s = fmap RendererImpl $ Compat.initialGraphicsState s
+ src/Hate/Graphics/Backend/Compat.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module Hate.Graphics.Backend.Compat (BackendCompat(), initialGraphicsState) where
+
+import Hate.Graphics.Backend.Compat.Types
+import Hate.Graphics.Backend.Compat.Shaders
+
+import Hate.Math
+import Hate.Graphics.Rendering
+import Hate.Graphics.Pipeline.Util
+import Hate.Graphics.Pipeline
+import Hate.Graphics.Types
+import Hate.Graphics.Backend.Util
+
+
+import Control.Monad.State
+
+import qualified Graphics.Rendering.OpenGL as GL
+import Graphics.Rendering.OpenGL (($=))
+import qualified Graphics.GLUtil as U
+
+import Data.Vect.Float.OpenGL (orthoMatrix)
+import Data.List (groupBy)
+import Data.Maybe
+import Control.Applicative
+
+instance Renderer BackendCompat where
+    --initialRendererState = initialGraphicsState
+    render = renderBatch
+    contextRequirements _ = DesktopContext 4 4
+    updateScreenSize = updateScreenSz
+
+type Action a = (MonadState BackendCompat m, MonadIO m) => m a
+
+initialGraphicsState :: (Int, Int) -> IO BackendCompat
+initialGraphicsState screenSz =
+    BackendCompat <$> createPipelineNoUniformBindings solidColorPipelineDescs
+                  <*> createPipelineNoUniformBindings texturingPipelineDescs
+                  <*> createVertexStream
+                  <*> pure screenSz
+
+renderBatch :: [DrawRequest] -> Action ()
+renderBatch ds = mapM_ (\xs -> renderPipelineBatch (pipeline . head $ xs) xs) $ groupBy equalPipeline ds
+    where
+        a `equalPipeline` b = pipeline a == pipeline b
+
+renderPipelineBatch :: PipelineDescription -> [DrawRequest] -> Action ()
+renderPipelineBatch p ds = do
+    pip <- case p of
+        SolidColorPipeline color -> do
+            pip <- gets solidColorPipeline -- todo set up a proper solid color,
+            liftIO $ do
+                activatePipeline pip
+                colorUniformLocation <- GL.get $ GL.uniformLocation (program pip) "in_color"
+                GL.uniform colorUniformLocation $= color
+                return pip
+        TexturingPipeline texId -> do
+            pip <- gets texturingPipeline
+            liftIO $ activatePipeline pip
+            liftIO $ GL.textureBinding GL.Texture2D $= Just texId
+            return pip
+
+    forM_ ds $ \d -> do
+        let mat = transformation d .*. origin d
+        setScreenTransformationUniform mat pip
+
+        fromVertArrayIntoGlobal (vertices d, texCoords d)
+        let primitiveMode = vertexLayoutToGLLayout $ vertexLayout d
+        renderGlobalVertexStream primitiveMode
+
+setScreenTransformationUniform :: Mat4 -> Pipeline -> Action ()
+setScreenTransformationUniform t pip = do
+    (screenSizeX, screenSizeY) <- gets screenSize
+
+    let orthoScreenMat = orthoMatrix (0, (fromIntegral screenSizeX)) ((fromIntegral screenSizeY), 0) (-10, 10)
+    let drawMat = (transpose orthoScreenMat) .*. t
+    liftIO $ setUniformM4 pip "screen_transformation" drawMat
+
+renderGlobalVertexStream :: GL.PrimitiveMode -> Action ()
+renderGlobalVertexStream primitiveMode = do
+    vs <- gets globalVertexStream
+    liftIO $ do
+        GL.bindVertexArrayObject $= Just (vao vs)
+        GL.drawArrays primitiveMode 0 (fromIntegral $ vertNum vs)
+
+updateScreenSz :: (Int, Int) -> Action ()
+updateScreenSz sz = modify $ \g -> g { screenSize = sz }
+
+fromVertArrayInto :: ([Vec2], Maybe [Vec2]) -> VertexStream -> Action VertexStream
+fromVertArrayInto (verts, maybeTexCoords) s = liftIO $ do
+    GL.bindBuffer GL.ArrayBuffer $= Just (vbo s)
+    U.replaceBuffer GL.ArrayBuffer verts
+
+    -- fill in texture coordinates if needed
+    let texCoords' = fromMaybe (calculateTexCoords verts) maybeTexCoords
+    GL.bindBuffer GL.ArrayBuffer $= Just (texVbo s)
+    U.replaceBuffer GL.ArrayBuffer texCoords'
+
+    return $ s { vertNum = length verts }
+
+fromVertArrayIntoGlobal :: ([Vec2], Maybe [Vec2]) -> Action ()
+fromVertArrayIntoGlobal xs = do
+    m <- gets globalVertexStream
+    m' <- fromVertArrayInto xs m
+    modify $ \x -> x { globalVertexStream = m' }
+ src/Hate/Graphics/Backend/Compat/Shaders.hs view
@@ -0,0 +1,53 @@+module Hate.Graphics.Backend.Compat.Shaders
+    ( createPipelineNoUniformBindings
+    )
+where
+
+import Hate.Graphics.Shader
+import Hate.Graphics.Pipeline
+import Hate.Graphics.Pipeline.Util
+import qualified Data.ByteString.Char8 as BS (ByteString, pack)
+
+import qualified Graphics.Rendering.OpenGL as GL
+import Graphics.Rendering.OpenGL (($=))
+import qualified Graphics.GLUtil as U
+
+import Data.Maybe (catMaybes)
+
+shaderStr :: ShaderDesc -> String
+shaderStr (ShaderDesc p ins outs unifs body) = header ++ "void main() {\n" ++ body ++ "\n}\n"
+    where versionStr = "#version 330 core"
+          precisionStr = show p
+          inputsStr = unlines . map show $ ins
+          outputsStr = unlines . map show $ outs
+          uniformsStr = unlines . map show $ unifs
+          header = unlines [versionStr, precisionStr, inputsStr, outputsStr, uniformsStr]
+
+shaderBStr :: ShaderDesc -> BS.ByteString
+shaderBStr sd = BS.pack $ shaderStr sd
+
+-- |This is a workaround for pre-4.2 OpenGL
+createPipelineNoUniformBindings :: (ShaderDesc, ShaderDesc) -> IO Pipeline
+createPipelineNoUniformBindings (sdv, sdf) = do
+    let (bindsV, sdv') = stripUniformBindings sdv
+    let (bindsF, sdf') = stripUniformBindings sdf
+
+    p <- createPipelineSource (shaderBStr sdv') (shaderBStr sdf')
+    mapM_ (applyPendingUniformBinding p) (bindsV ++ bindsF) 
+    return p
+
+data PendingUniformBinding = PendingUniformBinding String GL.TextureUnit
+
+stripUniformBindings :: ShaderDesc -> ([PendingUniformBinding], ShaderDesc)
+stripUniformBindings sd = (,) (catMaybes . map uniformToPendingUniform . sdUniforms $ sd) (strip sd)
+    where strip sd = sd { sdUniforms = map strip1 $ sdUniforms sd }
+          strip1 (Uniform typeTag _ name) = Uniform typeTag Nothing name
+
+uniformToPendingUniform :: Uniform -> Maybe PendingUniformBinding
+uniformToPendingUniform (Uniform typeTag maybeBinding name) = 
+    fmap (\(Binding x) -> PendingUniformBinding name (GL.TextureUnit (fromIntegral x))) maybeBinding
+
+applyPendingUniformBinding :: Pipeline -> PendingUniformBinding -> IO ()
+applyPendingUniformBinding p (PendingUniformBinding name x) = do
+    loc <- GL.get $ GL.uniformLocation (program p) name
+    GL.uniform loc $= x
+ src/Hate/Graphics/Backend/Compat/Types.hs view
@@ -0,0 +1,12 @@+module Hate.Graphics.Backend.Compat.Types where
+
+import Hate.Graphics.Types
+import Hate.Graphics.Pipeline
+
+-- supposedly needs more vertex streams and pipelines in the future
+data BackendCompat = BackendCompat { 
+    solidColorPipeline :: Pipeline,
+    texturingPipeline :: Pipeline,
+    globalVertexStream :: VertexStream,
+    screenSize :: (Int, Int)
+}
+ src/Hate/Graphics/Backend/Modern.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE FlexibleContexts #-}
+
+module Hate.Graphics.Backend.Modern (BackendModern(), initialGraphicsState) where
+
+import Hate.Graphics.Backend.Modern.Types
+
+import Hate.Math
+import Hate.Graphics.Backend.Modern.Shaders
+import Hate.Graphics.Rendering
+import Hate.Graphics.Pipeline.Util
+import Hate.Graphics.Pipeline
+import Hate.Graphics.Types
+import Hate.Graphics.Backend.Util
+
+import Control.Monad.State
+
+import qualified Graphics.Rendering.OpenGL as GL
+import Graphics.Rendering.OpenGL (($=))
+import qualified Graphics.GLUtil as U
+
+import Data.Vect.Float.OpenGL (orthoMatrix)
+import Data.List (groupBy)
+import Data.Maybe
+import Control.Applicative
+
+instance Renderer BackendModern where
+    --initialRendererState = initialGraphicsState
+    render = renderBatch
+    contextRequirements _ = DesktopContext 4 4
+    updateScreenSize = updateScreenSz
+
+type Action a = (MonadState BackendModern m, MonadIO m) => m a
+
+initialGraphicsState :: (Int, Int) -> IO BackendModern
+initialGraphicsState screenSz =
+    BackendModern <$> createPipeline solidColorPipelineDescs
+                  <*> createPipeline texturingPipelineDescs
+                  <*> createVertexStream
+                  <*> pure screenSz
+
+renderBatch :: [DrawRequest] -> Action ()
+renderBatch ds = mapM_ (\xs -> renderPipelineBatch (pipeline . head $ xs) xs) $ groupBy equalPipeline ds
+    where
+        a `equalPipeline` b = pipeline a == pipeline b
+
+-- informal contract > p == pipeline . head $ ds
+renderPipelineBatch :: PipelineDescription -> [DrawRequest] -> Action ()
+renderPipelineBatch p ds = do
+    pip <- case p of
+        SolidColorPipeline color -> do
+            pip <- gets solidColorPipeline
+            liftIO $ do
+                activatePipeline pip
+                colorUniformLocation <- GL.get $ GL.uniformLocation (program pip) "in_color"
+                GL.uniform colorUniformLocation $= color
+                return pip
+
+        TexturingPipeline texId -> do
+            pip <- gets texturingPipeline
+            liftIO $ activatePipeline pip
+            liftIO $ GL.textureBinding GL.Texture2D $= Just texId
+            return pip
+
+    forM_ ds $ \d -> do
+        let mat = transformation d .*. origin d
+        setScreenTransformationUniform mat pip
+
+        fromVertArrayIntoGlobal (vertices d, texCoords d)
+        let primitiveMode = vertexLayoutToGLLayout $ vertexLayout d
+        renderGlobalVertexStream primitiveMode
+
+setScreenTransformationUniform :: Mat4 -> Pipeline -> Action ()
+setScreenTransformationUniform t pip = do
+    (screenSizeX, screenSizeY) <- gets screenSize
+
+    let orthoScreenMat = orthoMatrix (0, (fromIntegral screenSizeX)) ((fromIntegral screenSizeY), 0) (-10, 10)
+    let drawMat = (transpose orthoScreenMat) .*. t
+    liftIO $ setUniformM4 pip "screen_transformation" drawMat
+
+renderGlobalVertexStream :: GL.PrimitiveMode -> Action ()
+renderGlobalVertexStream primitiveMode = do
+    vs <- gets globalVertexStream
+    liftIO $ do
+        GL.bindVertexArrayObject $= Just (vao vs)
+        GL.drawArrays primitiveMode 0 (fromIntegral $ vertNum vs)
+
+
+updateScreenSz :: (Int, Int) -> Action ()
+updateScreenSz sz = modify $ \g -> g { screenSize = sz }
+
+fromVertArrayInto :: ([Vec2], Maybe [Vec2]) -> VertexStream -> Action VertexStream
+fromVertArrayInto (verts, maybeTexCoords) s = liftIO $ do
+    GL.bindBuffer GL.ArrayBuffer $= Just (vbo s)
+    U.replaceBuffer GL.ArrayBuffer verts
+
+    -- fill in texture coordinates if needed
+    let texCoords' = fromMaybe (calculateTexCoords verts) maybeTexCoords
+    GL.bindBuffer GL.ArrayBuffer $= Just (texVbo s)
+    U.replaceBuffer GL.ArrayBuffer texCoords'
+
+    return $ s { vertNum = length verts }
+
+fromVertArrayIntoGlobal :: ([Vec2], Maybe [Vec2]) -> Action ()
+fromVertArrayIntoGlobal xs = do
+    m <- gets globalVertexStream
+    m' <- fromVertArrayInto xs m
+    modify $ \x -> x { globalVertexStream = m' }
+ src/Hate/Graphics/Backend/Modern/Shaders.hs view
@@ -0,0 +1,24 @@+module Hate.Graphics.Backend.Modern.Shaders
+	( createPipeline
+	)
+where
+
+import Hate.Graphics.Shader
+import Hate.Graphics.Pipeline
+import Hate.Graphics.Pipeline.Util
+import qualified Data.ByteString.Char8 as BS (ByteString, pack)
+
+shaderStr :: ShaderDesc -> String
+shaderStr (ShaderDesc p ins outs unifs body) = header ++ "void main() {\n" ++ body ++ "\n}\n"
+    where versionStr = "#version 440 core"
+          precisionStr = show p
+          inputsStr = unlines . map show $ ins
+          outputsStr = unlines . map show $ outs
+          uniformsStr = unlines . map show $ unifs
+          header = unlines [versionStr, precisionStr, inputsStr, outputsStr, uniformsStr]
+
+shaderBStr :: ShaderDesc -> BS.ByteString
+shaderBStr sd = BS.pack $ shaderStr sd
+
+createPipeline :: (ShaderDesc, ShaderDesc) -> IO Pipeline
+createPipeline (sdv, sdf) = createPipelineSource (shaderBStr sdv) (shaderBStr sdf)
+ src/Hate/Graphics/Backend/Modern/Types.hs view
@@ -0,0 +1,12 @@+module Hate.Graphics.Backend.Modern.Types where
+
+import Hate.Graphics.Types
+import Hate.Graphics.Pipeline
+
+-- supposedly needs more vertex streams and pipelines in the future
+data BackendModern = BackendModern { 
+    solidColorPipeline :: Pipeline,
+    texturingPipeline :: Pipeline,
+    globalVertexStream :: VertexStream,
+    screenSize :: (Int, Int)
+}
+ src/Hate/Graphics/Backend/Util.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes #-}
+
+module Hate.Graphics.Backend.Util where
+
+--import qualified Codec.Picture as JP
+--import Data.Vector.Storable (unsafeWith)
+
+import Hate.Graphics.Types
+import Hate.Graphics.Shader
+import Hate.Math
+
+import qualified Graphics.Rendering.OpenGL as GL
+import Graphics.Rendering.OpenGL (($=))
+import qualified Graphics.GLUtil as U
+import qualified Data.ByteString.Char8 as BS (ByteString)
+
+import Data.List (maximumBy)
+import Data.Ord
+
+createVertexStream :: IO VertexStream
+createVertexStream = do
+    _vao <- (GL.genObjectName :: IO GL.VertexArrayObject)
+    _vbo <- U.makeBuffer GL.ArrayBuffer ([] :: [Vec2])
+    _texVbo <- U.makeBuffer GL.ArrayBuffer ([] :: [Vec2])
+    let _vertNum = 0
+
+    GL.bindVertexArrayObject $= Just _vao
+    GL.bindBuffer GL.ArrayBuffer $= (Just _vbo)
+    GL.vertexAttribArray (GL.AttribLocation 0) $= GL.Enabled
+    GL.vertexAttribPointer (GL.AttribLocation 0) $= (GL.ToFloat, GL.VertexArrayDescriptor 2 GL.Float 0 U.offset0)
+
+    GL.bindBuffer GL.ArrayBuffer $= (Just _texVbo)
+    GL.vertexAttribArray (GL.AttribLocation 1) $= GL.Enabled
+    GL.vertexAttribPointer (GL.AttribLocation 1) $= (GL.ToFloat, GL.VertexArrayDescriptor 2 GL.Float 0 U.offset0)
+
+    return $ VertexStream { vao = _vao, vbo = _vbo, texVbo = _texVbo, vertNum = _vertNum }
+
+calculateTexCoords :: [Vec2] -> [Vec2]
+calculateTexCoords verts = map (flipY . pointwise scaleFactor) verts
+    where
+        maxX = _1 $ maximumBy (comparing _1) verts
+        maxY = _2 $ maximumBy (comparing _2) verts
+        scaleFactor = Vec2 (1 / maxX) (1 / maxY)
+        flipY (Vec2 x y) = Vec2 x y
+
+vertexLayoutToGLLayout :: VertexLayout -> GL.PrimitiveMode
+vertexLayoutToGLLayout FanVertexLayout = GL.TriangleFan
+vertexLayoutToGLLayout StripVertexLayout = GL.TriangleStrip
+vertexLayoutToGLLayout LinesVertexLayout = GL.Lines
+
+type ShaderSource = BS.ByteString
+
+-- global, shared pipeline things
+globalShader :: [Input] -> [Output] -> [Uniform] -> String -> ShaderDesc
+globalShader = ShaderDesc MediumPrecision
+
+globalVertexInputs :: [Input]
+globalVertexInputs =
+    [ Input Vec2Tag (Just $ Location 0) "position"
+    , Input Vec2Tag (Just $ Location 1) "texcoord"
+    ]
+
+globalVertexUniforms :: [Uniform]
+globalVertexUniforms = [Uniform Mat4Tag Nothing "screen_transformation"]
+
+globalVertexShader :: [Input] -> [Output] -> [Uniform] -> String -> ShaderDesc
+globalVertexShader i o u s = globalShader
+    (globalVertexInputs ++ i)
+    o
+    (globalVertexUniforms ++ u)
+    s
+
+globalFragmentOutputs :: [Output]
+globalFragmentOutputs = [Output Vec4Tag "color"]
+
+globalFragmentUniforms :: [Uniform]
+globalFragmentUniforms = [] -- TODO: add time
+
+globalFragmentShader :: [Input] -> [Uniform] -> String -> ShaderDesc
+globalFragmentShader i u s = globalShader
+    i
+    globalFragmentOutputs
+    (globalFragmentUniforms ++ u)
+    s
+
+makeGlobalPipelineDescs :: [Input] -> [Uniform] -> [Varying] -> [Uniform] -> String -> String -> (ShaderDesc, ShaderDesc)
+makeGlobalPipelineDescs vertexInputs vertexUniforms varyings fragmentUniforms vss fss =
+    ( globalVertexShader vertexInputs (map toOutput varyings) vertexUniforms vss
+    , globalFragmentShader (map toInput varyings) fragmentUniforms fss
+    )
+
+solidColorPipelineDescs :: (ShaderDesc, ShaderDesc)
+solidColorPipelineDescs = makeGlobalPipelineDescs [] [] []
+    [Uniform Vec4Tag Nothing "in_color"]
+    vss fss
+    where
+        vss = unlines
+            ["    gl_Position = screen_transformation * vec4(position, 0, 1);"
+            ]
+
+        fss = "    color = in_color;"
+
+texturingPipelineDescs :: (ShaderDesc, ShaderDesc)
+texturingPipelineDescs = makeGlobalPipelineDescs
+    [] -- no additional vertex inputs
+    [] -- no additional vertex uniforms
+    [ Varying Vec2Tag "var_position"
+    , Varying Vec2Tag "var_texcoord"
+    ]
+    [Uniform Sampler2DTag Nothing "mainTexture"] -- our sprite texture
+    vss
+    fss
+    where
+        vss = unlines
+            ["    gl_Position = screen_transformation * vec4(position, 0, 1);"
+            ,"    var_position = position / 10;"
+            ,"    var_texcoord = texcoord;"
+            ]
+        fss = "    color = texture(mainTexture, var_texcoord);"
+ src/Hate/Graphics/Pipeline.hs view
@@ -0,0 +1,10 @@+module Hate.Graphics.Pipeline where++import qualified Graphics.Rendering.OpenGL as GL++-- |Pipeline object is a complete package needed to render something on the screen.+data Pipeline = Pipeline {+    vertexShader   :: GL.Shader,+    fragmentShader :: GL.Shader,+    program        :: GL.Program+    }
+ src/Hate/Graphics/Pipeline/Util.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE FlexibleContexts #-}
+
+module Hate.Graphics.Pipeline.Util
+    ( activatePipeline
+    , setUniformM4 
+    , createPipelineSource   
+    )
+where
+
+import Hate.Graphics.Pipeline
+
+import Graphics.Rendering.OpenGL(($=))
+import qualified Graphics.Rendering.OpenGL as GL
+import qualified Graphics.GLUtil as U
+
+import Data.Vect.Float
+import Data.Vect.Float.OpenGL
+
+import qualified Data.ByteString as BS
+
+activatePipeline :: Pipeline -> IO ()
+activatePipeline p = GL.currentProgram $= Just (program p)
+
+--setUniformM3 :: Pipeline -> String -> Mat3 -> IO ()
+--setUniformM3 = setUniformMatGeneric U.uniformGLMat4
+
+setUniformM4 :: Pipeline -> String -> Mat4 -> IO ()
+setUniformM4 = setUniformMatGeneric U.uniformGLMat4
+
+setUniformMatGeneric setter pip name mat = do
+    activatePipeline pip
+    matLoc <- GL.get (GL.uniformLocation (program pip) name)
+
+    let tMat = mat
+    glmat <- makeGLMatrix tMat
+    setter matLoc $= glmat
+
+-- "deep" utils
+createPipelineSource :: BS.ByteString -> BS.ByteString -> IO Pipeline
+createPipelineSource vss fss = do 
+    vs <- U.loadShaderBS "VertexShader" GL.VertexShader vss
+    fs <- U.loadShaderBS "FragmentShader" GL.FragmentShader fss
+    prog <- U.linkShaderProgram [vs, fs]
+
+    return $ Pipeline vs fs prog
+
+-- |This function takes paths to vertex and fragment shaders
+createPipelineFromFiles :: FilePath -> FilePath -> IO Pipeline
+createPipelineFromFiles vertShaderPath fragShaderPath = do
+    vs <- BS.readFile vertShaderPath
+    fs <- BS.readFile fragShaderPath
+    createPipelineSource vs fs
+ src/Hate/Graphics/Rendering.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE ExistentialQuantification #-}
+
+module Hate.Graphics.Rendering where
+
+import Hate.Graphics.Types (DrawRequest)
+import Control.Monad.State
+import Control.Monad.IO.Class
+
+-- | Context type specifies the expectation on surroundings of the backend
+-- Desktop means OpenGL, ES - OpenGL ES, and Web - WebGL.
+-- Desktop context can require a minor and major version.
+data ContextRequirements = DesktopContext Int Int | ESContext Int | WebContext
+
+type ScreenSize = (Int, Int)
+
+-- | A class for renderer backends, i.e. something that can render stuff
+class Renderer a where
+    contextRequirements  :: a -> ContextRequirements
+    --initialRendererState :: ScreenSize -> IO a
+    updateScreenSize     :: (MonadState a m, MonadIO m) => ScreenSize -> m ()
+    render               :: (MonadState a m, MonadIO m) => [DrawRequest] -> m ()
+
+-- PIMPL MY RIDE
+data RendererI = forall a. (Renderer a) => RendererImpl a
+ src/Hate/Graphics/Shader.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}
+
+module Hate.Graphics.Shader where
+
+-- data
+type Name = String
+
+data FloatPrecision = HighPrecision | MediumPrecision | LowPrecision
+
+newtype Location = Location Int
+
+data TypeTag = FloatTag | Vec2Tag | Vec3Tag | Vec4Tag | Mat2Tag | Mat3Tag | Mat4Tag | Sampler2DTag
+
+data Input = Input TypeTag (Maybe Location) Name
+    
+data Output = Output TypeTag Name
+
+newtype Binding = Binding Int
+
+data Uniform = Uniform TypeTag (Maybe Binding) Name
+
+data Varying = Varying TypeTag Name
+
+data ShaderDesc = ShaderDesc {
+    sdFloatPrecision :: FloatPrecision,
+    sdInputs :: [Input],
+    sdOutputs :: [Output],
+    sdUniforms :: [Uniform],
+    sdBody :: String
+}
+
+-- show
+showMaybe :: Show a => Maybe a -> String
+showMaybe (Just x) = show x
+showMaybe Nothing = ""
+
+instance Show FloatPrecision where
+    show HighPrecision      = "precision highp float;"
+    show MediumPrecision    = "precision mediump float;"
+    show LowPrecision       = "precision lowp float;"
+
+instance Show Location where 
+    show (Location loc) =  "layout(location = " ++ show loc ++ ") "
+
+instance Show TypeTag where
+    show FloatTag = "float"
+    show Vec2Tag = "vec2"
+    show Vec3Tag = "vec3"
+    show Vec4Tag = "vec4"
+    show Mat2Tag = "mat2"
+    show Mat3Tag = "mat3"
+    show Mat4Tag = "mat4"
+    show Sampler2DTag = "sampler2D"
+
+instance Show Input where
+    show (Input tag loc name) = showMaybe loc ++ "in " ++ show tag ++ " " ++ name ++ ";"
+
+instance Show Output where
+    show (Output tag name) = "out " ++ show tag ++ " " ++ name ++ ";"
+
+instance Show Binding where 
+    show (Binding bnd) =  "layout(binding = " ++ show bnd ++ ") "
+
+instance Show Uniform where
+    show (Uniform tag bnd name) = showMaybe bnd ++ "uniform " ++ show tag ++ " " ++ name ++ ";"
+
+-- Utils
+
+toInput :: Varying -> Input
+toInput (Varying tag name) = Input tag Nothing name
+
+toOutput :: Varying -> Output
+toOutput (Varying tag name) = Output tag name
+ src/Hate/Graphics/Shapes.hs view
@@ -0,0 +1,38 @@+module Hate.Graphics.Shapes where
+
+import Hate.Graphics.Types
+import Hate.Math
+
+-- |Moves the drawn primitive. Note that order of operations matters.
+translate :: Vec2 -> DrawRequest -> DrawRequest
+translate p d = d { transformation = newT }
+    where oldT = transformation d
+          newT = oldT .*. positionToMatrix4 p
+
+-- |Scales the drawn primitive.
+scaled :: Vec2 -> DrawRequest -> DrawRequest
+scaled s d = d { transformation = newT }
+    where oldT = transformation d
+          newT = oldT .*. scaleToMatrix4 s
+
+-- |Rotates the drawn primitive in the Z axis (keeps it flat).
+rotated :: Float -> DrawRequest -> DrawRequest
+rotated r d = d { transformation = newT }
+    where oldT = transformation d
+          newT = oldT .*. rotationToMatrix4 r
+
+colored :: Color -> DrawRequest -> DrawRequest
+colored color (DrawRequest verts coords origin layout transf _) = DrawRequest verts coords origin layout transf (SolidColorPipeline color)
+
+-- |Constructs a 'DrawRequest' containing vertices in a shape of a circle.
+-- /Note that the current implementation always produces a fixed amount of vertices./
+circle :: Float -> DrawRequest
+circle r = DrawRequest verts Nothing one FanVertexLayout one (SolidColorPipeline (Vec4 1.0 1.0 1.0 1.0))
+    where
+        verts = map (flip rotateVec $ vec2 0 r) $ angles
+        angles = map ((* pi2) . (/ segNum)) $ [0..(segNum-1)]
+        pi2 = 2 * pi
+        segNum = 100
+
+line :: Vec2 -> Vec2 -> DrawRequest
+line start end = DrawRequest [start, end] Nothing one LinesVertexLayout one (SolidColorPipeline (Vec4 1.0 1.0 1.0 1.0))
+ src/Hate/Graphics/Sprite.hs view
@@ -0,0 +1,111 @@+module Hate.Graphics.Sprite 
+    ( loadSprite
+    , loadSpriteSheet
+    , sprite
+    , spriteSheet
+    , spritePart
+    )
+where
+
+import Hate.Graphics.Types
+import Hate.Math
+
+import qualified Codec.Picture as JP
+import qualified Codec.Picture.RGBA8 as JPU
+import Data.Vector.Storable (unsafeWith)
+
+import System.Exit
+import qualified Graphics.Rendering.OpenGL as GL
+import Graphics.Rendering.OpenGL (($=))
+
+import Control.Applicative
+
+--drawSquare t = draw $ Polygon $ transform t [vec 0 0, vec 0 1, vec 1 1, vec 1 0]
+
+loadImageDataIntoTexture :: JP.Image JP.PixelRGBA8 -> IO ()
+loadImageDataIntoTexture (JP.Image width height dat) = do
+    unsafeWith dat $ GL.build2DMipmaps GL.Texture2D GL.RGBA8 (fromIntegral width) (fromIntegral height)
+      . GL.PixelData GL.RGBA GL.UnsignedByte
+
+loadImageDataIntoTexture _ = error "Not yet supported"
+
+getImageSize :: JP.Image JP.PixelRGBA8 -> (Int, Int)
+getImageSize (JP.Image width height _) = (width, height)
+
+-- |Loads a file from disk and constructs a drawable sprite.
+loadSprite :: FilePath -> IO Sprite
+loadSprite path = do
+    image <- (fmap . fmap) JPU.fromDynamicImage $ JP.readImage path
+    case image of
+        (Left err) -> do print err
+                         exitWith (ExitFailure 1)
+        (Right imgData) -> do 
+            texId <- GL.genObjectName :: IO GL.TextureObject
+            GL.textureBinding GL.Texture2D $= Just texId
+            loadImageDataIntoTexture imgData
+            GL.textureFilter  GL.Texture2D GL.$= ((GL.Nearest, Just GL.Nearest), GL.Nearest)
+            GL.textureWrapMode GL.Texture2D GL.S $= (GL.Repeated, GL.Repeat)
+            GL.textureWrapMode GL.Texture2D GL.T $= (GL.Repeated, GL.Repeat)
+            return $ Sprite { texture = texId, size = getImageSize imgData }
+
+-- |Loads a sprite sheet from disk.
+loadSpriteSheet :: FilePath
+                -> (Int, Int) -- ^ The size in tiles of the sheet
+                -> IO SpriteSheet
+loadSpriteSheet path sz = SpriteSheet <$> loadSprite path <*> pure sz
+
+-- |Creates a 'DrawRequest' that draws a sprite. The 'OriginReference' parameter specifices the
+-- "hooking point" for the rotations and translations.
+sprite :: OriginReference -> Sprite -> DrawRequest
+sprite originRef (Sprite (w,h) t) = DrawRequest quad Nothing originMat FanVertexLayout one (TexturingPipeline t)
+    where
+        quad = [Vec2 0 0, Vec2 fw 0, Vec2 fw fh, Vec2 0 fh]
+        fw = fromIntegral w
+        fh = fromIntegral h
+        originMat = calcSpriteOriginMatrix originRef fw fh
+
+-- |Creates a 'DrawRequest' with a rectangle cut out of a regular sprite sheet. The number specifies the 
+-- index of the sprite, counting from the top-left one.
+spriteSheet :: OriginReference -> Int -> SpriteSheet -> DrawRequest
+spriteSheet originRef num (SpriteSheet (Sprite (w,h) t) (sx, sy)) = DrawRequest quad texCoords originMat FanVertexLayout one (TexturingPipeline t)
+    where 
+        quad = [Vec2 0 0, Vec2 fw 0, Vec2 fw fh, Vec2 0 fh]
+        texCoords = Just $ [ Vec2 txStart tyStart
+                           , Vec2 txEnd tyStart
+                           , Vec2 txEnd tyEnd
+                           , Vec2 txStart tyEnd
+                           ]
+
+        coordX = num `mod` sx
+        coordY = num `div` sx
+
+        txStart = fromIntegral coordX / fromIntegral sx
+        tyStart = fromIntegral coordY / fromIntegral sy
+        txEnd = fromIntegral (coordX + 1) / fromIntegral sx
+        tyEnd = fromIntegral (coordY + 1) / fromIntegral sy
+
+        fw = fromIntegral w / fromIntegral sx
+        fh = fromIntegral h / fromIntegral sy
+        originMat = calcSpriteOriginMatrix originRef fw fh
+
+-- | Creates a 'DrawRequest' with a rectangle cut out of a sprite
+spritePart :: (Vec2, Vec2) -> Sprite -> DrawRequest
+spritePart (Vec2 x1 y1, Vec2 x2 y2) (Sprite (w,h) t) = dr
+    where
+        dr = DrawRequest quad texCoords one FanVertexLayout one (TexturingPipeline t)
+        quad = [Vec2 0 0, Vec2 fw 0, Vec2 fw fh, Vec2 0 fh]
+
+        texCoords = Just $ [ Vec2 x1 y1
+                           , Vec2 x2 y1
+                           , Vec2 x2 y2
+                           , Vec2 x1 y2
+                           ]
+
+        fw = fromIntegral w * (x2-x1)
+        fh = fromIntegral h * (y2-y1)
+
+calcSpriteOriginMatrix :: OriginReference -> Float -> Float -> Mat4
+calcSpriteOriginMatrix originRef fw fh =
+    case originRef of
+        TopLeft -> one
+        Middle -> positionToMatrix4 $ Vec2 (-fw/2) (-fh/2)
+ src/Hate/Graphics/Types.hs view
@@ -0,0 +1,41 @@+module Hate.Graphics.Types where++import qualified Graphics.Rendering.OpenGL as GL+import Data.Vect.Float+import Data.Vect.Float.Instances()++-- |A general type for a graphical mesh, either in indexed or raw form.+data VertexStream = VertexStream {+    vao :: GL.VertexArrayObject,+    vbo :: GL.BufferObject,+    texVbo :: GL.BufferObject,+    vertNum :: Int+} deriving (Eq, Show)++-- userspace data+data PipelineDescription = SolidColorPipeline Vec4 | TexturingPipeline GL.TextureObject deriving (Eq, Show)++data VertexLayout = FanVertexLayout | StripVertexLayout | LinesVertexLayout deriving (Eq, Show)++data OriginReference = TopLeft | Middle deriving (Eq, Show)++data DrawRequest = DrawRequest { +    vertices :: [Vec2],+    texCoords :: Maybe [Vec2],+    origin :: Mat4,+    vertexLayout :: VertexLayout,+    transformation :: Mat4,+    pipeline :: PipelineDescription+} deriving (Eq, Show)++data Sprite = Sprite {+    size :: (Int, Int),+    texture :: GL.TextureObject+} deriving (Eq, Show)++data SpriteSheet = SpriteSheet {+    spriteRef :: Sprite,+    sheetSize :: (Int, Int)+}++type Color = Vec4
+ src/Hate/Math.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE FlexibleInstances #-}
+
+{-|
+Module      : Hate.Math
+Description : Hate mathematical utilities
+License     : MIT
+Maintainer  : bananu7@o2.pl
+Stability   : provisional
+Portability : full
+
+This module mostly works with primitives from @Data.Vect@,
+adding some utilities useful in rendering and OpenGL
+interoperability.
+
+-}
+
+module Hate.Math 
+    ( module Data.Vect.Float
+    , module Hate.Math.Util
+    , module Hate.Math.Types
+    )
+where
+
+import Data.Vect.Float
+import Data.Vect.Float.Instances()
+
+import Hate.Math.Types
+import Hate.Math.Util
+ src/Hate/Math/Transformable/Class.hs view
@@ -0,0 +1,6 @@+module Hate.Math.Transformable.Class where++import Hate.Math.Types++class Transformable t where+    transform :: Transformation -> t -> t
+ src/Hate/Math/Types.hs view
@@ -0,0 +1,12 @@+module Hate.Math.Types where+	+import Data.Vect.Float+import Data.Vect.Float.Instances()++data Transformation = Transformation { +    position :: Vec2,+    rotation :: Rotation,+    scale :: Vec2+} deriving (Eq, Show)++type Rotation = Float
+ src/Hate/Math/Util.hs view
@@ -0,0 +1,61 @@+module Hate.Math.Util where
+
+import Hate.Math.Types
+import Data.Vect.Float
+
+identityTransform :: Transformation
+identityTransform = Transformation 0 0 1
+
+rotateVec :: Rotation -> Vec2 -> Vec2
+rotateVec a (Vec2 x y) = Vec2 (x * cos a - y * sin a) (x * sin a + y * cos a)
+
+--type Vec2 = GL.Vertex2 Float
+vec2 :: Float -> Float -> Vec2 
+vec2 = Vec2
+
+toMatrix :: Transformation -> Mat3
+toMatrix (Transformation p r s) = rotationToMatrix r .*. positionToMatrix p .*. scaleToMatrix s
+
+positionToMatrix :: Vec2 -> Mat3
+positionToMatrix (Vec2 x y) = Mat3 
+    (Vec3 1 0 x)
+    (Vec3 0 1 y)
+    (Vec3 0 0 1)
+
+rotationToMatrix :: Float -> Mat3
+rotationToMatrix r = Mat3
+    (Vec3 (cos r) (negate $ sin r) 0)
+    (Vec3 (sin r) (         cos r) 0)
+    (Vec3 0                      0 1)
+
+scaleToMatrix :: Vec2 -> Mat3
+scaleToMatrix (Vec2 x y) = Mat3
+    (Vec3 x 0 0)
+    (Vec3 0 y 0)
+    (Vec3 0 0 1)
+
+
+toMatrix4 :: Transformation -> Mat4
+toMatrix4 (Transformation p r s) =  positionToMatrix4 p .*. rotationToMatrix4 r .*. scaleToMatrix4 s
+
+positionToMatrix4 :: Vec2 -> Mat4
+positionToMatrix4 (Vec2 x y) = Mat4
+    (Vec4 1 0 0 x)
+    (Vec4 0 1 0 y)
+    (Vec4 0 0 1 0)
+    (Vec4 0 0 0 1)
+
+rotationToMatrix4 :: Float -> Mat4
+rotationToMatrix4 r = Mat4
+    (Vec4 (cos r) (negate $ sin r) 0 0)
+    (Vec4 (sin r) (         cos r) 0 0)
+    (Vec4 0                      0 1 0)
+    (Vec4 0                      0 0 1)
+
+scaleToMatrix4 :: Vec2 -> Mat4
+scaleToMatrix4 (Vec2 x y) = Mat4
+    (Vec4 x 0 0 0)
+    (Vec4 0 y 0 0)
+    (Vec4 0 0 1 0)
+    (Vec4 0 0 0 1)
+