module Render ( createLightMap, renderLayer, pingPongBetween, bindFBO, ) where import Control.Lens import Control.Monad import Control.Monad.Primitive import Data.Preload.Render import qualified Data.Vector.Fusion.Stream.Monadic as VFSM import qualified Data.Vector.Mutable as MV import qualified Data.Vector.Unboxed.Mutable as UMV import Dodge.Data.Config import Dodge.WindowSize import Foreign hiding (rotate) import Geometry.Data import Graphics.GL.Core45 import Picture.Data import Shader import Shader.Data {- | Determine where light is shining in the world. think of the produced texture as showing what RGB values should be "taken away" from the shape colors. -} createLightMap :: Configuration -> -- | number of walls GLsizei -> -- | number of silhoutte lines to draw Int -> -- | number of "caps" to attempt to draw Int -> -- | the texture object giving positions TO -> -- | the texture object giving normals TO -> [(Point3, Float, Point3)] -> -- Lights RenderData -> IO () createLightMap cfig = case shadrendertype of InstancingShads -> instanceLightMap cfig NoShadows -> const . const . const renderLightingNoShadows NoLighting -> const . const . const . const . const . const renderFlatLighting ComputeShader -> renderComputeShadows _ -> renderShadows shadrendertype where shadrendertype = cfig ^. graphics_shadow_rendering renderLightingNoShadows :: TO -> TO -> [(V3 GLfloat, GLfloat, V3 GLfloat)] -> RenderData -> IO () renderLightingNoShadows positiontexture normaltexture lightPoints pdata = do glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata))) let ltextShad = _lightingTextureShader pdata -- we assume that the renderbuffer's depth has been correctly set elsewhere -- we will not be changing that here glDepthMask GL_FALSE -- clearColor is specified differently in preloadRender -- the colors are inverted withArray [1, 1, 1, 1] $ \ptr -> glClearNamedFramebufferfv (pdata ^. fboLighting . _1 . unFBO) GL_COLOR 0 ptr glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR glEnable GL_STENCIL_TEST glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP flip VFSM.mapM_ (VFSM.fromList lightPoints) $ \(V3 x y z, rad, V3 r g b) -> do glDepthFunc GL_LESS -- setup stencil glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE with 0 $ \ptr -> glClearNamedFramebufferiv (pdata ^. fboLighting . _1 . unFBO) GL_STENCIL 0 ptr glDisable GL_CULL_FACE glStencilFunc GL_ALWAYS 0 255 --draw lightmap itself glDepthFunc GL_ALWAYS -- bind world position texture glBindTextureUnit 0 (positiontexture ^. unTO) glBindTextureUnit 1 (normaltexture ^. unTO) glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE glStencilFunc GL_EQUAL 0 255 glUseProgram (ltextShad ^. shaderUINT) glUniform3f 0 x y z glUniform4f 1 r g b rad glBindVertexArray $ ltextShad ^. shaderVAO . vaoName glDrawArrays (_unPrimitiveMode (_shaderPrimitive ltextShad)) 0 (fromIntegral (4 :: Int)) --cleanup: may not be necessary, depending on what comes after... glDisable GL_CULL_FACE glDisable GL_STENCIL_TEST renderComputeShadows :: GLsizei -> Int -> Int -> TO -> TO -> [(Point3, Float, Point3)] -> RenderData -> IO () renderComputeShadows _ _ _ toPos tanormals lightPoints rd = do withArray (lightsToArray lightPoints) $ \ptr -> glNamedBufferSubData (rd ^. lightsUBO) 0 620 ptr glBindTextureUnit 0 (toPos ^. unTO) glBindTextureUnit 1 (tanormals ^. unTO) glBindImageTexture 5 (rd ^. fboLighting . _2 . unTO) 0 GL_FALSE 0 GL_WRITE_ONLY GL_RGBA8 glUseProgram (rd ^. computeShadowShader) glDispatchCompute 800 835 1 -- the following should be later, just before the texture is accessed --glMemoryBarrier GL_SHADER_IMAGE_ACCESS_BARRIER_BIT glMemoryBarrier GL_TEXTURE_FETCH_BARRIER_BIT return () renderFlatLighting :: RenderData -> IO () renderFlatLighting pdata = do glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata))) withArray [0, 0, 0, 1] $ \ptr -> glClearNamedFramebufferfv (pdata ^. fboLighting . _1 . unFBO) GL_COLOR 0 ptr glDisable GL_CULL_FACE glDepthFunc GL_ALWAYS glDepthMask GL_FALSE renderShadows :: ShadowRendering -> GLsizei -> Int -> Int -> TO -> TO -> [(Point3, Float, Point3)] -> RenderData -> IO () renderShadows shadrendertype nWalls nSils nCaps positiontexture normaltexture lightPoints pdata = do glBindFramebuffer GL_FRAMEBUFFER $ pdata ^. fboLighting . _1 . unFBO --(_unFBO (fst (_fboLighting pdata))) let llinesShad = _lightingLineShadowShader pdata lcapShad = _lightingCapShader pdata lwallShad = _lightingWallShadShader pdata ltextShad = _lightingTextureShader pdata -- we assume that the renderbuffer's depth has been correctly set elsewhere -- we will not be changing that here glDepthMask GL_FALSE -- clearColor is specified differently in preloadRender -- the colors are inverted withArray [1, 1, 1, 1] $ \ptr -> glClearNamedFramebufferfv (pdata ^. fboLighting . _1 . unFBO) GL_COLOR 0 ptr -- setup stencil and blending glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR glEnable GL_STENCIL_TEST glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP -- bind world position texture-- these will be used by the lighting shader glBindTextureUnit 0 (positiontexture ^. unTO) glBindTextureUnit 1 (normaltexture ^. unTO) -- for each of the lights: -- 1. stencil out the shadows from this light's point of view -- 2. calculate lighting based on each fragment's position and normal flip VFSM.mapM_ (VFSM.fromList lightPoints) $ \(V3 x y z, rad, V3 r g b) -> do glDepthFunc GL_LESS -- setup stencil glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE with 0 $ \ptr -> glClearNamedFramebufferiv (pdata ^. fboLighting . _1 . unFBO) GL_STENCIL 0 ptr glDisable GL_CULL_FACE glStencilFunc GL_ALWAYS 0 255 --draw wall shadows glUseProgram (_shaderUINT lwallShad) glUniform3f 0 x y z glUniform1f 1 rad glBindVertexArray $ lwallShad ^. shaderVAO . vaoName -- Just (_vao $ _shadVAO lwallShad) glDrawArrays (_unPrimitiveMode $ _shaderPrimitive lwallShad) 0 nWalls case shadrendertype of GeoObjShads -> do --draw silhouette shadows glEnable GL_DEPTH_CLAMP glUseProgram (_shaderUINT llinesShad) glUniform3f 0 x y z glUniform1f 1 rad glBindVertexArray (_vaoName $ _shaderVAO llinesShad) glDrawElements (_unPrimitiveMode $ _shaderPrimitive llinesShad) (fromIntegral nSils) GL_UNSIGNED_SHORT nullPtr --draw caps on the near plane as required glEnable GL_CULL_FACE glCullFace GL_FRONT glUseProgram (_shaderUINT lcapShad) glUniform3f 0 x y z glBindVertexArray $ lcapShad ^. shaderVAO . vaoName --Just (_vao $ _shadVAO lcapShad) glDrawElements (_unPrimitiveMode $ _shaderPrimitive lcapShad) (fromIntegral nCaps) GL_UNSIGNED_SHORT nullPtr glDisable GL_DEPTH_CLAMP _ -> return () --draw lightmap itself glDepthFunc GL_ALWAYS glDisable GL_CULL_FACE glColorMask GL_TRUE GL_TRUE GL_TRUE GL_FALSE glStencilFunc GL_EQUAL 0 255 glUseProgram (ltextShad ^. shaderUINT) --Just (_shadProg ltextShad) glUniform3f 0 x y z glUniform4f 1 r g b rad glBindVertexArray $ ltextShad ^. shaderVAO . vaoName -- Just (_vao $ _shadVAO ltextShad) glDrawArrays (_unPrimitiveMode (_shaderPrimitive ltextShad)) 0 (fromIntegral (4 :: Int)) --cleanup: may not be necessary, depending on what comes after... glDisable GL_STENCIL_TEST glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE lightsToArray :: [(Point3, Float, Point3)] -> [Float] lightsToArray xs = concat (take 20 $ map t xs ++ repeat [0, 0, 0, 0]) ++ concat (take 20 $ map s xs ++ repeat [0, 0, 0, 0]) where t (V3 a b c, _, _) = [a, b, c, 1] s (_, d, V3 e f g) = [e, f, g, d] instanceLightMap :: Configuration -> -- | number of walls GLsizei -> -- | number of silhoutte lines to draw Int -> -- | number of "caps" to attempt to draw Int -> -- | the texture object giving positions TO -> TO -> [(Point3, Float, Point3)] -> -- Lights RenderData -> IO () instanceLightMap cfig nWalls nSils nCaps toPos tanormals lightPoints pdata = do let lcapShad = _shadowCapShader pdata (xsize, ysize) = getWindowSize _graphics_world_resolution cfig withArray (lightsToArray lightPoints) $ \ptr -> glNamedBufferSubData (pdata ^. lightsUBO) 0 620 ptr forM_ [0 .. 19] $ \i -> glCopyImageSubData (pdata ^. rboBaseBloom) GL_RENDERBUFFER 0 0 0 0 (pdata ^. fboShadow . _2 . _2 . unTO) GL_TEXTURE_2D_ARRAY 0 0 0 i xsize ysize 1 glBindFramebuffer GL_FRAMEBUFFER $ pdata ^. fboShadow . _1 . unFBO glDepthMask GL_FALSE withArray [0, 0, 0, 0] $ \ptr -> glClearNamedFramebufferfv (pdata ^. fboShadow . _1 . unFBO) GL_COLOR 0 ptr with 0 $ \ptr -> glClearNamedFramebufferiv (pdata ^. fboShadow . _1 . unFBO) GL_STENCIL 0 ptr -- stencil out the shadows from each light's point of view -- setup stencil glEnable GL_STENCIL_TEST glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP glDepthFunc GL_LESS glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE glDisable GL_BLEND glDisable GL_CULL_FACE glStencilFunc GL_ALWAYS 0 255 --draw wall shadows glUseProgram $ pdata ^. shadowWallShader . shaderUINT glBindVertexArray $ pdata ^. shadowWallShader . shaderVAO . vaoName glDrawArrays (_unPrimitiveMode $ pdata ^. shadowWallShader . shaderPrimitive) 0 nWalls --draw silhouette shadows glUseProgram $ pdata ^. shadowEdgeShader . shaderUINT glBindVertexArray $ pdata ^. shadowEdgeShader . shaderVAO . vaoName glDrawElements (_unPrimitiveMode $ pdata ^. shadowEdgeShader . shaderPrimitive) (fromIntegral nSils) GL_UNSIGNED_SHORT nullPtr --draw caps on the near plane as required glEnable GL_CULL_FACE glCullFace GL_BACK --glCullFace GL_FRONT glUseProgram (_shaderUINT lcapShad) glBindVertexArray $ lcapShad ^. shaderVAO . vaoName glDrawElements (_unPrimitiveMode $ _shaderPrimitive lcapShad) (fromIntegral nCaps) GL_UNSIGNED_SHORT nullPtr --draw lightmap itself glDepthFunc GL_ALWAYS glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE glStencilFunc GL_EQUAL 0 255 glUseProgram (pdata ^. shadowLightShader . shaderUINT) -- bind world position texture glBindTextureUnit 0 (toPos ^. unTO) glBindTextureUnit 1 (tanormals ^. unTO) glDrawArrays (_unPrimitiveMode $ pdata ^. shadowLightShader . shaderPrimitive) 0 1 glDisable GL_CULL_FACE glDisable GL_STENCIL_TEST --draw to the lighting framebuffer here glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata))) withArray [1, 1, 1, 1] $ \ptr -> glClearNamedFramebufferfv (pdata ^. fboLighting . _1 . unFBO) GL_COLOR 0 ptr glBindTextureUnit 0 (pdata ^. fboShadow . _2 . _1 . unTO) glEnable GL_BLEND glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR glUseProgram (pdata ^. shadowCombineShader . shaderUINT) glDrawArrays GL_POINTS 0 1 withArray [GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT] $ \ptr -> glInvalidateNamedFramebufferData (pdata ^. fboShadow . _1 . unFBO) 2 ptr -- assumes that vertices have already been sent to the shader pingPongBetween :: (FBO, TO) -> (FBO, TO) -> Shader -> IO () pingPongBetween (fb1, to1) (fb2, to2) fs = do glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb2) glBindTextureUnit 0 (_unTO to1) drawShader fs 4 glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb1) glBindTextureUnit 0 (_unTO to2) drawShader fs 4 renderLayer :: Layer -> MV.MVector (PrimState IO) (Shader, VBO) -> UMV.MVector (PrimState IO) Int -> IO () renderLayer layer shads counts = do let layerCounts = UMV.slice (ln * numLayers) 6 counts MV.imapM_ (drawShaderLay ln layerCounts) shads where ln = layerNum layer bindFBO :: FBO -> IO () bindFBO fb = --bindFramebuffer Framebuffer $= fb glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb)