{-# LANGUAGE NoMonomorphismRestriction, ScopedTypeVariables #-} module Render where import Control.Monad ( forM, when ) import Data.Either import Data.Maybe import Foreign.Ptr import Data.IORef import Graphics.Rendering.OpenGL as GL import Graphics.UI.GLUT import System.Environment data Viewer = Viewer { image_dirty :: Bool, mwindow_size :: Maybe Size, mrendering :: Maybe (IO ()), fbo_state :: FBOState, viewing_position :: (Int, Int), viewing_scale :: Double } data FBOState = NoFBO | HaveFBO FBOContext deriving (Eq, Show) data FBOContext = FBOContext { fbo :: FramebufferObject, fbo_tex :: TextureObject, fbo_size :: Size } deriving (Eq, Show) init_display = do name <- getProgName args <- getArgs args' <- initialize name args initialDisplayMode $= [RGBAMode, WithDepthBuffer, DoubleBuffered] createWindow "Render" return args' new_viewer = do args <- init_display viewer_ref <- newIORef $ Viewer True Nothing Nothing NoFBO (0,0) 1.0 displayCallback $= viewer_display viewer_ref reshapeCallback $= Just (reshape_viewer_window viewer_ref) keyboardMouseCallback $= Just (glut_event_handler viewer_ref) return (viewer_ref, args) reshape_viewer_window :: IORef Viewer -> ReshapeCallback reshape_viewer_window viewer_ref new_window_size = modifyIORef viewer_ref $ \viewer -> viewer { mwindow_size = Just new_window_size } view_rendering viewer_ref r = modifyIORef viewer_ref $ \viewer -> viewer { image_dirty = True, mrendering = Just r } initialize_fbo viewer_ref = do [fbo :: FramebufferObject] <- genObjectNames 1 [fbo_tex :: TextureObject] <- genObjectNames 1 bindFramebufferEXT FramebufferTarget fbo textureBinding Texture2D $= Just fbo_tex texImage2D Nothing NoProxy 0 RGBA8 (TextureSize2D 4096 4096) 0 (PixelData RGBA UnsignedByte nullPtr) textureFilter Texture2D $= ((Nearest, Nothing), Nearest) framebufferTexture2DEXT FramebufferTarget (ColorAttachment 0) Texture2D fbo_tex 0 textureBinding Texture2D $= Nothing let fbo_context = FBOContext fbo fbo_tex (Size 4096 4096) modifyIORef viewer_ref $ \viewer -> viewer { fbo_state = HaveFBO fbo_context } unbindFramebufferEXT FramebufferTarget rasterize_rendering viewer_ref = do HaveFBO fbo_context <- readIORef viewer_ref >>= return . fbo_state bindFramebufferEXT FramebufferTarget $ fbo fbo_context preservingAttrib [ViewportAttributes] $ do viewport $= (Position 0 0, fbo_size fbo_context) fromJust . mrendering =<< readIORef viewer_ref finish unbindFramebufferEXT FramebufferTarget modifyIORef viewer_ref $ \viewer -> viewer { image_dirty = False } viewer_display viewer_ref = do whenM (readIORef viewer_ref >>= return . isJust . mwindow_size) $ do whenM (readIORef viewer_ref >>= return . (== NoFBO) . fbo_state) $ initialize_fbo viewer_ref whenM ( do rerender <- readIORef viewer_ref >>= return . image_dirty can_render <- readIORef viewer_ref >>= return . isJust . mrendering return $ can_render && rerender ) $ rasterize_rendering viewer_ref window_size <- return . fromJust . mwindow_size =<< readIORef viewer_ref HaveFBO fbo_context <- readIORef viewer_ref >>= return . fbo_state (pos_x, pos_y) <- readIORef viewer_ref >>= return . viewing_position s <- readIORef viewer_ref >>= return . viewing_scale viewport $= (Position 0 0, window_size) -- Establish the coordinate center (0,0) -> (1.0, 1.0) matrixMode $= Modelview 0 loadIdentity scale s s 1 translate $ Vector3 (-1.0 :: Double) (-1.0) 0.0 scale (2.0 :: Double) 2.0 0.0 -- scale (1.0 / fromIntegral (width window_size)) (1.0 / fromIntegral (height window_size)) (1.0 :: Double) translate $ Vector3 (-1.0 * fromIntegral pos_x) (-1.0 * fromIntegral pos_y) (0.0 :: Double) -- Now render the FBO image. clearColor $= Color4 1.0 1.0 1.0 0.0 clear [ColorBuffer] texture Texture2D $= Enabled textureBinding Texture2D $= Just (fbo_tex fbo_context) textureFunction $= Replace polygonMode $= (Fill, Fill) let max_x :: Double = fromIntegral $ width $ fbo_size fbo_context let max_y :: Double = fromIntegral $ height $ fbo_size fbo_context renderPrimitive Quads $ do color $ Color3 (0.0 :: Double) 0.0 0.0 vertex $ Vertex2 (0.0 :: Double) 0.0 texCoord $ TexCoord2 (0.0 :: Double) 0.0 vertex $ Vertex2 (0.0 :: Double) max_y texCoord $ TexCoord2 (0.0 :: Double) 1.0 vertex $ Vertex2 max_x max_y texCoord $ TexCoord2 (1.0 :: Double) 1.0 vertex $ Vertex2 max_x 0.0 texCoord $ TexCoord2 (1.0 :: Double) 0.0 textureBinding Texture2D $= Nothing finish swapBuffers return () glut_event_handler viewer_ref key key_state mods pos = do (pos_x, pos_y) <- readIORef viewer_ref >>= return . viewing_position let (pos_x', pos_y') = case (key,key_state,mods,pos) of (SpecialKey KeyRight, Down, Modifiers Up Up Up, _) -> (pos_x + 10, pos_y) (SpecialKey KeyLeft, Down, Modifiers Up Up Up, _) -> (pos_x - 10, pos_y) (SpecialKey KeyUp, Down, Modifiers Up Up Up, _) -> (pos_x, pos_y + 10) (SpecialKey KeyDown, Down, Modifiers Up Up Up, _) -> (pos_x, pos_y - 10) otherwise -> (pos_x, pos_y) s <- readIORef viewer_ref >>= return . viewing_scale let s' = case (key,key_state,mods,pos) of (SpecialKey KeyUp, Down, Modifiers Down Up Up, _) -> s * 2 (SpecialKey KeyDown, Down, Modifiers Down Up Up, _) -> s * 0.5 otherwise -> s modifyIORef viewer_ref $ \viewer -> viewer { viewing_position = (pos_x', pos_y'), viewing_scale = s' } postRedisplay Nothing whenM :: Monad m => m Bool -> m () -> m () whenM bm m = bm >>= (\b -> when b m) width :: Size -> GLsizei width (Size w _) = w height :: Size -> GLsizei height (Size _ h) = h