module Graphics.LambdaCube.RenderSystem.GL.GpuProgram where import Data.List import Control.Monad import Foreign import Foreign.C.String import Graphics.Rendering.OpenGL.Raw.Core31 import qualified Graphics.Rendering.OpenGL.Raw.EXT as EXT import Graphics.LambdaCube.GpuProgram import Graphics.LambdaCube.HardwareVertexBuffer -- Some drivers (e.g. OS X on nvidia) incorrectly determine the attribute binding automatically -- and end up aliasing existing built-ins. So avoid! Fixed builtins are: -- a builtin custom attrib name -- ---------------------------------------------- -- 0 gl_Vertex vertex -- 1 n/a blendWeights -- 2 gl_Normal normal -- 3 gl_Color colour -- 4 gl_SecondaryColor secondary_colour -- 5 gl_FogCoord fog_coord -- 7 n/a blendIndices -- 8 gl_MultiTexCoord0 uv0 -- 9 gl_MultiTexCoord1 uv1 -- 10 gl_MultiTexCoord2 uv2 -- 11 gl_MultiTexCoord3 uv3 -- 12 gl_MultiTexCoord4 uv4 -- 13 gl_MultiTexCoord5 uv5 -- 14 gl_MultiTexCoord6 uv6, tangent -- 15 gl_MultiTexCoord7 uv7, binormal getFixedAttributeIndex :: VertexElementSemantic -> Int -> Int getFixedAttributeIndex semantic index = case semantic of VES_POSITION -> 0 VES_BLEND_WEIGHTS -> 1 VES_NORMAL -> 2 VES_DIFFUSE -> 3 VES_SPECULAR -> 4 VES_BLEND_INDICES -> 7 VES_TEXTURE_COORDINATES -> 8 + index VES_TANGENT -> 14 VES_BINORMAL -> 15 isAttributeValid :: VertexElementSemantic -> Int -> Bool isAttributeValid semantic _ = case semantic of VES_POSITION -> False VES_NORMAL -> False VES_DIFFUSE -> False VES_SPECULAR -> False VES_TEXTURE_COORDINATES -> False VES_BLEND_WEIGHTS -> True VES_BLEND_INDICES -> True VES_BINORMAL -> True VES_TANGENT -> True data GLGpuProgram = GLGpuProgram { glgpShaderObject :: GLuint } deriving (Eq,Ord) data GLLinkedGpuProgram = GLLinkedGpuProgram { gllgpProgramObject :: GLuint } deriving (Eq,Ord) instance LinkedGpuProgram GLLinkedGpuProgram instance GpuProgram GLGpuProgram withGLStringLen :: String -> ((Ptr b, Int) -> IO a) -> IO a withGLStringLen s act = withCAStringLen s $ \(p,len) -> act (castPtr p, fromIntegral len) setShaderSource :: GLuint -> [String] -> IO () setShaderSource shader srcs = do let len = genericLength srcs withMany withGLStringLen srcs $ \charBufsAndLengths -> do let (charBufs, lengths) = unzip charBufsAndLengths withArray charBufs $ \charBufsBuf -> withArray (map fromIntegral lengths) $ \lengthsBuf -> glShaderSource shader len charBufsBuf lengthsBuf shaderInfoLog :: GLuint -> IO String shaderInfoLog a = allocaArray maxLen $ \s -> alloca $ \l -> do glGetShaderInfoLog a (fromIntegral maxLen) l s len <- peek l peekCStringLen (castPtr s, fromIntegral len) where maxLen = 256 programInfoLog :: GLuint -> IO String programInfoLog a = allocaArray maxLen $ \s -> alloca $ \l -> do glGetProgramInfoLog a (fromIntegral maxLen) l s len <- peek l peekCStringLen (castPtr s, fromIntegral len) where maxLen = 256 --compileShader :: GpuProgramType -> String -> IO (Either a String) compileShader :: Integral a => a -> String -> IO (Either GLuint [Char]) compileShader ptype src = do shader <- glCreateShader $ fromIntegral ptype setShaderSource shader [src] glCompileShader shader -- TEMP CODE infoLog <- shaderInfoLog shader --infoM "ResourceLibrary" $ "shader infoLog cmp " ++ infoLog ok <- alloca $ \buf -> glGetShaderiv shader gl_COMPILE_STATUS buf >> peek buf case ok == fromIntegral gl_TRUE of False -> do glDeleteShader shader --errl <- GL.get GL.errors return $ Right $ "shader compilation failed: " ++ infoLog -- ++ concat [show c ++ " - " ++ m ++ "\n" | GL.Error c m <- errl] True -> return $ Left shader mkGLGpuProgram :: GpuProgramType -> String -> IO (Either GLGpuProgram String) mkGLGpuProgram ptype src = do let stype = case ptype of GPT_VERTEX_PROGRAM -> gl_VERTEX_SHADER GPT_FRAGMENT_PROGRAM -> gl_FRAGMENT_SHADER GPT_GEOMETRY_PROGRAM -> EXT.gl_GEOMETRY_SHADER s <- compileShader stype src case s of Right m -> return $ Right m Left sh -> return $ Left $ GLGpuProgram sh mkGLLinkedGpuProgram :: [GLGpuProgram] -> IO (Either GLLinkedGpuProgram String) mkGLLinkedGpuProgram pl = do p <- glCreateProgram forM_ (map glgpShaderObject pl) $ \sh -> glAttachShader p sh glLinkProgram p infoLog <- programInfoLog p -- infoM "ResourceLibrary" $ "program infoLog cmp " ++ infoLog ok <- alloca $ \buf -> glGetProgramiv p gl_LINK_STATUS buf >> peek buf case ok == fromIntegral gl_TRUE of False -> do glDeleteProgram p --errl <- GL.get GL.errors return $ Right $ "GpuProgram linking failed: " ++ infoLog -- ++ concat [show c ++ " - " ++ m ++ "\n" | GL.Error c m <- errl] True -> return $ Left $ GLLinkedGpuProgram p