galaxy 1.0.0
Real-Time C++23 Game Programming Framework. Built on data-driven design principles and agile software engineering.
Loading...
Searching...
No Matches
Application.cpp
Go to the documentation of this file.
1
7
8#include <BS_thread_pool.hpp>
9#include <entt/locator/locator.hpp>
10#include <entt/signal/dispatcher.hpp>
11#include <SFML/Graphics.hpp>
12#include <SFML/Window.hpp>
13#include <sol/sol.hpp>
14
26
27#include "Application.hpp"
28
29namespace galaxy
30{
31 namespace core
32 {
33 App::App(std::string_view log_dir, std::string_view config_file)
34 {
36 setup_logging(log_dir);
38 setup_config(config_file);
39 setup_fs();
44 setup_meta();
47
48 //
49 // Add external libraries to Lua.
50 // Inject all configured galaxy into Lua.
51 // Add engine services to lua.
52 //
54
56 // core::entt::locator<core::Loader>::ref().load_all();
57 }
58
60 {
61 entt::locator<scene::SceneManager>::reset();
62 entt::locator<sol::state>::reset();
63 entt::locator<meta::EntityFactory>::reset();
64 entt::locator<meta::SystemFactory>::reset();
65 entt::locator<entt::dispatcher>::reset();
66 entt::locator<sf::RenderWindow>::reset();
67 entt::locator<fs::VirtualFileSystem>::reset();
68 entt::locator<Config>::reset();
69 entt::locator<BS::light_thread_pool>::reset();
70
71 GALAXY_LOG(GALAXY_INFO, "Application closed.");
72 entt::locator<logging::Log>::reset();
73 }
74
75 void App::load()
76 {
77 const auto path = settings::root_dir() / settings::asset_pack();
78
79 auto& sm = entt::locator<scene::SceneManager>::value();
80 sm.load_app(path.string());
81 }
82
83 void App::run()
84 {
85 // https://stackoverflow.com/a/59446610
86
87 auto& window = entt::locator<sf::RenderWindow>::value();
88 auto& dispatcher = entt::locator<entt::dispatcher>::value();
89 auto& manager = entt::locator<scene::SceneManager>::value();
90
91 using clock = std::chrono::high_resolution_clock;
92 using duration = std::chrono::duration<double>;
93 using time_point = std::chrono::time_point<clock, duration>;
94
95 constexpr const auto one_second = std::chrono::seconds {1};
96
97 // Number of updates per second.
98 // Same as std::ratio<1, ups>, using 1000 with std::milli, etc.
99 const auto dt = one_second / settings::ups();
100
101 duration accum {0};
102 duration elapsed {0};
103 double alpha {0.0};
104
105 time_point now = clock::now();
106 time_point previous = now;
107
108 unsigned int frames = 0u;
109 unsigned int updates = 0u;
110 duration perf_counter {0};
111
112 while (window.isOpen())
113 {
114 now = clock::now();
115 elapsed = now - previous;
116 previous = now;
117
118 accum += elapsed;
119 perf_counter += elapsed;
120
121 while (accum >= dt)
122 {
123 accum -= dt;
124 alpha = dt / one_second;
126
127 // nui.begin_input();
128 handle_events(window);
129 // nui.end_input();
130 manager.update();
131
132 updates++;
133 }
134
135 window.clear(sf::Color::White);
136 manager.render();
137 window.display();
138
139 frames++;
140
141 if (perf_counter >= one_second)
142 {
143 window.setTitle(std::format("{0} | UPS: {1}, FPS: {2}", settings::window_title(), updates, frames));
144
145 frames = 0;
146 updates = 0;
147 perf_counter = std::chrono::nanoseconds {0};
148 }
149 }
150 }
151
153 {
154 // Configure platform specific behaviour.
155 platform::configure_terminal();
156
157 // Seed pseudo-random algorithms.
158 std::srand(static_cast<unsigned int>(std::time(nullptr)));
159 }
160
161 void App::setup_logging(std::string_view log_dir)
162 {
163 // Configure logging.
164 const auto now = std::chrono::zoned_time {std::chrono::current_zone(), std::chrono::system_clock::now()}.get_local_time();
165 const std::string log_path = std::format("{0}{1}{2}", log_dir, std::format("{0:%d-%m-%Y-[%H-%M]}", now), ".log");
166 if (!std::filesystem::exists(log_dir))
167 {
168 std::filesystem::create_directory(log_dir);
169 }
170
171 entt::locator<logging::Log>::emplace();
174 GALAXY_LOG(GALAXY_INFO, "App started.");
175 }
176
178 {
179 // Configure threadpool.
180 // Use half of available cores minus 2 for audio and main thread.
181 auto cores = (std::thread::hardware_concurrency() / 2) - 2;
182 if (cores < 4)
183 {
184 GALAXY_LOG(GALAXY_FATAL, "This app requires a minimum of 4 cores, only {0} were found.", cores);
185 }
186
187 entt::locator<BS::light_thread_pool>::emplace(cores);
188 }
189
190 void App::setup_config(std::string_view config_file)
191 {
192 auto& config = entt::locator<Config>::emplace(config_file);
195 }
196
198 {
199 entt::locator<fs::VirtualFileSystem>::emplace();
200 }
201
203 {
204 auto& window = entt::locator<sf::RenderWindow>::emplace();
205 window.setVisible(false);
206
207 sf::VideoMode mode;
208 mode.bitsPerPixel = sf::VideoMode::getDesktopMode().bitsPerPixel;
209 mode.size.x = settings::window_width();
210 mode.size.y = settings::window_height();
211
212 sf::ContextSettings context;
213 context.antiAliasingLevel = settings::msaa();
214 context.attributeFlags = sf::ContextSettings::Default;
215 context.depthBits = 24;
216 context.majorVersion = 3;
217 context.minorVersion = 2;
218 context.sRgbCapable = false;
219 context.stencilBits = 8;
220
221 const auto state = settings::fullscreen() ? sf::State::Fullscreen : sf::State::Windowed;
222 window.create(mode, settings::window_title(), sf::Style::Default, state, context);
223
224 if (!settings::window_icon().empty())
225 {
226 auto& fs = entt::locator<fs::VirtualFileSystem>::value();
227 auto data = fs.read_binary(settings::window_icon());
228
229 sf::Image image;
230 if (image.loadFromMemory(data.data(), data.size()))
231 {
232 // Copies, so can delete image after.
233 window.setIcon(image);
234 }
235 else
236 {
237 GALAXY_LOG(GALAXY_ERROR, "Failed to load window icon '{0}'.", settings::window_icon());
238 }
239 }
240
241 if (!settings::cursor_icon().empty())
242 {
243 window.setMouseCursorVisible(true);
244
246 {
247 GALAXY_LOG(GALAXY_WARNING, "Did not specify cursor size properly, must be same size as texture. Reverting to system default.");
248 }
249 else
250 {
251 auto& fs = entt::locator<fs::VirtualFileSystem>::value();
252 auto data = fs.read_binary(settings::window_icon());
253
254 m_cursor = sf::Cursor::createFromPixels(data.data(), settings::cursor_icon_size(), settings::cursor_hotspot());
255 if (m_cursor.has_value())
256 {
257 window.setMouseCursor(m_cursor.value());
258 }
259 else
260 {
261 GALAXY_LOG(GALAXY_ERROR, "Failed to load custom cursor: {0}.", settings::cursor_icon());
262 }
263 }
264 }
265 else
266 {
267 window.setMouseCursorVisible(settings::cursor_visible());
268 }
269
270 window.setMouseCursorGrabbed(settings::cursor_grabbed());
271 window.setVerticalSyncEnabled(settings::vsync());
272
273 if (!window.setActive(true))
274 {
275 GALAXY_LOG(GALAXY_FATAL, "Failed to get OpenGL active context.");
276 }
277
278 window.setVisible(true);
279
280 if (!window.hasFocus())
281 {
282 window.requestFocus();
283 }
284 }
285
287 {
288 entt::locator<entt::dispatcher>::emplace();
289 for (auto i = 0; i < sf::Joystick::Count; i++)
290 {
291 if (sf::Joystick::isConnected(i))
292 {
293 GALAXY_LOG(GALAXY_WARNING, "Gamepad detected at index {0}. Gamepads are not supported.", i);
294 }
295 }
296 }
297
299 {
300 }
301
303 {
304 // entt::locator<Loader>::make();
305 }
306
308 {
309 auto& sf = entt::locator<meta::SystemFactory>::emplace();
310 auto& ef = entt::locator<meta::EntityFactory>::emplace();
311
312 ef.register_component<components::Tag>("Tag");
313 /*
314 em.register_component<components::Animated>("Animated");
315 em.register_component<components::Circle>("Circle");
316 em.register_component<components::Ellipse>("Ellipse");
317 em.register_component<components::Point>("Point");
318 em.register_component<components::Polygon>("Polygon");
319 em.register_component<components::Polyline>("Polyline");
320 em.register_component<components::RigidBody>("RigidBody");
321 em.register_component<components::Script>("Script");
322 em.register_component<components::Sprite>("Sprite");
323 em.register_component<components::Text>("Text");
324 em.register_component<components::TileMap>("TileMap");
325 em.register_component<components::Transform>("Transform");
326 em.register_component<flags::DenySerialization>("DenySerialization");
327 em.register_component<flags::Disabled>("Disabled");
328
329 em.register_dependencies<components::Animated, components::Sprite>();
330 em.register_dependencies<components::Circle, components::Transform>();
331 em.register_dependencies<components::Ellipse, components::Transform>();
332 em.register_dependencies<components::Point, components::Transform>();
333 em.register_dependencies<components::Polygon, components::Transform>();
334 em.register_dependencies<components::Polyline, components::Transform>();
335 em.register_dependencies<components::RigidBody, components::Transform>();
336 em.register_dependencies<components::Sprite, components::Transform>();
337 em.register_dependencies<components::Text, components::Transform>();
338 */
339 }
340
342 {
343 auto& lua = entt::locator<sol::state>::emplace();
344 lua.open_libraries(
345 sol::lib::base,
346 sol::lib::package,
347 sol::lib::coroutine,
348 sol::lib::string,
349 sol::lib::os,
350 sol::lib::math,
351 sol::lib::table,
352 sol::lib::io,
353 sol::lib::utf8
354 );
355 }
356
358 {
359 // entt::locator<media::SoundEngine>::make(listener_count);
360 // entt::locator<media::MusicEngine>::make(listener_count);
361 // entt::locator<media::VoiceEngine>::make(listener_count);
362 // entt::locator<resource::SoundCache>::make();
363 // entt::locator<resource::MusicCache>::make();
364 // entt::locator<resource::VoiceCache>::make();
365 // entt::locator<resource::VideoCache>::make();
366 // entt::locator<resource::Animations>::make();
367 // entt::locator<resource::Shaders>::make();
368 // entt::locator<resource::Fonts>::make();
369 // entt::locator<resource::Textures>::make();
370 // entt::locator<resource::Prefabs>::make();
371 // entt::locator<resource::Scripts>::make();
372 entt::locator<scene::SceneManager>::emplace();
373 }
374
375 void App::handle_events(sf::RenderWindow& window)
376 {
377 window.handleEvents([](auto&& event) {
378 using Event = std::decay_t<decltype(event)>;
379
380 if constexpr (std::is_same_v<Event, sf::Event::Closed>)
381 {
382 entt::locator<sf::RenderWindow>::value().close();
383 }
384 else
385 {
386 entt::locator<entt::dispatcher>::value().trigger(event);
387 }
388 });
389 }
390 } // namespace core
391} // namespace galaxy
#define GALAXY_ADD_SINK(sink,...)
Definition Log.hpp:28
#define GALAXY_INFO
Log.hpp galaxy.
Definition Log.hpp:22
#define GALAXY_WARNING
Definition Log.hpp:24
#define GALAXY_LOG(level, msg,...)
Definition Log.hpp:29
#define GALAXY_FATAL
Definition Log.hpp:26
#define GALAXY_ERROR
Definition Log.hpp:25
Tag an entity.
Definition Tag.hpp:21
void run()
Runs the app.
~App()
Destructor.
void setup_config(std::string_view config_file)
void setup_logging(std::string_view log_dir)
void load()
Loads the default appdata file.
std::optional< sf::Cursor > m_cursor
SFML cursor handle. Needs to be kept in memory.
void handle_events(sf::RenderWindow &window)
App(std::string_view log_dir, std::string_view config_file)
Default constructor.
Logs a message to the console.
void inject()
Inject everything into Lua.
Definition Lua.cpp:14
Timer.hpp galaxy.
Definition Async.hpp:17
Application.hpp galaxy.
static auto cursor_icon() noexcept -> const std::string &
Cursor texture file in vfs.
Definition Settings.cpp:157
static auto window_icon() noexcept -> const std::string &
Window icon file in vfs.
Definition Settings.cpp:122
static auto msaa() noexcept -> bool
Enable MSAA.
Definition Settings.cpp:142
static auto vsync() noexcept -> bool
Vsync control.
Definition Settings.cpp:137
static auto window_width() noexcept -> int
Window creation width.
Definition Settings.cpp:107
static auto asset_pack() noexcept -> const std::string &
Name of packed assets file.
Definition Settings.cpp:187
static auto window_height() noexcept -> int
Window creation height.
Definition Settings.cpp:112
static auto root_dir() noexcept -> std::filesystem::path
Current root directory of application, unless it has been changed.
Definition Settings.cpp:172
static auto cursor_hotspot() noexcept -> const sf::Vector2u &
Cursor selector point (hotspot).
Definition Settings.cpp:167
static auto cursor_visible() noexcept -> bool
Show/hide mouse cursor.
Definition Settings.cpp:147
static auto set_config_to_default(core::Config &config) -> void
Restore all config settings to default.
Definition Settings.cpp:12
static auto set_delta_time(const double dt) noexcept -> void
Set galaxy delta time.
Definition Settings.cpp:92
static auto window_title() noexcept -> const std::string &
Window title.
Definition Settings.cpp:117
static auto cursor_grabbed() noexcept -> bool
Grab/release mouse cursor.
Definition Settings.cpp:152
static auto ups() noexcept -> double
Game updates per second, independant of FPS, see "fixed timestep gameloop".
Definition Settings.cpp:102
static auto cursor_icon_size() noexcept -> const sf::Vector2u &
Cursor icon texture size.
Definition Settings.cpp:162
static auto set_settings_from_config(core::Config &config) -> void
Set all our settings using the provided config file.
Definition Settings.cpp:53
static auto fullscreen() noexcept -> bool
Window maximized state.
Definition Settings.cpp:127