summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2024-03-01 07:14:51 +0100
committerStanislaw Halik <sthalik@misaki.pl>2024-03-01 11:08:43 +0100
commitfa8fb9598ed255057badc888da7e22227c45de1c (patch)
treeb102acae5bc15c08ffe913409b6328f4b661ec0d
parent486e591169af724107ffaf6f24cd1e81b24529a9 (diff)
wip1
-rw-r--r--compat/is-complete.hpp21
-rw-r--r--compat/safe-ptr.hpp1
-rw-r--r--editor/app.cpp2
-rw-r--r--editor/imgui-misc.cpp2
m---------external/magnum0
-rw-r--r--floormat/main.hpp14
-rw-r--r--main/ctor.cpp2
-rw-r--r--main/draw.cpp206
-rw-r--r--main/main-impl.cpp53
-rw-r--r--main/main-impl.hpp18
-rw-r--r--main/projection.cpp79
-rw-r--r--main/setup.cpp107
-rw-r--r--serialize/savegame.cpp1
-rw-r--r--src/critter.cpp82
-rw-r--r--src/object.cpp62
-rw-r--r--src/object.hpp3
-rw-r--r--src/scenery.cpp5
-rw-r--r--src/timer-fwd.hpp1
-rw-r--r--src/timer.hpp5
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]);