lambdacube-engine-0.2.2: Graphics/LambdaCube/VertexBufferVector.hs
module Graphics.LambdaCube.VertexBufferVector where
import Control.Applicative
import Control.Monad
import Data.List (foldl')
import Data.Vector (Vector)
import Data.Word
import Foreign.Marshal.Array
import Foreign.Ptr
import Foreign.Storable
import Graphics.LambdaCube.HardwareBuffer
import Graphics.LambdaCube.HardwareIndexBuffer
import Graphics.LambdaCube.HardwareVertexBuffer
import Graphics.LambdaCube.Mesh
import Graphics.LambdaCube.RenderOperation
import Graphics.LambdaCube.RenderSystem
import Graphics.LambdaCube.Types hiding (Vector)
import Graphics.LambdaCube.VertexIndexData
import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as MV
-- TODO: fix diffuse and specular types
-- | The types of 'VectorVertexData' without the data.
data VectorVertexType
= VVT_POSITION
| VVT_BLEND_WEIGHTS
| VVT_BLEND_INDICES
| VVT_NORMAL
| VVT_DIFFUSE
| VVT_SPECULAR
| VVT_TEXTURE_COORDINATES1
| VVT_TEXTURE_COORDINATES2
| VVT_TEXTURE_COORDINATES3
| VVT_BINORMAL
| VVT_TANGENT
deriving (Eq,Ord,Show)
-- | Primitive buffer data
data VectorVertexData
= VVD_POSITION (Vector Vec3)
| VVD_BLEND_WEIGHTS (Vector FloatType)
| VVD_BLEND_INDICES (Vector Int)
| VVD_NORMAL (Vector Vec3)
| VVD_DIFFUSE (Vector Vec4)
| VVD_SPECULAR (Vector Vec3)
| VVD_TEXTURE_COORDINATES1 (Vector FloatType)
| VVD_TEXTURE_COORDINATES2 (Vector Vec2)
| VVD_TEXTURE_COORDINATES3 (Vector Vec3)
| VVD_BINORMAL (Vector Vec3)
| VVD_TANGENT (Vector Vec3)
deriving (Show)
type VVB = Vector VectorVertexData
type VIB = Vector Int
-- | Multimaterial geometry, including a default geometry for
-- submeshes that don't provide their own.
data VMesh
= VMesh
{ vmSubMeshList :: [VSubMesh] -- ^ The collection of single-material submeshes making up the mesh.
, vmSharedVertexData :: Maybe VVB -- ^ Default geometry for submeshes.
}
-- | Geometry with associated material.
data VSubMesh
= VSubMesh
{ vsmMaterialName :: String -- ^ The material associated with the submesh.
, vsmOperationType :: OperationType -- ^ The type of primitives making up the geometry.
, vsmVertexData :: Maybe VVB -- ^ Optional vertex buffer (supplied by the containing mesh if absent).
, vsmIndexData :: Maybe VIB -- ^ Optional index buffer.
}
-- | Extract the type of the vertex data.
vectorVertexType :: VectorVertexData -> VectorVertexType
vectorVertexType v = case v of
VVD_POSITION _ -> VVT_POSITION
VVD_BLEND_WEIGHTS _ -> VVT_BLEND_WEIGHTS
VVD_BLEND_INDICES _ -> VVT_BLEND_INDICES
VVD_NORMAL _ -> VVT_NORMAL
VVD_DIFFUSE _ -> VVT_DIFFUSE
VVD_SPECULAR _ -> VVT_SPECULAR
VVD_TEXTURE_COORDINATES1 _ -> VVT_TEXTURE_COORDINATES1
VVD_TEXTURE_COORDINATES2 _ -> VVT_TEXTURE_COORDINATES2
VVD_TEXTURE_COORDINATES3 _ -> VVT_TEXTURE_COORDINATES3
VVD_BINORMAL _ -> VVT_BINORMAL
VVD_TANGENT _ -> VVT_TANGENT
{-
TODO:
- implement interleaved to non-interleaved vertex buffer conversion function
- implement much faster VectorVertexData conversion via storable vector interface
-}
toVectorIndexData :: HardwareIndexBuffer ib => IndexData ib -> IO VIB
toVectorIndexData idata = do
let ib = idIndexBuffer idata
n = idIndexCount idata
isize = getIndexSize ib
buf <- lock ib (idIndexStart idata * isize) (n * isize) HBL_READ_ONLY
v <- MV.new n
case getIndexType ib of
IT_16BIT -> forM [0..n-1] $ \i -> do
a <- peek $ plusPtr buf $ i*isize :: IO Word16
MV.write v i $ fromIntegral a
IT_32BIT -> forM [0..n-1] $ \i -> do
a <- peek $ plusPtr buf $ i*isize :: IO Word32
MV.write v i $ fromIntegral a
unlock ib
V.freeze v
fromVectorIndexData :: (RenderSystem rs vb ib q t p lp) => rs -> VIB -> IO (IndexData ib)
fromVectorIndexData rs vib = do
let indexCount = V.length vib
usage = HBU_STATIC -- TODO
-- create IndexData
ib <- createIndexBuffer rs IT_32BIT indexCount usage True
-- 1. lock buffer
ptr <- lock ib 0 (getSizeInBytes ib) HBL_NORMAL
-- 2. fill buffer
pokeArray (castPtr ptr) $ map (fromIntegral :: Int -> Word32) $ V.toList vib
-- 3. unlock buffer
unlock ib
return $ IndexData
{ idIndexBuffer = ib
, idIndexStart = 0
, idIndexCount = indexCount
}
toVectorVertexData :: HardwareVertexBuffer vb => VertexData vb -> IO (Vector VectorVertexData)
toVectorVertexData vd = do
let n = vdVertexCount vd
vl = vdElementList $ vdVertexDeclaration vd
vbmap = vbbBindingMap $ vdVertexBufferBinding vd
g ve f = do
let vb = vbmap IntMap.! (veSource ve)
vsize = getVertexSize vb
buf <- lock vb 0 (getSizeInBytes vb) HBL_READ_ONLY
let p0 = plusPtr buf $ veOffset ve
v <- MV.new n
forM [0..n-1] $ \i -> do
a <- peek $ plusPtr p0 $ i*vsize
MV.write v i $ f a
unlock vb
V.freeze v
gv ve = g ve id
gs ve = g ve id
createVDVector ve = case (veSemantic ve, veType ve) of
(VES_POSITION,VET_FLOAT3) -> VVD_POSITION <$> gv ve
(VES_BLEND_WEIGHTS,VET_FLOAT1) -> VVD_BLEND_WEIGHTS <$> gs ve
(VES_BLEND_INDICES,VET_SHORT1) -> VVD_BLEND_INDICES <$> gs ve
(VES_NORMAL,VET_FLOAT3) -> VVD_NORMAL <$> gv ve
(VES_DIFFUSE,VET_FLOAT4) -> VVD_DIFFUSE <$> gv ve
(VES_SPECULAR,VET_FLOAT3) -> VVD_SPECULAR <$> gv ve
(VES_TEXTURE_COORDINATES,VET_FLOAT1) -> VVD_TEXTURE_COORDINATES1 <$> gs ve
(VES_TEXTURE_COORDINATES,VET_FLOAT2) -> VVD_TEXTURE_COORDINATES2 <$> gv ve
(VES_TEXTURE_COORDINATES,VET_FLOAT3) -> VVD_TEXTURE_COORDINATES3 <$> gv ve
(VES_BINORMAL,VET_FLOAT3) -> VVD_BINORMAL <$> gv ve
(VES_TANGENT,VET_FLOAT3) -> VVD_TANGENT <$> gv ve
_ -> error "Not supported vertex buffer format"
l <- mapM createVDVector vl
return $ V.fromList l
fromVectorVertexData :: (RenderSystem rs vb ib q t p lp) => rs -> Vector VectorVertexData -> IO (VertexData vb)
fromVectorVertexData rs vd = do
let -- aggregated type: offset, veIndex Map, [VertexElement], vertex count
(size,_,elems,vcount) = foldl' f (0,Map.empty,[],0) $ V.toList vd
f (o,m,ex,_) ve = (o + getTypeSize vt, Map.insert vs (i+1) m,(vf,VertexElement 0 o vt vs i):ex,vc)
where
i = (Map.findWithDefault 0 vs m)
(vs,vt,vf,vc) = cf ve
g a ptr vsize = V.foldM' (\p ed -> do poke (castPtr p) ed; return $ plusPtr p vsize) (plusPtr ptr o) a
g' a ptr vsize = V.foldM' (\p ed -> do poke (castPtr p) ed; return $ plusPtr p vsize) (plusPtr ptr o) a
cf t = case t of
VVD_POSITION a -> (VES_POSITION,VET_FLOAT3,g a,V.length a)
VVD_BLEND_WEIGHTS a -> (VES_BLEND_WEIGHTS,VET_FLOAT1,g' a,V.length a)
VVD_BLEND_INDICES a -> (VES_BLEND_INDICES,VET_SHORT1,g' a,V.length a)
VVD_NORMAL a -> (VES_NORMAL,VET_FLOAT3,g a,V.length a)
VVD_DIFFUSE a -> (VES_DIFFUSE,VET_FLOAT4,g a,V.length a)
VVD_SPECULAR a -> (VES_SPECULAR,VET_FLOAT3,g a,V.length a)
VVD_TEXTURE_COORDINATES1 a -> (VES_TEXTURE_COORDINATES,VET_FLOAT1,g' a,V.length a)
VVD_TEXTURE_COORDINATES2 a -> (VES_TEXTURE_COORDINATES,VET_FLOAT2,g a,V.length a)
VVD_TEXTURE_COORDINATES3 a -> (VES_TEXTURE_COORDINATES,VET_FLOAT3,g a,V.length a)
VVD_BINORMAL a -> (VES_BINORMAL,VET_FLOAT3,g a,V.length a)
VVD_TANGENT a -> (VES_TANGENT,VET_FLOAT3,g a,V.length a)
-- create VertexDeclaration
decl = VertexDeclaration $ map snd elems
-- create VertexBufferBinding
usage = HBU_STATIC -- TODO
--debugM "fromVectorVertexData" $ "gemetry declaration: " ++ show decl
bufs <- mapM (\s -> createVertexBuffer rs s vcount usage True) [size]
let binding = VertexBufferBinding $ IntMap.fromList $ zip [0..] bufs
fillBuffer (d,b) = do
-- 1. lock buffer
ptr <- lock b 0 (getSizeInBytes b) HBL_NORMAL
let vsize = getVertexSize b
fillAttribute (vf,_) = vf ptr vsize
-- 2. fill vertex attributes according declaration
mapM_ fillAttribute d
-- 3. unlock buffer
unlock b
-- end fillBuffer
-- fill buffers with data
mapM_ fillBuffer $ zip [elems] bufs
-- create VertexData
return $ VertexData decl binding 0 vcount
-- utility functions
meshFromV :: (RenderSystem rs vb ib q t p lp) => rs -> VMesh -> IO (Mesh vb ib)
meshFromV rs vmesh = do
let convMVVD mvvd = case mvvd of
Nothing -> return Nothing
Just vvd -> Just <$> fromVectorVertexData rs vvd
convMVID mvid = case mvid of
Nothing -> return Nothing
Just vid -> Just <$> fromVectorIndexData rs vid
sm <- forM (vmSubMeshList vmesh) $ \vsm -> do
vdata <- convMVVD $ vsmVertexData vsm
idata <- convMVID $ vsmIndexData vsm
return $ SubMesh
{ smOperationType = vsmOperationType vsm
, smVertexData = vdata
, smIndexData = idata
, smMaterialName = vsmMaterialName vsm
}
svd <- convMVVD $ vmSharedVertexData vmesh
let mesh = Mesh
{ msSubMeshList = sm
, msSharedVertexData = svd
, msBoundRadius = undefined
}
r <- calculateBoundingRadius mesh
return mesh { msBoundRadius = r }
vFromMesh :: (HardwareVertexBuffer vb, HardwareIndexBuffer ib) => Mesh vb ib -> IO VMesh
vFromMesh mesh = do
let convMVD mvd = case mvd of
Nothing -> return Nothing
Just vd -> Just <$> toVectorVertexData vd
convMID mid = case mid of
Nothing -> return Nothing
Just ixd -> Just <$> toVectorIndexData ixd
vsm <- forM (msSubMeshList mesh) $ \sm -> do
vdata <- convMVD $ smVertexData sm
idata <- convMID $ smIndexData sm
return $ VSubMesh (smMaterialName sm) (smOperationType sm) vdata idata
vsvd <- convMVD $ msSharedVertexData mesh
return $ VMesh vsm vsvd