From 53873614aeb1dfc9be709dec7047b8d90b89bbde Mon Sep 17 00:00:00 2001 From: scawful Date: Thu, 30 Nov 2023 02:12:34 -0500 Subject: [PATCH] Editor and Gui improvements --- src/app/editor/dungeon_editor.cc | 12 +++- src/app/editor/dungeon_editor.h | 1 + src/app/editor/graphics_editor.cc | 101 +++++++++++++++++++++++------ src/app/editor/graphics_editor.h | 3 +- src/app/gfx/bitmap.h | 2 + src/app/gfx/snes_palette.cc | 3 + src/app/gui/canvas.cc | 102 ++++++++++++++++++++++++++++-- src/app/gui/canvas.h | 8 +++ src/app/gui/widgets.h | 4 ++ 9 files changed, 209 insertions(+), 27 deletions(-) diff --git a/src/app/editor/dungeon_editor.cc b/src/app/editor/dungeon_editor.cc index 20de4319..b3c1a994 100644 --- a/src/app/editor/dungeon_editor.cc +++ b/src/app/editor/dungeon_editor.cc @@ -322,13 +322,14 @@ void DungeonEditor::DrawObjectRenderer() { TableSetupColumn("Canvas"); ImGui::TableNextColumn(); - ImGui::BeginChild("DungeonObjectButtons", ImVec2(0, 0), true); + ImGui::BeginChild("DungeonObjectButtons", ImVec2(250, 0), true); int selected_object = 0; int i = 0; for (const auto object_name : zelda3::dungeon::Type1RoomObjectNames) { if (ImGui::Selectable(object_name.data(), selected_object == i)) { selected_object = i; + current_object_ = i; object_renderer_.LoadObject(i); rom()->RenderBitmap(object_renderer_.bitmap()); object_loaded_ = true; @@ -356,6 +357,15 @@ void DungeonEditor::DrawObjectRenderer() { ImGui::EndTable(); } + + if (object_loaded_) { + ImGui::Begin("Memory Viewer", &object_loaded_, 0); + auto memory = object_renderer_.memory(); + static MemoryEditor mem_edit; + mem_edit.DrawContents((void*)object_renderer_.mutable_memory(), + memory.size()); + ImGui::End(); + } } } // namespace editor diff --git a/src/app/editor/dungeon_editor.h b/src/app/editor/dungeon_editor.h index a27181f0..4408be6c 100644 --- a/src/app/editor/dungeon_editor.h +++ b/src/app/editor/dungeon_editor.h @@ -57,6 +57,7 @@ class DungeonEditor : public Editor, bool palette_showing_ = false; bool refresh_graphics_ = false; + int current_object_ = 0; uint64_t current_palette_id_ = 0; uint64_t current_palette_group_id_ = 0; diff --git a/src/app/editor/graphics_editor.cc b/src/app/editor/graphics_editor.cc index 11cfcd29..05cf9c87 100644 --- a/src/app/editor/graphics_editor.cc +++ b/src/app/editor/graphics_editor.cc @@ -100,7 +100,7 @@ void GraphicsEditor::DrawGfxEditToolset() { ImGui::TableNextColumn(); if (ImGui::Button(ICON_MD_ZOOM_IN)) { - if (current_scale_ <= 8.0f) { + if (current_scale_ <= 16.0f) { current_scale_ += 1.0f; } } @@ -160,7 +160,7 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() { absl::StrFormat("%02X", key).c_str()); } }, - ImVec2(0x100 + 1, 0x40 + 1), 0x20, /*scale=*/1.0f, + ImVec2(0x100 + 1, 0x40 + 1), 0x20, sheet_scale_, /*grid_size=*/16.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::EndChild(); @@ -179,41 +179,79 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_TabListPopupButton)) { if (ImGui::TabItemButton( - "+", ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | - ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | - ImGuiTableFlags_BordersV)) { + "+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) { open_sheets_.insert(next_tab_id++); } // Submit our regular tabs for (auto& each : open_sheets_) { bool open = true; - if (ImGui::BeginTabItem(absl::StrFormat("%d", each).c_str(), &open, ImGuiTabItemFlags_None)) { + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + release_queue_.push(each); + } + if (ImGui::IsItemHovered()) { + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left)) { + release_queue_.push(each); + child_window_sheets_.insert(each); + } + } ImGui::BeginChild( absl::StrFormat("##GfxEditPaletteChild%d", each).c_str(), ImVec2(0, 0), true, ImGuiWindowFlags_NoDecoration | - ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGuiWindowFlags_AlwaysVerticalScrollbar | + ImGuiWindowFlags_AlwaysHorizontalScrollbar); current_sheet_ = each; current_sheet_canvas_.UpdateColorPainter( *rom()->bitmap_manager()[each], current_color_, [&]() { - int position = - current_sheet_canvas_.GetCurrentDrawnTilePosition().x + - (current_sheet_canvas_.GetCurrentDrawnTilePosition().y * - 0x20 * 8); - gfx::SNESColor color(current_color_); - rom()->bitmap_manager()[each]->WriteWordToPixel(position, - color.GetSNES()); + // Convert the ImVec4 into a 16-bit value + Uint8 r = static_cast(current_color_.x * 31); + Uint8 g = static_cast(current_color_.y * 31); + Uint8 b = static_cast(current_color_.z * 31); + Uint16 snesColor = + ((r & 0x1F) << 10) | ((g & 0x1F) << 5) | (b & 0x1F); + + auto click_position = + current_sheet_canvas_.GetCurrentDrawnTilePosition(); + + // Calculate the tile index for x and y based on the + // click_position + int tile_index_x = + (static_cast(click_position.x) % 8) / tile_size_; + int tile_index_y = + (static_cast(click_position.y) % 8) / tile_size_; + + // Calculate the pixel start position based on tile index and tile + // size + ImVec2 start_position; + start_position.x = tile_index_x * tile_size_; + start_position.y = tile_index_y * tile_size_; + + // Get the current map's bitmap from the BitmapTable + gfx::Bitmap& current_bitmap = *rom()->bitmap_manager()[each]; + + // Update the bitmap's pixel data based on the start_position and + // tile_data + for (int y = 0; y < tile_size_; ++y) { + for (int x = 0; x < tile_size_; ++x) { + int pixel_index = + (start_position.y + y) * current_bitmap.width() + + (start_position.x + x); + current_bitmap.WriteToPixel(pixel_index, snesColor); + } + } + + // rom()->bitmap_manager()[each]->WriteToPixel(position, + // snesColor); + rom()->UpdateBitmap( rom()->mutable_bitmap_manager()->mutable_bitmap(each).get()); }, - ImVec2(0x100 + 1, 0x40 + 1), tile_size_ * current_scale_, - current_scale_, 16.0f); + ImVec2(0x100, 0x40), tile_size_, current_scale_, 8.0f); ImGui::EndChild(); - ImGui::EndTabItem(); } @@ -222,12 +260,39 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { ImGui::EndTabBar(); } - ImGui::Separator(); while (!release_queue_.empty()) { auto each = release_queue_.top(); open_sheets_.erase(each); release_queue_.pop(); } + + if (!child_window_sheets_.empty()) { + int id_to_release = -1; + for (const auto& id : child_window_sheets_) { + bool active = true; + ImGui::SetNextWindowPos(ImGui::GetIO().MousePos, ImGuiCond_Once); + ImGui::SetNextWindowSize(ImVec2(0x100 + 1 * 16, 0x40 + 1 * 16), + ImGuiCond_Once); + ImGui::Begin(absl::StrFormat("##GfxEditPaletteChildWindow%d", id).c_str(), + &active, ImGuiWindowFlags_AlwaysUseWindowPadding); + current_sheet_ = id; + current_sheet_canvas_.UpdateColorPainter( + *rom()->bitmap_manager()[id], current_color_, + [&]() { + + }, + ImVec2(0x100, 0x40), tile_size_, current_scale_, 8.0f); + ImGui::End(); + + if (active == false) { + id_to_release = id; + } + } + if (id_to_release != -1) { + child_window_sheets_.erase(id_to_release); + } + } + return absl::OkStatus(); } diff --git a/src/app/editor/graphics_editor.h b/src/app/editor/graphics_editor.h index bd72e446..22ce4caf 100644 --- a/src/app/editor/graphics_editor.h +++ b/src/app/editor/graphics_editor.h @@ -99,8 +99,9 @@ class GraphicsEditor : public SharedROM { ImVec4 current_color_; uint16_t current_sheet_ = 0; - uint8_t tile_size_ = 0x04; + uint8_t tile_size_ = 0x08; std::set open_sheets_; + std::set child_window_sheets_; std::stack release_queue_; uint64_t edit_palette_group_name_index_ = 0; uint64_t edit_palette_group_index_ = 0; diff --git a/src/app/gfx/bitmap.h b/src/app/gfx/bitmap.h index 921f8926..139c4a3c 100644 --- a/src/app/gfx/bitmap.h +++ b/src/app/gfx/bitmap.h @@ -107,6 +107,8 @@ class Bitmap { auto data() const { return data_.data(); } auto mutable_data() { return data_; } auto mutable_pixel_data() { return pixel_data_; } + auto surface() const { return surface_.get(); } + auto mutable_surface() { return surface_.get(); } auto vector() const { return data_; } auto at(int i) const { return data_[i]; } diff --git a/src/app/gfx/snes_palette.cc b/src/app/gfx/snes_palette.cc index 293ca12c..d54289d0 100644 --- a/src/app/gfx/snes_palette.cc +++ b/src/app/gfx/snes_palette.cc @@ -214,6 +214,9 @@ SNESPalette ReadPaletteFromROM(int offset, int num_colors, const uchar* rom) { new_color.green = ((color >> 5) & 0x1F) * 8; new_color.blue = ((color >> 10) & 0x1F) * 8; colors[color_offset].SetSNES(ConvertRGBtoSNES(new_color)); + if (color_offset == 0) { + colors[color_offset].SetTransparent(true); + } color_offset++; offset += 2; } diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index fdf4e9a8..201bd90f 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -35,6 +35,7 @@ void Canvas::UpdateColorPainter(const gfx::Bitmap &bitmap, const ImVec4 &color, const std::function &event, ImVec2 bg_size, int tile_size, float scale, float grid_size) { + global_scale_ = scale; if (scale != 1.0f) { bg_size.x *= scale / 2; bg_size.y *= scale / 2; @@ -92,6 +93,19 @@ void Canvas::DrawContextMenu() { // Contents of the Context Menu if (ImGui::BeginPopup("context")) { ImGui::MenuItem("Show Grid", nullptr, &enable_grid_); + ImGui::Selectable("Show Labels", &enable_hex_tile_labels_); + if (ImGui::MenuItem("8x8", nullptr, custom_step_ == 8.0f)) { + custom_step_ = 8.0f; + } + if (ImGui::MenuItem("16x16", nullptr, custom_step_ == 16.0f)) { + custom_step_ = 16.0f; + } + if (ImGui::MenuItem("32x32", nullptr, custom_step_ == 32.0f)) { + custom_step_ = 32.0f; + } + if (ImGui::MenuItem("64x64", nullptr, custom_step_ == 64.0f)) { + custom_step_ = 64.0f; + } if (ImGui::MenuItem("Reset Position", nullptr, false)) { scrolling_.x = 0; scrolling_.y = 0; @@ -134,7 +148,7 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { // Draw the currently selected tile on the overworld here // Save the coordinates of the selected tile. - drawn_tile_pos_ = mouse_pos; + drawn_tile_pos_ = io.MousePos; return true; } @@ -175,7 +189,7 @@ bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int size) { IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255)); if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { - drawn_tile_pos_ = painter_pos; + drawn_tile_pos_ = mouse_pos; return true; } @@ -188,9 +202,8 @@ bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int size) { void Canvas::DrawTileSelector(int size) { const ImGuiIO &io = ImGui::GetIO(); - const bool is_hovered = ImGui::IsItemHovered(); // Hovered - const ImVec2 origin(canvas_p0_.x + scrolling_.x, - canvas_p0_.y + scrolling_.y); // Lock scrolled origin + const bool is_hovered = ImGui::IsItemHovered(); + const ImVec2 origin(canvas_p0_.x + scrolling_.x, canvas_p0_.y + scrolling_.y); const ImVec2 mouse_pos(io.MousePos.x - origin.x, io.MousePos.y - origin.y); if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { @@ -300,6 +313,62 @@ void Canvas::DrawOutline(int x, int y, int w, int h) { draw_list_->AddRect(origin, size, IM_COL32(255, 255, 255, 255)); } +void Canvas::DrawSelectRect(int tile_size, float scale) { + const ImGuiIO &io = ImGui::GetIO(); + static ImVec2 drag_start_pos; + static bool dragging = false; + + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + if (!points_.empty()) { + points_.clear(); + } + // Snap the start position to the nearest grid point with scaling + // consideration + drag_start_pos.x = + std::floor(io.MousePos.x / (tile_size * scale)) * tile_size * scale; + drag_start_pos.y = + std::floor(io.MousePos.y / (tile_size * scale)) * tile_size * scale; + dragging = true; + } + + if (dragging) { + ImVec2 current_pos = io.MousePos; + ImVec2 grid_pos; + grid_pos.x = + std::floor(current_pos.x / (tile_size * scale)) * tile_size * scale; + grid_pos.y = + std::floor(current_pos.y / (tile_size * scale)) * tile_size * scale; + + // Calculate rect_min and rect_max considering the drag direction + ImVec2 rect_min, rect_max; + rect_min.x = + (grid_pos.x < drag_start_pos.x) ? grid_pos.x : drag_start_pos.x; + rect_min.y = + (grid_pos.y < drag_start_pos.y) ? grid_pos.y : drag_start_pos.y; + rect_max.x = (grid_pos.x >= drag_start_pos.x) + ? grid_pos.x + tile_size * scale + : drag_start_pos.x + tile_size * scale; + rect_max.y = (grid_pos.y >= drag_start_pos.y) + ? grid_pos.y + tile_size * scale + : drag_start_pos.y + tile_size * scale; + + draw_list_->AddRect(rect_min, rect_max, kRectangleBorder); + + if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) { + dragging = false; + // Convert the coordinates to scale-independent form + ImVec2 scaled_rect_min, scaled_rect_max; + scaled_rect_min.x = rect_min.x * scale; + scaled_rect_min.y = rect_min.y * scale; + scaled_rect_max.x = rect_max.x * scale; + scaled_rect_max.y = rect_max.y * scale; + + points_.push_back(scaled_rect_min); + points_.push_back(scaled_rect_max); + } + } +} + void Canvas::DrawRect(int x, int y, int w, int h, ImVec4 color) { ImVec2 origin(canvas_p0_.x + scrolling_.x + x, canvas_p0_.y + scrolling_.y + y); @@ -319,16 +388,35 @@ void Canvas::DrawGrid(float grid_step) { // Draw grid + all lines in the canvas draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true); if (enable_grid_) { + if (custom_step_ != 0.f) grid_step = custom_step_; + grid_step *= global_scale_; // Apply global scale to grid step for (float x = fmodf(scrolling_.x, grid_step); x < canvas_sz_.x; x += grid_step) draw_list_->AddLine(ImVec2(canvas_p0_.x + x, canvas_p0_.y), ImVec2(canvas_p0_.x + x, canvas_p1_.y), - IM_COL32(200, 200, 200, 40)); + IM_COL32(200, 200, 200, 50), 0.5f); for (float y = fmodf(scrolling_.y, grid_step); y < canvas_sz_.y; y += grid_step) draw_list_->AddLine(ImVec2(canvas_p0_.x, canvas_p0_.y + y), ImVec2(canvas_p1_.x, canvas_p0_.y + y), - IM_COL32(200, 200, 200, 40)); + IM_COL32(200, 200, 200, 50), 0.5f); + + if (enable_hex_tile_labels_) { + // Draw the hex ID of the tile in the center of the tile square + for (float x = fmodf(scrolling_.x, grid_step); x < canvas_sz_.x; + x += grid_step) { + for (float y = fmodf(scrolling_.y, grid_step); y < canvas_sz_.y; + y += grid_step) { + int tile_x = (x - scrolling_.x) / grid_step; + int tile_y = (y - scrolling_.y) / grid_step; + int tile_id = tile_x + (tile_y * 16); + std::string hex_id = absl::StrFormat("%02X", tile_id); + draw_list_->AddText(ImVec2(canvas_p0_.x + x + (grid_step / 2) - 4, + canvas_p0_.y + y + (grid_step / 2) - 4), + IM_COL32(255, 255, 255, 255), hex_id.data()); + } + } + } } } diff --git a/src/app/gui/canvas.h b/src/app/gui/canvas.h index 077687e9..e7c5a26a 100644 --- a/src/app/gui/canvas.h +++ b/src/app/gui/canvas.h @@ -68,6 +68,7 @@ class Canvas { void DrawBitmapTable(const BitmapTable& gfx_bin); void DrawOutline(int x, int y, int w, int h); + void DrawSelectRect(int tile_size, float scale = 1.0f); void DrawRect(int x, int y, int w, int h, ImVec4 color); void DrawText(std::string text, int x, int y); void DrawGrid(float grid_step = 64.0f); @@ -85,12 +86,19 @@ class Canvas { } auto IsMouseHovering() const { return is_hovered_; } + void set_global_scale(float scale) { global_scale_ = scale; } + auto global_scale() const { return global_scale_; } + private: bool enable_grid_ = true; + bool enable_hex_tile_labels_ = false; bool enable_context_menu_ = true; bool custom_canvas_size_ = false; bool is_hovered_ = false; + float custom_step_ = 8.0f; + float global_scale_ = 1.0f; + ImDrawList* draw_list_; ImVector points_; ImVec2 scrolling_; diff --git a/src/app/gui/widgets.h b/src/app/gui/widgets.h index 6f963174..e2e21d49 100644 --- a/src/app/gui/widgets.h +++ b/src/app/gui/widgets.h @@ -16,6 +16,10 @@ namespace yaze { namespace app { namespace gui { +class DynamicLayout { + +}; + TextEditor::LanguageDefinition GetAssemblyLanguageDef(); void RenderTabItem(const std::string& title,