1 #include "Application.hpp"
3 #include "Environment.hpp"
4 #include "FrameCounter.hpp"
6 #include "StateControl.hpp"
7 #include "TextureIndex.hpp"
10 #include "../audio/Sound.hpp"
11 #include "../graphics/ArrayTexture.hpp"
12 #include "../graphics/Font.hpp"
13 #include "../graphics/Texture.hpp"
14 #include "../io/TokenStreamReader.hpp"
15 #include "../model/shapes.hpp"
16 #include "../world/BlockType.hpp"
17 #include "../world/BlockTypeRegistry.hpp"
18 #include "../world/Entity.hpp"
24 #include <SDL_image.h>
26 using std::runtime_error;
32 HeadlessApplication::HeadlessApplication(HeadlessEnvironment &e)
38 HeadlessApplication::~HeadlessApplication() {
43 Application::Application(Environment &e)
44 : HeadlessApplication(e)
49 Application::~Application() {
54 void HeadlessApplication::RunN(size_t n) {
55 Uint32 last = SDL_GetTicks();
56 for (size_t i = 0; HasState() && i < n; ++i) {
57 Uint32 now = SDL_GetTicks();
58 int delta = now - last;
64 void HeadlessApplication::RunT(size_t t) {
65 Uint32 last = SDL_GetTicks();
66 Uint32 finish = last + t;
67 while (HasState() && last < finish) {
68 Uint32 now = SDL_GetTicks();
69 int delta = now - last;
75 void HeadlessApplication::RunS(size_t n, size_t t) {
76 for (size_t i = 0; HasState() && i < n; ++i) {
80 std::cout << std::setfill(' ') << std::setw(5) << std::right << (i + 1) << std::endl;
82 std::cout << std::flush;
88 void HeadlessApplication::Run() {
89 Uint32 last = SDL_GetTicks();
91 Uint32 now = SDL_GetTicks();
92 int delta = now - last;
98 void HeadlessApplication::Loop(int dt) {
99 env.counter.EnterFrame();
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 env.window.GrabMouse();
159 case SDL_WINDOWEVENT_FOCUS_LOST:
160 env.window.ReleaseMouse();
162 case SDL_WINDOWEVENT_RESIZED:
163 env.viewport.Resize(event.data1, event.data2);
170 void HeadlessApplication::Update(int dt) {
171 env.counter.EnterUpdate();
173 GetState().Update(dt);
175 env.counter.ExitUpdate();
178 void Application::Update(int dt) {
179 env.counter.EnterUpdate();
180 env.audio.Update(dt);
182 GetState().Update(dt);
184 env.counter.ExitUpdate();
187 void Application::Render() {
188 // gl implementation may (and will probably) delay vsync blocking until
189 // the first write after flipping, which is this clear call
190 env.viewport.Clear();
191 env.counter.EnterRender();
194 GetState().Render(env.viewport);
197 env.counter.ExitRender();
202 void HeadlessApplication::PushState(State *s) {
203 if (!states.empty()) {
204 states.top()->OnPause();
208 if (s->ref_count == 1) {
214 State *HeadlessApplication::PopState() {
215 State *s = states.top();
219 if (!states.empty()) {
220 states.top()->OnResume();
225 State *HeadlessApplication::SwitchState(State *s_new) {
226 State *s_old = states.top();
227 states.top() = s_new;
231 if (s_old->ref_count == 0) {
234 if (s_new->ref_count == 1) {
241 State &HeadlessApplication::GetState() {
242 return *states.top();
245 void HeadlessApplication::CommitStates() {
246 env.state.Commit(*this);
249 bool HeadlessApplication::HasState() const noexcept {
250 return !states.empty();
254 void StateControl::Commit(HeadlessApplication &app) {
255 while (!cue.empty()) {
260 app.PushState(m.state);
263 app.SwitchState(m.state);
269 while (app.HasState()) {
274 while (app.HasState() && &app.GetState() != m.state) {
279 while (app.HasState()) {
280 if (app.PopState() == m.state) {
289 AssetLoader::AssetLoader(const string &base)
290 : fonts(base + "fonts/")
291 , sounds(base + "sounds/")
292 , textures(base + "textures/")
293 , data(base + "data/") {
297 Assets::Assets(const AssetLoader &loader)
298 : large_ui_font(loader.LoadFont("DejaVuSans", 24))
299 , small_ui_font(loader.LoadFont("DejaVuSans", 16)) {
305 CuboidShape block_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }});
306 StairShape stair_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f });
307 CuboidShape slab_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }});
311 void AssetLoader::LoadBlockTypes(const std::string &set_name, BlockTypeRegistry ®, TextureIndex &tex_index) const {
312 string full = data + set_name + ".types";
313 std::ifstream file(full);
315 throw std::runtime_error("failed to open block type file " + full);
317 TokenStreamReader in(file);
322 while (in.HasMore()) {
323 in.ReadIdentifier(type_name);
324 in.Skip(Token::EQUALS);
328 in.Skip(Token::ANGLE_BRACKET_OPEN);
329 while (in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
330 in.ReadIdentifier(name);
331 in.Skip(Token::EQUALS);
332 if (name == "visible") {
333 type.visible = in.GetBool();
334 } else if (name == "texture") {
335 in.ReadString(tex_name);
336 type.texture = tex_index.GetID(tex_name);
337 } else if (name == "color") {
338 in.ReadVec(type.color);
339 } else if (name == "outline") {
340 in.ReadVec(type.outline_color);
341 } else if (name == "label") {
342 in.ReadString(type.label);
343 } else if (name == "luminosity") {
344 type.luminosity = in.GetInt();
345 } else if (name == "block_light") {
346 type.block_light = in.GetBool();
347 } else if (name == "collision") {
348 type.collision = in.GetBool();
349 } else if (name == "collide_block") {
350 type.collide_block = in.GetBool();
351 } else if (name == "shape") {
352 in.ReadIdentifier(shape_name);
353 if (shape_name == "block") {
354 type.shape = &block_shape;
355 type.fill = { true, true, true, true, true, true };
356 } else if (shape_name == "slab") {
357 type.shape = &slab_shape;
358 type.fill = { false, true, false, false, false, false };
359 } else if (shape_name == "stair") {
360 type.shape = &stair_shape;
361 type.fill = { false, true, false, false, false, true };
363 throw runtime_error("unknown block shape: " + shape_name);
366 throw runtime_error("unknown block property: " + name);
368 in.Skip(Token::SEMICOLON);
370 in.Skip(Token::ANGLE_BRACKET_CLOSE);
371 in.Skip(Token::SEMICOLON);
377 Font AssetLoader::LoadFont(const string &name, int size) const {
378 string full = fonts + name + ".ttf";
379 return Font(full.c_str(), size);
382 Sound AssetLoader::LoadSound(const string &name) const {
383 string full = sounds + name + ".wav";
384 return Sound(full.c_str());
387 Texture AssetLoader::LoadTexture(const string &name) const {
388 string full = textures + name + ".png";
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, 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);
416 void AssetLoader::LoadTextures(const TextureIndex &index, ArrayTexture &tex) const {
417 // TODO: where the hell should that size come from?
418 tex.Reserve(16, 16, index.Size(), Format());
419 for (const auto &entry : index.Entries()) {
420 LoadTexture(entry.first, tex, entry.second);
425 TextureIndex::TextureIndex()
430 int TextureIndex::GetID(const string &name) {
431 auto entry = id_map.find(name);
432 if (entry == id_map.end()) {
433 auto result = id_map.emplace(name, Size());
434 return result.first->second;
436 return entry->second;
441 void FrameCounter::EnterFrame() noexcept {
442 last_enter = SDL_GetTicks();
443 last_tick = last_enter;
446 void FrameCounter::EnterHandle() noexcept {
450 void FrameCounter::ExitHandle() noexcept {
451 current.handle = Tick();
454 void FrameCounter::EnterUpdate() noexcept {
458 void FrameCounter::ExitUpdate() noexcept {
459 current.update = Tick();
462 void FrameCounter::EnterRender() noexcept {
466 void FrameCounter::ExitRender() noexcept {
467 current.render = Tick();
470 void FrameCounter::ExitFrame() noexcept {
471 Uint32 now = SDL_GetTicks();
472 current.total = now - last_enter;
473 current.running = current.handle + current.update + current.render;
474 current.waiting = current.total - current.running;
478 if (cur_frame >= NUM_FRAMES) {
487 int FrameCounter::Tick() noexcept {
488 Uint32 now = SDL_GetTicks();
489 int delta = now - last_tick;
494 void FrameCounter::Accumulate() noexcept {
495 sum.handle += current.handle;
496 sum.update += current.update;
497 sum.render += current.render;
498 sum.running += current.running;
499 sum.waiting += current.waiting;
500 sum.total += current.total;
502 max.handle = std::max(current.handle, max.handle);
503 max.update = std::max(current.update, max.update);
504 max.render = std::max(current.render, max.render);
505 max.running = std::max(current.running, max.running);
506 max.waiting = std::max(current.waiting, max.waiting);
507 max.total = std::max(current.total, max.total);
509 current = Frame<int>();
512 void FrameCounter::Push() noexcept {
514 avg.handle = sum.handle * factor;
515 avg.update = sum.update * factor;
516 avg.render = sum.render * factor;
517 avg.running = sum.running * factor;
518 avg.waiting = sum.waiting * factor;
519 avg.total = sum.total * factor;