aboutsummaryrefslogtreecommitdiff
path: root/Graphics/Glyph/ObjLoader.hs
blob: 78f010a4ee52c4013e97cf875fc5e27b3f658cfa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
module Graphics.Glyph.ObjLoader where

import Graphics.Glyph.BufferBuilder
import Graphics.Glyph.Util
import Debug.Trace

import Control.Monad
import Data.Either
import Data.String.Utils
import Data.Array
import System.IO
import qualified Data.Map as M

import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Char8 as C

data ObjectFile a = ObjectFile [ObjectStatement a]

data ObjectStatement a =
    Nop |
    VertexStatement (a,a,a)       |
    TextureStatement (a,a)    |
    VertexNormalStatement (a,a,a) |
    UseMaterialStatement (String)           |
    MaterialLibraryStatement String         |
    FaceStatement [(Int,Int,Int)]   deriving Show

foldl2 :: a -> [b] -> (a -> b -> a) -> a
foldl2 a b c = foldl  c a b

isNop :: ObjectStatement a -> Bool
isNop x = case x of 
    Nop -> True
    _ -> False

isVertex :: ObjectStatement a -> Bool
isVertex (VertexStatement _) = True
isVertex _ = False

isNormal :: ObjectStatement a -> Bool
isNormal (VertexNormalStatement _) = True
isNormal _ = False

isTexture :: ObjectStatement a -> Bool
isTexture (TextureStatement _) = True
isTexture _ = False

basicBuildObject :: (Floating b, IsModelBuilder b a) => ObjectFile b -> a ()
basicBuildObject (ObjectFile list) =
    let fromList lst = listArray (0,length lst-1) lst in

    -- Set up the lists as arrays for fast access
    let vertexList  = fromList $ map (\stmt -> 
                        case stmt of
                            (VertexStatement v) -> v
                            _ -> (0,0,0)) (filter isVertex list) in

    let normalList  = fromList $ map (\stmt -> 
                        case stmt of
                            (VertexNormalStatement v) -> v
                            _ -> (0,0,0)) (filter isNormal list) in

    let textureList  = fromList $ map (\stmt -> 
                        case stmt of
                            (TextureStatement v) -> v
                            _ -> (0,0)) (filter isTexture list) in

    forM_ list $ \stmt ->
        case stmt of
            (FaceStatement arr) ->
                forM_ arr $ \(a,b,c) -> do
                    when (c >= 0) (uncurry3 plotNormal  $ normalList  ! (c-1))
                    when (b >= 0) (uncurry  plotTexture $ textureList ! (b-1))
                    when (a >= 0) (uncurry3 plotVertex3 $ vertexList  ! (a-1))
            _ -> return ()
                        

loadObjFromBytestring :: (Read b) => L.ByteString -> ([String], ObjectFile b)
loadObjFromBytestring _contents =
    let contents::[L.ByteString] ; contents = C.split '\n' _contents in
    let mys2n str = case str of
         "" -> -1
         _ -> read str in

    let s2t s = case split "/" s of
         [a,b,c] -> Just (mapT3 mys2n (a,b,c))
         [a,b] -> Just (mapT3 mys2n (a,b,""))
         [a] -> Just (mapT3 mys2n (a,"",""))
         _ -> Nothing in

    let compiled =
            map (\(num,line) -> case words $ C.unpack line of
    
            [] -> Right Nop -- This is an empty line
            (('#':_):_)  -> Right Nop -- This is a comment, so use a 'nop'
            ("o":_) -> Right Nop -- Not really of use
            
            ["v",x,y,z]  -> Right $ VertexStatement ( (read x), (read y), (read z))
            ["vt",x,y]   -> Right $ TextureStatement ( (read x), (read y))
            ["vn",x,y,z] -> Right $ VertexNormalStatement ( (read x), (read y), (read z))
            ["usemtl", mtl] -> Right $ UseMaterialStatement mtl
            ["mtllib", lib] -> Right $ MaterialLibraryStatement lib

            ("f":_tail) -> case mapM s2t _tail of
                Just lst -> Right $ FaceStatement lst
                _ -> Left $ foldl (++) "" ["Syntax error in face value on line ", show num, " `", C.unpack line, "'" ]

            _ -> Left $ foldl (++) "" ["Unrecognized Sequence on line ", show num, " `", C.unpack line, "'" ]

            ) (zip [(1::Int)..] contents) in

    ( lefts compiled, ObjectFile (filter (not.isNop) $ rights compiled) )
    

loadObjFromHandle :: (Read b) => Handle -> IO ([String], ObjectFile b)
loadObjFromHandle = loadObjFromHandleWithFilter id

loadObjFromHandleWithFilter :: (Read b) => (L.ByteString -> L.ByteString) -> Handle -> IO ([String], ObjectFile b)
loadObjFromHandleWithFilter _filter handle =
    liftM (loadObjFromBytestring . _filter) (L.hGetContents handle)

loadObjFile :: (Read b) => FilePath -> IO ([String], ObjectFile b)
loadObjFile = loadObjFileWithFilter id

loadObjFileWithFilter :: (Read b) => (L.ByteString -> L.ByteString) -> FilePath -> IO ([String], ObjectFile b)
loadObjFileWithFilter filt path = loadObjFromHandleWithFilter filt =<< openFile path ReadMode