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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
|
{-# LANGUAGE BangPatterns, FlexibleContexts, TypeSynonymInstances, FlexibleInstances #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
module LambdaCube.GL.Input where
import Control.Applicative
import Control.Exception
import Control.Monad
import Control.Monad.Reader
import Control.Monad.Writer
import Data.Maybe
import Data.IORef
import Data.Map (Map)
import Data.IntMap (IntMap)
import Data.Vector (Vector,(//),(!))
import Data.Word
import Data.String
import Data.Typeable
import Foreign
import qualified Data.Dependent.Map as DMap
import qualified Data.IntMap as IM
import qualified Data.Set as S
import qualified Data.Map as Map
import qualified Data.Vector as V
import qualified Data.Vector.Algorithms.Intro as I
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as SB
import Graphics.GL.Core33
import LambdaCube.GL.Input.Type
import LambdaCube.GL.Type as T
import LambdaCube.GL.Util
import LambdaCube.IR as IR
import LambdaCube.Linear as IR
import LambdaCube.PipelineSchema
import qualified LambdaCube.IR as IR
schemaFromPipeline :: IR.Pipeline -> PipelineSchema
schemaFromPipeline a = PipelineSchema (Map.fromList sl) (foldl Map.union Map.empty ul)
where
(sl,ul) = unzip [( (sName,ObjectArraySchema sPrimitive (fmap cvt sStreams))
, sUniforms
)
| IR.Slot sName sStreams sUniforms sPrimitive _ <- V.toList $ IR.slots a
]
cvt a = case toStreamType a of
Just v -> v
Nothing -> error "internal error (schemaFromPipeline)"
mkUniform :: [(String,InputType)] -> IO (Map String GLUniform)
mkUniform l = do
unis <- forM l $ \(n,t) -> do
uni <- initializeUniform t
return (n,uni)
return (Map.fromList unis)
allocStorage :: PipelineSchema -> IO GLStorage
allocStorage sch = do
let sm = Map.fromList $ zip (Map.keys $ objectArrays sch) [0..]
len = Map.size sm
unis <- mkUniform $ Map.toList $ uniforms sch
seed <- newIORef 0
slotV <- V.replicateM len $ newIORef (GLSlot IM.empty V.empty Ordered)
size <- newIORef (0,0)
ppls <- newIORef $ V.singleton Nothing
return $ GLStorage
{ schema = sch
, slotMap = sm
, slotVector = slotV
, objSeed = seed
, uniformSetup = unis
, screenSize = size
, pipelines = ppls
}
disposeStorage :: GLStorage -> IO ()
disposeStorage _ = putStrLn "not implemented: disposeStorage"
-- object
addObject :: GLStorage -> String -> Primitive -> Maybe (IndexStream Buffer) -> Map String (Stream Buffer) -> [String] -> IO Object
addObject input slotName prim indices attribs uniformNames = do
let sch = schema input
forM_ uniformNames $ \n -> case Map.lookup n (uniforms sch) of
Nothing -> fail $ "Unknown uniform: " ++ show n
_ -> return ()
case Map.lookup slotName (objectArrays sch) of
Nothing -> fail $ "Unknown slot: " ++ show slotName
Just (ObjectArraySchema sPrim sAttrs) -> do
when (sPrim /= (primitiveToFetchPrimitive prim)) $ fail $
"Primitive mismatch for slot (" ++ show slotName ++ ") expected " ++ show sPrim ++ " but got " ++ show prim
let sType = fmap streamToStreamType attribs
when (sType /= sAttrs) $ fail $ unlines $
[ "Attribute stream mismatch for slot (" ++ show slotName ++ ") expected "
, show sAttrs
, " but got "
, show sType
]
let slotIdx = case slotName `Map.lookup` slotMap input of
Nothing -> error $ "internal error (slot index): " ++ show slotName
Just i -> i
seed = objSeed input
order <- newIORef 0
enabled <- newIORef True
index <- readIORef seed
modifyIORef seed (1+)
unis <- mkUniform [(n,t) | n <- uniformNames,
let t = fromMaybe (error $ "missing uniform: " ++ n)
$ Map.lookup n (uniforms sch)]
cmdsRef <- newIORef (V.singleton V.empty)
let obj = Object
{ objSlot = slotIdx
, objPrimitive = prim
, objIndices = indices
, objAttributes = attribs
, objUniSetup = unis
, objOrder = order
, objEnabled = enabled
, objId = index
, objCommands = cmdsRef
}
modifyIORef' (slotVector input ! slotIdx) $ \(GLSlot objs _ _) -> GLSlot (IM.insert index obj objs) V.empty Generate
-- generate GLObjectCommands for the new object
{-
foreach pipeline:
foreach realted program:
generate commands
-}
ppls <- readIORef $ pipelines input
let topUnis = uniformSetup input
cmds <- V.forM ppls $ \mp -> case mp of
Nothing -> return V.empty
Just p -> do
Just ic <- readIORef $ glInput p
case icSlotMapInputToPipeline ic ! slotIdx of
Nothing -> do
--putStrLn $ " ** slot is not used!"
return V.empty -- this slot is not used in that pipeline
Just pSlotIdx -> do
--putStrLn "slot is used!"
--where
let emptyV = V.replicate (V.length $ glPrograms p) []
return $ emptyV // [(prgIdx,createObjectCommands (glTexUnitMapping p) topUnis obj (glPrograms p ! prgIdx))| prgIdx <- glSlotPrograms p ! pSlotIdx]
writeIORef cmdsRef cmds
return obj
removeObject :: GLStorage -> Object -> IO ()
removeObject p obj = modifyIORef (slotVector p ! objSlot obj) $ \(GLSlot !objs _ _) -> GLSlot (IM.delete (objId obj) objs) V.empty Generate
enableObject :: Object -> Bool -> IO ()
enableObject obj b = writeIORef (objEnabled obj) b
setObjectOrder :: GLStorage -> Object -> Int -> IO ()
setObjectOrder p obj i = do
writeIORef (objOrder obj) i
modifyIORef (slotVector p ! objSlot obj) $ \(GLSlot objs sorted _) -> GLSlot objs sorted Reorder
uniformSetter :: GLStorage -> Map String InputSetter
uniformSetter = uniformSetup
objectUniformSetter :: Object -> Map GLUniformName InputSetter
objectUniformSetter = objUniSetup
setScreenSize :: GLStorage -> Word -> Word -> IO ()
setScreenSize p w h = writeIORef (screenSize p) (w,h)
sortSlotObjects :: GLStorage -> IO ()
sortSlotObjects p = V.forM_ (slotVector p) $ \slotRef -> do
GLSlot objMap sortedV ord <- readIORef slotRef
let cmpFun (a,_) (b,_) = a `compare` b
doSort objs = do
ordObjsM <- V.thaw objs
I.sortBy cmpFun ordObjsM
ordObjs <- V.freeze ordObjsM
writeIORef slotRef (GLSlot objMap ordObjs Ordered)
case ord of
Ordered -> return ()
Generate -> do
objs <- V.forM (V.fromList $ IM.elems objMap) $ \obj -> do
ord <- readIORef $ objOrder obj
return (ord,obj)
doSort objs
Reorder -> do
objs <- V.forM sortedV $ \(_,obj) -> do
ord <- readIORef $ objOrder obj
return (ord,obj)
doSort objs
createObjectCommands :: Map String (IORef GLint) -> Map String GLUniform -> Object -> GLProgram -> [GLObjectCommand]
createObjectCommands texUnitMap topUnis obj prg = objUniCmds ++ objStreamCmds ++ [objDrawCmd]
where
-- object draw command
objDrawCmd = case objIndices obj of
Nothing -> GLDrawArrays prim 0 (fromIntegral count)
Just (IndexStream (Buffer arrs bo) arrIdx start idxCount) -> GLDrawElements prim (fromIntegral idxCount) idxType bo ptr
where
ArrayDesc arrType arrLen arrOffs arrSize = arrs ! arrIdx
idxType = arrayTypeToGLType arrType
ptr = intPtrToPtr $! fromIntegral (arrOffs + start * sizeOfArrayType arrType)
where
objAttrs = objAttributes obj
prim = primitiveToGLType $ objPrimitive obj
count = head [c | Stream _ _ _ _ c <- Map.elems objAttrs]
-- object uniform commands
-- texture slot setup commands
objUniCmds = uniCmds ++ texCmds
where
uniCmds = [GLSetUniform i u | (n,i) <- uniMap, let u = Map.findWithDefault (topUni n) n objUnis]
uniMap = Map.toList $ inputUniforms prg
topUni n = Map.findWithDefault (error $ "internal error (createObjectCommands): " ++ show n) n topUnis
objUnis = objUniSetup obj
texUnis = S.toList $ inputTextureUniforms prg
texCmds = [ GLBindTexture (inputTypeToTextureTarget $ uniInputType u) texUnit u
| n <- texUnis
, let u = Map.findWithDefault (topUni n) n objUnis
, let texUnit = Map.findWithDefault (error $ "internal error (createObjectCommands - Texture Unit): " ++ show n) n texUnitMap
]
uniInputType (GLTypedUniform ty _) = unwitnessType ty
uniInputType (GLUniform r) = objectType r
-- object attribute stream commands
objStreamCmds = [attrCmd i s | (i,name) <- Map.elems attrMap, let s = fromMaybe (error $ "missing attribute: " ++ name) $ Map.lookup name objAttrs]
where
attrMap = inputStreams prg
objAttrs = objAttributes obj
attrCmd i s = case s of
Stream ty (Buffer arrs bo) arrIdx start len -> case ty of
Attribute_Word -> setIntAttrib 1
Attribute_V2U -> setIntAttrib 2
Attribute_V3U -> setIntAttrib 3
Attribute_V4U -> setIntAttrib 4
Attribute_Int -> setIntAttrib 1
Attribute_V2I -> setIntAttrib 2
Attribute_V3I -> setIntAttrib 3
Attribute_V4I -> setIntAttrib 4
Attribute_Float -> setFloatAttrib 1
Attribute_V2F -> setFloatAttrib 2
Attribute_V3F -> setFloatAttrib 3
Attribute_V4F -> setFloatAttrib 4
Attribute_M22F -> setFloatAttrib 4
Attribute_M23F -> setFloatAttrib 6
Attribute_M24F -> setFloatAttrib 8
Attribute_M32F -> setFloatAttrib 6
Attribute_M33F -> setFloatAttrib 9
Attribute_M34F -> setFloatAttrib 12
Attribute_M42F -> setFloatAttrib 8
Attribute_M43F -> setFloatAttrib 12
Attribute_M44F -> setFloatAttrib 16
where
setFloatAttrib n = GLSetVertexAttribArray i bo n glType (ptr n)
setIntAttrib n = GLSetVertexAttribIArray i bo n glType (ptr n)
ArrayDesc arrType arrLen arrOffs arrSize = arrs ! arrIdx
glType = arrayTypeToGLType arrType
ptr compCnt = intPtrToPtr $! fromIntegral (arrOffs + start * fromIntegral compCnt * sizeOfArrayType arrType)
-- constant generic attribute
constAttr -> GLSetVertexAttrib i constAttr
newtype UniM a = UniM (ReaderT (Map GLUniformName GLUniform) (Writer [IO ()]) a)
deriving instance Functor UniM
deriving instance Applicative UniM
deriving instance Monad UniM
deriving instance MonadReader (Map String GLUniform) UniM
deriving instance MonadWriter [IO ()] UniM
(@=) :: (Typeable a, Uniformable a) => String -> IO a -> UniM ()
name @= val = do
u <- do
us <- ask
return $ us Map.! name
case u of
GLTypedUniform ty ref -> do
case DMap.lookup ty (uniformContexts val) of
Just UniformContext -> do
tell [val >>= writeIORef ref . GLUniformValue]
Nothing -> do
tell [throwIO $ typeMismatch ty ref]
GLUniform ref -> case withTypes val ref <$> eqT of
Just Refl -> tell [val >>= writeIORef ref]
Nothing -> tell [ Prelude.putStrLn $ "WARNING: "++show (objectType ref)++" variable "
++ show name
++ " cannot recieve value " ++ show (typeRep val)
, throwIO $ typeMismatch ref val
]
updateUniforms :: GLStorage -> UniM a -> IO ()
updateUniforms storage (UniM m) = sequence_ l where
setters = uniformSetup storage
l = execWriter $ runReaderT m setters
updateObjectUniforms :: Object -> UniM a -> IO ()
updateObjectUniforms object (UniM m) = sequence_ l where
setters = objectUniformSetter object
l = execWriter $ runReaderT m setters
-- | Set a uniform ref.
setGLUniform :: Typeable a =>
(forall v. Typeable v => TypeTag v -> Maybe (UniformContext a v))
-> String -- ^ For warning messages, name of uniform.
-> GLUniform -- ^ Uniform ref to set.
-> a -- ^ Value to store.
-> IO ()
setGLUniform resolv name u val = case u of
GLTypedUniform ty ref -> do
case resolv ty of
Just UniformContext -> writeIORef ref $ GLUniformValue val
Nothing -> warn $ unwords [ "Cannot set", show $ unwitnessType ty
, "uniform", name
, "to", show (typeOf val)
, "value." ]
GLUniform ref -> case withTypes (Just val) ref <$> eqT of
Just Refl -> writeIORef ref val
Nothing -> warn $ unwords [ "uniform", name
, "only accepts values of type"
, show $ typeRep ref ]
where warn s = putStrLn $ "WARNING: " ++ s
-- | Lookup and set a Uniform ref.
setUniformRef :: ( Typeable a
, Show name, Ord name
) => (forall v. Typeable v => TypeTag v -> Maybe (UniformContext a v))
-> name
-> Map name GLUniform
-> a
-> IO ()
setUniformRef resolv name us val = case Map.lookup name us of
Nothing -> warn $ "unknown uniform: " ++ show name
Just u -> setGLUniform resolv (show name) u val
where warn s = putStrLn $ "WARNING: " ++ s
uniformOf :: (Show name, Ord name, GLData a have) =>
TypeTag have
-> name
-> Map name GLUniform
-> a
-> IO ()
uniformOf have = setUniformRef $ knownContext have
uniform :: (Typeable a, Show name, Ord name, Uniformable a) =>
name -> Map name GLUniform -> a -> IO ()
uniform n o a = setUniformRef (resolveContext a) n o a
uniformFTexture2D :: SB.ByteString -> Map GLUniformName GLUniform -> TextureData -> IO ()
uniformFTexture2D =
-- TODO: Check that the uniform is of the expected FTexture2D type.
uniform . SB.unpack
uniformBool :: (Show name, Ord name) => name -> Map name GLUniform -> Bool -> IO ()
uniformV2B :: (Show name, Ord name, GLData a (GLVector 2 Word32)) => name -> Map name GLUniform -> a -> IO ()
uniformV3B :: (Show name, Ord name, GLData a (GLVector 3 Word32)) => name -> Map name GLUniform -> a -> IO ()
uniformV4B :: (Show name, Ord name, GLData a (GLVector 4 Word32)) => name -> Map name GLUniform -> a -> IO ()
uniformBool = uniformOf TypeBool
uniformV2B = uniformOf TypeV2B
uniformV3B = uniformOf TypeV3B
uniformV4B = uniformOf TypeV4B
uniformWord :: (Show name, Ord name) => name -> Map name GLUniform -> Word32 -> IO ()
uniformV2U :: (Typeable f, GLData (f Word32) (GLVector 2 Word32)) =>
String -> Map GLUniformName GLUniform -> f Word32 -> IO ()
uniformV3U :: (Typeable f, GLData (f Word32) (GLVector 3 Word32)) =>
String -> Map GLUniformName GLUniform -> f Word32 -> IO ()
uniformV4U :: (Typeable f, GLData (f Word32) (GLVector 4 Word32)) =>
String -> Map GLUniformName GLUniform -> f Word32 -> IO ()
uniformWord = uniformOf TypeWord
uniformV2U = uniformOf TypeV2U
uniformV3U = uniformOf TypeV3U
uniformV4U = uniformOf TypeV4U
uniformFloat :: (Show name, Ord name) => name -> Map name GLUniform -> Float -> IO ()
uniformV2F :: (Typeable f, GLData (f Float) (GLVector 2 Float)) =>
String -> Map GLUniformName GLUniform -> f Float -> IO ()
uniformV3F :: (Typeable f, GLData (f Float) (GLVector 3 Float)) =>
String -> Map GLUniformName GLUniform -> f Float -> IO ()
uniformV4F :: (Typeable f, GLData (f Float) (GLVector 4 Float)) =>
String -> Map GLUniformName GLUniform -> f Float -> IO ()
uniformFloat = uniformOf TypeFloat
uniformV2F = setUniformRef (knownContext TypeV2F)
uniformV3F = setUniformRef (knownContext TypeV3F)
uniformV4F = setUniformRef (knownContext TypeV4F)
uniformInt :: (Show name, Ord name) => name -> Map name GLUniform -> Int32 -> IO ()
uniformV2I :: (Typeable f, GLData (f Int32) (GLVector 2 Int32)) =>
String -> Map GLUniformName GLUniform -> f Int32 -> IO ()
uniformV3I :: (Typeable f, GLData (f Int32) (GLVector 3 Int32)) =>
String -> Map GLUniformName GLUniform -> f Int32 -> IO ()
uniformV4I :: (Typeable f, GLData (f Int32) (GLVector 4 Int32)) =>
String -> Map GLUniformName GLUniform -> f Int32 -> IO ()
uniformInt = uniformOf TypeInt
uniformV2I = uniformOf TypeV2I
uniformV3I = uniformOf TypeV3I
uniformV4I = uniformOf TypeV4I
{-
Note: This works to infer the type Float for literals without fixing the matrix type:
type family MatrixComponent m where
MatrixComponent (f (g c)) = c
MatrixComponent (f c) = c
uniformM44F :: ( MatrixComponent a ~ Float , GLData a (GLMatrix 4 4 Float))
=> String -> Map String GLUniform -> a -> IO ()
However, it breaks the ability to partially apply without a type signature.
Therefore, I'm forcing LambdaCube's internal matrix types for uniformM*
functions.
-}
uniformM22F :: (Show name, Ord name) => name -> Map name GLUniform -> M22F -> IO ()
uniformM23F :: (Show name, Ord name) => name -> Map name GLUniform -> M23F -> IO ()
uniformM24F :: (Show name, Ord name) => name -> Map name GLUniform -> M24F -> IO ()
uniformM32F :: (Show name, Ord name) => name -> Map name GLUniform -> M32F -> IO ()
uniformM33F :: (Show name, Ord name) => name -> Map name GLUniform -> M33F -> IO ()
uniformM34F :: (Show name, Ord name) => name -> Map name GLUniform -> M34F -> IO ()
uniformM42F :: (Show name, Ord name) => name -> Map name GLUniform -> M42F -> IO ()
uniformM43F :: (Show name, Ord name) => name -> Map name GLUniform -> M43F -> IO ()
uniformM44F :: (Show name, Ord name) => name -> Map name GLUniform -> M44F -> IO ()
uniformM22F = uniformOf TypeM22F
uniformM23F = uniformOf TypeM23F
uniformM24F = uniformOf TypeM24F
uniformM32F = uniformOf TypeM32F
uniformM33F = uniformOf TypeM33F
uniformM34F = uniformOf TypeM34F
uniformM42F = uniformOf TypeM42F
uniformM43F = uniformOf TypeM43F
uniformM44F = uniformOf TypeM44F
|