1 #include "Application.hpp"
3 #include "Environment.hpp"
4 #include "FrameCounter.hpp"
6 #include "StateControl.hpp"
9 #include "../audio/Sound.hpp"
10 #include "../graphics/ArrayTexture.hpp"
11 #include "../graphics/CubeMap.hpp"
12 #include "../graphics/Font.hpp"
13 #include "../graphics/Texture.hpp"
14 #include "../io/TokenStreamReader.hpp"
15 #include "../model/bounds.hpp"
16 #include "../model/Model.hpp"
17 #include "../model/ModelRegistry.hpp"
18 #include "../model/Shape.hpp"
19 #include "../model/ShapeRegistry.hpp"
20 #include "../shared/ResourceIndex.hpp"
21 #include "../world/BlockType.hpp"
22 #include "../world/BlockTypeRegistry.hpp"
23 #include "../world/Entity.hpp"
29 #include <SDL_image.h>
36 HeadlessApplication::HeadlessApplication(HeadlessEnvironment &e)
42 HeadlessApplication::~HeadlessApplication() {
47 Application::Application(Environment &e)
48 : HeadlessApplication(e)
53 Application::~Application() {
58 void HeadlessApplication::RunN(size_t n) {
59 Uint32 last = SDL_GetTicks();
60 for (size_t i = 0; HasState() && i < n; ++i) {
61 Uint32 now = SDL_GetTicks();
62 int delta = now - last;
68 void HeadlessApplication::RunT(size_t t) {
69 Uint32 last = SDL_GetTicks();
70 Uint32 finish = last + t;
71 while (HasState() && last < finish) {
72 Uint32 now = SDL_GetTicks();
73 int delta = now - last;
79 void HeadlessApplication::RunS(size_t n, size_t t) {
80 for (size_t i = 0; HasState() && i < n; ++i) {
84 cout << setfill(' ') << setw(5) << right << (i + 1) << endl;
92 void HeadlessApplication::Run() {
93 Uint32 last = SDL_GetTicks();
95 Uint32 now = SDL_GetTicks();
96 int delta = now - last;
102 void HeadlessApplication::Loop(int dt) {
103 env.counter.EnterFrame();
105 if (!HasState()) return;
108 if (!HasState()) return;
109 env.counter.ExitFrame();
112 void Application::Loop(int dt) {
113 env.counter.EnterFrame();
115 if (!HasState()) return;
118 if (!HasState()) return;
120 env.counter.ExitFrame();
124 void HeadlessApplication::HandleEvents() {
125 env.counter.EnterHandle();
127 while (HasState() && SDL_PollEvent(&event)) {
131 env.counter.ExitHandle();
134 void HeadlessApplication::Handle(const SDL_Event &event) {
135 GetState().Handle(event);
139 void Application::HandleEvents() {
140 env.counter.EnterHandle();
142 while (HasState() && SDL_PollEvent(&event)) {
146 env.counter.ExitHandle();
149 void Application::Handle(const SDL_Event &event) {
150 switch (event.type) {
151 case SDL_WINDOWEVENT:
152 Handle(event.window);
155 GetState().Handle(event);
160 void Application::Handle(const SDL_WindowEvent &event) {
161 switch (event.event) {
162 case SDL_WINDOWEVENT_FOCUS_GAINED:
163 GetState().OnFocus();
165 case SDL_WINDOWEVENT_FOCUS_LOST:
168 case SDL_WINDOWEVENT_RESIZED:
169 env.viewport.Resize(event.data1, event.data2);
170 GetState().OnResize(env.viewport);
177 void HeadlessApplication::Update(int dt) {
178 env.counter.EnterUpdate();
180 GetState().Update(dt);
182 env.counter.ExitUpdate();
185 void Application::Update(int dt) {
186 env.counter.EnterUpdate();
187 env.audio.Update(dt);
189 GetState().Update(dt);
191 env.counter.ExitUpdate();
194 void Application::Render() {
195 // gl implementation may (and will probably) delay vsync blocking until
196 // the first write after flipping, which is this clear call
197 env.viewport.Clear();
198 env.counter.EnterRender();
201 GetState().Render(env.viewport);
204 env.counter.ExitRender();
209 void HeadlessApplication::PushState(State *s) {
210 if (!states.empty()) {
211 states.top()->OnPause();
215 if (s->ref_count == 1) {
221 State *HeadlessApplication::PopState() {
222 State *s = states.top();
226 if (!states.empty()) {
227 states.top()->OnResume();
232 State *HeadlessApplication::SwitchState(State *s_new) {
233 State *s_old = states.top();
234 states.top() = s_new;
238 if (s_old->ref_count == 0) {
241 if (s_new->ref_count == 1) {
248 State &HeadlessApplication::GetState() {
249 return *states.top();
252 void HeadlessApplication::CommitStates() {
253 env.state.Commit(*this);
256 bool HeadlessApplication::HasState() const noexcept {
257 return !states.empty();
261 void StateControl::Commit(HeadlessApplication &app) {
262 while (!cue.empty()) {
267 app.PushState(m.state);
270 app.SwitchState(m.state);
276 while (app.HasState()) {
281 while (app.HasState() && &app.GetState() != m.state) {
286 while (app.HasState()) {
287 if (app.PopState() == m.state) {
296 AssetLoader::AssetLoader(const string &base)
297 : fonts(base + "fonts/")
298 , sounds(base + "sounds/")
299 , textures(base + "textures/")
300 , data(base + "data/") {
304 Assets::Assets(const AssetLoader &loader)
305 : large_ui_font(loader.LoadFont("DejaVuSans", 24))
306 , small_ui_font(loader.LoadFont("DejaVuSans", 16)) {
310 void AssetLoader::LoadBlockTypes(
311 const string &set_name,
312 BlockTypeRegistry ®,
313 ResourceIndex &snd_index,
314 ResourceIndex &tex_index,
315 const ShapeRegistry &shapes
317 string full = data + set_name + ".types";
320 throw runtime_error("failed to open block type file " + full);
322 TokenStreamReader in(file);
324 while (in.HasMore()) {
326 in.ReadIdentifier(type.name);
327 in.Skip(Token::EQUALS);
328 if (in.Peek().type == Token::IDENTIFIER) {
330 in.ReadIdentifier(proto);
331 type.Copy(reg.Get(proto));
333 type.Read(in, snd_index, tex_index, shapes);
334 in.Skip(Token::SEMICOLON);
339 CubeMap AssetLoader::LoadCubeMap(const string &name) const {
340 string full = textures + name;
341 string right = full + "-right.png";
342 string left = full + "-left.png";
343 string top = full + "-top.png";
344 string bottom = full + "-bottom.png";
345 string back = full + "-back.png";
346 string front = full + "-front.png";
352 if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
354 cm.Data(CubeMap::RIGHT, *srf);
356 SDL_FreeSurface(srf);
359 SDL_FreeSurface(srf);
361 if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
363 cm.Data(CubeMap::LEFT, *srf);
365 SDL_FreeSurface(srf);
368 SDL_FreeSurface(srf);
370 if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
372 cm.Data(CubeMap::TOP, *srf);
374 SDL_FreeSurface(srf);
377 SDL_FreeSurface(srf);
379 if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
381 cm.Data(CubeMap::BOTTOM, *srf);
383 SDL_FreeSurface(srf);
386 SDL_FreeSurface(srf);
388 if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
390 cm.Data(CubeMap::BACK, *srf);
392 SDL_FreeSurface(srf);
395 SDL_FreeSurface(srf);
397 if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
399 cm.Data(CubeMap::FRONT, *srf);
401 SDL_FreeSurface(srf);
404 SDL_FreeSurface(srf);
412 Font AssetLoader::LoadFont(const string &name, int size) const {
413 string full = fonts + name + ".ttf";
414 return Font(full.c_str(), size);
417 void AssetLoader::LoadModels(
418 const string &set_name,
419 ModelRegistry &models,
420 ResourceIndex &tex_index,
421 const ShapeRegistry &shapes
423 string full = data + set_name + ".models";
426 throw runtime_error("failed to open model file " + full);
428 TokenStreamReader in(file);
431 while (in.HasMore()) {
432 in.ReadIdentifier(model_name);
433 in.Skip(Token::EQUALS);
434 in.Skip(Token::ANGLE_BRACKET_OPEN);
435 Model &model = models.Add(model_name);
436 while (in.HasMore() && in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
437 in.ReadIdentifier(prop_name);
438 in.Skip(Token::EQUALS);
439 if (prop_name == "root") {
440 model.RootPart().Read(in, tex_index, shapes);
441 } else if (prop_name == "body") {
442 model.SetBody(in.GetULong());
443 } else if (prop_name == "eyes") {
444 model.SetEyes(in.GetULong());
446 while (in.HasMore() && in.Peek().type != Token::SEMICOLON) {
450 in.Skip(Token::SEMICOLON);
453 in.Skip(Token::ANGLE_BRACKET_CLOSE);
454 in.Skip(Token::SEMICOLON);
458 void AssetLoader::LoadShapes(const string &set_name, ShapeRegistry &shapes) const {
459 string full = data + set_name + ".shapes";
462 throw runtime_error("failed to open shape file " + full);
464 TokenStreamReader in(file);
466 while (in.HasMore()) {
467 in.ReadIdentifier(shape_name);
468 in.Skip(Token::EQUALS);
469 Shape &shape = shapes.Add(shape_name);
471 in.Skip(Token::SEMICOLON);
475 Sound AssetLoader::LoadSound(const string &name) const {
476 string full = sounds + name + ".wav";
477 return Sound(full.c_str());
480 Texture AssetLoader::LoadTexture(const string &name) const {
481 string full = textures + name + ".png";
483 SDL_Surface *srf = IMG_Load(full.c_str());
485 throw SDLError("IMG_Load");
489 SDL_FreeSurface(srf);
493 void AssetLoader::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
494 string full = textures + name + ".png";
495 SDL_Surface *srf = IMG_Load(full.c_str());
497 throw SDLError("IMG_Load");
501 tex.Data(layer, *srf);
503 SDL_FreeSurface(srf);
506 SDL_FreeSurface(srf);
509 void AssetLoader::LoadTextures(const ResourceIndex &index, ArrayTexture &tex) const {
510 // TODO: where the hell should that size come from?
511 tex.Reserve(16, 16, index.Size(), Format());
512 for (const auto &entry : index.Entries()) {
513 LoadTexture(entry.first, tex, entry.second);
518 void FrameCounter::EnterFrame() noexcept {
519 last_enter = SDL_GetTicks();
520 last_tick = last_enter;
523 void FrameCounter::EnterHandle() noexcept {
527 void FrameCounter::ExitHandle() noexcept {
528 current.handle = Tick();
531 void FrameCounter::EnterUpdate() noexcept {
535 void FrameCounter::ExitUpdate() noexcept {
536 current.update = Tick();
539 void FrameCounter::EnterRender() noexcept {
543 void FrameCounter::ExitRender() noexcept {
544 current.render = Tick();
547 void FrameCounter::ExitFrame() noexcept {
548 Uint32 now = SDL_GetTicks();
549 current.total = now - last_enter;
550 current.running = current.handle + current.update + current.render;
551 current.waiting = current.total - current.running;
555 if (cur_frame >= NUM_FRAMES) {
564 int FrameCounter::Tick() noexcept {
565 Uint32 now = SDL_GetTicks();
566 int delta = now - last_tick;
571 void FrameCounter::Accumulate() noexcept {
572 sum.handle += current.handle;
573 sum.update += current.update;
574 sum.render += current.render;
575 sum.running += current.running;
576 sum.waiting += current.waiting;
577 sum.total += current.total;
579 max.handle = std::max(current.handle, max.handle);
580 max.update = std::max(current.update, max.update);
581 max.render = std::max(current.render, max.render);
582 max.running = std::max(current.running, max.running);
583 max.waiting = std::max(current.waiting, max.waiting);
584 max.total = std::max(current.total, max.total);
586 current = Frame<int>();
589 void FrameCounter::Push() noexcept {
591 avg.handle = sum.handle * factor;
592 avg.update = sum.update * factor;
593 avg.render = sum.render * factor;
594 avg.running = sum.running * factor;
595 avg.waiting = sum.waiting * factor;
596 avg.total = sum.total * factor;
604 void FrameCounter::Print(ostream &out) const {
605 out << fixed << right << setprecision(2) << setfill(' ')
606 << "PEAK handle: " << setw(2) << peak.handle
607 << ".00ms, update: " << setw(2) << peak.update
608 << ".00ms, render: " << setw(2) << peak.render
609 << ".00ms, running: " << setw(2) << peak.running
610 << ".00ms, waiting: " << setw(2) << peak.waiting
611 << ".00ms, total: " << setw(2) << peak.total
613 << " AVG handle: " << setw(5) << avg.handle
614 << "ms, update: " << setw(5) << avg.update
615 << "ms, render: " << setw(5) << avg.render
616 << "ms, running: " << setw(5) << avg.running
617 << "ms, waiting: " << setw(5) << avg.waiting
618 << "ms, total: " << setw(5) << avg.total