diff options
Diffstat (limited to 'editor')
-rw-r--r-- | editor/CMakeLists.txt | 14 | ||||
-rw-r--r-- | editor/app.cpp | 0 | ||||
-rw-r--r-- | editor/app.hpp | 0 | ||||
-rw-r--r-- | editor/camera.cpp | 88 | ||||
-rw-r--r-- | editor/editor.cpp | 234 | ||||
-rw-r--r-- | editor/editor.hpp | 93 | ||||
-rw-r--r-- | editor/imgui-raii.hpp | 86 | ||||
-rw-r--r-- | editor/imgui.cpp | 240 | ||||
-rw-r--r-- | editor/keyboard.cpp | 38 | ||||
-rw-r--r-- | editor/update.cpp | 55 |
10 files changed, 848 insertions, 0 deletions
diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt new file mode 100644 index 00000000..8d3a0bf5 --- /dev/null +++ b/editor/CMakeLists.txt @@ -0,0 +1,14 @@ +set(self ${PROJECT_NAME}-editor) + +corrade_add_resource(res ../resources.conf) + +if(MSVC) + set_property(SOURCE "${res}" APPEND PROPERTY COMPILE_OPTIONS "-W0") +else() + set_property(SOURCE "${res}" APPEND PROPERTY COMPILE_OPTIONS "-w") +endif() + +add_executable(${self} "${res}" "../loader/loader-impl.cpp") +target_link_libraries(${self} ${PROJECT_NAME}-main) + +install(TARGETS ${self} RUNTIME DESTINATION bin) diff --git a/editor/app.cpp b/editor/app.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/editor/app.cpp diff --git a/editor/app.hpp b/editor/app.hpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/editor/app.hpp diff --git a/editor/camera.cpp b/editor/camera.cpp new file mode 100644 index 00000000..7af77211 --- /dev/null +++ b/editor/camera.cpp @@ -0,0 +1,88 @@ +#include "app.hpp" +#include <Magnum/GL/DefaultFramebuffer.h> + +namespace floormat { + +void app::do_camera(double dt) +{ + if (keys[key::camera_reset]) + reset_camera_offset(); + else + { + Vector2d dir{}; + + if (keys[key::camera_up]) + dir += Vector2d{0, -1}; + else if (keys[key::camera_down]) + dir += Vector2d{0, 1}; + if (keys[key::camera_left]) + dir += Vector2d{-1, 0}; + else if (keys[key::camera_right]) + dir += Vector2d{1, 0}; + + if (dir != Vector2d{}) + { + constexpr double screens_per_second = 1; + const auto pixels_per_second = windowSize().length() / screens_per_second; + auto camera_offset = _shader.camera_offset(); + const auto max_camera_offset = Vector2d(windowSize() * 10); + + camera_offset -= dir.normalized() * dt * pixels_per_second; + camera_offset[0] = std::clamp(camera_offset[0], -max_camera_offset[0], max_camera_offset[0]); + camera_offset[1] = std::clamp(camera_offset[1], -max_camera_offset[1], max_camera_offset[1]); + + _shader.set_camera_offset(camera_offset); + } + else + return; + } + recalc_cursor_tile(); + if (_cursor_tile) + do_mouse_move(*_cursor_tile); +} + +void app::reset_camera_offset() +{ + _shader.set_camera_offset(tile_shader::project({TILE_MAX_DIM*-.5*dTILE_SIZE[0], TILE_MAX_DIM*-.5*dTILE_SIZE[1], 0})); + recalc_cursor_tile(); +} + +void app::recalc_cursor_tile() +{ + if (_cursor_pixel && !_cursor_in_imgui) + _cursor_tile = pixel_to_tile(Vector2d(*_cursor_pixel)); + else + _cursor_tile = std::nullopt; +} + +global_coords app::pixel_to_tile(Vector2d position) const +{ + 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 }; +} + +std::array<std::int16_t, 4> app::get_draw_bounds() const noexcept +{ + + 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}; +} + +} // namespace floormat diff --git a/editor/editor.cpp b/editor/editor.cpp new file mode 100644 index 00000000..f3c8b157 --- /dev/null +++ b/editor/editor.cpp @@ -0,0 +1,234 @@ +#include "editor.hpp" +#include "serialize/json-helper.hpp" +#include "serialize/tile-atlas.hpp" +#include "src/loader.hpp" +#include "random.hpp" +#include "compat/assert.hpp" +#include "compat/unreachable.hpp" +#include "src/tile-defs.hpp" +#include "src/world.hpp" +#include <Corrade/Containers/StringStlView.h> +#include <filesystem> +#include <vector> + +namespace floormat { + +static const std::filesystem::path image_path{IMAGE_PATH, std::filesystem::path::generic_format}; + +tile_type::tile_type(editor_mode mode, Containers::StringView name) : _name{name}, _mode{mode} +{ + load_atlases(); +} + +void tile_type::load_atlases() +{ + using atlas_array = std::vector<std::shared_ptr<tile_atlas>>; + for (auto& atlas : json_helper::from_json<atlas_array>(image_path/(_name + ".json"))) + { + Containers::StringView name = atlas->name(); + if (auto x = name.findLast('.'); x) + name = name.prefix(x.data()); + auto& [_, vec] = _permutation; + vec.reserve((std::size_t)atlas->num_tiles()); + _atlases[name] = std::move(atlas); + } +} + +std::shared_ptr<tile_atlas> tile_type::maybe_atlas(Containers::StringView str) +{ + auto it = std::find_if(_atlases.begin(), _atlases.end(), [&](const auto& tuple) -> bool { + const auto& [x, _] = tuple; + return Containers::StringView{x} == str; + }); + if (it == _atlases.end()) + return nullptr; + else + return it->second; +} + +std::shared_ptr<tile_atlas> tile_type::atlas(Containers::StringView str) +{ + if (auto ptr = maybe_atlas(str); ptr) + return ptr; + else + fm_abort("no such atlas: %s", str.cbegin()); +} + +void tile_type::clear_selection() +{ + _selected_tile = {}; + _permutation = {}; + _selection_mode = sel_none; +} + +void tile_type::select_tile(const std::shared_ptr<tile_atlas>& atlas, std::size_t variant) +{ + fm_assert(atlas); + clear_selection(); + _selection_mode = sel_tile; + _selected_tile = { atlas, variant % atlas->num_tiles() }; +} + +void tile_type::select_tile_permutation(const std::shared_ptr<tile_atlas>& atlas) +{ + fm_assert(atlas); + clear_selection(); + _selection_mode = sel_perm; + _permutation = { atlas, {} }; +} + +bool tile_type::is_tile_selected(const std::shared_ptr<const tile_atlas>& atlas, std::size_t variant) const +{ + return atlas && _selection_mode == sel_tile && _selected_tile && + atlas == _selected_tile.atlas && variant == _selected_tile.variant; +} + +bool tile_type::is_permutation_selected(const std::shared_ptr<const tile_atlas>& atlas) const +{ + const auto& [perm, _] = _permutation; + return atlas && _selection_mode == sel_perm && perm == atlas; +} + +bool tile_type::is_atlas_selected(const std::shared_ptr<const tile_atlas>& atlas) const +{ + switch (_selection_mode) + { + default: + case sel_none: + return false; + case sel_perm: + return is_permutation_selected(atlas); + case sel_tile: + return atlas && _selected_tile && atlas == _selected_tile.atlas; + } +} + +template<std::random_access_iterator T> +void fisher_yates(T begin, T end) +{ + const auto N = std::distance(begin, end); + for (auto i = N-1; i >= 1; i--) + { + const auto j = random(i+1); + using std::swap; + swap(begin[i], begin[j]); + } +} + +tile_image tile_type::get_selected_perm() +{ + auto& [atlas, vec] = _permutation; + const std::size_t N = atlas->num_tiles(); + if (N == 0) + return {}; + if (vec.empty()) + { + for (std::size_t i = 0; i < N; i++) + vec.push_back(i); + fisher_yates(vec.begin(), vec.end()); + } + const auto idx = vec.back(); + vec.pop_back(); + return {atlas, idx}; +} + +tile_image tile_type::get_selected() +{ + switch (_selection_mode) + { + case sel_none: + return {}; + case sel_tile: + return _selected_tile; + case sel_perm: + return get_selected_perm(); + default: + fm_warn_once("invalid editor mode '%u'", (unsigned)_selection_mode); + break; + } +} + +void tile_type::place_tile(world& world, global_coords pos, tile_image& img) +{ + const auto& [c, t] = world[pos]; + const auto& [atlas, variant] = img; + switch (_mode) + { + default: + fm_warn_once("invalid editor mode '%u'", (unsigned)_mode); + break; + case editor_mode::select: + break; + case editor_mode::floor: { + const auto& [c, t] = world[pos]; + t.ground_image = { atlas, variant }; + break; + } + case editor_mode::walls: { + break; // todo + } + } +} + +editor::editor() +{ + set_mode(editor_mode::floor); // TODO +} + +void editor::set_mode(editor_mode mode) +{ + _mode = mode; + on_release(); +} + +const tile_type* editor::current() const +{ + switch (_mode) + { + case editor_mode::select: + return nullptr; + case editor_mode::floor: + return &_floor; + case editor_mode::walls: + return nullptr; // todo + default: + fm_warn_once("invalid editor mode '%u'", (unsigned)_mode); + return nullptr; + } +} + +tile_type* editor::current() +{ + return const_cast<tile_type*>(static_cast<const editor&>(*this).current()); +} + +void editor::on_release() +{ + _last_pos = std::nullopt; +} + +void editor::on_mouse_move(world& world, const global_coords pos) +{ + if (_last_pos && *_last_pos != pos) + { + _last_pos = pos; + on_click(world, pos); + } +} + +void editor::on_click(world& world, global_coords pos) +{ + if (auto* mode = current(); mode) + { + auto opt = mode->get_selected(); + if (opt) + { + _last_pos = pos; + mode->place_tile(world, pos, opt); + } + else + on_release(); + } +} + +} // namespace floormat diff --git a/editor/editor.hpp b/editor/editor.hpp new file mode 100644 index 00000000..28ba153c --- /dev/null +++ b/editor/editor.hpp @@ -0,0 +1,93 @@ +#pragma once +#include "compat/defs.hpp" +#include "tile-atlas.hpp" +#include "global-coords.hpp" +#include "tile.hpp" + +#include <cstdint> +#include <tuple> +#include <optional> +#include <vector> +#include <map> +#include <memory> +#include <Corrade/Containers/StringView.h> + +namespace floormat { + +enum class editor_mode : unsigned char { + select, floor, walls, +}; + +struct world; + +struct tile_type final +{ + tile_type(editor_mode mode, Containers::StringView name); + std::shared_ptr<tile_atlas> maybe_atlas(Containers::StringView str); + std::shared_ptr<tile_atlas> atlas(Containers::StringView str); + auto cbegin() const { return _atlases.cbegin(); } + auto cend() const { return _atlases.cend(); } + auto begin() const { return _atlases.cbegin(); } + auto end() const { return _atlases.cend(); } + Containers::StringView name() const { return _name; } + editor_mode mode() const { return _mode; } + + void clear_selection(); + void select_tile(const std::shared_ptr<tile_atlas>& atlas, std::size_t variant); + void select_tile_permutation(const std::shared_ptr<tile_atlas>& atlas); + bool is_tile_selected(const std::shared_ptr<const tile_atlas>& atlas, std::size_t variant) const; + bool is_permutation_selected(const std::shared_ptr<const tile_atlas>& atlas) const; + bool is_atlas_selected(const std::shared_ptr<const tile_atlas>& atlas) const; + tile_image get_selected(); + void place_tile(world& world, global_coords pos, tile_image& img); + +private: + enum selection_mode : std::uint8_t { + sel_none, sel_tile, sel_perm, + }; + enum rotation : std::uint8_t { + rot_N, rot_W, + }; + + std::string _name; + std::map<std::string, std::shared_ptr<tile_atlas>> _atlases; + tile_image _selected_tile; + std::tuple<std::shared_ptr<tile_atlas>, std::vector<std::size_t>> _permutation; + selection_mode _selection_mode = sel_none; + editor_mode _mode; + rotation _rotation{}; + + void load_atlases(); + tile_image get_selected_perm(); +}; + +struct editor final +{ + [[nodiscard]] bool dirty() const { return _dirty; } + void set_dirty(bool value) { _dirty = value; } + [[nodiscard]] editor_mode mode() const { return _mode; } + void set_mode(editor_mode mode); + + tile_type& floor() { return _floor; } + const tile_type& floor() const { return _floor; } + + tile_type* current(); + const tile_type* current() const; + + void on_click(world& world, global_coords pos); + void on_mouse_move(world& world, const global_coords pos); + void on_release(); + + editor(); + editor(editor&&) noexcept = default; + editor& operator=(editor&&) noexcept = default; + fm_DECLARE_DELETED_COPY_ASSIGNMENT(editor); + +private: + tile_type _floor{editor_mode::floor, "floor"}; + std::optional<global_coords> _last_pos; + editor_mode _mode = editor_mode::select; + bool _dirty = false; +}; + +} // namespace floormat diff --git a/editor/imgui-raii.hpp b/editor/imgui-raii.hpp new file mode 100644 index 00000000..afae29d6 --- /dev/null +++ b/editor/imgui-raii.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include <Corrade/Containers/StringView.h> +#include <Magnum/Math/Color.h> +#ifndef __CLION_IDE__zz +#include <imgui.h> +#endif + +namespace floormat::imgui { + +struct raii_wrapper final +{ + using F = void(*)(void); + raii_wrapper(F fn) : dtor{fn} {} + raii_wrapper() = default; + ~raii_wrapper() { if (dtor) dtor(); } + raii_wrapper(const raii_wrapper&) = delete; + raii_wrapper& operator=(const raii_wrapper&) = delete; + raii_wrapper& operator=(raii_wrapper&&) = delete; + raii_wrapper(raii_wrapper&& other) noexcept : dtor{other.dtor} { other.dtor = nullptr; } + inline operator bool() const noexcept { return dtor != nullptr; } + + F dtor = nullptr; +}; + +[[nodiscard]] static inline raii_wrapper begin_window(Containers::StringView name = {}, + ImGuiWindowFlags_ flags = ImGuiWindowFlags_None) +{ + if (name.isEmpty()) + name = "floormat editor"; + if (ImGui::Begin(name.data(), nullptr, flags)) + return {&ImGui::End}; + else + return {}; +} + +[[nodiscard]] static inline raii_wrapper begin_main_menu() +{ + if (ImGui::BeginMainMenuBar()) + return {&ImGui::EndMainMenuBar}; + else + return {}; +} +[[nodiscard]] static inline raii_wrapper begin_menu(Containers::StringView name, bool enabled = true) +{ + if (ImGui::BeginMenu(name.data(), enabled)) + return {&ImGui::EndMenu}; + else + return {}; +} + +[[nodiscard]] static inline raii_wrapper begin_list_box(Containers::StringView name, ImVec2 size = {}) +{ + if (ImGui::BeginListBox(name.data(), size)) + return {&ImGui::EndListBox}; + else + return {}; +} + +[[nodiscard]] static inline raii_wrapper tree_node(Containers::StringView name, ImGuiTreeNodeFlags_ flags = ImGuiTreeNodeFlags_None) +{ + if (ImGui::TreeNodeEx(name.data(), flags)) + return {&ImGui::TreePop}; + else + return {}; +} + +[[nodiscard]] static inline raii_wrapper push_style_var(ImGuiStyleVar_ var, Vector2 value) +{ + ImGui::PushStyleVar(var, {value[0], value[1]}); + return {[]{ ImGui::PopStyleVar(); }}; +} + +[[nodiscard]] static inline raii_wrapper push_style_var(ImGuiStyleVar_ var, float value) +{ + ImGui::PushStyleVar(var, value); + return {[]{ ImGui::PopStyleVar(); }}; +} + +[[nodiscard]] static inline raii_wrapper push_style_color(ImGuiCol_ var, const Color4& value) +{ + ImGui::PushStyleColor(var, {value[0], value[1], value[2], value[3]}); + return {[]{ ImGui::PopStyleColor(); }}; +} + +} // namespace floormat::imgui diff --git a/editor/imgui.cpp b/editor/imgui.cpp new file mode 100644 index 00000000..b0777d5d --- /dev/null +++ b/editor/imgui.cpp @@ -0,0 +1,240 @@ +#include "app.hpp" +#include <Magnum/GL/Renderer.h> +#include "imgui-raii.hpp" +#include <Magnum/ImGuiIntegration/Context.h> + +namespace floormat { + +using namespace floormat::imgui; + +void app::init_imgui(Vector2i size) +{ + if (!_imgui.context()) + _imgui = ImGuiIntegration::Context(Vector2{size}, size, size); + else + _imgui.relayout(Vector2{size}, size, size); +} + +void app::render_menu() +{ + GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add, GL::Renderer::BlendEquation::Add); + GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::SourceAlpha, GL::Renderer::BlendFunction::OneMinusSourceAlpha); + GL::Renderer::enable(GL::Renderer::Feature::Blending); + + GL::Renderer::enable(GL::Renderer::Feature::ScissorTest); + GL::Renderer::disable(GL::Renderer::Feature::FaceCulling); + GL::Renderer::disable(GL::Renderer::Feature::DepthTest); + + _imgui.drawFrame(); +} + +float app::draw_main_menu() +{ + float main_menu_height = 0; + if (auto b = begin_main_menu()) + { + if (auto b = begin_menu("File")) + { + ImGui::MenuItem("Open", "Ctrl+O"); + ImGui::MenuItem("Recent"); + ImGui::Separator(); + ImGui::MenuItem("Save", "Ctrl+S"); + ImGui::MenuItem("Save as...", "Ctrl+Shift+S"); + ImGui::Separator(); + ImGui::MenuItem("Close"); + } + if (auto b = begin_menu("Mode")) + { + ImGui::MenuItem("Select", "F1", _editor.mode() == editor_mode::select); + ImGui::MenuItem("Floor", "F2", _editor.mode() == editor_mode::floor); + ImGui::MenuItem("Walls", "F3", _editor.mode() == editor_mode::walls); + } + + main_menu_height = ImGui::GetContentRegionMax().y; + } + return main_menu_height; +} + +void app::draw_ui() +{ + ImGui::GetIO().IniFilename = nullptr; + _imgui.newFrame(); + ImGui::StyleColorsDark(&ImGui::GetStyle()); + + const float main_menu_height = draw_main_menu(); + draw_editor_pane(_editor.floor(), main_menu_height); + draw_fps(); + draw_cursor_coord(); + ImGui::EndFrame(); +} + +void app::draw_editor_pane(tile_type& type, float main_menu_height) +{ + constexpr + Color4 color_perm_selected{1, 1, 1, .7f}, + color_selected{1, 0.843f, 0, .8f}, + color_hover{0, .8f, 1, .7f}; + + if (ImGui::GetIO().WantTextInput && !isTextInputActive()) + startTextInput(); + else if (!ImGui::GetIO().WantTextInput && isTextInputActive()) + stopTextInput(); + + [[maybe_unused]] const raii_wrapper vars[] = { + push_style_var(ImGuiStyleVar_WindowPadding, {8, 8}), + push_style_var(ImGuiStyleVar_WindowBorderSize, 0), + push_style_var(ImGuiStyleVar_FramePadding, {4, 4}), + push_style_color(ImGuiCol_WindowBg, {0, 0, 0, .5}), + push_style_color(ImGuiCol_FrameBg, {0, 0, 0, 0}), + }; + + const auto& style = ImGui::GetStyle(); + tile_type* const ed = _editor.current(); + + if (main_menu_height > 0) + { + ImGui::SetNextWindowPos({0, main_menu_height+style.WindowPadding.y}); + ImGui::SetNextFrameWantCaptureKeyboard(false); + ImGui::SetNextWindowSize({420, windowSize()[1] - main_menu_height - style.WindowPadding.y}); + if (const auto flags = ImGuiWindowFlags_(ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings); + auto b = begin_window({}, flags)) + { + const float window_width = ImGui::GetWindowWidth() - 32; + + char buf[128]; + //ImGui::SetNextWindowBgAlpha(.2f); + + if (auto b = begin_list_box("##atlases", {-FLT_MIN, -1})) + { + for (const auto& [k, v] : type) + { + ///const auto& k_ = k; + const auto& v_ = v; + const auto click_event = [&] { + if (ed) + { + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) + ed->select_tile_permutation(v_); + else if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) + ed->clear_selection(); + } + }; + const auto do_caption = [&] { + if (ed) + { + click_event(); + if (ed->is_atlas_selected(v)) + { + ImGui::SameLine(); + ImGui::Text(" (selected)"); + } + } + { + snprintf(buf, sizeof(buf), "%zu", (std::size_t)v_->num_tiles()); + ImGui::SameLine(window_width - ImGui::CalcTextSize(buf).x - style.FramePadding.x - 4); + ImGui::Text("%s", buf); + } + }; + const auto N = v->num_tiles(); + if (const auto flags = ImGuiTreeNodeFlags_(ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Framed); + auto b = tree_node(k.data(), flags)) + { + do_caption(); + [[maybe_unused]] const raii_wrapper vars[] = { + push_style_var(ImGuiStyleVar_FramePadding, {2, 2}), + push_style_color(ImGuiCol_ButtonHovered, color_hover), + }; + const bool perm_selected = ed ? ed->is_permutation_selected(v) : false; + constexpr std::size_t per_row = 8; + for (std::size_t i = 0; i < N; i++) + { + const bool selected = ed ? ed->is_tile_selected(v, i) : false; + + if (i > 0 && i % per_row == 0) + ImGui::NewLine(); + + [[maybe_unused]] const raii_wrapper vars[] = { + selected ? push_style_color(ImGuiCol_Button, color_selected) : raii_wrapper{}, + selected ? push_style_color(ImGuiCol_ButtonHovered, color_selected) : raii_wrapper{}, + perm_selected ? push_style_color(ImGuiCol_Button, color_perm_selected) : raii_wrapper{}, + perm_selected ? push_style_color(ImGuiCol_ButtonHovered, color_perm_selected) : raii_wrapper{}, + }; + + snprintf(buf, sizeof(buf), "##item_%zu", i); + const auto uv = v->texcoords_for_id(i); + ImGui::ImageButton(buf, (void*)&v->texture(), {TILE_SIZE[0]/2, TILE_SIZE[1]/2}, + { uv[3][0], uv[3][1] }, { uv[0][0], uv[0][1] }); + if (ed) + { + if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) + ed->select_tile(v, i); + else + click_event(); + } + ImGui::SameLine(); + } + ImGui::NewLine(); + } + else + do_caption(); + } + } + } + } +} + +void app::draw_fps() +{ + auto c1 = push_style_var(ImGuiStyleVar_FramePadding, {0, 0}); + auto c2 = push_style_var(ImGuiStyleVar_WindowPadding, {0, 0}); + auto c3 = push_style_var(ImGuiStyleVar_WindowBorderSize, 0); + auto c4 = push_style_var(ImGuiStyleVar_WindowMinSize, {1, 1}); + auto c5 = push_style_var(ImGuiStyleVar_ScrollbarSize, 0); + auto c6 = push_style_color(ImGuiCol_Text, {0, 1, 0, 1}); + + char buf[16]; + const double hz = _frame_time > 1e-6f ? (int)std::round(10./(double)_frame_time + .05) * .1 : 9999; + snprintf(buf, sizeof(buf), "%.1f FPS", hz); + const ImVec2 size = ImGui::CalcTextSize(buf); + + ImGui::SetNextWindowPos({windowSize()[0] - size.x - 4, 3}); + ImGui::SetNextWindowSize(size); + + if (auto flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground; + auto b = begin_window("framerate", ImGuiWindowFlags_(flags))) + { + ImGui::Text("%s", buf); + } +} + +void app::draw_cursor_coord() +{ + if (!_cursor_tile) + return; + + auto c1 = push_style_var(ImGuiStyleVar_FramePadding, {0, 0}); + auto c2 = push_style_var(ImGuiStyleVar_WindowPadding, {0, 0}); + auto c3 = push_style_var(ImGuiStyleVar_WindowBorderSize, 0); + auto c4 = push_style_var(ImGuiStyleVar_WindowMinSize, {1, 1}); + auto c5 = push_style_var(ImGuiStyleVar_ScrollbarSize, 0); + auto c6 = push_style_color(ImGuiCol_Text, {.9f, .9f, .9f, 1}); + + char buf[64]; + const auto coord = *_cursor_tile; + const auto chunk = coord.chunk(); + const auto local = coord.local(); + snprintf(buf, sizeof(buf), "%hd:%hd - %hhu:%hhu", chunk.x, chunk.y, local.x, local.y); + const auto size = ImGui::CalcTextSize(buf); + + ImGui::SetNextWindowPos({windowSize()[0]/2 - size.x/2, 3}); + ImGui::SetNextWindowSize(size); + if (auto flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoInputs | + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground; + auto b = begin_window("tile-coord", ImGuiWindowFlags_(flags))) + { + ImGui::Text("%s", buf); + } +} + +} // namespace floormat diff --git a/editor/keyboard.cpp b/editor/keyboard.cpp new file mode 100644 index 00000000..a700d0f4 --- /dev/null +++ b/editor/keyboard.cpp @@ -0,0 +1,38 @@ +#include "app.hpp" + +namespace floormat { + +void app::do_key(KeyEvent::Key k, KeyEvent::Modifiers m, bool pressed, bool repeated) +{ + //using Mods = KeyEvent::Modifiers; + + (void)m; + (void)repeated; + + const key x = fm_begin(switch (k) + { + using enum KeyEvent::Key; + using enum key; + + default: return COUNT; + case W: return camera_up; + case A: return camera_left; + case S: return camera_down; + case D: return camera_right; + case Home: return camera_reset; + case R: return rotate_tile; + case F5: return quicksave; + case F9: return quickload; + case Esc: return quit; + }); + + if (x != key::COUNT) + keys[x] = pressed; +} + +app::~app() +{ + loader_::destroy(); +} + +} // namespace floormat diff --git a/editor/update.cpp b/editor/update.cpp new file mode 100644 index 00000000..ebd1881b --- /dev/null +++ b/editor/update.cpp @@ -0,0 +1,55 @@ +#include "app.hpp" + +namespace floormat { + +//#define FM_NO_BINDINGS + +void app::make_test_chunk(chunk& c) +{ + constexpr auto N = TILE_MAX_DIM; + for (auto [x, k, pt] : c) { +#if defined FM_NO_BINDINGS + const auto& atlas = floor1; +#else + const auto& atlas = pt.x != pt.y && (pt.x == N/2 || pt.y == N/2) ? floor2 : floor1; +#endif + x.ground_image = { atlas, k % atlas->num_tiles() }; + } +#ifdef FM_NO_BINDINGS + const auto& wall1 = floor1, wall2 = floor1; +#endif + constexpr auto K = N/2; + c[{K, K }].wall_north = { wall1, 0 }; + c[{K, K }].wall_west = { wall2, 0 }; + c[{K, K+1}].wall_north = { wall1, 0 }; + c[{K+1, K }].wall_west = { wall2, 0 }; +} + +void app::do_mouse_click(const global_coords pos, int button) +{ + if (button == SDL_BUTTON_LEFT) + _editor.on_click(_world, pos); + else + _editor.on_release(); +} + +void app::do_mouse_release(int button) +{ + (void)button; + _editor.on_release(); +} + +void app::do_mouse_move(global_coords pos) +{ + _editor.on_mouse_move(_world, pos); +} + +void app::update(double dt) +{ + do_camera(dt); + draw_ui(); + if (keys[key::quit]) + Platform::Sdl2Application::exit(0); +} + +} // namespace floormat |