diff options
author | Joe Crayne <joe@jerkface.net> | 2019-06-14 19:22:29 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-06-14 19:22:29 -0400 |
commit | bc7855492d8bdb21437c6d6b94a9c1872da45f1f (patch) | |
tree | 2577c9bbae84957bec410976bab6fa6021653837 | |
parent | 95f7c67b576f4e1ae897ea010da90d406681f018 (diff) |
crayne parser: higher level interface.
-rw-r--r-- | src/Data/Wavefront.hs | 167 | ||||
-rw-r--r-- | src/Wavefront.hs | 137 | ||||
-rw-r--r-- | src/Wavefront/Lex.hs | 6 | ||||
-rw-r--r-- | wavefront-obj.cabal | 2 |
4 files changed, 309 insertions, 3 deletions
diff --git a/src/Data/Wavefront.hs b/src/Data/Wavefront.hs new file mode 100644 index 0000000..2284d38 --- /dev/null +++ b/src/Data/Wavefront.hs | |||
@@ -0,0 +1,167 @@ | |||
1 | {-# LANGUAGE ConstraintKinds #-} | ||
2 | {-# LANGUAGE DeriveFunctor #-} | ||
3 | {-# LANGUAGE FlexibleInstances #-} | ||
4 | {-# LANGUAGE KindSignatures #-} | ||
5 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
6 | {-# LANGUAGE PatternSynonyms #-} | ||
7 | {-# LANGUAGE RankNTypes #-} | ||
8 | {-# LANGUAGE StandaloneDeriving #-} | ||
9 | {-# LANGUAGE UndecidableInstances #-} | ||
10 | module Data.Wavefront where | ||
11 | |||
12 | import Data.Kind | ||
13 | import Data.Text (Text) | ||
14 | import Numeric.Natural | ||
15 | import qualified Rank2 | ||
16 | |||
17 | data WavefrontOBJ v = WavefrontOBJ { | ||
18 | objLocations :: v Location | ||
19 | , objTexCoords :: v TexCoord | ||
20 | , objNormals :: v Normal | ||
21 | , objPoints :: v (Element Point) | ||
22 | , objLines :: v (Element Line) | ||
23 | , objFaces :: v (Element Face) | ||
24 | , objMtlLibs :: v Text | ||
25 | } | ||
26 | |||
27 | type ForThisWavefrontOBJ (c :: * -> Constraint) v = | ||
28 | ( c (v Location) | ||
29 | , c (v TexCoord) | ||
30 | , c (v Normal) | ||
31 | , c (v (Element Point)) | ||
32 | , c (v (Element Line)) | ||
33 | , c (v (Element Face)) | ||
34 | , c (v Text) ) | ||
35 | |||
36 | type ForAllWavefrontOBJ (c :: * -> Constraint) = | ||
37 | ( c Location | ||
38 | , c TexCoord | ||
39 | , c Normal | ||
40 | , c (Element Point) | ||
41 | , c (Element Line) | ||
42 | , c (Element Face) | ||
43 | , c Text | ||
44 | ) | ||
45 | |||
46 | deriving instance ForThisWavefrontOBJ Eq v => Eq (WavefrontOBJ v) | ||
47 | deriving instance ForThisWavefrontOBJ Show v => Show (WavefrontOBJ v) | ||
48 | |||
49 | instance Rank2.Functor WavefrontOBJ where | ||
50 | f <$> obj = obj | ||
51 | { objLocations = f (objLocations obj) | ||
52 | , objTexCoords = f (objTexCoords obj) | ||
53 | , objNormals = f (objNormals obj) | ||
54 | , objPoints = f (objPoints obj) | ||
55 | , objLines = f (objLines obj) | ||
56 | , objFaces = f (objFaces obj) | ||
57 | , objMtlLibs = f (objMtlLibs obj) | ||
58 | } | ||
59 | |||
60 | class Rank2.Functor g => Payload c g where | ||
61 | mapPayload :: proxy c -> (forall a. c a => p a -> q a) -> g p -> g q | ||
62 | |||
63 | instance ForAllWavefrontOBJ c => Payload c WavefrontOBJ where | ||
64 | mapPayload _ f obj = obj | ||
65 | { objLocations = f (objLocations obj) | ||
66 | , objTexCoords = f (objTexCoords obj) | ||
67 | , objNormals = f (objNormals obj) | ||
68 | , objPoints = f (objPoints obj) | ||
69 | , objLines = f (objLines obj) | ||
70 | , objFaces = f (objFaces obj) | ||
71 | , objMtlLibs = f (objMtlLibs obj) | ||
72 | } | ||
73 | |||
74 | |||
75 | |||
76 | -- | A location is a 4-floating vector. You can access to its components by | ||
77 | -- pattern matching on them: | ||
78 | -- | ||
79 | -- > let Location x y z w = Location 1 2 3 4 | ||
80 | -- | ||
81 | -- That type is strict and unboxed. | ||
82 | data Location = Location { | ||
83 | locX :: {-# UNPACK #-} !Float | ||
84 | , locY :: {-# UNPACK #-} !Float | ||
85 | , locZ :: {-# UNPACK #-} !Float | ||
86 | , locW :: {-# UNPACK #-} !Float | ||
87 | } deriving (Eq,Show) | ||
88 | |||
89 | -- | A texture coordinate is a 3D-floating vector. You can access to its | ||
90 | -- components by pattern matching on them: | ||
91 | -- | ||
92 | -- > let TexCoord r s t = TexCoord 0.1 0.2 0.3 | ||
93 | -- | ||
94 | -- That type is strcit and unboxed. | ||
95 | data TexCoord = TexCoord { | ||
96 | texcoordR :: {-# UNPACK #-} !Float | ||
97 | , texcoordS :: {-# UNPACK #-} !Float | ||
98 | , texcoordT :: {-# UNPACK #-} !Float | ||
99 | } deriving (Eq,Show) | ||
100 | |||
101 | |||
102 | -- | A normal is a 3-floating vector. You can access to its components by | ||
103 | -- pattern matching on them: | ||
104 | -- | ||
105 | -- > let Normal nx ny nz = Normal 0.1 0.2 0.3 | ||
106 | -- | ||
107 | -- That type is strict and unboxed. | ||
108 | data Normal = Normal { | ||
109 | norX :: {-# UNPACK #-} !Float | ||
110 | , norY :: {-# UNPACK #-} !Float | ||
111 | , norZ :: {-# UNPACK #-} !Float | ||
112 | } deriving (Eq,Show) | ||
113 | |||
114 | -- | A point is a single index that references the locations. It’s a canonical | ||
115 | -- type that truly represents a polygonal point. | ||
116 | data Point = Point { | ||
117 | pointLocIndex :: {-# UNPACK #-} !Int | ||
118 | } deriving (Eq,Show) | ||
119 | |||
120 | -- | A line index is a pair of indices. @'LineIndex' vi vti@. @vi@ references | ||
121 | -- the locations and @vti@ indexes the texture coordinates. If @vti == | ||
122 | -- 'Nothing'@, then that 'LineIndex' doesn’t have texture coordinates | ||
123 | -- associated with. | ||
124 | data LineIndex = LineIndex { | ||
125 | lineLocIndex :: {-# UNPACK #-} !Int | ||
126 | , lineTexCoordIndex :: !(Maybe Int) | ||
127 | } deriving (Eq,Show) | ||
128 | |||
129 | -- | A line gathers two line indices accessible by pattern matching or | ||
130 | -- 'lineIndexA' and 'lineIndexB'. | ||
131 | data Line = Line { | ||
132 | lineIndexA :: LineIndex | ||
133 | , lineIndexB :: LineIndex | ||
134 | } deriving (Eq,Show) | ||
135 | |||
136 | -- | A face index is a triplet of indices. @'FaceIndex' vi vti vni@ is a face | ||
137 | -- that indexes the locations with @vi@, the texture coordinates with @vti@ and | ||
138 | -- the normals with @vni@. An index set to 'Nothing' means /no information/. | ||
139 | -- That is, if @vni == 'Nothing'@, then that 'FaceIndex' doesn’t have a normal | ||
140 | -- associated with. | ||
141 | data FaceIndex = FaceIndex { | ||
142 | faceLocIndex :: {-# UNPACK #-} !Int | ||
143 | , faceTexCoordIndex :: !(Maybe Int) | ||
144 | , faceNorIndex :: !(Maybe Int) | ||
145 | } deriving (Eq,Show) | ||
146 | |||
147 | -- | A face gathers several 'FaceIndex' to build up faces. It has a least three | ||
148 | -- vertices | ||
149 | data Face = Face FaceIndex FaceIndex FaceIndex [FaceIndex] deriving (Eq,Show) | ||
150 | |||
151 | pattern Triangle :: FaceIndex -> FaceIndex -> FaceIndex -> Face | ||
152 | pattern Triangle a b c = Face a b c [] | ||
153 | |||
154 | pattern Quad :: FaceIndex -> FaceIndex -> FaceIndex -> FaceIndex -> Face | ||
155 | pattern Quad a b c d = Face a b c [d] | ||
156 | |||
157 | -- | An element holds a value along with the user-defined object’s name (if | ||
158 | -- any), the associated groups, the used material and the smoothing group the | ||
159 | -- element belongs to (if any). Those values can be used to sort the data per | ||
160 | -- object or per group and to lookup materials. | ||
161 | data Element a = Element { | ||
162 | elObject :: Maybe Text | ||
163 | , elGroups :: [Text] | ||
164 | , elMtl :: Maybe Text | ||
165 | , elSmoothingGroup :: Natural | ||
166 | , elValue :: a | ||
167 | } deriving (Eq,Show,Functor) | ||
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) | ||
diff --git a/src/Wavefront/Lex.hs b/src/Wavefront/Lex.hs index 7123184..78c6f1d 100644 --- a/src/Wavefront/Lex.hs +++ b/src/Wavefront/Lex.hs | |||
@@ -377,9 +377,9 @@ data ParamSpec = ParamU | ParamV | |||
377 | deriving (Eq,Ord,Show,Enum) | 377 | deriving (Eq,Ord,Show,Enum) |
378 | 378 | ||
379 | data RefTriple = RefTriple | 379 | data RefTriple = RefTriple |
380 | { refV :: Int | 380 | { refV :: {-# UNPACK #-} !Int |
381 | , refT :: Maybe Int | 381 | , refT :: !(Maybe Int) |
382 | , refN :: Maybe Int | 382 | , refN :: !(Maybe Int) |
383 | } | 383 | } |
384 | -- data RefTriple = RefTriple Int (Maybe Int) (Maybe Int) | 384 | -- data RefTriple = RefTriple Int (Maybe Int) (Maybe Int) |
385 | deriving (Eq,Ord,Show) | 385 | deriving (Eq,Ord,Show) |
diff --git a/wavefront-obj.cabal b/wavefront-obj.cabal index 1ccbc91..a771fe9 100644 --- a/wavefront-obj.cabal +++ b/wavefront-obj.cabal | |||
@@ -49,6 +49,8 @@ library | |||
49 | , Codec.Wavefront.FreeForm | 49 | , Codec.Wavefront.FreeForm |
50 | , Codec.Wavefront.Token | 50 | , Codec.Wavefront.Token |
51 | , Wavefront.Lex | 51 | , Wavefront.Lex |
52 | , Data.Wavefront | ||
53 | , Wavefront | ||
52 | -- other-modules: | 54 | -- other-modules: |
53 | other-extensions: ForeignFunctionInterface | 55 | other-extensions: ForeignFunctionInterface |
54 | , UnicodeSyntax | 56 | , UnicodeSyntax |