diff options
Diffstat (limited to 'Spear/GLSL/Management.hs')
| -rw-r--r-- | Spear/GLSL/Management.hs | 297 |
1 files changed, 0 insertions, 297 deletions
diff --git a/Spear/GLSL/Management.hs b/Spear/GLSL/Management.hs deleted file mode 100644 index 81cf45f..0000000 --- a/Spear/GLSL/Management.hs +++ /dev/null | |||
| @@ -1,297 +0,0 @@ | |||
| 1 | module Spear.GLSL.Management | ||
| 2 | ( | ||
| 3 | -- * Data types | ||
| 4 | GLSLShader | ||
| 5 | , GLSLProgram | ||
| 6 | , ShaderType(..) | ||
| 7 | -- * Program manipulation | ||
| 8 | , newProgram | ||
| 9 | , releaseProgram | ||
| 10 | , linkProgram | ||
| 11 | , useProgram | ||
| 12 | , withGLSLProgram | ||
| 13 | -- * Shader manipulation | ||
| 14 | , attachShader | ||
| 15 | , detachShader | ||
| 16 | , loadShader | ||
| 17 | , newShader | ||
| 18 | , releaseShader | ||
| 19 | -- ** Source loading | ||
| 20 | , loadSource | ||
| 21 | , shaderSource | ||
| 22 | , readSource | ||
| 23 | , compile | ||
| 24 | -- * Location | ||
| 25 | , attribLocation | ||
| 26 | , fragLocation | ||
| 27 | , uniformLocation | ||
| 28 | -- * Helper functions | ||
| 29 | , ($=) | ||
| 30 | , Data.StateVar.get | ||
| 31 | ) | ||
| 32 | where | ||
| 33 | |||
| 34 | |||
| 35 | import Spear.Setup | ||
| 36 | |||
| 37 | import Control.Monad ((<=<), forM) | ||
| 38 | import Control.Monad.Trans.State as State | ||
| 39 | import Control.Monad.Trans.Error | ||
| 40 | import Control.Monad.Trans.Class | ||
| 41 | import Control.Monad (mapM_, when) | ||
| 42 | import qualified Data.ByteString.Char8 as B | ||
| 43 | import Data.StateVar | ||
| 44 | import Foreign.Ptr | ||
| 45 | import Foreign.Storable | ||
| 46 | import Foreign.C.String | ||
| 47 | import Foreign.Marshal.Alloc (alloca) | ||
| 48 | import Foreign.Marshal.Array (withArray) | ||
| 49 | import Graphics.Rendering.OpenGL.Raw.Core31 | ||
| 50 | import System.Directory (doesFileExist, getCurrentDirectory, setCurrentDirectory) | ||
| 51 | import Unsafe.Coerce | ||
| 52 | |||
| 53 | |||
| 54 | -- | Represents a GLSL shader handle. | ||
| 55 | data GLSLShader = GLSLShader | ||
| 56 | { getShader :: GLuint | ||
| 57 | , getShaderKey :: Resource | ||
| 58 | } | ||
| 59 | |||
| 60 | |||
| 61 | -- | Represents a GLSL program handle. | ||
| 62 | data GLSLProgram = GLSLProgram | ||
| 63 | { getProgram :: GLuint | ||
| 64 | , getProgramKey :: Resource | ||
| 65 | } | ||
| 66 | |||
| 67 | |||
| 68 | -- | Encodes several shader types. | ||
| 69 | data ShaderType = VertexShader | FragmentShader deriving (Eq, Show) | ||
| 70 | |||
| 71 | |||
| 72 | toGLShader :: ShaderType -> GLenum | ||
| 73 | toGLShader VertexShader = gl_VERTEX_SHADER | ||
| 74 | toGLShader FragmentShader = gl_FRAGMENT_SHADER | ||
| 75 | |||
| 76 | |||
| 77 | -- | Apply the given function to the GLSLProgram's id. | ||
| 78 | withGLSLProgram :: GLSLProgram -> (GLuint -> a) -> a | ||
| 79 | withGLSLProgram prog f = f $ getProgram prog | ||
| 80 | |||
| 81 | |||
| 82 | -- | Get the location of the given uniform variable within the given program. | ||
| 83 | uniformLocation :: GLSLProgram -> String -> GettableStateVar GLint | ||
| 84 | uniformLocation prog var = makeGettableStateVar get | ||
| 85 | where | ||
| 86 | get = withCString var $ \str -> glGetUniformLocation (getProgram prog) (unsafeCoerce str) | ||
| 87 | |||
| 88 | |||
| 89 | -- | Get or set the location of the given variable to a fragment shader colour number. | ||
| 90 | fragLocation :: GLSLProgram -> String -> StateVar GLint | ||
| 91 | fragLocation prog var = makeStateVar get set | ||
| 92 | where | ||
| 93 | get = withCString var $ \str -> glGetFragDataLocation (getProgram prog) (unsafeCoerce str) | ||
| 94 | set idx = withCString var $ \str -> | ||
| 95 | glBindFragDataLocation (getProgram prog) (unsafeCoerce idx) (unsafeCoerce str) | ||
| 96 | |||
| 97 | |||
| 98 | -- | Get or set the location of the given attribute within the given program. | ||
| 99 | attribLocation :: GLSLProgram -> String -> StateVar GLint | ||
| 100 | attribLocation prog var = makeStateVar get set | ||
| 101 | where | ||
| 102 | get = withCString var $ \str -> glGetAttribLocation (getProgram prog) (unsafeCoerce str) | ||
| 103 | set idx = withCString var $ \str -> | ||
| 104 | glBindAttribLocation (getProgram prog) (unsafeCoerce idx) (unsafeCoerce str) | ||
| 105 | |||
| 106 | |||
| 107 | -- | Create a 'GLSLProgram'. | ||
| 108 | newProgram :: [GLSLShader] -> Setup GLSLProgram | ||
| 109 | newProgram shaders = do | ||
| 110 | h <- setupIO glCreateProgram | ||
| 111 | when (h == 0) $ setupError "glCreateProgram failed" | ||
| 112 | rkey <- register $ deleteProgram h | ||
| 113 | let program = GLSLProgram h rkey | ||
| 114 | |||
| 115 | mapM_ (setupIO . attachShader program) shaders | ||
| 116 | linkProgram program | ||
| 117 | |||
| 118 | return program | ||
| 119 | |||
| 120 | |||
| 121 | -- | Release the given 'GLSLProgram'. | ||
| 122 | releaseProgram :: GLSLProgram -> Setup () | ||
| 123 | releaseProgram = release . getProgramKey | ||
| 124 | |||
| 125 | |||
| 126 | -- | Delete the given 'GLSLProgram'. | ||
| 127 | deleteProgram :: GLuint -> IO () | ||
| 128 | --deleteProgram = glDeleteProgram | ||
| 129 | deleteProgram prog = do | ||
| 130 | putStrLn $ "Deleting shader program " ++ show prog | ||
| 131 | glDeleteProgram prog | ||
| 132 | |||
| 133 | |||
| 134 | -- | Link the given GLSL program. | ||
| 135 | linkProgram :: GLSLProgram -> Setup () | ||
| 136 | linkProgram prog = do | ||
| 137 | let h = getProgram prog | ||
| 138 | err <- setupIO $ do | ||
| 139 | glLinkProgram h | ||
| 140 | alloca $ \statptr -> do | ||
| 141 | glGetProgramiv h gl_LINK_STATUS statptr | ||
| 142 | status <- peek statptr | ||
| 143 | case status of | ||
| 144 | 0 -> getStatus glGetProgramiv glGetProgramInfoLog h | ||
| 145 | _ -> return "" | ||
| 146 | |||
| 147 | case length err of | ||
| 148 | 0 -> return () | ||
| 149 | _ -> setupError err | ||
| 150 | |||
| 151 | |||
| 152 | -- | Use the given GLSL program. | ||
| 153 | useProgram :: GLSLProgram -> IO () | ||
| 154 | useProgram prog = glUseProgram $ getProgram prog | ||
| 155 | |||
| 156 | |||
| 157 | -- | Attach the given GLSL shader to the given GLSL program. | ||
| 158 | attachShader :: GLSLProgram -> GLSLShader -> IO () | ||
| 159 | attachShader prog shader = glAttachShader (getProgram prog) (getShader shader) | ||
| 160 | |||
| 161 | |||
| 162 | -- | Detach the given GLSL shader from the given GLSL program. | ||
| 163 | detachShader :: GLSLProgram -> GLSLShader -> IO () | ||
| 164 | detachShader prog shader = glDetachShader (getProgram prog) (getShader shader) | ||
| 165 | |||
| 166 | |||
| 167 | -- | Load a shader from the file specified by the given string. | ||
| 168 | -- | ||
| 169 | -- This function creates a new shader. To load source code into an existing shader, | ||
| 170 | -- see 'loadSource', 'shaderSource' and 'readSource'. | ||
| 171 | loadShader :: FilePath -> ShaderType -> Setup GLSLShader | ||
| 172 | loadShader file shaderType = do | ||
| 173 | shader <- newShader shaderType | ||
| 174 | loadSource file shader | ||
| 175 | compile file shader | ||
| 176 | return shader | ||
| 177 | |||
| 178 | |||
| 179 | -- | Create a new shader. | ||
| 180 | newShader :: ShaderType -> Setup GLSLShader | ||
| 181 | newShader shaderType = do | ||
| 182 | h <- setupIO $ glCreateShader (toGLShader shaderType) | ||
| 183 | case h of | ||
| 184 | 0 -> setupError "glCreateShader failed" | ||
| 185 | _ -> do | ||
| 186 | rkey <- register $ deleteShader h | ||
| 187 | return $ GLSLShader h rkey | ||
| 188 | |||
| 189 | |||
| 190 | -- | Release the given 'GLSLShader'. | ||
| 191 | releaseShader :: GLSLShader -> Setup () | ||
| 192 | releaseShader = release . getShaderKey | ||
| 193 | |||
| 194 | |||
| 195 | -- | Free the given shader. | ||
| 196 | deleteShader :: GLuint -> IO () | ||
| 197 | --deleteShader = glDeleteShader | ||
| 198 | deleteShader shader = do | ||
| 199 | putStrLn $ "Deleting shader " ++ show shader | ||
| 200 | glDeleteShader shader | ||
| 201 | |||
| 202 | |||
| 203 | -- | Load a shader source from the file specified by the given string into the given shader. | ||
| 204 | loadSource :: FilePath -> GLSLShader -> Setup () | ||
| 205 | loadSource file h = do | ||
| 206 | exists <- setupIO $ doesFileExist file | ||
| 207 | case exists of | ||
| 208 | False -> setupError "the specified shader file does not exist" | ||
| 209 | True -> setupIO $ do | ||
| 210 | code <- readSource file | ||
| 211 | withCString code $ shaderSource h | ||
| 212 | |||
| 213 | |||
| 214 | -- | Load the given shader source into the given shader. | ||
| 215 | shaderSource :: GLSLShader -> CString -> IO () | ||
| 216 | shaderSource shader str = | ||
| 217 | let ptr = unsafeCoerce str | ||
| 218 | in withArray [ptr] $ flip (glShaderSource (getShader shader) 1) nullPtr | ||
| 219 | |||
| 220 | |||
| 221 | -- | Compile the given shader. | ||
| 222 | compile :: FilePath -> GLSLShader -> Setup () | ||
| 223 | compile file shader = do | ||
| 224 | let h = getShader shader | ||
| 225 | |||
| 226 | -- Compile | ||
| 227 | setupIO $ glCompileShader h | ||
| 228 | |||
| 229 | -- Verify status | ||
| 230 | err <- setupIO $ alloca $ \statusPtr -> do | ||
| 231 | glGetShaderiv h gl_COMPILE_STATUS statusPtr | ||
| 232 | result <- peek statusPtr | ||
| 233 | case result of | ||
| 234 | 0 -> getStatus glGetShaderiv glGetShaderInfoLog h | ||
| 235 | _ -> return "" | ||
| 236 | |||
| 237 | case length err of | ||
| 238 | 0 -> return () | ||
| 239 | _ -> setupError $ "Unable to compile shader " ++ file ++ ":\n" ++ err | ||
| 240 | |||
| 241 | |||
| 242 | type StatusCall = GLuint -> GLenum -> Ptr GLint -> IO () | ||
| 243 | type LogCall = GLuint -> GLsizei -> Ptr GLsizei -> Ptr GLchar -> IO () | ||
| 244 | |||
| 245 | |||
| 246 | getStatus :: StatusCall -> LogCall -> GLuint -> IO String | ||
| 247 | getStatus getStatus getLog h = do | ||
| 248 | alloca $ \lenPtr -> do | ||
| 249 | getStatus h gl_INFO_LOG_LENGTH lenPtr | ||
| 250 | len <- peek lenPtr | ||
| 251 | case len of | ||
| 252 | 0 -> return "" | ||
| 253 | _ -> withCString (replicate (unsafeCoerce len) '\0') $ getErrorString getLog h (unsafeCoerce len) | ||
| 254 | |||
| 255 | |||
| 256 | getErrorString :: LogCall -> GLuint -> GLsizei -> CString -> IO String | ||
| 257 | getErrorString getLog h len str = do | ||
| 258 | let ptr = unsafeCoerce str | ||
| 259 | getLog h len nullPtr ptr | ||
| 260 | peekCString str | ||
| 261 | |||
| 262 | |||
| 263 | -- | Load the shader source specified by the given file. | ||
| 264 | -- | ||
| 265 | -- This function implements an #include mechanism, so the given file can | ||
| 266 | -- refer to other files. | ||
| 267 | readSource :: FilePath -> IO String | ||
| 268 | readSource = fmap B.unpack . readSource' | ||
| 269 | |||
| 270 | |||
| 271 | readSource' :: FilePath -> IO B.ByteString | ||
| 272 | readSource' file = do | ||
| 273 | let includeB = B.pack "#include" | ||
| 274 | newLineB = B.pack "\n" | ||
| 275 | isInclude = ((==) includeB) . B.take 8 | ||
| 276 | clean = B.dropWhile (\c -> c == ' ') | ||
| 277 | cleanInclude = B.filter (\c -> c /= '"') . B.dropWhile (\c -> c /= ' ') | ||
| 278 | toLines = B.splitWith (\c -> c == '\n' || c == '\r') | ||
| 279 | addNewLine s = if (B.last s /= '\n') then B.append s newLineB else s | ||
| 280 | parse = fmap B.concat . (fmap . fmap $ flip B.append newLineB) . sequence . | ||
| 281 | fmap (processLine . clean) . toLines | ||
| 282 | processLine l = | ||
| 283 | if isInclude l | ||
| 284 | then readSource' $ B.unpack . clean . cleanInclude $ l | ||
| 285 | else return l | ||
| 286 | |||
| 287 | contents <- B.readFile file | ||
| 288 | |||
| 289 | dir <- getCurrentDirectory | ||
| 290 | let dir' = dir ++ "/" ++ (reverse . dropWhile (\c -> c /= '/') . reverse) file | ||
| 291 | |||
| 292 | setCurrentDirectory dir' | ||
| 293 | code <- parse contents | ||
| 294 | setCurrentDirectory dir | ||
| 295 | |||
| 296 | return code | ||
| 297 | |||
