]> git.localhorst.tv Git - blank.git/blob - src/app/app.cpp
textures
[blank.git] / src / app / app.cpp
1 #include "Application.hpp"
2 #include "Assets.hpp"
3 #include "Environment.hpp"
4 #include "FrameCounter.hpp"
5 #include "State.hpp"
6 #include "StateControl.hpp"
7
8 #include "init.hpp"
9 #include "../audio/Sound.hpp"
10 #include "../graphics/ArrayTexture.hpp"
11 #include "../graphics/Font.hpp"
12 #include "../graphics/Texture.hpp"
13 #include "../world/BlockType.hpp"
14 #include "../world/Entity.hpp"
15
16 #include <iostream>
17 #include <stdexcept>
18 #include <SDL_image.h>
19
20 using std::string;
21
22
23 namespace blank {
24
25 Application::Application(Environment &e)
26 : env(e)
27 , states() {
28
29 }
30
31 Application::~Application() {
32         env.audio.StopAll();
33 }
34
35
36 void Application::RunN(size_t n) {
37         Uint32 last = SDL_GetTicks();
38         for (size_t i = 0; HasState() && i < n; ++i) {
39                 Uint32 now = SDL_GetTicks();
40                 int delta = now - last;
41                 Loop(delta);
42                 last = now;
43         }
44 }
45
46 void Application::RunT(size_t t) {
47         Uint32 last = SDL_GetTicks();
48         Uint32 finish = last + t;
49         while (HasState() && last < finish) {
50                 Uint32 now = SDL_GetTicks();
51                 int delta = now - last;
52                 Loop(delta);
53                 last = now;
54         }
55 }
56
57 void Application::RunS(size_t n, size_t t) {
58         for (size_t i = 0; HasState() && i < n; ++i) {
59                 Loop(t);
60         }
61 }
62
63
64 void Application::Run() {
65         Uint32 last = SDL_GetTicks();
66         env.window.GrabMouse();
67         while (HasState()) {
68                 Uint32 now = SDL_GetTicks();
69                 int delta = now - last;
70                 Loop(delta);
71                 last = now;
72         }
73 }
74
75 void Application::Loop(int dt) {
76         env.counter.EnterFrame();
77         HandleEvents();
78         if (!HasState()) return;
79         Update(dt);
80         env.state.Commit(*this);
81         if (!HasState()) return;
82         Render();
83         env.counter.ExitFrame();
84 }
85
86
87 void Application::HandleEvents() {
88         env.counter.EnterHandle();
89         SDL_Event event;
90         while (HasState() && SDL_PollEvent(&event)) {
91                 Handle(event);
92                 env.state.Commit(*this);
93         }
94         env.counter.ExitHandle();
95 }
96
97 void Application::Handle(const SDL_Event &event) {
98         switch (event.type) {
99                 case SDL_QUIT:
100                         env.state.PopAll();
101                         break;
102                 case SDL_WINDOWEVENT:
103                         Handle(event.window);
104                         break;
105                 default:
106                         GetState().Handle(event);
107                         break;
108         }
109 }
110
111 void Application::Handle(const SDL_WindowEvent &event) {
112         switch (event.event) {
113                 case SDL_WINDOWEVENT_FOCUS_GAINED:
114                         env.window.GrabMouse();
115                         break;
116                 case SDL_WINDOWEVENT_FOCUS_LOST:
117                         env.window.ReleaseMouse();
118                         break;
119                 case SDL_WINDOWEVENT_RESIZED:
120                         env.viewport.Resize(event.data1, event.data2);
121                         break;
122                 default:
123                         break;
124         }
125 }
126
127 void Application::Update(int dt) {
128         env.counter.EnterUpdate();
129         if (HasState()) {
130                 GetState().Update(dt);
131         }
132         env.counter.ExitUpdate();
133 }
134
135 void Application::Render() {
136         // gl implementation may (and will probably) delay vsync blocking until
137         // the first write after flipping, which is this clear call
138         env.viewport.Clear();
139         env.counter.EnterRender();
140
141         if (HasState()) {
142                 GetState().Render(env.viewport);
143         }
144
145         env.counter.ExitRender();
146         env.window.Flip();
147 }
148
149
150 void Application::PushState(State *s) {
151         states.emplace(s);
152 }
153
154 State *Application::PopState() {
155         State *s = states.top();
156         states.pop();
157         return s;
158 }
159
160 State *Application::SwitchState(State *s_new) {
161         State *s_old = states.top();
162         states.top() = s_new;
163         return s_old;
164 }
165
166 State &Application::GetState() {
167         return *states.top();
168 }
169
170 bool Application::HasState() const noexcept {
171         return !states.empty();
172 }
173
174
175 void StateControl::Commit(Application &app) {
176         while (!cue.empty()) {
177                 Memo m(cue.front());
178                 cue.pop();
179                 switch (m.cmd) {
180                         case PUSH:
181                                 app.PushState(m.state);
182                                 break;
183                         case SWITCH:
184                                 app.SwitchState(m.state);
185                                 break;
186                         case POP:
187                                 app.PopState();
188                                 break;
189                         case POP_ALL:
190                                 while (app.HasState()) {
191                                         app.PopState();
192                                 }
193                                 break;
194                 }
195         }
196 }
197
198
199 Assets::Assets(const string &base)
200 : fonts(base + "fonts/")
201 , sounds(base + "sounds/")
202 , textures(base + "textures/") {
203
204 }
205
206 Font Assets::LoadFont(const string &name, int size) const {
207         string full = fonts + name + ".ttf";
208         return Font(full.c_str(), size);
209 }
210
211 Sound Assets::LoadSound(const string &name) const {
212         string full = sounds + name + ".wav";
213         return Sound(full.c_str());
214 }
215
216 Texture Assets::LoadTexture(const string &name) const {
217         string full = textures + name + ".png";
218         Texture tex;
219         SDL_Surface *srf = IMG_Load(full.c_str());
220         if (!srf) {
221                 throw SDLError("IMG_Load");
222         }
223         tex.Bind();
224         tex.Data(*srf);
225         SDL_FreeSurface(srf);
226         return tex;
227 }
228
229 void Assets::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
230         string full = textures + name + ".png";
231         SDL_Surface *srf = IMG_Load(full.c_str());
232         if (!srf) {
233                 throw SDLError("IMG_Load");
234         }
235         tex.Bind();
236         try {
237                 tex.Data(layer, *srf);
238         } catch (...) {
239                 SDL_FreeSurface(srf);
240                 throw;
241         }
242         SDL_FreeSurface(srf);
243 }
244
245
246 void FrameCounter::EnterFrame() noexcept {
247         last_enter = SDL_GetTicks();
248         last_tick = last_enter;
249 }
250
251 void FrameCounter::EnterHandle() noexcept {
252         Tick();
253 }
254
255 void FrameCounter::ExitHandle() noexcept {
256         current.handle = Tick();
257 }
258
259 void FrameCounter::EnterUpdate() noexcept {
260         Tick();
261 }
262
263 void FrameCounter::ExitUpdate() noexcept {
264         current.update = Tick();
265 }
266
267 void FrameCounter::EnterRender() noexcept {
268         Tick();
269 }
270
271 void FrameCounter::ExitRender() noexcept {
272         current.render = Tick();
273 }
274
275 void FrameCounter::ExitFrame() noexcept {
276         Uint32 now = SDL_GetTicks();
277         current.total = now - last_enter;
278         current.running = current.handle + current.update + current.render;
279         current.waiting = current.total - current.running;
280         Accumulate();
281
282         ++cur_frame;
283         if (cur_frame >= NUM_FRAMES) {
284                 Push();
285                 cur_frame = 0;
286                 changed = true;
287         } else {
288                 changed = false;
289         }
290 }
291
292 int FrameCounter::Tick() noexcept {
293         Uint32 now = SDL_GetTicks();
294         int delta = now - last_tick;
295         last_tick = now;
296         return delta;
297 }
298
299 void FrameCounter::Accumulate() noexcept {
300         sum.handle += current.handle;
301         sum.update += current.update;
302         sum.render += current.render;
303         sum.running += current.running;
304         sum.waiting += current.waiting;
305         sum.total += current.total;
306
307         max.handle = std::max(current.handle, max.handle);
308         max.update = std::max(current.update, max.update);
309         max.render = std::max(current.render, max.render);
310         max.running = std::max(current.running, max.running);
311         max.waiting = std::max(current.waiting, max.waiting);
312         max.total = std::max(current.total, max.total);
313
314         current = Frame<int>();
315 }
316
317 void FrameCounter::Push() noexcept {
318         peak = max;
319         avg.handle = sum.handle * factor;
320         avg.update = sum.update * factor;
321         avg.render = sum.render * factor;
322         avg.running = sum.running * factor;
323         avg.waiting = sum.waiting * factor;
324         avg.total = sum.total * factor;
325
326         sum = Frame<int>();
327         max = Frame<int>();
328 }
329
330 }