From 15a58dd66de9aeb2bc8466795899c143dbdb66e6 Mon Sep 17 00:00:00 2001 From: caiowakamatsu Date: Sat, 11 Apr 2026 08:24:32 -0300 Subject: [PATCH] implement basic level editing --- source/game/components.hpp | 4 +- source/game/engine.cpp | 88 +++++++++++++++++++++++++++++++++----- source/game/engine.hpp | 8 ++++ source/game/level.cpp | 14 ++++++ source/game/level.hpp | 2 + 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/source/game/components.hpp b/source/game/components.hpp index f4e959c..c30ae0d 100644 --- a/source/game/components.hpp +++ b/source/game/components.hpp @@ -10,8 +10,8 @@ struct position final : strong::alias { using strong::alias::alias; }; -struct tile_position final : strong::alias { - using strong::alias::alias; +struct tile_position final : strong::alias { + using strong::alias::alias; }; struct render_id : strong::alias { diff --git a/source/game/engine.cpp b/source/game/engine.cpp index e85ce0b..f1ead91 100644 --- a/source/game/engine.cpp +++ b/source/game/engine.cpp @@ -290,6 +290,50 @@ engine::add_single_tile_to_render_request(render::request request, return request.add_lines(lines, glm::vec3(1.0f, 1.0f, 0.0f)); } + +std::optional +engine::calculate_intersected_tile(glm::mat4 view, glm::mat4 projection, + glm::vec2 mouse) const { + const float width = static_cast(display.extent().x); + const float height = static_cast(display.extent().y); + + float x = (2.0f * mouse.x) / width - 1.0f; + float y = 1.0f - (2.0f * mouse.y) / height; + + glm::vec4 near_clip = glm::vec4(x, y, -1.0f, 1.0f); + glm::vec4 far_clip = glm::vec4(x, y, 1.0f, 1.0f); + + glm::vec4 near_world = glm::inverse(projection * view) * near_clip; + glm::vec4 far_world = glm::inverse(projection * view) * far_clip; + + near_world /= near_world.w; + far_world /= far_world.w; + + glm::vec3 ray_world = glm::normalize(glm::vec3(far_world - near_world)); + glm::vec3 cam_pos = glm::vec3(near_world); + + if (std::abs(ray_world.y) < 1e-5f) { + return std::nullopt; + } + + float t = -cam_pos.y / ray_world.y; + glm::vec3 hit = cam_pos + ray_world * t; + + const auto full_dimensions = area_size * 2.0f; + const float cell_width = full_dimensions.x / 22.0f; + const float cell_depth = full_dimensions.z / 16.0f; + + const auto start = area_origin - area_size; + + int tile_x = static_cast((hit.x - start.x) / cell_width); + int tile_y = static_cast((hit.z - start.z) / cell_depth); + + tile_x = std::clamp(tile_x, 0, 21); + tile_y = std::clamp(tile_y, 0, 15); + + return glm::uvec2(tile_x, tile_y); +} + engine::mesh_bounds engine::compute_size_of_mesh(const mesh_node &mesh) { auto bounds = mesh_bounds(); @@ -441,6 +485,28 @@ void engine::do_level_editor_ui() { ImGui::Checkbox("Enabled", &is_editing_level); + static int wall_type = 0; + ImGui::Combo("Wall Type", &wall_type, "cork\0solid\0\0"); + level_editing_data.type_to_write = + static_cast(wall_type); + + static int wall_height = 0; + ImGui::Combo("Wall Height", &wall_height, "half\0full\0\0"); + level_editing_data.height_to_write = + static_cast(wall_height); + + if (level_editing_data.intersected_tile.has_value()) { + // Todo: Check for validity based on same types + if (ImGui::Button("Add Wall To Tile")) { + auto entity = current_level->get_or_create_entity_at( + *level_editing_data.intersected_tile); + auto &wall = current_level->get_entities() + ->get_or_emplace(entity); + wall.type = level_editing_data.type_to_write; + wall.heights[wall.wall_count++] = level_editing_data.height_to_write; + } + } + ImGui::End(); } @@ -479,12 +545,6 @@ void engine::run() { auto request = render::request(); - if (!ImGui::GetIO().WantCaptureMouse) { - const auto mx = ImGui::GetIO().MousePos; - std::println("mouse {}, {}", mx.x, mx.y); - // request = request.add_circle({mx.x, mx.y}, 1.0f); - } - request = add_level_bounds_to_render_request(request); if (current_level.has_value()) { request = add_level_to_render_request(request, &(*current_level)); @@ -498,10 +558,18 @@ void engine::run() { request = add_tiles_to_render_request(request); } - level_editing_data.intersected_tile = glm::uvec2(5, 5); - if (level_editing_data.intersected_tile.has_value()) { - request = add_single_tile_to_render_request( - request, *level_editing_data.intersected_tile); + if (debug_drawing.level_editor) { + if (!ImGui::GetIO().WantCaptureMouse) { + const auto mx = ImGui::GetIO().MousePos; + if (ImGui::GetIO().MouseDown[0]) { + level_editing_data.intersected_tile = + calculate_intersected_tile(view, projection, {mx.x, mx.y}); + } + if (level_editing_data.intersected_tile.has_value()) { + request = add_single_tile_to_render_request( + request, *level_editing_data.intersected_tile); + } + } } renderer.render(request, view, projection); diff --git a/source/game/engine.hpp b/source/game/engine.hpp index 69cac23..2a4f113 100644 --- a/source/game/engine.hpp +++ b/source/game/engine.hpp @@ -1,6 +1,7 @@ #ifndef TANKS_ENGINE_HPP #define TANKS_ENGINE_HPP +#include "game/components.hpp" #include #include @@ -47,6 +48,9 @@ private: std::optional current_level = level(); struct { std::optional intersected_tile; + + components::wall_height height_to_write; + components::wall_type type_to_write; } level_editing_data; bool is_editing_level = false; struct { @@ -81,6 +85,10 @@ private: add_single_tile_to_render_request(render::request request, glm::uvec2 tile) const; + [[nodiscard]] std::optional + calculate_intersected_tile(glm::mat4 view, glm::mat4 projection, + glm::vec2 mouse) const; + struct mesh_bounds { glm::vec3 min; glm::vec3 max; diff --git a/source/game/level.cpp b/source/game/level.cpp index dd1db2a..a9d0b42 100644 --- a/source/game/level.cpp +++ b/source/game/level.cpp @@ -44,4 +44,18 @@ entt::basic_registry *level::get_entities() { return &entities; } glm::uvec2 level::get_bounds() const noexcept { return {WIDTH, HEIGHT}; } +std::uint32_t level::get_or_create_entity_at(glm::uvec2 tile) { + for (const auto [entity, e_tile] : + entities.view()->each()) { + if (static_cast(e_tile) == tile) { + return entity; + } + } + + auto created = entities.create(); + entities.emplace(created, tile); + + return created; +} + } // namespace trb::game diff --git a/source/game/level.hpp b/source/game/level.hpp index 02716c1..bafbe23 100644 --- a/source/game/level.hpp +++ b/source/game/level.hpp @@ -22,6 +22,8 @@ public: [[nodiscard]] glm::uvec2 get_bounds() const noexcept; [[nodiscard]] entt::basic_registry *get_entities(); + + [[nodiscard]] std::uint32_t get_or_create_entity_at(glm::uvec2 tile); }; } // namespace trb::game