44{-# LANGUAGE OverloadedRecordDot #-}
55module Ghengin.Geometry.Obj where
66
7- import Prelude
8- import qualified Prelude.Linear as Linear
7+ import Prelude hiding (log )
98import Control.Monad
9+ import qualified Prelude.Linear as Linear
1010
1111import qualified Data.Vector as V
12+ import qualified Data.Vector.Storable as SV
1213
1314import Geomancy
1415
1516import Codec.Wavefront
1617
18+ import Ghengin.Core.Log
1719import Ghengin.Core.Type.Compatible
18- import Ghengin.Core.Prelude (GHList (.. ))
1920import Ghengin.Core.Render.Pipeline
2021import Ghengin.Core.Render.Property
2122import Ghengin.Core.Renderer
2223import Ghengin.Core.Mesh
23- import Ghengin.Core.Mesh.Vertex
2424import qualified Data.Linear.Alias as Alias
2525
2626import qualified Control.Functor.Linear as Linear
2727import qualified Control.Monad.IO.Class.Linear as Linear
2828
29+ import Ghengin.Geometry.Normals
30+
31+ -- | Loads a 'WavefrontOBJ' from a file.
32+ --
33+ -- Useful if one desires to embed the model with template haskell
34+ loadObjFile :: FilePath -> IO (Either String WavefrontOBJ )
35+ loadObjFile = fromFile
36+
37+ -- | Load an .obj file and create a mesh from it.
2938loadObjMesh :: (CompatibleMesh ts π , CompatibleVertex '[Vec3 , Vec3 ] π )
3039 => FilePath
3140 -> RenderPipeline π ps
@@ -34,27 +43,50 @@ loadObjMesh :: (CompatibleMesh ts π, CompatibleVertex '[Vec3, Vec3] π)
3443 -- The property bindings need to set when creating it.
3544 ⊸ Renderer (Mesh '[Vec3 , Vec3 ] ts , RenderPipeline π ps )
3645loadObjMesh filepath rp props = Linear. do
37- Linear. liftSystemIOU (fromFile filepath) Linear. >>= \ case
46+ Linear. liftSystemIOU (loadObjFile filepath) Linear. >>= \ case
3847 Linear. Ur (Left err) -> Linear. do
3948 destroyRenderPipeline rp
4049 Alias. forget props
4150 Linear. liftSystemIO (fail err)
4251 Linear. Ur (Right wavefrontObj) ->
43- let
44- locs = V. map getLoc $ wavefrontObj. objLocations
45- normals = V. map getNormal $ wavefrontObj. objNormals
46- faces = V. map (. elValue) wavefrontObj. objFaces
47-
48- getLoc :: Location -> Vec3
49- getLoc (Location x y z _) = vec3 x (- y) z
50-
51- getNormal :: Normal -> Vec3
52- getNormal (Normal x y z) = vec3 x y z
53-
54- vertices = V. zipWith (:&:) locs normals
55- ixs = V. concatMap (\ (Face a b c d) -> V. fromList $
56- [ a. faceLocIndex , b. faceLocIndex , c. faceLocIndex ] ++ map (. faceLocIndex) d
57- ) faces
58- in
59- createMeshWithIxsSV rp props (V. convert vertices) (V. convert $ V. map fromIntegral ixs)
52+ createObjMesh wavefrontObj rp props
53+
54+ -- | Create a mesh from a 'WavefrontOBJ'
55+ createObjMesh :: (CompatibleMesh ts π , CompatibleVertex '[Vec3 , Vec3 ] π )
56+ => WavefrontOBJ
57+ -> RenderPipeline π ps
58+ ⊸ PropertyBindings ts
59+ -- ^ These must NOT be added after creating the mesh.
60+ -- The property bindings need to set when creating it.
61+ ⊸ Renderer (Mesh '[Vec3 , Vec3 ] ts , RenderPipeline π ps )
62+ createObjMesh wavefrontObj rp props =
63+ let
64+ locs = V. map getLoc $ wavefrontObj. objLocations
65+ normals = V. map getNormal $ wavefrontObj. objNormals
66+ faces = V. map (. elValue) wavefrontObj. objFaces
67+
68+ getLoc :: Location -> Vec3
69+ getLoc (Location x y z _) = vec3 x (- y) z
70+
71+ -- get normal, currently ignoring face
72+ getNormal :: Normal -> Vec3
73+ getNormal (Normal x y z) = vec3 x y z
74+
75+ vertices
76+ | V. length normals == 0
77+ || V. length locs /= V. length normals {- bad object file! -}
78+ = V. zipWith (:&:) locs (computeNormals ixs locs)
79+ | otherwise
80+ = V. zipWith (:&:) locs normals
81+
82+ ixs = V. concatMap (\ (Face a b c _) -> V. fromList $ map (\ x -> x- 1 ) {- face indices are 1-indexed-}
83+ [ a. faceLocIndex , b. faceLocIndex , c. faceLocIndex ]
84+ ) faces
85+ in Linear. do
86+ if (V. length normals > 0 && V. length locs /= V. length normals)
87+ then log $ toLogStr $ " createObjMesh: length of normals ("
88+ ++ show (V. length normals) ++ " ) is not the same as length of locations ("
89+ ++ show (V. length locs) ++ " )! Ignoring and recomputing normals from scratch..."
90+ else Linear. pure ()
91+ createMeshWithIxsSV rp props (V. convert vertices) (SV. map fromIntegral $ V. convert ixs)
6092
0 commit comments