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