module Graphics.Rendering.Ombra.Geometry.Internal (
MonadGeometry(..),
LoadedBuffer,
LoadedAttribute,
LoadedGeometry(..),
vertex,
triangle,
mkGeometry,
buildGeometry,
buildGeometryT,
decompose,
mapVertices,
removeAttribute,
drawGeometry
) where
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Except
import Control.Monad.Trans.State
import Data.Foldable (foldlM)
import qualified Data.Hashable as H
import qualified Data.HashMap.Lazy as H
import Data.List (foldl')
import Data.Proxy
import Data.Word (Word16)
import Graphics.Rendering.Ombra.Geometry.Types
import Graphics.Rendering.Ombra.Internal.GL
import Graphics.Rendering.Ombra.Internal.Resource
import Graphics.Rendering.Ombra.Internal.TList (Remove, Append)
import Graphics.Rendering.Ombra.Shader.CPU
import Graphics.Rendering.Ombra.Shader.Language.Types (ShaderType(size))
import Graphics.Rendering.Ombra.Vector
class (GLES, MonadGL m) => MonadGeometry m where
getAttribute :: Attribute 'S i
=> AttrCol (i ': is)
-> m (Either String LoadedAttribute)
getElementBuffer :: Elements is -> m (Either String LoadedBuffer)
getGeometry :: Geometry (i ': is) -> m (Either String LoadedGeometry)
data LoadedGeometry = LoadedGeometry {
elementCount :: Int,
vao :: VertexArrayObject
}
newtype LoadedBuffer = LoadedBuffer Buffer
data LoadedAttribute = LoadedAttribute GLUInt [(Buffer, GLUInt -> GL ())]
rehashGeometry :: Geometry is -> Geometry is
rehashGeometry g = let Triangles elemsHash _ = elements g
in g { geometryHash = H.hashWithSalt (topHash g) elemsHash }
emptyGeometry :: Attributes is => Geometry is
emptyGeometry = rehashGeometry $ Geometry 0 0 emptyAttrCol (Triangles 0 []) (1)
downList :: NotTop p => AttrTable p (i ': is) -> [CPU 'S i] -> [CPU 'S i]
downList AttrEnd xs = xs
downList (AttrCell x _ down) xs = downList down $ x : xs
foldVertices :: NotTop p
=> (AttrVertex is -> b -> b)
-> b
-> AttrTable p is
-> (Int, b)
foldVertices f acc AttrEnd = (1, acc)
foldVertices f acc cell@(AttrCell _ _ down) =
let (didx, acc') = foldVertices f acc down
idx = didx + 1
widx = fromIntegral idx
in (idx, f (AttrVertex widx cell) acc')
addVertex :: Attributes is
=> Vertex is
-> Geometry is
-> (AttrVertex is, Geometry is)
addVertex v g =
let top' = addTop v $ top g
topHash = H.hash top'
idx = lastIndex g + 1
av = case top' of
AttrTop _ _ c -> AttrVertex (fromIntegral idx) c
in ( av
, rehashGeometry $ g { topHash = topHash
, top = top'
, lastIndex = idx
}
)
addTriangle :: Attributes is
=> Triangle (AttrVertex is)
-> Geometry is
-> Geometry is
addTriangle t g = let Triangles h ts = elements g
elements' = Triangles (H.hashWithSalt (H.hash t) h)
(t : ts)
in rehashGeometry $ g { elements = elements' }
vertex :: (Monad m, Attributes is)
=> Vertex is
-> GeometryBuilderT is m (AttrVertex is)
vertex = GeometryBuilderT . state . addVertex
triangle :: (Monad m, Attributes is)
=> AttrVertex is
-> AttrVertex is
-> AttrVertex is
-> GeometryBuilderT is m ()
triangle x y z = GeometryBuilderT . state $ \g -> ((), addTriangle t g)
where t = Triangle x y z
buildGeometry :: Attributes (i ': is)
=> GeometryBuilder (i ': is) ()
-> Geometry (i ': is)
buildGeometry (GeometryBuilderT m) = execState m emptyGeometry
buildGeometryT :: (Monad m, Attributes (i ': is))
=> GeometryBuilderT (i ': is) m ()
-> m (Geometry (i ': is))
buildGeometryT (GeometryBuilderT m) = execStateT m emptyGeometry
mkGeometry :: (GLES, Attributes (i ': is))
=> [Triangle (Vertex (i ': is))]
-> Geometry (i ': is)
mkGeometry t = buildGeometry (foldlM add H.empty t >> return ())
where add vertices (Triangle v1 v2 v3) =
do (vertices1, av1) <- mvertex vertices v1
(vertices2, av2) <- mvertex vertices1 v2
(vertices3, av3) <- mvertex vertices2 v3
triangle av1 av2 av3
return vertices3
mvertex vertices v =
case H.lookup v vertices of
Just av -> return (vertices, av)
Nothing -> do av <- vertex v
return (H.insert v av vertices, av)
attrVertexToVertex :: Attributes is => AttrVertex is -> Vertex is
attrVertexToVertex (AttrVertex _ tab) = rowToVertex tab
decompose :: Geometry (i ': is) -> [Triangle (Vertex (i ': is))]
decompose g@(Geometry _ _ _ (Triangles _ triangles) _) =
flip map triangles $ fmap attrVertexToVertex
type AttrVertexMap is v = H.HashMap (AttrVertex is) v
mapVertices :: (Attributes is, Attributes is', GLES)
=> (Triangle (Vertex is) -> a)
-> ([a] -> Vertex is -> Vertex is')
-> Geometry is
-> Geometry is'
mapVertices getValue (transVert :: [a] -> Vertex is -> Vertex is')
(Geometry _ _ (AttrTop _ _ row0) (Triangles thash triangles) _) =
let accTriangle vertMap tri@(Triangle v1 v2 v3) (values, triangles) =
let value = getValue $ fmap attrVertexToVertex tri
values' = foldr (flip (H.insertWith (++)) [value])
values
[v1, v2, v3]
tri' = fmap (vertMap H.!) tri
in (values', tri' : triangles)
accVertex valueMap avert (vertMap, geom) =
let value = valueMap H.! avert
vert = attrVertexToVertex avert
vert' = transVert value vert
(avert', geom') = addVertex vert' geom
vertMap' = H.insert avert avert' vertMap
in (vertMap', geom')
(valueMap, triangles') = foldr (accTriangle vertMap)
(H.empty, [])
triangles
(_, (vertMap, Geometry tophash' _ top' _ lidx)) =
foldVertices (accVertex valueMap)
(H.empty, emptyGeometry)
row0
geom' = Geometry tophash' 0 top' (Triangles thash triangles') lidx
in rehashGeometry geom'
removeAttribute :: ( RemoveAttr i is
, Attributes is
, Attributes (Remove i is)
, GLES
)
=> (a -> i)
-> Geometry is
-> Geometry (Remove i is)
removeAttribute g = mapVertices (const ()) (const $ removeAttr g)
class RemoveAttr i is where
removeAttr :: (a -> i) -> Vertex is -> Vertex (Remove i is)
instance (Remove i '[i', i] ~ '[i']) =>
RemoveAttr i '[i', i] where
removeAttr g (Attr g' x :~ _) = Attr g' x
instance RemoveAttr i is' => RemoveAttr i (i ': is') where
removeAttr g (Attr _ _ :~ v) = removeAttr g v
instance ( RemoveAttr i is'
, Remove i (i' ': is') ~ (i' ': Remove i is')
) => RemoveAttr i (i' ': is') where
removeAttr g (Attr g' x) = Attr g' x
removeAttr g (Attr g' x :~ v) = Attr g' x :~ removeAttr g v
instance GLES => Resource (AttrCol (i ': is)) LoadedAttribute GL where
loadResource (AttrTop _ _ down :: AttrCol (i ': is)) =
fmap (Right . uncurry LoadedAttribute) .
flip execStateT (0, []) $
withAttributes (Proxy :: Proxy 'S) (undefined :: i) vs $
\_ (g :: Proxy g) c ->
do (i, as) <- get
arr <- lift $ encodeAttribute g c
buf <- lift $
loadBuffer gl_ARRAY_BUFFER
arr
let sz = fromIntegral . size $
(undefined :: g)
set = setAttribute g . (+ i)
put (i + sz, (buf, set) : as)
where vs = downList down []
unloadResource _ (LoadedAttribute _ as) =
mapM_ (\(buf, _) -> deleteBuffer buf) as
instance GLES => Resource (Elements is) LoadedBuffer GL where
loadResource (Triangles _ ts) =
liftIO (encodeUShorts elems) >>=
fmap (Right . LoadedBuffer) .
loadBuffer gl_ELEMENT_ARRAY_BUFFER
. fromUInt16Array
where elems = ts >>= ids
ids (Triangle (AttrVertex x _)
(AttrVertex y _)
(AttrVertex z _)) = [x, y, z]
unloadResource _ (LoadedBuffer buf) = deleteBuffer buf
instance (GLES, MonadGeometry m, EmbedIO m) =>
Resource (Geometry (i ': is)) LoadedGeometry m where
loadResource = loadGeometry
unloadResource _ = gl . deleteGeometry
loadGeometry :: (GLES, MonadGeometry m)
=> Geometry (i ': is)
-> m (Either String LoadedGeometry)
loadGeometry geometry@(Geometry _ _ _ _ _) = runExceptT $
do vao <- lift $ gl createVertexArray
lift . gl $ bindVertexArray vao
ExceptT . setAttrTop (0 :: GLUInt) $ top geometry
LoadedBuffer eb <- ExceptT . getElementBuffer $ elements geometry
lift . gl $ do bindBuffer gl_ELEMENT_ARRAY_BUFFER eb
bindVertexArray noVAO
bindBuffer gl_ELEMENT_ARRAY_BUFFER noBuffer
bindBuffer gl_ARRAY_BUFFER noBuffer
return $ LoadedGeometry (elementCount $ elements geometry) vao
where elementCount (Triangles _ ts) = 3 * length ts
setAttrTop :: (GLES, MonadGeometry m, Attributes (i ': is))
=> GLUInt
-> AttrCol (i ': is)
-> m (Either String ())
setAttrTop i0 col0 = runExceptT . (>> return ()) $
foldTop (\geti col@(AttrTop _ _ _) ->
do i <- geti
LoadedAttribute sz as <- ExceptT $ getAttribute col
lift . gl $
mapM_ (\(buf, set) ->
do bindBuffer gl_ARRAY_BUFFER buf
enableVertexAttribArray i
set i
) as
return $ i + sz
) (return i0) col0
deleteGeometry :: GLES => LoadedGeometry -> GL ()
deleteGeometry (LoadedGeometry _ vao) = deleteVertexArray vao
loadBuffer :: GLES => GLEnum -> AnyArray -> GL Buffer
loadBuffer ty bufData =
do buffer <- createBuffer
bindBuffer ty buffer
bufferData ty bufData gl_STATIC_DRAW
bindBuffer ty noBuffer
return buffer
drawGeometry :: MonadGeometry m => Geometry (i ': is) -> m ()
drawGeometry g = getGeometry g >>= \eg ->
case eg of
Left _ -> return ()
Right (LoadedGeometry ec vao) ->
gl $ do bindVertexArray vao
drawElements gl_TRIANGLES
(fromIntegral ec)
gl_UNSIGNED_SHORT
nullGLPtr
bindVertexArray noVAO