{-# LANGUAGE TypeOperators, ScopedTypeVariables, ExistentialQuantification #-} {-# OPTIONS_GHC -Wall #-} ---------------------------------------------------------------------- -- | -- Module : Shady.CompileE -- Copyright : (c) Conal Elliott 2009 -- License : AGPLv3 -- -- Maintainer : conal@conal.net -- Stability : experimental -- -- Generate and compile vertex and fragment shaders. -- -- In this version, shader programs are represented by functions function -- a single expression to a single expression. See also CompileEs, which -- allows functions between more flexible representations. ---------------------------------------------------------------------- module Shady.CompileE ( -- VShaderF, FShaderF, SProgramF(..) Pos, (:->)(..), ShaderVF , GLSL(..), shaderProgram -- , ShaderExe(..), sinker, compile ) where -- import Control.Applicative (liftA3) import Text.PrettyPrint.Leijen import qualified Text.PrettyPrint.Leijen as L import Text.PrettyPrint.Leijen.PrettyPrec (PrettyPrec) import Text.PrettyPrint.Leijen.DocExpr -- import Shady.Misc (Sink) import Shady.Language.Glom import Shady.Language.Exp import Shady.Language.GLSL -- import Shady.Color (Color) {- import Shady.MechanicsGL (setupShader,glUseProgram,glMaxTextureUnits) import Shady.Uniform import Shady.Attribute -} -- | For gl_Position type Pos = R4 {-------------------------------------------------------------------- Generate and compile shader programs --------------------------------------------------------------------} infixr 7 :->, :-^, :-* -- | Vertex shader type a :-^ v = a :=>* (Pos,v) -- | Fragment shader type v :-* o = v :=>* (R4,o) -- type v :--> o = v :=>* (R4,o) -- | For building vertex/fragment shader pairs. The idea is that a -- complete parameterized shader program has type @u :=> a :- v :--> o@, -- which expands to @u :=> (a :-^> v, v :-* o)@. -- -- u == uniform, a == (vertex) attribute, v == varying, o == fragment output. -- -- When @o == ()@ (color-only output), use the short-hand @u :=> a :-> v@. -- | General vertex/fragment shader pair. data a :-> o = forall v. (HasType v, HasExpr v, PrettyPrec v) => ShaderVF (a :-^ v) (v :-* o) -- | Vertex/fragment pair with no extra output besides color type ShaderVF a = a :-> () -- | GLSL vertex program, fragment program, uniform and vertex attribute. data GLSL u a = GLSL String String (Pat u) (Pat a) instance (HasExpr u, HasExpr a) => Pretty (GLSL u a) where pretty (GLSL v f u a) = announce "vertex " v <$> announce "fragment" f <$> pretty (u,a) where announce l sh = text (l ++ ": ") L.<+> align (pretty sh) instance (HasExpr u, HasExpr a) => Show (GLSL u a) where show = show . pretty -- | Compile a parameterized shader program. TODO: generalize to non-() -- outputs, i.e., to @u :=> a :-> o@. shaderProgram :: (HasType a, HasExpr a, HasType u, HasExpr u) => (u :=> ShaderVF a) -> GLSL u a shaderProgram uav = case uav (patE u) of ShaderVF vert frag -> let v = pat "_varying" vertOut = vert (patE a) fragOut = frag (patE v) uD = D [ Uniform ] u aD = D [Attribute] a vD = D [ Varying ] v vsh = shader [uD,aD,vD] (glPosition :* v ) vertOut fsh = shader [uD, vD] (glFragColor :* UnitG) fragOut in GLSL (show vsh) (show fsh) u a where -- Uniform/varying variables u = pat "_uniform" a = pat "_attribute" -- The awkward "case" keeps ghc's brain from exploding. -- TODO: What do we want to do when o /= ()? shader :: (HasExpr a, HasType a) => [Declaration] -> Pat a -> E a -> Shader shader decls p e = Sh decls [mainDef (p =: e)] {- -- | Executable shader data ShaderExe u a = ShaderExe { xSelect :: IO () -- ^ install this exe , xSinkU :: Sink u -- ^ set uniform , xsinkA :: Sink [a] -- ^ set attribute } sinker :: GLSL u a -> IO (ShaderExe u a) sinker (GLSL vsh fsh u a) = do p <- setupShader vsh fsh units <- glMaxTextureUnits return $ ShaderExe (glUseProgram p) (setUniform units u p) (setAttribute a p) {- -- | Compile a parameterized shader program. Set up a static (for now) -- vertex mesh, and give a sink for setting uniforms and rendering. compile :: (HasType a, HasExpr a, HasType u, HasExpr u) => (u :=> ShaderVF a) -> IO () -> [a] -> IO (Sink u) compile shf draw as = sinker (shaderProgram shf) >>= renderSE draw as renderSE :: IO () -> [a] -> ShaderExe u a -> IO (Sink u) renderSE draw as (ShaderExe useProg setU setA) = do useProg setA as return $ \ u -> useProg >> setU u >> draw -- TODO: Maybe eliminate ShaderExe, collapsing sinker & renderSE into -- compile -} -- | Compile a parameterized shader program. Set up a static (for now) -- vertex mesh, and give a sink for setting uniforms and rendering. compile :: (HasType a, HasExpr a, HasType u, HasExpr u) => (u :=> ShaderVF a) -> IO () -> [a] -> IO (Sink u) compile shf draw as = do -- print (pretty g) p <- setupShader vsh fsh units <- glMaxTextureUnits let useProg = glUseProgram p setA = setAttribute pa p setU = setUniform units pu p useProg setA as return $ \ u -> useProg >> setU u >> draw where GLSL vsh fsh pu pa = shaderProgram shf -- TODO: switch from Sink [a] to Sink (Vbos a), so that the [a] -> Vbos -- conversion can be done up front. Vbos = Glom Vbo. Then simplify the -- signature to Vbos a -> u -> IO (). -- For now I'm wiring in a fixed mesh. -}