summaryrefslogtreecommitdiffstats
path: root/src/Data/Obj3D/OpenGL.hs
blob: b126310d910bbf66403938e9733d4a68b8e5610a (plain)
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
module Data.Obj3D.OpenGL ( Interleaved
                         , InterleavedObj
                         , interleavedArrayFormat
                         , indexArrayFormat
                         , interleaveObj
                         , makeInterleavedArrays
                         , bindInterleavedArrays
                         , drawObject
                         ) where

import Data.Obj3D

import Control.Arrow
import Control.Monad.State
import Data.Function (on)
import Data.List (genericIndex, sortBy)
import Data.Maybe
import Graphics.Rendering.OpenGL.GL (GLfloat, GLuint)
import Graphics.Rendering.OpenGL.GL.BeginEnd (PrimitiveMode(..))
import Graphics.Rendering.OpenGL.GL.VertexArrays (DataType(..), InterleavedArrays(..), interleavedArrays, drawElements)
import Foreign.ForeignPtr
import Foreign.Marshal.Array (pokeArray)
import Foreign.Ptr
import Foreign.Storable

import qualified Data.Map as M


data Interleaved = Interleaved
    { texCoordS :: GLfloat
    , texCoordT :: GLfloat
    , normalX   :: GLfloat
    , normalY   :: GLfloat
    , normalZ   :: GLfloat
    , vertexX   :: GLfloat
    , vertexY   :: GLfloat
    , vertexZ   :: GLfloat
    } deriving Show

instance Storable Interleaved where
    sizeOf _ = 8 * sizeOf (undefined :: GLfloat)
    alignment _ = alignment (undefined :: GLfloat)
    
    peek _ = fail "Can't read Interleaved"
    
    poke ptr obj = pokeArray (castPtr ptr) $ map ($ obj) [texCoordS, texCoordT, normalX, normalY, normalZ, vertexX, vertexY, vertexZ]

type InterleavedObj = (ForeignPtr Interleaved, [(ForeignPtr GLuint, Int)])

type Interleaver a = State (Integer, M.Map FaceVertex Integer) a

interleavedArrayFormat :: InterleavedArrays
interleavedArrayFormat = T2fN3fV3f

indexArrayFormat :: DataType
indexArrayFormat = UnsignedInt

getIndex :: FaceVertex -> Interleaver Integer
getIndex v = do
  ix <- gets $ M.lookup v . snd
  case ix of
    Just i  -> return i
    Nothing -> do
               i <- gets $ fst
               modify $ (+1) *** (M.insert v i)
               return i

makeVertexData :: ObjModel -> FaceVertex -> Interleaved
makeVertexData model (v, t, n) = Interleaved tu (-tv) nx ny nz vx vy vz
    where
      (vx, vy, vz) = rtf3 $ genericIndex (objVertices model) v
      (tu, tv) = rtf2 $ if isJust t then genericIndex (objTexCoords model) $ fromJust t else (0, 0)
      (nx, ny, nz) = rtf3 $ if isJust n then genericIndex (objNormals model) $ fromJust n else (0, 0, 0)
      rtf2 (x, y) = (realToFrac x, realToFrac y)
      rtf3 (x, y, z) = (realToFrac x, realToFrac y, realToFrac z)


interleaveObj :: ObjModel -> ([Interleaved], [[Integer]])
interleaveObj model = evalState interleaveObj' (0, M.empty)
    where
      interleaveObj' :: Interleaver ([Interleaved], [[Integer]])
      interleaveObj' = do
        indices <- forM (objFaces model) $ mapM getIndex . concatMap (\(v1, v2, v3) -> [v1, v2, v3])
        
        packedVertices <- gets $ map fst . sortBy (compare `on` snd) . M.toList . snd
        
        return (map (makeVertexData model) packedVertices, indices)

makeInterleavedArrays :: ObjModel -> IO InterleavedObj
makeInterleavedArrays model = do
  let (vertexdata, indices) = interleaveObj model
  
  interleavedptr <- mallocForeignPtrArray $ length vertexdata
  withForeignPtr interleavedptr $ \ptr -> pokeArray ptr vertexdata
  
  indexptrs <- forM indices $ \objindices -> do
                                indexptr <- mallocForeignPtrArray $ length objindices
                                withForeignPtr indexptr $ \ptr -> pokeArray ptr $ map fromIntegral objindices
                                return (indexptr, length objindices)
  
  return (interleavedptr, indexptrs)

bindInterleavedArrays :: InterleavedObj -> IO ()
bindInterleavedArrays (obj, _) = withForeignPtr obj $ interleavedArrays interleavedArrayFormat 0

drawObject :: InterleavedObj -> Int -> IO ()
drawObject (_, bufs) i = withForeignPtr buf $ drawElements Triangles (fromIntegral l) indexArrayFormat
    where
      (buf, l) = bufs!!i