module Graphics.RecordGL.Vertex (bufferVertices, bindVertices, reloadVertices
, deleteVertices, enableVertices, enableVertices'
, enableVertexFields, fieldToVAD
, ViableVertex, BufferedVertices(..)) where
import Graphics.RecordGL.Uniforms
import Record.Introspection
import Record.Types
import BasePrelude
import qualified Data.Map as M
import Data.Proxy
import qualified Data.Vector.Storable as V
import Foreign.Ptr (plusPtr)
import Foreign.Storable
import GHC.TypeLits
import Graphics.GLUtil hiding (Elem, throwError)
import Graphics.Rendering.OpenGL (BufferTarget (..),
VertexArrayDescriptor (..),
bindBuffer, ($=))
import qualified Graphics.Rendering.OpenGL as GL
data BufferedVertices a =
BufferedVertices {getVertexBuffer :: GL.BufferObject}
bufferVertices :: (Storable rs, BufferSource (v rs)) => v rs -> IO (BufferedVertices rs)
bufferVertices = fmap BufferedVertices . fromSource ArrayBuffer
reloadVertices :: Storable rs
=> BufferedVertices rs
-> V.Vector rs
-> IO ()
reloadVertices b v = do
bindBuffer ArrayBuffer $= Just (getVertexBuffer b)
replaceVector ArrayBuffer v
deleteVertices :: BufferedVertices a -> IO ()
deleteVertices = GL.deleteObjectNames . (: []) . getVertexBuffer
bindVertices :: BufferedVertices a -> IO ()
bindVertices = (bindBuffer ArrayBuffer $=) . Just . getVertexBuffer
type ViableVertex t = (HasFieldNames t, HasFieldSizes t, HasFieldDims t,
HasFieldGLTypes t, Storable t)
enableVertices :: forall f r. ViableVertex r
=> ShaderProgram -> f r -> IO (Maybe String)
enableVertices s _ = enableAttribs s (Proxy :: Proxy r)
enableVertices' :: forall f r. ViableVertex r
=> ShaderProgram -> f r -> IO ()
enableVertices' s _ = enableAttribs s (Proxy::Proxy r) >>=
maybe (return ()) error
data FieldDescriptor = FieldDescriptor { fieldName :: String
, fieldOffset :: Int
, fieldDim :: Int
, fieldType :: GL.VariableType }
deriving Show
fieldDescriptors :: ViableVertex t => t -> [FieldDescriptor]
fieldDescriptors x = getZipList $
FieldDescriptor <$> zl (fieldNames x)
<*> zl (scanl (+) 0 $ fieldSizes x)
<*> zl (fieldDims x)
<*> zl (fieldGLTypes x)
where zl = ZipList
enableVertexFields :: forall p r. ViableVertex r
=> ShaderProgram -> p r -> IO ()
enableVertexFields s _ = enableSomeAttribs s p >>= maybe (return ()) error
where
p = Proxy::Proxy r
enableSomeAttribs :: forall v. ViableVertex v
=> ShaderProgram -> Proxy v -> IO (Maybe String)
enableSomeAttribs s p = go $ fieldDescriptors (undefined::v)
where go [] = return Nothing
go (fd:fds) =
let n = fieldName fd
shaderAttribs = attribs s
in case M.lookup n shaderAttribs of
Nothing -> return (Just $ "Unexpected attribute " ++ n)
Just (_,t)
| fieldType fd == t -> do enableAttrib s n
setAttrib s n GL.ToFloat $
descriptorVAD p fd
go fds
| otherwise -> return . Just $ "Type mismatch in " ++ n
enableAttribs :: forall v. ViableVertex v
=> ShaderProgram -> Proxy v -> IO (Maybe String)
enableAttribs s p = go (map (second snd) $ M.assocs (attribs s))
where
go [] = return Nothing
go ((l, t):as) = case find ((== l) . fieldName) fs of
Nothing -> return (Just $ "GLSL expecting " ++ l)
Just fd
| fieldType fd == t -> do
enableAttrib s l
setAttrib s l GL.ToFloat $
descriptorVAD p fd
go as
| otherwise -> return . Just $ "Type mismatch in " ++ l
fs = fieldDescriptors (undefined::v)
descriptorVAD :: forall t a. Storable t
=> Proxy t -> FieldDescriptor -> VertexArrayDescriptor a
descriptorVAD _ fd = VertexArrayDescriptor (fromIntegral $ fieldDim fd)
(variableDataType $ fieldType fd)
(fromIntegral $
sizeOf (undefined::t))
(offset0 `plusPtr` fieldOffset fd)
namesAndOffsets :: (HasFieldNames t, HasFieldSizes t) => t -> [(String, Int)]
namesAndOffsets x = zip (fieldNames x) (scanl (+) 0 (fieldSizes x))
fieldToVAD :: forall sy r v a proxy.
(Field' sy r (v a), HasFieldNames r, HasFieldSizes r, HasGLType a, Storable r, Num (v a),
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707
KnownSymbol sy,
#else
SingI sy,
#endif
Foldable v)
=> FieldName sy -> r -> GL.VertexArrayDescriptor a
fieldToVAD _ _ = GL.VertexArrayDescriptor dim
(glType (undefined::a))
(fromIntegral sz)
(offset0 `plusPtr` offset)
where
sz = sizeOf (undefined::r)
dim = getSum $ foldMap (const (Sum 1)) (0::v a)
Just offset = lookup n $ namesAndOffsets (undefined::r)
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707
n = symbolVal (Proxy::Proxy sy)
#else
n = fromSing (sing::Sing sy)
#endif