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"
23 #include <SDL_image.h>
25 using std::runtime_error;
31 Application::Application(Environment &e)
37 Application::~Application() {
42 void Application::RunN(size_t n) {
43 Uint32 last = SDL_GetTicks();
44 for (size_t i = 0; HasState() && i < n; ++i) {
45 Uint32 now = SDL_GetTicks();
46 int delta = now - last;
52 void Application::RunT(size_t t) {
53 Uint32 last = SDL_GetTicks();
54 Uint32 finish = last + t;
55 while (HasState() && last < finish) {
56 Uint32 now = SDL_GetTicks();
57 int delta = now - last;
63 void Application::RunS(size_t n, size_t t) {
64 for (size_t i = 0; HasState() && i < n; ++i) {
70 void Application::Run() {
71 Uint32 last = SDL_GetTicks();
72 env.window.GrabMouse();
74 Uint32 now = SDL_GetTicks();
75 int delta = now - last;
81 void Application::Loop(int dt) {
82 env.counter.EnterFrame();
84 if (!HasState()) return;
86 env.state.Commit(*this);
87 if (!HasState()) return;
89 env.counter.ExitFrame();
93 void Application::HandleEvents() {
94 env.counter.EnterHandle();
96 while (HasState() && SDL_PollEvent(&event)) {
98 env.state.Commit(*this);
100 env.counter.ExitHandle();
103 void Application::Handle(const SDL_Event &event) {
104 switch (event.type) {
105 case SDL_WINDOWEVENT:
106 Handle(event.window);
109 GetState().Handle(event);
114 void Application::Handle(const SDL_WindowEvent &event) {
115 switch (event.event) {
116 case SDL_WINDOWEVENT_FOCUS_GAINED:
117 env.window.GrabMouse();
119 case SDL_WINDOWEVENT_FOCUS_LOST:
120 env.window.ReleaseMouse();
122 case SDL_WINDOWEVENT_RESIZED:
123 env.viewport.Resize(event.data1, event.data2);
130 void Application::Update(int dt) {
131 env.counter.EnterUpdate();
132 env.audio.Update(dt);
134 GetState().Update(dt);
136 env.counter.ExitUpdate();
139 void Application::Render() {
140 // gl implementation may (and will probably) delay vsync blocking until
141 // the first write after flipping, which is this clear call
142 env.viewport.Clear();
143 env.counter.EnterRender();
146 GetState().Render(env.viewport);
149 env.counter.ExitRender();
154 void Application::PushState(State *s) {
155 if (!states.empty()) {
156 states.top()->OnPause();
160 if (s->ref_count == 1) {
166 State *Application::PopState() {
167 State *s = states.top();
171 if (!states.empty()) {
172 states.top()->OnResume();
177 State *Application::SwitchState(State *s_new) {
178 State *s_old = states.top();
179 states.top() = s_new;
183 if (s_old->ref_count == 0) {
186 if (s_new->ref_count == 1) {
193 State &Application::GetState() {
194 return *states.top();
197 bool Application::HasState() const noexcept {
198 return !states.empty();
202 void StateControl::Commit(Application &app) {
203 while (!cue.empty()) {
208 app.PushState(m.state);
211 app.SwitchState(m.state);
217 while (app.HasState()) {
226 Assets::Assets(const string &base)
227 : fonts(base + "fonts/")
228 , sounds(base + "sounds/")
229 , textures(base + "textures/")
230 , data(base + "data/")
231 , large_ui_font(LoadFont("DejaVuSans", 24))
232 , small_ui_font(LoadFont("DejaVuSans", 16)) {
238 CuboidShape block_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }});
239 StairShape stair_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f });
240 CuboidShape slab_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }});
244 void Assets::LoadBlockTypes(const std::string &set_name, BlockTypeRegistry ®, TextureIndex &tex_index) const {
245 string full = data + set_name + ".types";
246 std::ifstream file(full);
248 throw std::runtime_error("failed to open block type file " + full);
250 TokenStreamReader in(file);
255 while (in.HasMore()) {
256 in.ReadIdentifier(type_name);
257 in.Skip(Token::EQUALS);
261 in.Skip(Token::ANGLE_BRACKET_OPEN);
262 while (in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
263 in.ReadIdentifier(name);
264 in.Skip(Token::EQUALS);
265 if (name == "visible") {
266 type.visible = in.GetBool();
267 } else if (name == "texture") {
268 in.ReadString(tex_name);
269 type.texture = tex_index.GetID(tex_name);
270 } else if (name == "color") {
271 in.ReadVec(type.color);
272 } else if (name == "label") {
273 in.ReadString(type.label);
274 } else if (name == "luminosity") {
275 type.luminosity = in.GetInt();
276 } else if (name == "block_light") {
277 type.block_light = in.GetBool();
278 } else if (name == "collision") {
279 type.collision = in.GetBool();
280 } else if (name == "collide_block") {
281 type.collide_block = in.GetBool();
282 } else if (name == "shape") {
283 in.ReadIdentifier(shape_name);
284 if (shape_name == "block") {
285 type.shape = &block_shape;
286 type.fill = { true, true, true, true, true, true };
287 } else if (shape_name == "slab") {
288 type.shape = &slab_shape;
289 type.fill = { false, true, false, false, false, false };
290 } else if (shape_name == "stair") {
291 type.shape = &stair_shape;
292 type.fill = { false, true, false, false, false, true };
294 throw runtime_error("unknown block shape: " + shape_name);
297 throw runtime_error("unknown block property: " + name);
299 in.Skip(Token::SEMICOLON);
301 in.Skip(Token::ANGLE_BRACKET_CLOSE);
302 in.Skip(Token::SEMICOLON);
308 Font Assets::LoadFont(const string &name, int size) const {
309 string full = fonts + name + ".ttf";
310 return Font(full.c_str(), size);
313 Sound Assets::LoadSound(const string &name) const {
314 string full = sounds + name + ".wav";
315 return Sound(full.c_str());
318 Texture Assets::LoadTexture(const string &name) const {
319 string full = textures + name + ".png";
321 SDL_Surface *srf = IMG_Load(full.c_str());
323 throw SDLError("IMG_Load");
327 SDL_FreeSurface(srf);
331 void Assets::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
332 string full = textures + name + ".png";
333 SDL_Surface *srf = IMG_Load(full.c_str());
335 throw SDLError("IMG_Load");
339 tex.Data(layer, *srf);
341 SDL_FreeSurface(srf);
344 SDL_FreeSurface(srf);
347 void Assets::LoadTextures(const TextureIndex &index, ArrayTexture &tex) const {
348 // TODO: where the hell should that size come from?
349 tex.Reserve(16, 16, index.Size(), Format());
350 for (const auto &entry : index.Entries()) {
351 LoadTexture(entry.first, tex, entry.second);
356 TextureIndex::TextureIndex()
361 int TextureIndex::GetID(const string &name) {
362 auto entry = id_map.find(name);
363 if (entry == id_map.end()) {
364 auto result = id_map.emplace(name, Size());
365 return result.first->second;
367 return entry->second;
372 void FrameCounter::EnterFrame() noexcept {
373 last_enter = SDL_GetTicks();
374 last_tick = last_enter;
377 void FrameCounter::EnterHandle() noexcept {
381 void FrameCounter::ExitHandle() noexcept {
382 current.handle = Tick();
385 void FrameCounter::EnterUpdate() noexcept {
389 void FrameCounter::ExitUpdate() noexcept {
390 current.update = Tick();
393 void FrameCounter::EnterRender() noexcept {
397 void FrameCounter::ExitRender() noexcept {
398 current.render = Tick();
401 void FrameCounter::ExitFrame() noexcept {
402 Uint32 now = SDL_GetTicks();
403 current.total = now - last_enter;
404 current.running = current.handle + current.update + current.render;
405 current.waiting = current.total - current.running;
409 if (cur_frame >= NUM_FRAMES) {
418 int FrameCounter::Tick() noexcept {
419 Uint32 now = SDL_GetTicks();
420 int delta = now - last_tick;
425 void FrameCounter::Accumulate() noexcept {
426 sum.handle += current.handle;
427 sum.update += current.update;
428 sum.render += current.render;
429 sum.running += current.running;
430 sum.waiting += current.waiting;
431 sum.total += current.total;
433 max.handle = std::max(current.handle, max.handle);
434 max.update = std::max(current.update, max.update);
435 max.render = std::max(current.render, max.render);
436 max.running = std::max(current.running, max.running);
437 max.waiting = std::max(current.waiting, max.waiting);
438 max.total = std::max(current.total, max.total);
440 current = Frame<int>();
443 void FrameCounter::Push() noexcept {
445 avg.handle = sum.handle * factor;
446 avg.update = sum.update * factor;
447 avg.render = sum.render * factor;
448 avg.running = sum.running * factor;
449 avg.waiting = sum.waiting * factor;
450 avg.total = sum.total * factor;