diff options
Diffstat (limited to 'Backend/GL/Mesh.hs')
-rw-r--r-- | Backend/GL/Mesh.hs | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/Backend/GL/Mesh.hs b/Backend/GL/Mesh.hs new file mode 100644 index 0000000..261e6cc --- /dev/null +++ b/Backend/GL/Mesh.hs | |||
@@ -0,0 +1,232 @@ | |||
1 | {-# LANGUAGE TupleSections #-} | ||
2 | module Backend.GL.Mesh ( | ||
3 | loadMesh, | ||
4 | saveMesh, | ||
5 | addMesh, | ||
6 | compileMesh, | ||
7 | updateMesh, | ||
8 | Mesh(..), | ||
9 | MeshPrimitive(..), | ||
10 | MeshAttribute(..) | ||
11 | ) where | ||
12 | |||
13 | import Control.Applicative | ||
14 | import Control.Monad | ||
15 | import Data.Binary | ||
16 | import Data.ByteString.Char8 (ByteString) | ||
17 | import Foreign.Ptr | ||
18 | import Data.Int | ||
19 | import Foreign.Storable | ||
20 | import Foreign.Marshal.Utils | ||
21 | import System.IO.Unsafe | ||
22 | import qualified Data.ByteString.Char8 as SB | ||
23 | import qualified Data.ByteString.Lazy as LB | ||
24 | import qualified Data.Trie as T | ||
25 | import qualified Data.Vector.Storable as V | ||
26 | import qualified Data.Vector.Storable.Mutable as MV | ||
27 | |||
28 | import Backend.GL | ||
29 | import Backend.GL.Type as T | ||
30 | import IR as IR | ||
31 | |||
32 | fileVersion :: Int32 | ||
33 | fileVersion = 1 | ||
34 | |||
35 | data MeshAttribute | ||
36 | = A_Float (V.Vector Float) | ||
37 | | A_V2F (V.Vector V2F) | ||
38 | | A_V3F (V.Vector V3F) | ||
39 | | A_V4F (V.Vector V4F) | ||
40 | | A_M22F (V.Vector M22F) | ||
41 | | A_M33F (V.Vector M33F) | ||
42 | | A_M44F (V.Vector M44F) | ||
43 | | A_Int (V.Vector Int32) | ||
44 | | A_Word (V.Vector Word32) | ||
45 | |||
46 | data MeshPrimitive | ||
47 | = P_Points | ||
48 | | P_TriangleStrip | ||
49 | | P_Triangles | ||
50 | | P_TriangleStripI (V.Vector Int32) | ||
51 | | P_TrianglesI (V.Vector Int32) | ||
52 | |||
53 | data Mesh | ||
54 | = Mesh | ||
55 | { mAttributes :: T.Trie MeshAttribute | ||
56 | , mPrimitive :: MeshPrimitive | ||
57 | , mGPUData :: Maybe GPUData | ||
58 | } | ||
59 | |||
60 | data GPUData | ||
61 | = GPUData | ||
62 | { dPrimitive :: Primitive | ||
63 | , dStreams :: T.Trie (Stream Buffer) | ||
64 | , dIndices :: Maybe (IndexStream Buffer) | ||
65 | } | ||
66 | |||
67 | loadMesh :: String -> IO Mesh | ||
68 | loadMesh n = compileMesh =<< decode <$> LB.readFile n | ||
69 | |||
70 | saveMesh :: String -> Mesh -> IO () | ||
71 | saveMesh n m = LB.writeFile n (encode m) | ||
72 | |||
73 | addMesh :: GLPipelineInput -> ByteString -> Mesh -> [ByteString] -> IO Object | ||
74 | addMesh input slotName (Mesh _ _ (Just (GPUData prim streams indices))) objUniNames = do | ||
75 | -- select proper attributes | ||
76 | let Just (SlotSchema slotPrim slotStreams) = T.lookup slotName $! T.slots $! T.schema input | ||
77 | filterStream n s | ||
78 | | T.member n slotStreams = Just s | ||
79 | | otherwise = Nothing | ||
80 | addObject input slotName prim indices (T.mapBy filterStream streams) objUniNames | ||
81 | addMesh _ _ _ _ = fail "addMesh: only compiled mesh with GPUData is supported" | ||
82 | |||
83 | withV w a f = w a (\p -> f $ castPtr p) | ||
84 | |||
85 | meshAttrToArray :: MeshAttribute -> Array | ||
86 | meshAttrToArray (A_Float v) = Array ArrFloat (1 * V.length v) $ withV V.unsafeWith v | ||
87 | meshAttrToArray (A_V2F v) = Array ArrFloat (2 * V.length v) $ withV V.unsafeWith v | ||
88 | meshAttrToArray (A_V3F v) = Array ArrFloat (3 * V.length v) $ withV V.unsafeWith v | ||
89 | meshAttrToArray (A_V4F v) = Array ArrFloat (4 * V.length v) $ withV V.unsafeWith v | ||
90 | meshAttrToArray (A_M22F v) = Array ArrFloat (4 * V.length v) $ withV V.unsafeWith v | ||
91 | meshAttrToArray (A_M33F v) = Array ArrFloat (9 * V.length v) $ withV V.unsafeWith v | ||
92 | meshAttrToArray (A_M44F v) = Array ArrFloat (16 * V.length v) $ withV V.unsafeWith v | ||
93 | meshAttrToArray (A_Int v) = Array ArrInt32 (1 * V.length v) $ withV V.unsafeWith v | ||
94 | meshAttrToArray (A_Word v) = Array ArrWord32 (1 * V.length v) $ withV V.unsafeWith v | ||
95 | |||
96 | meshAttrToStream :: Buffer -> Int -> MeshAttribute -> Stream Buffer | ||
97 | meshAttrToStream b i (A_Float v) = Stream TFloat b i 0 (V.length v) | ||
98 | meshAttrToStream b i (A_V2F v) = Stream TV2F b i 0 (V.length v) | ||
99 | meshAttrToStream b i (A_V3F v) = Stream TV3F b i 0 (V.length v) | ||
100 | meshAttrToStream b i (A_V4F v) = Stream TV4F b i 0 (V.length v) | ||
101 | meshAttrToStream b i (A_M22F v) = Stream TM22F b i 0 (V.length v) | ||
102 | meshAttrToStream b i (A_M33F v) = Stream TM33F b i 0 (V.length v) | ||
103 | meshAttrToStream b i (A_M44F v) = Stream TM44F b i 0 (V.length v) | ||
104 | meshAttrToStream b i (A_Int v) = Stream TInt b i 0 (V.length v) | ||
105 | meshAttrToStream b i (A_Word v) = Stream TWord b i 0 (V.length v) | ||
106 | |||
107 | {- | ||
108 | updateBuffer :: Buffer -> [(Int,Array)] -> IO () | ||
109 | |||
110 | | Stream | ||
111 | { streamType :: StreamType | ||
112 | , streamBuffer :: b | ||
113 | , streamArrIdx :: Int | ||
114 | , streamStart :: Int | ||
115 | , streamLength :: Int | ||
116 | } | ||
117 | |||
118 | -- stream of index values (for index buffer) | ||
119 | data IndexStream b | ||
120 | = IndexStream | ||
121 | { indexBuffer :: b | ||
122 | , indexArrIdx :: Int | ||
123 | , indexStart :: Int | ||
124 | , indexLength :: Int | ||
125 | } | ||
126 | -} | ||
127 | updateMesh :: Mesh -> [(ByteString,MeshAttribute)] -> Maybe MeshPrimitive -> IO () | ||
128 | updateMesh (Mesh dMA dMP (Just (GPUData _ dS dI))) al mp = do | ||
129 | -- check type match | ||
130 | let arrayChk (Array t1 s1 _) (Array t2 s2 _) = t1 == t2 && s1 == s2 | ||
131 | ok = and [T.member n dMA && arrayChk (meshAttrToArray a1) (meshAttrToArray a2) | (n,a1) <- al, let Just a2 = T.lookup n dMA] | ||
132 | if not ok then putStrLn "updateMesh: attribute mismatch!" | ||
133 | else do | ||
134 | forM_ al $ \(n,a) -> do | ||
135 | case T.lookup n dS of | ||
136 | Just (Stream _ b i _ _) -> updateBuffer b [(i,meshAttrToArray a)] | ||
137 | _ -> return () | ||
138 | {- | ||
139 | case mp of | ||
140 | Nothing -> return () | ||
141 | Just p -> do | ||
142 | let ok2 = case (dMP,p) of | ||
143 | (Just (P_TriangleStripI v1, P_TriangleStripI v2) -> V.length v1 == V.length v2 | ||
144 | (P_TrianglesI v1, P_TrianglesI v2) -> V.length v1 == V.length v2 | ||
145 | (a,b) -> a == b | ||
146 | -} | ||
147 | |||
148 | compileMesh :: Mesh -> IO Mesh | ||
149 | compileMesh (Mesh attrs mPrim Nothing) = do | ||
150 | let mkIndexBuf v = do | ||
151 | iBuf <- compileBuffer [Array ArrWord32 (V.length v) $ withV V.unsafeWith v] | ||
152 | return $! Just $! IndexStream iBuf 0 0 (V.length v) | ||
153 | vBuf <- compileBuffer [meshAttrToArray a | a <- T.elems attrs] | ||
154 | (indices,prim) <- case mPrim of | ||
155 | P_Points -> return (Nothing,PointList) | ||
156 | P_TriangleStrip -> return (Nothing,TriangleStrip) | ||
157 | P_Triangles -> return (Nothing,TriangleList) | ||
158 | P_TriangleStripI v -> (,TriangleStrip) <$> mkIndexBuf v | ||
159 | P_TrianglesI v -> (,TriangleList) <$> mkIndexBuf v | ||
160 | let streams = T.fromList $! zipWith (\i (n,a) -> (n,meshAttrToStream vBuf i a)) [0..] (T.toList attrs) | ||
161 | gpuData = GPUData prim streams indices | ||
162 | return $! Mesh attrs mPrim (Just gpuData) | ||
163 | |||
164 | compileMesh mesh = return mesh | ||
165 | |||
166 | sblToV :: Storable a => [SB.ByteString] -> V.Vector a | ||
167 | sblToV ls = v | ||
168 | where | ||
169 | offs o (s:xs) = (o,s):offs (o + SB.length s) xs | ||
170 | offs _ [] = [] | ||
171 | cnt = sum (map SB.length ls) `div` (sizeOf $ V.head v) | ||
172 | v = unsafePerformIO $ do | ||
173 | mv <- MV.new cnt | ||
174 | MV.unsafeWith mv $ \dst -> forM_ (offs 0 ls) $ \(o,s) -> | ||
175 | SB.useAsCStringLen s $ \(src,len) -> moveBytes (plusPtr dst o) src len | ||
176 | V.unsafeFreeze mv | ||
177 | |||
178 | vToSB :: Storable a => V.Vector a -> SB.ByteString | ||
179 | vToSB v = unsafePerformIO $ do | ||
180 | let len = V.length v * sizeOf (V.head v) | ||
181 | V.unsafeWith v $ \p -> SB.packCStringLen (castPtr p,len) | ||
182 | |||
183 | instance Storable a => Binary (V.Vector a) where | ||
184 | put v = put $ vToSB v | ||
185 | get = do s <- get ; return $ sblToV [s] | ||
186 | |||
187 | instance Binary MeshAttribute where | ||
188 | put (A_Float a) = putWord8 0 >> put a | ||
189 | put (A_V2F a) = putWord8 1 >> put a | ||
190 | put (A_V3F a) = putWord8 2 >> put a | ||
191 | put (A_V4F a) = putWord8 3 >> put a | ||
192 | put (A_M22F a) = putWord8 4 >> put a | ||
193 | put (A_M33F a) = putWord8 5 >> put a | ||
194 | put (A_M44F a) = putWord8 6 >> put a | ||
195 | put (A_Int a) = putWord8 7 >> put a | ||
196 | put (A_Word a) = putWord8 8 >> put a | ||
197 | get = do | ||
198 | tag_ <- getWord8 | ||
199 | case tag_ of | ||
200 | 0 -> A_Float <$> get | ||
201 | 1 -> A_V2F <$> get | ||
202 | 2 -> A_V3F <$> get | ||
203 | 3 -> A_V4F <$> get | ||
204 | 4 -> A_M22F <$> get | ||
205 | 5 -> A_M33F <$> get | ||
206 | 6 -> A_M44F <$> get | ||
207 | 7 -> A_Int <$> get | ||
208 | 8 -> A_Word <$> get | ||
209 | _ -> fail "no parse" | ||
210 | |||
211 | instance Binary MeshPrimitive where | ||
212 | put P_Points = putWord8 0 | ||
213 | put P_TriangleStrip = putWord8 1 | ||
214 | put P_Triangles = putWord8 2 | ||
215 | put (P_TriangleStripI a) = putWord8 3 >> put a | ||
216 | put (P_TrianglesI a) = putWord8 4 >> put a | ||
217 | get = do | ||
218 | tag_ <- getWord8 | ||
219 | case tag_ of | ||
220 | 0 -> return P_Points | ||
221 | 1 -> return P_TriangleStrip | ||
222 | 2 -> return P_Triangles | ||
223 | 3 -> P_TriangleStripI <$> get | ||
224 | 4 -> P_TrianglesI <$> get | ||
225 | _ -> fail "no parse" | ||
226 | |||
227 | instance Binary Mesh where | ||
228 | put (Mesh a b _) = put (T.toList a) >> put b | ||
229 | get = do | ||
230 | a <- get | ||
231 | b <- get | ||
232 | return $! Mesh (T.fromList a) b Nothing | ||