aboutsummaryrefslogtreecommitdiff
path: root/Graphics/Glyph/Shaders.hs
blob: 01f27b6b0acb8a9a6b77a08384d92db673ceb904 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
module Graphics.Glyph.Shaders where

import Graphics.Rendering.OpenGL
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Control.Monad
import Data.Maybe
import Data.List as List
import Graphics.Glyph.Util

{- Load a shader from a file giving the type of the shader
 - to load.
 - This function returns the shader log as a string and
 - a shader as a maybe. Nothing if the shader didn't complie
 - and Just if the shader did compile
 -}
class IsShaderSource a where
    loadShader :: ShaderType -> a -> IO (String, Maybe Shader)

instance IsShaderSource FilePath where
    loadShader typ path = loadShader typ =<< BS.readFile path

instance IsShaderSource BS.ByteString where
    loadShader typ src = do
        shader <- createShader typ
        shaderSourceBS shader $= src
        compileShader shader
    
        ok <- get (compileStatus shader)
        infoLog <- get (shaderInfoLog shader)
    
        unless ok $
            deleteObjectNames [shader]
    
        return ( infoLog, if not ok then Nothing else Just shader );

instance IsShaderSource BSL.ByteString where
    loadShader typ = loadShader typ . toStrict
        where toStrict = BS.concat . BSL.toChunks


{- Load multiple shaders -}
loadShaders :: (IsShaderSource a) => [(ShaderType,a)] -> IO [(String, Maybe Shader)]
loadShaders = mapM ( uncurry loadShader )

{- Return the sucessfully complied shaders
 - as a new array of working shaders -}
workingShaders :: [(a, Maybe Shader)] -> [Shader]
workingShaders = mapMaybe snd

{- Create a program from a list of working shaders -}
createShaderProgram :: [Shader] -> IO (String, Maybe Program)
createShaderProgram shaders = do
    p <- createProgram
    mapM_ (attachShader p) shaders
    linkProgram p

    ok <- get $ linkStatus p
    info <- get $ programInfoLog p

    unless ok $
        deleteObjectNames [p]

    return ( info, not ok ? Nothing $ Just p )

{- Creates a shader program, but will only build the program if all the
 - shaders compiled correctly -}
createShaderProgramSafe :: [(String,Maybe Shader)] -> IO (String, Maybe Program)
createShaderProgramSafe shaders = 
    not (List.all (isJust.snd) shaders) ?
        return (concatMap fst shaders, Nothing) $
        createShaderProgram $ workingShaders shaders
        

{- Get the uniform form a program. -}
getUniform :: Uniform a => String -> IO (Maybe (StateVar a))
getUniform name =
    get currentProgram >>= (\pr -> case pr of
            Just p -> liftM (Just . uniform) (get $ uniformLocation p name)
            Nothing -> return Nothing )

getUniformForProgram :: Uniform a => String -> Program -> IO (StateVar a)
getUniformForProgram name prog =
    liftM uniform (get $ uniformLocation prog name)
    

getUniformLocation :: String -> IO (Maybe UniformLocation)
getUniformLocation name =
    get currentProgram >>= maybe (return Nothing) (\prog ->
        liftM Just (get $ uniformLocation prog name) )

loadProgramSafe ::
    (IsShaderSource a,
     IsShaderSource b,
     IsShaderSource c) =>
        a -> b -> Maybe c -> IO (Maybe Program)
loadProgramSafe vert frag geom = do
    shaders <- sequence $ catMaybes [
        Just $ loadShader VertexShader vert,
        Just $ loadShader FragmentShader frag,
        liftM (loadShader GeometryShader) geom]
    -- mapM_ (putStrLn . fst) shaders
    (linklog, maybeProg) <- createShaderProgramSafe shaders

    if isNothing maybeProg then do
        putStrLn "Failed to link program"
        putStrLn linklog
        return Nothing
        else return maybeProg