]> git.localhorst.tv Git - gong.git/blob - src/app/app.cpp
code, assets, and other stuff stolen from blank
[gong.git] / src / app / app.cpp
1 #include "Application.hpp"
2 #include "Assets.hpp"
3 #include "Environment.hpp"
4 #include "FrameCounter.hpp"
5 #include "MessageState.hpp"
6 #include "ResourceIndex.hpp"
7 #include "State.hpp"
8 #include "StateControl.hpp"
9
10 #include "init.hpp"
11 #include "../audio/Sound.hpp"
12 #include "../graphics/ArrayTexture.hpp"
13 #include "../graphics/CubeMap.hpp"
14 #include "../graphics/Font.hpp"
15 #include "../graphics/Texture.hpp"
16 #include "../io/TokenStreamReader.hpp"
17
18 #include <fstream>
19 #include <iomanip>
20 #include <iostream>
21 #include <stdexcept>
22 #include <SDL_image.h>
23
24 using namespace std;
25
26
27 namespace gong {
28 namespace app {
29
30 HeadlessApplication::HeadlessApplication(HeadlessEnvironment &e)
31 : env(e)
32 , states() {
33
34 }
35
36 HeadlessApplication::~HeadlessApplication() {
37
38 }
39
40
41 Application::Application(Environment &e)
42 : HeadlessApplication(e)
43 , env(e) {
44
45 }
46
47 Application::~Application() {
48         env.audio.StopAll();
49 }
50
51
52 void HeadlessApplication::RunN(size_t n) {
53         Uint32 last = SDL_GetTicks();
54         for (size_t i = 0; HasState() && i < n; ++i) {
55                 Uint32 now = SDL_GetTicks();
56                 int delta = now - last;
57                 Loop(delta);
58                 last = now;
59         }
60 }
61
62 void HeadlessApplication::RunT(size_t t) {
63         Uint32 last = SDL_GetTicks();
64         Uint32 finish = last + t;
65         while (HasState() && last < finish) {
66                 Uint32 now = SDL_GetTicks();
67                 int delta = now - last;
68                 Loop(delta);
69                 last = now;
70         }
71 }
72
73 void HeadlessApplication::RunS(size_t n, size_t t) {
74         for (size_t i = 0; HasState() && i < n; ++i) {
75                 Loop(t);
76                 cout << '.';
77                 if (i % 32 == 31) {
78                         cout << setfill(' ') << setw(5) << right << (i + 1) << endl;
79                 } else {
80                         cout << flush;
81                 }
82         }
83 }
84
85
86 void HeadlessApplication::Run() {
87         Uint32 last = SDL_GetTicks();
88         while (HasState()) {
89                 Uint32 now = SDL_GetTicks();
90                 int delta = now - last;
91                 Loop(delta);
92                 last = now;
93         }
94 }
95
96 void HeadlessApplication::Loop(int dt) {
97         env.counter.EnterFrame();
98         HandleEvents();
99         if (!HasState()) return;
100         Update(dt);
101         CommitStates();
102         if (!HasState()) return;
103         env.counter.ExitFrame();
104 }
105
106 void Application::Loop(int dt) {
107         env.counter.EnterFrame();
108         HandleEvents();
109         if (!HasState()) return;
110         Update(dt);
111         CommitStates();
112         if (!HasState()) return;
113         Render();
114         env.counter.ExitFrame();
115 }
116
117
118 void HeadlessApplication::HandleEvents() {
119         env.counter.EnterHandle();
120         SDL_Event event;
121         while (HasState() && SDL_PollEvent(&event)) {
122                 Handle(event);
123                 CommitStates();
124         }
125         env.counter.ExitHandle();
126 }
127
128 void HeadlessApplication::Handle(const SDL_Event &event) {
129         GetState().Handle(event);
130 }
131
132
133 void Application::HandleEvents() {
134         env.counter.EnterHandle();
135         SDL_Event event;
136         while (HasState() && SDL_PollEvent(&event)) {
137                 Handle(event);
138                 CommitStates();
139         }
140         env.counter.ExitHandle();
141 }
142
143 void Application::Handle(const SDL_Event &event) {
144         switch (event.type) {
145                 case SDL_WINDOWEVENT:
146                         Handle(event.window);
147                         break;
148                 default:
149                         GetState().Handle(event);
150                         break;
151         }
152 }
153
154 void Application::Handle(const SDL_WindowEvent &event) {
155         switch (event.event) {
156                 case SDL_WINDOWEVENT_FOCUS_GAINED:
157                         GetState().OnFocus();
158                         break;
159                 case SDL_WINDOWEVENT_FOCUS_LOST:
160                         GetState().OnBlur();
161                         break;
162                 case SDL_WINDOWEVENT_RESIZED:
163                         env.viewport.Resize(event.data1, event.data2);
164                         GetState().OnResize(env.viewport);
165                         break;
166                 default:
167                         break;
168         }
169 }
170
171 void HeadlessApplication::Update(int dt) {
172         env.counter.EnterUpdate();
173         if (HasState()) {
174                 GetState().Update(dt);
175         }
176         env.counter.ExitUpdate();
177 }
178
179 void Application::Update(int dt) {
180         env.counter.EnterUpdate();
181         env.audio.Update(dt);
182         if (HasState()) {
183                 GetState().Update(dt);
184         }
185         env.counter.ExitUpdate();
186 }
187
188 void Application::Render() {
189         // gl implementation may (and will probably) delay vsync blocking until
190         // the first write after flipping, which is this clear call
191         env.viewport.Clear();
192         env.counter.EnterRender();
193
194         if (HasState()) {
195                 GetState().Render(env.viewport);
196         }
197
198         env.counter.ExitRender();
199         env.window.Flip();
200 }
201
202
203 void HeadlessApplication::PushState(State *s) {
204         if (!states.empty()) {
205                 states.top()->OnPause();
206         }
207         states.emplace(s);
208         ++s->ref_count;
209         if (s->ref_count == 1) {
210                 s->OnEnter();
211         }
212         s->OnResume();
213 }
214
215 State *HeadlessApplication::PopState() {
216         State *s = states.top();
217         states.pop();
218         s->OnPause();
219         s->OnExit();
220         if (!states.empty()) {
221                 states.top()->OnResume();
222         }
223         return s;
224 }
225
226 State *HeadlessApplication::SwitchState(State *s_new) {
227         State *s_old = states.top();
228         states.top() = s_new;
229         --s_old->ref_count;
230         ++s_new->ref_count;
231         s_old->OnPause();
232         if (s_old->ref_count == 0) {
233                 s_old->OnExit();
234         }
235         if (s_new->ref_count == 1) {
236                 s_new->OnEnter();
237         }
238         s_new->OnResume();
239         return s_old;
240 }
241
242 State &HeadlessApplication::GetState() {
243         return *states.top();
244 }
245
246 void HeadlessApplication::CommitStates() {
247         env.state.Commit(*this);
248 }
249
250 bool HeadlessApplication::HasState() const noexcept {
251         return !states.empty();
252 }
253
254
255 void StateControl::Commit(HeadlessApplication &app) {
256         while (!cue.empty()) {
257                 Memo m(cue.front());
258                 cue.pop();
259                 switch (m.cmd) {
260                         case PUSH:
261                                 app.PushState(m.state);
262                                 break;
263                         case SWITCH:
264                                 app.SwitchState(m.state);
265                                 break;
266                         case POP:
267                                 app.PopState();
268                                 break;
269                         case POP_ALL:
270                                 while (app.HasState()) {
271                                         app.PopState();
272                                 }
273                                 break;
274                         case POP_AFTER:
275                                 while (app.HasState() && &app.GetState() != m.state) {
276                                         app.PopState();
277                                 }
278                                 break;
279                         case POP_UNTIL:
280                                 while (app.HasState()) {
281                                         if (app.PopState() == m.state) {
282                                                 break;
283                                         }
284                                 }
285                 }
286         }
287 }
288
289
290 AssetLoader::AssetLoader(const string &base)
291 : fonts(base + "fonts/")
292 , sounds(base + "sounds/")
293 , textures(base + "textures/")
294 , data(base + "data/") {
295
296 }
297
298 Assets::Assets(const AssetLoader &loader)
299 : large_ui_font(loader.LoadFont("DejaVuSans", 24))
300 , small_ui_font(loader.LoadFont("DejaVuSans", 16)) {
301
302 }
303
304 graphics::CubeMap AssetLoader::LoadCubeMap(const string &name) const {
305         string full = textures + name;
306         string right = full + "-right.png";
307         string left = full + "-left.png";
308         string top = full + "-top.png";
309         string bottom = full + "-bottom.png";
310         string back = full + "-back.png";
311         string front = full + "-front.png";
312
313         graphics::CubeMap cm;
314         cm.Bind();
315         SDL_Surface *srf;
316
317         if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
318         try {
319                 cm.Data(graphics::CubeMap::RIGHT, *srf);
320         } catch (...) {
321                 SDL_FreeSurface(srf);
322                 throw;
323         }
324         SDL_FreeSurface(srf);
325
326         if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
327         try {
328                 cm.Data(graphics::CubeMap::LEFT, *srf);
329         } catch (...) {
330                 SDL_FreeSurface(srf);
331                 throw;
332         }
333         SDL_FreeSurface(srf);
334
335         if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
336         try {
337                 cm.Data(graphics::CubeMap::TOP, *srf);
338         } catch (...) {
339                 SDL_FreeSurface(srf);
340                 throw;
341         }
342         SDL_FreeSurface(srf);
343
344         if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
345         try {
346                 cm.Data(graphics::CubeMap::BOTTOM, *srf);
347         } catch (...) {
348                 SDL_FreeSurface(srf);
349                 throw;
350         }
351         SDL_FreeSurface(srf);
352
353         if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
354         try {
355                 cm.Data(graphics::CubeMap::BACK, *srf);
356         } catch (...) {
357                 SDL_FreeSurface(srf);
358                 throw;
359         }
360         SDL_FreeSurface(srf);
361
362         if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
363         try {
364                 cm.Data(graphics::CubeMap::FRONT, *srf);
365         } catch (...) {
366                 SDL_FreeSurface(srf);
367                 throw;
368         }
369         SDL_FreeSurface(srf);
370
371         cm.FilterNearest();
372         cm.WrapEdge();
373
374         return cm;
375 }
376
377 graphics::Font AssetLoader::LoadFont(const string &name, int size) const {
378         string full = fonts + name + ".ttf";
379         return graphics::Font(full.c_str(), size);
380 }
381
382 audio::Sound AssetLoader::LoadSound(const string &name) const {
383         string full = sounds + name + ".wav";
384         return audio::Sound(full.c_str());
385 }
386
387 graphics::Texture AssetLoader::LoadTexture(const string &name) const {
388         string full = textures + name + ".png";
389         graphics::Texture tex;
390         SDL_Surface *srf = IMG_Load(full.c_str());
391         if (!srf) {
392                 throw SDLError("IMG_Load");
393         }
394         tex.Bind();
395         tex.Data(*srf);
396         SDL_FreeSurface(srf);
397         return tex;
398 }
399
400 void AssetLoader::LoadTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
401         string full = textures + name + ".png";
402         SDL_Surface *srf = IMG_Load(full.c_str());
403         if (!srf) {
404                 throw SDLError("IMG_Load");
405         }
406         tex.Bind();
407         try {
408                 tex.Data(layer, *srf);
409         } catch (...) {
410                 SDL_FreeSurface(srf);
411                 throw;
412         }
413         SDL_FreeSurface(srf);
414 }
415
416
417 void FrameCounter::EnterFrame() noexcept {
418         last_enter = SDL_GetTicks();
419         last_tick = last_enter;
420 }
421
422 void FrameCounter::EnterHandle() noexcept {
423         Tick();
424 }
425
426 void FrameCounter::ExitHandle() noexcept {
427         current.handle = Tick();
428 }
429
430 void FrameCounter::EnterUpdate() noexcept {
431         Tick();
432 }
433
434 void FrameCounter::ExitUpdate() noexcept {
435         current.update = Tick();
436 }
437
438 void FrameCounter::EnterRender() noexcept {
439         Tick();
440 }
441
442 void FrameCounter::ExitRender() noexcept {
443         current.render = Tick();
444 }
445
446 void FrameCounter::ExitFrame() noexcept {
447         Uint32 now = SDL_GetTicks();
448         current.total = now - last_enter;
449         current.running = current.handle + current.update + current.render;
450         current.waiting = current.total - current.running;
451         Accumulate();
452
453         ++cur_frame;
454         if (cur_frame >= NUM_FRAMES) {
455                 Push();
456                 cur_frame = 0;
457                 changed = true;
458         } else {
459                 changed = false;
460         }
461 }
462
463 int FrameCounter::Tick() noexcept {
464         Uint32 now = SDL_GetTicks();
465         int delta = now - last_tick;
466         last_tick = now;
467         return delta;
468 }
469
470 void FrameCounter::Accumulate() noexcept {
471         sum.handle += current.handle;
472         sum.update += current.update;
473         sum.render += current.render;
474         sum.running += current.running;
475         sum.waiting += current.waiting;
476         sum.total += current.total;
477
478         max.handle = std::max(current.handle, max.handle);
479         max.update = std::max(current.update, max.update);
480         max.render = std::max(current.render, max.render);
481         max.running = std::max(current.running, max.running);
482         max.waiting = std::max(current.waiting, max.waiting);
483         max.total = std::max(current.total, max.total);
484
485         current = Frame<int>();
486 }
487
488 void FrameCounter::Push() noexcept {
489         peak = max;
490         avg.handle = sum.handle * factor;
491         avg.update = sum.update * factor;
492         avg.render = sum.render * factor;
493         avg.running = sum.running * factor;
494         avg.waiting = sum.waiting * factor;
495         avg.total = sum.total * factor;
496
497         //Print(cout);
498
499         sum = Frame<int>();
500         max = Frame<int>();
501 }
502
503 void FrameCounter::Print(ostream &out) const {
504         out << fixed << right << setprecision(2) << setfill(' ')
505                 << "PEAK handle: " << setw(2) << peak.handle
506                 << ".00ms, update: " << setw(2) << peak.update
507                 << ".00ms, render: " << setw(2) << peak.render
508                 << ".00ms, running: " << setw(2) << peak.running
509                 << ".00ms, waiting: " << setw(2) << peak.waiting
510                 << ".00ms, total: " << setw(2) << peak.total
511                 << ".00ms" << endl
512                 << " AVG handle: " << setw(5) << avg.handle
513                 << "ms, update: " << setw(5) << avg.update
514                 << "ms, render: " << setw(5) << avg.render
515                 << "ms, running: " << setw(5) << avg.running
516                 << "ms, waiting: " << setw(5) << avg.waiting
517                 << "ms, total: " << setw(5) << avg.total
518                 << "ms" << endl;
519 }
520
521
522 MessageState::MessageState(Environment &env)
523 : env(env) {
524         message.Position(glm::vec3(0.0f), graphics::Gravity::CENTER);
525         message.Hide();
526         press_key.Position(glm::vec3(0.0f, env.assets.large_ui_font.LineSkip(), 0.0f), graphics::Gravity::CENTER);
527         press_key.Set(env.assets.small_ui_font, "press any key to continue");
528         press_key.Show();
529 }
530
531 void MessageState::SetMessage(const char *msg) {
532         message.Set(env.assets.large_ui_font, msg);
533         message.Show();
534 }
535
536 void MessageState::ClearMessage() {
537         message.Hide();
538 }
539
540 void MessageState::Handle(const SDL_Event &e) {
541         if (e.type == SDL_QUIT || e.type == SDL_KEYDOWN) {
542                 env.state.Pop();
543         }
544 }
545
546 void MessageState::Update(int) {
547
548 }
549
550 void MessageState::Render(graphics::Viewport &viewport) {
551         if (message.Visible()) {
552                 message.Render(viewport);
553         }
554         if (press_key.Visible()) {
555                 press_key.Render(viewport);
556         }
557 }
558
559 }
560 }