diff options
author | Joe Crayne <joe@jerkface.net> | 2019-06-16 23:47:34 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-06-17 02:34:36 -0400 |
commit | f9a26d4785260fef8fcb2a19bc8edcfccab94dbf (patch) | |
tree | 89abb5aa9a8808198134238fc89ee111d3fb1ac0 | |
parent | 93616e1bd801d9a4bf78e2da3cb076af91534580 (diff) |
Scale loaded mesh to fit viewport.
-rw-r--r-- | LoadMesh.hs | 82 | ||||
-rw-r--r-- | MeshSketch.hs | 3 |
2 files changed, 78 insertions, 7 deletions
diff --git a/LoadMesh.hs b/LoadMesh.hs index affadba..9eaa047 100644 --- a/LoadMesh.hs +++ b/LoadMesh.hs | |||
@@ -1,5 +1,6 @@ | |||
1 | {-# LANGUAGE LambdaCase #-} | 1 | {-# LANGUAGE LambdaCase #-} |
2 | {-# LANGUAGE RecordWildCards #-} | 2 | {-# LANGUAGE RecordWildCards #-} |
3 | {-# LANGUAGE FlexibleContexts #-} | ||
3 | module LoadMesh where | 4 | module LoadMesh where |
4 | 5 | ||
5 | import LambdaCube.GL as LambdaCubeGL -- renderer | 6 | import LambdaCube.GL as LambdaCubeGL -- renderer |
@@ -15,13 +16,16 @@ import qualified Data.ByteString as SB | |||
15 | import qualified Data.ByteString.Lazy.Char8 as L | 16 | import qualified Data.ByteString.Lazy.Char8 as L |
16 | import Data.Text (unpack,Text) | 17 | import Data.Text (unpack,Text) |
17 | import Data.List (groupBy,nub) | 18 | import Data.List (groupBy,nub) |
19 | import Numeric.LinearAlgebra hiding ((<>)) | ||
18 | 20 | ||
19 | import Codec.Picture as Juicy | 21 | import Codec.Picture as Juicy |
20 | import Wavefront | 22 | import Wavefront |
21 | import Wavefront.Types | 23 | import Wavefront.Types |
22 | import Data.Aeson | 24 | import Data.Aeson |
23 | 25 | ||
24 | type MeshData = ([(Mesh,Maybe Text)],MtlLib) | 26 | type MeshData = ( [(Mesh,Maybe Text)] -- List of uniform-material meshes (and the name of the material). |
27 | , MtlLib -- Material definitions. | ||
28 | ) | ||
25 | 29 | ||
26 | loadOBJ :: String -> IO (Either String MeshData) | 30 | loadOBJ :: String -> IO (Either String MeshData) |
27 | loadOBJ fname = L.readFile fname >>= \bs -> do | 31 | loadOBJ fname = L.readFile fname >>= \bs -> do |
@@ -31,10 +35,76 @@ loadOBJ fname = L.readFile fname >>= \bs -> do | |||
31 | return $ Right (objToMesh obj,mtlLib) | 35 | return $ Right (objToMesh obj,mtlLib) |
32 | 36 | ||
33 | 37 | ||
34 | uploadOBJToGPU :: MeshData -> IO ([(GPUMesh, Maybe Text)], MtlLib) | 38 | data BoundingBox = BoundingBox |
35 | uploadOBJToGPU (subModels,mtlLib) = do | 39 | { minX :: Float |
36 | gpuSubModels <- forM subModels $ \(mesh,mat) -> LambdaCubeGL.uploadMeshToGPU mesh >>= \a -> return (a,mat) | 40 | , maxX :: Float |
37 | return (gpuSubModels,mtlLib) | 41 | , minY :: Float |
42 | , maxY :: Float | ||
43 | , minZ :: Float | ||
44 | , maxZ :: Float | ||
45 | } | ||
46 | deriving (Eq,Ord,Show) | ||
47 | |||
48 | instance Semigroup BoundingBox where | ||
49 | a <> b = BoundingBox | ||
50 | { minX = if minX b < minX a then minX b else minX a | ||
51 | , maxX = if maxX b > maxX a then maxX b else maxX a | ||
52 | , minY = if minY b < minY a then minY b else minY a | ||
53 | , maxY = if maxY b > maxY a then maxY b else maxY a | ||
54 | , minZ = if minZ b < minZ a then minZ b else minZ a | ||
55 | , maxZ = if maxZ b > maxZ a then maxZ b else maxZ a | ||
56 | } | ||
57 | instance Monoid BoundingBox where mempty = BoundingBox 0 0 0 0 0 0 | ||
58 | |||
59 | attribBoundingBox :: Map String MeshAttribute -> BoundingBox | ||
60 | attribBoundingBox attrib = case Map.lookup "position" attrib of | ||
61 | Just (A_V3F vs) -> V.foldr (\(V3 x y z ) bb -> bb <> BoundingBox x x y y z z) mempty vs | ||
62 | Just (A_V4F vs) -> V.foldr (\(V4 x y z _) bb -> bb <> BoundingBox x x y y z z) mempty vs | ||
63 | _ -> mempty | ||
64 | |||
65 | bbnorm :: BoundingBox -> Float | ||
66 | bbnorm (BoundingBox x0 x y0 y z0 z) = sqrt $ (x-x0)^2 + (y-y0)^2 + (z-z0)^2 | ||
67 | |||
68 | scaleWithin :: BoundingBox -> BoundingBox -> Matrix Float | ||
69 | scaleWithin meshbb scalebb = | ||
70 | if meshbb <> scalebb /= scalebb | ||
71 | || (let {m=bbnorm meshbb; s=bbnorm scalebb} in m < 0.1*s) | ||
72 | then let tr0 = (4><4) [ 1,0,0, negate $ (minX meshbb + maxX meshbb)/2 | ||
73 | , 0,1,0, negate $ (minY meshbb + maxY meshbb)/2 | ||
74 | , 0,0,1, negate $ (minZ meshbb + maxZ meshbb)/2 | ||
75 | , 0,0,0, 1 ] | ||
76 | sc = (4><4) [s,0,0,0 | ||
77 | ,0,s,0,0 | ||
78 | ,0,0,s,0 | ||
79 | ,0,0,0,1] | ||
80 | s = minimum [sx,sy,sz] | ||
81 | sx = (maxX scalebb - minX scalebb) / (maxX meshbb - minX meshbb) | ||
82 | sy = (maxY scalebb - minY scalebb) / (maxY meshbb - minY meshbb) | ||
83 | sz = (maxZ scalebb - minZ scalebb) / (maxZ meshbb - minZ meshbb) | ||
84 | tr1 = (4><4) [ 1,0,0, (minX scalebb + maxX scalebb)/2 | ||
85 | , 0,1,0, (minY scalebb + maxY scalebb)/2 | ||
86 | , 0,0,1, (minZ scalebb + maxZ scalebb)/2 | ||
87 | , 0,0,0, 1 ] | ||
88 | in tr1 <> sc <> tr0 | ||
89 | else ident 4 | ||
90 | |||
91 | transV3 t (V3 x y z) = let v = t #> fromList [x,y,z,1] in V3 (v!0/v!3) (v!1/v!3) (v!2/v!3) | ||
92 | transV4 t (V4 x y z w) = let v = t #> fromList [x,y,z,w] in V4 (v!0) (v!1) (v!2) (v!3) | ||
93 | |||
94 | tranformAttribute t (A_V3F v) = A_V3F $ transV3 t <$> v | ||
95 | tranformAttribute t (A_V4F v) = A_V4F $ transV4 t <$> v | ||
96 | |||
97 | transformMesh :: Matrix Float -> Mesh -> Mesh | ||
98 | transformMesh t m = m | ||
99 | { mAttributes = Map.adjust (tranformAttribute t) "position" (mAttributes m) | ||
100 | } | ||
101 | |||
102 | uploadOBJToGPU :: Maybe BoundingBox -> MeshData -> IO (([(GPUMesh, Maybe Text)], MtlLib),Matrix Float) | ||
103 | uploadOBJToGPU scalebb (subModels,mtlLib) = do | ||
104 | let meshbb = foldMap (attribBoundingBox . mAttributes . fst) subModels :: BoundingBox | ||
105 | m = maybe (ident 4) (scaleWithin meshbb) scalebb | ||
106 | gpuSubModels <- forM subModels $ \(mesh,mat) -> LambdaCubeGL.uploadMeshToGPU (transformMesh m mesh) >>= \a -> return (a,mat) | ||
107 | return ((gpuSubModels,mtlLib),m) | ||
38 | 108 | ||
39 | uploadMtlLib :: MtlLib -> IO (Map Text (ObjMaterial,TextureData)) | 109 | uploadMtlLib :: MtlLib -> IO (Map Text (ObjMaterial,TextureData)) |
40 | uploadMtlLib mtlLib = do | 110 | uploadMtlLib mtlLib = do |
@@ -45,7 +115,7 @@ uploadMtlLib mtlLib = do | |||
45 | checkerTex <- LambdaCubeGL.uploadTexture2DToGPU checkerImage | 115 | checkerTex <- LambdaCubeGL.uploadTexture2DToGPU checkerImage |
46 | -- load images and upload to gpu | 116 | -- load images and upload to gpu |
47 | textureLib <- forM (Map.fromList $ zip usedTextures usedTextures) $ \fname -> Juicy.readImage fname >>= \case | 117 | textureLib <- forM (Map.fromList $ zip usedTextures usedTextures) $ \fname -> Juicy.readImage fname >>= \case |
48 | Left err -> putStrLn err >> return checkerTex | 118 | Left err -> putStrLn (fname ++": "++err) >> return checkerTex |
49 | Right img -> LambdaCubeGL.uploadTexture2DToGPU img | 119 | Right img -> LambdaCubeGL.uploadTexture2DToGPU img |
50 | whiteTex <- LambdaCubeGL.uploadTexture2DToGPU whiteImage | 120 | whiteTex <- LambdaCubeGL.uploadTexture2DToGPU whiteImage |
51 | -- pair textures and materials | 121 | -- pair textures and materials |
diff --git a/MeshSketch.hs b/MeshSketch.hs index f1ee612..a153f5e 100644 --- a/MeshSketch.hs +++ b/MeshSketch.hs | |||
@@ -163,7 +163,8 @@ xzPlaneVector = fromList [ 0,1,0 -- unit normal | |||
163 | uploadState :: IsWidget glarea => MeshData -> glarea -> GLStorage -> IO State | 163 | uploadState :: IsWidget glarea => MeshData -> glarea -> GLStorage -> IO State |
164 | uploadState obj glarea storage = do | 164 | uploadState obj glarea storage = do |
165 | -- load OBJ geometry and material descriptions | 165 | -- load OBJ geometry and material descriptions |
166 | (objMesh,mtlLib) <- uploadOBJToGPU obj | 166 | let workarea = BoundingBox (-1.5) (1.5) (-1.5) 1.5 (-1.5) (1.5) |
167 | ((objMesh,mtlLib),objscale) <- uploadOBJToGPU (Just workarea) obj | ||
167 | -- load materials textures | 168 | -- load materials textures |
168 | gpuMtlLib <- uploadMtlLib mtlLib | 169 | gpuMtlLib <- uploadMtlLib mtlLib |
169 | -- add OBJ to pipeline input | 170 | -- add OBJ to pipeline input |