diff options
Diffstat (limited to 'src/Wavefront.hs')
-rw-r--r-- | src/Wavefront.hs | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/Wavefront.hs b/src/Wavefront.hs new file mode 100644 index 0000000..6195eee --- /dev/null +++ b/src/Wavefront.hs | |||
@@ -0,0 +1,137 @@ | |||
1 | {-# LANGUAGE FlexibleContexts #-} | ||
2 | module Wavefront where | ||
3 | |||
4 | import Data.Wavefront | ||
5 | import Wavefront.Lex | ||
6 | |||
7 | import Control.Arrow | ||
8 | import Control.Monad.State | ||
9 | import qualified Data.ByteString.Lazy.Char8 as L | ||
10 | import qualified Data.DList as DList | ||
11 | ;import Data.DList (DList) | ||
12 | import Data.Functor.Identity | ||
13 | import qualified Data.IntMap as IntMap | ||
14 | import Data.Text.Encoding (decodeUtf8) | ||
15 | import qualified Data.Vector as Vector | ||
16 | ;import Data.Vector (Vector) | ||
17 | import qualified Rank2 | ||
18 | |||
19 | newtype Count x = Count Int | ||
20 | |||
21 | updateCount :: MonadState s m => (s -> Count x) -> (Count x -> s -> s) -> m () | ||
22 | updateCount field setField = do | ||
23 | Count c0 <- gets field | ||
24 | let c = succ c0 | ||
25 | c `seq` modify (setField $ Count c) | ||
26 | |||
27 | fixupRef :: Count x -> Int -> Int | ||
28 | fixupRef (Count n) x | x > 0 = x - 1 -- Renumber from 0. | ||
29 | | otherwise = n + x -- Negative values are relative. | ||
30 | |||
31 | fixupTriple :: WavefrontOBJ Count -> RefTriple -> RefTriple | ||
32 | fixupTriple o (RefTriple v t n) = | ||
33 | RefTriple (fixupRef (objLocations o) v) | ||
34 | (fixupRef (objTexCoords o) <$> t) | ||
35 | (fixupRef (objNormals o) <$> n) | ||
36 | |||
37 | objBookKeeping :: Monad m => ObjBuilder m -> ObjBuilder (StateT (WavefrontOBJ Count) m) | ||
38 | objBookKeeping builder = (lift Rank2.<$> builder) | ||
39 | { vertex = \xs -> do lift $ vertex builder xs | ||
40 | updateCount objLocations $ \x o -> o { objLocations = x } | ||
41 | , vertexT = \xs -> do lift $ vertexT builder xs | ||
42 | updateCount objTexCoords $ \x o -> o { objTexCoords = x } | ||
43 | , vertexN = \xs -> do lift $ vertexN builder xs | ||
44 | updateCount objNormals $ \x o -> o { objNormals = x } | ||
45 | , points = \xs -> do | ||
46 | n <- gets objLocations | ||
47 | lift $ points builder $ fixupRef n <$> xs | ||
48 | , line = \ts -> do | ||
49 | o <- get | ||
50 | lift $ line builder $ fixupTriple o <$> ts | ||
51 | , face = \ts -> do | ||
52 | o <- get | ||
53 | lift $ face builder $ fixupTriple o <$> ts | ||
54 | } | ||
55 | |||
56 | mkv :: [Double] -> Location | ||
57 | mkv cs = Location x y z w where (x:y:z:w:_) = map realToFrac cs | ||
58 | |||
59 | mkt :: [Double] -> TexCoord | ||
60 | mkt cs = TexCoord x y z where (x:y:z:_) = map realToFrac cs | ||
61 | |||
62 | mkn :: [Double] -> Normal | ||
63 | mkn cs = Normal x y z where (x:y:z:_) = map realToFrac cs | ||
64 | |||
65 | mkl :: RefTriple -> RefTriple -> Line | ||
66 | mkl (RefTriple a at _) (RefTriple b bt _) = Line (LineIndex a at) (LineIndex b bt) | ||
67 | |||
68 | -- I'd have thought these would be Coercible, but I guess not. | ||
69 | mkF :: RefTriple -> FaceIndex | ||
70 | mkF (RefTriple a at an) = FaceIndex a at an | ||
71 | |||
72 | elemental :: Element () -> x -> Element x | ||
73 | elemental element x = fmap (const x) element | ||
74 | |||
75 | modifyFirst :: MonadState (c, d) m => (c -> c) -> m () | ||
76 | modifyFirst = modify' . first | ||
77 | |||
78 | buildOBJ :: ObjBuilder (State (WavefrontOBJ DList,Element ())) | ||
79 | buildOBJ = (nullBuilder $ pure ()) | ||
80 | { vertex = \xs -> modifyFirst $ \o -> o { objLocations = objLocations o `DList.snoc` mkv xs } | ||
81 | , vertexT = \xs -> modifyFirst $ \o -> o { objTexCoords = objTexCoords o `DList.snoc` mkt xs } | ||
82 | , vertexN = \xs -> modifyFirst $ \o -> o { objNormals = objNormals o `DList.snoc` mkn xs } | ||
83 | , points = \xs -> do | ||
84 | let p = map Point xs :: [Point] | ||
85 | (pts,element) <- gets (objPoints *** elemental) | ||
86 | modifyFirst $ \o -> o { objPoints = pts `DList.append` fmap element (DList.fromList p) } | ||
87 | , line = \xs -> do | ||
88 | (lns,element) <- gets (objLines *** elemental) | ||
89 | let l = zipWith mkl xs (tail xs) | ||
90 | -- Line requires at least two points. We'll ignore it otherwise. | ||
91 | when (not $ null l) $ | ||
92 | modifyFirst $ \o -> o { objLines = lns `DList.append` fmap element (DList.fromList l) } | ||
93 | , face = \xs -> do | ||
94 | (fcs,element) <- gets (objFaces *** elemental) | ||
95 | case map mkF xs of | ||
96 | a:b:c:ds -> modifyFirst $ \o -> o { objFaces = fcs `DList.snoc` element (Face a b c ds) } | ||
97 | _ -> return () -- Ignore faces with less than 3 indices. | ||
98 | , mtllib = \xs -> do | ||
99 | let l = map decodeUtf8 xs | ||
100 | libs <- gets (objMtlLibs . fst) | ||
101 | modifyFirst $ \o -> o { objMtlLibs = libs `DList.append` DList.fromList l } | ||
102 | , groups = \xs -> do | ||
103 | let g = map decodeUtf8 xs | ||
104 | modify' $ second $ \e -> e { elGroups = g } | ||
105 | , objectName = \x -> do | ||
106 | let o = decodeUtf8 x | ||
107 | modify' $ second $ \e -> e { elObject = Just o } | ||
108 | , usemtl = \x -> do | ||
109 | let mtl = decodeUtf8 x | ||
110 | modify' $ second $ \e -> e { elMtl = Just mtl } | ||
111 | , smoothingGroup = \x -> when (x > 0) $ do | ||
112 | modify $ second $ \e -> e { elSmoothingGroup = fromIntegral x } | ||
113 | } | ||
114 | |||
115 | parse :: L.ByteString -> WavefrontOBJ Vector | ||
116 | parse bs = Rank2.fmap (Vector.fromList . DList.toList) obj | ||
117 | where | ||
118 | b = objBookKeeping buildOBJ | ||
119 | ls = WavefrontOBJ | ||
120 | { objLocations = DList.empty | ||
121 | , objTexCoords = DList.empty | ||
122 | , objNormals = DList.empty | ||
123 | , objPoints = DList.empty | ||
124 | , objLines = DList.empty | ||
125 | , objFaces = DList.empty | ||
126 | , objMtlLibs = DList.empty | ||
127 | } | ||
128 | c = Rank2.fmap (const $ Count 0) ls :: WavefrontOBJ Count | ||
129 | el = Element | ||
130 | { elObject = Nothing | ||
131 | , elGroups = [] | ||
132 | , elMtl = Nothing | ||
133 | , elSmoothingGroup = 0 | ||
134 | , elValue = () | ||
135 | } | ||
136 | substvars = ObjConfig IntMap.empty | ||
137 | (obj,_) = execState (runStateT (parseOBJ b substvars bs) c) (ls,el) | ||