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>
31 using std::runtime_error;
37 HeadlessApplication::HeadlessApplication(HeadlessEnvironment &e)
43 HeadlessApplication::~HeadlessApplication() {
48 Application::Application(Environment &e)
49 : HeadlessApplication(e)
54 Application::~Application() {
59 void HeadlessApplication::RunN(size_t n) {
60 Uint32 last = SDL_GetTicks();
61 for (size_t i = 0; HasState() && i < n; ++i) {
62 Uint32 now = SDL_GetTicks();
63 int delta = now - last;
69 void HeadlessApplication::RunT(size_t t) {
70 Uint32 last = SDL_GetTicks();
71 Uint32 finish = last + t;
72 while (HasState() && last < finish) {
73 Uint32 now = SDL_GetTicks();
74 int delta = now - last;
80 void HeadlessApplication::RunS(size_t n, size_t t) {
81 for (size_t i = 0; HasState() && i < n; ++i) {
85 std::cout << std::setfill(' ') << std::setw(5) << std::right << (i + 1) << std::endl;
87 std::cout << std::flush;
93 void HeadlessApplication::Run() {
94 Uint32 last = SDL_GetTicks();
96 Uint32 now = SDL_GetTicks();
97 int delta = now - last;
103 void HeadlessApplication::Loop(int dt) {
104 env.counter.EnterFrame();
106 if (!HasState()) return;
109 if (!HasState()) return;
110 env.counter.ExitFrame();
113 void Application::Loop(int dt) {
114 env.counter.EnterFrame();
116 if (!HasState()) return;
119 if (!HasState()) return;
121 env.counter.ExitFrame();
125 void HeadlessApplication::HandleEvents() {
126 env.counter.EnterHandle();
128 while (HasState() && SDL_PollEvent(&event)) {
132 env.counter.ExitHandle();
135 void HeadlessApplication::Handle(const SDL_Event &event) {
136 GetState().Handle(event);
140 void Application::HandleEvents() {
141 env.counter.EnterHandle();
143 while (HasState() && SDL_PollEvent(&event)) {
147 env.counter.ExitHandle();
150 void Application::Handle(const SDL_Event &event) {
151 switch (event.type) {
152 case SDL_WINDOWEVENT:
153 Handle(event.window);
156 GetState().Handle(event);
161 void Application::Handle(const SDL_WindowEvent &event) {
162 switch (event.event) {
163 case SDL_WINDOWEVENT_FOCUS_GAINED:
164 GetState().OnFocus();
166 case SDL_WINDOWEVENT_FOCUS_LOST:
169 case SDL_WINDOWEVENT_RESIZED:
170 env.viewport.Resize(event.data1, event.data2);
171 GetState().OnResize(env.viewport);
178 void HeadlessApplication::Update(int dt) {
179 env.counter.EnterUpdate();
181 GetState().Update(dt);
183 env.counter.ExitUpdate();
186 void Application::Update(int dt) {
187 env.counter.EnterUpdate();
188 env.audio.Update(dt);
190 GetState().Update(dt);
192 env.counter.ExitUpdate();
195 void Application::Render() {
196 // gl implementation may (and will probably) delay vsync blocking until
197 // the first write after flipping, which is this clear call
198 env.viewport.Clear();
199 env.counter.EnterRender();
202 GetState().Render(env.viewport);
205 env.counter.ExitRender();
210 void HeadlessApplication::PushState(State *s) {
211 if (!states.empty()) {
212 states.top()->OnPause();
216 if (s->ref_count == 1) {
222 State *HeadlessApplication::PopState() {
223 State *s = states.top();
227 if (!states.empty()) {
228 states.top()->OnResume();
233 State *HeadlessApplication::SwitchState(State *s_new) {
234 State *s_old = states.top();
235 states.top() = s_new;
239 if (s_old->ref_count == 0) {
242 if (s_new->ref_count == 1) {
249 State &HeadlessApplication::GetState() {
250 return *states.top();
253 void HeadlessApplication::CommitStates() {
254 env.state.Commit(*this);
257 bool HeadlessApplication::HasState() const noexcept {
258 return !states.empty();
262 void StateControl::Commit(HeadlessApplication &app) {
263 while (!cue.empty()) {
268 app.PushState(m.state);
271 app.SwitchState(m.state);
277 while (app.HasState()) {
282 while (app.HasState() && &app.GetState() != m.state) {
287 while (app.HasState()) {
288 if (app.PopState() == m.state) {
297 AssetLoader::AssetLoader(const string &base)
298 : fonts(base + "fonts/")
299 , sounds(base + "sounds/")
300 , textures(base + "textures/")
301 , data(base + "data/") {
305 Assets::Assets(const AssetLoader &loader)
306 : large_ui_font(loader.LoadFont("DejaVuSans", 24))
307 , small_ui_font(loader.LoadFont("DejaVuSans", 16)) {
311 void AssetLoader::LoadBlockTypes(
312 const string &set_name,
313 BlockTypeRegistry ®,
314 ResourceIndex &snd_index,
315 ResourceIndex &tex_index,
316 const ShapeRegistry &shapes
318 string full = data + set_name + ".types";
319 std::ifstream file(full);
321 throw std::runtime_error("failed to open block type file " + full);
323 TokenStreamReader in(file);
325 while (in.HasMore()) {
327 in.ReadIdentifier(type.name);
328 in.Skip(Token::EQUALS);
329 if (in.Peek().type == Token::IDENTIFIER) {
331 in.ReadIdentifier(proto);
332 type.Copy(reg.Get(proto));
334 type.Read(in, snd_index, tex_index, shapes);
335 in.Skip(Token::SEMICOLON);
336 reg.Add(std::move(type));
340 CubeMap AssetLoader::LoadCubeMap(const string &name) const {
341 string full = textures + name;
342 string right = full + "-right.png";
343 string left = full + "-left.png";
344 string top = full + "-top.png";
345 string bottom = full + "-bottom.png";
346 string back = full + "-back.png";
347 string front = full + "-front.png";
353 if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
355 cm.Data(CubeMap::RIGHT, *srf);
357 SDL_FreeSurface(srf);
360 SDL_FreeSurface(srf);
362 if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
364 cm.Data(CubeMap::LEFT, *srf);
366 SDL_FreeSurface(srf);
369 SDL_FreeSurface(srf);
371 if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
373 cm.Data(CubeMap::TOP, *srf);
375 SDL_FreeSurface(srf);
378 SDL_FreeSurface(srf);
380 if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
382 cm.Data(CubeMap::BOTTOM, *srf);
384 SDL_FreeSurface(srf);
387 SDL_FreeSurface(srf);
389 if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
391 cm.Data(CubeMap::BACK, *srf);
393 SDL_FreeSurface(srf);
396 SDL_FreeSurface(srf);
398 if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
400 cm.Data(CubeMap::FRONT, *srf);
402 SDL_FreeSurface(srf);
405 SDL_FreeSurface(srf);
413 Font AssetLoader::LoadFont(const string &name, int size) const {
414 string full = fonts + name + ".ttf";
415 return Font(full.c_str(), size);
418 void AssetLoader::LoadModels(
419 const string &set_name,
420 ModelRegistry &models,
421 ResourceIndex &tex_index,
422 const ShapeRegistry &shapes
424 string full = data + set_name + ".models";
425 std::ifstream file(full);
427 throw std::runtime_error("failed to open model file " + full);
429 TokenStreamReader in(file);
432 while (in.HasMore()) {
433 in.ReadIdentifier(model_name);
434 in.Skip(Token::EQUALS);
435 in.Skip(Token::ANGLE_BRACKET_OPEN);
436 Model &model = models.Add(model_name);
437 while (in.HasMore() && in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
438 in.ReadIdentifier(prop_name);
439 in.Skip(Token::EQUALS);
440 if (prop_name == "root") {
441 model.RootPart().Read(in, tex_index, shapes);
442 } else if (prop_name == "body") {
443 model.SetBody(in.GetULong());
444 } else if (prop_name == "eyes") {
445 model.SetEyes(in.GetULong());
447 while (in.HasMore() && in.Peek().type != Token::SEMICOLON) {
451 in.Skip(Token::SEMICOLON);
454 in.Skip(Token::ANGLE_BRACKET_CLOSE);
455 in.Skip(Token::SEMICOLON);
459 void AssetLoader::LoadShapes(const string &set_name, ShapeRegistry &shapes) const {
460 string full = data + set_name + ".shapes";
461 std::ifstream file(full);
463 throw std::runtime_error("failed to open shape file " + full);
465 TokenStreamReader in(file);
467 while (in.HasMore()) {
468 in.ReadIdentifier(shape_name);
469 in.Skip(Token::EQUALS);
470 Shape &shape = shapes.Add(shape_name);
472 in.Skip(Token::SEMICOLON);
476 Sound AssetLoader::LoadSound(const string &name) const {
477 string full = sounds + name + ".wav";
478 return Sound(full.c_str());
481 Texture AssetLoader::LoadTexture(const string &name) const {
482 string full = textures + name + ".png";
484 SDL_Surface *srf = IMG_Load(full.c_str());
486 throw SDLError("IMG_Load");
490 SDL_FreeSurface(srf);
494 void AssetLoader::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
495 string full = textures + name + ".png";
496 SDL_Surface *srf = IMG_Load(full.c_str());
498 throw SDLError("IMG_Load");
502 tex.Data(layer, *srf);
504 SDL_FreeSurface(srf);
507 SDL_FreeSurface(srf);
510 void AssetLoader::LoadTextures(const ResourceIndex &index, ArrayTexture &tex) const {
511 // TODO: where the hell should that size come from?
512 tex.Reserve(16, 16, index.Size(), Format());
513 for (const auto &entry : index.Entries()) {
514 LoadTexture(entry.first, tex, entry.second);
519 void FrameCounter::EnterFrame() noexcept {
520 last_enter = SDL_GetTicks();
521 last_tick = last_enter;
524 void FrameCounter::EnterHandle() noexcept {
528 void FrameCounter::ExitHandle() noexcept {
529 current.handle = Tick();
532 void FrameCounter::EnterUpdate() noexcept {
536 void FrameCounter::ExitUpdate() noexcept {
537 current.update = Tick();
540 void FrameCounter::EnterRender() noexcept {
544 void FrameCounter::ExitRender() noexcept {
545 current.render = Tick();
548 void FrameCounter::ExitFrame() noexcept {
549 Uint32 now = SDL_GetTicks();
550 current.total = now - last_enter;
551 current.running = current.handle + current.update + current.render;
552 current.waiting = current.total - current.running;
556 if (cur_frame >= NUM_FRAMES) {
565 int FrameCounter::Tick() noexcept {
566 Uint32 now = SDL_GetTicks();
567 int delta = now - last_tick;
572 void FrameCounter::Accumulate() noexcept {
573 sum.handle += current.handle;
574 sum.update += current.update;
575 sum.render += current.render;
576 sum.running += current.running;
577 sum.waiting += current.waiting;
578 sum.total += current.total;
580 max.handle = std::max(current.handle, max.handle);
581 max.update = std::max(current.update, max.update);
582 max.render = std::max(current.render, max.render);
583 max.running = std::max(current.running, max.running);
584 max.waiting = std::max(current.waiting, max.waiting);
585 max.total = std::max(current.total, max.total);
587 current = Frame<int>();
590 void FrameCounter::Push() noexcept {
592 avg.handle = sum.handle * factor;
593 avg.update = sum.update * factor;
594 avg.render = sum.render * factor;
595 avg.running = sum.running * factor;
596 avg.waiting = sum.waiting * factor;
597 avg.total = sum.total * factor;