1 #include "Application.hpp"
6 #include "../graphics/Viewport.hpp"
7 #include "../io/Token.hpp"
8 #include "../io/TokenStreamReader.hpp"
9 #include "../world/Planet.hpp"
10 #include "../world/Simulation.hpp"
11 #include "../world/Sun.hpp"
15 #include <SDL_image.h>
23 Application::Application(Window &win, graphics::Viewport &vp)
29 Application::~Application() {
33 void Application::PushState(State *s) {
35 if (!states.empty()) {
36 states.top()->OnPause();
40 if (s->ref_count == 1) {
46 State *Application::PopState() {
47 State *s = states.top();
51 if (!states.empty()) {
52 states.top()->OnResume();
57 State *Application::SwitchState(State *s_new) {
59 State *s_old = states.top();
64 if (s_old->ref_count == 0) {
67 if (s_new->ref_count == 1) {
74 State &Application::GetState() {
78 bool Application::HasState() const noexcept {
79 return !states.empty();
83 void Application::Run() {
84 Uint32 last = SDL_GetTicks();
86 Uint32 now = SDL_GetTicks();
87 int delta = now - last;
93 void Application::Loop(int dt) {
95 if (!HasState()) return;
96 GetState().Update(dt);
97 if (!HasState()) return;
99 GetState().Render(viewport);
103 void Application::HandleEvents() {
105 while (HasState() && SDL_PollEvent(&event)) {
106 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) {
107 viewport.Resize(event.window.data1, event.window.data2);
109 GetState().Handle(event);
113 void State::Handle(const SDL_Event &event) {
114 switch (event.type) {
116 OnKeyDown(event.key);
121 case SDL_MOUSEBUTTONDOWN:
122 OnMouseDown(event.button);
124 case SDL_MOUSEBUTTONUP:
125 OnMouseUp(event.button);
127 case SDL_MOUSEMOTION:
128 OnMouseMotion(event.motion);
131 OnMouseWheel(event.wheel);
136 case SDL_WINDOWEVENT:
137 Handle(event.window);
145 void State::Handle(const SDL_WindowEvent &event) {
146 switch (event.event) {
147 case SDL_WINDOWEVENT_FOCUS_GAINED:
150 case SDL_WINDOWEVENT_FOCUS_LOST:
153 case SDL_WINDOWEVENT_RESIZED:
154 OnResize(event.data1, event.data2);
161 void State::Update(int dt) {
165 void State::Render(graphics::Viewport &viewport) {
169 void State::OnQuit() {
170 while (App().HasState()) {
178 , data_path(path + "data/")
179 , font_path(path + "fonts/")
180 , skin_path(path + "skins/")
181 , sky_path(path + "skies/")
182 , tile_path(path + "tiles/")
183 , random(0x6283B64CEFE57925)
185 graphics::Font(font_path + "DejaVuSans.ttf", 32),
186 graphics::Font(font_path + "DejaVuSans.ttf", 24),
187 graphics::Font(font_path + "DejaVuSans.ttf", 16)
190 std::ifstream resource_file(data_path + "resources");
191 io::TokenStreamReader resource_reader(resource_file);
192 ReadResources(resource_reader);
196 std::ifstream tile_file(data_path + "tile_types");
197 io::TokenStreamReader tile_reader(tile_file);
198 ReadTileTypes(tile_reader);
202 graphics::Format format;
203 textures.tiles.Bind();
204 textures.tiles.Reserve(256, 256, 14, format);
205 LoadTileTexture("algae", textures.tiles, 0);
206 LoadTileTexture("desert", textures.tiles, 1);
207 LoadTileTexture("forest", textures.tiles, 2);
208 LoadTileTexture("grass", textures.tiles, 3);
209 LoadTileTexture("ice", textures.tiles, 4);
210 LoadTileTexture("jungle", textures.tiles, 5);
211 LoadTileTexture("mountain", textures.tiles, 6);
212 LoadTileTexture("ocean", textures.tiles, 7);
213 LoadTileTexture("rock", textures.tiles, 8);
214 LoadTileTexture("sand", textures.tiles, 9);
215 LoadTileTexture("taiga", textures.tiles, 10);
216 LoadTileTexture("tundra", textures.tiles, 11);
217 LoadTileTexture("water", textures.tiles, 12);
218 LoadTileTexture("wheat", textures.tiles, 13);
219 textures.tiles.FilterTrilinear();
221 textures.skins.Bind();
222 textures.skins.Reserve(256, 256, 9, format);
223 LoadSkinTexture("plain", textures.skins, 0);
224 LoadSkinTexture("stripes", textures.skins, 1);
225 LoadSkinTexture("dots", textures.skins, 2);
226 LoadSkinTexture("lines", textures.skins, 3);
227 LoadSkinTexture("spots", textures.skins, 4);
228 LoadSkinTexture("circles", textures.skins, 5);
229 textures.skins.FilterTrilinear();
232 LoadSkyTexture("blue", textures.sky);
233 textures.sky.FilterTrilinear();
234 textures.sky.WrapEdge();
240 void Assets::ReadResources(io::TokenStreamReader &in) {
241 while (in.HasMore()) {
243 in.ReadIdentifier(name);
244 in.Skip(io::Token::EQUALS);
247 if (data.resources.Has(name)) {
248 id = data.resources[name].id;
252 id = data.resources.Add(res);
255 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
256 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
257 in.ReadIdentifier(name);
258 in.Skip(io::Token::EQUALS);
259 if (name == "label") {
260 in.ReadString(data.resources[id].label);
261 } else if (name == "density") {
262 data.resources[id].density = in.GetDouble();
263 } else if (name == "energy") {
264 data.resources[id].energy = in.GetDouble();
265 data.resources[id].inverse_energy = 1.0 / data.resources[id].energy;
266 } else if (name == "state") {
267 in.ReadIdentifier(name);
268 if (name == "solid") {
269 data.resources[id].state = world::Resource::SOLID;
270 } else if (name == "liquid") {
271 data.resources[id].state = world::Resource::LIQUID;
272 } else if (name == "gas") {
273 data.resources[id].state = world::Resource::GAS;
274 } else if (name == "plasma") {
275 data.resources[id].state = world::Resource::PLASMA;
277 throw std::runtime_error("unknown resource state '" + name + "'");
279 } else if (name == "base_color") {
280 in.ReadVec(data.resources[id].base_color);
281 } else if (name == "compatibility") {
282 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
283 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
284 in.ReadIdentifier(name);
286 if (data.resources.Has(name)) {
287 sub_id = data.resources[name].id;
291 sub_id = data.resources.Add(res);
293 in.Skip(io::Token::COLON);
294 double value = in.GetDouble();
295 in.Skip(io::Token::SEMICOLON);
296 data.resources[id].compatibility[sub_id] = value;
298 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
300 throw std::runtime_error("unknown resource property '" + name + "'");
302 in.Skip(io::Token::SEMICOLON);
304 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
305 in.Skip(io::Token::SEMICOLON);
309 void Assets::ReadTileTypes(io::TokenStreamReader &in) {
310 while (in.HasMore()) {
312 in.ReadIdentifier(name);
313 in.Skip(io::Token::EQUALS);
316 if (data.tile_types.Has(name)) {
317 id = data.tile_types[name].id;
319 world::TileType type;
321 id = data.tile_types.Add(type);
324 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
325 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
326 in.ReadIdentifier(name);
327 in.Skip(io::Token::EQUALS);
328 if (name == "label") {
329 in.ReadString(data.tile_types[id].label);
330 } else if (name == "texture") {
331 data.tile_types[id].texture = in.GetInt();
332 } else if (name == "yield") {
333 in.Skip(io::Token::BRACKET_OPEN);
334 while (in.Peek().type != io::Token::BRACKET_CLOSE) {
335 world::TileType::Yield yield;
336 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
337 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
338 in.ReadIdentifier(name);
339 in.Skip(io::Token::EQUALS);
340 if (name == "resource") {
341 in.ReadIdentifier(name);
342 yield.resource = data.resources[name].id;
343 } else if (name == "ubiquity") {
344 yield.ubiquity = in.GetDouble();
346 throw std::runtime_error("unknown tile type yield property '" + name + "'");
348 in.Skip(io::Token::SEMICOLON);
350 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
351 data.tile_types[id].resources.push_back(yield);
352 if (in.Peek().type == io::Token::COMMA) {
353 in.Skip(io::Token::COMMA);
356 in.Skip(io::Token::BRACKET_CLOSE);
358 throw std::runtime_error("unknown tile type property '" + name + "'");
360 in.Skip(io::Token::SEMICOLON);
362 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
363 in.Skip(io::Token::SEMICOLON);
367 void Assets::LoadTileTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
368 string path = tile_path + name + ".png";
369 SDL_Surface *srf = IMG_Load(path.c_str());
371 throw SDLError("IMG_Load");
374 tex.Data(layer, *srf);
376 SDL_FreeSurface(srf);
379 SDL_FreeSurface(srf);
382 void Assets::LoadSkinTexture(const string &name, graphics::ArrayTexture &tex, int layer) const {
383 string path = skin_path + name + ".png";
384 SDL_Surface *srf = IMG_Load(path.c_str());
386 throw SDLError("IMG_Load");
389 tex.Data(layer, *srf);
391 SDL_FreeSurface(srf);
394 SDL_FreeSurface(srf);
397 void Assets::LoadSkyTexture(const string &name, graphics::CubeMap &cm) const {
398 string full = sky_path + name;
399 string right = full + "-right.png";
400 string left = full + "-left.png";
401 string top = full + "-top.png";
402 string bottom = full + "-bottom.png";
403 string back = full + "-back.png";
404 string front = full + "-front.png";
406 SDL_Surface *srf = nullptr;
408 if (!(srf = IMG_Load(right.c_str()))) throw SDLError("IMG_Load");
410 cm.Data(graphics::CubeMap::RIGHT, *srf);
412 SDL_FreeSurface(srf);
415 SDL_FreeSurface(srf);
417 if (!(srf = IMG_Load(left.c_str()))) throw SDLError("IMG_Load");
419 cm.Data(graphics::CubeMap::LEFT, *srf);
421 SDL_FreeSurface(srf);
424 SDL_FreeSurface(srf);
426 if (!(srf = IMG_Load(top.c_str()))) throw SDLError("IMG_Load");
428 cm.Data(graphics::CubeMap::TOP, *srf);
430 SDL_FreeSurface(srf);
433 SDL_FreeSurface(srf);
435 if (!(srf = IMG_Load(bottom.c_str()))) throw SDLError("IMG_Load");
437 cm.Data(graphics::CubeMap::BOTTOM, *srf);
439 SDL_FreeSurface(srf);
442 SDL_FreeSurface(srf);
444 if (!(srf = IMG_Load(back.c_str()))) throw SDLError("IMG_Load");
446 cm.Data(graphics::CubeMap::BACK, *srf);
448 SDL_FreeSurface(srf);
451 SDL_FreeSurface(srf);
453 if (!(srf = IMG_Load(front.c_str()))) throw SDLError("IMG_Load");
455 cm.Data(graphics::CubeMap::FRONT, *srf);
457 SDL_FreeSurface(srf);
460 SDL_FreeSurface(srf);
463 void Assets::LoadUniverse(const string &name, world::Simulation &sim) const {
464 std::ifstream universe_file(data_path + name);
465 io::TokenStreamReader universe_reader(universe_file);
466 ReadBody(universe_reader, sim);
467 universe_reader.Skip(io::Token::SEMICOLON);
470 world::Body *Assets::ReadBody(io::TokenStreamReader &in, world::Simulation &sim) const {
471 std::unique_ptr<world::Body> body;
473 in.ReadIdentifier(name);
475 world::Sun *sun = new world::Sun;
478 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
479 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
480 in.ReadIdentifier(name);
481 in.Skip(io::Token::EQUALS);
482 ReadSunProperty(name, in, *sun, sim);
483 in.Skip(io::Token::SEMICOLON);
485 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
486 in.Skip(io::Token::SEMICOLON);
487 } else if (name == "Planet") {
488 in.Skip(io::Token::PARENTHESIS_OPEN);
489 int sidelength = in.GetInt();
490 in.Skip(io::Token::PARENTHESIS_CLOSE);
491 world::Planet *planet = new world::Planet(sidelength);
492 sim.AddPlanet(*planet);
494 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
495 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
496 in.ReadIdentifier(name);
497 in.Skip(io::Token::EQUALS);
498 ReadPlanetProperty(name, in, *planet, sim);
499 in.Skip(io::Token::SEMICOLON);
501 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
503 throw std::runtime_error("unknown body class " + name);
505 return body.release();
508 void Assets::ReadSunProperty(const std::string &name, io::TokenStreamReader &in, world::Sun &sun, world::Simulation &sim) const {
509 if (name == "color") {
510 glm::dvec3 color(0.0);
513 } else if (name == "luminosity") {
514 sun.Luminosity(in.GetDouble());
516 ReadBodyProperty(name, in, sun, sim);
520 void Assets::ReadPlanetProperty(const std::string &name, io::TokenStreamReader &in, world::Planet &planet, world::Simulation &sim) const {
521 if (name == "generate") {
523 in.ReadIdentifier(gen);
524 if (gen == "earthlike") {
525 world::GenerateEarthlike(data.tile_types, planet);
526 } else if (gen == "test") {
527 world::GenerateTest(data.tile_types, planet);
529 throw std::runtime_error("unknown surface generator " + gen);
531 } else if (name == "atmosphere") {
533 in.ReadIdentifier(atm);
534 planet.Atmosphere(data.resources[atm].id);
536 ReadBodyProperty(name, in, planet, sim);
540 void Assets::ReadBodyProperty(const std::string &name, io::TokenStreamReader &in, world::Body &body, world::Simulation &sim) const {
541 if (name == "name") {
543 in.ReadString(value);
545 } else if (name == "mass") {
546 body.Mass(in.GetDouble());
547 } else if (name == "radius") {
548 body.Radius(in.GetDouble());
549 } else if (name == "axial_tilt") {
550 glm::dvec2 tilt(0.0);
552 body.AxialTilt(tilt);
553 } else if (name == "rotation") {
554 body.Rotation(in.GetDouble());
555 } else if (name == "angular_momentum") {
556 body.AngularMomentum(in.GetDouble());
557 } else if (name == "orbit") {
558 in.Skip(io::Token::ANGLE_BRACKET_OPEN);
559 while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
561 in.ReadIdentifier(oname);
562 in.Skip(io::Token::EQUALS);
563 if (oname == "SMA" || oname == "semi_major_axis") {
564 body.GetOrbit().SemiMajorAxis(in.GetDouble());
565 } else if (oname == "ECC" || oname == "eccentricity") {
566 body.GetOrbit().Eccentricity(in.GetDouble());
567 } else if (oname == "INC" || oname == "inclination") {
568 body.GetOrbit().Inclination(in.GetDouble());
569 } else if (oname == "ASC" || oname == "ascending_node" || oname == "longitude_ascending") {
570 body.GetOrbit().LongitudeAscending(in.GetDouble());
571 } else if (oname == "ARG" || oname == "APE" || oname == "argument_periapsis") {
572 body.GetOrbit().ArgumentPeriapsis(in.GetDouble());
573 } else if (oname == "MNA" || oname == "mean_anomaly") {
574 body.GetOrbit().MeanAnomaly(in.GetDouble());
576 throw std::runtime_error("unknown orbit property " + oname);
578 in.Skip(io::Token::SEMICOLON);
580 in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
581 } else if (name == "children") {
582 in.Skip(io::Token::BRACKET_OPEN);
583 while (in.Peek().type != io::Token::BRACKET_CLOSE) {
584 world::Body *b = ReadBody(in, sim);
586 if (in.Peek().type == io::Token::COMMA) {
587 in.Skip(io::Token::COMMA);
590 in.Skip(io::Token::BRACKET_CLOSE);
592 throw std::runtime_error("unknown body property " + name);