module Graphics.Rendering.Ombra.Geometry (
Geometry,
Triangle(..),
mkGeometry,
mapVertices,
decompose,
Attributes,
AttrVertex,
GeometryBuilder,
GeometryBuilderT,
vertex,
triangle,
buildGeometry,
buildGeometryT,
GeometryVertex(..)
) where
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.Backend (GLES)
import Graphics.Rendering.Ombra.Geometry.Types
import Graphics.Rendering.Ombra.Internal.TList (Remove, Append)
import Graphics.Rendering.Ombra.Shader.CPU
import Graphics.Rendering.Ombra.Vector
rehashGeometry :: Geometry g -> Geometry g
rehashGeometry g = let Triangles elemsHash _ = elements g
in g { geometryHash = H.hashWithSalt (topHash g) elemsHash }
emptyGeometry :: GeometryVertex g => Geometry g
emptyGeometry = rehashGeometry $ Geometry 0 0 emptyAttrCol (Triangles 0 []) (1)
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 :: GeometryVertex g
=> VertexAttributes (AttributeTypes g)
-> Geometry g
-> (AttrVertex (AttributeTypes g), Geometry g)
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 :: GeometryVertex g
=> Triangle (AttrVertex (AttributeTypes g))
-> Geometry g
-> Geometry g
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, GeometryVertex g)
=> Vertex g
-> GeometryBuilderT g m (AttrVertex (AttributeTypes g))
vertex = GeometryBuilderT . state . addVertex . toVertexAttributes
triangle :: (Monad m, GeometryVertex g)
=> AttrVertex (AttributeTypes g)
-> AttrVertex (AttributeTypes g)
-> AttrVertex (AttributeTypes g)
-> GeometryBuilderT g m ()
triangle x y z = GeometryBuilderT . state $ \g -> ((), addTriangle t g)
where t = Triangle x y z
buildGeometry :: GeometryVertex g => GeometryBuilder g () -> Geometry g
buildGeometry (GeometryBuilderT m) = execState m emptyGeometry
buildGeometryT :: (Monad m, GeometryVertex g)
=> GeometryBuilderT g m ()
-> m (Geometry g)
buildGeometryT (GeometryBuilderT m) = execStateT m emptyGeometry
mkGeometry :: (GLES, GeometryVertex g)
=> [Triangle (Vertex g)]
-> Geometry g
mkGeometry t = buildGeometry (foldlM add H.empty t >> return ())
where add verts (Triangle v1 v2 v3) =
do (verts1, av1) <- mvertex verts $ toVertexAttributes v1
(verts2, av2) <- mvertex verts1 $ toVertexAttributes v2
(verts3, av3) <- mvertex verts2 $ toVertexAttributes v3
triangle av1 av2 av3
return verts3
mvertex vertices v =
case H.lookup v vertices of
Just av -> return (vertices, av)
Nothing -> do av <- vertex $ fromVertexAttributes v
return (H.insert v av vertices, av)
attrVertexToVertex :: Attributes is => AttrVertex is -> VertexAttributes is
attrVertexToVertex (AttrVertex _ tab) = rowToVertexAttributes tab
decompose :: GeometryVertex g => Geometry g -> [Triangle (Vertex g)]
decompose g@(Geometry _ _ _ (Triangles _ triangles) _) =
flip map triangles $ fmap (fromVertexAttributes . attrVertexToVertex)
type AttrVertexMap is v = H.HashMap (AttrVertex is) v
mapVertices :: forall a g g'. (GLES, GeometryVertex g, GeometryVertex g')
=> (Triangle (Vertex g) -> a)
-> ([a] -> Vertex g -> Vertex g')
-> Geometry g
-> Geometry g'
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 ( fromVertexAttributes
. attrVertexToVertex
) tri
values' = foldr (flip (H.insertWith (++)) [value])
values
[v1, v2, v3]
tri' = fmap (vertMap H.!) tri
in (values', tri' : triangles)
accVertex :: H.HashMap (AttrVertex (AttributeTypes g)) [a]
-> AttrVertex (AttributeTypes g)
-> ( H.HashMap (AttrVertex (AttributeTypes g))
(AttrVertex (AttributeTypes g'))
, Geometry g'
)
-> ( H.HashMap (AttrVertex (AttributeTypes g))
(AttrVertex (AttributeTypes g'))
, Geometry g'
)
accVertex valueMap avert (vertMap, geom) =
let value = valueMap H.! avert
vert = fromVertexAttributes $ attrVertexToVertex avert
vert' = toVertexAttributes $ 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'