]> git.localhorst.tv Git - blobs.git/blob - src/app/app.cpp
basic sky box
[blobs.git] / src / app / app.cpp
1 #include "Application.hpp"
2 #include "Assets.hpp"
3 #include "State.hpp"
4
5 #include "init.hpp"
6 #include "../graphics/Viewport.hpp"
7 #include "../io/Token.hpp"
8 #include "../io/TokenStreamReader.hpp"
9
10 #include <fstream>
11 #include <SDL.h>
12 #include <SDL_image.h>
13
14 using std::string;
15
16
17 namespace blobs {
18 namespace app {
19
20 Application::Application(Window &win, graphics::Viewport &vp)
21 : window(win)
22 , viewport(vp)
23 , states() {
24 }
25
26 Application::~Application() {
27 }
28
29
30 void Application::PushState(State *s) {
31         s->app = this;
32         if (!states.empty()) {
33                 states.top()->OnPause();
34         }
35         states.emplace(s);
36         ++s->ref_count;
37         if (s->ref_count == 1) {
38                 s->OnEnter();
39         }
40         s->OnResume();
41 }
42
43 State *Application::PopState() {
44         State *s = states.top();
45         states.pop();
46         s->OnPause();
47         s->OnExit();
48         if (!states.empty()) {
49                 states.top()->OnResume();
50         }
51         return s;
52 }
53
54 State *Application::SwitchState(State *s_new) {
55         s_new->app = this;
56         State *s_old = states.top();
57         states.top() = s_new;
58         --s_old->ref_count;
59         ++s_new->ref_count;
60         s_old->OnPause();
61         if (s_old->ref_count == 0) {
62                 s_old->OnExit();
63         }
64         if (s_new->ref_count == 1) {
65                 s_new->OnEnter();
66         }
67         s_new->OnResume();
68         return s_old;
69 }
70
71 State &Application::GetState() {
72         return *states.top();
73 }
74
75 bool Application::HasState() const noexcept {
76         return !states.empty();
77 }
78
79
80 void Application::Run() {
81         Uint32 last = SDL_GetTicks();
82         while (HasState()) {
83                 Uint32 now = SDL_GetTicks();
84                 int delta = now - last;
85                 Loop(delta);
86                 last = now;
87         }
88 }
89
90 void Application::Loop(int dt) {
91         HandleEvents();
92         if (!HasState()) return;
93         GetState().Update(dt);
94         if (!HasState()) return;
95         viewport.Clear();
96         GetState().Render(viewport);
97         window.Flip();
98 }
99
100 void Application::HandleEvents() {
101         SDL_Event event;
102         while (HasState() && SDL_PollEvent(&event)) {
103                 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
104                         viewport.Resize(event.window.data1, event.window.data2);
105                 }
106                 GetState().Handle(event);
107         }
108 }
109
110 void State::Handle(const SDL_Event &event) {
111         switch (event.type) {
112                 case SDL_KEYDOWN:
113                         OnKeyDown(event.key);
114                         break;
115                 case SDL_KEYUP:
116                         OnKeyUp(event.key);
117                         break;
118                 case SDL_MOUSEBUTTONDOWN:
119                         OnMouseDown(event.button);
120                         break;
121                 case SDL_MOUSEBUTTONUP:
122                         OnMouseUp(event.button);
123                         break;
124                 case SDL_MOUSEMOTION:
125                         OnMouseMotion(event.motion);
126                         break;
127                 case SDL_MOUSEWHEEL:
128                         OnMouseWheel(event.wheel);
129                         break;
130                 case SDL_QUIT:
131                         OnQuit();
132                         break;
133                 case SDL_WINDOWEVENT:
134                         Handle(event.window);
135                         break;
136                 default:
137                         // ignore
138                         break;
139         }
140 }
141
142 void State::Handle(const SDL_WindowEvent &event) {
143         switch (event.event) {
144                 case SDL_WINDOWEVENT_FOCUS_GAINED:
145                         OnFocus();
146                         break;
147                 case SDL_WINDOWEVENT_FOCUS_LOST:
148                         OnBlur();
149                         break;
150                 case SDL_WINDOWEVENT_RESIZED:
151                         OnResize(event.data1, event.data2);
152                         break;
153                 default:
154                         break;
155         }
156 }
157
158 void State::Update(int dt) {
159         OnUpdate(dt);
160 }
161
162 void State::Render(graphics::Viewport &viewport) {
163         OnRender(viewport);
164 }
165
166 void State::OnQuit() {
167         while (App().HasState()) {
168                 App().PopState();
169         }
170 }
171
172
173 Assets::Assets()
174 : path("assets/")
175 , data_path(path + "data/")
176 , font_path(path + "fonts/")
177 , skin_path(path + "skins/")
178 , sky_path(path + "skies/")
179 , tile_path(path + "tiles/")
180 , random(0x6283B64CEFE57925)
181 , fonts{
182         graphics::Font(font_path + "DejaVuSans.ttf", 32),
183         graphics::Font(font_path + "DejaVuSans.ttf", 24),
184         graphics::Font(font_path + "DejaVuSans.ttf", 16)
185 } {
186         {
187                 std::ifstream resource_file(data_path + "resources");
188                 io::TokenStreamReader resource_reader(resource_file);
189                 ReadResources(resource_reader);
190         }
191
192         {
193                 std::ifstream tile_file(data_path + "tile_types");
194                 io::TokenStreamReader tile_reader(tile_file);
195                 ReadTileTypes(tile_reader);
196         }
197
198
199         graphics::Format format;
200         textures.tiles.Bind();
201         textures.tiles.Reserve(256, 256, 14, format);
202         LoadTileTexture("algae",    textures.tiles,  0);
203         LoadTileTexture("desert",   textures.tiles,  1);
204         LoadTileTexture("forest",   textures.tiles,  2);
205         LoadTileTexture("grass",    textures.tiles,  3);
206         LoadTileTexture("ice",      textures.tiles,  4);
207         LoadTileTexture("jungle",   textures.tiles,  5);
208         LoadTileTexture("mountain", textures.tiles,  6);
209         LoadTileTexture("ocean",    textures.tiles,  7);
210         LoadTileTexture("rock",     textures.tiles,  8);
211         LoadTileTexture("sand",     textures.tiles,  9);
212         LoadTileTexture("taiga",    textures.tiles, 10);
213         LoadTileTexture("tundra",   textures.tiles, 11);
214         LoadTileTexture("water",    textures.tiles, 12);
215         LoadTileTexture("wheat",    textures.tiles, 13);
216         textures.tiles.FilterTrilinear();
217
218         textures.skins.Bind();
219         textures.skins.Reserve(256, 256, 9, format);
220         LoadSkinTexture("plain", textures.skins, 0);
221         LoadSkinTexture("stripes", textures.skins, 1);
222         LoadSkinTexture("dots", textures.skins, 2);
223         LoadSkinTexture("lines", textures.skins, 3);
224         LoadSkinTexture("spots", textures.skins, 4);
225         LoadSkinTexture("circles", textures.skins, 5);
226         textures.skins.FilterTrilinear();
227
228         textures.sky.Bind();
229         LoadSkyTexture("blue", textures.sky);
230         textures.sky.FilterTrilinear();
231         textures.sky.WrapEdge();
232 }
233
234 Assets::~Assets() {
235 }
236
237 void Assets::ReadResources(io::TokenStreamReader &in) {
238         while (in.HasMore()) {
239                 string name;
240                 in.ReadIdentifier(name);
241                 in.Skip(io::Token::EQUALS);
242
243                 int id = 0;
244                 if (data.resources.Has(name)) {
245                         id = data.resources[name].id;
246                 } else {
247                         world::Resource res;
248                         res.name = name;
249                         id = data.resources.Add(res);
250                 }
251
252                 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
253                 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
254                         in.ReadIdentifier(name);
255                         in.Skip(io::Token::EQUALS);
256                         if (name == "label") {
257                                 in.ReadString(data.resources[id].label);
258                         } else if (name == "density") {
259                                 data.resources[id].density = in.GetDouble();
260                         } else if (name == "energy") {
261                                 data.resources[id].energy = in.GetDouble();
262                                 data.resources[id].inverse_energy = 1.0 / data.resources[id].energy;
263                         } else if (name == "state") {
264                                 in.ReadIdentifier(name);
265                                 if (name == "solid") {
266                                         data.resources[id].state = world::Resource::SOLID;
267                                 } else if (name == "liquid") {
268                                         data.resources[id].state = world::Resource::LIQUID;
269                                 } else if (name == "gas") {
270                                         data.resources[id].state = world::Resource::GAS;
271                                 } else if (name == "plasma") {
272                                         data.resources[id].state = world::Resource::PLASMA;
273                                 } else {
274                                         throw std::runtime_error("unknown resource state '" + name + "'");
275                                 }
276                         } else if (name == "base_color") {
277                                 in.ReadVec(data.resources[id].base_color);
278                         } else if (name == "compatibility") {
279                                 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
280                                 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
281                                         in.ReadIdentifier(name);
282                                         int sub_id = 0;
283                                         if (data.resources.Has(name)) {
284                                                 sub_id = data.resources[name].id;
285                                         } else {
286                                                 world::Resource res;
287                                                 res.name = name;
288                                                 sub_id = data.resources.Add(res);
289                                         }
290                                         in.Skip(io::Token::COLON);
291                                         double value = in.GetDouble();
292                                         in.Skip(io::Token::SEMICOLON);
293                                         data.resources[id].compatibility[sub_id] = value;
294                                 }
295                                 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
296                         } else {
297                                 throw std::runtime_error("unknown resource property '" + name + "'");
298                         }
299                         in.Skip(io::Token::SEMICOLON);
300                 }
301                 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
302                 in.Skip(io::Token::SEMICOLON);
303         }
304 }
305
306 void Assets::ReadTileTypes(io::TokenStreamReader &in) {
307         while (in.HasMore()) {
308                 string name;
309                 in.ReadIdentifier(name);
310                 in.Skip(io::Token::EQUALS);
311
312                 int id = 0;
313                 if (data.tile_types.Has(name)) {
314                         id = data.tile_types[name].id;
315                 } else {
316                         world::TileType type;
317                         type.name = name;
318                         id = data.tile_types.Add(type);
319                 }
320
321                 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
322                 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
323                         in.ReadIdentifier(name);
324                         in.Skip(io::Token::EQUALS);
325                         if (name == "label") {
326                                 in.ReadString(data.tile_types[id].label);
327                         } else if (name == "texture") {
328                                 data.tile_types[id].texture = in.GetInt();
329                         } else if (name == "yield") {
330                                 in.Skip(io::Token::BRACKET_OPEN);
331                                 while (in.Peek().type != io::Token::BRACKET_CLOSE) {
332                                         world::TileType::Yield yield;
333                                         in.Skip(io::Token::ANGLE_BRACKET_OPEN);
334                                         while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
335                                                 in.ReadIdentifier(name);
336                                                 in.Skip(io::Token::EQUALS);
337                                                 if (name == "resource") {
338                                                         in.ReadIdentifier(name);
339                                                         yield.resource = data.resources[name].id;
340                                                 } else if (name == "ubiquity") {
341                                                         yield.ubiquity = in.GetDouble();
342                                                 } else {
343                                                         throw std::runtime_error("unknown tile type yield property '" + name + "'");
344                                                 }
345                                                 in.Skip(io::Token::SEMICOLON);
346                                         }
347                                         in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
348                                         data.tile_types[id].resources.push_back(yield);
349                                         if (in.Peek().type == io::Token::COMMA) {
350                                                 in.Skip(io::Token::COMMA);
351                                         }
352                                 }
353                                 in.Skip(io::Token::BRACKET_CLOSE);
354                         } else {
355                                 throw std::runtime_error("unknown tile type property '" + name + "'");
356                         }
357                         in.Skip(io::Token::SEMICOLON);
358                 }
359                 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
360                 in.Skip(io::Token::SEMICOLON);
361         }
362 }
363
364 void Assets::LoadTileTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
365         string path = tile_path + name + ".png";
366         SDL_Surface *srf = IMG_Load(path.c_str());
367         if (!srf) {
368                 throw SDLError("IMG_Load");
369         }
370         try {
371                 tex.Data(layer, *srf);
372         } catch (...) {
373                 SDL_FreeSurface(srf);
374                 throw;
375         }
376         SDL_FreeSurface(srf);
377 }
378
379 void Assets::LoadSkinTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
380         string path = skin_path + name + ".png";
381         SDL_Surface *srf = IMG_Load(path.c_str());
382         if (!srf) {
383                 throw SDLError("IMG_Load");
384         }
385         try {
386                 tex.Data(layer, *srf);
387         } catch (...) {
388                 SDL_FreeSurface(srf);
389                 throw;
390         }
391         SDL_FreeSurface(srf);
392 }
393
394 void Assets::LoadSkyTexture(const string &name, graphics::CubeMap &cm) const {
395         string full = sky_path + name;
396         string right = full + "-right.png";
397         string left = full + "-left.png";
398         string top = full + "-top.png";
399         string bottom = full + "-bottom.png";
400         string back = full + "-back.png";
401         string front = full + "-front.png";
402
403         SDL_Surface *srf = nullptr;
404
405         if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
406         try {
407                 cm.Data(graphics::CubeMap::RIGHT, *srf);
408         } catch (...) {
409                 SDL_FreeSurface(srf);
410                 throw;
411         }
412         SDL_FreeSurface(srf);
413
414         if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
415         try {
416                 cm.Data(graphics::CubeMap::LEFT, *srf);
417         } catch (...) {
418                 SDL_FreeSurface(srf);
419                 throw;
420         }
421         SDL_FreeSurface(srf);
422
423         if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
424         try {
425                 cm.Data(graphics::CubeMap::TOP, *srf);
426         } catch (...) {
427                 SDL_FreeSurface(srf);
428                 throw;
429         }
430         SDL_FreeSurface(srf);
431
432         if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
433         try {
434                 cm.Data(graphics::CubeMap::BOTTOM, *srf);
435         } catch (...) {
436                 SDL_FreeSurface(srf);
437                 throw;
438         }
439         SDL_FreeSurface(srf);
440
441         if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
442         try {
443                 cm.Data(graphics::CubeMap::BACK, *srf);
444         } catch (...) {
445                 SDL_FreeSurface(srf);
446                 throw;
447         }
448         SDL_FreeSurface(srf);
449
450         if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
451         try {
452                 cm.Data(graphics::CubeMap::FRONT, *srf);
453         } catch (...) {
454                 SDL_FreeSurface(srf);
455                 throw;
456         }
457         SDL_FreeSurface(srf);
458 }
459
460 }
461 }