diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2022-12-03 17:37:59 -0700 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2022-12-03 17:37:59 -0700 |
commit | ba59711a51b4fee34009b1fe6afdce9ef8e60ae0 (patch) | |
tree | 7274bd2c9007abe08c8db7cea9e55babfd041125 /Graphics/Glyph/BufferBuilder.hs | |
parent | 601f77922490888c3ae9986674e332a5192008ec (diff) | |
download | terralloc-master.tar.gz terralloc-master.tar.bz2 terralloc-master.zip |
Diffstat (limited to 'Graphics/Glyph/BufferBuilder.hs')
-rw-r--r-- | Graphics/Glyph/BufferBuilder.hs | 466 |
1 files changed, 250 insertions, 216 deletions
diff --git a/Graphics/Glyph/BufferBuilder.hs b/Graphics/Glyph/BufferBuilder.hs index b23f6ba..8a41f9e 100644 --- a/Graphics/Glyph/BufferBuilder.hs +++ b/Graphics/Glyph/BufferBuilder.hs @@ -1,25 +1,25 @@ -{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE BangPatterns #-} -{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} + module Graphics.Glyph.BufferBuilder where -import Graphics.Rendering.OpenGL -import Foreign.Storable -import Foreign.Ptr -import Foreign.Marshal.Array import Data.Array.Storable import qualified Data.Foldable as Fold -import Data.Sequence as Seq import Data.Map as Map - -import Graphics.Glyph.Util +import Data.Sequence as Seq +import Foreign.Marshal.Array +import Foreign.Ptr +import Foreign.Storable import Graphics.Glyph.GLMath - +import Graphics.Glyph.Util +import Graphics.Rendering.OpenGL import System.IO.Unsafe import Unsafe.Coerce -data BufferBuilder3D = Plot BufferBuilder3D (GLfloat,GLfloat,GLfloat) Int Int | End +data BufferBuilder3D = Plot BufferBuilder3D (GLfloat, GLfloat, GLfloat) Int Int | End + bufferSize :: BufferBuilder3D -> Int bufferSize End = 0 bufferSize (Plot _ _ l _) = l @@ -31,78 +31,78 @@ nelem (Plot _ _ _ l) = l sizeofGLfloat :: Int sizeofGLfloat = 4 -simpleCube :: Num a => [(a,a,a)] -simpleCube = trianglesFromQuads [ - (-1, 1,-1) - , ( 1, 1,-1) - , ( 1,-1,-1) - , (-1,-1,-1) - - , (-1, 1, 1) - , ( 1, 1, 1) - , ( 1,-1, 1) - , (-1,-1, 1) - - , (-1, 1, 1) - , ( 1, 1, 1) - , ( 1, 1,-1) - , (-1, 1,-1) - - , (-1,-1, 1) - , ( 1,-1, 1) - , ( 1,-1,-1) - , (-1,-1,-1) - - , (-1,-1, 1) - , (-1, 1, 1) - , (-1, 1,-1) - , (-1,-1,-1) - - , ( 1,-1, 1) - , ( 1, 1, 1) - , ( 1, 1,-1) - , ( 1,-1,-1) +simpleCube :: Num a => [(a, a, a)] +simpleCube = + trianglesFromQuads + [ (-1, 1, -1), + (1, 1, -1), + (1, -1, -1), + (-1, -1, -1), + (-1, 1, 1), + (1, 1, 1), + (1, -1, 1), + (-1, -1, 1), + (-1, 1, 1), + (1, 1, 1), + (1, 1, -1), + (-1, 1, -1), + (-1, -1, 1), + (1, -1, 1), + (1, -1, -1), + (-1, -1, -1), + (-1, -1, 1), + (-1, 1, 1), + (-1, 1, -1), + (-1, -1, -1), + (1, -1, 1), + (1, 1, 1), + (1, 1, -1), + (1, -1, -1) ] class Monad a => IsModelBuilder b a where - plotVertex3 :: b -> b -> b -> a () - plotNormal :: b -> b -> b -> a () - plotTexture :: b -> b ->a () + plotVertex3 :: b -> b -> b -> a () + plotNormal :: b -> b -> b -> a () + plotTexture :: b -> b -> a () {- A state monad that keeps track of operations - and will compile them into a buffer -} data BuilderM b a = BuilderM (Builder (BuildDatum b)) a -data Builder b = Builder - !(Builder b) -- before - !(Builder b) -- after - | LeafBuilder !(Seq b) deriving Show + +data Builder b + = Builder + !(Builder b) -- before + !(Builder b) -- after + | LeafBuilder !(Seq b) + deriving (Show) instance IsModelBuilder GLfloat (BuilderM GLfloat) where - plotVertex3 x y z = bVertex3 (x,y,z) - plotNormal x y z = bNormal3 (x,y,z) - plotTexture x y = bTexture2 (x,y) - -data BuildDatum b = - VertexLink (b,b,b) | - NormalLink (b,b,b) | - ColorLink (b,b,b,b) | - TextureLink (b,b) deriving Show - -data CompiledBuild b = CompiledBuild { - bStride :: Int, - bEnabled :: (Bool,Bool,Bool), - nElems :: Int, + plotVertex3 x y z = bVertex3 (x, y, z) + plotNormal x y z = bNormal3 (x, y, z) + plotTexture x y = bTexture2 (x, y) + +data BuildDatum b + = VertexLink (b, b, b) + | NormalLink (b, b, b) + | ColorLink (b, b, b, b) + | TextureLink (b, b) + deriving (Show) + +data CompiledBuild b = CompiledBuild + { bStride :: Int, + bEnabled :: (Bool, Bool, Bool), + nElems :: Int, array :: Ptr b, arrayBytes :: Int -} + } bufferLength :: (Integral a) => CompiledBuild b -> a bufferLength = fromIntegral . nElems instance Show (CompiledBuild x) where - show (CompiledBuild stride enabled n ptr nbytes) = - "[CompiledBuild stride="++!stride++" enabled"++!enabled++" n="++!n++" ptr="++!ptr++" nbytes="++!nbytes++"]" + show (CompiledBuild stride enabled n ptr nbytes) = + "[CompiledBuild stride=" ++! stride ++ " enabled" ++! enabled ++ " n=" ++! n ++ " ptr=" ++! ptr ++ " nbytes=" ++! nbytes ++ "]" instance Functor (BuilderM t) where fmap f b = b >>= (return . f) @@ -115,206 +115,240 @@ instance Applicative (BuilderM t) where return (fn a) instance Monad (BuilderM t) where - (BuilderM !builder1 _) >> (BuilderM !builder2 ret) = - BuilderM (builder1 ><> builder2) ret - where - b1@(LeafBuilder !seq1) ><> b2@(LeafBuilder !seq2) - | Seq.length seq1 + Seq.length seq2 < 128 = LeafBuilder (seq1 >< seq2) - | otherwise = Builder b1 b2 - (Builder !b1 !b2) ><> leaf@(LeafBuilder !_) = - (Builder b1 (b2 ><> leaf)) - builder1' ><> builder2' = (Builder builder1' builder2') + (BuilderM !builder1 _) >> (BuilderM !builder2 ret) = + BuilderM (builder1 ><> builder2) ret + where + b1@(LeafBuilder !seq1) ><> b2@(LeafBuilder !seq2) + | Seq.length seq1 + Seq.length seq2 < 128 = LeafBuilder (seq1 >< seq2) + | otherwise = Builder b1 b2 + (Builder !b1 !b2) ><> leaf@(LeafBuilder !_) = + (Builder b1 (b2 ><> leaf)) + builder1' ><> builder2' = (Builder builder1' builder2') - b1@(BuilderM _ ret) >>= func = b1 >> func ret + b1@(BuilderM _ ret) >>= func = b1 >> func ret - return = BuilderM (LeafBuilder Seq.empty) + return = BuilderM (LeafBuilder Seq.empty) instance Functor Builder where - fmap f (Builder b1 b2) = (Builder (fmap f b1) (fmap f b2)) - fmap f (LeafBuilder seq') = (LeafBuilder (fmap f seq')) + fmap f (Builder b1 b2) = (Builder (fmap f b1) (fmap f b2)) + fmap f (LeafBuilder seq') = (LeafBuilder (fmap f seq')) instance Fold.Foldable Builder where - foldl f ini (Builder b1 b2) = - Fold.foldl f (Fold.foldl f ini b1) b2 - foldl f ini (LeafBuilder seq') = - Fold.foldl f ini seq' + foldl f ini (Builder b1 b2) = + Fold.foldl f (Fold.foldl f ini b1) b2 + foldl f ini (LeafBuilder seq') = + Fold.foldl f ini seq' - foldr f ini (Builder b1 b2) = - Fold.foldr f (Fold.foldr f ini b2) b1 - foldr f ini (LeafBuilder seq') = - Fold.foldr f ini seq' + foldr f ini (Builder b1 b2) = + Fold.foldr f (Fold.foldr f ini b2) b1 + foldr f ini (LeafBuilder seq') = + Fold.foldr f ini seq' -expandBuilder :: Builder a -> b -> (b -> a -> (b,[a])) -> Builder a +expandBuilder :: Builder a -> b -> (b -> a -> (b, [a])) -> Builder a expandBuilder builder ini f = snd $ expandBuilder' builder ini f - where expandBuilder' :: Builder a -> b -> (b -> a -> (b,[a])) -> (b,Builder a) - - expandBuilder' (Builder builder1 builder2) ini' f' = - let (snowball1,newBuilder1) = expandBuilder' builder1 ini' f' - (snowball2,newBuilder2) = expandBuilder' builder2 snowball1 f' in - (snowball2,Builder newBuilder1 newBuilder2) - - expandBuilder' (LeafBuilder seq1) initial func = - let (sequ,snow) = Fold.foldl' (\(seq', snowball) datum -> - let (snow',lst) = func snowball datum in - (seq' >< Seq.fromList lst,snow')) (Seq.empty,initial) seq1 in - (snow,LeafBuilder sequ) + where + expandBuilder' :: Builder a -> b -> (b -> a -> (b, [a])) -> (b, Builder a) + + expandBuilder' (Builder builder1 builder2) ini' f' = + let (snowball1, newBuilder1) = expandBuilder' builder1 ini' f' + (snowball2, newBuilder2) = expandBuilder' builder2 snowball1 f' + in (snowball2, Builder newBuilder1 newBuilder2) + expandBuilder' (LeafBuilder seq1) initial func = + let (sequ, snow) = + Fold.foldl' + ( \(seq', snowball) datum -> + let (snow', lst) = func snowball datum + in (seq' >< Seq.fromList lst, snow') + ) + (Seq.empty, initial) + seq1 + in (snow, LeafBuilder sequ) {- Add a vertex to the current builder -} -bVertex3 :: (a,a,a) -> BuilderM a () +bVertex3 :: (a, a, a) -> BuilderM a () bVertex3 vert = BuilderM (LeafBuilder (Seq.singleton $ VertexLink vert)) () -bTexture2 :: (a,a) -> BuilderM a () +bTexture2 :: (a, a) -> BuilderM a () bTexture2 tex = BuilderM (LeafBuilder (Seq.singleton $ TextureLink tex)) () -bNormal3 :: (a,a,a) -> BuilderM a () +bNormal3 :: (a, a, a) -> BuilderM a () bNormal3 norm = BuilderM (LeafBuilder (Seq.singleton $ NormalLink norm)) () -bColor4 :: (a,a,a,a) -> BuilderM a () +bColor4 :: (a, a, a, a) -> BuilderM a () bColor4 col = BuilderM (LeafBuilder (Seq.singleton $ ColorLink col)) () writeAndAvance :: (Storable a) => [a] -> Ptr a -> IO (Ptr a) -writeAndAvance (a:as) ptr = poke ptr a >> writeAndAvance as (advancePtr ptr 1) +writeAndAvance (a : as) ptr = poke ptr a >> writeAndAvance as (advancePtr ptr 1) writeAndAvance [] ptr = return ptr compilingBuilder :: (Storable b, Num b, Show b) => BuilderM b x -> IO (CompiledBuild b) compilingBuilder (BuilderM builder _) = do + putStrLn "COMPILING" + -- Size of the elements TODO unhardcode this + let sizeof = sizeOf $ builderElem builder + where + builderElem :: Builder (BuildDatum a) -> a + builderElem _ = unsafeCoerce (0 :: Int) + + {- Simply figure out what types of elementse + - exist in this buffer -} + let (bn, bc, bt, nVerts) = + Fold.foldl' + ( \(bn, bc, bt, len) ele -> + case ele of + NormalLink _ -> (True, bc, bt, len) + ColorLink _ -> (bn, True, bt, len) + TextureLink _ -> (bn, bc, True, len) + VertexLink _ -> (bn, bc, bt, len + 1) + ) + (False, False, False, 0) + builder + {- Calculate the stride; number of floats per element -} + let stride = (3 + (?) bn * 3 + (?) bc * 4 + (?) bt * 2) * sizeof + where + (?) True = 1 + (?) False = 0 + + let nbytes = stride * nVerts + putStrLn $ "Mallocing array of size: " ++! nbytes + array <- mallocArray nbytes + + -- Tuple + -- Pointer to current element, current normal/color/texture + putStrLn "Writing array buffer" + !_ <- + Fold.foldlM + ( \(ptr, cn, cc, ct) ele -> + -- trace ("foldl " ++! ele) $ + case ele of + NormalLink nn -> return (ptr, nn, cc, ct) + ColorLink nc -> return (ptr, cn, nc, ct) + TextureLink nt -> return (ptr, cn, cc, nt) + VertexLink vert -> do + ptr' <- + writeAndAvance (tp3 True vert) ptr + >>= writeAndAvance (tp3 bn cn) + >>= writeAndAvance (tp4 bc cc) + >>= writeAndAvance (tp2 bt ct) + return (ptr', cn, cc, ct) + ) + (array, (0, 0, 0), (0, 0, 0, 0), (0, 0)) + builder + putStrLn "Buffer written" + let !compiledRet = CompiledBuild stride (bn, bc, bt) nVerts array nbytes + putStrLn $ "COMPILE COMPLETE" ++! compiledRet + return compiledRet + where + tp2 True (a, b) = [a, b] + tp2 False _ = [] - putStrLn "COMPILING" - -- Size of the elements TODO unhardcode this - let sizeof = sizeOf $ builderElem builder - where builderElem :: Builder (BuildDatum a) -> a - builderElem _ = unsafeCoerce (0::Int) - - {- Simply figure out what types of elementse - - exist in this buffer -} - let (bn,bc,bt,nVerts) = Fold.foldl' (\(bn,bc,bt,len) ele -> - case ele of - NormalLink _ -> (True,bc,bt,len) - ColorLink _ -> (bn,True,bt,len) - TextureLink _ -> (bn,bc,True,len) - VertexLink _ -> (bn,bc,bt,len+1)) (False,False,False,0) builder - {- Calculate the stride; number of floats per element -} - let stride = (3 + (?)bn * 3 + (?)bc * 4 + (?)bt * 2) * sizeof - where (?) True = 1 - (?) False = 0 - - let nbytes = stride * nVerts - putStrLn $ "Mallocing array of size: " ++! nbytes - array <- mallocArray nbytes - - -- Tuple - -- Pointer to current element, current normal/color/texture - putStrLn "Writing array buffer" - !_ <- Fold.foldlM (\(ptr, cn, cc, ct) ele -> - -- trace ("foldl " ++! ele) $ - case ele of - NormalLink nn -> return (ptr,nn,cc,ct) - ColorLink nc -> return (ptr,cn,nc,ct) - TextureLink nt -> return (ptr,cn,cc,nt) - VertexLink vert -> do - ptr' <- writeAndAvance (tp3 True vert) ptr >>= - writeAndAvance (tp3 bn cn) >>= - writeAndAvance (tp4 bc cc) >>= - writeAndAvance (tp2 bt ct) - return (ptr',cn,cc,ct) ) ( array, (0,0,0), (0,0,0,0), (0,0) ) builder - putStrLn "Buffer written" - let !compiledRet = CompiledBuild stride (bn,bc,bt) nVerts array nbytes - putStrLn $ "COMPILE COMPLETE" ++! compiledRet - return compiledRet + tp3 True (a, b, c) = [a, b, c] + tp3 False _ = [] - where - tp2 True (a,b) = [a,b] - tp2 False _ = [] - - tp3 True (a,b,c) = [a,b,c] - tp3 False _ = [] - - tp4 True (a,b,c,d) = [a,b,c,d] - tp4 False _ = [] + tp4 True (a, b, c, d) = [a, b, c, d] + tp4 False _ = [] storableArrayToBuffer :: (Storable el) => BufferTarget -> StorableArray Int el -> IO BufferObject storableArrayToBuffer target arr = do - let sizeof = sizeOf $ unsafePerformIO (readArray arr 0) - [buffer] <- genObjectNames 1 - bindBuffer target $= Just buffer - len <- getBounds arr >>= (\(a,b) -> return $ (b - a) * sizeof ) - withStorableArray arr $ \ptr -> - bufferData target $= (fromIntegral len, ptr, StaticDraw) - return buffer + let sizeof = sizeOf $ unsafePerformIO (readArray arr 0) + [buffer] <- genObjectNames 1 + bindBuffer target $= Just buffer + len <- getBounds arr >>= (\(a, b) -> return $ (b - a) * sizeof) + withStorableArray arr $ \ptr -> + bufferData target $= (fromIntegral len, ptr, StaticDraw) + return buffer ptrToBuffer :: (Storable b) => BufferTarget -> Int -> Ptr b -> IO BufferObject ptrToBuffer target len ptr = do - -- len is length in bytes - [buffer] <- genObjectNames 1 - bindBuffer target $= Just buffer - bufferData target $= (fromIntegral len, ptr, StaticDraw) - return buffer + -- len is length in bytes + [buffer] <- genObjectNames 1 + bindBuffer target $= Just buffer + bufferData target $= (fromIntegral len, ptr, StaticDraw) + return buffer vertexArrayDescriptor :: CompiledBuild GLfloat -> VertexArrayDescriptor GLfloat vertexArrayDescriptor (CompiledBuild stride _ _ _ _) = VertexArrayDescriptor 3 Float (fromIntegral stride) (wordPtrToPtr 0) normalArrayDescriptor :: CompiledBuild GLfloat -> Maybe (VertexArrayDescriptor GLfloat) -normalArrayDescriptor (CompiledBuild stride (True,_,_) _ _ _) = - Just $ VertexArrayDescriptor 3 Float - (fromIntegral stride) (wordPtrToPtr (3*4)) -normalArrayDescriptor _ = Nothing +normalArrayDescriptor (CompiledBuild stride (True, _, _) _ _ _) = + Just $ + VertexArrayDescriptor + 3 + Float + (fromIntegral stride) + (wordPtrToPtr (3 * 4)) +normalArrayDescriptor _ = Nothing colorArrayDescriptor :: CompiledBuild GLfloat -> Maybe (VertexArrayDescriptor GLfloat) -colorArrayDescriptor (CompiledBuild stride tup@(_,True,_) _ _ _) = - Just $ VertexArrayDescriptor 4 Float - (fromIntegral stride) (wordPtrToPtr (offset tup)) - where offset (b1,_,_) = if b1 then (6*4) else (3*4) - -colorArrayDescriptor _ = Nothing +colorArrayDescriptor (CompiledBuild stride tup@(_, True, _) _ _ _) = + Just $ + VertexArrayDescriptor + 4 + Float + (fromIntegral stride) + (wordPtrToPtr (offset tup)) + where + offset (b1, _, _) = if b1 then (6 * 4) else (3 * 4) +colorArrayDescriptor _ = Nothing textureArrayDescriptor :: CompiledBuild GLfloat -> Maybe (VertexArrayDescriptor GLfloat) -textureArrayDescriptor (CompiledBuild stride tup@(_,_,True) _ _ _) = - Just $ VertexArrayDescriptor 2 Float - (fromIntegral stride) (wordPtrToPtr (offset tup)) - where offset (b1,b2,_) = (3 + (ifp b1 3) + (ifp b2 4)) * 4 - ifp b x = if b then x else 0 -textureArrayDescriptor _ = Nothing +textureArrayDescriptor (CompiledBuild stride tup@(_, _, True) _ _ _) = + Just $ + VertexArrayDescriptor + 2 + Float + (fromIntegral stride) + (wordPtrToPtr (offset tup)) + where + offset (b1, b2, _) = (3 + (ifp b1 3) + (ifp b2 4)) * 4 + ifp b x = if b then x else 0 +textureArrayDescriptor _ = Nothing + createBufferObject :: BufferTarget -> CompiledBuild GLfloat -> IO BufferObject createBufferObject target (CompiledBuild _ _ _ arr len) = ptrToBuffer target len arr mapListInsert :: (Ord k) => k -> a -> Map.Map k [a] -> Map.Map k [a] mapListInsert key val map = - flip (Map.insert key) map $ - case Map.lookup key map of - Nothing -> [val] - Just x -> (val:x) + flip (Map.insert key) map $ + case Map.lookup key map of + Nothing -> [val] + Just x -> (val : x) -inferingNormals :: (RealFloat a,Ord a,Show a) => BuilderM a b -> BuilderM a b +inferingNormals :: (RealFloat a, Ord a, Show a) => BuilderM a b -> BuilderM a b inferingNormals (BuilderM builder ret) = - let (normalMap,_,_) = Fold.foldl' (\(newMap, v1, v2) datum -> - case datum of - VertexLink w -> - case (v1,v2) of - (Just u, Just v) -> - let (Vec3 normal) = (Vec3 u <-> Vec3 v) × (Vec3 u <-> Vec3 w) in - (insertWith (zipWithT3 (+)) w normal newMap, Nothing, Nothing) - (Just u, Nothing) -> (newMap, v1, Just w) - (Nothing,Nothing) -> (newMap, Just w, Nothing) - _ -> (newMap,v1,v2) - ) (Map.empty,Nothing,Nothing) builder in - - let newBuilder = expandBuilder builder () $ \() datum -> - case datum of - VertexLink tup -> - let normalLink = NormalLink $ maybe (0,0,0) id $ Map.lookup tup normalMap in - ((),[normalLink, datum]) - _ -> ((),[datum]) in - - (BuilderM newBuilder ret) - + let (normalMap, _, _) = + Fold.foldl' + ( \(newMap, v1, v2) datum -> + case datum of + VertexLink w -> + case (v1, v2) of + (Just u, Just v) -> + let (Vec3 normal) = (Vec3 u <-> Vec3 v) × (Vec3 u <-> Vec3 w) + in (insertWith (zipWithT3 (+)) w normal newMap, Nothing, Nothing) + (Just u, Nothing) -> (newMap, v1, Just w) + (Nothing, Nothing) -> (newMap, Just w, Nothing) + _ -> (newMap, v1, v2) + ) + (Map.empty, Nothing, Nothing) + builder + in let newBuilder = expandBuilder builder () $ \() datum -> + case datum of + VertexLink tup -> + let normalLink = NormalLink $ maybe (0, 0, 0) id $ Map.lookup tup normalMap + in ((), [normalLink, datum]) + _ -> ((), [datum]) + in (BuilderM newBuilder ret) trianglesFromQuads :: [a] -> [a] -trianglesFromQuads (a:b:c:d:xs) = [a,b,c,a,c,d] ++ trianglesFromQuads xs +trianglesFromQuads (a : b : c : d : xs) = [a, b, c, a, c, d] ++ trianglesFromQuads xs trianglesFromQuads l = l -translating :: (Num a) => (a,a,a) -> BuilderM a b -> BuilderM a b +translating :: (Num a) => (a, a, a) -> BuilderM a b -> BuilderM a b translating trans (BuilderM builder ret) = do - BuilderM (flip fmap builder $ \datum -> - case datum of - VertexLink tup -> VertexLink $ zipWithT3 (+) tup trans - _ -> datum) ret + BuilderM + ( flip fmap builder $ \datum -> + case datum of + VertexLink tup -> VertexLink $ zipWithT3 (+) tup trans + _ -> datum + ) + ret |