249 lines
8.6 KiB
Haskell
249 lines
8.6 KiB
Haskell
module Render (
|
|
createLightMap,
|
|
renderLayer,
|
|
pingPongBetween,
|
|
bindFBO,
|
|
) where
|
|
|
|
import Control.Lens
|
|
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 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 ::
|
|
Config ->
|
|
-- | 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
|
|
NoShadows -> const . const . const renderLightingNoShadows
|
|
NoLighting -> const . const . const . const . const . const renderFlatLighting
|
|
_ -> 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
|
|
|
|
|
|
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
|
|
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
|
|
-- note that the stencil shadows rely on the depth buffer
|
|
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
|
|
|
|
-- 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) numShads counts
|
|
MV.imapM_ (drawShaderLay ln layerCounts) shads
|
|
where
|
|
ln = fromEnum layer
|
|
|
|
bindFBO :: FBO -> IO ()
|
|
bindFBO fb =
|
|
--bindFramebuffer Framebuffer $= fb
|
|
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb)
|