summaryrefslogtreecommitdiff
path: root/src/Wavefront.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Wavefront.hs')
-rw-r--r--src/Wavefront.hs137
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 #-}
2module Wavefront where
3
4import Data.Wavefront
5import Wavefront.Lex
6
7import Control.Arrow
8import Control.Monad.State
9import qualified Data.ByteString.Lazy.Char8 as L
10import qualified Data.DList as DList
11 ;import Data.DList (DList)
12import Data.Functor.Identity
13import qualified Data.IntMap as IntMap
14import Data.Text.Encoding (decodeUtf8)
15import qualified Data.Vector as Vector
16 ;import Data.Vector (Vector)
17import qualified Rank2
18
19newtype Count x = Count Int
20
21updateCount :: MonadState s m => (s -> Count x) -> (Count x -> s -> s) -> m ()
22updateCount field setField = do
23 Count c0 <- gets field
24 let c = succ c0
25 c `seq` modify (setField $ Count c)
26
27fixupRef :: Count x -> Int -> Int
28fixupRef (Count n) x | x > 0 = x - 1 -- Renumber from 0.
29 | otherwise = n + x -- Negative values are relative.
30
31fixupTriple :: WavefrontOBJ Count -> RefTriple -> RefTriple
32fixupTriple o (RefTriple v t n) =
33 RefTriple (fixupRef (objLocations o) v)
34 (fixupRef (objTexCoords o) <$> t)
35 (fixupRef (objNormals o) <$> n)
36
37objBookKeeping :: Monad m => ObjBuilder m -> ObjBuilder (StateT (WavefrontOBJ Count) m)
38objBookKeeping 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
56mkv :: [Double] -> Location
57mkv cs = Location x y z w where (x:y:z:w:_) = map realToFrac cs
58
59mkt :: [Double] -> TexCoord
60mkt cs = TexCoord x y z where (x:y:z:_) = map realToFrac cs
61
62mkn :: [Double] -> Normal
63mkn cs = Normal x y z where (x:y:z:_) = map realToFrac cs
64
65mkl :: RefTriple -> RefTriple -> Line
66mkl (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.
69mkF :: RefTriple -> FaceIndex
70mkF (RefTriple a at an) = FaceIndex a at an
71
72elemental :: Element () -> x -> Element x
73elemental element x = fmap (const x) element
74
75modifyFirst :: MonadState (c, d) m => (c -> c) -> m ()
76modifyFirst = modify' . first
77
78buildOBJ :: ObjBuilder (State (WavefrontOBJ DList,Element ()))
79buildOBJ = (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
115parse :: L.ByteString -> WavefrontOBJ Vector
116parse 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)