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
25#include "galaxy/scripting/Lua.hpp"
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 settings::set_delta_time(dt / one_second);
101
102 duration accum {0};
103 duration elapsed {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
125 // nui.begin_input();
126 handle_events(window);
127 // nui.end_input();
128 manager.update();
129
130 updates++;
131 }
132
133 window.clear(sf::Color::White);
134 manager.render();
135 window.display();
136
137 frames++;
138
139 if (perf_counter >= one_second)
140 {
141 window.setTitle(std::format("{0} | UPS: {1}, FPS: {2}", settings::window_title(), updates, frames));
142
143 frames = 0;
144 updates = 0;
145 perf_counter = std::chrono::nanoseconds {0};
146 }
147 }
148 }
149
151 {
152 // Configure platform specific behaviour.
153 platform::configure_terminal();
154
155 // Seed pseudo-random algorithms.
156 std::srand(static_cast<unsigned int>(std::time(nullptr)));
157 }
158
159 void App::setup_logging(std::string_view log_dir)
160 {
161 // Configure logging.
162 const auto now = std::chrono::zoned_time {std::chrono::current_zone(), std::chrono::system_clock::now()}.get_local_time();
163 const std::string log_path = std::format("{0}{1}{2}", log_dir, std::format("{0:%d-%m-%Y-[%H-%M]}", now), ".log");
164 if (!std::filesystem::exists(log_dir))
165 {
166 std::filesystem::create_directory(log_dir);
167 }
168
169 entt::locator<logging::Log>::emplace();
170 GALAXY_ADD_SINK(logging::FileSink, log_path);
171 GALAXY_ADD_SINK(logging::ConsoleSink);
172 GALAXY_LOG(GALAXY_INFO, "App started.");
173 }
174
176 {
177 // Configure threadpool.
178 // Use half of available cores minus 2 for audio and main thread.
179 auto cores = (std::thread::hardware_concurrency() / 2) - 2;
180 if (cores < 4)
181 {
182 GALAXY_LOG(GALAXY_FATAL, "This app requires a minimum of 4 cores, only {0} were found.", cores);
183 }
184
185 entt::locator<BS::light_thread_pool>::emplace(cores);
186 }
187
188 void App::setup_config(std::string_view config_file)
189 {
190 auto& config = entt::locator<Config>::emplace(config_file);
193 }
194
196 {
197 entt::locator<fs::VirtualFileSystem>::emplace();
198 }
199
201 {
202 auto& window = entt::locator<sf::RenderWindow>::emplace();
203 window.setVisible(false);
204
205 sf::VideoMode mode;
206 mode.bitsPerPixel = sf::VideoMode::getDesktopMode().bitsPerPixel;
207 mode.size.x = settings::window_width();
208 mode.size.y = settings::window_height();
209
210 sf::ContextSettings context;
211 context.antiAliasingLevel = settings::msaa();
212 context.attributeFlags = sf::ContextSettings::Default;
213 context.depthBits = 24;
214 context.majorVersion = 3;
215 context.minorVersion = 2;
216 context.sRgbCapable = false;
217 context.stencilBits = 8;
218
219 const auto state = settings::fullscreen() ? sf::State::Fullscreen : sf::State::Windowed;
220 window.create(mode, settings::window_title(), sf::Style::Default, state, context);
221
222 if (!settings::window_icon().empty())
223 {
224 auto& fs = entt::locator<fs::VirtualFileSystem>::value();
225 auto data = fs.read_binary(settings::window_icon());
226
227 sf::Image image;
228 if (image.loadFromMemory(data.data(), data.size()))
229 {
230 // Copies, so can delete image after.
231 window.setIcon(image);
232 }
233 else
234 {
235 GALAXY_LOG(GALAXY_ERROR, "Failed to load window icon '{0}'.", settings::window_icon());
236 }
237 }
238
239 if (!settings::cursor_icon().empty())
240 {
241 window.setMouseCursorVisible(true);
242
243 if (settings::cursor_icon_size().x == 0 || settings::cursor_icon_size().y == 0)
244 {
245 GALAXY_LOG(GALAXY_WARNING, "Did not specify cursor size properly, must be same size as texture. Reverting to system default.");
246 }
247 else
248 {
249 auto& fs = entt::locator<fs::VirtualFileSystem>::value();
250 auto data = fs.read_binary(settings::window_icon());
251
252 m_cursor = sf::Cursor::createFromPixels(data.data(), settings::cursor_icon_size(), settings::cursor_hotspot());
253 if (m_cursor.has_value())
254 {
255 window.setMouseCursor(m_cursor.value());
256 }
257 else
258 {
259 GALAXY_LOG(GALAXY_ERROR, "Failed to load custom cursor: {0}.", settings::cursor_icon());
260 }
261 }
262 }
263 else
264 {
265 window.setMouseCursorVisible(settings::cursor_visible());
266 }
267
268 window.setMouseCursorGrabbed(settings::cursor_grabbed());
269 window.setVerticalSyncEnabled(settings::vsync());
270
271 if (!window.setActive(true))
272 {
273 GALAXY_LOG(GALAXY_FATAL, "Failed to get OpenGL active context.");
274 }
275
276 window.setVisible(true);
277
278 if (!window.hasFocus())
279 {
280 window.requestFocus();
281 }
282 }
283
285 {
286 entt::locator<entt::dispatcher>::emplace();
287 for (auto i = 0; i < sf::Joystick::Count; i++)
288 {
289 if (sf::Joystick::isConnected(i))
290 {
291 GALAXY_LOG(GALAXY_WARNING, "Gamepad detected at index {0}. Gamepads are not supported.", i);
292 }
293 }
294 }
295
297 {
298 }
299
301 {
302 // entt::locator<Loader>::make();
303 }
304
306 {
307 auto& sf = entt::locator<meta::SystemFactory>::emplace();
308 auto& ef = entt::locator<meta::EntityFactory>::emplace();
309
310 ef.register_component<components::Tag>("Tag");
311 /*
312 em.register_component<components::Animated>("Animated");
313 em.register_component<components::Circle>("Circle");
314 em.register_component<components::Ellipse>("Ellipse");
315 em.register_component<components::Point>("Point");
316 em.register_component<components::Polygon>("Polygon");
317 em.register_component<components::Polyline>("Polyline");
318 em.register_component<components::RigidBody>("RigidBody");
319 em.register_component<components::Script>("Script");
320 em.register_component<components::Sprite>("Sprite");
321 em.register_component<components::Text>("Text");
322 em.register_component<components::TileMap>("TileMap");
323 em.register_component<components::Transform>("Transform");
324 em.register_component<flags::DenySerialization>("DenySerialization");
325 em.register_component<flags::Disabled>("Disabled");
326
327 em.register_dependencies<components::Animated, components::Sprite>();
328 em.register_dependencies<components::Circle, components::Transform>();
329 em.register_dependencies<components::Ellipse, components::Transform>();
330 em.register_dependencies<components::Point, components::Transform>();
331 em.register_dependencies<components::Polygon, components::Transform>();
332 em.register_dependencies<components::Polyline, components::Transform>();
333 em.register_dependencies<components::RigidBody, components::Transform>();
334 em.register_dependencies<components::Sprite, components::Transform>();
335 em.register_dependencies<components::Text, components::Transform>();
336 */
337 }
338
340 {
341 auto& lua = entt::locator<sol::state>::emplace();
342 lua.open_libraries(
343 sol::lib::base,
344 sol::lib::package,
345 sol::lib::coroutine,
346 sol::lib::string,
347 sol::lib::os,
348 sol::lib::math,
349 sol::lib::table,
350 sol::lib::io,
351 sol::lib::utf8
352 );
353 }
354
356 {
357 // entt::locator<media::SoundEngine>::make(listener_count);
358 // entt::locator<media::MusicEngine>::make(listener_count);
359 // entt::locator<media::VoiceEngine>::make(listener_count);
360 // entt::locator<resource::SoundCache>::make();
361 // entt::locator<resource::MusicCache>::make();
362 // entt::locator<resource::VoiceCache>::make();
363 // entt::locator<resource::VideoCache>::make();
364 // entt::locator<resource::Animations>::make();
365 // entt::locator<resource::Shaders>::make();
366 // entt::locator<resource::Fonts>::make();
367 // entt::locator<resource::Textures>::make();
368 // entt::locator<resource::Prefabs>::make();
369 // entt::locator<resource::Scripts>::make();
370 entt::locator<scene::SceneManager>::emplace();
371 }
372
373 void App::handle_events(sf::RenderWindow& window)
374 {
375 window.handleEvents([](auto&& event) {
376 using Event = std::decay_t<decltype(event)>;
377
378 if constexpr (std::is_same_v<Event, sf::Event::Closed>)
379 {
380 entt::locator<sf::RenderWindow>::value().close();
381 }
382 else
383 {
384 entt::locator<entt::dispatcher>::value().trigger(event);
385 }
386 });
387 }
388 } // namespace core
389} // namespace galaxy
#define GALAXY_ADD_SINK(sink,...)
Definition Log.hpp:27
#define GALAXY_INFO
Log.hpp galaxy.
Definition Log.hpp:22
#define GALAXY_WARNING
Definition Log.hpp:23
#define GALAXY_LOG(level, msg,...)
Definition Log.hpp:28
#define GALAXY_FATAL
Definition Log.hpp:25
#define GALAXY_ERROR
Definition Log.hpp:24
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.
void inject()
Inject everything into Lua.
Definition Lua.cpp:14
Timer.hpp galaxy.
Definition Timer.cpp:18
Application.hpp galaxy.
static auto set_settings_from_config() -> void
Set all our settings using the provided config file.
Definition Settings.cpp:67
static auto asset_pack() noexcept -> const std::string &
Name of packed assets file.
Definition Settings.cpp:215
static auto root_dir() noexcept -> std::filesystem::path
Current root directory of application, unless it has been changed.
Definition Settings.cpp:200
static auto set_config_to_default() -> void
Restore all config settings to default.
Definition Settings.cpp:22