diff options
author | Stanislaw Halik <sthalik@misaki.pl> | 2024-03-01 07:14:51 +0100 |
---|---|---|
committer | Stanislaw Halik <sthalik@misaki.pl> | 2024-03-01 11:08:43 +0100 |
commit | fa8fb9598ed255057badc888da7e22227c45de1c (patch) | |
tree | b102acae5bc15c08ffe913409b6328f4b661ec0d | |
parent | 486e591169af724107ffaf6f24cd1e81b24529a9 (diff) |
wip1
-rw-r--r-- | compat/is-complete.hpp | 21 | ||||
-rw-r--r-- | compat/safe-ptr.hpp | 1 | ||||
-rw-r--r-- | editor/app.cpp | 2 | ||||
-rw-r--r-- | editor/imgui-misc.cpp | 2 | ||||
m--------- | external/magnum | 0 | ||||
-rw-r--r-- | floormat/main.hpp | 14 | ||||
-rw-r--r-- | main/ctor.cpp | 2 | ||||
-rw-r--r-- | main/draw.cpp | 206 | ||||
-rw-r--r-- | main/main-impl.cpp | 53 | ||||
-rw-r--r-- | main/main-impl.hpp | 18 | ||||
-rw-r--r-- | main/projection.cpp | 79 | ||||
-rw-r--r-- | main/setup.cpp | 107 | ||||
-rw-r--r-- | serialize/savegame.cpp | 1 | ||||
-rw-r--r-- | src/critter.cpp | 82 | ||||
-rw-r--r-- | src/object.cpp | 62 | ||||
-rw-r--r-- | src/object.hpp | 3 | ||||
-rw-r--r-- | src/scenery.cpp | 5 | ||||
-rw-r--r-- | src/timer-fwd.hpp | 1 | ||||
-rw-r--r-- | src/timer.hpp | 5 |
19 files changed, 261 insertions, 403 deletions
diff --git a/compat/is-complete.hpp b/compat/is-complete.hpp deleted file mode 100644 index d6bba88b..00000000 --- a/compat/is-complete.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -namespace floormat::detail_type_traits { - -namespace { template<class T> class IsComplete_ { - // from <Corrade/Containers/Pointer.h> - template<class U> static char get(U*, decltype(sizeof(U))* = nullptr); - static short get(...); - public: - enum: bool { value = sizeof(get(static_cast<T*>(nullptr))) == sizeof(char) }; -}; } // namespace - -} // namespace floormat::detail_type_traits - -namespace floormat { - -template<typename T> -constexpr inline bool is_complete = - bool(::floormat::detail_type_traits::IsComplete_<T>::value); - -} // namespace floormat diff --git a/compat/safe-ptr.hpp b/compat/safe-ptr.hpp index 2a77ea72..163ede9b 100644 --- a/compat/safe-ptr.hpp +++ b/compat/safe-ptr.hpp @@ -1,6 +1,5 @@ #pragma once #include "compat/assert.hpp" -#include "compat/defs.hpp" #include <type_traits> #include <Corrade/Tags.h> diff --git a/editor/app.cpp b/editor/app.cpp index 10f4b25d..bda4dc6c 100644 --- a/editor/app.cpp +++ b/editor/app.cpp @@ -68,7 +68,7 @@ shared_ptr_wrapper<critter> app::ensure_player_character(world& w) { critter_proto cproto; cproto.name = "Player"_s; - cproto.speed = 10; + cproto.speed = 1; // todo! was: 10 cproto.playable = true; ret.ptr = w.make_object<critter>(w.make_id(), global_coords{}, cproto); _character_id = ret.ptr->id; diff --git a/editor/imgui-misc.cpp b/editor/imgui-misc.cpp index b397945f..9add31b1 100644 --- a/editor/imgui-misc.cpp +++ b/editor/imgui-misc.cpp @@ -9,7 +9,7 @@ using namespace floormat::imgui; void app::draw_fps() { const auto dpi = M->dpi_scale(); - const auto frame_time = M->smoothed_dt(); + const auto frame_time = M->smoothed_frame_time(); char buf[16]; const double hz = frame_time > 1e-6f ? (int)std::round(10./(double)frame_time + .05) * .1 : 9999; snformat(buf, "{:.1f} FPS"_cf, hz); diff --git a/external/magnum b/external/magnum -Subproject 09336bc1d845ea55c39ae10f55f1dd18e1f085a +Subproject df66ecbcc09e9b00ec70b3f2f90c01420aba88a diff --git a/floormat/main.hpp b/floormat/main.hpp index a6f67ab0..f28ffebc 100644 --- a/floormat/main.hpp +++ b/floormat/main.hpp @@ -27,8 +27,12 @@ class astar; struct floormat_main { - struct draw_bounds final { int16_t minx, maxx, miny, maxy; }; - struct meshes final { + struct draw_bounds final + { + int16_t minx, maxx, miny, maxy; + }; + struct meshes final + { ground_mesh& ground; wall_mesh& wall; anim_mesh& anim; @@ -51,7 +55,7 @@ struct floormat_main virtual struct lightmap_shader& lightmap_shader() noexcept = 0; virtual const tile_shader& shader() const noexcept = 0; virtual void bind() noexcept = 0; - constexpr float smoothed_dt() const noexcept { return _frame_time; } + float smoothed_frame_time() const noexcept; virtual fm_settings& settings() noexcept = 0; virtual const fm_settings& settings() const noexcept = 0; @@ -88,8 +92,8 @@ struct floormat_main [[maybe_unused]] static void debug_break(); protected: - float _frame_time = 0; - Vector2 _dpi_scale{1, 1}, _virtual_scale{1, 1}; + float _smoothed_frame_time = 0; + Vector2 _dpi_scale{ 1, 1 }, _virtual_scale{ 1, 1 }; Vector2i _framebuffer_size; bool _do_render_vobjs : 1 = true; }; diff --git a/main/ctor.cpp b/main/ctor.cpp index 19a4546a..402e7db0 100644 --- a/main/ctor.cpp +++ b/main/ctor.cpp @@ -23,7 +23,7 @@ main_impl::main_impl(floormat_app& app, fm_settings&& se, int& argc, char** argv (void)setSwapInterval(0); set_fp_mask(); arrayReserve(_clickable_scenery, 128); - tm.timeline = Time::now(); + timeline = Time::now(); } class world& main_impl::reset_world(class world&& w) noexcept diff --git a/main/draw.cpp b/main/draw.cpp index 79e9ccfe..f039e321 100644 --- a/main/draw.cpp +++ b/main/draw.cpp @@ -4,131 +4,15 @@ #include "src/camera-offset.hpp" #include "src/anim-atlas.hpp" #include "main/clickable.hpp" -#include "src/light.hpp" -#include "src/log.hpp" #include "src/timer.hpp" #include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/ArrayView.h> #include <Magnum/GL/DefaultFramebuffer.h> #include <Magnum/GL/Renderer.h> -#include <Magnum/GL/RenderbufferFormat.h> -#include <Magnum/GL/TextureFormat.h> #include <Magnum/Math/Color.h> -#include <Magnum/Math/Functions.h> -#include <thread> -#include <algorithm> namespace floormat { -void main_impl::recalc_viewport(Vector2i fb_size, Vector2i win_size) noexcept -{ - _dpi_scale = dpiScaling(); - _virtual_scale = Vector2(fb_size) / Vector2(win_size); - update_window_state(); - _shader.set_scale(Vector2{fb_size}); - - GL::Renderer::setDepthMask(true); - -#ifdef FM_USE_DEPTH32 - { - framebuffer.fb = GL::Framebuffer{{ {}, fb_size }}; - - framebuffer.color = GL::Texture2D{}; - framebuffer.color.setStorage(1, GL::TextureFormat::RGBA8, fb_size); - framebuffer.depth = GL::Renderbuffer{}; - framebuffer.depth.setStorage(GL::RenderbufferFormat::DepthComponent32F, fb_size); - - framebuffer.fb.attachTexture(GL::Framebuffer::ColorAttachment{0}, framebuffer.color, 0); - framebuffer.fb.attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, framebuffer.depth); - framebuffer.fb.clearColor(0, Color4{0.f, 0.f, 0.f, 1.f}); - framebuffer.fb.clearDepth(0); - - framebuffer.fb.bind(); - } -#else - GL::defaultFramebuffer.setViewport({{}, fb_size }); - GL::defaultFramebuffer.clearColor(Color4{0.f, 0.f, 0.f, 1.f}); - GL::defaultFramebuffer.clearDepthStencil(0, 0); - GL::defaultFramebuffer.bind(); -#endif - - // -- state --- - glEnable(GL_LINE_SMOOTH); - using BlendEquation = GL::Renderer::BlendEquation; - using BlendFunction = GL::Renderer::BlendFunction; - using DepthFunction = GL::Renderer::DepthFunction; - using ProvokingVertex = GL::Renderer::ProvokingVertex; - using Feature = GL::Renderer::Feature; - GL::Renderer::setBlendEquation(BlendEquation::Add, BlendEquation::Add); - GL::Renderer::setBlendFunction(BlendFunction::SourceAlpha, BlendFunction::OneMinusSourceAlpha); - GL::Renderer::disable(Feature::FaceCulling); - GL::Renderer::disable(Feature::DepthTest); - GL::Renderer::enable(Feature::Blending); - GL::Renderer::enable(Feature::ScissorTest); - GL::Renderer::enable(Feature::DepthClamp); - GL::Renderer::setDepthFunction(DepthFunction::Greater); - GL::Renderer::setScissor({{}, fb_size}); - GL::Renderer::setProvokingVertex(ProvokingVertex::FirstVertexConvention); - - // -- user-- - app.on_viewport_event(fb_size); - tm.timeline = Time::now(); -} - -global_coords main_impl::pixel_to_tile(Vector2d position) const noexcept -{ - auto vec = pixel_to_tile_(position); - auto vec_ = Math::floor(vec); - return { (int32_t)vec_.x(), (int32_t)vec.y(), 0 }; -} - -Vector2d main_impl::pixel_to_tile_(Vector2d position) const noexcept -{ - constexpr Vector2d pixel_size(TILE_SIZE2); - constexpr Vector2d half{.5, .5}; - const Vector2d px = position - Vector2d{window_size()}*.5 - _shader.camera_offset(); - return tile_shader::unproject(px*.5) / pixel_size + half; -} - -auto main_impl::get_draw_bounds() const noexcept -> draw_bounds -{ - using limits = std::numeric_limits<int16_t>; - auto x0 = limits::max(), x1 = limits::min(), y0 = limits::max(), y1 = limits::min(); - - const auto win = Vector2d(window_size()); - - chunk_coords list[] = { - 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(), - }; - - auto center = pixel_to_tile(Vector2d{win[0]/2, win[1]/2}).chunk(); - - for (auto p : list) - { - x0 = Math::min(x0, p.x); - x1 = Math::max(x1, p.x); - y0 = Math::min(y0, p.y); - y1 = Math::max(y1, p.y); - } - // todo test this with the default viewport size using --magnum-dpi-scaling=1 - x0 -= 1; y0 -= 1; x1 += 1; y1 += 1; - - constexpr int16_t mx = tile_shader::max_screen_tiles.x()/(int16_t)2, - my = tile_shader::max_screen_tiles.y()/(int16_t)2; - int16_t minx = center.x - mx + 1, maxx = center.x + mx, - miny = center.y - my + 1, maxy = center.y + my; - - x0 = Math::clamp(x0, minx, maxx); - x1 = Math::clamp(x1, minx, maxx); - y0 = Math::clamp(y0, miny, maxy); - y1 = Math::clamp(y1, miny, maxy); - - return {x0, x1, y0, y1}; -} - void main_impl::draw_world() noexcept { const auto [z_min, z_max, z_cur, only] = app.get_z_bounds(); @@ -200,32 +84,10 @@ void main_impl::draw_world() noexcept GL::Renderer::disable(GL::Renderer::Feature::DepthTest); } -bool floormat_main::check_chunk_visible(const Vector2d& offset, const Vector2i& size) noexcept -{ - constexpr Vector3d len = dTILE_SIZE * TILE_MAX_DIM20d; - enum : size_t { x, y, }; - constexpr Vector2d p00 = tile_shader::project(Vector3d(0, 0, 0)), - p10 = tile_shader::project(Vector3d(len[x], 0, 0)), - p01 = tile_shader::project(Vector3d(0, len[y], 0)), - p11 = tile_shader::project(Vector3d(len[x], len[y], 0)); - constexpr auto xx = std::minmax({ p00[x], p10[x], p01[x], p11[x], }), - yy = std::minmax({ p00[y], p10[y], p01[y], p11[y], }); - constexpr auto minx = xx.first, maxx = xx.second, miny = yy.first, maxy = yy.second; - constexpr int W = (int)(maxx - minx + .5 + 1e-16), H = (int)(maxy - miny + .5 + 1e-16); - const auto X = (int)(minx + (offset[x] + size[x])*.5), Y = (int)(miny + (offset[y] + size[y])*.5); - return X + W > 0 && X < size[x] && Y + H > 0 && Y < size[y]; -} - -#if 0 -#ifndef FM_NO_DEBUG -static size_t good_frames, bad_frames; // NOLINT -#endif -#endif - void main_impl::do_update() // todo! move to separate file { constexpr auto eps = 1e-5f; - const auto dt = tm.timeline.update(); + const auto dt = timeline.update(); if (auto secs = Time::to_seconds(dt); secs > eps) { #if 1 @@ -242,51 +104,6 @@ void main_impl::do_update() // todo! move to separate file else swapBuffers(); -#if 0 -#ifndef FM_NO_DEBUG - if (dt_expected.value == 0 || !is_log_verbose()) [[likely]] - void(); - else if (auto d = Math::abs(dt - dt_expected.value); d <= 4e-3f) - { - dt = dt_expected.value + 1e-6f; - ++good_frames; - } - else [[unlikely]] - { - if (good_frames) - { - DBG_nospace << ++bad_frames << " bad frame " << d << ", expected:" << dt_expected.value << " good-frames:" << good_frames; - good_frames = 0; - } - } -#endif - - dt = Math::clamp(dt, 1e-5f, Math::max(.2f, dt_expected.value)); - - if constexpr((false)) - { - static struct - { - Timeline tl{}; - float time = 0; - unsigned frames = 0; - } t; - - if (t.frames++ == 0) - t.tl.start(); - else - t.time += dt; - - if (t.frames > 200) - { - Debug{} << "time" - << (double)t.time * 1000. / t.frames - << (double)t.tl.currentFrameDuration() * 1000. / t.frames; - t = {}; - } - } -#endif - app.update(dt); } @@ -318,27 +135,6 @@ void main_impl::drawEvent() // todo! move to separate file (with `do_update') swapBuffers(); redraw(); - -#if 0 - if (dt_expected.do_sleep && (false)) - { - constexpr float ε = 1e-3f; - const float Δt൦ = timeline.previousFrameDuration(), sleep_secs = dt_expected.value - Δt൦ - dt_expected.jitter; - if (sleep_secs > ε) - 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.previousFrameDuration() - dt_expected.value; - constexpr float α = .1f; - dt_expected.jitter = Math::max(dt_expected.jitter + Δt * α, - dt_expected.jitter * (1-α) + Δt * α); - dt_expected.jitter = std::copysign(std::fmin(dt_expected.value, std::fabs(dt_expected.jitter)), dt_expected.jitter); - } - else - { - dt_expected.jitter = 0; - dt_expected.value = 0; - } -#endif } ArrayView<const clickable> main_impl::clickable_scenery() const noexcept diff --git a/main/main-impl.cpp b/main/main-impl.cpp index 11561a4a..9f1e1e97 100644 --- a/main/main-impl.cpp +++ b/main/main-impl.cpp @@ -1,6 +1,6 @@ #include "main-impl.hpp" -#include "src/search-astar.hpp" #include "src/search.hpp" +#include "src/search-astar.hpp" #include <Magnum/Platform/Sdl2Application.h> namespace floormat { @@ -8,25 +8,12 @@ namespace floormat { floormat_main::floormat_main() noexcept = default; floormat_main::~floormat_main() noexcept = default; -main_impl::~main_impl() noexcept +floormat_main* floormat_main::create(floormat_app& app, fm_settings&& options) { - reset_world(); + auto* ret = new main_impl(app, move(options), options.argc, const_cast<char**>(options.argv)); + return ret; } -void main_impl::quit(int status) { Platform::Sdl2Application::exit(status); } -class world& main_impl::world() noexcept { return _world; } -SDL_Window* main_impl::window() noexcept { return Sdl2Application::window(); } -fm_settings& main_impl::settings() noexcept { return s; } -const fm_settings& main_impl::settings() const noexcept { return s; } -tile_shader& main_impl::shader() noexcept { return _shader; } -const tile_shader& main_impl::shader() const noexcept { return _shader; } -struct lightmap_shader& main_impl::lightmap_shader() noexcept { return _lightmap_shader; } -bool main_impl::is_text_input_active() const noexcept { return const_cast<main_impl&>(*this).isTextInputActive(); } -void main_impl::start_text_input() noexcept { startTextInput(); } -void main_impl::stop_text_input() noexcept { stopTextInput(); } -Platform::Sdl2Application& main_impl::application() noexcept { return *this; } -const Platform::Sdl2Application& main_impl::application() const noexcept { return *this; } - int main_impl::exec() { _framebuffer_size = framebufferSize(); @@ -34,20 +21,11 @@ int main_impl::exec() return Sdl2Application::exec(); } -floormat_main* floormat_main::create(floormat_app& app, fm_settings&& options) -{ - auto* ret = new main_impl(app, move(options), options.argc, const_cast<char**>(options.argv)); - return ret; -} - -Vector2i floormat_main::window_size() const noexcept +main_impl::~main_impl() noexcept { - return _framebuffer_size; + reset_world(); } -void floormat_main::set_render_vobjs(bool value) { _do_render_vobjs = value; } -bool floormat_main::is_rendering_vobjs() const { return _do_render_vobjs; } - void main_impl::set_cursor(uint32_t cursor) noexcept { if (cursor != _mouse_cursor || _mouse_cursor == (uint32_t)-1) @@ -63,8 +41,27 @@ uint32_t main_impl::cursor() const noexcept return (uint32_t)static_cast<App*>(const_cast<main_impl*>(this))->cursor(); } +void main_impl::quit(int status) { Platform::Sdl2Application::exit(status); } +class world& main_impl::world() noexcept { return _world; } +SDL_Window* main_impl::window() noexcept { return Sdl2Application::window(); } +fm_settings& main_impl::settings() noexcept { return s; } +const fm_settings& main_impl::settings() const noexcept { return s; } +tile_shader& main_impl::shader() noexcept { return _shader; } +const tile_shader& main_impl::shader() const noexcept { return _shader; } +struct lightmap_shader& main_impl::lightmap_shader() noexcept { return _lightmap_shader; } +bool main_impl::is_text_input_active() const noexcept { return const_cast<main_impl&>(*this).isTextInputActive(); } +void main_impl::start_text_input() noexcept { startTextInput(); } +void main_impl::stop_text_input() noexcept { stopTextInput(); } +Platform::Sdl2Application& main_impl::application() noexcept { return *this; } +const Platform::Sdl2Application& main_impl::application() const noexcept { return *this; } struct texture_unit_cache& main_impl::texture_unit_cache() { return _tuc; } path_search& main_impl::search() { return *_search; } astar& main_impl::astar() { return *_astar; } +Vector2i floormat_main::window_size() const noexcept { return _framebuffer_size; } +void floormat_main::set_render_vobjs(bool value) { _do_render_vobjs = value; } +bool floormat_main::is_rendering_vobjs() const { return _do_render_vobjs; } + +float floormat_main::smoothed_frame_time() const noexcept { return _smoothed_frame_time; } + } // namespace floormat diff --git a/main/main-impl.hpp b/main/main-impl.hpp index 01be6187..62346893 100644 --- a/main/main-impl.hpp +++ b/main/main-impl.hpp @@ -59,6 +59,7 @@ struct main_impl final : Platform::Sdl2Application, floormat_main class world& reset_world() noexcept override; class world& reset_world(class world&& w) noexcept override; SDL_Window* window() noexcept override; + void update_window_state(); fm_settings& settings() noexcept override; const fm_settings& settings() const noexcept override; @@ -86,7 +87,6 @@ struct main_impl final : Platform::Sdl2Application, floormat_main void drawEvent() override; void bind() noexcept override; void do_update(); - void update_window_state(); struct meshes meshes() noexcept override; bool is_text_input_active() const noexcept override; @@ -103,11 +103,8 @@ struct main_impl final : Platform::Sdl2Application, floormat_main class astar& astar() override; private: - struct { - Ns last_frame_duration{0}; - Time frame_start; - Time timeline; // todo rename - } tm; + Time timeline; + float _frame_time = 0; struct texture_unit_cache _tuc; fm_settings s; @@ -127,15 +124,6 @@ private: safe_ptr<path_search> _search; safe_ptr<class astar> _astar; -#if 0 - struct { - float value = 0; - float jitter = 0; - bool do_sleep = false; - bool has_focus = true; - } dt_expected; -#endif - void recalc_viewport(Vector2i fb_size, Vector2i win_size) noexcept; void draw_world() noexcept; //bool draw_lights(chunk& c, const std::array<chunk*, 8>& neighbors) noexcept; diff --git a/main/projection.cpp b/main/projection.cpp new file mode 100644 index 00000000..8572d2de --- /dev/null +++ b/main/projection.cpp @@ -0,0 +1,79 @@ +#include "main-impl.hpp" +#include "src/tile-constants.hpp" +#include <algorithm> +#include <limits> + +namespace floormat { + +global_coords main_impl::pixel_to_tile(Vector2d position) const noexcept +{ + auto vec = pixel_to_tile_(position); + auto vec_ = Math::floor(vec); + return { (int32_t)vec_.x(), (int32_t)vec.y(), 0 }; +} + +Vector2d main_impl::pixel_to_tile_(Vector2d position) const noexcept +{ + constexpr Vector2d pixel_size{tile_size_xy}; + constexpr Vector2d half{.5, .5}; + const Vector2d px = position - Vector2d{window_size()}*.5 - _shader.camera_offset(); + return tile_shader::unproject(px*.5) / pixel_size + half; +} + +auto main_impl::get_draw_bounds() const noexcept -> draw_bounds +{ + using limits = std::numeric_limits<int16_t>; + auto x0 = limits::max(), x1 = limits::min(), y0 = limits::max(), y1 = limits::min(); + + const auto win = Vector2d(window_size()); + + chunk_coords list[] = { + 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(), + }; + + auto center = pixel_to_tile(Vector2d{win[0]/2, win[1]/2}).chunk(); + + for (auto p : list) + { + x0 = Math::min(x0, p.x); + x1 = Math::max(x1, p.x); + y0 = Math::min(y0, p.y); + y1 = Math::max(y1, p.y); + } + // todo test this with the default viewport size using --magnum-dpi-scaling=1 + x0 -= 1; y0 -= 1; x1 += 1; y1 += 1; + + constexpr int16_t mx = tile_shader::max_screen_tiles.x()/(int16_t)2, + my = tile_shader::max_screen_tiles.y()/(int16_t)2; + int16_t minx = center.x - mx + 1, maxx = center.x + mx, + miny = center.y - my + 1, maxy = center.y + my; + + x0 = Math::clamp(x0, minx, maxx); + x1 = Math::clamp(x1, minx, maxx); + y0 = Math::clamp(y0, miny, maxy); + y1 = Math::clamp(y1, miny, maxy); + + return {x0, x1, y0, y1}; +} + +bool floormat_main::check_chunk_visible(const Vector2d& offset, const Vector2i& size) noexcept +{ + constexpr Vector3d len = dTILE_SIZE * TILE_MAX_DIM20d; + enum : size_t { x, y, }; + constexpr Vector2d p00 = tile_shader::project(Vector3d(0, 0, 0)), + p10 = tile_shader::project(Vector3d(len[x], 0, 0)), + p01 = tile_shader::project(Vector3d(0, len[y], 0)), + p11 = tile_shader::project(Vector3d(len[x], len[y], 0)); + constexpr auto xx = std::minmax({ p00[x], p10[x], p01[x], p11[x], }), + yy = std::minmax({ p00[y], p10[y], p01[y], p11[y], }); + constexpr auto minx = xx.first, maxx = xx.second, miny = yy.first, maxy = yy.second; + constexpr int W = (int)(maxx - minx + .5 + 1e-16), H = (int)(maxy - miny + .5 + 1e-16); + const auto X = (int)(minx + (offset[x] + size[x])*.5), Y = (int)(miny + (offset[y] + size[y])*.5); + return X + W > 0 && X < size[x] && Y + H > 0 && Y < size[y]; +} + + +} diff --git a/main/setup.cpp b/main/setup.cpp index 7014cc0d..2c5600e3 100644 --- a/main/setup.cpp +++ b/main/setup.cpp @@ -1,10 +1,70 @@ #include "main-impl.hpp" -#include <algorithm> -#include <Corrade/Containers/StringView.h> -#include <Corrade/Containers/StringIterable.h> +#include "floormat/app.hpp" +#include <cr/StringView.h> +#include <cr/StringIterable.h> +#include <mg/Functions.h> +#include <Magnum/GL/Renderer.h> +#include <Magnum/GL/RenderbufferFormat.h> +#include <Magnum/GL/TextureFormat.h> +#include <algorithm> // todo! std::minmax namespace floormat { +void main_impl::recalc_viewport(Vector2i fb_size, Vector2i win_size) noexcept +{ + _dpi_scale = dpiScaling(); + _virtual_scale = Vector2(fb_size) / Vector2(win_size); + update_window_state(); + _shader.set_scale(Vector2{fb_size}); + + GL::Renderer::setDepthMask(true); + +#ifdef FM_USE_DEPTH32 + { + framebuffer.fb = GL::Framebuffer{{ {}, fb_size }}; + + framebuffer.color = GL::Texture2D{}; + framebuffer.color.setStorage(1, GL::TextureFormat::RGBA8, fb_size); + framebuffer.depth = GL::Renderbuffer{}; + framebuffer.depth.setStorage(GL::RenderbufferFormat::DepthComponent32F, fb_size); + + framebuffer.fb.attachTexture(GL::Framebuffer::ColorAttachment{0}, framebuffer.color, 0); + framebuffer.fb.attachRenderbuffer(GL::Framebuffer::BufferAttachment::Depth, framebuffer.depth); + framebuffer.fb.clearColor(0, Color4{0.f, 0.f, 0.f, 1.f}); + framebuffer.fb.clearDepth(0); + + framebuffer.fb.bind(); + } +#else + GL::defaultFramebuffer.setViewport({{}, fb_size }); + GL::defaultFramebuffer.clearColor(Color4{0.f, 0.f, 0.f, 1.f}); + GL::defaultFramebuffer.clearDepthStencil(0, 0); + GL::defaultFramebuffer.bind(); +#endif + + // -- state --- + glEnable(GL_LINE_SMOOTH); + using BlendEquation = GL::Renderer::BlendEquation; + using BlendFunction = GL::Renderer::BlendFunction; + using DepthFunction = GL::Renderer::DepthFunction; + using ProvokingVertex = GL::Renderer::ProvokingVertex; + using Feature = GL::Renderer::Feature; + GL::Renderer::setBlendEquation(BlendEquation::Add, BlendEquation::Add); + GL::Renderer::setBlendFunction(BlendFunction::SourceAlpha, BlendFunction::OneMinusSourceAlpha); + GL::Renderer::disable(Feature::FaceCulling); + GL::Renderer::disable(Feature::DepthTest); + GL::Renderer::enable(Feature::Blending); + GL::Renderer::enable(Feature::ScissorTest); + GL::Renderer::enable(Feature::DepthClamp); + GL::Renderer::setDepthFunction(DepthFunction::Greater); + GL::Renderer::setScissor({{}, fb_size}); + GL::Renderer::setProvokingVertex(ProvokingVertex::FirstVertexConvention); + + // -- user-- + app.on_viewport_event(fb_size); + timeline = Time::now(); +} + auto main_impl::make_window_flags(const fm_settings& s) -> Configuration::WindowFlags { using flag = Configuration::WindowFlag; @@ -43,50 +103,23 @@ auto main_impl::make_gl_conf(const fm_settings&) -> GLConfiguration .setStencilBufferSize(0); } +void main_impl::update_window_state() // todo! window minimized, out of focus, fake vsync etc +{ +} + + 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) + 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 Math::clamp(dpymode.refresh_rate, 30, 400); return 30; } -void main_impl::update_window_state() -{ - const auto flags = (SDL_WindowFlags)SDL_GetWindowFlags(window()); - -#if 0 - dt_expected.do_sleep = true; - dt_expected.jitter = 0; - dt_expected.has_focus = true; - if (flags & SDL_WINDOW_HIDDEN) - { - dt_expected.has_focus = false; - dt_expected.value = 1; - } - else if (int interval = std::abs(SDL_GL_GetSwapInterval()); s.vsync && interval > 0) - { - int hz = get_window_refresh_rate(window()) / interval; - if (!(flags & SDL_WINDOW_INPUT_FOCUS)) - { - dt_expected.value = 2.f / hz; - dt_expected.has_focus = false; - } - else - dt_expected.value = 1.f/hz; - } - else - { - dt_expected.do_sleep = false; - dt_expected.value = 1e-1f; - } -#endif -} - auto main_impl::meshes() noexcept -> struct meshes { return { _ground_mesh, _wall_mesh, _anim_mesh, }; diff --git a/serialize/savegame.cpp b/serialize/savegame.cpp index 10f4d1cf..0fb3c4b1 100644 --- a/serialize/savegame.cpp +++ b/serialize/savegame.cpp @@ -218,6 +218,7 @@ struct visitor_ do_visit(obj.name, f); if (PROTO >= 22) [[likely]] do_visit(obj.speed, f); + fm_soft_assert(obj.speed >= 0); do_visit(obj.offset_frac, f); constexpr struct { diff --git a/src/critter.cpp b/src/critter.cpp index fb3d7e52..a96e5418 100644 --- a/src/critter.cpp +++ b/src/critter.cpp @@ -16,8 +16,7 @@ namespace floormat { namespace { -constexpr double framerate = 60, move_speed = 60; -constexpr double frame_time = 1/framerate; +using F = float; constexpr auto arrows_to_dir(bool left, bool right, bool up, bool down) { @@ -33,6 +32,7 @@ constexpr auto arrows_to_dir(bool left, bool right, bool up, bool down) switch (bits) { using enum rotation; + default: std::unreachable(); case 0: return rotation{rotation_COUNT}; case L | U: return W; case L | D: return S; @@ -43,28 +43,30 @@ constexpr auto arrows_to_dir(bool left, bool right, bool up, bool down) case R: return NE; case U: return NW; } - std::unreachable(); - fm_assert(false); } constexpr Vector2 rotation_to_vec(rotation r) { - constexpr double c = move_speed * frame_time; - constexpr double d = c / Vector2d{1, 1}.length(); + constexpr double framerate = 60, move_speed = 60; + constexpr double frame_time = F(1)/framerate; + constexpr double b = move_speed * frame_time; + constexpr double inv_sqrt_2 = F{1}/Math::sqrt(F{2}); + constexpr auto d = F(b * inv_sqrt_2); + constexpr auto c = F(b); constexpr Vector2 array[8] = { - Vector2(Vector2d{ 0, -1} * c), - Vector2(Vector2d{ 1, -1} * d), - Vector2(Vector2d{ 1, 0} * c), - Vector2(Vector2d{ 1, 1} * d), - Vector2(Vector2d{ 0, 1} * c), - Vector2(Vector2d{-1, 1} * d), - Vector2(Vector2d{-1, 0} * c), - Vector2(Vector2d{-1, -1} * d), + Vector2{ 0, -1} * c, // N + Vector2{ 1, -1} * d, // NE + Vector2{ 1, 0} * c, // E + Vector2{ 1, 1} * d, // SE + Vector2{ 0, 1} * c, // S + Vector2{-1, 1} * d, // SW + Vector2{-1, 0} * c, // W + Vector2{-1, -1} * d, // NW }; - CORRADE_ASSUME(r < rotation_COUNT); - return array[(size_t)r]; + auto value = array[(size_t)r]; + return value; } constexpr std::array<rotation, 3> rotation_to_similar(rotation r) @@ -133,26 +135,6 @@ void critter::update(size_t i, Ns dt) { if (playable) { -#if 0 - static auto TL = Time::now(); - static Ns TIME{0}; - static unsigned FRAMES; - - if (++FRAMES == 0) - TL = Time::now(); - else - TIME += dt; - - if (++FRAMES > 240) - { - auto t = TL.update(); - Debug{} << "player time" << Time::to_milliseconds(TIME) << Time::to_milliseconds(t); - Debug{} << Time::to_milliseconds(TIME) / Time::to_milliseconds(t); - TIME = Ns{0}; - FRAMES = 0; - } -#endif - const auto new_r = arrows_to_dir(b_L, b_R, b_U, b_D); if (new_r == rotation_COUNT) { @@ -173,16 +155,9 @@ void critter::update_nonplayable(size_t i, Ns dt) void critter::update_movement(size_t i, Ns dt, rotation new_r) { - fm_assert(new_r < rotation_COUNT); - fm_assert(is_dynamic()); - - auto nframes = allocate_frame_time(dt * speed); - if (nframes == 0) - { - static unsigned foo; - //Debug{} << ++foo << "stopped"; - return; - } + const auto fps = atlas->info().fps; + fm_assert(fps > 0); + const auto nframes = allocate_frame_time(delta, dt, speed, fps); const auto rotations = rotation_to_similar(new_r); const unsigned nvecs = (int)new_r & 1 ? 3 : 1; @@ -193,19 +168,19 @@ void critter::update_movement(size_t i, Ns dt, rotation new_r) c->ensure_passability(); - for (auto k = 0u; k < nframes; k++) + for (uint32_t k = 0; k < nframes; k++) { - for (unsigned j = 0; j < nvecs; j++) + for (uint8_t j = 0; j < nvecs; j++) { - const auto vec = rotation_to_vec(rotations[j]); - constexpr auto frac = 65535u; - constexpr auto inv_frac = 1.f / (float)frac; + const auto vec = rotation_to_vec(rotations[j]); + constexpr auto frac = F{65535}; + constexpr auto inv_frac = 1 / frac; const auto sign_vec = Math::sign(vec); auto offset_ = vec + Vector2(offset_frac) * sign_vec * inv_frac; auto off_i = Vector2i(offset_); if (!off_i.isZero()) { - offset_frac = Vector2us(Math::abs(Math::fmod(offset_, 1.f)) * frac); + offset_frac = Vector2us(Math::abs(Math::fmod(offset_, F(1))) * frac); if (can_move_to(off_i)) { move_to(i, off_i, new_r); @@ -215,7 +190,7 @@ void critter::update_movement(size_t i, Ns dt, rotation new_r) } else { - offset_frac = Vector2us(Math::abs(Math::min({1.f,1.f}, offset_)) * frac); + offset_frac = Vector2us(Math::abs(Math::min({F(1),F(1)}, offset_)) * frac); break; } } @@ -243,6 +218,7 @@ critter::critter(object_id id, class chunk& c, const critter_proto& proto) : name = "(Unnamed)"_s; fm_soft_assert(atlas->check_rotation(r)); object::set_bbox_(offset, bbox_offset, Vector2ub(iTILE_SIZE2/2), pass); + fm_soft_assert(speed >= 0); } } // namespace floormat diff --git a/src/object.cpp b/src/object.cpp index 9aca4705..1c242887 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -269,39 +269,47 @@ void object::move_to(Magnum::Vector2i delta) move_to(i, delta, r); } -uint32_t object::allocate_frame_time(Ns dt, uint16_t& accum, uint32_t hz) +uint32_t object::allocate_frame_time(uint16_t& accum, Ns dt_, float speed, uint32_t hz) { + constexpr auto second = uint64_t(1e9), u16_max = uint64_t{65535}; + //dt_ = Ns(16e6); + fm_assert(dt_ >= Ns{}); fm_assert(hz > 0); - fm_assert(dt >= Ns{0}); - constexpr auto ns_in_sec = Ns::Type{Ns(1e9)}; - //const auto count = Ns::Type{ns_in_sec / hz} + accum}; - const auto nsecs = Ns::Type{dt} + accum * ns_in_sec / Ns::Type{65535}; - const auto frame_duration = ns_in_sec / hz; - const auto frames = (uint32_t)(nsecs / frame_duration); - const auto rem = nsecs % frame_duration; - const auto new_accum_ = rem * Ns::Type{65535} / ns_in_sec; - const auto new_accum = (uint16_t)Math::clamp(new_accum_, Ns::Type{0}, Ns::Type{65535}); - [[maybe_unused]] const auto old_accum = accum; - accum = new_accum; -#if 0 - DBG_nospace << "alloc-frame-time: " - << "dt:" << fraction(Time::to_milliseconds(dt)) << "ms" - << ", secs:" << fraction(Time::to_milliseconds(Ns{nsecs}), 1) << " ms" - << ", frames:" << frames - << ", acc:" << new_accum_ - << ", rem:" << rem; + fm_assert(speed >= 0); + + const auto frame_duration_ns = second / hz; + const auto dt_nsec = (uint64_t)int64_t{dt_}; + + const auto nsecs_from_accum = accum * second / u16_max; + const auto nsecs = dt_nsec + nsecs_from_accum; + + const auto ticks = (uint64_t)(nsecs * speed); + const auto rem_ns = ticks % frame_duration_ns; + const auto rem_ = rem_ns * u16_max / second; + + const auto frames = (uint32_t)(ticks / frame_duration_ns); + + const auto rem = (uint16_t)Math::clamp(rem_, uint64_t(0), uint64_t(65535)); + const auto old_accum = accum; (void)old_accum; + accum = rem; +#if 1 + if (frames) + { + DBG_nospace << "alloc-frame: " + << "dt:" << fraction(Time::to_milliseconds(Ns(dt_nsec))) << " ms" + << ", dt-nsec:" << Time::to_milliseconds(Ns(dt_nsec)) + << ", dt-acc:" << Time::to_milliseconds(Ns(nsecs_from_accum)) + << ", Hz:" << hz + << ", old-acc:" << old_accum + << ", frames:" << frames + //<< ", duration:" << Time::to_milliseconds(Ns(frame_duration_ns)) + << ", ticks:" << fraction(Time::to_milliseconds(Ns(ticks)), 1) + << ", rem:" << Time::to_milliseconds(Ns(rem_ns)); + } #endif return frames; } -uint32_t object::allocate_frame_time(Ns dt) -{ - fm_assert(atlas); - auto hz = atlas->info().fps; - fm_assert(hz > 0); - return allocate_frame_time(dt, delta, hz); -} - void object::set_bbox_(Vector2b offset_, Vector2b bb_offset_, Vector2ub bb_size_, pass_mode pass_) { const_cast<Vector2b&>(offset) = offset_; diff --git a/src/object.hpp b/src/object.hpp index e462c8db..b2cf2884 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -85,8 +85,7 @@ struct object void move_to(size_t& i, Vector2i delta, rotation new_r); void move_to(Vector2i delta); - static uint32_t allocate_frame_time(Ns dt, uint16_t& accum, uint32_t hz); - uint32_t allocate_frame_time(Ns dt); + static uint32_t allocate_frame_time(uint16_t& accum, Ns dt, float speed, uint32_t hz); protected: object(object_id id, class chunk& c, const object_proto& proto); diff --git a/src/scenery.cpp b/src/scenery.cpp index b5fd3b59..20fdcad7 100644 --- a/src/scenery.cpp +++ b/src/scenery.cpp @@ -89,8 +89,9 @@ void door_scenery::update(scenery& s, size_t, Ns dt) fm_assert(s.atlas); auto& anim = *s.atlas; const auto nframes = (int)anim.info().nframes; - fm_debug_assert(anim.info().fps > 0 && anim.info().fps <= 0xff); - const auto n = (int)s.allocate_frame_time(dt); + const auto fps = anim.info().fps; + fm_debug_assert(fps > 0 && fps <= 0xff); + const auto n = (int)s.allocate_frame_time(s.delta, dt, 1, fps); if (n == 0) return; const int8_t dir = closing ? 1 : -1; diff --git a/src/timer-fwd.hpp b/src/timer-fwd.hpp index 5e11ce7b..9831319a 100644 --- a/src/timer-fwd.hpp +++ b/src/timer-fwd.hpp @@ -6,6 +6,7 @@ namespace Magnum::Math { template<class T> class Nanoseconds; } namespace floormat { using Ns = Math::Nanoseconds<int64_t>; +//long double operator/(Ns a, Ns b) noexcept; struct Time final { diff --git a/src/timer.hpp b/src/timer.hpp index 24554363..d9b0f602 100644 --- a/src/timer.hpp +++ b/src/timer.hpp @@ -1,12 +1,9 @@ #pragma once #include "timer-fwd.hpp" -#include <mg/Time.h> +#include <mg/Time.h> // todo! replace with my own namespace floormat { -long double operator/(Ns a, Ns b) noexcept; -using namespace Magnum::Math::Literals::TimeLiterals; - constexpr inline size_t fm_DATETIME_BUF_SIZE = 32; const char* format_datetime_to_string(char(&buf)[fm_DATETIME_BUF_SIZE]); |