{-# LANGUAGE DeriveAnyClass #-}
module Resource.Gltf.Model
( Mesh
, MeshPrimitive
, Stuff(..)
, mergeStuff
, unzipStuff
, StuffLike
, mergeStuffLike
, VertexAttrs(..)
) where
import RIO
import Codec.GlTF.Material qualified as GlTF (Material)
import Data.Semigroup (Semigroup(..))
import Geomancy (Vec2)
import Geomancy.Vec3 qualified as Vec3
import Graphics.Gl.Block (Block)
import Graphics.Gl.Block qualified as Block
import RIO.List qualified as List
import RIO.Vector qualified as Vector
type Mesh = Vector MeshPrimitive
type MeshPrimitive = (Maybe (Int, GlTF.Material), Stuff)
data Stuff = Stuff
{ sPositions :: Vector Vec3.Packed
, sIndices :: Vector Word32
, sAttrs :: Vector VertexAttrs
}
deriving (Eq, Show, Generic)
instance Semigroup Stuff where
{-# INLINE (<>) #-}
a <> b = mergeStuff [a, b]
{-# INLINE sconcat #-}
sconcat = mergeStuff
instance Monoid Stuff where
mempty = Stuff
{ sPositions = mempty
, sIndices = mempty
, sAttrs = mempty
}
{-# INLINE mconcat #-}
mconcat = mergeStuff
mergeStuff :: Foldable t => t Stuff -> Stuff
mergeStuff source = Stuff
{ sPositions = Vector.concat allPositions
, sIndices = Vector.concat offsetIndices
, sAttrs = Vector.concat allAttrs
}
where
(allPositions, allAttrs, numPositions, allIndices) = unzipStuff source
offsetIndices = List.zipWith applyOffset chunkOffsets allIndices
where
applyOffset off = fmap (+ off)
chunkOffsets = List.scanl' (+) 0 numPositions
unzipStuff
:: Foldable t
=> t Stuff
-> ( [Vector Vec3.Packed]
, [Vector VertexAttrs]
, [Word32]
, [Vector Word32]
)
unzipStuff source = List.unzip4 do
Stuff{..} <- toList source
pure
( sPositions
, sAttrs
, fromIntegral $ Vector.length sPositions {- sic! -}
, sIndices
)
type StuffLike attrs = (Vector Vec3.Packed, Vector Word32, Vector attrs)
mergeStuffLike :: Foldable t => t (StuffLike attrs) -> (StuffLike attrs)
mergeStuffLike source =
( Vector.concat allPositions
, Vector.concat offsetIndices
, Vector.concat allAttrs
)
where
(allPositions, allIndices, allAttrs) = List.unzip3 (toList source)
offsetIndices = List.zipWith applyOffset chunkOffsets allIndices
where
applyOffset off = fmap (+ fromIntegral off)
chunkOffsets = List.scanl' (+) 0 $ map Vector.length allPositions
data VertexAttrs = VertexAttrs
{ vaTexCoord :: Vec2
, vaNormal :: Vec3.Packed
, vaTangent :: Vec3.Packed
}
deriving (Eq, Ord, Show, Generic, Block)
deriving Storable via (Block.Packed VertexAttrs)