From 0bf45c86a923193ada9165cc70f2b2a138ae42a6 Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 26 Nov 2023 23:12:04 -0500 Subject: [PATCH] Update GraphicsEditor, fix CommandHandler --- src/app/editor/graphics_editor.cc | 116 +++++++++++++++++++++++------- src/app/editor/graphics_editor.h | 9 ++- src/app/gfx/bitmap.cc | 22 ++++++ src/app/gfx/bitmap.h | 21 ++++-- src/app/gui/canvas.cc | 75 ++++++++++++++++++- src/app/gui/canvas.h | 9 ++- src/app/gui/input.cc | 5 +- src/app/gui/input.h | 2 +- src/app/gui/pipeline.cc | 6 -- src/app/rom.h | 4 +- src/cli/command_handler.h | 2 +- 11 files changed, 224 insertions(+), 47 deletions(-) diff --git a/src/app/editor/graphics_editor.cc b/src/app/editor/graphics_editor.cc index 73a317ab..11cfcd29 100644 --- a/src/app/editor/graphics_editor.cc +++ b/src/app/editor/graphics_editor.cc @@ -53,18 +53,17 @@ absl::Status GraphicsEditor::UpdateGfxEdit() { ImGui::TableHeadersRow(); NEXT_COLUMN(); - UpdateGfxSheetList(); + status_ = UpdateGfxSheetList(); - NEXT_COLUMN() { - if (rom()->isLoaded()) { - status_ = UpdateGfxTabView(); - } + NEXT_COLUMN(); + if (rom()->isLoaded()) { + DrawGfxEditToolset(); + status_ = UpdateGfxTabView(); } - NEXT_COLUMN() { - if (rom()->isLoaded()) { - status_ = UpdatePaletteColumn(); - } + NEXT_COLUMN(); + if (rom()->isLoaded()) { + status_ = UpdatePaletteColumn(); } } ImGui::EndTable(); @@ -73,6 +72,52 @@ absl::Status GraphicsEditor::UpdateGfxEdit() { return absl::OkStatus(); } +void GraphicsEditor::DrawGfxEditToolset() { + if (ImGui::BeginTable("##GfxEditToolset", 7, ImGuiTableFlags_SizingFixedFit, + ImVec2(0, 0))) { + for (const auto& name : {"Select", "Pencil", "Fill", "Zoom Out", "Zoom In", + "Current Color", "Tile Size"}) + ImGui::TableSetupColumn(name); + + ImGui::TableNextColumn(); + if (ImGui::Button(ICON_MD_SELECT_ALL)) { + } + + ImGui::TableNextColumn(); + if (ImGui::Button(ICON_MD_DRAW)) { + } + + ImGui::TableNextColumn(); + if (ImGui::Button(ICON_MD_FORMAT_COLOR_FILL)) { + } + + ImGui::TableNextColumn(); + if (ImGui::Button(ICON_MD_ZOOM_OUT)) { + if (current_scale_ >= 0.0f) { + current_scale_ -= 1.0f; + } + } + + ImGui::TableNextColumn(); + if (ImGui::Button(ICON_MD_ZOOM_IN)) { + if (current_scale_ <= 8.0f) { + current_scale_ += 1.0f; + } + } + + ImGui::TableNextColumn(); + ImGui::ColorEdit4("Palette Color", (float*)¤t_color_, + ImGuiColorEditFlags_NoInputs | + ImGuiColorEditFlags_NoLabel | + ImGuiColorEditFlags_NoAlpha); + + ImGui::TableNextColumn(); + gui::InputHexByte("Tile Size", &tile_size_, 0x02); + + ImGui::EndTable(); + } +} + absl::Status GraphicsEditor::UpdateGfxSheetList() { ImGui::BeginChild( "##GfxEditChild", ImVec2(0, 0), true, @@ -115,7 +160,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() { absl::StrFormat("%02X", key).c_str()); } }, - ImVec2(0x100 + 1, 0x40 + 1), 0x20, 16.0f); + ImVec2(0x100 + 1, 0x40 + 1), 0x20, /*scale=*/1.0f, + /*grid_size=*/16.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); ImGui::EndChild(); } @@ -132,12 +178,11 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_TabListPopupButton)) { - // TODO: Manage the room that is being added to the tab bar. if (ImGui::TabItemButton( "+", ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV)) { - open_sheets_.insert(next_tab_id++); // Add new tab + open_sheets_.insert(next_tab_id++); } // Submit our regular tabs @@ -151,9 +196,22 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { ImVec2(0, 0), true, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysVerticalScrollbar); - current_sheet_canvas_.Update(*rom()->bitmap_manager()[each], - ImVec2(0x100 + 1, 0x40 + 1), 0x20, 4.0f, - 16.0f); + 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()); + rom()->UpdateBitmap( + rom()->mutable_bitmap_manager()->mutable_bitmap(each).get()); + }, + ImVec2(0x100 + 1, 0x40 + 1), tile_size_ * current_scale_, + current_scale_, 16.0f); ImGui::EndChild(); ImGui::EndTabItem(); @@ -174,21 +232,32 @@ absl::Status GraphicsEditor::UpdateGfxTabView() { } absl::Status GraphicsEditor::UpdatePaletteColumn() { + auto palette_group = rom()->GetPaletteGroup( + kPaletteGroupAddressesKeys[edit_palette_group_name_index_]); + + auto palette = palette_group[edit_palette_index_]; + if (rom()->isLoaded()) { gui::TextWithSeparators("ROM Palette"); - ImGui::Combo("Palette", (int*)&edit_palette_group_, + ImGui::SetNextItemWidth(100.f); + ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_, kPaletteGroupAddressesKeys, IM_ARRAYSIZE(kPaletteGroupAddressesKeys)); - gui::InputHex("Palette Index", &edit_palette_index_); + ImGui::SetNextItemWidth(100.f); + gui::InputHex("Palette Group Index", &edit_palette_index_); } - auto palette_group = rom()->mutable_palette_group( - kPaletteGroupAddressesKeys[edit_palette_group_]) - [edit_palette_group_index_]; - auto palette = palette_group[edit_palette_index_]; - gui::SelectablePalettePipeline(edit_palette_index_, refresh_graphics_, + gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_, palette); + if (refresh_graphics_) { + rom()->bitmap_manager()[current_sheet_]->ApplyPaletteWithTransparent( + palette, edit_palette_sub_index_); + rom()->UpdateBitmap( + rom()->mutable_bitmap_manager()->mutable_bitmap(current_sheet_).get()); + refresh_graphics_ = false; + } + return absl::OkStatus(); } @@ -202,11 +271,8 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() { RETURN_IF_ERROR(rom()->LoadLinkGraphics()); // Split it into the pose data frames - // Create an animation step display for the poses - // Allow the user to modify the frames used in an anim step - // LinkOAM_AnimationSteps: // #_0D85FB diff --git a/src/app/editor/graphics_editor.h b/src/app/editor/graphics_editor.h index dfa5f988..bd72e446 100644 --- a/src/app/editor/graphics_editor.h +++ b/src/app/editor/graphics_editor.h @@ -67,9 +67,9 @@ class GraphicsEditor : public SharedROM { absl::Status Update(); private: - // Graphics Editor Tab absl::Status UpdateGfxEdit(); + void DrawGfxEditToolset(); absl::Status UpdateGfxSheetList(); absl::Status UpdateGfxTabView(); absl::Status UpdatePaletteColumn(); @@ -97,14 +97,19 @@ class GraphicsEditor : public SharedROM { absl::Status DecompressSuperDonkey(); + ImVec4 current_color_; uint16_t current_sheet_ = 0; + uint8_t tile_size_ = 0x04; std::set open_sheets_; std::stack release_queue_; - uint64_t edit_palette_group_ = 0; + uint64_t edit_palette_group_name_index_ = 0; uint64_t edit_palette_group_index_ = 0; uint64_t edit_palette_index_ = 0; + uint64_t edit_palette_sub_index_ = 0; float sheet_scale_ = 2.0f; + float current_scale_ = 4.0f; + // Prototype Graphics Viewer int current_palette_ = 0; uint64_t current_offset_ = 0; uint64_t current_size_ = 0; diff --git a/src/app/gfx/bitmap.cc b/src/app/gfx/bitmap.cc index 058c3c6e..c254817e 100644 --- a/src/app/gfx/bitmap.cc +++ b/src/app/gfx/bitmap.cc @@ -163,6 +163,28 @@ void Bitmap::ApplyPalette(const SNESPalette &palette) { SDL_LockSurface(surface_.get()); } +void Bitmap::ApplyPaletteWithTransparent(const SNESPalette &palette, + int index) { + palette_ = palette; + auto start_index = index * 7; + std::vector colors; + colors.push_back(ImVec4(0, 0, 0, 0)); + for (int i = start_index; i < start_index + 7; ++i) { + colors.push_back(palette.GetColor(i).GetRGB()); + } + + SDL_UnlockSurface(surface_.get()); + int i = 0; + for (auto &each : colors) { + surface_->format->palette->colors[i].r = each.x; + surface_->format->palette->colors[i].g = each.y; + surface_->format->palette->colors[i].b = each.z; + surface_->format->palette->colors[i].a = each.w; + i++; + } + SDL_LockSurface(surface_.get()); +} + void Bitmap::ApplyPalette(const std::vector &palette) { SDL_UnlockSurface(surface_.get()); for (int i = 0; i < palette.size(); ++i) { diff --git a/src/app/gfx/bitmap.h b/src/app/gfx/bitmap.h index 88bf8bf3..921f8926 100644 --- a/src/app/gfx/bitmap.h +++ b/src/app/gfx/bitmap.h @@ -24,7 +24,6 @@ class Bitmap { Bitmap(int width, int height, int depth, int data_size); Bitmap(int width, int height, int depth, const Bytes &data) : width_(width), height_(height), depth_(depth), data_(data) { - // CreateTextureFromData(); InitializeFromData(width, height, depth, data); } @@ -42,20 +41,21 @@ class Bitmap { void Create(int width, int height, int depth, int data_size); void Create(int width, int height, int depth, const Bytes &data); - void InitializeFromData(uint32_t width, uint32_t height, - uint32_t depth, const Bytes &data); + void InitializeFromData(uint32_t width, uint32_t height, uint32_t depth, + const Bytes &data); void ReserveData(uint32_t width, uint32_t height, uint32_t depth, uint32_t size); void CreateTexture(std::shared_ptr renderer); void UpdateTexture(std::shared_ptr renderer); - void CreateTexture(SDL_Renderer* renderer); - void UpdateTexture(SDL_Renderer* renderer); + void CreateTexture(SDL_Renderer *renderer); + void UpdateTexture(SDL_Renderer *renderer); void SaveSurfaceToFile(std::string_view filename); void SetSurface(SDL_Surface *surface); void ApplyPalette(const SNESPalette &palette); + void ApplyPaletteWithTransparent(const SNESPalette &palette, int index); void ApplyPalette(const std::vector &palette); void WriteToPixel(int position, uchar value) { @@ -66,6 +66,15 @@ class Bitmap { modified_ = true; } + void WriteWordToPixel(int position, uint16_t value) { + if (pixel_data_ == nullptr) { + pixel_data_ = data_.data(); + } + this->pixel_data_[position] = value & 0xFF; + this->pixel_data_[position + 1] = (value >> 8) & 0xFF; + modified_ = true; + } + void Cleanup() { // Reset texture_ if (texture_) { @@ -171,6 +180,8 @@ class BitmapManager { return nullptr; } + auto mutable_bitmap(int id) { return bitmap_cache_[id]; } + using value_type = std::pair>; using iterator = std::unordered_map>::iterator; diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index b8a3b529..fdf4e9a8 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -19,16 +19,38 @@ constexpr ImGuiButtonFlags kMouseFlags = void Canvas::Update(const gfx::Bitmap &bitmap, ImVec2 bg_size, int tile_size, float scale, float grid_size) { + if (scale != 1.0f) { + bg_size.x *= scale / 2; + bg_size.y *= scale / 2; + } DrawBackground(bg_size); DrawContextMenu(); DrawTileSelector(tile_size); - DrawBitmap(bitmap, 0, 0, scale); + DrawBitmap(bitmap, 2, scale); + DrawGrid(grid_size); + DrawOverlay(); +} + +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) { + if (scale != 1.0f) { + bg_size.x *= scale / 2; + bg_size.y *= scale / 2; + } + DrawBackground(bg_size); + DrawContextMenu(); + DrawBitmap(bitmap, 2, scale); + if (DrawSolidTilePainter(color, tile_size)) { + event(); + } DrawGrid(grid_size); DrawOverlay(); } void Canvas::UpdateEvent(const std::function &event, ImVec2 bg_size, - int tile_size, float grid_size) { + int tile_size, float scale, float grid_size) { DrawBackground(bg_size); DrawContextMenu(); event(); @@ -123,6 +145,47 @@ bool Canvas::DrawTilePainter(const Bitmap &bitmap, int size, float scale) { return false; } +bool Canvas::DrawSolidTilePainter(const ImVec4 &color, int size) { + const ImGuiIO &io = ImGui::GetIO(); + const bool is_hovered = ImGui::IsItemHovered(); + is_hovered_ = is_hovered; + // Lock scrolled origin + 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) { + // Reset the previous tile hover + if (!points_.empty()) { + points_.clear(); + } + + // Calculate the coordinates of the mouse + ImVec2 painter_pos; + painter_pos.x = std::floor((double)mouse_pos.x / size) * size; + painter_pos.y = std::floor((double)mouse_pos.y / size) * size; + + auto painter_pos_end = ImVec2(painter_pos.x + size, painter_pos.y + size); + points_.push_back(painter_pos); + points_.push_back(painter_pos_end); + + draw_list_->AddRectFilled( + ImVec2(origin.x + painter_pos.x, origin.y + painter_pos.y), + ImVec2(origin.x + painter_pos.x + size, + origin.y + painter_pos.y + size), + IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255)); + + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + drawn_tile_pos_ = painter_pos; + return true; + } + + } else { + // Erase the hover when the mouse is not in the canvas window. + points_.clear(); + } + return false; +} + void Canvas::DrawTileSelector(int size) { const ImGuiIO &io = ImGui::GetIO(); const bool is_hovered = ImGui::IsItemHovered(); // Hovered @@ -196,6 +259,14 @@ void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, bool ready) { } } +void Canvas::DrawBitmap(const Bitmap &bitmap, int border_offset, float scale) { + draw_list_->AddImage( + (void *)bitmap.texture(), + ImVec2(canvas_p0_.x + border_offset, canvas_p0_.y + border_offset), + ImVec2(canvas_p0_.x + (bitmap.width() * scale), + canvas_p0_.y + (bitmap.height() * scale))); +} + void Canvas::DrawBitmap(const Bitmap &bitmap, int x_offset, int y_offset, float scale) { draw_list_->AddImage( diff --git a/src/app/gui/canvas.h b/src/app/gui/canvas.h index e263d0c4..077687e9 100644 --- a/src/app/gui/canvas.h +++ b/src/app/gui/canvas.h @@ -25,8 +25,13 @@ class Canvas { void Update(const gfx::Bitmap& bitmap, ImVec2 bg_size, int tile_size, float scale = 1.0f, float grid_size = 64.0f); + void UpdateColorPainter(const gfx::Bitmap& bitmap, const ImVec4& color, + const std::function& event, ImVec2 bg_size, + int tile_size, float scale = 1.0f, + float grid_size = 64.0f); + void UpdateEvent(const std::function& event, ImVec2 bg_size, - int tile_size, float grid_size = 64.0f); + int tile_size, float scale = 1.0f, float grid_size = 64.0f); // Background for the Canvas represents region without any content drawn to // it, but can be controlled by the user. @@ -40,6 +45,7 @@ class Canvas { // and allows the user to left click to paint the tile or right // click to select a new tile to paint with. bool DrawTilePainter(const Bitmap& bitmap, int size, float scale = 1.0f); + bool DrawSolidTilePainter(const ImVec4& color, int size); // Dictates which tile is currently selected based on what the user clicks // in the canvas window. Represented and split apart into a grid of tiles. @@ -56,6 +62,7 @@ class Canvas { // Draws the contents of the Bitmap image to the Canvas void DrawBitmap(const Bitmap& bitmap, int border_offset = 0, bool ready = true); + void DrawBitmap(const Bitmap& bitmap, int border_offset, float scale); void DrawBitmap(const Bitmap& bitmap, int x_offset = 0, int y_offset = 0, float scale = 1.0f); diff --git a/src/app/gui/input.cc b/src/app/gui/input.cc index cf50d364..7c2dd82b 100644 --- a/src/app/gui/input.cc +++ b/src/app/gui/input.cc @@ -126,8 +126,9 @@ bool InputHexWord(const char* label, uint16_t* data, float input_width) { ImGuiInputTextFlags_CharsHexadecimal); } -bool InputHexByte(const char* label, uint8_t* data, float input_width) { - return ImGui::InputScalarLeft(label, ImGuiDataType_U8, data, &kStepOneHex, +bool InputHexByte(const char* label, uint8_t* data, uint8_t step, + float input_width) { + return ImGui::InputScalarLeft(label, ImGuiDataType_U8, data, &step, &kStepFastHex, "%02X", input_width, ImGuiInputTextFlags_CharsHexadecimal); } diff --git a/src/app/gui/input.h b/src/app/gui/input.h index b8894a39..1542a702 100644 --- a/src/app/gui/input.h +++ b/src/app/gui/input.h @@ -19,7 +19,7 @@ IMGUI_API bool InputHex(const char* label, uint64_t* data); IMGUI_API bool InputHexShort(const char* label, uint32_t* data); IMGUI_API bool InputHexWord(const char* label, uint16_t* data, float input_width = 50.f); -IMGUI_API bool InputHexByte(const char* label, uint8_t* data, +IMGUI_API bool InputHexByte(const char* label, uint8_t* data, uint8_t step = 0x01, float input_width = 50.f); using ItemLabelFlags = enum ItemLabelFlag { diff --git a/src/app/gui/pipeline.cc b/src/app/gui/pipeline.cc index bd3a0b88..e94f1685 100644 --- a/src/app/gui/pipeline.cc +++ b/src/app/gui/pipeline.cc @@ -29,12 +29,6 @@ void SelectablePalettePipeline(uint64_t& palette_id, bool& refresh_graphics, ImGui::BeginGroup(); // Lock X position ImGui::Text("Palette"); for (int n = 0; n < palette.size(); n++) { - // static gfx::SNESColor transparent_color; - // if ((n % 8) == 0) { - // gui::SNESColorButton("##transparent", transparent_color, 0, - // ImVec2(20, 20)); - // ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); - // } ImGui::PushID(n); if ((n % 7) != 0) ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); diff --git a/src/app/rom.h b/src/app/rom.h index 4e1e616f..fd1febc1 100644 --- a/src/app/rom.h +++ b/src/app/rom.h @@ -425,6 +425,8 @@ class ROM : public core::ExperimentFlags { gfx::Bitmap* mutable_graphics_sheet(int index) { return &graphics_bin_.at(index); } + auto bitmap_manager() { return graphics_manager_; } + auto mutable_bitmap_manager() { return &graphics_manager_; } auto title() const { return title_; } auto size() const { return size_; } @@ -499,8 +501,6 @@ class ROM : public core::ExperimentFlags { std::stack bitmaps_to_create_; std::stack bitmaps_to_render_; - auto bitmap_manager() { return graphics_manager_; } - std::vector> main_blockset_ids; std::vector> room_blockset_ids; std::vector> spriteset_ids; diff --git a/src/cli/command_handler.h b/src/cli/command_handler.h index 84eb78c3..2c0a995e 100644 --- a/src/cli/command_handler.h +++ b/src/cli/command_handler.h @@ -14,7 +14,7 @@ #include "absl/strings/str_cat.h" #include "app/core/common.h" // for PcToSnes, SnesToPc #include "app/core/constants.h" // for RETURN_IF_ERROR -#include "app/core/pipeline.h" +#include "app/gui/pipeline.h" #include "app/gfx/bitmap.h" #include "app/gfx/compression.h" #include "app/gfx/snes_palette.h"