packages feed

dx9d3d-0.1: DirectX9/D3D/Utility/FVF.hs

module DirectX9.D3D.Utility.FVF
where
import Foreign.Marshal.Array ( pokeArray )
import Foreign.Ptr  ( Ptr, plusPtr )
import Foreign.Storable ( pokeByteOff )

import DirectX9.Types
import DirectX9.D3D.Raw
import DirectX9.D3D.Format
import DirectX9.D3D.Constant
import DirectX9.D3D.Device
import DirectX9.D3D.Resource


class FVF a where
    sizeOfOne :: a -> Int
    sizeOfAll :: a -> Int
    fvfFlags :: a -> D3DFVF
    writeFVF :: a -> Ptr b -> IO (Ptr b)

-- todo: optimise indexes that can fit into 16-bits
fvfCreateIndexBuffer :: (Num a, Storable a) => Device -> DWORD -> D3DPOOL -> [a] -> IO IndexBuffer
fvfCreateIndexBuffer dev usage pool arr = do
    ib <- devCreateIndexBuffer dev bytes usage format pool
    withLockedIndexBuffer ib 0 bytes 0 $ flip pokeArray arr
    return ib
    where
        (format,bytes) = case (sizeOf (arr!!0)) of
            4 -> (d3DFMT_INDEX32, 4*(length arr))
            2 -> (d3DFMT_INDEX16, 2*(length arr))
            _ -> (0,0)


fvfToVertexElems :: D3DFVF -> [D3DVERTEXELEMENT9]
fvfToVertexElems fvf = reverse $ foldl offset [] $ concat $ snd $ foldl fun (fvf,[]) fields
    where
        fun :: (D3DFVF, [[a]]) -> (D3DFVF,[a]) -> (D3DFVF,[[a]])
        fun (fvf, old) (f, d)
            | f<=fvf            = ((fvf-f),d:old)
            | otherwise         = (fvf,old)
        nextOffset [] = 0
        nextOffset (x:_) =
            d3DVERTEXELEMENT9_Offset x
            + case lookup (d3DVERTEXELEMENT9_Type x) sizes of
                Just x -> x
                Nothing -> 999
        sizes = 
            [(d3DDECLTYPE_FLOAT2    ,8)
            ,(d3DDECLTYPE_FLOAT3    ,12)
            ,(d3DDECLTYPE_FLOAT4    ,16)
            ,(d3DDECLTYPE_D3DCOLOR   ,4)
            ]
        offset old (t,u) = (D3DVERTEXELEMENT9 0 (nextOffset old) t d3DDECLMETHOD_DEFAULT u 0 ):old
        fields =
            [(d3DFVF_TEX8,      replicate 8 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX7,      replicate 7 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX6,      replicate 6 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX5,      replicate 5 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX4,      replicate 4 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX3,      replicate 3 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX2,      replicate 2 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_TEX1,      replicate 1 (d3DDECLTYPE_FLOAT2,d3DDECLUSAGE_TEXCOORD))
            ,(d3DFVF_SPECULAR,  [(d3DDECLTYPE_D3DCOLOR,d3DDECLUSAGE_COLOR)])
            ,(d3DFVF_DIFFUSE,   [(d3DDECLTYPE_D3DCOLOR,d3DDECLUSAGE_COLOR)])
            ,(d3DFVF_NORMAL,    [(d3DDECLTYPE_FLOAT3,d3DDECLUSAGE_NORMAL)])
            ,(d3DFVF_XYZRHW,    [(d3DDECLTYPE_FLOAT4,d3DDECLUSAGE_POSITION)])
            ,(d3DFVF_XYZ,       [(d3DDECLTYPE_FLOAT3,d3DDECLUSAGE_POSITION)])
            ]


fvfCreateVertexBuffer :: (FVF a) => Device -> DWORD -> D3DPOOL -> a -> IO VertexBuffer
fvfCreateVertexBuffer dev usage pool o = do
    vb <- devCreateVertexBuffer dev bytes usage (fvfFlags o) pool
    withLockedVertexBuffer vb 0 bytes 0 (writeFVF o)
    return vb
    where bytes = sizeOfAll o

fvfCreateVertexDeclaration :: (FVF a) => Device -> Int -> a -> IO VertexDeclaration
fvfCreateVertexDeclaration dev stream fvf = devCreateVertexDeclaration dev decl
    where
        setStream x = x { d3DVERTEXELEMENT9_Stream = fromIntegral stream}
        decl = map setStream $ fvfToVertexElems $ fvfFlags fvf

    
