GPipe-GLFW4-2.0.0: test/Test/Common.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wno-missing-signatures #-}
module Test.Common where
import Control.Monad.IO.Class (liftIO)
import Data.Traversable (forM)
import GHC.Float (double2Float)
import qualified System.Environment as Env
import Graphics.GPipe
import qualified Graphics.GPipe.Context.GLFW as GLFW (getTime)
import Test.Control (Controller (..))
xAxis :: [(V3 Float, V3 Float)]
xAxis = zip (repeat $ V3 1 0 0) -- red
[V3 (-1) 0 0, V3 1 0 0, V3 0.8 (-0.1) 0, V3 0.8 0 0, V3 1 (-0.1) 0]
yAxis :: [(V3 Float, V3 Float)]
yAxis = zip (repeat $ V3 0 1 0) -- green
[V3 0 (-1) 0, V3 0 1 0, V3 0 0.8 (-0.1), V3 0 0.9 (-0.05), V3 0 1 (-0.1)]
zAxis :: [(V3 Float, V3 Float)]
zAxis = zip (repeat $ V3 0 0 1) -- blue
[V3 0 0 (-1), V3 0 0 1, V3 (-0.1) 0 0.8, V3 (-0.1) 0 1]
plane :: [(V3 Float, V3 Float)]
plane = zip (repeat $ V3 1 1 1) -- white
[V3 (-2) (-0.1) (-2), V3 (-2) (-0.1) 2, V3 2 (-0.1) 2, V3 2 (-0.1) (-2), V3 (-2) (-0.1) (-2)]
data ShaderEnv os = ShaderEnv
{ extractProjU :: (Buffer os (Uniform (V4 (B4 Float))), Int)
, extractLinePA :: PrimitiveArray Lines (B3 Float, B3 Float)
, extractRastOpts :: (Side, PolygonMode, ViewPort, DepthRange)
}
initBuffers win rawMeshes = do
-- make mesh buffers
meshesB <- forM rawMeshes $ \pts -> do
buf <- newBuffer $ length pts
writeBuffer buf 0 pts
return buf
-- make projection matrix buffer
projMatB <- newBuffer 1
return (meshesB, projMatB)
projectLines :: Shader os (ShaderEnv os) (FragmentStream (V3 FFloat, FFloat))
projectLines = do
projMat <- getUniform extractProjU
linePS <- toPrimitiveStream extractLinePA
-- project points
let projectedLinePS = (\(c, p) -> (projMat !* point p, c)) <$> linePS
lineFS <- rasterize extractRastOpts projectedLinePS
-- write fragment depths and return frags
return $ withRasterizedInfo (\fr inf -> (fr, depth inf)) lineFS
where
depth RasterizedInfo {rasterizedFragCoord = (V4 _ _ z _)} = z
checkEnv :: IO ()
checkEnv = do
val <- Env.lookupEnv "LIBGL_ALWAYS_SOFTWARE"
let warn = case val of
Nothing -> " !! If you don't have hardware support, expect a crash."
_ -> ""
putStrLn $ "LIBGL_ALWAYS_SOFTWARE: " ++ show val ++ warn
-- XXX: just adds the draw call to the end of the shader & glues some init functions together
initRenderContext win rawMeshes = do
liftIO checkEnv
projShader <- compileShader (projectLines >>= drawWindowColorDepth (const (win, ContextColorOption NoBlending $ pure True, DepthOption Less True)))
(meshesB, projMatB) <- initBuffers win rawMeshes
return (meshesB, projMatB, projShader)
computeProjMat :: Float -> V2 Int -> M44 Float
computeProjMat frac (V2 w h) = camera2clip !*! world2camera !*! model2world
where
t = frac * 2 * pi
model2world = identity
world2camera = lookAt
-- eye: camera position in world coordinates
(V3 (3 * sin t) (0.5 + cos (t * 2)) (3 * cos t))
(V3 0 0.1 0) -- center: camera look-at target position in world coords
(V3 0 1 0) -- up: camera up-vector
camera2clip = perspective
(pi / 3) -- 60deg field of view "in y direction"
(fromIntegral w / fromIntegral h) -- aspect ratio
1 100 -- near and far clipping plane
renderStep
:: Window os RGBFloat Depth
-> V2 Int
-> ( [Buffer os (B3 Float, B3 Float)]
, Buffer os (Uniform (V4 (B4 Float)))
, CompiledShader os (ShaderEnv os)
)
-> Render os ()
renderStep win size (meshesB, projMatB, projShader) = do
clearWindowColor win 0.2 -- grey
clearWindowDepth win 1 -- far plane
meshPAs <- forM meshesB $ \mesh -> do
meshVA <- newVertexArray mesh
return $ toPrimitiveArray LineStrip meshVA
projShader $ ShaderEnv (projMatB, 0) (mconcat meshPAs) (Front, PolygonFill, ViewPort 0 size, DepthRange 0 1)
runContext :: (ContextHandler ctx) => ContextHandlerParameters ctx -> (forall os. ContextT ctx os IO a) -> IO a
runContext = runContextT
continue :: Monad m => a -> m Bool
continue _ = return False
mainloop win controller resources@(_, projMatB, _) hook = do
stop <- hook controller
Just now <- liftIO GLFW.getTime
if done now controller || stop
then return ()
else do
-- compute a projection matrix & write it to the buffer
size <- getFrameBufferSize win
writeBuffer projMatB 0 [computeProjMat (double2Float $ frac now controller) size]
-- render the scene and then loop
render $ renderStep win size resources
swapWindowBuffers win
mainloop win (next now controller) resources hook