-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathRender.hs
232 lines (205 loc) · 11.4 KB
/
Render.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
{-# LANGUAGE ScopedTypeVariables, PackageImports, TypeFamilies, FlexibleContexts, RecordWildCards, LambdaCase #-}
module Render where
import Control.Monad.Exception (MonadException)
import Control.Monad.IO.Class
import Graphics.GPipe
import qualified Graphics.GPipe.Context.GLFW as GLFW
import qualified Graphics.GPipe.Context.GLFW.Input as Input
import "lens" Control.Lens
import Control.Monad (unless,zipWithM,forM)
import Data.Word (Word32,Word8)
import Control.Applicative (pure)
import Data.Monoid (mappend)
import qualified Data.ByteString as SB
import qualified Data.Map as Map
import qualified Data.Trie as T
import qualified Data.Vector as V
import qualified Data.Vector.Storable as SV
import Codec.Picture as Juicy
import Codec.Picture.Types
import Data.Bits
import Data.Int
import BSP
import Camera
import Q3Patch
import Material
import Graphics
import Data.List (foldl',scanl')
tessellatePatch :: V.Vector DrawVertex -> Int -> Surface -> (V.Vector DrawVertex,V.Vector Int)
tessellatePatch drawV level sf@Surface{..} = case srSurfaceType of
Patch -> (V.concat vl,V.concat il)
where
(w,h) = srPatchSize
gridF :: [DrawVertex] -> [[DrawVertex]]
gridF l = case splitAt w l of
(x,[]) -> [x]
(x,xs) -> x:gridF xs
grid = gridF $ V.toList $ V.take srNumVertices $ V.drop srFirstVertex drawV
controls = [V.fromList $ concat [take 3 $ drop x l | l <- lines] | x <- [0,2..w-3], y <- [0,2..h-3], let lines = take 3 $ drop y grid]
patches = [tessellate c level | c <- controls]
(vl,il) = unzip $ reverse $ snd $ foldl' (\(o,l) (v,i) -> (o+V.length v, (v,V.map (+o) i):l)) (0,[]) patches
_ -> mempty
convertSurface BSPLevel{..} indexBufferQ3 vertexBufferQ3 lightmapCount patchInfo sf@Surface{..} = do
let Shader name sfFlags _ = blShaders V.! srShaderNum
noDraw = sfFlags .&. 0x80 /= 0
lightmap = toFloat (if srLightmapNum < 0 || srLightmapNum > lightmapCount then lightmapCount else srLightmapNum)
emitStream prim firstVertex numVertices firstIndex numIndices = if noDraw then return (0,mempty) else do
vertexArrayQ3 <- (takeVertices numVertices . dropVertices firstVertex) <$> newVertexArray vertexBufferQ3
indexArrayQ3 <- (takeIndices numIndices . dropIndices firstIndex) <$> newIndexArray indexBufferQ3 Nothing
return (srShaderNum, (,) lightmap <$> toPrimitiveArrayIndexed prim indexArrayQ3 vertexArrayQ3)
case srSurfaceType of
Flare -> return (srShaderNum,mempty)
Patch -> let ((firstVertex,firstIndex),(numVertices,numIndices)) = patchInfo
in emitStream TriangleStrip firstVertex numVertices firstIndex numIndices
_ -> emitStream TriangleList srFirstVertex srNumVertices srFirstIndex srNumIndices
type CF = WindowFormat RGBAFloat Depth
type AttInput = (B3 Float, B3 Float, B2 Float, B2 Float, B4 Float)
type A = PrimitiveArray Triangles AttInput
type Tables os = (Texture1D os (Format RFloat),
Texture1D os (Format RFloat),
Texture1D os (Format RFloat),
Texture1D os (Format RFloat),
Texture1D os (Format RFloat))
type UniInput = (B2 Int32, (B3 Float, B Float, B Float, B Float, B3 Float, B3 Float, B3 Float))
missingMaterial :: (ContextHandler w, MonadIO m, MonadException m) => Window os RGBAFloat Depth -> Buffer os (Uniform UniInput) -> ContextT w os m (CompiledShader os (RenderInput os))
missingMaterial win uniformBuffer = do
liftIO (putStr "-")
compileShader $ do
uni <- getUniform (const (uniformBuffer,0))
primitiveStream <- toPrimitiveStream riStream
let viewMat = lookAt' (viewOrigin uni) (viewTarget uni) (viewUp uni)
V2 w h = windowSize uni
projMat = perspective (pi/3) (toFloat w / toFloat h) 1 10000
make3d (V3 x y z) = projMat !*! viewMat !* V4 x y z 1
primitiveStream2 = fmap (\(_, (pos,_,_,_,color)) -> (make3d pos, color)) primitiveStream
fragmentStream <- rasterize (\ri -> (FrontAndBack, ViewPort (V2 0 0) (riScreenSize ri), DepthRange 0 1)) primitiveStream2
let fragmentStream2 = withRasterizedInfo (\a r -> (a, rasterizedFragCoord r ^. _z)) fragmentStream
drawWindowColorDepth (const (win,ContextColorOption NoBlending (pure True),DepthOption Lequal True)) fragmentStream2
compileMaterial :: (ContextHandler w, MonadIO m, MonadException m) => Window os RGBAFloat Depth -> (Texture2DArray os (Format RGBFloat)) -> Texture2D os (Format RGBAFloat) -> T.Trie (Texture2D os (Format RGBAFloat)) -> Tables os -> Buffer os (Uniform UniInput) -> CommonAttrs -> ContextT w os m (CompiledShader os (RenderInput os))
compileMaterial win lightMap checkerTex texInfo tables uniformBuffer shaderInfo = do
liftIO (putStr ".")
compileShader $ do
uni <- getUniform (const (uniformBuffer,0))
let filter = SamplerFilter Linear Linear Linear Nothing
edge = (Repeat, undefined)
(sinT,squareT,sawT,invSawT,triT) = tables
wt <- WaveTable <$> newSampler1D (const (sinT, filter, edge))
<*> newSampler1D (const (squareT, filter, edge))
<*> newSampler1D (const (sawT, filter, edge))
<*> newSampler1D (const (invSawT, filter, edge))
<*> newSampler1D (const (triT, filter, edge))
<*> newSampler1D (const (triT, filter, edge)) -- TODO: generate noise
mkShader win lightMap checkerTex texInfo wt uni shaderInfo
renderQuake :: V3 Float -> BSPLevel -> T.Trie CommonAttrs -> T.Trie (Juicy.Image PixelRGBA8) -> IO ()
renderQuake startPos bsp@BSPLevel{..} shaderInfo imageInfo =
runContextT (GLFW.defaultHandleConfig { GLFW.configEventPolicy = Nothing }) $ do
win <- newWindow (WindowFormatColorDepth RGBA32F Depth32) (GLFW.defaultWindowConfig "Q3 Viewer")
-- pre tesselate patches and append to static draw verices and indices
let patches = map (tessellatePatch blDrawVertices 5) $ V.toList blSurfaces
(verticesQ3,indicesQ3) = mconcat $ (blDrawVertices,blDrawIndices):patches
patchSize = [(V.length v, V.length i) | (v,i) <- patches]
patchOffset = scanl' (\(offsetV,offsetI) (v,i) -> (offsetV + v, offsetI + i)) (V.length blDrawVertices, V.length blDrawIndices) patchSize
patchInfo = zip patchOffset patchSize
vertexBufferQ3 :: Buffer os AttInput <- newBuffer (V.length verticesQ3)
writeBuffer vertexBufferQ3 0 [(dvPosition, dvNormal, dvDiffuseUV, dvLightmaptUV, dvColor) | DrawVertex{..} <- V.toList verticesQ3]
indexBufferQ3 :: Buffer os (B Word32) <- newBuffer $ V.length indicesQ3
writeBuffer indexBufferQ3 0 $ map fromIntegral $ V.toList indicesQ3
-- for wave functions
tables <- setupTables
uniformBuffer :: Buffer os (Uniform UniInput) <- newBuffer 1
-- load lightmap textures
let toRGB (r:g:b:l) = V3 r g b : toRGB l
toRGB _ = []
lightmapCount = (V.length blLightmaps)
lightmapArray <- newTexture2DArray RGB8 (V3 128 128 (lightmapCount+1)) 1
V.forM_ (V.indexed blLightmaps) $ \(i, lm) ->
writeTexture2DArray lightmapArray 0 (V3 0 0 i) (V3 128 128 1) $ toRGB . SB.unpack . lmMap $ lm
-- Write a white texture last
writeTexture2DArray lightmapArray 0 (V3 0 0 lightmapCount) (V3 128 128 1) (repeat (V3 1 1 (1::Float)))
-- load diffuse textures
checkerTex <- newTexture2D RGBA8 (V2 8 8) 1
let whiteBlack = cycle [V4 0 0 0 1, V4 1 1 0 1] :: [V4 Float]
blackWhite = tail whiteBlack
writeTexture2D checkerTex 0 0 (V2 8 8) (cycle (take 8 whiteBlack ++ take 8 blackWhite))
let mkTex i@(Image w h _) = do
tex <- newTexture2D SRGB8A8 (V2 w h) (floor ((max (log (fromIntegral w :: Float)) (log (fromIntegral h :: Float))) / log 2) :: Int)
writeTexture2D tex 0 0 (V2 w h) $ pixelFoldMap (\(PixelRGBA8 r g b a) -> [V4 r g b a]) i
generateTexture2DMipmap tex
return tex
texInfo <- sequence $ mkTex <$> imageInfo
-- shader for unknown materials
missingMaterialShader <- missingMaterial win uniformBuffer
-- create shader for each material
usedShaders <- mapM (\Shader{..} -> maybe (return missingMaterialShader) (compileMaterial win lightmapArray checkerTex texInfo tables uniformBuffer) $ T.lookup shName shaderInfo) blShaders
let surfaces vpSize = do
surface0 <- zipWithM (convertSurface bsp indexBufferQ3 vertexBufferQ3 lightmapCount) patchInfo $ V.toList blSurfaces
-- group surfaces by material
let surface1 = Map.unionsWith mappend . map (\(k,v) -> Map.singleton k [v]) $ surface0
--surface1 = Map.unionsWith mappend . map (uncurry Map.singleton) $ surface0
surface2 = Map.mapWithKey
(\shader surface ->
( caSort <$> T.lookup (shName $ blShaders V.! shader) shaderInfo
-- pass to shader every per render varying input
-- geometry, diffuse texture, lightmap texture
, (usedShaders V.! shader) (RenderInput vpSize (mconcat surface))
)
) surface1
-- sort surfaces by render queue
sorted = concat $ Map.elems $ Map.unionsWith mappend [Map.singleton k [v] | (k,v) <- Map.elems surface2]
allSurf = sequence_ sorted
allSurf
liftIO $ putStrLn "Hello 1"
liftIO $ GLFW.setTime 0
Just t0 <- liftIO $ GLFW.getTime
renderLoop win uniformBuffer (s0 startPos) t0 surfaces
renderLoop win uniformBuffer s t renderings = do
-- read input
Just (mx,my) <- Input.getCursorPos win
let keyIsPressed k = fmap (== Input.KeyState'Pressed) . fmap (maybe Input.KeyState'Released id) $ Input.getKey win k
keys <- (,,,,) <$> keyIsPressed Input.Key'Left
<*> keyIsPressed Input.Key'Up
<*> keyIsPressed Input.Key'Down
<*> keyIsPressed Input.Key'Right
<*> keyIsPressed Input.Key'RightShift
Just t' <- liftIO $ GLFW.getTime
size <- getFrameBufferSize win
let s'@(eye,center,up,_) = calcCam (t'-t) (realToFrac mx, realToFrac my) keys s
{-
uniform tuple:
entityRGB, entityAlpha, identityLight, time, viewOrigin, viewTarget, viewUp
(V3 Float, Float, Float, Float, V3 Float, V3 Float, V3 Float)
-}
writeBuffer uniformBuffer 0 [(fromIntegral <$> size,(V3 1 1 1, 1, 1, realToFrac t', eye, center, up))]
render $ do
clearWindowColor win 0.5
clearWindowDepth win 1
renderings size
swapWindowBuffers win
Just closeRequested <- GLFW.windowShouldClose win
unless closeRequested $ do
GLFW.mainstep win GLFW.Poll
renderLoop win uniformBuffer s' t' renderings
-- Utility code
tableTexture table = do
let size = length table
--f :: Float -> Normalized Word32
--f x = floor $ min 255 $ max 0 $ 128 + 128 * x
tex <- newTexture1D R8 size 1
writeTexture1D tex 0 0 size (table :: [Float]) -- $ map f table
return tex
setupTables = do
let funcTableSize = 1024 :: Float
sinTexture = [sin (i*2*pi/(funcTableSize-1)) | i <- [0..funcTableSize-1]]
squareTexture = [if i < funcTableSize / 2 then 1 else -1 | i <- [0..funcTableSize-1]]
sawToothTexture = [i / funcTableSize | i <- [0..funcTableSize-1]]
inverseSawToothTexture = reverse [i / funcTableSize | i <- [0..funcTableSize-1]]
triangleTexture = l1 ++ map ((-1)*) l1
where
n = funcTableSize / 4
l0 = [i / n | i <- [0..n-1]]
l1 = l0 ++ reverse l0
(,,,,) <$> tableTexture sinTexture
<*> tableTexture squareTexture
<*> tableTexture sawToothTexture
<*> tableTexture inverseSawToothTexture
<*> tableTexture triangleTexture