1 #include "Application.hpp"
3 #include "Environment.hpp"
4 #include "FrameCounter.hpp"
5 #include "MessageState.hpp"
6 #include "ResourceIndex.hpp"
8 #include "StateControl.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"
22 #include <SDL_image.h>
30 HeadlessApplication::HeadlessApplication(HeadlessEnvironment &e)
36 HeadlessApplication::~HeadlessApplication() {
41 Application::Application(Environment &e)
42 : HeadlessApplication(e)
47 Application::~Application() {
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;
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;
73 void HeadlessApplication::RunS(size_t n, size_t t) {
74 for (size_t i = 0; HasState() && i < n; ++i) {
78 cout << setfill(' ') << setw(5) << right << (i + 1) << endl;
86 void HeadlessApplication::Run() {
87 Uint32 last = SDL_GetTicks();
89 Uint32 now = SDL_GetTicks();
90 int delta = now - last;
96 void HeadlessApplication::Loop(int dt) {
97 env.counter.EnterFrame();
99 if (!HasState()) return;
102 if (!HasState()) return;
103 env.counter.ExitFrame();
106 void Application::Loop(int dt) {
107 env.counter.EnterFrame();
109 if (!HasState()) return;
112 if (!HasState()) return;
114 env.counter.ExitFrame();
118 void HeadlessApplication::HandleEvents() {
119 env.counter.EnterHandle();
121 while (HasState() && SDL_PollEvent(&event)) {
125 env.counter.ExitHandle();
128 void HeadlessApplication::Handle(const SDL_Event &event) {
129 GetState().Handle(event);
133 void Application::HandleEvents() {
134 env.counter.EnterHandle();
136 while (HasState() && SDL_PollEvent(&event)) {
140 env.counter.ExitHandle();
143 void Application::Handle(const SDL_Event &event) {
144 switch (event.type) {
145 case SDL_WINDOWEVENT:
146 Handle(event.window);
149 GetState().Handle(event);
154 void Application::Handle(const SDL_WindowEvent &event) {
155 switch (event.event) {
156 case SDL_WINDOWEVENT_FOCUS_GAINED:
157 GetState().OnFocus();
159 case SDL_WINDOWEVENT_FOCUS_LOST:
162 case SDL_WINDOWEVENT_RESIZED:
163 env.viewport.Resize(event.data1, event.data2);
164 GetState().OnResize(env.viewport);
171 void HeadlessApplication::Update(int dt) {
172 env.counter.EnterUpdate();
174 GetState().Update(dt);
176 env.counter.ExitUpdate();
179 void Application::Update(int dt) {
180 env.counter.EnterUpdate();
181 env.audio.Update(dt);
183 GetState().Update(dt);
185 env.counter.ExitUpdate();
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();
195 GetState().Render(env.viewport);
198 env.counter.ExitRender();
203 void HeadlessApplication::PushState(State *s) {
204 if (!states.empty()) {
205 states.top()->OnPause();
209 if (s->ref_count == 1) {
215 State *HeadlessApplication::PopState() {
216 State *s = states.top();
220 if (!states.empty()) {
221 states.top()->OnResume();
226 State *HeadlessApplication::SwitchState(State *s_new) {
227 State *s_old = states.top();
228 states.top() = s_new;
232 if (s_old->ref_count == 0) {
235 if (s_new->ref_count == 1) {
242 State &HeadlessApplication::GetState() {
243 return *states.top();
246 void HeadlessApplication::CommitStates() {
247 env.state.Commit(*this);
250 bool HeadlessApplication::HasState() const noexcept {
251 return !states.empty();
255 void StateControl::Commit(HeadlessApplication &app) {
256 while (!cue.empty()) {
261 app.PushState(m.state);
264 app.SwitchState(m.state);
270 while (app.HasState()) {
275 while (app.HasState() && &app.GetState() != m.state) {
280 while (app.HasState()) {
281 if (app.PopState() == m.state) {
290 AssetLoader::AssetLoader(const string &base)
291 : fonts(base + "fonts/")
292 , sounds(base + "sounds/")
293 , textures(base + "textures/")
294 , data(base + "data/") {
298 Assets::Assets(const AssetLoader &loader)
299 : large_ui_font(loader.LoadFont("DejaVuSans", 24))
300 , small_ui_font(loader.LoadFont("DejaVuSans", 16)) {
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";
313 graphics::CubeMap cm;
317 if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
319 cm.Data(graphics::CubeMap::RIGHT, *srf);
321 SDL_FreeSurface(srf);
324 SDL_FreeSurface(srf);
326 if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
328 cm.Data(graphics::CubeMap::LEFT, *srf);
330 SDL_FreeSurface(srf);
333 SDL_FreeSurface(srf);
335 if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
337 cm.Data(graphics::CubeMap::TOP, *srf);
339 SDL_FreeSurface(srf);
342 SDL_FreeSurface(srf);
344 if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
346 cm.Data(graphics::CubeMap::BOTTOM, *srf);
348 SDL_FreeSurface(srf);
351 SDL_FreeSurface(srf);
353 if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
355 cm.Data(graphics::CubeMap::BACK, *srf);
357 SDL_FreeSurface(srf);
360 SDL_FreeSurface(srf);
362 if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
364 cm.Data(graphics::CubeMap::FRONT, *srf);
366 SDL_FreeSurface(srf);
369 SDL_FreeSurface(srf);
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);
382 audio::Sound AssetLoader::LoadSound(const string &name) const {
383 string full = sounds + name + ".wav";
384 return audio::Sound(full.c_str());
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());
392 throw SDLError("IMG_Load");
396 SDL_FreeSurface(srf);
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());
404 throw SDLError("IMG_Load");
408 tex.Data(layer, *srf);
410 SDL_FreeSurface(srf);
413 SDL_FreeSurface(srf);
417 void FrameCounter::EnterFrame() noexcept {
418 last_enter = SDL_GetTicks();
419 last_tick = last_enter;
422 void FrameCounter::EnterHandle() noexcept {
426 void FrameCounter::ExitHandle() noexcept {
427 current.handle = Tick();
430 void FrameCounter::EnterUpdate() noexcept {
434 void FrameCounter::ExitUpdate() noexcept {
435 current.update = Tick();
438 void FrameCounter::EnterRender() noexcept {
442 void FrameCounter::ExitRender() noexcept {
443 current.render = Tick();
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;
454 if (cur_frame >= NUM_FRAMES) {
463 int FrameCounter::Tick() noexcept {
464 Uint32 now = SDL_GetTicks();
465 int delta = now - last_tick;
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;
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);
485 current = Frame<int>();
488 void FrameCounter::Push() noexcept {
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;
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
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
522 MessageState::MessageState(Environment &env)
524 message.Position(glm::vec3(0.0f), graphics::Gravity::CENTER);
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");
531 void MessageState::SetMessage(const char *msg) {
532 message.Set(env.assets.large_ui_font, msg);
536 void MessageState::ClearMessage() {
540 void MessageState::Handle(const SDL_Event &e) {
541 if (e.type == SDL_QUIT || e.type == SDL_KEYDOWN) {
546 void MessageState::Update(int) {
550 void MessageState::Render(graphics::Viewport &viewport) {
551 if (message.Visible()) {
552 message.Render(viewport);
554 if (press_key.Visible()) {
555 press_key.Render(viewport);