{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} module Main where import Control.Monad (unless) import Control.Monad.Except (MonadError, liftEither, runExceptT) import Control.Monad.IO.Class (MonadIO (..)) import Data.Bifunctor (first) import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as B8 import Data.Function (fix) import qualified Data.Vector.Unboxed as UV import Foreign.Marshal.Array import Graphics.GL import SDL import System.FilePath (()) import Text.Show.Pretty (pPrint) import Typograffiti import Typograffiti.GL vertexShader :: ByteString vertexShader = B8.pack $ unlines [ "#version 330 core" , "uniform mat4 projection;" , "uniform mat4 modelview;" , "in vec2 position;" , "in vec2 uv;" , "out vec2 fuv;" , "void main () {" , " fuv = uv;" , " gl_Position = projection * modelview * vec4(position.xy, 0.0, 1.0);" , "}" ] fragmentShader :: ByteString fragmentShader = B8.pack $ unlines [ "#version 330 core" , "in vec2 fuv;" , "out vec4 fcolor;" , "uniform sampler2D tex;" , "void main () {" , " fcolor = texture(tex, fuv);" , "}" ] -- TODO: Include a default Cache. -- That allows translation, scale, rotation and color. instance Layout (V2 Float) where translate = (+) makeAllocateWord :: ( MonadIO m , MonadError TypograffitiError m ) => Window -> m (Atlas -> String -> m (AllocatedRendering (V2 Float) m)) makeAllocateWord window = do -- Compile our shader program let position = 0 uv = 1 liftGL = liftEither . first TypograffitiErrorGL vert <- liftGL =<< compileOGLShader vertexShader GL_VERTEX_SHADER frag <- liftGL =<< compileOGLShader fragmentShader GL_FRAGMENT_SHADER prog <- liftGL =<< compileOGLProgram [ ("position", fromIntegral position) , ("uv", fromIntegral uv) ] [vert, frag] glUseProgram prog -- Get our uniform locations projection <- getUniformLocation prog "projection" modelview <- getUniformLocation prog "modelview" tex <- getUniformLocation prog "tex" -- Return a function that will generate new words return $ \atlas string -> do liftIO $ putStrLn $ unwords ["Allocating", string] vao <- newBoundVAO pbuf <- newBuffer uvbuf <- newBuffer -- Generate our string geometry geom <- stringTris atlas True string let (ps, uvs) = UV.unzip geom -- Buffer the geometry into our attributes bufferGeometry position pbuf ps bufferGeometry uv uvbuf uvs glBindVertexArray 0 let draw (V2 x y) = do liftIO $ pPrint (string, V2 x y) glUseProgram prog wsz <- get (windowSize window) let pj :: M44 Float = orthoProjection wsz mv :: M44 Float = mat4Translate (V3 x y 0) updateUniform prog projection pj updateUniform prog modelview mv updateUniform prog tex (0 :: Int) glBindVertexArray vao withBoundTextures [atlasTexture atlas] $ do drawVAO prog vao GL_TRIANGLES (fromIntegral $ UV.length ps) glBindVertexArray 0 release = liftIO $ do withArray [pbuf, uvbuf] $ glDeleteBuffers 2 withArray [vao] $ glDeleteVertexArrays 1 (tl, br) = boundingBox ps size = br - tl return AllocatedRendering { arDraw = draw , arRelease = release , arSize = round <$> size } main :: IO () main = do SDL.initializeAll let openGL = defaultOpenGL { glProfile = Core Debug 3 3 } wcfg = defaultWindow { windowInitialSize = V2 640 480 , windowOpenGL = Just openGL , windowResizable = True } w <- createWindow "Typograffiti" wcfg _ <- glCreateContext w let ttfName = "assets" "Lora-Regular.ttf" e <- runExceptT $ do -- Get the atlas atlas <- allocAtlas ttfName (GlyphSizeInPixels 16 16) asciiChars allocWord <- makeAllocateWord w (draw, _, cache) <- loadText allocWord atlas mempty $ unlines [ "Hey there!" , "This is a test of the emergency word system." , "Quit at any time." ] -- Forever loop, drawing stuff fix $ \loop -> do events <- fmap eventPayload <$> pollEvents glClearColor 0 0 0 1 glClear GL_COLOR_BUFFER_BIT (V2 dw dh) <- glGetDrawableSize w glViewport 0 0 (fromIntegral dw) (fromIntegral dh) draw $ V2 10 32 glSwapWindow w unless (QuitEvent `elem` events) loop _ <- unloadMissingWords cache "" return () either (fail . show) return e