diff options
| -rw-r--r-- | Demos/Pong/Main.hs | 18 | ||||
| -rw-r--r-- | Spear/App.hs | 26 | ||||
| -rw-r--r-- | Spear/Assets/Image.hsc | 18 | ||||
| -rw-r--r-- | Spear/Assets/Model.hsc | 60 | ||||
| -rw-r--r-- | Spear/GL.hs | 42 | ||||
| -rw-r--r-- | Spear/Game.hs | 172 | ||||
| -rw-r--r-- | Spear/Render/AnimatedModel.hs | 13 | ||||
| -rw-r--r-- | Spear/Render/Core/Buffer.hs | 10 | ||||
| -rw-r--r-- | Spear/Render/Core/Geometry.hs | 12 | ||||
| -rw-r--r-- | Spear/Render/Core/Shader.hs | 32 | ||||
| -rw-r--r-- | Spear/Render/Core/State.hs | 8 | ||||
| -rw-r--r-- | Spear/Render/Immediate.hs | 38 | ||||
| -rw-r--r-- | Spear/Render/StaticModel.hs | 13 | ||||
| -rw-r--r-- | Spear/Scene/Loader.hs | 14 | ||||
| -rw-r--r-- | Spear/Window.hs | 6 |
15 files changed, 238 insertions, 244 deletions
diff --git a/Demos/Pong/Main.hs b/Demos/Pong/Main.hs index de8e6f2..df90020 100644 --- a/Demos/Pong/Main.hs +++ b/Demos/Pong/Main.hs | |||
| @@ -41,16 +41,16 @@ initGame window = do | |||
| 41 | 41 | ||
| 42 | endGame :: Game GameState () | 42 | endGame :: Game GameState () |
| 43 | endGame = do | 43 | endGame = do |
| 44 | game <- getGameState | 44 | game <- get |
| 45 | runSubGame' (deleteImmRenderer $ immRenderState game) (renderCoreState game) | 45 | runSubGame' (deleteImmRenderer $ immRenderState game) (renderCoreState game) |
| 46 | 46 | ||
| 47 | 47 | ||
| 48 | step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool | 48 | step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool |
| 49 | step elapsed dt inputEvents = do | 49 | step elapsed dt inputEvents = do |
| 50 | gs <- getGameState | 50 | gs <- get |
| 51 | events <- processInput (window gs) | 51 | events <- processInput (window gs) |
| 52 | --when (events /= []) $ gameIO . putStrLn $ show events | 52 | --when (events /= []) $ liftIO . putStrLn $ show events |
| 53 | modifyGameState $ \gs -> | 53 | modify $ \gs -> |
| 54 | gs | 54 | gs |
| 55 | { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gs) | 55 | { world = stepWorld (realToFrac elapsed) (realToFrac dt) events (world gs) |
| 56 | } | 56 | } |
| @@ -67,18 +67,18 @@ exitRequested = elem (KeyDown KEY_ESC) | |||
| 67 | 67 | ||
| 68 | render :: Game GameState () | 68 | render :: Game GameState () |
| 69 | render = do | 69 | render = do |
| 70 | gameState <- getGameState | 70 | gameState <- get |
| 71 | immRenderState' <- flip execSubGame (immRenderState gameState) $ do | 71 | immRenderState' <- flip execSubGame (immRenderState gameState) $ do |
| 72 | immStart | 72 | immStart |
| 73 | immSetViewProjectionMatrix (viewProjection gameState) | 73 | immSetViewProjectionMatrix (viewProjection gameState) |
| 74 | -- Clear the background to a different colour than the playable area to make | 74 | -- Clear the background to a different colour than the playable area to make |
| 75 | -- the latter distinguishable. | 75 | -- the latter distinguishable. |
| 76 | gameIO $ do | 76 | liftIO $ do |
| 77 | setClearColour (0.2, 0.2, 0.2, 0.0) | 77 | setClearColour (0.2, 0.2, 0.2, 0.0) |
| 78 | clearBuffers [ColourBuffer] | 78 | clearBuffers [ColourBuffer] |
| 79 | render' $ world gameState | 79 | render' $ world gameState |
| 80 | immEnd | 80 | immEnd |
| 81 | saveGameState $ gameState { immRenderState = immRenderState' } | 81 | put $ gameState { immRenderState = immRenderState' } |
| 82 | 82 | ||
| 83 | render' :: [GameObject] -> Game ImmRenderState () | 83 | render' :: [GameObject] -> Game ImmRenderState () |
| 84 | render' world = do | 84 | render' world = do |
| @@ -122,7 +122,7 @@ resize (ResizeEvent w h) = | |||
| 122 | bottom = if r > 1 then 0 else -pad | 122 | bottom = if r > 1 then 0 else -pad |
| 123 | top = if r > 1 then 1 else 1 + pad | 123 | top = if r > 1 then 1 else 1 + pad |
| 124 | in do | 124 | in do |
| 125 | gameIO $ setViewport 0 0 w h | 125 | liftIO $ setViewport 0 0 w h |
| 126 | modifyGameState $ \state -> state { | 126 | modify $ \state -> state { |
| 127 | viewProjection = Matrix4.ortho left right bottom top (-1) 1 | 127 | viewProjection = Matrix4.ortho left right bottom top (-1) 1 |
| 128 | } | 128 | } |
diff --git a/Spear/App.hs b/Spear/App.hs index 61ea3b1..1520eee 100644 --- a/Spear/App.hs +++ b/Spear/App.hs | |||
| @@ -76,15 +76,15 @@ loop :: App s -> Window -> Game s () | |||
| 76 | loop app window = do | 76 | loop app window = do |
| 77 | -- For convenience, trigger an initial resize followed by a render of the | 77 | -- For convenience, trigger an initial resize followed by a render of the |
| 78 | -- application's initial state. | 78 | -- application's initial state. |
| 79 | (width, height) <- gameIO $ getWindowSize window | 79 | (width, height) <- liftIO $ getWindowSize window |
| 80 | resizeApp app (ResizeEvent width height) | 80 | resizeApp app (ResizeEvent width height) |
| 81 | renderApp app | 81 | renderApp app |
| 82 | 82 | ||
| 83 | let ddt = fpsToDdt . maxFPS . appOptions $ app -- Desired render time step. | 83 | let ddt = fpsToDdt . maxFPS . appOptions $ app -- Desired render time step. |
| 84 | let animationDdt = fpsToDdt . animationFPS . appOptions $ app -- Desired animation time step. | 84 | let animationDdt = fpsToDdt . animationFPS . appOptions $ app -- Desired animation time step. |
| 85 | 85 | ||
| 86 | timer <- gameIO newTimer | 86 | timer <- liftIO newTimer |
| 87 | gameIO $ Timer.start timer | 87 | liftIO $ Timer.start timer |
| 88 | let lastAnimationTime = lastTick timer | 88 | let lastAnimationTime = lastTick timer |
| 89 | loop' window ddt animationDdt lastAnimationTime timer app | 89 | loop' window ddt animationDdt lastAnimationTime timer app |
| 90 | 90 | ||
| @@ -97,16 +97,16 @@ loop' :: | |||
| 97 | App s -> | 97 | App s -> |
| 98 | Game s () | 98 | Game s () |
| 99 | loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do | 99 | loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do |
| 100 | timer <- gameIO $ tick inputTimer | 100 | timer <- liftIO $ tick inputTimer |
| 101 | windowEvents <- gameIO $ pollWindowEvents window | 101 | windowEvents <- liftIO $ pollWindowEvents window |
| 102 | close <- gameIO $ shouldWindowClose window | 102 | close <- liftIO $ shouldWindowClose window |
| 103 | 103 | ||
| 104 | (continue, lastAnimationTimeNextFrame) <- case animationDdt of | 104 | (continue, lastAnimationTimeNextFrame) <- case animationDdt of |
| 105 | 0 -> do | 105 | 0 -> do |
| 106 | -- Variable time step game animation. | 106 | -- Variable time step game animation. |
| 107 | let t = timeDeltaToSec $ runningTime timer | 107 | let t = timeDeltaToSec $ runningTime timer |
| 108 | let dt = timeDeltaToSec $ deltaTime timer | 108 | let dt = timeDeltaToSec $ deltaTime timer |
| 109 | inputEvents <- gameIO $ pollInputEvents window | 109 | inputEvents <- liftIO $ pollInputEvents window |
| 110 | continue <- stepApp app t dt inputEvents | 110 | continue <- stepApp app t dt inputEvents |
| 111 | return (continue, lastAnimationTime) | 111 | return (continue, lastAnimationTime) |
| 112 | 112 | ||
| @@ -118,15 +118,15 @@ loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do | |||
| 118 | let timeBudgetThisFrame = timeBudget + deltaTime timer | 118 | let timeBudgetThisFrame = timeBudget + deltaTime timer |
| 119 | let timeBudgetNextFrame = timeBudgetThisFrame `mod` ddt | 119 | let timeBudgetNextFrame = timeBudgetThisFrame `mod` ddt |
| 120 | let steps = timeBudgetThisFrame `div` ddt -} | 120 | let steps = timeBudgetThisFrame `div` ddt -} |
| 121 | --gameIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt | 121 | --liftIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt |
| 122 | let elapsed = runningTime timer | 122 | let elapsed = runningTime timer |
| 123 | let dt = timeDeltaToSec ddt | 123 | let dt = timeDeltaToSec ddt |
| 124 | let timeBudgetThisFrame = timeDiff lastAnimationTime (lastTick timer) | 124 | let timeBudgetThisFrame = timeDiff lastAnimationTime (lastTick timer) |
| 125 | let steps = timeBudgetThisFrame `div` ddt | 125 | let steps = timeBudgetThisFrame `div` ddt |
| 126 | let lastAnimationTimeNextFrame = timeAdd lastAnimationTime (steps * ddt) | 126 | let lastAnimationTimeNextFrame = timeAdd lastAnimationTime (steps * ddt) |
| 127 | --gameIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt | 127 | --liftIO . print $ "Steps: " ++ show steps ++ ", Budget: " ++ show timeBudgetThisFrame ++ ", ddt: " ++ show ddt |
| 128 | continue <- and <$> forM [1..steps] (\i -> do | 128 | continue <- and <$> forM [1..steps] (\i -> do |
| 129 | inputEvents <- gameIO $ pollInputEvents window | 129 | inputEvents <- liftIO $ pollInputEvents window |
| 130 | let t = timeDeltaToSec $ elapsed + i * ddt | 130 | let t = timeDeltaToSec $ elapsed + i * ddt |
| 131 | stepApp app t dt inputEvents) | 131 | stepApp app t dt inputEvents) |
| 132 | return (continue, lastAnimationTimeNextFrame) | 132 | return (continue, lastAnimationTimeNextFrame) |
| @@ -138,16 +138,16 @@ loop' window renderDdt animationDdt lastAnimationTime inputTimer app = do | |||
| 138 | -- For smoother resizing, render only while not resizing. | 138 | -- For smoother resizing, render only while not resizing. |
| 139 | unless resized $ do | 139 | unless resized $ do |
| 140 | renderApp app | 140 | renderApp app |
| 141 | gameIO $ swapBuffers window | 141 | liftIO $ swapBuffers window |
| 142 | 142 | ||
| 143 | -- Limit frame rate if so requested by the application. | 143 | -- Limit frame rate if so requested by the application. |
| 144 | -- This currently makes the rendering stutter and is not very desirable. | 144 | -- This currently makes the rendering stutter and is not very desirable. |
| 145 | when ((maxFPS . appOptions $ app) > 0) $ do | 145 | when ((maxFPS . appOptions $ app) > 0) $ do |
| 146 | frameEnd <- gameIO now | 146 | frameEnd <- liftIO now |
| 147 | let ddt = renderDdt | 147 | let ddt = renderDdt |
| 148 | let frameTime = timeDiff (lastTick timer) frameEnd | 148 | let frameTime = timeDiff (lastTick timer) frameEnd |
| 149 | when (frameTime < ddt) $ do | 149 | when (frameTime < ddt) $ do |
| 150 | gameIO $ Timer.sleep (ddt - frameTime) | 150 | liftIO $ Timer.sleep (ddt - frameTime) |
| 151 | 151 | ||
| 152 | when (continue && not close) $ do | 152 | when (continue && not close) $ do |
| 153 | loop' window renderDdt animationDdt lastAnimationTimeNextFrame timer app | 153 | loop' window renderDdt animationDdt lastAnimationTimeNextFrame timer app |
diff --git a/Spear/Assets/Image.hsc b/Spear/Assets/Image.hsc index f9fc025..db90afe 100644 --- a/Spear/Assets/Image.hsc +++ b/Spear/Assets/Image.hsc | |||
| @@ -46,24 +46,24 @@ data CImage = CImage | |||
| 46 | instance Storable CImage where | 46 | instance Storable CImage where |
| 47 | sizeOf _ = #{size Image} | 47 | sizeOf _ = #{size Image} |
| 48 | alignment _ = alignment (undefined :: CInt) | 48 | alignment _ = alignment (undefined :: CInt) |
| 49 | 49 | ||
| 50 | peek ptr = do | 50 | peek ptr = do |
| 51 | width <- #{peek Image, width} ptr | 51 | width <- #{peek Image, width} ptr |
| 52 | height <- #{peek Image, height} ptr | 52 | height <- #{peek Image, height} ptr |
| 53 | bpp <- #{peek Image, bpp} ptr | 53 | bpp <- #{peek Image, bpp} ptr |
| 54 | pixels <- #{peek Image, pixels} ptr | 54 | pixels <- #{peek Image, pixels} ptr |
| 55 | return $ CImage width height bpp pixels | 55 | return $ CImage width height bpp pixels |
| 56 | 56 | ||
| 57 | poke ptr (CImage width height bpp pixels) = do | 57 | poke ptr (CImage width height bpp pixels) = do |
| 58 | #{poke Image, width} ptr width | 58 | #{poke Image, width} ptr width |
| 59 | #{poke Image, height} ptr height | 59 | #{poke Image, height} ptr height |
| 60 | #{poke Image, bpp} ptr bpp | 60 | #{poke Image, bpp} ptr bpp |
| 61 | #{poke Image, pixels} ptr pixels | 61 | #{poke Image, pixels} ptr pixels |
| 62 | 62 | ||
| 63 | -- | Represents an image 'Resource'. | 63 | -- | An image resource. |
| 64 | data Image = Image | 64 | data Image = Image |
| 65 | { imageData :: CImage | 65 | { imageData :: CImage |
| 66 | , rkey :: Resource | 66 | , rkey :: ReleaseKey |
| 67 | } | 67 | } |
| 68 | 68 | ||
| 69 | instance ResourceClass Image where | 69 | instance ResourceClass Image where |
| @@ -84,15 +84,15 @@ loadImage file = do | |||
| 84 | dotPos <- case elemIndex '.' file of | 84 | dotPos <- case elemIndex '.' file of |
| 85 | Nothing -> gameError $ "file name has no extension: " ++ file | 85 | Nothing -> gameError $ "file name has no extension: " ++ file |
| 86 | Just p -> return p | 86 | Just p -> return p |
| 87 | 87 | ||
| 88 | let ext = map toLower . tail . snd $ splitAt dotPos file | 88 | let ext = map toLower . tail . snd $ splitAt dotPos file |
| 89 | 89 | ||
| 90 | result <- gameIO . alloca $ \ptr -> do | 90 | result <- liftIO . alloca $ \ptr -> do |
| 91 | status <- withCString file $ \fileCstr -> do | 91 | status <- withCString file $ \fileCstr -> do |
| 92 | case ext of | 92 | case ext of |
| 93 | "bmp" -> bmp_load fileCstr ptr | 93 | "bmp" -> bmp_load fileCstr ptr |
| 94 | _ -> return ImageNoSuitableLoader | 94 | _ -> return ImageNoSuitableLoader |
| 95 | 95 | ||
| 96 | case status of | 96 | case status of |
| 97 | ImageSuccess -> peek ptr >>= return . Right | 97 | ImageSuccess -> peek ptr >>= return . Right |
| 98 | ImageReadError -> return . Left $ "read error" | 98 | ImageReadError -> return . Left $ "read error" |
| @@ -100,7 +100,7 @@ loadImage file = do | |||
| 100 | ImageFileNotFound -> return . Left $ "file not found" | 100 | ImageFileNotFound -> return . Left $ "file not found" |
| 101 | ImageInvalidFormat -> return . Left $ "invalid format" | 101 | ImageInvalidFormat -> return . Left $ "invalid format" |
| 102 | ImageNoSuitableLoader -> return . Left $ "no suitable loader for extension " ++ ext | 102 | ImageNoSuitableLoader -> return . Left $ "no suitable loader for extension " ++ ext |
| 103 | 103 | ||
| 104 | case result of | 104 | case result of |
| 105 | Right image -> register (freeImage image) >>= return . Image image | 105 | Right image -> register (freeImage image) >>= return . Image image |
| 106 | Left err -> gameError $ "loadImage: " ++ err | 106 | Left err -> gameError $ "loadImage: " ++ err |
diff --git a/Spear/Assets/Model.hsc b/Spear/Assets/Model.hsc index 74666f2..02e1edf 100644 --- a/Spear/Assets/Model.hsc +++ b/Spear/Assets/Model.hsc | |||
| @@ -65,12 +65,12 @@ data Vec2 = Vec2 {-# UNPACK #-} !Float {-# UNPACK #-} !Float | |||
| 65 | instance Storable Vec2 where | 65 | instance Storable Vec2 where |
| 66 | sizeOf _ = 2*sizeFloat | 66 | sizeOf _ = 2*sizeFloat |
| 67 | alignment _ = alignment (undefined :: CFloat) | 67 | alignment _ = alignment (undefined :: CFloat) |
| 68 | 68 | ||
| 69 | peek ptr = do | 69 | peek ptr = do |
| 70 | f0 <- peekByteOff ptr 0 | 70 | f0 <- peekByteOff ptr 0 |
| 71 | f1 <- peekByteOff ptr sizeFloat | 71 | f1 <- peekByteOff ptr sizeFloat |
| 72 | return $ Vec2 f0 f1 | 72 | return $ Vec2 f0 f1 |
| 73 | 73 | ||
| 74 | poke ptr (Vec2 f0 f1) = do | 74 | poke ptr (Vec2 f0 f1) = do |
| 75 | pokeByteOff ptr 0 f0 | 75 | pokeByteOff ptr 0 f0 |
| 76 | pokeByteOff ptr sizeFloat f1 | 76 | pokeByteOff ptr sizeFloat f1 |
| @@ -81,13 +81,13 @@ data Vec3 = Vec3 {-# UNPACK #-} !Float {-# UNPACK #-} !Float {-# UNPACK #-} !Flo | |||
| 81 | instance Storable Vec3 where | 81 | instance Storable Vec3 where |
| 82 | sizeOf _ = 3*sizeFloat | 82 | sizeOf _ = 3*sizeFloat |
| 83 | alignment _ = alignment (undefined :: CFloat) | 83 | alignment _ = alignment (undefined :: CFloat) |
| 84 | 84 | ||
| 85 | peek ptr = do | 85 | peek ptr = do |
| 86 | f0 <- peekByteOff ptr 0 | 86 | f0 <- peekByteOff ptr 0 |
| 87 | f1 <- peekByteOff ptr sizeFloat | 87 | f1 <- peekByteOff ptr sizeFloat |
| 88 | f2 <- peekByteOff ptr (2*sizeFloat) | 88 | f2 <- peekByteOff ptr (2*sizeFloat) |
| 89 | return $ Vec3 f0 f1 f2 | 89 | return $ Vec3 f0 f1 f2 |
| 90 | 90 | ||
| 91 | poke ptr (Vec3 f0 f1 f2) = do | 91 | poke ptr (Vec3 f0 f1 f2) = do |
| 92 | pokeByteOff ptr 0 f0 | 92 | pokeByteOff ptr 0 f0 |
| 93 | pokeByteOff ptr sizeFloat f1 | 93 | pokeByteOff ptr sizeFloat f1 |
| @@ -99,12 +99,12 @@ data TexCoord = TexCoord {-# UNPACK #-} !Float {-# UNPACK #-} !Float | |||
| 99 | instance Storable TexCoord where | 99 | instance Storable TexCoord where |
| 100 | sizeOf _ = 2*sizeFloat | 100 | sizeOf _ = 2*sizeFloat |
| 101 | alignment _ = alignment (undefined :: CFloat) | 101 | alignment _ = alignment (undefined :: CFloat) |
| 102 | 102 | ||
| 103 | peek ptr = do | 103 | peek ptr = do |
| 104 | f0 <- peekByteOff ptr 0 | 104 | f0 <- peekByteOff ptr 0 |
| 105 | f1 <- peekByteOff ptr sizeFloat | 105 | f1 <- peekByteOff ptr sizeFloat |
| 106 | return $ TexCoord f0 f1 | 106 | return $ TexCoord f0 f1 |
| 107 | 107 | ||
| 108 | poke ptr (TexCoord f0 f1) = do | 108 | poke ptr (TexCoord f0 f1) = do |
| 109 | pokeByteOff ptr 0 f0 | 109 | pokeByteOff ptr 0 f0 |
| 110 | pokeByteOff ptr sizeFloat f1 | 110 | pokeByteOff ptr sizeFloat f1 |
| @@ -122,23 +122,23 @@ data CTriangle = CTriangle | |||
| 122 | instance Storable CTriangle where | 122 | instance Storable CTriangle where |
| 123 | sizeOf _ = #{size triangle} | 123 | sizeOf _ = #{size triangle} |
| 124 | alignment _ = alignment (undefined :: CUShort) | 124 | alignment _ = alignment (undefined :: CUShort) |
| 125 | 125 | ||
| 126 | peek ptr = do | 126 | peek ptr = do |
| 127 | v0 <- #{peek triangle, vertexIndices[0]} ptr | 127 | v0 <- #{peek triangle, vertexIndices[0]} ptr |
| 128 | v1 <- #{peek triangle, vertexIndices[1]} ptr | 128 | v1 <- #{peek triangle, vertexIndices[1]} ptr |
| 129 | v2 <- #{peek triangle, vertexIndices[2]} ptr | 129 | v2 <- #{peek triangle, vertexIndices[2]} ptr |
| 130 | 130 | ||
| 131 | t0 <- #{peek triangle, textureIndices[0]} ptr | 131 | t0 <- #{peek triangle, textureIndices[0]} ptr |
| 132 | t1 <- #{peek triangle, textureIndices[1]} ptr | 132 | t1 <- #{peek triangle, textureIndices[1]} ptr |
| 133 | t2 <- #{peek triangle, textureIndices[2]} ptr | 133 | t2 <- #{peek triangle, textureIndices[2]} ptr |
| 134 | 134 | ||
| 135 | return $ CTriangle v0 v1 v2 t0 t1 t2 | 135 | return $ CTriangle v0 v1 v2 t0 t1 t2 |
| 136 | 136 | ||
| 137 | poke ptr (CTriangle v0 v1 v2 t0 t1 t2) = do | 137 | poke ptr (CTriangle v0 v1 v2 t0 t1 t2) = do |
| 138 | #{poke triangle, vertexIndices[0]} ptr v0 | 138 | #{poke triangle, vertexIndices[0]} ptr v0 |
| 139 | #{poke triangle, vertexIndices[1]} ptr v1 | 139 | #{poke triangle, vertexIndices[1]} ptr v1 |
| 140 | #{poke triangle, vertexIndices[2]} ptr v2 | 140 | #{poke triangle, vertexIndices[2]} ptr v2 |
| 141 | 141 | ||
| 142 | #{poke triangle, textureIndices[0]} ptr t0 | 142 | #{poke triangle, textureIndices[0]} ptr t0 |
| 143 | #{poke triangle, textureIndices[1]} ptr t1 | 143 | #{poke triangle, textureIndices[1]} ptr t1 |
| 144 | #{poke triangle, textureIndices[2]} ptr t2 | 144 | #{poke triangle, textureIndices[2]} ptr t2 |
| @@ -149,7 +149,7 @@ data Box = Box {-# UNPACK #-} !Vec3 {-# UNPACK #-} !Vec3 | |||
| 149 | instance Storable Box where | 149 | instance Storable Box where |
| 150 | sizeOf _ = 6 * sizeFloat | 150 | sizeOf _ = 6 * sizeFloat |
| 151 | alignment _ = alignment (undefined :: CFloat) | 151 | alignment _ = alignment (undefined :: CFloat) |
| 152 | 152 | ||
| 153 | peek ptr = do | 153 | peek ptr = do |
| 154 | xmin <- peekByteOff ptr 0 | 154 | xmin <- peekByteOff ptr 0 |
| 155 | ymin <- peekByteOff ptr sizeFloat | 155 | ymin <- peekByteOff ptr sizeFloat |
| @@ -158,7 +158,7 @@ instance Storable Box where | |||
| 158 | ymax <- peekByteOff ptr $ 4*sizeFloat | 158 | ymax <- peekByteOff ptr $ 4*sizeFloat |
| 159 | zmax <- peekByteOff ptr $ 5*sizeFloat | 159 | zmax <- peekByteOff ptr $ 5*sizeFloat |
| 160 | return $ Box (Vec3 xmin ymin zmin) (Vec3 xmax ymax zmax) | 160 | return $ Box (Vec3 xmin ymin zmin) (Vec3 xmax ymax zmax) |
| 161 | 161 | ||
| 162 | poke ptr (Box (Vec3 xmin ymin zmin) (Vec3 xmax ymax zmax)) = do | 162 | poke ptr (Box (Vec3 xmin ymin zmin) (Vec3 xmax ymax zmax)) = do |
| 163 | pokeByteOff ptr 0 xmin | 163 | pokeByteOff ptr 0 xmin |
| 164 | pokeByteOff ptr sizeFloat ymin | 164 | pokeByteOff ptr sizeFloat ymin |
| @@ -173,11 +173,11 @@ newtype Skin = Skin { skinName :: B.ByteString } | |||
| 173 | instance Storable Skin where | 173 | instance Storable Skin where |
| 174 | sizeOf (Skin s) = 64 | 174 | sizeOf (Skin s) = 64 |
| 175 | alignment _ = 1 | 175 | alignment _ = 1 |
| 176 | 176 | ||
| 177 | peek ptr = do | 177 | peek ptr = do |
| 178 | s <- B.packCString $ unsafeCoerce ptr | 178 | s <- B.packCString $ unsafeCoerce ptr |
| 179 | return $ Skin s | 179 | return $ Skin s |
| 180 | 180 | ||
| 181 | poke ptr (Skin s) = do | 181 | poke ptr (Skin s) = do |
| 182 | B.useAsCStringLen s $ \(sptr, len) -> copyArray (unsafeCoerce ptr) sptr len | 182 | B.useAsCStringLen s $ \(sptr, len) -> copyArray (unsafeCoerce ptr) sptr len |
| 183 | 183 | ||
| @@ -193,13 +193,13 @@ data Animation = Animation | |||
| 193 | instance Storable Animation where | 193 | instance Storable Animation where |
| 194 | sizeOf _ = #{size animation} | 194 | sizeOf _ = #{size animation} |
| 195 | alignment _ = alignment (undefined :: CUInt) | 195 | alignment _ = alignment (undefined :: CUInt) |
| 196 | 196 | ||
| 197 | peek ptr = do | 197 | peek ptr = do |
| 198 | name <- B.packCString (unsafeCoerce ptr) | 198 | name <- B.packCString (unsafeCoerce ptr) |
| 199 | start <- #{peek animation, start} ptr | 199 | start <- #{peek animation, start} ptr |
| 200 | end <- #{peek animation, end} ptr | 200 | end <- #{peek animation, end} ptr |
| 201 | return $ Animation name start end | 201 | return $ Animation name start end |
| 202 | 202 | ||
| 203 | poke ptr (Animation name start end) = do | 203 | poke ptr (Animation name start end) = do |
| 204 | B.useAsCStringLen name $ \(sptr, len) -> copyArray (unsafeCoerce ptr) sptr len | 204 | B.useAsCStringLen name $ \(sptr, len) -> copyArray (unsafeCoerce ptr) sptr len |
| 205 | #{poke animation, start} ptr start | 205 | #{poke animation, start} ptr start |
| @@ -224,7 +224,7 @@ data Model = Model | |||
| 224 | instance Storable Model where | 224 | instance Storable Model where |
| 225 | sizeOf _ = #{size Model} | 225 | sizeOf _ = #{size Model} |
| 226 | alignment _ = alignment (undefined :: CUInt) | 226 | alignment _ = alignment (undefined :: CUInt) |
| 227 | 227 | ||
| 228 | peek ptr = do | 228 | peek ptr = do |
| 229 | numFrames <- #{peek Model, numFrames} ptr | 229 | numFrames <- #{peek Model, numFrames} ptr |
| 230 | numVertices <- #{peek Model, numVertices} ptr | 230 | numVertices <- #{peek Model, numVertices} ptr |
| @@ -232,7 +232,7 @@ instance Storable Model where | |||
| 232 | numTexCoords <- #{peek Model, numTexCoords} ptr | 232 | numTexCoords <- #{peek Model, numTexCoords} ptr |
| 233 | numSkins <- #{peek Model, numSkins} ptr | 233 | numSkins <- #{peek Model, numSkins} ptr |
| 234 | numAnimations <- #{peek Model, numAnimations} ptr | 234 | numAnimations <- #{peek Model, numAnimations} ptr |
| 235 | pVerts <- peek (unsafeCoerce ptr) | 235 | pVerts <- peek (unsafeCoerce ptr) |
| 236 | pNormals <- peekByteOff ptr sizePtr | 236 | pNormals <- peekByteOff ptr sizePtr |
| 237 | pTexCoords <- peekByteOff ptr (2*sizePtr) | 237 | pTexCoords <- peekByteOff ptr (2*sizePtr) |
| 238 | pTriangles <- peekByteOff ptr (3*sizePtr) | 238 | pTriangles <- peekByteOff ptr (3*sizePtr) |
| @@ -247,7 +247,7 @@ instance Storable Model where | |||
| 247 | return $ | 247 | return $ |
| 248 | Model vertices normals texCoords triangles skins animations | 248 | Model vertices normals texCoords triangles skins animations |
| 249 | numFrames numVertices numTriangles numTexCoords numSkins numAnimations | 249 | numFrames numVertices numTriangles numTexCoords numSkins numAnimations |
| 250 | 250 | ||
| 251 | poke ptr | 251 | poke ptr |
| 252 | (Model verts normals texCoords tris skins animations | 252 | (Model verts normals texCoords tris skins animations |
| 253 | numFrames numVerts numTris numTex numSkins numAnimations) = | 253 | numFrames numVerts numTris numTex numSkins numAnimations) = |
| @@ -288,7 +288,7 @@ data Triangle = Triangle | |||
| 288 | instance Storable Triangle where | 288 | instance Storable Triangle where |
| 289 | sizeOf _ = #{size model_triangle} | 289 | sizeOf _ = #{size model_triangle} |
| 290 | alignment _ = alignment (undefined :: Float) | 290 | alignment _ = alignment (undefined :: Float) |
| 291 | 291 | ||
| 292 | peek ptr = do | 292 | peek ptr = do |
| 293 | v0 <- #{peek model_triangle, v0} ptr | 293 | v0 <- #{peek model_triangle, v0} ptr |
| 294 | v1 <- #{peek model_triangle, v1} ptr | 294 | v1 <- #{peek model_triangle, v1} ptr |
| @@ -300,7 +300,7 @@ instance Storable Triangle where | |||
| 300 | t1 <- #{peek model_triangle, t1} ptr | 300 | t1 <- #{peek model_triangle, t1} ptr |
| 301 | t2 <- #{peek model_triangle, t2} ptr | 301 | t2 <- #{peek model_triangle, t2} ptr |
| 302 | return $ Triangle v0 v1 v2 n0 n1 n2 t0 t1 t2 | 302 | return $ Triangle v0 v1 v2 n0 n1 n2 t0 t1 t2 |
| 303 | 303 | ||
| 304 | poke ptr (Triangle v0 v1 v2 n0 n1 n2 t0 t1 t2) = do | 304 | poke ptr (Triangle v0 v1 v2 n0 n1 n2 t0 t1 t2) = do |
| 305 | #{poke model_triangle, v0} ptr v0 | 305 | #{poke model_triangle, v0} ptr v0 |
| 306 | #{poke model_triangle, v1} ptr v1 | 306 | #{poke model_triangle, v1} ptr v1 |
| @@ -335,16 +335,16 @@ loadModel file = do | |||
| 335 | dotPos <- case elemIndex '.' file of | 335 | dotPos <- case elemIndex '.' file of |
| 336 | Nothing -> gameError $ "file name has no extension: " ++ file | 336 | Nothing -> gameError $ "file name has no extension: " ++ file |
| 337 | Just p -> return p | 337 | Just p -> return p |
| 338 | 338 | ||
| 339 | let ext = map toLower . tail . snd $ splitAt dotPos file | 339 | let ext = map toLower . tail . snd $ splitAt dotPos file |
| 340 | 340 | ||
| 341 | result <- gameIO . alloca $ \ptr -> do | 341 | result <- liftIO . alloca $ \ptr -> do |
| 342 | status <- withCString file $ \fileCstr -> do | 342 | status <- withCString file $ \fileCstr -> do |
| 343 | case ext of | 343 | case ext of |
| 344 | "md2" -> md2_load fileCstr 0 0 ptr | 344 | "md2" -> md2_load fileCstr 0 0 ptr |
| 345 | "obj" -> obj_load fileCstr 0 0 ptr | 345 | "obj" -> obj_load fileCstr 0 0 ptr |
| 346 | _ -> return ModelNoSuitableLoader | 346 | _ -> return ModelNoSuitableLoader |
| 347 | 347 | ||
| 348 | case status of | 348 | case status of |
| 349 | ModelSuccess -> do | 349 | ModelSuccess -> do |
| 350 | model <- peek ptr | 350 | model <- peek ptr |
| @@ -355,7 +355,7 @@ loadModel file = do | |||
| 355 | ModelFileNotFound -> return . Left $ "file not found" | 355 | ModelFileNotFound -> return . Left $ "file not found" |
| 356 | ModelFileMismatch -> return . Left $ "file mismatch" | 356 | ModelFileMismatch -> return . Left $ "file mismatch" |
| 357 | ModelNoSuitableLoader -> return . Left $ "no suitable loader for extension " ++ ext | 357 | ModelNoSuitableLoader -> return . Left $ "no suitable loader for extension " ++ ext |
| 358 | 358 | ||
| 359 | case result of | 359 | case result of |
| 360 | Right model -> return model | 360 | Right model -> return model |
| 361 | Left err -> gameError $ "loadModel: " ++ err | 361 | Left err -> gameError $ "loadModel: " ++ err |
| @@ -392,7 +392,7 @@ transformVerts model f = model { vertices = vertices' } | |||
| 392 | where | 392 | where |
| 393 | n = numVerts model * numFrames model | 393 | n = numVerts model * numFrames model |
| 394 | vertices' = S.generate n f' | 394 | vertices' = S.generate n f' |
| 395 | f' i = f $ vertices model S.! i | 395 | f' i = f $ vertices model S.! i |
| 396 | 396 | ||
| 397 | -- | Transform the model's normals. | 397 | -- | Transform the model's normals. |
| 398 | transformNormals :: Model -> (Vec3 -> Vec3) -> Model | 398 | transformNormals :: Model -> (Vec3 -> Vec3) -> Model |
| @@ -400,14 +400,14 @@ transformNormals model f = model { normals = normals' } | |||
| 400 | where | 400 | where |
| 401 | n = numVerts model * numFrames model | 401 | n = numVerts model * numFrames model |
| 402 | normals' = S.generate n f' | 402 | normals' = S.generate n f' |
| 403 | f' i = f $ normals model S.! i | 403 | f' i = f $ normals model S.! i |
| 404 | 404 | ||
| 405 | -- | Translate the model such that its lowest point has y = 0. | 405 | -- | Translate the model such that its lowest point has y = 0. |
| 406 | toGround :: Model -> IO Model | 406 | toGround :: Model -> IO Model |
| 407 | toGround model = | 407 | toGround model = |
| 408 | let model' = model { vertices = S.generate n $ \i -> vertices model S.! i } | 408 | let model' = model { vertices = S.generate n $ \i -> vertices model S.! i } |
| 409 | n = numVerts model * numFrames model | 409 | n = numVerts model * numFrames model |
| 410 | in | 410 | in |
| 411 | with model' model_to_ground >> return model' | 411 | with model' model_to_ground >> return model' |
| 412 | 412 | ||
| 413 | foreign import ccall "Model.h model_to_ground" | 413 | foreign import ccall "Model.h model_to_ground" |
diff --git a/Spear/GL.hs b/Spear/GL.hs index f463109..3c1734b 100644 --- a/Spear/GL.hs +++ b/Spear/GL.hs | |||
| @@ -36,7 +36,7 @@ module Spear.GL | |||
| 36 | Data.StateVar.get, | 36 | Data.StateVar.get, |
| 37 | 37 | ||
| 38 | -- * VAOs | 38 | -- * VAOs |
| 39 | VAO, | 39 | VAO(..), |
| 40 | newVAO, | 40 | newVAO, |
| 41 | bindVAO, | 41 | bindVAO, |
| 42 | unbindVAO, | 42 | unbindVAO, |
| @@ -48,7 +48,7 @@ module Spear.GL | |||
| 48 | drawElements, | 48 | drawElements, |
| 49 | 49 | ||
| 50 | -- * Buffers | 50 | -- * Buffers |
| 51 | GLBuffer, | 51 | GLBuffer(..), |
| 52 | TargetBuffer (..), | 52 | TargetBuffer (..), |
| 53 | BufferUsage (..), | 53 | BufferUsage (..), |
| 54 | newBuffer, | 54 | newBuffer, |
| @@ -122,7 +122,7 @@ import Unsafe.Coerce | |||
| 122 | -- | A GLSL shader handle. | 122 | -- | A GLSL shader handle. |
| 123 | data GLSLShader = GLSLShader | 123 | data GLSLShader = GLSLShader |
| 124 | { getShader :: GLuint, | 124 | { getShader :: GLuint, |
| 125 | getShaderKey :: Resource | 125 | getShaderKey :: ReleaseKey |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | instance ResourceClass GLSLShader where | 128 | instance ResourceClass GLSLShader where |
| @@ -131,7 +131,7 @@ instance ResourceClass GLSLShader where | |||
| 131 | -- | A GLSL program handle. | 131 | -- | A GLSL program handle. |
| 132 | data GLSLProgram = GLSLProgram | 132 | data GLSLProgram = GLSLProgram |
| 133 | { getProgram :: GLuint, | 133 | { getProgram :: GLuint, |
| 134 | getProgramKey :: Resource | 134 | getProgramKey :: ReleaseKey |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | instance ResourceClass GLSLProgram where | 137 | instance ResourceClass GLSLProgram where |
| @@ -173,11 +173,11 @@ attribLocation prog var = makeStateVar get set | |||
| 173 | -- | Create a new program. | 173 | -- | Create a new program. |
| 174 | newProgram :: [GLSLShader] -> Game s GLSLProgram | 174 | newProgram :: [GLSLShader] -> Game s GLSLProgram |
| 175 | newProgram shaders = do | 175 | newProgram shaders = do |
| 176 | h <- gameIO glCreateProgram | 176 | h <- liftIO glCreateProgram |
| 177 | when (h == 0) $ gameError "glCreateProgram failed" | 177 | when (h == 0) $ gameError "glCreateProgram failed" |
| 178 | rkey <- register $ deleteProgram h | 178 | rkey <- register $ deleteProgram h |
| 179 | let program = GLSLProgram h rkey | 179 | let program = GLSLProgram h rkey |
| 180 | mapM_ (gameIO . attachShader program) shaders | 180 | mapM_ (liftIO . attachShader program) shaders |
| 181 | linkProgram program | 181 | linkProgram program |
| 182 | return program | 182 | return program |
| 183 | 183 | ||
| @@ -192,7 +192,7 @@ deleteProgram prog = do | |||
| 192 | linkProgram :: GLSLProgram -> Game s () | 192 | linkProgram :: GLSLProgram -> Game s () |
| 193 | linkProgram prog = do | 193 | linkProgram prog = do |
| 194 | let h = getProgram prog | 194 | let h = getProgram prog |
| 195 | err <- gameIO $ do | 195 | err <- liftIO $ do |
| 196 | glLinkProgram h | 196 | glLinkProgram h |
| 197 | alloca $ \statptr -> do | 197 | alloca $ \statptr -> do |
| 198 | glGetProgramiv h GL_LINK_STATUS statptr | 198 | glGetProgramiv h GL_LINK_STATUS statptr |
| @@ -235,7 +235,7 @@ loadShader shaderType file = do | |||
| 235 | -- | Create a new shader. | 235 | -- | Create a new shader. |
| 236 | newShader :: ShaderType -> Game s GLSLShader | 236 | newShader :: ShaderType -> Game s GLSLShader |
| 237 | newShader shaderType = do | 237 | newShader shaderType = do |
| 238 | h <- gameIO $ glCreateShader (toGLShader shaderType) | 238 | h <- liftIO $ glCreateShader (toGLShader shaderType) |
| 239 | case h of | 239 | case h of |
| 240 | 0 -> gameError "glCreateShader failed" | 240 | 0 -> gameError "glCreateShader failed" |
| 241 | _ -> do | 241 | _ -> do |
| @@ -253,10 +253,10 @@ deleteShader shader = do | |||
| 253 | -- into the shader. | 253 | -- into the shader. |
| 254 | loadSource :: FilePath -> GLSLShader -> Game s () | 254 | loadSource :: FilePath -> GLSLShader -> Game s () |
| 255 | loadSource file h = do | 255 | loadSource file h = do |
| 256 | exists <- gameIO $ doesFileExist file | 256 | exists <- liftIO $ doesFileExist file |
| 257 | case exists of | 257 | case exists of |
| 258 | False -> gameError "the specified shader file does not exist" | 258 | False -> gameError "the specified shader file does not exist" |
| 259 | True -> gameIO $ do | 259 | True -> liftIO $ do |
| 260 | code <- readSource file | 260 | code <- readSource file |
| 261 | withCString code $ shaderSource h | 261 | withCString code $ shaderSource h |
| 262 | 262 | ||
| @@ -272,10 +272,10 @@ compile file shader = do | |||
| 272 | let h = getShader shader | 272 | let h = getShader shader |
| 273 | 273 | ||
| 274 | -- Compile | 274 | -- Compile |
| 275 | gameIO $ glCompileShader h | 275 | liftIO $ glCompileShader h |
| 276 | 276 | ||
| 277 | -- Verify status | 277 | -- Verify status |
| 278 | err <- gameIO $ | 278 | err <- liftIO $ |
| 279 | alloca $ \statusPtr -> do | 279 | alloca $ \statusPtr -> do |
| 280 | glGetShaderiv h GL_COMPILE_STATUS statusPtr | 280 | glGetShaderiv h GL_COMPILE_STATUS statusPtr |
| 281 | result <- peek statusPtr | 281 | result <- peek statusPtr |
| @@ -438,7 +438,7 @@ instance Uniform [Int] where | |||
| 438 | -- | A vertex array object. | 438 | -- | A vertex array object. |
| 439 | data VAO = VAO | 439 | data VAO = VAO |
| 440 | { getVAO :: GLuint, | 440 | { getVAO :: GLuint, |
| 441 | vaoKey :: Resource | 441 | vaoKey :: ReleaseKey |
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | instance ResourceClass VAO where | 444 | instance ResourceClass VAO where |
| @@ -454,7 +454,7 @@ instance Ord VAO where | |||
| 454 | -- | Create a new vao. | 454 | -- | Create a new vao. |
| 455 | newVAO :: Game s VAO | 455 | newVAO :: Game s VAO |
| 456 | newVAO = do | 456 | newVAO = do |
| 457 | h <- gameIO . alloca $ \ptr -> do | 457 | h <- liftIO . alloca $ \ptr -> do |
| 458 | glGenVertexArrays 1 ptr | 458 | glGenVertexArrays 1 ptr |
| 459 | peek ptr | 459 | peek ptr |
| 460 | 460 | ||
| @@ -533,11 +533,11 @@ drawElements mode count t idxs = glDrawElements mode (unsafeCoerce count) t idxs | |||
| 533 | -- | An OpenGL buffer. | 533 | -- | An OpenGL buffer. |
| 534 | data GLBuffer = GLBuffer | 534 | data GLBuffer = GLBuffer |
| 535 | { getBuffer :: GLuint, | 535 | { getBuffer :: GLuint, |
| 536 | rkey :: Resource | 536 | bufferKey :: ReleaseKey |
| 537 | } | 537 | } |
| 538 | 538 | ||
| 539 | instance ResourceClass GLBuffer where | 539 | instance ResourceClass GLBuffer where |
| 540 | getResource = rkey | 540 | getResource = bufferKey |
| 541 | 541 | ||
| 542 | -- | The type of target buffer. | 542 | -- | The type of target buffer. |
| 543 | data TargetBuffer | 543 | data TargetBuffer |
| @@ -580,7 +580,7 @@ fromUsage DynamicCopy = GL_DYNAMIC_COPY | |||
| 580 | -- | Create a new buffer. | 580 | -- | Create a new buffer. |
| 581 | newBuffer :: Game s GLBuffer | 581 | newBuffer :: Game s GLBuffer |
| 582 | newBuffer = do | 582 | newBuffer = do |
| 583 | h <- gameIO . alloca $ \ptr -> do | 583 | h <- liftIO . alloca $ \ptr -> do |
| 584 | glGenBuffers 1 ptr | 584 | glGenBuffers 1 ptr |
| 585 | peek ptr | 585 | peek ptr |
| 586 | 586 | ||
| @@ -656,7 +656,7 @@ withGLBuffer buf f = f $ getBuffer buf | |||
| 656 | -- | Represents a texture resource. | 656 | -- | Represents a texture resource. |
| 657 | data Texture = Texture | 657 | data Texture = Texture |
| 658 | { getTex :: GLuint, | 658 | { getTex :: GLuint, |
| 659 | texKey :: Resource | 659 | texKey :: ReleaseKey |
| 660 | } | 660 | } |
| 661 | 661 | ||
| 662 | instance Eq Texture where | 662 | instance Eq Texture where |
| @@ -672,7 +672,7 @@ instance ResourceClass Texture where | |||
| 672 | -- | Create a new texture. | 672 | -- | Create a new texture. |
| 673 | newTexture :: Game s Texture | 673 | newTexture :: Game s Texture |
| 674 | newTexture = do | 674 | newTexture = do |
| 675 | tex <- gameIO . alloca $ \ptr -> do | 675 | tex <- liftIO . alloca $ \ptr -> do |
| 676 | glGenTextures 1 ptr | 676 | glGenTextures 1 ptr |
| 677 | peek ptr | 677 | peek ptr |
| 678 | 678 | ||
| @@ -697,7 +697,7 @@ loadTextureImage :: | |||
| 697 | loadTextureImage file minFilter magFilter = do | 697 | loadTextureImage file minFilter magFilter = do |
| 698 | image <- loadImage file | 698 | image <- loadImage file |
| 699 | tex <- newTexture | 699 | tex <- newTexture |
| 700 | gameIO $ do | 700 | liftIO $ do |
| 701 | let w = width image | 701 | let w = width image |
| 702 | h = height image | 702 | h = height image |
| 703 | pix = pixels image | 703 | pix = pixels image |
| @@ -794,7 +794,7 @@ printGLError = | |||
| 794 | assertGL :: Game s a -> String -> Game s a | 794 | assertGL :: Game s a -> String -> Game s a |
| 795 | assertGL action err = do | 795 | assertGL action err = do |
| 796 | result <- action | 796 | result <- action |
| 797 | status <- gameIO getGLError | 797 | status <- liftIO getGLError |
| 798 | case status of | 798 | case status of |
| 799 | Just str -> gameError $ "OpenGL error raised: " ++ err ++ "; " ++ str | 799 | Just str -> gameError $ "OpenGL error raised: " ++ err ++ "; " ++ str |
| 800 | Nothing -> return result | 800 | Nothing -> return result |
diff --git a/Spear/Game.hs b/Spear/Game.hs index 14e3f20..92cc680 100644 --- a/Spear/Game.hs +++ b/Spear/Game.hs | |||
| @@ -1,130 +1,130 @@ | |||
| 1 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
| 1 | module Spear.Game | 2 | module Spear.Game |
| 2 | ( Game, | 3 | ( Game |
| 3 | GameException (..), | 4 | , GameException (..) |
| 4 | Resource, | 5 | , ResourceClass (..) |
| 5 | ResourceClass (..), | 6 | , ReleaseKey |
| 6 | 7 | -- * Game state | |
| 7 | -- * Game state | 8 | , get |
| 8 | getGameState, | 9 | , put |
| 9 | saveGameState, | 10 | , modify |
| 10 | modifyGameState, | 11 | -- * Game resources |
| 11 | 12 | , register | |
| 12 | -- * Game resources | 13 | , release |
| 13 | register, | 14 | , release' |
| 14 | release, | 15 | -- * Error handling |
| 15 | release', | 16 | , gameError |
| 16 | 17 | , assertMaybe | |
| 17 | -- * Error handling | 18 | , catch |
| 18 | gameError, | 19 | -- * Running and IO |
| 19 | assertMaybe, | 20 | , runGame |
| 20 | catchGameError, | 21 | , evalGame |
| 21 | catchGameErrorFinally, | 22 | , runSubGame |
| 22 | 23 | , runSubGame' | |
| 23 | -- * Running and IO | 24 | , evalSubGame |
| 24 | runGame, | 25 | , execSubGame |
| 25 | evalGame, | 26 | , runSiblingGame |
| 26 | runSubGame, | 27 | , runSiblingGame' |
| 27 | runSubGame', | 28 | , evalSiblingGame |
| 28 | evalSubGame, | 29 | , execSiblingGame |
| 29 | execSubGame, | 30 | , liftIO |
| 30 | runSiblingGame, | 31 | ) |
| 31 | runSiblingGame', | ||
| 32 | evalSiblingGame, | ||
| 33 | execSiblingGame, | ||
| 34 | gameIO, | ||
| 35 | ) | ||
| 36 | where | 32 | where |
| 37 | 33 | ||
| 38 | import Control.Monad.Catch | 34 | import Control.Monad.Catch |
| 39 | import Control.Monad.State.Strict | 35 | import Control.Monad.State.Strict |
| 40 | import Control.Monad.Trans.Class (lift) | 36 | import Control.Monad.Trans.Class (lift) |
| 41 | import qualified Control.Monad.Trans.Resource as R | 37 | import Control.Monad.Trans.Resource |
| 42 | |||
| 43 | 38 | ||
| 44 | type Resource = R.ReleaseKey | ||
| 45 | 39 | ||
| 40 | -- | Anything that holds a resource. | ||
| 41 | -- | ||
| 42 | -- This is a convenient wrapper so that we can define the general `release'` | ||
| 43 | -- function on any type of resource. | ||
| 46 | class ResourceClass a where | 44 | class ResourceClass a where |
| 47 | getResource :: a -> Resource | 45 | getResource :: a -> ReleaseKey |
| 48 | 46 | ||
| 49 | type Game s = StateT s (R.ResourceT IO) | ||
| 50 | 47 | ||
| 48 | -- | A game exception. | ||
| 49 | -- | ||
| 50 | -- This is mostly a convenient wrapper around `String` so that we can throw | ||
| 51 | -- strings directly with `gameError`. | ||
| 51 | newtype GameException = GameException String deriving (Show) | 52 | newtype GameException = GameException String deriving (Show) |
| 52 | 53 | ||
| 53 | instance Exception GameException | 54 | instance Exception GameException |
| 54 | 55 | ||
| 55 | 56 | ||
| 56 | -- | Retrieve the game state. | 57 | -- | The game monad. |
| 57 | getGameState :: Game s s | 58 | -- |
| 58 | getGameState = get | 59 | -- The game monad performs three different roles: |
| 59 | 60 | -- | |
| 60 | -- | Save the game state. | 61 | -- 1. I/O |
| 61 | saveGameState :: s -> Game s () | 62 | -- 2. Resource management. |
| 62 | saveGameState = put | 63 | -- 3. State management. |
| 63 | 64 | newtype Game s a = Game { getGame :: StateT s (ResourceT IO) a } | |
| 64 | -- | Modify the game state. | 65 | deriving |
| 65 | modifyGameState :: (s -> s) -> Game s () | 66 | ( Functor |
| 66 | modifyGameState = modify | 67 | , Applicative |
| 67 | 68 | , Monad | |
| 68 | -- | Register the given cleaner. | 69 | , MonadIO |
| 69 | register :: IO () -> Game s Resource | 70 | , MonadThrow |
| 70 | register = lift . R.register | 71 | , MonadCatch |
| 72 | , MonadState s | ||
| 73 | , MonadResource | ||
| 74 | ) | ||
| 71 | 75 | ||
| 72 | -- | Release the given 'Resource'. | ||
| 73 | release :: ResourceClass a => a -> Game s () | ||
| 74 | release = lift . R.release . getResource | ||
| 75 | 76 | ||
| 76 | -- | Release the given 'Resource'. | 77 | -- | Release the given 'Resource'. |
| 77 | release' :: ResourceClass a => a -> IO () | 78 | release' :: ResourceClass a => a -> Game s () |
| 78 | release' = R.release . getResource | 79 | release' = release . getResource |
| 79 | 80 | ||
| 80 | -- | Throw an error from the 'Game' monad. | 81 | -- | Throw an error from the 'Game' monad. |
| 81 | gameError :: String -> Game s a | 82 | gameError :: String -> Game s a |
| 82 | gameError = gameError' . GameException | 83 | gameError = throwM . GameException |
| 83 | |||
| 84 | -- | Throw an error from the 'Game' monad. | ||
| 85 | gameError' :: GameException -> Game s a | ||
| 86 | gameError' = lift . lift . throwM | ||
| 87 | 84 | ||
| 88 | -- | Throw the given error if given 'Nothing'. | 85 | -- | Throw the given error if given 'Nothing'. |
| 89 | assertMaybe :: Maybe a -> GameException -> Game s a | 86 | assertMaybe :: Maybe a -> GameException -> Game s a |
| 90 | assertMaybe Nothing err = gameError' err | 87 | assertMaybe Nothing err = throwM err |
| 91 | assertMaybe (Just x) _ = return x | 88 | assertMaybe (Just x) _ = return x |
| 92 | 89 | ||
| 93 | -- | Run the given game with the given error handler. | 90 | -- | Run the given game, unrolling the full monad stack and returning the game's |
| 94 | catchGameError :: Game s a -> (GameException -> Game s a) -> Game s a | 91 | -- result and its final state. |
| 95 | catchGameError = catch | 92 | -- |
| 96 | 93 | -- Any resources acquired by the given game are released when this returns. | |
| 97 | -- | Run the given game, catch any error, run the given finaliser and rethrow the error. | ||
| 98 | catchGameErrorFinally :: Game s a -> Game s a -> Game s a | ||
| 99 | catchGameErrorFinally game finally = catch game $ \err -> finally >> gameError' err | ||
| 100 | |||
| 101 | -- | Run the given game. | ||
| 102 | runGame :: Game s a -> s -> IO (a, s) | 94 | runGame :: Game s a -> s -> IO (a, s) |
| 103 | runGame game = R.runResourceT . runStateT game | 95 | runGame game = runResourceT . runStateT (getGame game) |
| 104 | 96 | ||
| 105 | -- | Run the given game and return its result. | 97 | -- | Run the given game and return its result. |
| 106 | evalGame :: Game s a -> s -> IO a | 98 | evalGame :: Game s a -> s -> IO a |
| 107 | evalGame g s = fst <$> runGame g s | 99 | evalGame g s = fst <$> runGame g s |
| 108 | 100 | ||
| 109 | -- | Fully run the given sub game, unrolling the entire monad stack. | 101 | -- | Run the given sub-game, unrolling the full monad stack and returning the |
| 102 | -- game's result and its final state. | ||
| 103 | -- | ||
| 104 | -- Like `runGame`, this frees any resources that are acquired by the sub-game. | ||
| 105 | -- If you want to keep acquired resources, see `runSiblingGame` instead. | ||
| 110 | runSubGame :: Game s a -> s -> Game t (a, s) | 106 | runSubGame :: Game s a -> s -> Game t (a, s) |
| 111 | runSubGame g s = gameIO $ runGame g s | 107 | runSubGame g s = liftIO $ runGame g s |
| 108 | |||
| 109 | -- | Run the given sub-game and return its result. | ||
| 110 | evalSubGame :: Game s a -> s -> Game t a | ||
| 111 | evalSubGame g s = fst <$> runSubGame g s | ||
| 112 | 112 | ||
| 113 | -- | Like 'runSubGame', but discarding the result. | 113 | -- | Like 'runSubGame', but discarding the result. |
| 114 | runSubGame' :: Game s a -> s -> Game t () | 114 | runSubGame' :: Game s a -> s -> Game t () |
| 115 | runSubGame' g s = void $ runSubGame g s | 115 | runSubGame' g s = void $ runSubGame g s |
| 116 | 116 | ||
| 117 | -- | Run the given sub game and return its result. | 117 | -- | Run the given sub-game and return its state. |
| 118 | evalSubGame :: Game s a -> s -> Game t a | ||
| 119 | evalSubGame g s = fst <$> runSubGame g s | ||
| 120 | |||
| 121 | -- | Run the given sub game and return its state. | ||
| 122 | execSubGame :: Game s a -> s -> Game t s | 118 | execSubGame :: Game s a -> s -> Game t s |
| 123 | execSubGame g s = snd <$> runSubGame g s | 119 | execSubGame g s = snd <$> runSubGame g s |
| 124 | 120 | ||
| 125 | -- | Run the given sibling game, unrolling StateT but not ResourceT. | 121 | -- | Run the given sibling game, unrolling the state transformer but not the |
| 122 | -- resource transformer. | ||
| 123 | -- | ||
| 124 | -- Unlike `runSubGame`, any resources acquired by the sibling game are *not* | ||
| 125 | -- released. | ||
| 126 | runSiblingGame :: Game s a -> s -> Game t (a, s) | 126 | runSiblingGame :: Game s a -> s -> Game t (a, s) |
| 127 | runSiblingGame g s = lift $ runStateT g s | 127 | runSiblingGame game = Game . lift . runStateT (getGame game) |
| 128 | 128 | ||
| 129 | -- | Like 'runSiblingGame', but discarding the result. | 129 | -- | Like 'runSiblingGame', but discarding the result. |
| 130 | runSiblingGame' :: Game s a -> s -> Game t () | 130 | runSiblingGame' :: Game s a -> s -> Game t () |
| @@ -137,7 +137,3 @@ evalSiblingGame g s = fst <$> runSiblingGame g s | |||
| 137 | -- | Run the given sibling game and return its state. | 137 | -- | Run the given sibling game and return its state. |
| 138 | execSiblingGame :: Game s a -> s -> Game t s | 138 | execSiblingGame :: Game s a -> s -> Game t s |
| 139 | execSiblingGame g s = snd <$> runSiblingGame g s | 139 | execSiblingGame g s = snd <$> runSiblingGame g s |
| 140 | |||
| 141 | -- | Perform the given IO action in the 'Game' monad. | ||
| 142 | gameIO :: IO a -> Game s a | ||
| 143 | gameIO = lift . lift | ||
diff --git a/Spear/Render/AnimatedModel.hs b/Spear/Render/AnimatedModel.hs index 8f0d6bd..e5b29ec 100644 --- a/Spear/Render/AnimatedModel.hs +++ b/Spear/Render/AnimatedModel.hs | |||
| @@ -66,7 +66,7 @@ data AnimatedModelResource = AnimatedModelResource | |||
| 66 | material :: Material, | 66 | material :: Material, |
| 67 | texture :: Texture, | 67 | texture :: Texture, |
| 68 | boxes :: V.Vector Box, | 68 | boxes :: V.Vector Box, |
| 69 | rkey :: Resource | 69 | rkey :: ReleaseKey |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | instance Eq AnimatedModelResource where | 72 | instance Eq AnimatedModelResource where |
| @@ -121,12 +121,12 @@ animatedModelResource | |||
| 121 | material | 121 | material |
| 122 | texture | 122 | texture |
| 123 | model = do | 123 | model = do |
| 124 | RenderModel elements numFrames numVertices <- gameIO . renderModelFromModel $ model | 124 | RenderModel elements numFrames numVertices <- liftIO . renderModelFromModel $ model |
| 125 | elementBuf <- newBuffer | 125 | elementBuf <- newBuffer |
| 126 | vao <- newVAO | 126 | vao <- newVAO |
| 127 | boxes <- gameIO $ modelBoxes model | 127 | boxes <- liftIO $ modelBoxes model |
| 128 | 128 | ||
| 129 | gameIO $ do | 129 | liftIO $ do |
| 130 | let elemSize = 56::CUInt | 130 | let elemSize = 56::CUInt |
| 131 | elemSize' = fromIntegral elemSize | 131 | elemSize' = fromIntegral elemSize |
| 132 | n = numVertices * numFrames | 132 | n = numVertices * numFrames |
| @@ -149,9 +149,8 @@ animatedModelResource | |||
| 149 | enableVAOAttrib texChan | 149 | enableVAOAttrib texChan |
| 150 | 150 | ||
| 151 | rkey <- register $ do | 151 | rkey <- register $ do |
| 152 | putStrLn "Releasing animated model resource" | 152 | release $ vaoKey vao |
| 153 | release' vao | 153 | release $ bufferKey elementBuf |
| 154 | release' elementBuf | ||
| 155 | 154 | ||
| 156 | return $ | 155 | return $ |
| 157 | AnimatedModelResource | 156 | AnimatedModelResource |
diff --git a/Spear/Render/Core/Buffer.hs b/Spear/Render/Core/Buffer.hs index db3437e..eaff475 100644 --- a/Spear/Render/Core/Buffer.hs +++ b/Spear/Render/Core/Buffer.hs | |||
| @@ -53,19 +53,19 @@ makeBufferAndView desc = do | |||
| 53 | 53 | ||
| 54 | makeBuffer :: BufferDesc -> Game RenderCoreState Buffer | 54 | makeBuffer :: BufferDesc -> Game RenderCoreState Buffer |
| 55 | makeBuffer (BufferDesc usage bufferData) = do | 55 | makeBuffer (BufferDesc usage bufferData) = do |
| 56 | handle <- gameIO $ alloca $ \ptr -> glGenBuffers 1 ptr >> peek ptr | 56 | handle <- liftIO $ alloca $ \ptr -> glGenBuffers 1 ptr >> peek ptr |
| 57 | resourceKey <- register $ deleteBuffer' handle | 57 | resourceKey <- register $ deleteBuffer' handle |
| 58 | let buffer = Buffer handle resourceKey usage | 58 | let buffer = Buffer handle resourceKey usage |
| 59 | gameIO $ updateBuffer buffer bufferData | 59 | liftIO $ updateBuffer buffer bufferData |
| 60 | modifyGameState (\state -> state { | 60 | modify (\state -> state { |
| 61 | buffers = HashMap.insert handle buffer (buffers state) }) | 61 | buffers = HashMap.insert handle buffer (buffers state) }) |
| 62 | return buffer | 62 | return buffer |
| 63 | 63 | ||
| 64 | deleteBuffer :: Buffer -> Game RenderCoreState () | 64 | deleteBuffer :: Buffer -> Game RenderCoreState () |
| 65 | deleteBuffer buffer = do | 65 | deleteBuffer buffer = do |
| 66 | modifyGameState (\state -> state { | 66 | modify (\state -> state { |
| 67 | buffers = HashMap.delete (bufferHandle buffer) (buffers state) }) | 67 | buffers = HashMap.delete (bufferHandle buffer) (buffers state) }) |
| 68 | release buffer | 68 | release' buffer |
| 69 | 69 | ||
| 70 | -- TODO: use glBufferSubData for updates. | 70 | -- TODO: use glBufferSubData for updates. |
| 71 | updateBuffer :: Buffer -> BufferData -> IO () | 71 | updateBuffer :: Buffer -> BufferData -> IO () |
diff --git a/Spear/Render/Core/Geometry.hs b/Spear/Render/Core/Geometry.hs index 6c05b38..10ff709 100644 --- a/Spear/Render/Core/Geometry.hs +++ b/Spear/Render/Core/Geometry.hs | |||
| @@ -87,23 +87,23 @@ newGeometryDesc = GeometryDesc | |||
| 87 | makeGeometry :: GeometryDesc -> Game RenderCoreState Geometry | 87 | makeGeometry :: GeometryDesc -> Game RenderCoreState Geometry |
| 88 | makeGeometry desc = do | 88 | makeGeometry desc = do |
| 89 | gdata <- geometryDescToData desc | 89 | gdata <- geometryDescToData desc |
| 90 | handle <- gameIO $ alloca $ \ptr -> glGenVertexArrays 1 ptr >> peek ptr | 90 | handle <- liftIO $ alloca $ \ptr -> glGenVertexArrays 1 ptr >> peek ptr |
| 91 | gameIO $ do | 91 | liftIO $ do |
| 92 | glBindVertexArray handle | 92 | glBindVertexArray handle |
| 93 | configureVertexAttributes gdata | 93 | configureVertexAttributes gdata |
| 94 | glBindVertexArray 0 | 94 | glBindVertexArray 0 |
| 95 | gdataRef <- gameIO $ newIORef gdata | 95 | gdataRef <- liftIO $ newIORef gdata |
| 96 | resourceKey <- register $ deleteGeometry' handle | 96 | resourceKey <- register $ deleteGeometry' handle |
| 97 | let geometry = Geometry handle resourceKey gdataRef | 97 | let geometry = Geometry handle resourceKey gdataRef |
| 98 | modifyGameState (\state -> state { | 98 | modify (\state -> state { |
| 99 | geometries = HashMap.insert handle geometry (geometries state) }) | 99 | geometries = HashMap.insert handle geometry (geometries state) }) |
| 100 | return geometry | 100 | return geometry |
| 101 | 101 | ||
| 102 | deleteGeometry :: Geometry -> Game RenderCoreState () | 102 | deleteGeometry :: Geometry -> Game RenderCoreState () |
| 103 | deleteGeometry geometry = do | 103 | deleteGeometry geometry = do |
| 104 | modifyGameState (\state -> state { | 104 | modify (\state -> state { |
| 105 | geometries = HashMap.delete (geometryVao geometry) (geometries state) }) | 105 | geometries = HashMap.delete (geometryVao geometry) (geometries state) }) |
| 106 | release geometry | 106 | release' geometry |
| 107 | 107 | ||
| 108 | renderGeometry :: Geometry -> IO () | 108 | renderGeometry :: Geometry -> IO () |
| 109 | renderGeometry geometry = do | 109 | renderGeometry geometry = do |
diff --git a/Spear/Render/Core/Shader.hs b/Spear/Render/Core/Shader.hs index 4ed4430..21db66f 100644 --- a/Spear/Render/Core/Shader.hs +++ b/Spear/Render/Core/Shader.hs | |||
| @@ -53,22 +53,22 @@ compileShader :: ShaderDesc -> Game RenderCoreState Shader | |||
| 53 | compileShader (ShaderDesc shaderType source defines) = do | 53 | compileShader (ShaderDesc shaderType source defines) = do |
| 54 | code <- case source of | 54 | code <- case source of |
| 55 | ShaderFromString code -> return code | 55 | ShaderFromString code -> return code |
| 56 | ShaderFromFile file -> gameIO $ readFile file | 56 | ShaderFromFile file -> liftIO $ readFile file |
| 57 | state <- getGameState | 57 | state <- get |
| 58 | let shaderHash = hash code -- TODO: Should also include defines. | 58 | let shaderHash = hash code -- TODO: Should also include defines. |
| 59 | case HashMap.lookup shaderHash (shaders state) of | 59 | case HashMap.lookup shaderHash (shaders state) of |
| 60 | Just shader -> return shader | 60 | Just shader -> return shader |
| 61 | Nothing -> do | 61 | Nothing -> do |
| 62 | let definesString = makeDefinesString defines | 62 | let definesString = makeDefinesString defines |
| 63 | handle <- gameIO $ glCreateShader (toGLShaderType shaderType) | 63 | handle <- liftIO $ glCreateShader (toGLShaderType shaderType) |
| 64 | gameIO $ withCStringLen code $ \(codeCString, codeLen) -> | 64 | liftIO $ withCStringLen code $ \(codeCString, codeLen) -> |
| 65 | withCStringLen definesString $ \(definesCString, definesLen) -> | 65 | withCStringLen definesString $ \(definesCString, definesLen) -> |
| 66 | withCStringLen header $ \(headerCString, headerLen) -> | 66 | withCStringLen header $ \(headerCString, headerLen) -> |
| 67 | withArray [headerCString, definesCString, codeCString] $ \strPtrs -> | 67 | withArray [headerCString, definesCString, codeCString] $ \strPtrs -> |
| 68 | withArray (fromIntegral <$> [headerLen, definesLen, codeLen] :: [GLint]) | 68 | withArray (fromIntegral <$> [headerLen, definesLen, codeLen] :: [GLint]) |
| 69 | $ \lengths -> | 69 | $ \lengths -> |
| 70 | glShaderSource handle 3 strPtrs lengths | 70 | glShaderSource handle 3 strPtrs lengths |
| 71 | err <- gameIO $ do | 71 | err <- liftIO $ do |
| 72 | glCompileShader handle | 72 | glCompileShader handle |
| 73 | alloca $ \statusPtr -> do | 73 | alloca $ \statusPtr -> do |
| 74 | glGetShaderiv handle GL_COMPILE_STATUS statusPtr | 74 | glGetShaderiv handle GL_COMPILE_STATUS statusPtr |
| @@ -87,7 +87,7 @@ compileShader (ShaderDesc shaderType source defines) = do | |||
| 87 | Nothing -> do | 87 | Nothing -> do |
| 88 | resourceKey <- register $ deleteShader' handle | 88 | resourceKey <- register $ deleteShader' handle |
| 89 | let shader = Shader handle resourceKey shaderType shaderHash | 89 | let shader = Shader handle resourceKey shaderType shaderHash |
| 90 | saveGameState $ state { | 90 | put $ state { |
| 91 | shaders = HashMap.insert shaderHash shader (shaders state) | 91 | shaders = HashMap.insert shaderHash shader (shaders state) |
| 92 | } | 92 | } |
| 93 | return shader | 93 | return shader |
| @@ -96,17 +96,17 @@ compileShader (ShaderDesc shaderType source defines) = do | |||
| 96 | 96 | ||
| 97 | compileShaderProgram :: [Shader] -> Game RenderCoreState ShaderProgram | 97 | compileShaderProgram :: [Shader] -> Game RenderCoreState ShaderProgram |
| 98 | compileShaderProgram shaders = do | 98 | compileShaderProgram shaders = do |
| 99 | state <- getGameState | 99 | state <- get |
| 100 | let programHash = hashShaders shaders | 100 | let programHash = hashShaders shaders |
| 101 | case HashMap.lookup programHash (shaderPrograms state) of | 101 | case HashMap.lookup programHash (shaderPrograms state) of |
| 102 | Just program -> return program | 102 | Just program -> return program |
| 103 | Nothing -> do | 103 | Nothing -> do |
| 104 | handle <- gameIO glCreateProgram | 104 | handle <- liftIO glCreateProgram |
| 105 | case handle of | 105 | case handle of |
| 106 | 0 -> gameError "Failed to create shader program" | 106 | 0 -> gameError "Failed to create shader program" |
| 107 | _ -> do | 107 | _ -> do |
| 108 | mapM_ (gameIO . glAttachShader handle) (shaderHandle <$> shaders) | 108 | mapM_ (liftIO . glAttachShader handle) (shaderHandle <$> shaders) |
| 109 | err <- gameIO $ do | 109 | err <- liftIO $ do |
| 110 | glLinkProgram handle | 110 | glLinkProgram handle |
| 111 | alloca $ \statusPtr -> do | 111 | alloca $ \statusPtr -> do |
| 112 | glGetProgramiv handle GL_LINK_STATUS statusPtr | 112 | glGetProgramiv handle GL_LINK_STATUS statusPtr |
| @@ -124,9 +124,9 @@ compileShaderProgram shaders = do | |||
| 124 | case err of | 124 | case err of |
| 125 | Nothing -> do | 125 | Nothing -> do |
| 126 | resourceKey <- register $ deleteShaderProgram' handle | 126 | resourceKey <- register $ deleteShaderProgram' handle |
| 127 | uniforms <- gameIO $ newIORef [] | 127 | uniforms <- liftIO $ newIORef [] |
| 128 | let program = ShaderProgram handle resourceKey programHash uniforms | 128 | let program = ShaderProgram handle resourceKey programHash uniforms |
| 129 | saveGameState $ state { | 129 | put $ state { |
| 130 | shaderPrograms = HashMap.insert programHash program (shaderPrograms state) | 130 | shaderPrograms = HashMap.insert programHash program (shaderPrograms state) |
| 131 | } | 131 | } |
| 132 | return program | 132 | return program |
| @@ -136,15 +136,15 @@ compileShaderProgram shaders = do | |||
| 136 | 136 | ||
| 137 | deleteShader :: Shader -> Game RenderCoreState () | 137 | deleteShader :: Shader -> Game RenderCoreState () |
| 138 | deleteShader shader = do | 138 | deleteShader shader = do |
| 139 | modifyGameState (\state -> state { | 139 | modify (\state -> state { |
| 140 | shaders = HashMap.delete (shaderHash shader) (shaders state) }) | 140 | shaders = HashMap.delete (shaderHash shader) (shaders state) }) |
| 141 | release shader | 141 | release' shader |
| 142 | 142 | ||
| 143 | deleteShaderProgram :: ShaderProgram -> Game RenderCoreState () | 143 | deleteShaderProgram :: ShaderProgram -> Game RenderCoreState () |
| 144 | deleteShaderProgram program = do | 144 | deleteShaderProgram program = do |
| 145 | modifyGameState (\state -> state { | 145 | modify (\state -> state { |
| 146 | shaderPrograms = HashMap.delete (shaderProgramHash program) (shaderPrograms state)}) | 146 | shaderPrograms = HashMap.delete (shaderProgramHash program) (shaderPrograms state)}) |
| 147 | release program | 147 | release' program |
| 148 | 148 | ||
| 149 | activateShaderProgram :: ShaderProgram -> IO () | 149 | activateShaderProgram :: ShaderProgram -> IO () |
| 150 | activateShaderProgram program = do | 150 | activateShaderProgram program = do |
diff --git a/Spear/Render/Core/State.hs b/Spear/Render/Core/State.hs index dac7b9a..aa42635 100644 --- a/Spear/Render/Core/State.hs +++ b/Spear/Render/Core/State.hs | |||
| @@ -18,7 +18,7 @@ data BufferUsage | |||
| 18 | -- | A data buffer (e.g., vertex attributes, indices). | 18 | -- | A data buffer (e.g., vertex attributes, indices). |
| 19 | data Buffer = Buffer | 19 | data Buffer = Buffer |
| 20 | { bufferHandle :: GLuint | 20 | { bufferHandle :: GLuint |
| 21 | , bufferResource :: Resource | 21 | , bufferResource :: ReleaseKey |
| 22 | , bufferUsage :: BufferUsage | 22 | , bufferUsage :: BufferUsage |
| 23 | } | 23 | } |
| 24 | 24 | ||
| @@ -72,7 +72,7 @@ data GeometryData = GeometryData | |||
| 72 | -- its state cannot become stale after an update. | 72 | -- its state cannot become stale after an update. |
| 73 | data Geometry = Geometry | 73 | data Geometry = Geometry |
| 74 | { geometryVao :: GLuint | 74 | { geometryVao :: GLuint |
| 75 | , geometryResource :: Resource | 75 | , geometryResource :: ReleaseKey |
| 76 | , geometryData :: IORef GeometryData | 76 | , geometryData :: IORef GeometryData |
| 77 | } | 77 | } |
| 78 | 78 | ||
| @@ -80,7 +80,7 @@ data Geometry = Geometry | |||
| 80 | -- | A shader. | 80 | -- | A shader. |
| 81 | data Shader = Shader | 81 | data Shader = Shader |
| 82 | { shaderHandle :: GLuint | 82 | { shaderHandle :: GLuint |
| 83 | , shaderResource :: Resource | 83 | , shaderResource :: ReleaseKey |
| 84 | , shaderType :: ShaderType | 84 | , shaderType :: ShaderType |
| 85 | , shaderHash :: Int | 85 | , shaderHash :: Int |
| 86 | } | 86 | } |
| @@ -102,7 +102,7 @@ data ShaderUniform | |||
| 102 | -- | A shader program. | 102 | -- | A shader program. |
| 103 | data ShaderProgram = ShaderProgram | 103 | data ShaderProgram = ShaderProgram |
| 104 | { shaderProgramHandle :: GLuint | 104 | { shaderProgramHandle :: GLuint |
| 105 | , shaderProgramResource :: Resource | 105 | , shaderProgramResource :: ReleaseKey |
| 106 | , shaderProgramHash :: Int | 106 | , shaderProgramHash :: Int |
| 107 | -- Dirty set of uniforms that have been set since the last time uniforms were | 107 | -- Dirty set of uniforms that have been set since the last time uniforms were |
| 108 | -- applied. OpenGL retains the values of uniforms for a program until the | 108 | -- applied. OpenGL retains the values of uniforms for a program until the |
diff --git a/Spear/Render/Immediate.hs b/Spear/Render/Immediate.hs index 3c5f6ad..26f6513 100644 --- a/Spear/Render/Immediate.hs +++ b/Spear/Render/Immediate.hs | |||
| @@ -78,20 +78,20 @@ deleteImmRenderer immState = do | |||
| 78 | 78 | ||
| 79 | immStart :: Game ImmRenderState () | 79 | immStart :: Game ImmRenderState () |
| 80 | immStart = do | 80 | immStart = do |
| 81 | state <- getGameState | 81 | state <- get |
| 82 | gameIO $ activateShaderProgram (shader state) | 82 | liftIO $ activateShaderProgram (shader state) |
| 83 | 83 | ||
| 84 | immEnd :: Game ImmRenderState () | 84 | immEnd :: Game ImmRenderState () |
| 85 | immEnd = do | 85 | immEnd = do |
| 86 | state <- getGameState | 86 | state <- get |
| 87 | gameIO $ deactivateShaderProgram (shader state) | 87 | liftIO $ deactivateShaderProgram (shader state) |
| 88 | 88 | ||
| 89 | immDrawTriangles :: [Vector3] -> Game ImmRenderState () | 89 | immDrawTriangles :: [Vector3] -> Game ImmRenderState () |
| 90 | immDrawTriangles vertices = do | 90 | immDrawTriangles vertices = do |
| 91 | unless (null vertices) $ do | 91 | unless (null vertices) $ do |
| 92 | loadMatrixStack | 92 | loadMatrixStack |
| 93 | state <- getGameState | 93 | state <- get |
| 94 | gameIO $ do | 94 | liftIO $ do |
| 95 | setPositions (triangles state) vertices | 95 | setPositions (triangles state) vertices |
| 96 | applyUniforms (shader state) | 96 | applyUniforms (shader state) |
| 97 | renderGeometry (triangles state) | 97 | renderGeometry (triangles state) |
| @@ -112,42 +112,42 @@ immDrawQuads2d = | |||
| 112 | immDrawQuads . (<$>) (\(p0, p1, p2, p3) -> (to3d p0, to3d p1, to3d p2, to3d p3)) | 112 | immDrawQuads . (<$>) (\(p0, p1, p2, p3) -> (to3d p0, to3d p1, to3d p2, to3d p3)) |
| 113 | 113 | ||
| 114 | immLoadIdentity :: Game ImmRenderState () | 114 | immLoadIdentity :: Game ImmRenderState () |
| 115 | immLoadIdentity = modifyGameState $ \state -> state { | 115 | immLoadIdentity = modify $ \state -> state { |
| 116 | matrixStack = [Matrix4.id] } | 116 | matrixStack = [Matrix4.id] } |
| 117 | 117 | ||
| 118 | immTranslate :: Vector3 -> Game ImmRenderState () | 118 | immTranslate :: Vector3 -> Game ImmRenderState () |
| 119 | immTranslate vector = modifyGameState $ pushMatrix (Matrix4.translatev vector) | 119 | immTranslate vector = modify $ pushMatrix (Matrix4.translatev vector) |
| 120 | 120 | ||
| 121 | immPushMatrix :: Matrix4 -> Game ImmRenderState () | 121 | immPushMatrix :: Matrix4 -> Game ImmRenderState () |
| 122 | immPushMatrix matrix = modifyGameState $ pushMatrix matrix | 122 | immPushMatrix matrix = modify $ pushMatrix matrix |
| 123 | 123 | ||
| 124 | immPopMatrix :: Game ImmRenderState () | 124 | immPopMatrix :: Game ImmRenderState () |
| 125 | immPopMatrix = modifyGameState $ \state -> state { | 125 | immPopMatrix = modify $ \state -> state { |
| 126 | matrixStack = case matrixStack state of | 126 | matrixStack = case matrixStack state of |
| 127 | [x] -> [x] -- Always keep the identity matrix on the stack. | 127 | [x] -> [x] -- Always keep the identity matrix on the stack. |
| 128 | x:xs -> xs } | 128 | x:xs -> xs } |
| 129 | 129 | ||
| 130 | immPreservingMatrix :: Game ImmRenderState a -> Game ImmRenderState a | 130 | immPreservingMatrix :: Game ImmRenderState a -> Game ImmRenderState a |
| 131 | immPreservingMatrix f = do | 131 | immPreservingMatrix f = do |
| 132 | originalStack <- matrixStack <$> getGameState | 132 | originalStack <- matrixStack <$> get |
| 133 | result <- f | 133 | result <- f |
| 134 | modifyGameState $ \state -> state { matrixStack = originalStack } | 134 | modify $ \state -> state { matrixStack = originalStack } |
| 135 | return result | 135 | return result |
| 136 | 136 | ||
| 137 | immSetColour :: Vector4 -> Game ImmRenderState () | 137 | immSetColour :: Vector4 -> Game ImmRenderState () |
| 138 | immSetColour colour = do | 138 | immSetColour colour = do |
| 139 | state <- getGameState | 139 | state <- get |
| 140 | gameIO $ setUniform (Vec4Uniform "Colour" colour) (shader state) | 140 | liftIO $ setUniform (Vec4Uniform "Colour" colour) (shader state) |
| 141 | 141 | ||
| 142 | immSetModelMatrix :: Matrix4 -> Game ImmRenderState () | 142 | immSetModelMatrix :: Matrix4 -> Game ImmRenderState () |
| 143 | immSetModelMatrix model = do | 143 | immSetModelMatrix model = do |
| 144 | state <- getGameState | 144 | state <- get |
| 145 | gameIO $ setUniform (Mat4Uniform "Model" model) (shader state) | 145 | liftIO $ setUniform (Mat4Uniform "Model" model) (shader state) |
| 146 | 146 | ||
| 147 | immSetViewProjectionMatrix :: Matrix4 -> Game ImmRenderState () | 147 | immSetViewProjectionMatrix :: Matrix4 -> Game ImmRenderState () |
| 148 | immSetViewProjectionMatrix viewProjection = do | 148 | immSetViewProjectionMatrix viewProjection = do |
| 149 | state <- getGameState | 149 | state <- get |
| 150 | gameIO $ setUniform (Mat4Uniform "ViewProjection" viewProjection) (shader state) | 150 | liftIO $ setUniform (Mat4Uniform "ViewProjection" viewProjection) (shader state) |
| 151 | 151 | ||
| 152 | -- Private | 152 | -- Private |
| 153 | 153 | ||
| @@ -157,7 +157,7 @@ pushMatrix matrix state = state { | |||
| 157 | 157 | ||
| 158 | loadMatrixStack :: Game ImmRenderState () | 158 | loadMatrixStack :: Game ImmRenderState () |
| 159 | loadMatrixStack = do | 159 | loadMatrixStack = do |
| 160 | state <- getGameState | 160 | state <- get |
| 161 | immSetModelMatrix (head $ matrixStack state) | 161 | immSetModelMatrix (head $ matrixStack state) |
| 162 | 162 | ||
| 163 | to3d :: Vector2 -> Vector3 | 163 | to3d :: Vector2 -> Vector3 |
diff --git a/Spear/Render/StaticModel.hs b/Spear/Render/StaticModel.hs index f4cddf8..5168cf2 100644 --- a/Spear/Render/StaticModel.hs +++ b/Spear/Render/StaticModel.hs | |||
| @@ -44,7 +44,7 @@ data StaticModelResource = StaticModelResource | |||
| 44 | material :: Material, | 44 | material :: Material, |
| 45 | texture :: Texture, | 45 | texture :: Texture, |
| 46 | boxes :: V.Vector Box, | 46 | boxes :: V.Vector Box, |
| 47 | rkey :: Resource | 47 | rkey :: ReleaseKey |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | instance Eq StaticModelResource where | 50 | instance Eq StaticModelResource where |
| @@ -74,12 +74,12 @@ staticModelResource :: | |||
| 74 | Model -> | 74 | Model -> |
| 75 | Game s StaticModelResource | 75 | Game s StaticModelResource |
| 76 | staticModelResource (StaticProgramChannels vertChan normChan texChan) material texture model = do | 76 | staticModelResource (StaticProgramChannels vertChan normChan texChan) material texture model = do |
| 77 | RenderModel elements _ numVertices <- gameIO . renderModelFromModel $ model | 77 | RenderModel elements _ numVertices <- liftIO . renderModelFromModel $ model |
| 78 | elementBuf <- newBuffer | 78 | elementBuf <- newBuffer |
| 79 | vao <- newVAO | 79 | vao <- newVAO |
| 80 | boxes <- gameIO $ modelBoxes model | 80 | boxes <- liftIO $ modelBoxes model |
| 81 | 81 | ||
| 82 | gameIO $ do | 82 | liftIO $ do |
| 83 | let elemSize = 32::CUInt | 83 | let elemSize = 32::CUInt |
| 84 | elemSize' = fromIntegral elemSize | 84 | elemSize' = fromIntegral elemSize |
| 85 | n = numVertices | 85 | n = numVertices |
| @@ -98,9 +98,8 @@ staticModelResource (StaticProgramChannels vertChan normChan texChan) material t | |||
| 98 | enableVAOAttrib texChan | 98 | enableVAOAttrib texChan |
| 99 | 99 | ||
| 100 | rkey <- register $ do | 100 | rkey <- register $ do |
| 101 | putStrLn "Releasing static model resource" | 101 | release $ vaoKey vao |
| 102 | release' vao | 102 | release $ bufferKey elementBuf |
| 103 | release' elementBuf | ||
| 104 | 103 | ||
| 105 | return $ | 104 | return $ |
| 106 | StaticModelResource | 105 | StaticModelResource |
diff --git a/Spear/Scene/Loader.hs b/Spear/Scene/Loader.hs index 3cd89f3..4bbbde0 100644 --- a/Spear/Scene/Loader.hs +++ b/Spear/Scene/Loader.hs | |||
| @@ -43,7 +43,7 @@ type Loader = Game SceneResources | |||
| 43 | -- | Load the scene specified by the given file. | 43 | -- | Load the scene specified by the given file. |
| 44 | loadScene :: FilePath -> Game s (SceneResources, SceneGraph) | 44 | loadScene :: FilePath -> Game s (SceneResources, SceneGraph) |
| 45 | loadScene file = do | 45 | loadScene file = do |
| 46 | result <- gameIO $ loadSceneGraphFromFile file | 46 | result <- liftIO $ loadSceneGraphFromFile file |
| 47 | case result of | 47 | case result of |
| 48 | Left err -> gameError $ show err | 48 | Left err -> gameError $ show err |
| 49 | Right g -> case validate g of | 49 | Right g -> case validate g of |
| @@ -85,9 +85,9 @@ loadResource key field modifyResources load = do | |||
| 85 | case M.lookup key $ field sceneData of | 85 | case M.lookup key $ field sceneData of |
| 86 | Just val -> return val | 86 | Just val -> return val |
| 87 | Nothing -> do | 87 | Nothing -> do |
| 88 | gameIO $ printf "Loading %s..." key | 88 | liftIO $ printf "Loading %s..." key |
| 89 | resource <- load | 89 | resource <- load |
| 90 | gameIO $ printf "done\n" | 90 | liftIO $ printf "done\n" |
| 91 | modifyResources key resource | 91 | modifyResources key resource |
| 92 | return resource | 92 | return resource |
| 93 | 93 | ||
| @@ -139,9 +139,9 @@ newModel (SceneLeaf _ props) = do | |||
| 139 | let rotation = asRotation $ value "rotation" props | 139 | let rotation = asRotation $ value "rotation" props |
| 140 | scale = asVec3 $ value "scale" props | 140 | scale = asVec3 $ value "scale" props |
| 141 | 141 | ||
| 142 | gameIO $ printf "Loading model %s..." name | 142 | liftIO $ printf "Loading model %s..." name |
| 143 | model <- loadModel' file rotation scale | 143 | model <- loadModel' file rotation scale |
| 144 | gameIO . putStrLn $ "done" | 144 | liftIO . putStrLn $ "done" |
| 145 | texture <- loadTexture tex | 145 | texture <- loadTexture tex |
| 146 | sceneRes <- get | 146 | sceneRes <- get |
| 147 | 147 | ||
| @@ -180,7 +180,7 @@ loadModel' file rotation scale = do | |||
| 180 | \(Vec3 x' y' z') -> Vec3 (x s * x') (y s * y') (z s * z') | 180 | \(Vec3 x' y' z') -> Vec3 (x s * x') (y s * y') (z s * z') |
| 181 | ) | 181 | ) |
| 182 | 182 | ||
| 183 | (fmap transform $ Model.loadModel file) >>= gameIO . toGround | 183 | (fmap transform $ Model.loadModel file) >>= liftIO . toGround |
| 184 | 184 | ||
| 185 | rotateModel :: Rotation -> Model -> Model | 185 | rotateModel :: Rotation -> Model -> Model |
| 186 | rotateModel (Rotation ax ay az order) model = | 186 | rotateModel (Rotation ax ay az order) model = |
| @@ -213,7 +213,7 @@ newShaderProgram (SceneLeaf _ props) = do | |||
| 213 | stype <- asString $ mandatory' "type" props | 213 | stype <- asString $ mandatory' "type" props |
| 214 | prog <- GL.newProgram [vertShader, fragShader] | 214 | prog <- GL.newProgram [vertShader, fragShader] |
| 215 | 215 | ||
| 216 | let getUniformLoc name = (gameIO . SV.get $ GL.uniformLocation prog name) `GL.assertGL` name | 216 | let getUniformLoc name = (liftIO . SV.get $ GL.uniformLocation prog name) `GL.assertGL` name |
| 217 | 217 | ||
| 218 | case stype of | 218 | case stype of |
| 219 | "static" -> do | 219 | "static" -> do |
diff --git a/Spear/Window.hs b/Spear/Window.hs index 2dcd1fa..75a38f7 100644 --- a/Spear/Window.hs +++ b/Spear/Window.hs | |||
| @@ -208,7 +208,7 @@ whenKeyUp = whenKeyInState GLFW.KeyState'Released | |||
| 208 | 208 | ||
| 209 | whenKeyInState :: GLFW.KeyState -> GLFW.Window -> Key -> Game s () -> Game s () | 209 | whenKeyInState :: GLFW.KeyState -> GLFW.Window -> Key -> Game s () -> Game s () |
| 210 | whenKeyInState state window key game = do | 210 | whenKeyInState state window key game = do |
| 211 | isDown <- fmap (==state) $ gameIO . GLFW.getKey window . toGLFWkey $ key | 211 | isDown <- fmap (==state) $ liftIO . GLFW.getKey window . toGLFWkey $ key |
| 212 | when isDown game | 212 | when isDown game |
| 213 | 213 | ||
| 214 | -- | Check whether the given keys are pressed and return the value associated | 214 | -- | Check whether the given keys are pressed and return the value associated |
| @@ -219,7 +219,7 @@ processKeys window = foldM f [] | |||
| 219 | f acc (key, result) = do | 219 | f acc (key, result) = do |
| 220 | isDown <- | 220 | isDown <- |
| 221 | fmap (== GLFW.KeyState'Pressed) $ | 221 | fmap (== GLFW.KeyState'Pressed) $ |
| 222 | gameIO . GLFW.getKey (glfwWindow window) . toGLFWkey $ | 222 | liftIO . GLFW.getKey (glfwWindow window) . toGLFWkey $ |
| 223 | key | 223 | key |
| 224 | return $ if isDown then result : acc else acc | 224 | return $ if isDown then result : acc else acc |
| 225 | 225 | ||
| @@ -231,7 +231,7 @@ processButtons window = foldM f [] | |||
| 231 | f acc (button, result) = do | 231 | f acc (button, result) = do |
| 232 | isDown <- | 232 | isDown <- |
| 233 | fmap (== GLFW.MouseButtonState'Pressed) $ | 233 | fmap (== GLFW.MouseButtonState'Pressed) $ |
| 234 | gameIO . GLFW.getMouseButton (glfwWindow window) . toGLFWbutton $ | 234 | liftIO . GLFW.getMouseButton (glfwWindow window) . toGLFWbutton $ |
| 235 | button | 235 | button |
| 236 | return $ if isDown then result : acc else acc | 236 | return $ if isDown then result : acc else acc |
| 237 | 237 | ||
