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 Vec4) | 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_COLOUR_ABGR) -> VVD_DIFFUSE <$> gv ve (VES_SPECULAR,VET_COLOUR_ABGR) -> 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_COLOUR_ABGR,g a,V.length a) VVD_SPECULAR a -> (VES_SPECULAR,VET_COLOUR_ABGR,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