module Graphics.Rendering.Ombra.Draw.Internal (
Draw,
DrawState,
ResStatus(..),
drawState,
drawInit,
clearBuffers,
drawLayer,
drawObject,
preloadGeometry,
preloadTexture,
preloadProgram,
removeGeometry,
removeTexture,
removeProgram,
checkGeometry,
checkTexture,
checkProgram,
textureSize,
setProgram,
resizeViewport,
runDraw,
execDraw,
evalDraw,
gl,
drawGet
) where
import qualified Graphics.Rendering.Ombra.Blend.Internal as Blend
import Graphics.Rendering.Ombra.Color
import Graphics.Rendering.Ombra.Geometry.Internal
import Graphics.Rendering.Ombra.Geometry.Types
import Graphics.Rendering.Ombra.Layer.Internal
import Graphics.Rendering.Ombra.Layer.Types
import Graphics.Rendering.Ombra.Object.Internal
import Graphics.Rendering.Ombra.Object.Types
import Graphics.Rendering.Ombra.Texture.Internal
import Graphics.Rendering.Ombra.Texture.Types
import Graphics.Rendering.Ombra.Backend (GLES)
import qualified Graphics.Rendering.Ombra.Backend as GL
import Graphics.Rendering.Ombra.Internal.GL hiding (Texture, Program, Buffer,
UniformLocation, cullFace,
depthMask, colorMask)
import qualified Graphics.Rendering.Ombra.Internal.GL as GL
import Graphics.Rendering.Ombra.Internal.Resource
import Graphics.Rendering.Ombra.Screen
import Graphics.Rendering.Ombra.Shader.Program
import qualified Graphics.Rendering.Ombra.Stencil.Internal as Stencil
import Graphics.Rendering.Ombra.Vector
import Data.Hashable (Hashable)
import Control.Monad (when)
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
data DrawState = DrawState {
currentProgram :: Maybe ProgramIndex,
loadedProgram :: Maybe LoadedProgram,
programs :: ResMap LoadedProgram,
uniforms :: ResMap UniformLocation,
elemBuffers :: ResMap LoadedBuffer,
attributes :: ResMap LoadedAttribute,
geometries :: ResMap LoadedGeometry,
textureImages :: ResMap LoadedTexture,
activeTextures :: Int,
viewportSize :: (Int, Int),
blendMode :: Maybe Blend.Mode,
stencilMode :: Maybe Stencil.Mode,
cullFace :: Maybe CullFace,
depthTest :: Bool,
depthMask :: Bool,
colorMask :: (Bool, Bool, Bool, Bool)
}
newtype Draw a = Draw { unDraw :: StateT DrawState GL a }
deriving (Functor, Applicative, Monad, MonadIO)
instance EmbedIO Draw where
embedIO f (Draw a) = Draw get >>= Draw . lift . embedIO f . evalStateT a
instance GLES => MonadScreen Draw where
currentViewport = viewportSize <$> Draw get
resizeViewport w h = do setViewport w h
Draw . modify $ \s ->
s { viewportSize = (w, h) }
instance GLES => MonadProgram Draw where
withProgram p act =
do current <- currentProgram <$> Draw get
when (current /= Just (programIndex p)) $
getProgram p >>= \elp ->
case elp of
Right lp -> do Draw . modify $ \s ->
s { currentProgram = Just $
programIndex p
, loadedProgram = Just lp
, activeTextures = 0
}
act lp
Left _ -> return ()
getUniform name = do mprg <- loadedProgram <$> Draw get
case mprg of
Just prg -> do map <- uniforms <$> Draw get
gl $ getResource' prg
(prg, name)
map
Nothing -> return $ Left "No loaded program."
instance GLES => MonadDrawingMode Draw where
withBlendMode m a = stateReset blendMode setBlendMode m a
withStencilMode m a = stateReset stencilMode setStencilMode m a
withDepthTest d a = stateReset depthTest setDepthTest d a
withDepthMask m a = stateReset depthMask setDepthMask m a
withColorMask m a = stateReset colorMask setColorMask m a
withCulling face a = stateReset cullFace setCullFace face a
instance GLES => MonadTexture Draw where
getTexture (TextureLoaded l) = return $ Right l
getTexture (TextureImage t) = getTextureImage t
getActiveTexturesCount = activeTextures <$> Draw get
setActiveTexturesCount n = Draw . modify $ \s ->
s { activeTextures = n }
newTexture w h fm fM = gl $ LoadedTexture w' h' <$> emptyTexture fm fM
where (w', h') = (fromIntegral w, fromIntegral h)
unusedTexture = removeTexture . TextureLoaded
instance GLES => MonadGeometry Draw where
getAttribute = getDrawResource gl attributes
getElementBuffer = getDrawResource gl elemBuffers
getGeometry = getDrawResource id geometries
instance MonadGL Draw where
gl = Draw . lift
drawState :: GLES
=> Int
-> Int
-> IO DrawState
drawState w h = do programs <- newGLResMap
elemBuffers <- newGLResMap
attributes <- newGLResMap
geometries <- newDrawResMap
uniforms <- newGLResMap
textureImages <- newGLResMap
return DrawState { currentProgram = Nothing
, loadedProgram = Nothing
, programs = programs
, elemBuffers = elemBuffers
, attributes = attributes
, geometries = geometries
, uniforms = uniforms
, textureImages = textureImages
, activeTextures = 0
, viewportSize = (w, h)
, blendMode = Nothing
, depthTest = True
, depthMask = True
, stencilMode = Nothing
, cullFace = Nothing
, colorMask = (True, True, True, True)
}
where newGLResMap :: IO (ResMap r)
newGLResMap = newResMap
newDrawResMap :: IO (ResMap r)
newDrawResMap = newResMap
drawInit :: GLES => Draw ()
drawInit = viewportSize <$> Draw get >>=
\(w, h) -> gl $ do clearColor 0.0 0.0 0.0 1.0
enable gl_DEPTH_TEST
depthFunc gl_LESS
viewport 0 0 (fromIntegral w) (fromIntegral h)
runDraw :: Draw a
-> DrawState
-> GL (a, DrawState)
runDraw (Draw a) = runStateT a
execDraw :: Draw a
-> DrawState
-> GL DrawState
execDraw (Draw a) = execStateT a
evalDraw :: Draw a
-> DrawState
-> GL a
evalDraw (Draw a) = evalStateT a
left :: Either String a -> Maybe String
left (Left x) = Just x
left _ = Nothing
preloadGeometry :: GLES => Geometry (i ': is) -> Draw (Maybe String)
preloadGeometry g = left <$> getGeometry g
preloadTexture :: GLES => Texture -> Draw (Maybe String)
preloadTexture t = left <$> getTexture t
preloadProgram :: GLES => Program gs is -> Draw (Maybe String)
preloadProgram p = left <$> getProgram p
removeGeometry :: GLES => Geometry (i ': is) -> Draw ()
removeGeometry g = removeDrawResource id geometries g
removeTexture :: GLES => Texture -> Draw ()
removeTexture (TextureImage i) = removeDrawResource gl textureImages i
removeTexture (TextureLoaded l) = gl $ unloadResource
(Nothing :: Maybe TextureImage) l
removeProgram :: GLES => Program gs is -> Draw ()
removeProgram = removeDrawResource gl programs
checkGeometry :: GLES => Geometry (i ': is) -> Draw (ResStatus ())
checkGeometry g = fmap (const ()) <$> checkDrawResource id geometries g
checkTexture :: (GLES, Num a) => Texture -> Draw (ResStatus (a, a))
checkTexture (TextureImage i) =
fmap loadedTextureSize <$> checkDrawResource gl textureImages i
checkTexture (TextureLoaded l) = return $ Loaded (loadedTextureSize l)
loadedTextureSize :: (GLES, Num a) => LoadedTexture -> (a, a)
loadedTextureSize (LoadedTexture w h _) = (fromIntegral w, fromIntegral h)
checkProgram :: GLES => Program gs is -> Draw (ResStatus ())
checkProgram p = fmap (const ()) <$> checkDrawResource gl programs p
stateReset :: (DrawState -> a) -> (a -> Draw ()) -> a -> Draw b -> Draw b
stateReset getOld set new act = do old <- getOld <$> Draw get
set new
b <- act
set old
return b
getTextureImage :: GLES => TextureImage
-> Draw (Either String LoadedTexture)
getTextureImage = getDrawResource gl textureImages
getProgram :: GLES => Program gs is -> Draw (Either String LoadedProgram)
getProgram = getDrawResource gl programs
setBlendMode :: GLES => Maybe Blend.Mode -> Draw ()
setBlendMode Nothing = do m <- blendMode <$> Draw get
case m of
Just _ -> gl $ disable gl_BLEND
Nothing -> return ()
Draw . modify $ \s -> s { blendMode = Nothing }
setBlendMode (Just newMode) =
do mOldMode <- blendMode <$> Draw get
case mOldMode of
Nothing -> do gl $ enable gl_BLEND
changeColor >> changeEquation >> changeFunction
Just oldMode ->
do when (Blend.constantColor oldMode /= constantColor)
changeColor
when (Blend.equation oldMode /= equation)
changeEquation
when (Blend.function oldMode /= function)
changeFunction
Draw . modify $ \s -> s { blendMode = Just newMode }
where constantColor = Blend.constantColor newMode
equation@(rgbEq, alphaEq) = Blend.equation newMode
function@(rgbs, rgbd, alphas, alphad) = Blend.function newMode
changeColor = case constantColor of
Just (Vec4 r g b a) -> gl $ blendColor r g b a
Nothing -> return ()
changeEquation = gl $ blendEquationSeparate rgbEq alphaEq
changeFunction = gl $ blendFuncSeparate rgbs rgbd
alphas alphad
setStencilMode :: GLES => Maybe Stencil.Mode -> Draw ()
setStencilMode Nothing = do m <- stencilMode <$> Draw get
case m of
Just _ -> gl $ disable gl_STENCIL_TEST
Nothing -> return ()
Draw . modify $ \s -> s { stencilMode = Nothing }
setStencilMode (Just newMode@(Stencil.Mode newFun newOp)) =
do mOldMode <- stencilMode <$> Draw get
case mOldMode of
Nothing -> do gl $ enable gl_STENCIL_TEST
sides newFun changeFunction
sides newOp changeOperation
Just (Stencil.Mode oldFun oldOp) ->
do when (oldFun /= newFun) $
sides newFun changeFunction
when (oldOp /= newOp) $
sides newOp changeOperation
Draw . modify $ \s -> s { stencilMode = Just newMode }
where changeFunction face f = let (t, v, m) = Stencil.function f
in gl $ stencilFuncSeparate face t v m
changeOperation face o = let (s, d, n) = Stencil.operation o
in gl $ stencilOpSeparate face s d n
sides (Stencil.FrontBack x) f = f gl_FRONT_AND_BACK x
sides (Stencil.Separate x y) f = f gl_FRONT x >> f gl_BACK y
setCullFace :: GLES => Maybe CullFace -> Draw ()
setCullFace Nothing = do old <- cullFace <$> Draw get
case old of
Just _ -> gl $ disable gl_CULL_FACE
Nothing -> return ()
Draw . modify $ \s -> s { cullFace = Nothing }
setCullFace (Just newFace) =
do old <- cullFace <$> Draw get
when (old == Nothing) . gl $ enable gl_CULL_FACE
case old of
Just oldFace | oldFace == newFace -> return ()
_ -> gl . GL.cullFace $ case newFace of
CullFront -> gl_FRONT
CullBack -> gl_BACK
CullFrontBack -> gl_FRONT_AND_BACK
Draw . modify $ \s -> s { cullFace = Just newFace }
setDepthTest :: GLES => Bool -> Draw ()
setDepthTest = setFlag depthTest (\x s -> s { depthTest = x })
(gl $ enable gl_DEPTH_TEST) (gl $ disable gl_DEPTH_TEST)
setDepthMask :: GLES => Bool -> Draw ()
setDepthMask = setFlag depthMask (\x s -> s { depthMask = x })
(gl $ GL.depthMask true) (gl $ GL.depthMask false)
setFlag :: (DrawState -> Bool)
-> (Bool -> DrawState -> DrawState)
-> Draw ()
-> Draw ()
-> Bool
-> Draw ()
setFlag getF setF enable disable new =
do old <- getF <$> Draw get
case (old, new) of
(False, True) -> enable
(True, False) -> disable
_ -> return ()
Draw . modify $ setF new
setColorMask :: GLES => (Bool, Bool, Bool, Bool) -> Draw ()
setColorMask new@(r, g, b, a) = do old <- colorMask <$> Draw get
when (old /= new) . gl $
GL.colorMask r' g' b' a'
Draw . modify $ \s -> s { colorMask = new }
where (r', g', b', a') = (bool r, bool g, bool b, bool a)
bool True = true
bool False = false
getDrawResource :: Resource i r m
=> (m (Either String r) -> Draw (Either String r))
-> (DrawState -> ResMap r)
-> i
-> Draw (Either String r)
getDrawResource lft mg i = do
map <- mg <$> Draw get
lft $ getResource i map
checkDrawResource :: Resource i r m
=> (m (ResStatus r) -> Draw (ResStatus r))
-> (DrawState -> ResMap r)
-> i
-> Draw (ResStatus r)
checkDrawResource lft mg i = do
map <- mg <$> Draw get
lft $ checkResource i map
removeDrawResource :: (Resource i r m, Hashable i)
=> (m () -> Draw ())
-> (DrawState -> ResMap r)
-> i
-> Draw ()
removeDrawResource lft mg i = do
s <- mg <$> Draw get
lft $ removeResource i s
drawGet :: Draw DrawState
drawGet = Draw get