diff options
author | 3gg <3gg@shellblade.net> | 2024-12-30 19:14:12 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2024-12-30 19:14:12 -0800 |
commit | f1939232bec72fffede16a55119bc7c4fb3057cf (patch) | |
tree | 379a8268ff765023527bbf0a4e2088396dba5f84 | |
parent | 34cac097d15cdd7ef0a0de8b9024db9acfe8618d (diff) |
Simplify Game monad.
-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 | ||