From 31f1850800fc0b0a115c54186b424850811a3693 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 9 Jul 2022 22:46:20 -0400 Subject: [PATCH] Created DungeonEditor and Canvas --- src/CMakeLists.txt | 2 + src/app/editor/dungeon_editor.cc | 58 +++++++++++++++++++ src/app/editor/dungeon_editor.h | 26 +++++++++ src/app/editor/editor.cc | 68 +++++----------------- src/app/editor/editor.h | 12 ++-- src/app/editor/overworld_editor.cc | 36 ++++++------ src/app/editor/overworld_editor.h | 18 +++--- src/app/gfx/bitmap.h | 1 + src/gui/canvas.cc | 93 ++++++++++++++++++++++++++++++ src/gui/canvas.h | 30 ++++++++++ 10 files changed, 259 insertions(+), 85 deletions(-) create mode 100644 src/app/editor/dungeon_editor.cc create mode 100644 src/app/editor/dungeon_editor.h create mode 100644 src/gui/canvas.cc create mode 100644 src/gui/canvas.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7f56e966..ef0560bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,8 +39,10 @@ add_executable( gui/input.cc gui/style.cc gui/widgets.cc + gui/canvas.cc app/editor/editor.cc app/editor/assembly_editor.cc + app/editor/dungeon_editor.cc app/editor/overworld_editor.cc app/rom.cc app/core/common.cc diff --git a/src/app/editor/dungeon_editor.cc b/src/app/editor/dungeon_editor.cc new file mode 100644 index 00000000..43b0230c --- /dev/null +++ b/src/app/editor/dungeon_editor.cc @@ -0,0 +1,58 @@ +#include "dungeon_editor.h" + +#include "gui/icons.h" + +namespace yaze { +namespace app { +namespace editor { + +void DungeonEditor::Update() { + DrawToolset(); + canvas_.Update(); +} + +void DungeonEditor::DrawToolset() { + if (ImGui::BeginTable("DWToolset", 9, toolset_table_flags_, ImVec2(0, 0))) { + ImGui::TableSetupColumn("#undoTool"); + ImGui::TableSetupColumn("#redoTool"); + ImGui::TableSetupColumn("#history"); + ImGui::TableSetupColumn("#separator"); + ImGui::TableSetupColumn("#bg1Tool"); + ImGui::TableSetupColumn("#bg2Tool"); + ImGui::TableSetupColumn("#bg3Tool"); + ImGui::TableSetupColumn("#itemTool"); + ImGui::TableSetupColumn("#spriteTool"); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_UNDO); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_REDO); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_MANAGE_HISTORY); + + ImGui::TableNextColumn(); + ImGui::Text(ICON_MD_MORE_VERT); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_FILTER_1); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_FILTER_2); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_FILTER_3); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_GRASS); + + ImGui::TableNextColumn(); + ImGui::Button(ICON_MD_PEST_CONTROL_RODENT); + ImGui::EndTable(); + } +} + +} // namespace editor +} // namespace app +} // namespace yaze \ No newline at end of file diff --git a/src/app/editor/dungeon_editor.h b/src/app/editor/dungeon_editor.h new file mode 100644 index 00000000..4063c6ac --- /dev/null +++ b/src/app/editor/dungeon_editor.h @@ -0,0 +1,26 @@ +#ifndef YAZE_APP_EDITOR_DUNGEONEDITOR_H +#define YAZE_APP_EDITOR_DUNGEONEDITOR_H + +#include + +#include "gui/icons.h" +#include "gui/canvas.h" + +namespace yaze { +namespace app { +namespace editor { +class DungeonEditor { + public: + void Update(); + + private: + void DrawToolset(); + + gui::Canvas canvas_; + ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit; +}; +} // namespace editor +} // namespace app +} // namespace yaze + +#endif diff --git a/src/app/editor/editor.cc b/src/app/editor/editor.cc index 3e4c5e3e..7c5888fc 100644 --- a/src/app/editor/editor.cc +++ b/src/app/editor/editor.cc @@ -7,10 +7,13 @@ #include #include "app/core/constants.h" +#include "app/editor/assembly_editor.h" +#include "app/editor/dungeon_editor.h" #include "app/editor/overworld_editor.h" #include "app/gfx/snes_palette.h" #include "app/gfx/tile.h" #include "app/rom.h" +#include "gui/canvas.h" #include "gui/icons.h" #include "gui/input.h" #include "gui/widgets.h" @@ -20,9 +23,6 @@ namespace app { namespace editor { Editor::Editor() { - asm_editor_.SetLanguageDefinition(gui::widgets::GetAssemblyLanguageDef()); - asm_editor_.SetPalette(TextEditor::GetDarkPalette()); - for (int i = 0; i < 8; i++) { current_palette_[i].x = (i * 0.21f); current_palette_[i].y = (i * 0.21f); @@ -346,67 +346,25 @@ void Editor::DrawProjectEditor() { } void Editor::DrawOverworldEditor() { - if (ImGui::BeginTabItem("Overworld")) { - overworld_editor_.Update(); - ImGui::EndTabItem(); - } + TAB_ITEM("Overworld") + overworld_editor_.Update(); + END_TAB_ITEM() } void Editor::DrawDungeonEditor() { - if (ImGui::BeginTabItem("Dungeon")) { - if (ImGui::BeginTable("DWToolset", 9, toolset_table_flags_, ImVec2(0, 0))) { - ImGui::TableSetupColumn("#undoTool"); - ImGui::TableSetupColumn("#redoTool"); - ImGui::TableSetupColumn("#history"); - ImGui::TableSetupColumn("#separator"); - ImGui::TableSetupColumn("#bg1Tool"); - ImGui::TableSetupColumn("#bg2Tool"); - ImGui::TableSetupColumn("#bg3Tool"); - ImGui::TableSetupColumn("#itemTool"); - ImGui::TableSetupColumn("#spriteTool"); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_UNDO); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_REDO); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_MANAGE_HISTORY); - - ImGui::TableNextColumn(); - ImGui::Text(ICON_MD_MORE_VERT); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_FILTER_1); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_FILTER_2); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_FILTER_3); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_GRASS); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_PEST_CONTROL_RODENT); - ImGui::EndTable(); - } - ImGui::EndTabItem(); - } + TAB_ITEM("Dungeon") + dungeon_editor_.Update(); + END_TAB_ITEM() } void Editor::DrawGraphicsEditor() { - if (ImGui::BeginTabItem("Graphics")) { - ImGui::EndTabItem(); - } + TAB_ITEM("Graphics") + END_TAB_ITEM() } void Editor::DrawSpriteEditor() { - if (ImGui::BeginTabItem("Sprites")) { - ImGui::EndTabItem(); - } + TAB_ITEM("Sprites") + END_TAB_ITEM() } } // namespace editor diff --git a/src/app/editor/editor.h b/src/app/editor/editor.h index f61b7f83..171964bb 100644 --- a/src/app/editor/editor.h +++ b/src/app/editor/editor.h @@ -8,10 +8,13 @@ #include #include "app/core/constants.h" -#include "app/editor/overworld_editor.h" -#include "app/gfx/tile.h" #include "app/editor/assembly_editor.h" +#include "app/editor/dungeon_editor.h" +#include "app/editor/overworld_editor.h" +#include "app/gfx/snes_palette.h" +#include "app/gfx/tile.h" #include "app/rom.h" +#include "gui/canvas.h" #include "gui/icons.h" #include "gui/input.h" @@ -40,14 +43,15 @@ class Editor { void DrawDungeonEditor(); void DrawGraphicsEditor(); void DrawSpriteEditor(); - + bool is_loaded_ = true; bool asm_is_loaded = false; rom::ROM rom_; - TextEditor asm_editor_; + gui::Canvas canvas_; AssemblyEditor assembly_editor_; OverworldEditor overworld_editor_; + DungeonEditor dungeon_editor_; std::shared_ptr sdl_renderer_; std::unordered_map image_cache_; diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index 7708589a..91451de2 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -10,24 +10,26 @@ #include "app/zelda3/overworld.h" #include "gui/icons.h" -// 1) find the gfx pointers (you could use ZS constant file) -// 2) decompress all the gfx with your lz2 decompressor -// 3) convert the 3bpp snes data into PC 4bpp (probably the hardest part) -// 4) get the tiles32 data -// 5) get the tiles16 data -// 6) get the map32 data (they must be decompressed as well with a lz2 -// variant not the same as gfx compression but pretty similar) 7) get the -// gfx data of the map yeah i forgot that one and load 4bpp in a pseudo vram -// and use that to render tiles on screen 8) try to render the tiles on the -// bitmap in black & white to start 9) get the palettes data and try to find -// how they're loaded in the game that's a big puzzle to solve then 9 you'll -// have an overworld map viewer, in less than few hours if are able to -// understand the data quickly +/** + * Drawing the Overworld + * Tips by Zarby + * + * 1) Find the graphics pointers (constants.h) + * 2) Convert the 3bpp SNES data into PC 4bpp (Hard) + * 3) Get the tiles32 data + * 4) Get the tiles16 data + * 5) Get the map32 data using lz2 variant decompression + * 6) Get the graphics data of the map + * 7) Load 4bpp into Pseudo VRAM for rendering tiles to screen + * 8) Render the tiles to a bitmap with a B&W palette to start + * 9) Get the palette data and find how it's loaded in game + * + */ namespace yaze { namespace app { namespace editor { -void OverworldEditor::SetupROM(app::rom::ROM &rom) { rom_ = rom; } +void OverworldEditor::SetupROM(rom::ROM &rom) { rom_ = rom; } void OverworldEditor::Update() { if (rom_.isLoaded() && !all_gfx_loaded_) { @@ -114,7 +116,7 @@ void OverworldEditor::DrawToolset() { ImGui::TableNextColumn(); if (ImGui::Button(ICON_MD_UPDATE)) { - overworld_.Load(rom_, allGfx16Ptr); + overworld_.Load(rom_, allgfxBitmap.GetData()); } ImGui::TableNextColumn(); @@ -302,7 +304,7 @@ void OverworldEditor::DrawTileSelector() { } } -void OverworldEditor::DrawTile16Selector() { +void OverworldEditor::DrawTile16Selector() const { static ImVec2 scrolling(0.0f, 0.0f); ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); auto canvas_sz = ImVec2(256 + 1, kNumSheetsToLoad * 64 + 1); @@ -359,7 +361,7 @@ void OverworldEditor::DrawTile16Selector() { draw_list->PopClipRect(); } -void OverworldEditor::DrawTile8Selector() { +void OverworldEditor::DrawTile8Selector() const { static ImVec2 scrolling(0.0f, 0.0f); ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); auto canvas_sz = ImVec2(256 + 1, kNumSheetsToLoad * 64 + 1); diff --git a/src/app/editor/overworld_editor.h b/src/app/editor/overworld_editor.h index 387982d6..6657479d 100644 --- a/src/app/editor/overworld_editor.h +++ b/src/app/editor/overworld_editor.h @@ -25,25 +25,25 @@ class OverworldEditor { void DrawOverworldMapSettings(); void DrawOverworldCanvas(); void DrawTileSelector(); - void DrawTile16Selector(); - void DrawTile8Selector(); + void DrawTile16Selector() const; + void DrawTile8Selector() const; void LoadBlockset(); void LoadGraphics(); + rom::ROM rom_; + zelda3::Overworld overworld_; gfx::SNESPalette palette_; - rom::ROM rom_; - + // pointer size 1048576 gfx::Bitmap tile16_blockset_bmp_; - uchar *tile16_blockset_ptr_ = new uchar[1048576]; + // pointer size 32768 gfx::Bitmap current_gfx_bmp_; - uchar *current_gfx_ptr_ = new uchar[(128 * 512) / 2]; + // pointer size 456704 gfx::Bitmap allgfxBitmap; - uchar *allGfx16Ptr = new uchar[(128 * 7136) / 2]; gfx::Bitmap mapblockset16Bitmap; @@ -62,14 +62,14 @@ class OverworldEditor { bool all_gfx_loaded_ = false; bool map_blockset_loaded_ = false; - ImVec4 current_palette_[8]; - constexpr static int kByteSize = 3; constexpr static int kMessageIdSize = 5; constexpr static int kNumSheetsToLoad = 100; constexpr static int kTile8DisplayHeight = 64; constexpr static float kInputFieldSize = 30.f; + ImVec4 current_palette_[8]; + ImGuiTableFlags toolset_table_flags = ImGuiTableFlags_SizingFixedFit; ImGuiTableFlags ow_map_flags = ImGuiTableFlags_Borders; ImGuiTableFlags ow_edit_flags = ImGuiTableFlags_Reorderable | diff --git a/src/app/gfx/bitmap.h b/src/app/gfx/bitmap.h index 7130ef74..5fd16c44 100644 --- a/src/app/gfx/bitmap.h +++ b/src/app/gfx/bitmap.h @@ -20,6 +20,7 @@ class Bitmap { int GetWidth() const { return width_; } int GetHeight() const { return height_; } void CreateTexture(std::shared_ptr renderer); + uchar *GetData() const { return pixel_data_; } SDL_Texture *GetTexture() const { return texture_; } private: diff --git a/src/gui/canvas.cc b/src/gui/canvas.cc new file mode 100644 index 00000000..d38ec9b6 --- /dev/null +++ b/src/gui/canvas.cc @@ -0,0 +1,93 @@ +#include "canvas.h" + +#include + +#include +#include + +namespace yaze { +namespace gui { + +void Canvas::Update() { + ImVec2 canvas_p0 = ImGui::GetCursorScreenPos(); + ImVec2 canvas_sz = ImGui::GetContentRegionAvail(); + auto canvas_p1 = ImVec2(canvas_p0.x + canvas_sz.x, canvas_p0.y + canvas_sz.y); + + // Draw border and background color + const ImGuiIO &io = ImGui::GetIO(); + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(canvas_p0, canvas_p1, IM_COL32(32, 32, 32, 255)); + draw_list->AddRect(canvas_p0, canvas_p1, IM_COL32(255, 255, 255, 255)); + + // This will catch our interactions + ImGui::InvisibleButton( + "canvas", canvas_sz, + ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + const bool is_hovered = ImGui::IsItemHovered(); // Hovered + const bool is_active = ImGui::IsItemActive(); // Held + const ImVec2 origin(canvas_p0.x + scrolling_.x, + canvas_p0.y + scrolling_.y); // Lock scrolled origin + const ImVec2 mouse_pos_in_canvas(io.MousePos.x - origin.x, + io.MousePos.y - origin.y); + + // Add first and second point + if (is_hovered && !dragging_select_ && + ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + points_.push_back(mouse_pos_in_canvas); + points_.push_back(mouse_pos_in_canvas); + dragging_select_ = true; + } + if (dragging_select_) { + points_.back() = mouse_pos_in_canvas; + if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) dragging_select_ = false; + } + + // Pan (we use a zero mouse threshold when there's no context menu) + const float mouse_threshold_for_pan = enable_context_menu_ ? -1.0f : 0.0f; + if (is_active && + ImGui::IsMouseDragging(ImGuiMouseButton_Right, mouse_threshold_for_pan)) { + scrolling_.x += io.MouseDelta.x; + scrolling_.y += io.MouseDelta.y; + } + + // Context menu (under default mouse threshold) + ImVec2 drag_delta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Right); + if (enable_context_menu_ && drag_delta.x == 0.0f && drag_delta.y == 0.0f) + ImGui::OpenPopupOnItemClick("context", ImGuiPopupFlags_MouseButtonRight); + if (ImGui::BeginPopup("context")) { + if (dragging_select_) points_.resize(points_.size() - 2); + dragging_select_ = false; + if (ImGui::MenuItem("Remove one", nullptr, false, points_.Size > 0)) { + points_.resize(points_.size() - 2); + } + ImGui::EndPopup(); + } + + // Draw grid + all lines in the canvas + draw_list->PushClipRect(canvas_p0, canvas_p1, true); + if (enable_grid_) { + const float GRID_STEP = 64.0f; + 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)); + 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)); + } + + for (int n = 0; n < points_.Size; n += 2) { + draw_list->AddRect( + ImVec2(origin.x + points_[n].x, origin.y + points_[n].y), + ImVec2(origin.x + points_[n + 1].x, origin.y + points_[n + 1].y), + IM_COL32(255, 255, 255, 255), 1.0f); + } + + draw_list->PopClipRect(); +} + +} // namespace gui +} // namespace yaze \ No newline at end of file diff --git a/src/gui/canvas.h b/src/gui/canvas.h new file mode 100644 index 00000000..4b5bddf8 --- /dev/null +++ b/src/gui/canvas.h @@ -0,0 +1,30 @@ +#ifndef YAZE_GUI_CANVAS_H +#define YAZE_GUI_CANVAS_H + +#include + +#include +#include + +namespace yaze { +namespace gui { +class Canvas { + public: + Canvas() = default; + + void Update(); + + private: + bool enable_grid_ = true; + bool enable_context_menu_ = true; + bool dragging_select_ = false; + + std::string title_; + + ImVector points_; + ImVec2 scrolling_; +}; +} // namespace gui +} // namespace yaze + +#endif \ No newline at end of file