diff options
-rw-r--r-- | Demos/Pong/Main.hs | 20 | ||||
-rw-r--r-- | Spear/App.hs | 51 | ||||
-rw-r--r-- | Spear/Game.hs | 12 | ||||
-rw-r--r-- | Spear/Render/Immediate.hs | 4 |
4 files changed, 55 insertions, 32 deletions
diff --git a/Demos/Pong/Main.hs b/Demos/Pong/Main.hs index 993c0ff..22b1021 100644 --- a/Demos/Pong/Main.hs +++ b/Demos/Pong/Main.hs | |||
@@ -7,7 +7,7 @@ import Pong | |||
7 | import Spear.App | 7 | import Spear.App |
8 | import Spear.Game | 8 | import Spear.Game |
9 | import Spear.Math.AABB | 9 | import Spear.Math.AABB |
10 | import Spear.Math.Matrix4 as Matrix4 hiding (position) | 10 | import Spear.Math.Matrix4 as Matrix4 |
11 | import Spear.Math.Spatial | 11 | import Spear.Math.Spatial |
12 | import Spear.Math.Spatial2 | 12 | import Spear.Math.Spatial2 |
13 | import Spear.Math.Vector | 13 | import Spear.Math.Vector |
@@ -20,12 +20,10 @@ import Spear.Sound.State | |||
20 | import Spear.Window | 20 | import Spear.Window |
21 | 21 | ||
22 | import Control.Monad (when) | 22 | import Control.Monad (when) |
23 | import Data.Maybe (mapMaybe) | ||
24 | 23 | ||
25 | 24 | ||
26 | data Pong = Pong | 25 | data Pong = Pong |
27 | { immRenderState :: ImmRenderState | 26 | { viewProjection :: Matrix4 |
28 | , viewProjection :: Matrix4 | ||
29 | , backgroundMusic :: SoundSource | 27 | , backgroundMusic :: SoundSource |
30 | , world :: [GameObject] | 28 | , world :: [GameObject] |
31 | } | 29 | } |
@@ -43,10 +41,6 @@ main = runApp app | |||
43 | 41 | ||
44 | initGame :: Game AppContext Pong | 42 | initGame :: Game AppContext Pong |
45 | initGame = do | 43 | initGame = do |
46 | renderCoreState <- contextRenderCoreState <$> get | ||
47 | (immRenderState, renderCoreState') <- runSiblingGame renderCoreState newImmRenderer | ||
48 | -- TODO: This can work if we use FlexibleContexts and change the function signatures. | ||
49 | --immRenderState <- newImmRenderer | ||
50 | music <- siblingGame $ do | 44 | music <- siblingGame $ do |
51 | musicBuffer <- loadAudioFile "/home/jeanne/Casual Tiki Party Main.wav" | 45 | musicBuffer <- loadAudioFile "/home/jeanne/Casual Tiki Party Main.wav" |
52 | music <- makeSoundSource | 46 | music <- makeSoundSource |
@@ -54,13 +48,10 @@ initGame = do | |||
54 | setSoundLoopMode music Loop | 48 | setSoundLoopMode music Loop |
55 | playSounds [music] | 49 | playSounds [music] |
56 | return music | 50 | return music |
57 | return $ Pong immRenderState Matrix4.id music newWorld | 51 | return $ Pong Matrix4.id music newWorld |
58 | 52 | ||
59 | endGame :: Game GameState () | 53 | endGame :: Game GameState () |
60 | endGame = do | 54 | endGame = return () |
61 | renderCoreState <- appRenderCoreState <$> get | ||
62 | game <- getGameState | ||
63 | exec' runSiblingGame renderCoreState (deleteImmRenderer $ immRenderState game) | ||
64 | 55 | ||
65 | 56 | ||
66 | step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool | 57 | step :: Elapsed -> Dt -> [InputEvent] -> Game GameState Bool |
@@ -86,7 +77,7 @@ exitRequested = elem (KeyDown KEY_ESC) | |||
86 | render :: Game GameState () | 77 | render :: Game GameState () |
87 | render = do | 78 | render = do |
88 | gameState <- getGameState | 79 | gameState <- getGameState |
89 | immRenderState' <- exec runSiblingGame (immRenderState gameState) $ do | 80 | siblingGame $ do |
90 | immStart | 81 | immStart |
91 | immSetViewProjectionMatrix (viewProjection gameState) | 82 | immSetViewProjectionMatrix (viewProjection gameState) |
92 | -- Clear the background to a different colour than the playable area to make | 83 | -- Clear the background to a different colour than the playable area to make |
@@ -95,7 +86,6 @@ render = do | |||
95 | clearBuffers [ColourBuffer] | 86 | clearBuffers [ColourBuffer] |
96 | render' $ world gameState | 87 | render' $ world gameState |
97 | immEnd | 88 | immEnd |
98 | putGameState $ gameState { immRenderState = immRenderState' } | ||
99 | 89 | ||
100 | render' :: [GameObject] -> Game ImmRenderState () | 90 | render' :: [GameObject] -> Game ImmRenderState () |
101 | render' world = do | 91 | render' world = do |
diff --git a/Spear/App.hs b/Spear/App.hs index 75bf6fa..6e8f5f2 100644 --- a/Spear/App.hs +++ b/Spear/App.hs | |||
@@ -1,3 +1,4 @@ | |||
1 | {-# LANGUAGE FlexibleInstances #-} | ||
1 | {-# LANGUAGE MultiParamTypeClasses #-} | 2 | {-# LANGUAGE MultiParamTypeClasses #-} |
2 | 3 | ||
3 | module Spear.App | 4 | module Spear.App |
@@ -19,6 +20,7 @@ where | |||
19 | 20 | ||
20 | import Spear.Game | 21 | import Spear.Game |
21 | import Spear.Render.Core.State | 22 | import Spear.Render.Core.State |
23 | import Spear.Render.Immediate | ||
22 | import Spear.Sound.Sound | 24 | import Spear.Sound.Sound |
23 | import Spear.Sound.State | 25 | import Spear.Sound.State |
24 | import Spear.Sys.Timer as Timer | 26 | import Spear.Sys.Timer as Timer |
@@ -78,46 +80,61 @@ data AppContext = AppContext | |||
78 | { contextWindow :: Window | 80 | { contextWindow :: Window |
79 | , contextRenderCoreState :: RenderCoreState | 81 | , contextRenderCoreState :: RenderCoreState |
80 | , contextSoundState :: SoundState | 82 | , contextSoundState :: SoundState |
83 | , contextImmRenderState :: ImmRenderState | ||
81 | } | 84 | } |
82 | 85 | ||
83 | instance HasState AppContext RenderCoreState where | 86 | instance HasState AppContext RenderCoreState where |
84 | getInnerState = contextRenderCoreState | 87 | getInnerState = contextRenderCoreState |
85 | setInnerState context renderCoreState = context { contextRenderCoreState = renderCoreState } | 88 | setInnerState context state = context { contextRenderCoreState = state } |
86 | 89 | ||
87 | instance HasState AppContext SoundState where | 90 | instance HasState AppContext SoundState where |
88 | getInnerState = contextSoundState | 91 | getInnerState = contextSoundState |
89 | setInnerState context soundState = context { contextSoundState = soundState } | 92 | setInnerState context state = context { contextSoundState = state } |
93 | |||
94 | instance HasState AppContext ImmRenderState where | ||
95 | getInnerState = contextImmRenderState | ||
96 | setInnerState context state = context { contextImmRenderState = state } | ||
90 | 97 | ||
91 | -- | Application state. | 98 | -- | Application state. |
92 | data AppState s = AppState | 99 | data AppState s = AppState |
93 | { appWindow :: Window | 100 | { appWindow :: Window |
94 | , appRenderCoreState :: RenderCoreState | 101 | , appRenderCoreState :: RenderCoreState |
95 | , appSoundState :: SoundState | 102 | , appSoundState :: SoundState |
96 | , customState :: s | 103 | , appImmRenderState :: ImmRenderState |
104 | , appCustomState :: s | ||
97 | } | 105 | } |
98 | 106 | ||
107 | -- Requires FlexibleInstances. | ||
108 | instance HasState (AppState s) s where | ||
109 | getInnerState = appCustomState | ||
110 | setInnerState appState state = appState { appCustomState = state } | ||
111 | |||
99 | instance HasState (AppState s) RenderCoreState where | 112 | instance HasState (AppState s) RenderCoreState where |
100 | getInnerState = appRenderCoreState | 113 | getInnerState = appRenderCoreState |
101 | setInnerState appState renderCoreState = appState { appRenderCoreState = renderCoreState } | 114 | setInnerState appState state = appState { appRenderCoreState = state } |
102 | 115 | ||
103 | instance HasState (AppState s) SoundState where | 116 | instance HasState (AppState s) SoundState where |
104 | getInnerState = appSoundState | 117 | getInnerState = appSoundState |
105 | setInnerState appState soundState = appState { appSoundState = soundState } | 118 | setInnerState appState state = appState { appSoundState = state } |
119 | |||
120 | instance HasState (AppState s) ImmRenderState where | ||
121 | getInnerState = appImmRenderState | ||
122 | setInnerState appState state = appState { appImmRenderState = state } | ||
106 | 123 | ||
107 | 124 | ||
108 | -- | Get the custom state in the app state. | 125 | -- | Get the custom state in the app state. |
109 | getGameState :: Game (AppState s) s | 126 | getGameState :: Game (AppState s) s |
110 | getGameState = customState <$> get | 127 | getGameState = appCustomState <$> get |
111 | 128 | ||
112 | -- | Put the custom state in the app state. | 129 | -- | Put the custom state in the app state. |
113 | putGameState :: s -> Game (AppState s) () | 130 | putGameState :: s -> Game (AppState s) () |
114 | putGameState custom = do | 131 | putGameState custom = do |
115 | appState <- get | 132 | appState <- get |
116 | put $ appState { customState = custom } | 133 | put $ appState { appCustomState = custom } |
117 | 134 | ||
118 | -- | Modify the custom state in the app state. | 135 | -- | Modify the custom state in the app state. |
119 | modifyGameState :: (s -> s) -> Game (AppState s) () | 136 | modifyGameState :: (s -> s) -> Game (AppState s) () |
120 | modifyGameState f = modify $ \appState -> appState { customState = f (customState appState )} | 137 | modifyGameState f = modify $ \appState -> appState { appCustomState = f (appCustomState appState )} |
121 | 138 | ||
122 | -- | Run the application. | 139 | -- | Run the application. |
123 | runApp :: App s -> IO () | 140 | runApp :: App s -> IO () |
@@ -129,21 +146,29 @@ runApp app = | |||
129 | withWindow (w, h) (title ops) $ \window -> | 146 | withWindow (w, h) (title ops) $ \window -> |
130 | withSoundContext $ eval runGame () $ do | 147 | withSoundContext $ eval runGame () $ do |
131 | -- Create initial context. | 148 | -- Create initial context. |
149 | -- We could modify function signatures such as: | ||
150 | -- newImmRenderer :: HasState s RenderCoreState => Game s ImmRenderState | ||
151 | -- to simplify things a bit. But I'm not sure I want HasState to | ||
152 | -- proliferate like that right now. | ||
132 | initialSoundState <- eval runSiblingGame () initSoundSystem | 153 | initialSoundState <- eval runSiblingGame () initSoundSystem |
133 | let context = AppContext window newRenderCoreState initialSoundState | 154 | (immRenderState, renderCoreState) <- runSiblingGame newRenderCoreState newImmRenderer |
155 | let context = AppContext window renderCoreState initialSoundState immRenderState | ||
134 | -- Create initial app state. | 156 | -- Create initial app state. |
135 | (gameState, context') <- runSiblingGame context (initApp app) | 157 | (gameState, context') <- runSiblingGame context (initApp app) |
136 | let appState = AppState { | 158 | let appState = AppState { |
137 | appWindow = contextWindow context' | 159 | appWindow = contextWindow context' |
138 | , appRenderCoreState = contextRenderCoreState context' | 160 | , appRenderCoreState = contextRenderCoreState context' |
139 | , appSoundState = contextSoundState context' | 161 | , appSoundState = contextSoundState context' |
140 | , customState = gameState | 162 | , appImmRenderState = contextImmRenderState context' |
163 | , appCustomState = gameState | ||
141 | } | 164 | } |
142 | -- Run app. | 165 | -- Run app. |
143 | (result, endGameState) <- runSiblingGame appState (loop app window) | 166 | (result, endAppState) <- runSubGame appState $ do |
167 | loop app window | ||
168 | endApp app | ||
144 | -- Shut down. | 169 | -- Shut down. |
145 | exec' runSiblingGame endGameState (endApp app) | 170 | exec' runSiblingGame (appRenderCoreState endAppState) $ deleteImmRenderer (appImmRenderState endAppState) |
146 | exec' runSiblingGame (appSoundState appState) destroySoundSystem | 171 | exec' runSiblingGame (appSoundState endAppState) destroySoundSystem |
147 | 172 | ||
148 | -- | Enter the main application loop. | 173 | -- | Enter the main application loop. |
149 | loop :: App s -> Window -> Game (AppState s) () | 174 | loop :: App s -> Window -> Game (AppState s) () |
diff --git a/Spear/Game.hs b/Spear/Game.hs index 1af8e9b..0c8b963 100644 --- a/Spear/Game.hs +++ b/Spear/Game.hs | |||
@@ -1,3 +1,4 @@ | |||
1 | {-# LANGUAGE FlexibleInstances #-} | ||
1 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | 2 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} |
2 | {-# LANGUAGE MultiParamTypeClasses #-} | 3 | {-# LANGUAGE MultiParamTypeClasses #-} |
3 | 4 | ||
@@ -79,6 +80,11 @@ class HasState s t where | |||
79 | getInnerState :: s -> t | 80 | getInnerState :: s -> t |
80 | setInnerState :: s -> t -> s | 81 | setInnerState :: s -> t -> s |
81 | 82 | ||
83 | -- Identity instance. | ||
84 | instance HasState s s where | ||
85 | getInnerState = id | ||
86 | setInnerState s s' = s' | ||
87 | |||
82 | 88 | ||
83 | -- | Release the given 'Resource'. | 89 | -- | Release the given 'Resource'. |
84 | release' :: ResourceClass a => a -> Game s () | 90 | release' :: ResourceClass a => a -> Game s () |
@@ -129,7 +135,7 @@ exec' runner game state = void $ runner game state | |||
129 | -- | Run a sibling game on nested state. | 135 | -- | Run a sibling game on nested state. |
130 | siblingGame :: HasState s t => Game t a -> Game s a | 136 | siblingGame :: HasState s t => Game t a -> Game s a |
131 | siblingGame tAction = do | 137 | siblingGame tAction = do |
132 | tState <- getInnerState <$> get | 138 | outerState <- getInnerState <$> get |
133 | (result, tState') <- runSiblingGame tState tAction | 139 | (result, outerState') <- runSiblingGame outerState tAction |
134 | modify $ \outerState -> setInnerState outerState tState' | 140 | modify $ \outerState -> setInnerState outerState outerState' |
135 | return result | 141 | return result |
diff --git a/Spear/Render/Immediate.hs b/Spear/Render/Immediate.hs index b3a8998..786e844 100644 --- a/Spear/Render/Immediate.hs +++ b/Spear/Render/Immediate.hs | |||
@@ -74,7 +74,9 @@ deleteImmRenderer immState = do | |||
74 | deleteGeometry (triangles immState) | 74 | deleteGeometry (triangles immState) |
75 | 75 | ||
76 | -- The functions below are all defined inside the Game ImmRenderState monad so | 76 | -- The functions below are all defined inside the Game ImmRenderState monad so |
77 | -- that all of the drawing can conveniently happen inside the monad. | 77 | -- that all of the drawing can conveniently happen inside the monad. They could |
78 | -- technically be defined inside MonadIO, but then we would have to explicitly | ||
79 | -- pass in the ImmRenderState. | ||
78 | 80 | ||
79 | immStart :: Game ImmRenderState () | 81 | immStart :: Game ImmRenderState () |
80 | immStart = do | 82 | immStart = do |