packages feed

gore-and-ash-lambdacube-0.2.0.0: src/Game/GoreAndAsh/LambdaCube/Module.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-|
Module      : Game.GoreAndAsh.LambdaCube.Module
Description : Monad transformer and instance for core module
Copyright   : (c) Anton Gushcha, 2016
License     : BSD3
Maintainer  : ncrashed@gmail.com
Stability   : experimental
Portability : POSIX
-}
module Game.GoreAndAsh.LambdaCube.Module(
    LambdaCubeT(..)
  ) where

import Control.Monad.Catch
import Control.Monad.Fix 
import Control.Monad.State.Strict
import Data.Proxy 

import Game.GoreAndAsh
import Game.GoreAndAsh.LambdaCube.State

import LambdaCube.GL as LambdaCubeGL

-- | Monad transformer of the core module.
--
-- [@s@] - State of next core module in modules chain;
--
-- [@m@] - Next monad in modules monad stack;
--
-- [@a@] - Type of result value;
--
-- How to embed module:
-- 
-- @
-- type AppStack = ModuleStack [LambdaCubeT, ... other modules ... ] IO
--
-- newtype AppMonad a = AppMonad (AppStack a)
--   deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadThrow, MonadCatch, MonadLambdaCube)
-- @
--
-- The module is not pure within first phase (see 'ModuleStack' docs), therefore only 'IO' can be used as end monad.
newtype LambdaCubeT s m a = LambdaCubeT { runLambdaCubeT :: StateT (LambdaCubeState s) m a }
  deriving (Functor, Applicative, Monad, MonadState (LambdaCubeState s), MonadFix, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask)

instance (MonadIO m, MonadThrow m, GameModule m s) => GameModule (LambdaCubeT s m) (LambdaCubeState s) where 
  type ModuleState (LambdaCubeT s m) = LambdaCubeState s
  runModule (LambdaCubeT m) s = do
    ((a, s'), nextState) <- runModule runModuleState (lambdacubeNextState s)
    return (a, s' {
        lambdacubeNextState = nextState 
      })  
    where
    runModuleState = flip runStateT s $ do 
      a <- m 
      renderStorages =<< get
      return a

  newModuleState = emptyLambdaCubeState <$> newModuleState
  withModule _ io = withModule (Proxy :: Proxy m) io
  cleanupModule = freeLambdaCubeState

-- | Render all queued storages
renderStorages :: (MonadIO m, MonadThrow m) => LambdaCubeState s -> m ()
renderStorages s@LambdaCubeState{..} = mapM_ renderStorage lambdacubeRenderOrder
  where
  renderStorage si = case getStorageInternal si s of 
    Nothing -> return ()
    Just storage -> case getRendererInternal (storageScheme si) s of 
      Nothing -> return ()
      Just renderer -> do 
        mres <- liftIO $ LambdaCubeGL.setStorage renderer storage
        case mres of 
          Just er -> throwM $! PipeLineIncompatible si er
          Nothing -> liftIO $ LambdaCubeGL.renderFrame renderer