type FVFVector4 = (Float,Float,Float,Float)
type FVFVector3 = (Float,Float,Float)
type FVFVector2 = (Float,Float)

writeFVFV4 :: FVFVector4 -> Ptr a -> IO (Ptr a)
writeFVFV4 (v1,v2,v3,v4) b = do
    pokeByteOff b 0 v1
    pokeByteOff b 4 v2
    pokeByteOff b 8 v3
    pokeByteOff b 12 v4
    return $ plusPtr b 16

writeFVFV3 :: FVFVector3 -> Ptr a -> IO (Ptr a)
writeFVFV3 (v1,v2,v3) b = do
    pokeByteOff b 0 v1
    pokeByteOff b 4 v2
    pokeByteOff b 8 v3
    return $ plusPtr b 12

writeFVFV2 :: FVFVector2 -> Ptr a -> IO (Ptr a)
writeFVFV2 (v1,v2) b = do
    pokeByteOff b 0 v1
    pokeByteOff b 4 v2
    return $ plusPtr b 8

writeFVFD :: D3DCOLOR -> Ptr a -> IO (Ptr a)
writeFVFD v b = do
    pokeByteOff b 0 v
    return $ plusPtr b 4

writeFVF' :: (a -> Ptr b -> IO (Ptr b)) -> [a] -> Ptr b -> IO (Ptr b)
writeFVF' f a s = foldl (\x y -> x >>= f y) (return s) a

newtype PosF = PosF [FVFVector3]
newtype PosNormF = PosNormF [(FVFVector3,FVFVector3)]
newtype PosDiffF = PosDiffF [(FVFVector3, D3DCOLOR)]
newtype PosNormDiffF = PosNormDiffF [(FVFVector3, FVFVector3, D3DCOLOR)]
newtype PosTexF = PosTexF [(FVFVector3, FVFVector2)]
newtype PosNormTexF = PosNormTexF [(FVFVector3, FVFVector3, FVFVector2)]

instance FVF PosF where
    sizeOfOne _ = 4*3
    sizeOfAll (PosF a) = (4*3) * length a
    fvfFlags _ = d3DFVF_XYZ
    writeFVF (PosF v) = writeFVF' w v
        where w v b = writeFVFV3 v b

instance FVF PosNormF where
    sizeOfOne _ = 4*3+4*3
    sizeOfAll (PosNormF v) = (4*3+4*3) * length v
    fvfFlags _ = d3DFVF_XYZ + d3DFVF_NORMAL
    writeFVF (PosNormF v) = writeFVF' w v
        where w (v,n) b = return b >>= writeFVFV3 v >>= writeFVFV3 n

instance FVF PosDiffF where
    sizeOfOne _ = 4*3+4
    sizeOfAll (PosDiffF v) = (4*3+3) * length v
    fvfFlags _ = d3DFVF_XYZ + d3DFVF_DIFFUSE
    writeFVF (PosDiffF v) = writeFVF' w v
        where w (v,d) b = return b >>= writeFVFV3 v >>= writeFVFD d

instance FVF PosNormDiffF where
    sizeOfOne _ = 4*3+4*3+4
    sizeOfAll (PosNormDiffF v) = (4*3+4*3+4) * length v
    fvfFlags _ = d3DFVF_XYZ+d3DFVF_NORMAL+d3DFVF_DIFFUSE
    writeFVF (PosNormDiffF v) = writeFVF' w v
        where w (v,n,d) b = return b >>= writeFVFV3 v >>= writeFVFV3 n >>= writeFVFD d

instance FVF PosTexF where
    sizeOfOne _ = 4*3+4*2
    sizeOfAll (PosTexF v) = (4*3+4*2) * length v
    fvfFlags _ = d3DFVF_XYZ+d3DFVF_TEX1
    writeFVF (PosTexF v) = writeFVF' w v
        where w (v,t) b = return b >>= writeFVFV3 v >>= writeFVFV2 t

instance FVF PosNormTexF where
    sizeOfOne _ = 4*3+4*3+4*2
    sizeOfAll (PosNormTexF v) = (4*3+4*3+4*2) * length v
    fvfFlags _ = d3DFVF_XYZ+d3DFVF_NORMAL+d3DFVF_TEX1
    writeFVF (PosNormTexF v) = writeFVF' w v
        where w (v,n,t) b = return b >>= writeFVFV3 v >>= writeFVFV3 n >>= writeFVFV2 t