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/CubeMap.hpp"
13 #include "../graphics/Font.hpp"
14 #include "../graphics/Texture.hpp"
15 #include "../io/TokenStreamReader.hpp"
16 #include "../model/bounds.hpp"
17 #include "../model/Shape.hpp"
18 #include "../model/ShapeRegistry.hpp"
19 #include "../world/BlockType.hpp"
20 #include "../world/BlockTypeRegistry.hpp"
21 #include "../world/Entity.hpp"
27 #include <SDL_image.h>
29 using std::runtime_error;
35 HeadlessApplication::HeadlessApplication(HeadlessEnvironment &e)
41 HeadlessApplication::~HeadlessApplication() {
46 Application::Application(Environment &e)
47 : HeadlessApplication(e)
52 Application::~Application() {
57 void HeadlessApplication::RunN(size_t n) {
58 Uint32 last = SDL_GetTicks();
59 for (size_t i = 0; HasState() && i < n; ++i) {
60 Uint32 now = SDL_GetTicks();
61 int delta = now - last;
67 void HeadlessApplication::RunT(size_t t) {
68 Uint32 last = SDL_GetTicks();
69 Uint32 finish = last + t;
70 while (HasState() && last < finish) {
71 Uint32 now = SDL_GetTicks();
72 int delta = now - last;
78 void HeadlessApplication::RunS(size_t n, size_t t) {
79 for (size_t i = 0; HasState() && i < n; ++i) {
83 std::cout << std::setfill(' ') << std::setw(5) << std::right << (i + 1) << std::endl;
85 std::cout << std::flush;
91 void HeadlessApplication::Run() {
92 Uint32 last = SDL_GetTicks();
94 Uint32 now = SDL_GetTicks();
95 int delta = now - last;
101 void HeadlessApplication::Loop(int dt) {
102 env.counter.EnterFrame();
104 if (!HasState()) return;
107 if (!HasState()) return;
108 env.counter.ExitFrame();
111 void Application::Loop(int dt) {
112 env.counter.EnterFrame();
114 if (!HasState()) return;
117 if (!HasState()) return;
119 env.counter.ExitFrame();
123 void HeadlessApplication::HandleEvents() {
124 env.counter.EnterHandle();
126 while (HasState() && SDL_PollEvent(&event)) {
130 env.counter.ExitHandle();
133 void HeadlessApplication::Handle(const SDL_Event &event) {
134 GetState().Handle(event);
138 void Application::HandleEvents() {
139 env.counter.EnterHandle();
141 while (HasState() && SDL_PollEvent(&event)) {
145 env.counter.ExitHandle();
148 void Application::Handle(const SDL_Event &event) {
149 switch (event.type) {
150 case SDL_WINDOWEVENT:
151 Handle(event.window);
154 GetState().Handle(event);
159 void Application::Handle(const SDL_WindowEvent &event) {
160 switch (event.event) {
161 case SDL_WINDOWEVENT_FOCUS_GAINED:
162 env.window.GrabMouse();
164 case SDL_WINDOWEVENT_FOCUS_LOST:
165 env.window.ReleaseMouse();
167 case SDL_WINDOWEVENT_RESIZED:
168 env.viewport.Resize(event.data1, event.data2);
175 void HeadlessApplication::Update(int dt) {
176 env.counter.EnterUpdate();
178 GetState().Update(dt);
180 env.counter.ExitUpdate();
183 void Application::Update(int dt) {
184 env.counter.EnterUpdate();
185 env.audio.Update(dt);
187 GetState().Update(dt);
189 env.counter.ExitUpdate();
192 void Application::Render() {
193 // gl implementation may (and will probably) delay vsync blocking until
194 // the first write after flipping, which is this clear call
195 env.viewport.Clear();
196 env.counter.EnterRender();
199 GetState().Render(env.viewport);
202 env.counter.ExitRender();
207 void HeadlessApplication::PushState(State *s) {
208 if (!states.empty()) {
209 states.top()->OnPause();
213 if (s->ref_count == 1) {
219 State *HeadlessApplication::PopState() {
220 State *s = states.top();
224 if (!states.empty()) {
225 states.top()->OnResume();
230 State *HeadlessApplication::SwitchState(State *s_new) {
231 State *s_old = states.top();
232 states.top() = s_new;
236 if (s_old->ref_count == 0) {
239 if (s_new->ref_count == 1) {
246 State &HeadlessApplication::GetState() {
247 return *states.top();
250 void HeadlessApplication::CommitStates() {
251 env.state.Commit(*this);
254 bool HeadlessApplication::HasState() const noexcept {
255 return !states.empty();
259 void StateControl::Commit(HeadlessApplication &app) {
260 while (!cue.empty()) {
265 app.PushState(m.state);
268 app.SwitchState(m.state);
274 while (app.HasState()) {
279 while (app.HasState() && &app.GetState() != m.state) {
284 while (app.HasState()) {
285 if (app.PopState() == m.state) {
294 AssetLoader::AssetLoader(const string &base)
295 : fonts(base + "fonts/")
296 , sounds(base + "sounds/")
297 , textures(base + "textures/")
298 , data(base + "data/") {
302 Assets::Assets(const AssetLoader &loader)
303 : large_ui_font(loader.LoadFont("DejaVuSans", 24))
304 , small_ui_font(loader.LoadFont("DejaVuSans", 16)) {
310 CuboidBounds block_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }});
311 StairBounds stair_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f });
312 CuboidBounds slab_shape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }});
316 void AssetLoader::LoadBlockTypes(
317 const string &set_name,
318 BlockTypeRegistry ®,
319 TextureIndex &tex_index,
320 const ShapeRegistry &shapes
322 string full = data + set_name + ".types";
323 std::ifstream file(full);
325 throw std::runtime_error("failed to open block type file " + full);
327 TokenStreamReader in(file);
332 while (in.HasMore()) {
333 in.ReadIdentifier(type_name);
334 in.Skip(Token::EQUALS);
338 in.Skip(Token::ANGLE_BRACKET_OPEN);
339 while (in.Peek().type != Token::ANGLE_BRACKET_CLOSE) {
340 in.ReadIdentifier(name);
341 in.Skip(Token::EQUALS);
342 if (name == "visible") {
343 type.visible = in.GetBool();
344 } else if (name == "texture") {
345 in.ReadString(tex_name);
346 type.textures.push_back(tex_index.GetID(tex_name));
347 } else if (name == "textures") {
348 in.Skip(Token::BRACKET_OPEN);
349 while (in.Peek().type != Token::BRACKET_CLOSE) {
350 in.ReadString(tex_name);
351 type.textures.push_back(tex_index.GetID(tex_name));
352 if (in.Peek().type == Token::COMMA) {
353 in.Skip(Token::COMMA);
356 in.Skip(Token::BRACKET_CLOSE);
357 } else if (name == "rgb_mod") {
358 in.ReadVec(type.rgb_mod);
359 } else if (name == "hsl_mod") {
360 in.ReadVec(type.hsl_mod);
361 } else if (name == "outline") {
362 in.ReadVec(type.outline_color);
363 } else if (name == "label") {
364 in.ReadString(type.label);
365 } else if (name == "luminosity") {
366 type.luminosity = in.GetInt();
367 } else if (name == "block_light") {
368 type.block_light = in.GetBool();
369 } else if (name == "collision") {
370 type.collision = in.GetBool();
371 } else if (name == "collide_block") {
372 type.collide_block = in.GetBool();
373 } else if (name == "generate") {
374 type.generate = in.GetBool();
375 } else if (name == "min_solidity") {
376 type.min_solidity = in.GetFloat();
377 } else if (name == "mid_solidity") {
378 type.mid_solidity = in.GetFloat();
379 } else if (name == "max_solidity") {
380 type.max_solidity = in.GetFloat();
381 } else if (name == "min_humidity") {
382 type.min_humidity = in.GetFloat();
383 } else if (name == "mid_humidity") {
384 type.mid_humidity = in.GetFloat();
385 } else if (name == "max_humidity") {
386 type.max_humidity = in.GetFloat();
387 } else if (name == "min_temperature") {
388 type.min_temperature = in.GetFloat();
389 } else if (name == "mid_temperature") {
390 type.mid_temperature = in.GetFloat();
391 } else if (name == "max_temperature") {
392 type.max_temperature = in.GetFloat();
393 } else if (name == "min_richness") {
394 type.min_richness = in.GetFloat();
395 } else if (name == "mid_richness") {
396 type.mid_richness = in.GetFloat();
397 } else if (name == "max_richness") {
398 type.max_richness = in.GetFloat();
399 } else if (name == "commonness") {
400 type.commonness = in.GetFloat();
401 } else if (name == "shape") {
402 in.ReadIdentifier(shape_name);
403 type.shape = &shapes.Get(shape_name);
405 std::cerr << "warning: unknown block type property " << name << std::endl;
406 while (in.Peek().type != Token::SEMICOLON) {
410 in.Skip(Token::SEMICOLON);
412 in.Skip(Token::ANGLE_BRACKET_CLOSE);
413 in.Skip(Token::SEMICOLON);
419 CubeMap AssetLoader::LoadCubeMap(const string &name) const {
420 string full = textures + name;
421 string right = full + "-right.png";
422 string left = full + "-left.png";
423 string top = full + "-top.png";
424 string bottom = full + "-bottom.png";
425 string back = full + "-back.png";
426 string front = full + "-front.png";
432 if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
434 cm.Data(CubeMap::RIGHT, *srf);
436 SDL_FreeSurface(srf);
439 SDL_FreeSurface(srf);
441 if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
443 cm.Data(CubeMap::LEFT, *srf);
445 SDL_FreeSurface(srf);
448 SDL_FreeSurface(srf);
450 if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
452 cm.Data(CubeMap::TOP, *srf);
454 SDL_FreeSurface(srf);
457 SDL_FreeSurface(srf);
459 if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
461 cm.Data(CubeMap::BOTTOM, *srf);
463 SDL_FreeSurface(srf);
466 SDL_FreeSurface(srf);
468 if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
470 cm.Data(CubeMap::BACK, *srf);
472 SDL_FreeSurface(srf);
475 SDL_FreeSurface(srf);
477 if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
479 cm.Data(CubeMap::FRONT, *srf);
481 SDL_FreeSurface(srf);
484 SDL_FreeSurface(srf);
492 Font AssetLoader::LoadFont(const string &name, int size) const {
493 string full = fonts + name + ".ttf";
494 return Font(full.c_str(), size);
497 void AssetLoader::LoadShapes(const string &set_name, ShapeRegistry &shapes) const {
498 string full = data + set_name + ".shapes";
499 std::ifstream file(full);
501 throw std::runtime_error("failed to open shape file " + full);
503 TokenStreamReader in(file);
505 while (in.HasMore()) {
506 in.ReadIdentifier(shape_name);
507 in.Skip(Token::EQUALS);
508 Shape &shape = shapes.Add(shape_name);
510 in.Skip(Token::SEMICOLON);
514 Sound AssetLoader::LoadSound(const string &name) const {
515 string full = sounds + name + ".wav";
516 return Sound(full.c_str());
519 Texture AssetLoader::LoadTexture(const string &name) const {
520 string full = textures + name + ".png";
522 SDL_Surface *srf = IMG_Load(full.c_str());
524 throw SDLError("IMG_Load");
528 SDL_FreeSurface(srf);
532 void AssetLoader::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
533 string full = textures + name + ".png";
534 SDL_Surface *srf = IMG_Load(full.c_str());
536 throw SDLError("IMG_Load");
540 tex.Data(layer, *srf);
542 SDL_FreeSurface(srf);
545 SDL_FreeSurface(srf);
548 void AssetLoader::LoadTextures(const TextureIndex &index, ArrayTexture &tex) const {
549 // TODO: where the hell should that size come from?
550 tex.Reserve(16, 16, index.Size(), Format());
551 for (const auto &entry : index.Entries()) {
552 LoadTexture(entry.first, tex, entry.second);
557 TextureIndex::TextureIndex()
562 int TextureIndex::GetID(const string &name) {
563 auto entry = id_map.find(name);
564 if (entry == id_map.end()) {
565 auto result = id_map.emplace(name, Size());
566 return result.first->second;
568 return entry->second;
573 void FrameCounter::EnterFrame() noexcept {
574 last_enter = SDL_GetTicks();
575 last_tick = last_enter;
578 void FrameCounter::EnterHandle() noexcept {
582 void FrameCounter::ExitHandle() noexcept {
583 current.handle = Tick();
586 void FrameCounter::EnterUpdate() noexcept {
590 void FrameCounter::ExitUpdate() noexcept {
591 current.update = Tick();
594 void FrameCounter::EnterRender() noexcept {
598 void FrameCounter::ExitRender() noexcept {
599 current.render = Tick();
602 void FrameCounter::ExitFrame() noexcept {
603 Uint32 now = SDL_GetTicks();
604 current.total = now - last_enter;
605 current.running = current.handle + current.update + current.render;
606 current.waiting = current.total - current.running;
610 if (cur_frame >= NUM_FRAMES) {
619 int FrameCounter::Tick() noexcept {
620 Uint32 now = SDL_GetTicks();
621 int delta = now - last_tick;
626 void FrameCounter::Accumulate() noexcept {
627 sum.handle += current.handle;
628 sum.update += current.update;
629 sum.render += current.render;
630 sum.running += current.running;
631 sum.waiting += current.waiting;
632 sum.total += current.total;
634 max.handle = std::max(current.handle, max.handle);
635 max.update = std::max(current.update, max.update);
636 max.render = std::max(current.render, max.render);
637 max.running = std::max(current.running, max.running);
638 max.waiting = std::max(current.waiting, max.waiting);
639 max.total = std::max(current.total, max.total);
641 current = Frame<int>();
644 void FrameCounter::Push() noexcept {
646 avg.handle = sum.handle * factor;
647 avg.update = sum.update * factor;
648 avg.render = sum.render * factor;
649 avg.running = sum.running * factor;
650 avg.waiting = sum.waiting * factor;
651 avg.total = sum.total * factor;