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 Application::Application(Environment &e)
38 Application::~Application() {
43 void Application::RunN(size_t n) {
44 Uint32 last = SDL_GetTicks();
45 for (size_t i = 0; HasState() && i < n; ++i) {
46 Uint32 now = SDL_GetTicks();
47 int delta = now - last;
53 void Application::RunT(size_t t) {
54 Uint32 last = SDL_GetTicks();
55 Uint32 finish = last + t;
56 while (HasState() && last < finish) {
57 Uint32 now = SDL_GetTicks();
58 int delta = now - last;
64 void Application::RunS(size_t n, size_t t) {
65 for (size_t i = 0; HasState() && i < n; ++i) {
69 std::cout << std::setfill(' ') << std::setw(5) << std::right << (i + 1) << std::endl;
71 std::cout << std::flush;
77 void Application::Run() {
78 Uint32 last = SDL_GetTicks();
79 env.window.GrabMouse();
81 Uint32 now = SDL_GetTicks();
82 int delta = now - last;
88 void Application::Loop(int dt) {
89 env.counter.EnterFrame();
91 if (!HasState()) return;
93 env.state.Commit(*this);
94 if (!HasState()) return;
96 env.counter.ExitFrame();
100 void Application::HandleEvents() {
101 env.counter.EnterHandle();
103 while (HasState() && SDL_PollEvent(&event)) {
105 env.state.Commit(*this);
107 env.counter.ExitHandle();
110 void Application::Handle(const SDL_Event &event) {
111 switch (event.type) {
112 case SDL_WINDOWEVENT:
113 Handle(event.window);
116 GetState().Handle(event);
121 void Application::Handle(const SDL_WindowEvent &event) {
122 switch (event.event) {
123 case SDL_WINDOWEVENT_FOCUS_GAINED:
124 env.window.GrabMouse();
126 case SDL_WINDOWEVENT_FOCUS_LOST:
127 env.window.ReleaseMouse();
129 case SDL_WINDOWEVENT_RESIZED:
130 env.viewport.Resize(event.data1, event.data2);
137 void Application::Update(int dt) {
138 env.counter.EnterUpdate();
139 env.audio.Update(dt);
141 GetState().Update(dt);
143 env.counter.ExitUpdate();
146 void Application::Render() {
147 // gl implementation may (and will probably) delay vsync blocking until
148 // the first write after flipping, which is this clear call
149 env.viewport.Clear();
150 env.counter.EnterRender();
153 GetState().Render(env.viewport);
156 env.counter.ExitRender();
161 void Application::PushState(State *s) {
162 if (!states.empty()) {
163 states.top()->OnPause();
167 if (s->ref_count == 1) {
173 State *Application::PopState() {
174 State *s = states.top();
178 if (!states.empty()) {
179 states.top()->OnResume();
184 State *Application::SwitchState(State *s_new) {
185 State *s_old = states.top();
186 states.top() = s_new;
190 if (s_old->ref_count == 0) {
193 if (s_new->ref_count == 1) {
200 State &Application::GetState() {
201 return *states.top();
204 bool Application::HasState() const noexcept {
205 return !states.empty();
209 void StateControl::Commit(Application &app) {
210 while (!cue.empty()) {
215 app.PushState(m.state);
218 app.SwitchState(m.state);
224 while (app.HasState()) {
233 Assets::Assets(const string &base)
234 : fonts(base + "fonts/")
235 , sounds(base + "sounds/")
236 , textures(base + "textures/")
237 , data(base + "data/")
238 , large_ui_font(LoadFont("DejaVuSans", 24))
239 , small_ui_font(LoadFont("DejaVuSans", 16)) {
245 CuboidShape block_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }});
246 StairShape stair_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f });
247 CuboidShape slab_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }});
251 void Assets::LoadBlockTypes(const std::string &set_name, BlockTypeRegistry ®, TextureIndex &tex_index) const {
252 string full = data + set_name + ".types";
253 std::ifstream file(full);
255 throw std::runtime_error("failed to open block type file " + full);
257 TokenStreamReader in(file);
262 while (in.HasMore()) {
263 in.ReadIdentifier(type_name);
264 in.Skip(Token::EQUALS);
268 in.Skip(Token::ANGLE_BRACKET_OPEN);
269 while (in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
270 in.ReadIdentifier(name);
271 in.Skip(Token::EQUALS);
272 if (name == "visible") {
273 type.visible = in.GetBool();
274 } else if (name == "texture") {
275 in.ReadString(tex_name);
276 type.texture = tex_index.GetID(tex_name);
277 } else if (name == "color") {
278 in.ReadVec(type.color);
279 } else if (name == "outline") {
280 in.ReadVec(type.outline_color);
281 } else if (name == "label") {
282 in.ReadString(type.label);
283 } else if (name == "luminosity") {
284 type.luminosity = in.GetInt();
285 } else if (name == "block_light") {
286 type.block_light = in.GetBool();
287 } else if (name == "collision") {
288 type.collision = in.GetBool();
289 } else if (name == "collide_block") {
290 type.collide_block = in.GetBool();
291 } else if (name == "shape") {
292 in.ReadIdentifier(shape_name);
293 if (shape_name == "block") {
294 type.shape = &block_shape;
295 type.fill = { true, true, true, true, true, true };
296 } else if (shape_name == "slab") {
297 type.shape = &slab_shape;
298 type.fill = { false, true, false, false, false, false };
299 } else if (shape_name == "stair") {
300 type.shape = &stair_shape;
301 type.fill = { false, true, false, false, false, true };
303 throw runtime_error("unknown block shape: " + shape_name);
306 throw runtime_error("unknown block property: " + name);
308 in.Skip(Token::SEMICOLON);
310 in.Skip(Token::ANGLE_BRACKET_CLOSE);
311 in.Skip(Token::SEMICOLON);
317 Font Assets::LoadFont(const string &name, int size) const {
318 string full = fonts + name + ".ttf";
319 return Font(full.c_str(), size);
322 Sound Assets::LoadSound(const string &name) const {
323 string full = sounds + name + ".wav";
324 return Sound(full.c_str());
327 Texture Assets::LoadTexture(const string &name) const {
328 string full = textures + name + ".png";
330 SDL_Surface *srf = IMG_Load(full.c_str());
332 throw SDLError("IMG_Load");
336 SDL_FreeSurface(srf);
340 void Assets::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
341 string full = textures + name + ".png";
342 SDL_Surface *srf = IMG_Load(full.c_str());
344 throw SDLError("IMG_Load");
348 tex.Data(layer, *srf);
350 SDL_FreeSurface(srf);
353 SDL_FreeSurface(srf);
356 void Assets::LoadTextures(const TextureIndex &index, ArrayTexture &tex) const {
357 // TODO: where the hell should that size come from?
358 tex.Reserve(16, 16, index.Size(), Format());
359 for (const auto &entry : index.Entries()) {
360 LoadTexture(entry.first, tex, entry.second);
365 TextureIndex::TextureIndex()
370 int TextureIndex::GetID(const string &name) {
371 auto entry = id_map.find(name);
372 if (entry == id_map.end()) {
373 auto result = id_map.emplace(name, Size());
374 return result.first->second;
376 return entry->second;
381 void FrameCounter::EnterFrame() noexcept {
382 last_enter = SDL_GetTicks();
383 last_tick = last_enter;
386 void FrameCounter::EnterHandle() noexcept {
390 void FrameCounter::ExitHandle() noexcept {
391 current.handle = Tick();
394 void FrameCounter::EnterUpdate() noexcept {
398 void FrameCounter::ExitUpdate() noexcept {
399 current.update = Tick();
402 void FrameCounter::EnterRender() noexcept {
406 void FrameCounter::ExitRender() noexcept {
407 current.render = Tick();
410 void FrameCounter::ExitFrame() noexcept {
411 Uint32 now = SDL_GetTicks();
412 current.total = now - last_enter;
413 current.running = current.handle + current.update + current.render;
414 current.waiting = current.total - current.running;
418 if (cur_frame >= NUM_FRAMES) {
427 int FrameCounter::Tick() noexcept {
428 Uint32 now = SDL_GetTicks();
429 int delta = now - last_tick;
434 void FrameCounter::Accumulate() noexcept {
435 sum.handle += current.handle;
436 sum.update += current.update;
437 sum.render += current.render;
438 sum.running += current.running;
439 sum.waiting += current.waiting;
440 sum.total += current.total;
442 max.handle = std::max(current.handle, max.handle);
443 max.update = std::max(current.update, max.update);
444 max.render = std::max(current.render, max.render);
445 max.running = std::max(current.running, max.running);
446 max.waiting = std::max(current.waiting, max.waiting);
447 max.total = std::max(current.total, max.total);
449 current = Frame<int>();
452 void FrameCounter::Push() noexcept {
454 avg.handle = sum.handle * factor;
455 avg.update = sum.update * factor;
456 avg.render = sum.render * factor;
457 avg.running = sum.running * factor;
458 avg.waiting = sum.waiting * factor;
459 avg.total = sum.total * factor;