GPipe-GLFW-1.2.3: test/Test/Common.hs
{-# LANGUAGE Rank2Types #-}
module Test.Common where
import Control.Monad.Exception (MonadException)
import Control.Monad.IO.Class (MonadIO)
import Data.Traversable (forM)
import Graphics.GPipe
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)]
-- TODO: make tests
-- * basic
-- * split-thread load/render
-- * render multiple windows
data ShaderEnv os = ShaderEnv
{ extractProjU :: (Buffer os (Uniform (V4 (B4 Float))), Int)
, extractLinePA :: PrimitiveArray Lines (B3 Float, B3 Float)
, extractRastOpts :: (Side, ViewPort, DepthRange)
}
initBuffers
:: MonadIO m
=> [[(V3 Float, V3 Float)]]
-> ContextT w os f m ([Buffer os (B3 Float, B3 Float)], Buffer os (Uniform (V4 (B4 Float))))
initBuffers 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 :: forall os f. Shader os f (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
-- TODO: just adds the draw call to the end of the shader & glues some init functions together
initRenderContext
:: (MonadIO m, MonadException m)
=> [[(V3 Float, V3 Float)]]
-> ContextT w os (ContextFormat RGBFloat Depth) m
( [Buffer os (B3 Float, B3 Float)]
, Buffer os (Uniform (V4 (B4 Float)))
, CompiledShader os (ContextFormat RGBFloat Depth) (ShaderEnv os)
)
initRenderContext rawMeshes = do
projShader <- compileShader (projectLines >>= drawContextColorDepth (const (ContextColorOption NoBlending $ pure True, DepthOption Less True)))
(meshesB, projMatB) <- initBuffers 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
basicRenderer
:: V2 Int
-> ( [Buffer os (B3 Float, B3 Float)]
, Buffer os (Uniform (V4 (B4 Float)))
, CompiledShader os (ContextFormat RGBFloat Depth) (ShaderEnv os)
)
-> Render os (ContextFormat RGBFloat Depth) ()
basicRenderer size (meshesB, projMatB, projShader) = do
clearContextColor 0.2 -- grey
clearContextDepth 1 -- far plane
meshPAs <- forM meshesB $ \mesh -> do
meshVA <- newVertexArray mesh
return $ toPrimitiveArray LineStrip meshVA
projShader $ ShaderEnv (projMatB, 0) (mconcat meshPAs) (Front, ViewPort 0 size, DepthRange 0 1)
mainloop
:: (MonadIO m, MonadException m)
=> (Int, Int)
-> ( [Buffer os (B3 Float, B3 Float)]
, Buffer os (Uniform (V4 (B4 Float)))
, CompiledShader os (ContextFormat RGBFloat Depth) (ShaderEnv os)
)
-> ContextT w os (ContextFormat RGBFloat Depth) m ()
mainloop frame resources@(_, projMatB, _)
| done frame = return ()
| otherwise = do
-- compute a projection matrix & write it to the buffer
size <- getContextBuffersSize
writeBuffer projMatB 0 [computeProjMat (frac frame) size]
-- render the scene and then loop
render $ basicRenderer size resources
swapContextBuffers
mainloop (next frame) resources
where
done (maxFrame, curFrame) = curFrame > maxFrame
frac (maxFrame, curFrame) = fromIntegral curFrame / fromIntegral maxFrame
next = fmap (+1)