summaryrefslogtreecommitdiff
path: root/LoadMesh.hs
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2019-04-17 18:39:12 -0400
committerJoe Crayne <joe@jerkface.net>2019-04-17 18:41:18 -0400
commit395f3b525090097c88434b03c88fe2fb8b8d7aba (patch)
tree0f60388ddd09bc69e7257be32959e95574a73a73 /LoadMesh.hs
parent64f1a100fc887fb2a8bc87e2ac6975e872010ef5 (diff)
Refactored object-loading demo.
Diffstat (limited to 'LoadMesh.hs')
-rw-r--r--LoadMesh.hs75
1 files changed, 75 insertions, 0 deletions
diff --git a/LoadMesh.hs b/LoadMesh.hs
new file mode 100644
index 0000000..69e66d6
--- /dev/null
+++ b/LoadMesh.hs
@@ -0,0 +1,75 @@
1{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE RecordWildCards #-}
3module LoadMesh where
4
5import LambdaCube.GL as LambdaCubeGL -- renderer
6import LambdaCube.GL.Mesh as LambdaCubeGL
7import MtlParser
8
9import Control.Monad
10import Data.Maybe
11import Data.Map (Map)
12import qualified Data.Map as Map
13import qualified Data.Vector as V
14import qualified Data.ByteString as SB
15import Data.Text (unpack,Text)
16import Data.List (groupBy,nub)
17
18import Codec.Picture as Juicy
19import Codec.Wavefront
20import Data.Aeson
21
22type MeshData = ([(Mesh,Maybe Text)],MtlLib)
23
24loadOBJ :: String -> IO (Either String MeshData)
25loadOBJ fname = fromFile fname >>= \case -- load geometry
26 Left err -> putStrLn err >> return (Left err)
27 Right obj@WavefrontOBJ{..} -> do
28 -- load materials
29 mtlLib <- mconcat . V.toList <$> mapM (readMtl . unpack) objMtlLibs
30 return $ Right (objToMesh obj,mtlLib)
31
32uploadOBJToGPU :: MeshData -> IO ([(GPUMesh, Maybe Text)], MtlLib)
33uploadOBJToGPU (subModels,mtlLib) = do
34 gpuSubModels <- forM subModels $ \(mesh,mat) -> LambdaCubeGL.uploadMeshToGPU mesh >>= \a -> return (a,mat)
35 return (gpuSubModels,mtlLib)
36
37uploadMtlLib :: MtlLib -> IO (Map Text (ObjMaterial,TextureData))
38uploadMtlLib mtlLib = do
39 -- collect used textures
40 let usedTextures = nub . concatMap (maybeToList . mtl_map_Kd) $ Map.elems mtlLib
41 whiteImage = Juicy.ImageRGB8 $ Juicy.generateImage (\_ _ -> Juicy.PixelRGB8 255 255 255) 1 1
42 checkerImage = Juicy.ImageRGB8 $ Juicy.generateImage (\x y -> if mod (x + y) 2 == 0 then Juicy.PixelRGB8 0 0 0 else Juicy.PixelRGB8 255 255 0) 2 2
43 checkerTex <- LambdaCubeGL.uploadTexture2DToGPU checkerImage
44 -- load images and upload to gpu
45 textureLib <- forM (Map.fromList $ zip usedTextures usedTextures) $ \fname -> Juicy.readImage fname >>= \case
46 Left err -> putStrLn err >> return checkerTex
47 Right img -> LambdaCubeGL.uploadTexture2DToGPU img
48 whiteTex <- LambdaCubeGL.uploadTexture2DToGPU whiteImage
49 -- pair textures and materials
50 return $ (\a -> (a, maybe whiteTex (fromMaybe checkerTex . flip Map.lookup textureLib) . mtl_map_Kd $ a)) <$> mtlLib
51
52objToMesh :: WavefrontOBJ -> [(Mesh,Maybe Text)]
53objToMesh WavefrontOBJ{..} = [(toMesh faceGroup, elMtl . head $ faceGroup) | faceGroup <- faces] where
54 faces = groupBy (\a b -> elMtl a == elMtl b) (V.toList objFaces)
55 toMesh l = Mesh
56 { mAttributes = Map.fromList
57 [ ("position", A_V4F position)
58 , ("normal", A_V3F normal)
59 , ("uvw", A_V3F texcoord)
60 ]
61 , mPrimitive = P_Triangles
62 } where
63 triangulate (Triangle a b c) = [a,b,c]
64 triangulate (Quad a b c d) = [a,b,c, c,d,a]
65 triangulate (Face a b c l) = a : b : c : concatMap (\(x,y) -> [a,x,y]) (zip (c:l) l) -- should work for convex polygons without holes
66 defaultPosition = Location 0 0 0 0
67 defaultNormal = Normal 0 0 0
68 defaultTexCoord = TexCoord 0 0 0
69 v !- i = v V.!? (i-1)
70 toVertex FaceIndex{..} = ( let Location x y z w = fromMaybe defaultPosition (objLocations !- faceLocIndex) in V4 x y z w
71 , let Normal x y z = fromMaybe defaultNormal ((objNormals !-) =<< faceNorIndex) in V3 x y z
72 , let TexCoord x y z = fromMaybe defaultTexCoord ((objTexCoords !-) =<< faceTexCoordIndex) in V3 x y z
73 )
74 (position,normal,texcoord) = V.unzip3 . V.concat . map (V.fromList . map toVertex . triangulate . elValue) $ l
75