summaryrefslogtreecommitdiffhomepage
path: root/main
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2022-10-26 03:51:18 +0200
committerStanislaw Halik <sthalik@misaki.pl>2022-10-26 03:51:18 +0200
commit8aae71d17c3927af76fae980cd0f04f93c628abd (patch)
treef62e1ecc6562ed9cdafa12106322ffd2bb1ea67b /main
parenta1b8d914bf457dd4508ca37610219ced93303292 (diff)
a
Diffstat (limited to 'main')
-rw-r--r--main/draw.cpp139
-rw-r--r--main/events.cpp1
-rw-r--r--main/main-impl.cpp250
-rw-r--r--main/setup.cpp116
4 files changed, 255 insertions, 251 deletions
diff --git a/main/draw.cpp b/main/draw.cpp
new file mode 100644
index 00000000..f1d4e4d8
--- /dev/null
+++ b/main/draw.cpp
@@ -0,0 +1,139 @@
+#include "main-impl.hpp"
+#include "floormat/app.hpp"
+#include "src/camera-offset.hpp"
+#include <Magnum/GL/DefaultFramebuffer.h>
+
+namespace floormat {
+
+void main_impl::recalc_viewport(Vector2i size) noexcept
+{
+ update_window_state();
+ GL::defaultFramebuffer.setViewport({{}, size });
+ _msaa_framebuffer.detach(GL::Framebuffer::ColorAttachment{0});
+ _msaa_renderbuffer = Magnum::GL::Renderbuffer{};
+ _msaa_renderbuffer.setStorageMultisample(s.msaa_samples, GL::RenderbufferFormat::RGBA8, size);
+ _msaa_framebuffer.setViewport({{}, size });
+ _msaa_framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _msaa_renderbuffer);
+ _shader.set_scale(Vector2{size});
+ app.on_viewport_event(size);
+}
+
+global_coords main_impl::pixel_to_tile(Vector2d position) const noexcept
+{
+ constexpr Vector2d pixel_size{dTILE_SIZE[0], dTILE_SIZE[1]};
+ constexpr Vector2d half{.5, .5};
+ const Vector2d px = position - Vector2d{windowSize()}*.5 - _shader.camera_offset()*.5;
+ const Vector2d vec = tile_shader::unproject(px) / pixel_size + half;
+ const auto x = (std::int32_t)std::floor(vec[0]), y = (std::int32_t)std::floor(vec[1]);
+ return { x, y };
+}
+
+auto main_impl::get_draw_bounds() const noexcept -> draw_bounds
+{
+
+ using limits = std::numeric_limits<std::int16_t>;
+ auto x0 = limits::max(), x1 = limits::min(), y0 = limits::max(), y1 = limits::min();
+
+ for (const auto win = Vector2d(windowSize());
+ auto p : {pixel_to_tile(Vector2d{0, 0}).chunk(),
+ pixel_to_tile(Vector2d{win[0]-1, 0}).chunk(),
+ pixel_to_tile(Vector2d{0, win[1]-1}).chunk(),
+ pixel_to_tile(Vector2d{win[0]-1, win[1]-1}).chunk()})
+ {
+ x0 = std::min(x0, p.x);
+ x1 = std::max(x1, p.x);
+ y0 = std::min(y0, p.y);
+ y1 = std::max(y1, p.y);
+ }
+ return {x0, x1, y0, y1};
+}
+
+void main_impl::draw_world() noexcept
+{
+ auto [minx, maxx, miny, maxy] = get_draw_bounds();
+
+ for (std::int16_t y = miny; y <= maxy; y++)
+ for (std::int16_t x = minx; x <= maxx; x++)
+ {
+ if (const chunk_coords c = {x, y}; !_world.contains(c))
+ app.maybe_initialize_chunk(c, _world[c]);
+ const chunk_coords c{x, y};
+ const with_shifted_camera_offset o{_shader, c};
+ _floor_mesh.draw(_shader, _world[c]);
+ }
+
+ for (std::int16_t y = miny; y <= maxy; y++)
+ for (std::int16_t x = minx; x <= maxx; x++)
+ {
+ const chunk_coords c{x, y};
+ const with_shifted_camera_offset o{_shader, c};
+ _wall_mesh.draw(_shader, _world[c]);
+ }
+}
+
+void main_impl::drawEvent()
+{
+ float dt = timeline.previousFrameDuration();
+ if (dt > 0)
+ {
+ const float RC1 = dt_expected.do_sleep ? 1.f : 1.f/15,
+ RC2 = dt_expected.do_sleep ? 1.f/15 : 1.f/60;
+ const float alpha1 = dt/(dt + RC1);
+ const float alpha2 = dt/(dt + RC2);
+
+ _frame_time1 = _frame_time1*(1-alpha1) + alpha1*dt;
+ _frame_time2 = _frame_time1*(1-alpha2) + alpha2*dt;
+ constexpr float max_deviation = 5 * 1e-3f;
+ if (std::fabs(_frame_time1 - _frame_time2) > max_deviation)
+ _frame_time1 = _frame_time2;
+ }
+ else
+ {
+ swapBuffers();
+ timeline.nextFrame();
+ }
+
+ dt = std::clamp(dt, 1e-5f, std::fmaxf(1e-1f, dt_expected.value));
+
+ app.update(dt);
+ _shader.set_tint({1, 1, 1, 1});
+
+ {
+ GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
+#ifndef FM_SKIP_MSAA
+ _msaa_framebuffer.clear(GL::FramebufferClear::Color);
+ _msaa_framebuffer.bind();
+#endif
+ draw_world();
+ app.draw_msaa();
+#ifndef FM_SKIP_MSAA
+ GL::defaultFramebuffer.bind();
+ GL::Framebuffer::blit(_msaa_framebuffer, GL::defaultFramebuffer, {{}, windowSize()}, GL::FramebufferBlit::Color);
+#endif
+ }
+
+ app.draw();
+
+ swapBuffers();
+ redraw();
+
+ if (dt_expected.do_sleep)
+ {
+ constexpr float eps = 1e-3f;
+ const float Δt൦ = timeline.currentFrameDuration(), sleep_secs = dt_expected.value - Δt൦ - dt_expected.jitter;
+ if (sleep_secs > eps)
+ std::this_thread::sleep_for(std::chrono::nanoseconds((long long)(sleep_secs * 1e9f)));
+ //fm_debug("jitter:%.1f sleep:%.0f", dt_expected.jitter*1000, sleep_secs*1000);
+ const float Δt = timeline.currentFrameDuration() - dt_expected.value;
+ constexpr float α = .1f;
+ dt_expected.jitter = std::fmax(dt_expected.jitter + Δt * α,
+ dt_expected.jitter * (1-α) + Δt * α);
+ dt_expected.jitter = std::copysignf(std::fminf(dt_expected.value, std::fabsf(dt_expected.jitter)), dt_expected.jitter);
+ }
+ else
+ dt_expected.jitter = 0;
+ timeline.nextFrame();
+}
+
+} // namespace floormat
+
diff --git a/main/events.cpp b/main/events.cpp
index 8b94ba7b..ad7e64d1 100644
--- a/main/events.cpp
+++ b/main/events.cpp
@@ -1,7 +1,6 @@
#include "main-impl.hpp"
#include "floormat/app.hpp"
#include "floormat/events.hpp"
-#include "compat/assert.hpp"
#include <SDL_events.h>
#include <SDL_video.h>
diff --git a/main/main-impl.cpp b/main/main-impl.cpp
index 1552c980..dec3948f 100644
--- a/main/main-impl.cpp
+++ b/main/main-impl.cpp
@@ -1,15 +1,5 @@
#include "main-impl.hpp"
-#include "floormat/app.hpp"
#include "compat/assert.hpp"
-#include "compat/fpu.hpp"
-#include "src/chunk.hpp"
-#include "src/world.hpp"
-#include "src/camera-offset.hpp"
-#include <Magnum/GL/DefaultFramebuffer.h>
-#include <chrono>
-#include <thread>
-
-//#define FM_SKIP_MSAA
namespace floormat {
@@ -17,246 +7,6 @@ floormat_main::floormat_main() noexcept = default;
floormat_main::~floormat_main() noexcept = default;
main_impl::~main_impl() noexcept = default;
-auto main_impl::make_window_flags(const fm_settings& s) -> Configuration::WindowFlags
-{
- using flag = Configuration::WindowFlag;
- Configuration::WindowFlags flags{};
- if (s.resizable)
- flags |= flag::Resizable;
- if (s.fullscreen)
- flags |= flag::Fullscreen;
- if (s.fullscreen_desktop)
- flags |= flag::FullscreenDesktop;
- if (s.borderless)
- flags |= flag::Borderless;
- if (s.maximized)
- flags |= flag::Maximized;
- return flags;
-}
-
-auto main_impl::make_conf(const fm_settings& s) -> Configuration
-{
- switch (s.log_level)
- {
- default:
- SDL_setenv("MAGNUM_LOG_LEVEL", "normal", 1);
- break;
- case fm_log_level::quiet:
- SDL_setenv("MAGNUM_LOG_LEVEL", "quiet", 1);
- break;
- case fm_log_level::verbose:
- SDL_setenv("MAGNUM_LOG_LEVEL", "verbose", 1);
- break;
- }
- return Configuration{}
- .setTitle(s.title)
- .setSize(s.resolution)
- .setWindowFlags(make_window_flags(s));
-}
-
-auto main_impl::make_gl_conf(const fm_settings& s) -> GLConfiguration
-{
- GLConfiguration::Flags flags{};
- using f = GLConfiguration::Flag;
- if (s.gpu_debug >= fm_gpu_debug::on)
- flags |= f::Debug | f::GpuValidation;
- if (s.gpu_debug >= fm_gpu_debug::robust)
- flags |= f::RobustAccess;
- else if (s.gpu_debug <= fm_gpu_debug::no_error)
- flags |= f::NoError;
- return GLConfiguration{}.setFlags(flags);
-}
-
-void main_impl::recalc_viewport(Vector2i size) noexcept
-{
- update_window_state();
- GL::defaultFramebuffer.setViewport({{}, size });
- _msaa_framebuffer.detach(GL::Framebuffer::ColorAttachment{0});
- _msaa_renderbuffer = Magnum::GL::Renderbuffer{};
- _msaa_renderbuffer.setStorageMultisample(s.msaa_samples, GL::RenderbufferFormat::RGBA8, size);
- _msaa_framebuffer.setViewport({{}, size });
- _msaa_framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _msaa_renderbuffer);
- _shader.set_scale(Vector2{size});
- app.on_viewport_event(size);
-}
-
-main_impl::main_impl(floormat_app& app, fm_settings&& s, int& fake_argc) noexcept :
- Platform::Sdl2Application{Arguments{fake_argc, nullptr},
- make_conf(s), make_gl_conf(s)},
- s{std::move(s)}, app{app}
-{
- switch (s.vsync) // NOLINT(bugprone-use-after-move)
- {
- case fm_tristate::on:
- (void)setSwapInterval(1);
- if (const auto list = GL::Context::current().extensionStrings();
- std::find(list.cbegin(), list.cend(), "EXT_swap_control_tear") != list.cbegin())
- (void)setSwapInterval(-1);
- break;
- case fm_tristate::off:
- setSwapInterval(0);
- break;
- default:
- break;
- }
- set_fp_mask();
- fm_assert(framebufferSize() == windowSize());
- timeline.start();
-}
-
-global_coords main_impl::pixel_to_tile(Vector2d position) const noexcept
-{
- constexpr Vector2d pixel_size{dTILE_SIZE[0], dTILE_SIZE[1]};
- constexpr Vector2d half{.5, .5};
- const Vector2d px = position - Vector2d{windowSize()}*.5 - _shader.camera_offset()*.5;
- const Vector2d vec = tile_shader::unproject(px) / pixel_size + half;
- const auto x = (std::int32_t)std::floor(vec[0]), y = (std::int32_t)std::floor(vec[1]);
- return { x, y };
-}
-
-auto main_impl::get_draw_bounds() const noexcept -> draw_bounds
-{
-
- using limits = std::numeric_limits<std::int16_t>;
- auto x0 = limits::max(), x1 = limits::min(), y0 = limits::max(), y1 = limits::min();
-
- for (const auto win = Vector2d(windowSize());
- auto p : {pixel_to_tile(Vector2d{0, 0}).chunk(),
- pixel_to_tile(Vector2d{win[0]-1, 0}).chunk(),
- pixel_to_tile(Vector2d{0, win[1]-1}).chunk(),
- pixel_to_tile(Vector2d{win[0]-1, win[1]-1}).chunk()})
- {
- x0 = std::min(x0, p.x);
- x1 = std::max(x1, p.x);
- y0 = std::min(y0, p.y);
- y1 = std::max(y1, p.y);
- }
- return {x0, x1, y0, y1};
-}
-
-void main_impl::draw_world() noexcept
-{
- auto [minx, maxx, miny, maxy] = get_draw_bounds();
-
- for (std::int16_t y = miny; y <= maxy; y++)
- for (std::int16_t x = minx; x <= maxx; x++)
- {
- if (const chunk_coords c = {x, y}; !_world.contains(c))
- app.maybe_initialize_chunk(c, _world[c]);
- const chunk_coords c{x, y};
- const with_shifted_camera_offset o{_shader, c};
- _floor_mesh.draw(_shader, _world[c]);
- }
-
- for (std::int16_t y = miny; y <= maxy; y++)
- for (std::int16_t x = minx; x <= maxx; x++)
- {
- const chunk_coords c{x, y};
- const with_shifted_camera_offset o{_shader, c};
- _wall_mesh.draw(_shader, _world[c]);
- }
-}
-
-static int get_window_refresh_rate(SDL_Window* window)
-{
- fm_assert(window != nullptr);
- if (int index = SDL_GetWindowDisplayIndex(window); index < 0)
- fm_warn_once("SDL_GetWindowDisplayIndex: %s", SDL_GetError());
- else if (SDL_DisplayMode dpymode; SDL_GetCurrentDisplayMode(index, &dpymode) < 0)
- fm_warn_once("SDL_GetCurrentDisplayMode: %s", SDL_GetError());
- else
- return std::max(30, dpymode.refresh_rate);
- return 30;
-}
-
-void main_impl::update_window_state()
-{
- const auto flags = (SDL_WindowFlags)SDL_GetWindowFlags(window());
-
- dt_expected.do_sleep = true;
- dt_expected.jitter = 0;
- if (flags & SDL_WINDOW_HIDDEN)
- dt_expected.value = 1;
- else if (!(flags & SDL_WINDOW_INPUT_FOCUS))
- dt_expected.value = 1.f / 30;
- else if (int interval = std::abs(SDL_GL_GetSwapInterval());
- s.vsync >= fm_tristate::maybe && interval > 0)
- dt_expected.value = 0.5f / (get_window_refresh_rate(window()));
-#if 1
- else if (!(flags & SDL_WINDOW_MOUSE_FOCUS))
- dt_expected.value = 1.f / 60;
-#endif
- else
- {
- dt_expected.do_sleep = false;
- dt_expected.value = 1e-1f;
- }
-}
-
-void main_impl::drawEvent()
-{
- float dt = timeline.previousFrameDuration();
- if (dt > 0)
- {
- const float RC1 = dt_expected.do_sleep ? 1.f : 1.f/15,
- RC2 = dt_expected.do_sleep ? 1.f/15 : 1.f/60;
- const float alpha1 = dt/(dt + RC1);
- const float alpha2 = dt/(dt + RC2);
-
- _frame_time1 = _frame_time1*(1-alpha1) + alpha1*dt;
- _frame_time2 = _frame_time1*(1-alpha2) + alpha2*dt;
- constexpr float max_deviation = 5 * 1e-3f;
- if (std::fabs(_frame_time1 - _frame_time2) > max_deviation)
- _frame_time1 = _frame_time2;
- }
- else
- {
- swapBuffers();
- timeline.nextFrame();
- }
-
- dt = std::clamp(dt, 1e-5f, std::fmaxf(1e-1f, dt_expected.value));
-
- app.update(dt);
- _shader.set_tint({1, 1, 1, 1});
-
- {
- GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
-#ifndef FM_SKIP_MSAA
- _msaa_framebuffer.clear(GL::FramebufferClear::Color);
- _msaa_framebuffer.bind();
-#endif
- draw_world();
- app.draw_msaa();
-#ifndef FM_SKIP_MSAA
- GL::defaultFramebuffer.bind();
- GL::Framebuffer::blit(_msaa_framebuffer, GL::defaultFramebuffer, {{}, windowSize()}, GL::FramebufferBlit::Color);
-#endif
- }
-
- app.draw();
-
- swapBuffers();
- redraw();
-
- if (dt_expected.do_sleep)
- {
- constexpr float eps = 1e-3f;
- const float Δt൦ = timeline.currentFrameDuration(), sleep_secs = dt_expected.value - Δt൦ - dt_expected.jitter;
- if (sleep_secs > eps)
- std::this_thread::sleep_for(std::chrono::nanoseconds((long long)(sleep_secs * 1e9f)));
- //fm_debug("jitter:%.1f sleep:%.0f", dt_expected.jitter*1000, sleep_secs*1000);
- const float Δt = timeline.currentFrameDuration() - dt_expected.value;
- constexpr float α = .1f;
- dt_expected.jitter = std::fmax(dt_expected.jitter + Δt * α,
- dt_expected.jitter * (1-α) + Δt * α);
- dt_expected.jitter = std::copysignf(std::fminf(dt_expected.value, std::fabsf(dt_expected.jitter)), dt_expected.jitter);
- }
- else
- dt_expected.jitter = 0;
- timeline.nextFrame();
-}
-
void main_impl::quit(int status) { Platform::Sdl2Application::exit(status); }
struct world& main_impl::world() noexcept { return _world; }
SDL_Window* main_impl::window() noexcept { return Sdl2Application::window(); }
diff --git a/main/setup.cpp b/main/setup.cpp
new file mode 100644
index 00000000..428a797b
--- /dev/null
+++ b/main/setup.cpp
@@ -0,0 +1,116 @@
+#include "main-impl.hpp"
+#include "compat/fpu.hpp"
+
+namespace floormat {
+
+main_impl::main_impl(floormat_app& app, fm_settings&& s, int& fake_argc) noexcept :
+ Platform::Sdl2Application{Arguments{fake_argc, nullptr},
+ make_conf(s), make_gl_conf(s)},
+ s{std::move(s)}, app{app}
+{
+ switch (s.vsync) // NOLINT(bugprone-use-after-move)
+ {
+ case fm_tristate::on:
+ (void)setSwapInterval(1);
+ if (const auto list = GL::Context::current().extensionStrings();
+ std::find(list.cbegin(), list.cend(), "EXT_swap_control_tear") != list.cbegin())
+ (void)setSwapInterval(-1);
+ break;
+ case fm_tristate::off:
+ setSwapInterval(0);
+ break;
+ default:
+ break;
+ }
+ set_fp_mask();
+ fm_assert(framebufferSize() == windowSize());
+ timeline.start();
+}
+
+auto main_impl::make_window_flags(const fm_settings& s) -> Configuration::WindowFlags
+{
+ using flag = Configuration::WindowFlag;
+ Configuration::WindowFlags flags{};
+ if (s.resizable)
+ flags |= flag::Resizable;
+ if (s.fullscreen)
+ flags |= flag::Fullscreen;
+ if (s.fullscreen_desktop)
+ flags |= flag::FullscreenDesktop;
+ if (s.borderless)
+ flags |= flag::Borderless;
+ if (s.maximized)
+ flags |= flag::Maximized;
+ return flags;
+}
+
+auto main_impl::make_conf(const fm_settings& s) -> Configuration
+{
+ switch (s.log_level)
+ {
+ default:
+ SDL_setenv("MAGNUM_LOG_LEVEL", "normal", 1);
+ break;
+ case fm_log_level::quiet:
+ SDL_setenv("MAGNUM_LOG_LEVEL", "quiet", 1);
+ break;
+ case fm_log_level::verbose:
+ SDL_setenv("MAGNUM_LOG_LEVEL", "verbose", 1);
+ break;
+ }
+ return Configuration{}
+ .setTitle(s.title)
+ .setSize(s.resolution)
+ .setWindowFlags(make_window_flags(s));
+}
+
+auto main_impl::make_gl_conf(const fm_settings& s) -> GLConfiguration
+{
+ GLConfiguration::Flags flags{};
+ using f = GLConfiguration::Flag;
+ if (s.gpu_debug >= fm_gpu_debug::on)
+ flags |= f::Debug | f::GpuValidation;
+ if (s.gpu_debug >= fm_gpu_debug::robust)
+ flags |= f::RobustAccess;
+ else if (s.gpu_debug <= fm_gpu_debug::no_error)
+ flags |= f::NoError;
+ return GLConfiguration{}.setFlags(flags);
+}
+
+static int get_window_refresh_rate(SDL_Window* window)
+{
+ fm_assert(window != nullptr);
+ if (int index = SDL_GetWindowDisplayIndex(window); index < 0)
+ fm_warn_once("SDL_GetWindowDisplayIndex: %s", SDL_GetError());
+ else if (SDL_DisplayMode dpymode; SDL_GetCurrentDisplayMode(index, &dpymode) < 0)
+ fm_warn_once("SDL_GetCurrentDisplayMode: %s", SDL_GetError());
+ else
+ return std::max(30, dpymode.refresh_rate);
+ return 30;
+}
+
+void main_impl::update_window_state()
+{
+ const auto flags = (SDL_WindowFlags)SDL_GetWindowFlags(window());
+
+ dt_expected.do_sleep = true;
+ dt_expected.jitter = 0;
+ if (flags & SDL_WINDOW_HIDDEN)
+ dt_expected.value = 1;
+ else if (!(flags & SDL_WINDOW_INPUT_FOCUS))
+ dt_expected.value = 1.f / 30;
+ else if (int interval = std::abs(SDL_GL_GetSwapInterval());
+ s.vsync >= fm_tristate::maybe && interval > 0)
+ dt_expected.value = 0.5f / (get_window_refresh_rate(window()));
+#if 1
+ else if (!(flags & SDL_WINDOW_MOUSE_FOCUS))
+ dt_expected.value = 1.f / 60;
+#endif
+ else
+ {
+ dt_expected.do_sleep = false;
+ dt_expected.value = 1e-1f;
+ }
+}
+
+} // namespace floormat