backend-infra-engineer: Release v0.3.2 snapshot
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gfx/resource/arena.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gui/canvas/canvas.h"
|
||||
#include "app/gui/core/color.h"
|
||||
#include "app/gui/core/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#define YAZE_APP_EDITOR_GFX_GROUP_EDITOR_H
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gui/canvas/canvas.h"
|
||||
#include "app/rom.h"
|
||||
|
||||
namespace yaze {
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
#include "graphics_editor.h"
|
||||
#include "app/editor/system/editor_card_registry.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/status/statusor.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/core/platform/clipboard.h"
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/compression.h"
|
||||
#include "app/gfx/scad_format.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/gui/modules/asset_browser.h"
|
||||
#include "app/gui/style.h"
|
||||
#include "app/gui/core/ui_helpers.h"
|
||||
#include "util/file_util.h"
|
||||
#include "app/platform/window.h"
|
||||
#include "app/gfx/resource/arena.h"
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/util/compression.h"
|
||||
#include "app/gfx/util/scad_format.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gfx/types/snes_tile.h"
|
||||
#include "app/gui/canvas/canvas.h"
|
||||
#include "app/gui/core/color.h"
|
||||
#include "app/gui/core/icons.h"
|
||||
#include "app/gui/core/input.h"
|
||||
#include "app/gui/widgets/asset_browser.h"
|
||||
#include "app/gui/core/style.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/gfx/debug/performance/performance_profiler.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui/misc/cpp/imgui_stdlib.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using gfx::kPaletteGroupAddressesKeys;
|
||||
using ImGui::Button;
|
||||
using ImGui::InputInt;
|
||||
@@ -42,30 +43,121 @@ constexpr ImGuiTableFlags kGfxEditTableFlags =
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable |
|
||||
ImGuiTableFlags_SizingFixedFit;
|
||||
|
||||
void GraphicsEditor::Initialize() {}
|
||||
void GraphicsEditor::Initialize() {
|
||||
if (!dependencies_.card_registry) return;
|
||||
auto* card_registry = dependencies_.card_registry;
|
||||
|
||||
card_registry->RegisterCard({.card_id = "graphics.sheet_editor", .display_name = "Sheet Editor",
|
||||
.icon = ICON_MD_EDIT, .category = "Graphics",
|
||||
.shortcut_hint = "Ctrl+Shift+1", .priority = 10});
|
||||
card_registry->RegisterCard({.card_id = "graphics.sheet_browser", .display_name = "Sheet Browser",
|
||||
.icon = ICON_MD_VIEW_LIST, .category = "Graphics",
|
||||
.shortcut_hint = "Ctrl+Shift+2", .priority = 20});
|
||||
card_registry->RegisterCard({.card_id = "graphics.player_animations", .display_name = "Player Animations",
|
||||
.icon = ICON_MD_PERSON, .category = "Graphics",
|
||||
.shortcut_hint = "Ctrl+Shift+3", .priority = 30});
|
||||
card_registry->RegisterCard({.card_id = "graphics.prototype_viewer", .display_name = "Prototype Viewer",
|
||||
.icon = ICON_MD_CONSTRUCTION, .category = "Graphics",
|
||||
.shortcut_hint = "Ctrl+Shift+4", .priority = 40});
|
||||
|
||||
// Show sheet editor by default when Graphics Editor is activated
|
||||
card_registry->ShowCard("graphics.sheet_editor");
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::Load() { return absl::OkStatus(); }
|
||||
absl::Status GraphicsEditor::Load() {
|
||||
gfx::ScopedTimer timer("GraphicsEditor::Load");
|
||||
|
||||
// Initialize all graphics sheets with appropriate palettes from ROM
|
||||
// This ensures textures are created for editing
|
||||
if (rom()->is_loaded()) {
|
||||
auto& sheets = gfx::Arena::Get().gfx_sheets();
|
||||
|
||||
// Apply default palettes to all sheets based on common SNES ROM structure
|
||||
// Sheets 0-112: Use overworld/dungeon palettes
|
||||
// Sheets 113-127: Use sprite palettes
|
||||
// Sheets 128-222: Use auxiliary/menu palettes
|
||||
|
||||
LOG_INFO("GraphicsEditor", "Initializing textures for %d graphics sheets", kNumGfxSheets);
|
||||
|
||||
int sheets_queued = 0;
|
||||
for (int i = 0; i < kNumGfxSheets; i++) {
|
||||
if (!sheets[i].is_active() || !sheets[i].surface()) {
|
||||
continue; // Skip inactive or surface-less sheets
|
||||
}
|
||||
|
||||
// Palettes are now applied during ROM loading in LoadAllGraphicsData()
|
||||
// Just queue texture creation for sheets that don't have textures yet
|
||||
if (!sheets[i].texture()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &sheets[i]);
|
||||
sheets_queued++;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets", sheets_queued);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::Update() {
|
||||
if (ImGui::BeginTabBar("##TabBar")) {
|
||||
status_ = UpdateGfxEdit();
|
||||
if (ImGui::BeginTabItem("Sheet Browser")) {
|
||||
if (!dependencies_.card_registry) return absl::OkStatus();
|
||||
auto* card_registry = dependencies_.card_registry;
|
||||
|
||||
static gui::EditorCard sheet_editor_card("Sheet Editor", ICON_MD_EDIT);
|
||||
static gui::EditorCard sheet_browser_card("Sheet Browser", ICON_MD_VIEW_LIST);
|
||||
static gui::EditorCard player_anims_card("Player Animations", ICON_MD_PERSON);
|
||||
static gui::EditorCard prototype_card("Prototype Viewer", ICON_MD_CONSTRUCTION);
|
||||
|
||||
sheet_editor_card.SetDefaultSize(900, 700);
|
||||
sheet_browser_card.SetDefaultSize(400, 600);
|
||||
player_anims_card.SetDefaultSize(500, 600);
|
||||
prototype_card.SetDefaultSize(600, 500);
|
||||
|
||||
// Sheet Editor Card - Check visibility flag exists and is true before rendering
|
||||
bool* sheet_editor_visible = card_registry->GetVisibilityFlag("graphics.sheet_editor");
|
||||
if (sheet_editor_visible && *sheet_editor_visible) {
|
||||
if (sheet_editor_card.Begin(sheet_editor_visible)) {
|
||||
status_ = UpdateGfxEdit();
|
||||
}
|
||||
sheet_editor_card.End();
|
||||
}
|
||||
|
||||
// Sheet Browser Card - Check visibility flag exists and is true before rendering
|
||||
bool* sheet_browser_visible = card_registry->GetVisibilityFlag("graphics.sheet_browser");
|
||||
if (sheet_browser_visible && *sheet_browser_visible) {
|
||||
if (sheet_browser_card.Begin(sheet_browser_visible)) {
|
||||
if (asset_browser_.Initialized == false) {
|
||||
asset_browser_.Initialize(gfx::Arena::Get().gfx_sheets());
|
||||
}
|
||||
asset_browser_.Draw(gfx::Arena::Get().gfx_sheets());
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
status_ = UpdateScadView();
|
||||
status_ = UpdateLinkGfxView();
|
||||
ImGui::EndTabBar();
|
||||
sheet_browser_card.End();
|
||||
}
|
||||
|
||||
// Player Animations Card - Check visibility flag exists and is true before rendering
|
||||
bool* player_anims_visible = card_registry->GetVisibilityFlag("graphics.player_animations");
|
||||
if (player_anims_visible && *player_anims_visible) {
|
||||
if (player_anims_card.Begin(player_anims_visible)) {
|
||||
status_ = UpdateLinkGfxView();
|
||||
}
|
||||
player_anims_card.End();
|
||||
}
|
||||
|
||||
// Prototype Viewer Card - Check visibility flag exists and is true before rendering
|
||||
bool* prototype_visible = card_registry->GetVisibilityFlag("graphics.prototype_viewer");
|
||||
if (prototype_visible && *prototype_visible) {
|
||||
if (prototype_card.Begin(prototype_visible)) {
|
||||
status_ = UpdateScadView();
|
||||
}
|
||||
prototype_card.End();
|
||||
}
|
||||
|
||||
CLEAR_AND_RETURN_STATUS(status_)
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||
if (ImGui::BeginTabItem("Sheet Editor")) {
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name :
|
||||
@@ -89,11 +181,24 @@ absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw the graphics editing toolset with enhanced ROM hacking features
|
||||
*
|
||||
* Enhanced Features:
|
||||
* - Multi-tool selection for different editing modes
|
||||
* - Real-time zoom controls for precise pixel editing
|
||||
* - Sheet copy/paste operations for ROM graphics management
|
||||
* - Color picker integration with SNES palette system
|
||||
* - Tile size controls for 8x8 and 16x16 SNES tiles
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Toolset updates are batched to minimize ImGui overhead
|
||||
* - Color buttons use cached palette data for fast rendering
|
||||
* - Zoom controls update canvas scaling without full redraw
|
||||
*/
|
||||
void GraphicsEditor::DrawGfxEditToolset() {
|
||||
if (ImGui::BeginTable("##GfxEditToolset", 9, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
@@ -121,35 +226,13 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
|
||||
TableNextColumn();
|
||||
if (Button(ICON_MD_CONTENT_COPY)) {
|
||||
#if YAZE_LIB_PNG == 1
|
||||
std::vector<uint8_t> png_data =
|
||||
gfx::Arena::Get().gfx_sheets().at(current_sheet_).GetPngData();
|
||||
core::CopyImageToClipboard(png_data);
|
||||
#else
|
||||
// PNG support disabled - show message or alternative action
|
||||
status_ = absl::UnimplementedError("PNG export not available in this build");
|
||||
#endif
|
||||
status_ = absl::UnimplementedError("PNG export functionality removed");
|
||||
}
|
||||
HOVER_HINT("Copy to Clipboard");
|
||||
|
||||
TableNextColumn();
|
||||
if (Button(ICON_MD_CONTENT_PASTE)) {
|
||||
#if YAZE_LIB_PNG == 1
|
||||
std::vector<uint8_t> png_data;
|
||||
int width, height;
|
||||
core::GetImageFromClipboard(png_data, width, height);
|
||||
if (png_data.size() > 0) {
|
||||
gfx::Arena::Get()
|
||||
.mutable_gfx_sheets()
|
||||
->at(current_sheet_)
|
||||
.Create(width, height, 8, png_data);
|
||||
Renderer::Get().UpdateBitmap(
|
||||
&gfx::Arena::Get().mutable_gfx_sheets()->at(current_sheet_));
|
||||
}
|
||||
#else
|
||||
// PNG support disabled - show message or alternative action
|
||||
status_ = absl::UnimplementedError("PNG import not available in this build");
|
||||
#endif
|
||||
status_ = absl::UnimplementedError("PNG import functionality removed");
|
||||
}
|
||||
HOVER_HINT("Paste from Clipboard");
|
||||
|
||||
@@ -168,17 +251,35 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
// Enhanced palette color picker with SNES-specific features
|
||||
auto bitmap = gfx::Arena::Get().gfx_sheets()[current_sheet_];
|
||||
auto palette = bitmap.palette();
|
||||
|
||||
// Display palette colors in a grid layout for better ROM hacking workflow
|
||||
for (int i = 0; i < palette.size(); i++) {
|
||||
if (i > 0 && i % 8 == 0) {
|
||||
ImGui::NewLine(); // New row every 8 colors (SNES palette standard)
|
||||
}
|
||||
ImGui::SameLine();
|
||||
auto color =
|
||||
ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
|
||||
palette[i].rgb().z / 255.0f, 255.0f);
|
||||
|
||||
// Convert SNES color to ImGui format with proper scaling
|
||||
auto color = ImVec4(palette[i].rgb().x / 255.0f, palette[i].rgb().y / 255.0f,
|
||||
palette[i].rgb().z / 255.0f, 1.0f);
|
||||
|
||||
// Enhanced color button with tooltip showing SNES color value
|
||||
if (ImGui::ColorButton(absl::StrFormat("Palette Color %d", i).c_str(),
|
||||
color)) {
|
||||
color, ImGuiColorEditFlags_NoTooltip)) {
|
||||
current_color_ = color;
|
||||
}
|
||||
|
||||
// Add tooltip with SNES color information
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("SNES Color: $%04X\nRGB: (%d, %d, %d)",
|
||||
palette[i].snes(),
|
||||
static_cast<int>(palette[i].rgb().x),
|
||||
static_cast<int>(palette[i].rgb().y),
|
||||
static_cast<int>(palette[i].rgb().z));
|
||||
}
|
||||
}
|
||||
|
||||
TableNextColumn();
|
||||
@@ -216,36 +317,43 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
graphics_bin_canvas_.DrawBackground(ImVec2(0x100 + 1, 0x40 + 1));
|
||||
graphics_bin_canvas_.DrawContextMenu();
|
||||
if (value.is_active()) {
|
||||
auto texture = value.texture();
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
(ImTextureID)(intptr_t)texture,
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x +
|
||||
value.width() * sheet_scale_,
|
||||
graphics_bin_canvas_.zero_point().y +
|
||||
value.height() * sheet_scale_));
|
||||
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
open_sheets_.insert(key);
|
||||
// Ensure texture exists for active sheets
|
||||
if (!value.texture() && value.surface()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &value);
|
||||
}
|
||||
|
||||
auto texture = value.texture();
|
||||
if (texture) {
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
(ImTextureID)(intptr_t)texture,
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2),
|
||||
ImVec2(graphics_bin_canvas_.zero_point().x +
|
||||
value.width() * sheet_scale_,
|
||||
graphics_bin_canvas_.zero_point().y +
|
||||
value.height() * sheet_scale_));
|
||||
|
||||
// Add a slightly transparent rectangle behind the text
|
||||
ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2);
|
||||
ImVec2 text_size =
|
||||
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
|
||||
ImVec2 rent_min(text_pos.x, text_pos.y);
|
||||
ImVec2 rent_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
current_sheet_ = key;
|
||||
open_sheets_.insert(key);
|
||||
}
|
||||
|
||||
graphics_bin_canvas_.draw_list()->AddRectFilled(rent_min, rent_max,
|
||||
IM_COL32(0, 125, 0, 128));
|
||||
// Add a slightly transparent rectangle behind the text
|
||||
ImVec2 text_pos(graphics_bin_canvas_.zero_point().x + 2,
|
||||
graphics_bin_canvas_.zero_point().y + 2);
|
||||
ImVec2 text_size =
|
||||
ImGui::CalcTextSize(absl::StrFormat("%02X", key).c_str());
|
||||
ImVec2 rect_min(text_pos.x, text_pos.y);
|
||||
ImVec2 rect_max(text_pos.x + text_size.x, text_pos.y + text_size.y);
|
||||
|
||||
graphics_bin_canvas_.draw_list()->AddText(
|
||||
text_pos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
graphics_bin_canvas_.draw_list()->AddRectFilled(rect_min, rect_max,
|
||||
IM_COL32(0, 125, 0, 128));
|
||||
|
||||
graphics_bin_canvas_.draw_list()->AddText(
|
||||
text_pos, IM_COL32(125, 255, 125, 255),
|
||||
absl::StrFormat("%02X", key).c_str());
|
||||
}
|
||||
key++;
|
||||
}
|
||||
graphics_bin_canvas_.DrawGrid(16.0f);
|
||||
@@ -262,6 +370,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
gfx::ScopedTimer timer("graphics_editor_update_gfx_tab_view");
|
||||
|
||||
static int next_tab_id = 0;
|
||||
constexpr ImGuiTabBarFlags kGfxEditTabBarFlags =
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable |
|
||||
@@ -302,13 +412,17 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
auto draw_tile_event = [&]() {
|
||||
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, ¤t_bitmap,
|
||||
current_color_);
|
||||
Renderer::Get().UpdateBitmap(¤t_bitmap);
|
||||
// Notify Arena that this sheet has been modified for cross-editor synchronization
|
||||
gfx::Arena::Get().NotifySheetModified(sheet_id);
|
||||
};
|
||||
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
|
||||
nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id),
|
||||
current_color_, draw_tile_event, tile_size_, current_scale_);
|
||||
|
||||
// Notify Arena that this sheet has been modified for cross-editor synchronization
|
||||
gfx::Arena::Get().NotifySheetModified(sheet_id);
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
@@ -339,7 +453,7 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
current_sheet_ = id;
|
||||
// ImVec2(0x100, 0x40),
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
|
||||
nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
|
||||
[&]() {
|
||||
|
||||
},
|
||||
@@ -363,24 +477,66 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
auto palette_group = *rom()->palette_group().get_group(
|
||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
auto palette = palette_group.palette(edit_palette_index_);
|
||||
gui::TextWithSeparators("ROM Palette");
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::TextWithSeparators("ROM Palette Management");
|
||||
|
||||
// Quick palette presets for common SNES graphics types
|
||||
ImGui::Text("Quick Presets:");
|
||||
if (ImGui::Button("Overworld")) {
|
||||
edit_palette_group_name_index_ = 0; // Dungeon Main
|
||||
edit_palette_index_ = 0;
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Dungeon")) {
|
||||
edit_palette_group_name_index_ = 0; // Dungeon Main
|
||||
edit_palette_index_ = 1;
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Sprites")) {
|
||||
edit_palette_group_name_index_ = 4; // Sprites Aux1
|
||||
edit_palette_index_ = 0;
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// Apply current palette to current sheet
|
||||
if (ImGui::Button("Apply to Current Sheet") && !open_sheets_.empty()) {
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Apply to All Sheets")) {
|
||||
// Apply current palette to all active sheets
|
||||
for (int i = 0; i < kNumGfxSheets; i++) {
|
||||
auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->data()[i];
|
||||
if (sheet.is_active() && sheet.surface()) {
|
||||
sheet.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
|
||||
// Notify Arena that this sheet has been modified
|
||||
gfx::Arena::Get().NotifySheetModified(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SetNextItemWidth(150.f);
|
||||
ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
|
||||
kPaletteGroupAddressesKeys,
|
||||
IM_ARRAYSIZE(kPaletteGroupAddressesKeys));
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHex("Palette Group Index", &edit_palette_index_);
|
||||
gui::InputHex("Palette Index", &edit_palette_index_);
|
||||
ImGui::SetNextItemWidth(100.f);
|
||||
gui::InputHex("Sub-Palette", &edit_palette_sub_index_);
|
||||
|
||||
gui::SelectablePalettePipeline(edit_palette_sub_index_, refresh_graphics_,
|
||||
palette);
|
||||
|
||||
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||
gfx::Arena::Get()
|
||||
.mutable_gfx_sheets()
|
||||
->data()[current_sheet_]
|
||||
.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
|
||||
Renderer::Get().UpdateBitmap(
|
||||
&gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_]);
|
||||
auto& current = gfx::Arena::Get().mutable_gfx_sheets()->data()[current_sheet_];
|
||||
if (current.is_active() && current.surface()) {
|
||||
current.SetPaletteWithTransparent(palette, edit_palette_sub_index_);
|
||||
// Notify Arena that this sheet has been modified
|
||||
gfx::Arena::Get().NotifySheetModified(current_sheet_);
|
||||
}
|
||||
refresh_graphics_ = false;
|
||||
}
|
||||
}
|
||||
@@ -388,8 +544,6 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdateLinkGfxView() {
|
||||
TAB_ITEM("Player Animations")
|
||||
|
||||
if (ImGui::BeginTable("##PlayerAnimationTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name : {"Canvas", "Animation Steps", "Properties"})
|
||||
@@ -431,15 +585,10 @@ absl::Status GraphicsEditor::UpdateLinkGfxView() {
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
END_TAB_ITEM()
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdateScadView() {
|
||||
TAB_ITEM("Prototype")
|
||||
|
||||
RETURN_IF_ERROR(DrawToolset())
|
||||
|
||||
if (open_memory_editor_) {
|
||||
ImGui::Begin("Memory Editor", &open_memory_editor_);
|
||||
RETURN_IF_ERROR(DrawMemoryEditor())
|
||||
@@ -496,31 +645,6 @@ absl::Status GraphicsEditor::UpdateScadView() {
|
||||
}
|
||||
END_TABLE()
|
||||
|
||||
END_TAB_ITEM()
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::DrawToolset() {
|
||||
static constexpr absl::string_view kGfxToolsetColumnNames[] = {
|
||||
"#memoryEditor",
|
||||
};
|
||||
|
||||
if (ImGui::BeginTable("GraphicsToolset", 1, ImGuiTableFlags_SizingFixedFit,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name : kGfxToolsetColumnNames)
|
||||
ImGui::TableSetupColumn(name.data());
|
||||
|
||||
TableNextColumn();
|
||||
if (Button(absl::StrCat(ICON_MD_MEMORY, "Open Memory Editor").c_str())) {
|
||||
if (!open_memory_editor_) {
|
||||
open_memory_editor_ = true;
|
||||
} else {
|
||||
open_memory_editor_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -532,7 +656,7 @@ absl::Status GraphicsEditor::DrawCgxImport() {
|
||||
SameLine();
|
||||
|
||||
if (ImGui::Button("Open CGX")) {
|
||||
auto filename = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto filename = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
cgx_file_name_ = filename;
|
||||
cgx_file_path_ = std::filesystem::absolute(filename).string();
|
||||
is_open_ = true;
|
||||
@@ -550,7 +674,8 @@ absl::Status GraphicsEditor::DrawCgxImport() {
|
||||
cgx_bitmap_.Create(0x80, 0x200, 8, decoded_cgx_);
|
||||
if (col_file_) {
|
||||
cgx_bitmap_.SetPalette(decoded_col_);
|
||||
Renderer::Get().RenderBitmap(&cgx_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &cgx_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +686,7 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
||||
InputText("##ScrFile", &scr_file_name_);
|
||||
|
||||
if (ImGui::Button("Open SCR")) {
|
||||
auto filename = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto filename = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
scr_file_name_ = filename;
|
||||
scr_file_path_ = std::filesystem::absolute(filename).string();
|
||||
is_open_ = true;
|
||||
@@ -580,7 +705,8 @@ absl::Status GraphicsEditor::DrawScrImport() {
|
||||
scr_bitmap_.Create(0x100, 0x100, 8, decoded_scr_data_);
|
||||
if (scr_loaded_) {
|
||||
scr_bitmap_.SetPalette(decoded_col_);
|
||||
Renderer::Get().RenderBitmap(&scr_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &scr_bitmap_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +719,7 @@ absl::Status GraphicsEditor::DrawPaletteControls() {
|
||||
SameLine();
|
||||
|
||||
if (ImGui::Button("Open COL")) {
|
||||
auto filename = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto filename = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
col_file_name_ = filename;
|
||||
col_file_path_ = std::filesystem::absolute(filename).string();
|
||||
status_ = temp_rom_.LoadFromFile(col_file_path_,
|
||||
@@ -642,7 +768,7 @@ absl::Status GraphicsEditor::DrawObjImport() {
|
||||
SameLine();
|
||||
|
||||
if (ImGui::Button("Open OBJ")) {
|
||||
auto filename = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto filename = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
obj_file_path_ = std::filesystem::absolute(filename).string();
|
||||
status_ = temp_rom_.LoadFromFile(obj_file_path_);
|
||||
is_open_ = true;
|
||||
@@ -660,7 +786,7 @@ absl::Status GraphicsEditor::DrawTilemapImport() {
|
||||
SameLine();
|
||||
|
||||
if (ImGui::Button("Open Tilemap")) {
|
||||
auto filename = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto filename = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
tilemap_file_path_ = std::filesystem::absolute(filename).string();
|
||||
status_ = tilemap_rom_.LoadFromFile(tilemap_file_path_);
|
||||
status_ = tilemap_rom_.LoadFromFile(tilemap_file_path_);
|
||||
@@ -683,7 +809,7 @@ absl::Status GraphicsEditor::DrawFileImport() {
|
||||
SameLine();
|
||||
|
||||
if (ImGui::Button("Open BIN")) {
|
||||
auto filename = core::FileDialogWrapper::ShowOpenFileDialog();
|
||||
auto filename = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
file_path_ = filename;
|
||||
status_ = temp_rom_.LoadFromFile(file_path_);
|
||||
is_open_ = true;
|
||||
@@ -782,7 +908,8 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
Renderer::Get().RenderBitmap(&bin_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &bin_bitmap_);
|
||||
gfx_loaded_ = true;
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -811,7 +938,8 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
gfx_sheets_[i].SetPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
Renderer::Get().RenderBitmap(&gfx_sheets_[i]);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &gfx_sheets_[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -835,7 +963,8 @@ absl::Status GraphicsEditor::DecompressSuperDonkey() {
|
||||
gfx_sheets_[i].SetPalette(z3_rom_palette_);
|
||||
}
|
||||
|
||||
Renderer::Get().RenderBitmap(&gfx_sheets_[i]);
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &gfx_sheets_[i]);
|
||||
i++;
|
||||
}
|
||||
super_donkey_ = true;
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/editor/graphics/palette_editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_tile.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/modules/asset_browser.h"
|
||||
#include "app/editor/palette/palette_editor.h"
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/types/snes_tile.h"
|
||||
#include "app/gui/canvas/canvas.h"
|
||||
#include "app/gui/app/editor_layout.h"
|
||||
#include "app/gui/widgets/asset_browser.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/overworld/overworld.h"
|
||||
#include "zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
@@ -105,7 +106,6 @@ class GraphicsEditor : public Editor {
|
||||
absl::Status DrawTilemapImport();
|
||||
|
||||
// Other Functions
|
||||
absl::Status DrawToolset();
|
||||
absl::Status DrawPaletteControls();
|
||||
absl::Status DrawClipboardImport();
|
||||
absl::Status DrawExperimentalFeatures();
|
||||
@@ -115,6 +115,8 @@ class GraphicsEditor : public Editor {
|
||||
absl::Status DecompressSuperDonkey();
|
||||
|
||||
// Member Variables
|
||||
// Card visibility managed by EditorCardManager
|
||||
|
||||
ImVec4 current_color_;
|
||||
uint16_t current_sheet_ = 0;
|
||||
uint8_t tile_size_ = 0x01;
|
||||
|
||||
@@ -1,527 +0,0 @@
|
||||
#include "palette_editor.h"
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/color.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
using ImGui::AcceptDragDropPayload;
|
||||
using ImGui::BeginChild;
|
||||
using ImGui::BeginDragDropTarget;
|
||||
using ImGui::BeginGroup;
|
||||
using ImGui::BeginPopup;
|
||||
using ImGui::BeginPopupContextItem;
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::Button;
|
||||
using ImGui::ColorButton;
|
||||
using ImGui::ColorPicker4;
|
||||
using ImGui::EndChild;
|
||||
using ImGui::EndDragDropTarget;
|
||||
using ImGui::EndGroup;
|
||||
using ImGui::EndPopup;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::GetContentRegionAvail;
|
||||
using ImGui::GetStyle;
|
||||
using ImGui::OpenPopup;
|
||||
using ImGui::PopID;
|
||||
using ImGui::PushID;
|
||||
using ImGui::SameLine;
|
||||
using ImGui::Selectable;
|
||||
using ImGui::Separator;
|
||||
using ImGui::SetClipboardText;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
using ImGui::Text;
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
constexpr ImGuiTableFlags kPaletteTableFlags =
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable |
|
||||
ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Hideable;
|
||||
|
||||
constexpr ImGuiColorEditFlags kPalNoAlpha = ImGuiColorEditFlags_NoAlpha;
|
||||
|
||||
constexpr ImGuiColorEditFlags kPalButtonFlags = ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip;
|
||||
|
||||
constexpr ImGuiColorEditFlags kColorPopupFlags =
|
||||
ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV |
|
||||
ImGuiColorEditFlags_DisplayHex;
|
||||
|
||||
namespace {
|
||||
int CustomFormatString(char* buf, size_t buf_size, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
#ifdef IMGUI_USE_STB_SPRINTF
|
||||
int w = stbsp_vsnprintf(buf, (int)buf_size, fmt, args);
|
||||
#else
|
||||
int w = vsnprintf(buf, buf_size, fmt, args);
|
||||
#endif
|
||||
va_end(args);
|
||||
if (buf == nullptr) return w;
|
||||
if (w == -1 || w >= (int)buf_size) w = (int)buf_size - 1;
|
||||
buf[w] = 0;
|
||||
return w;
|
||||
}
|
||||
|
||||
static inline float color_saturate(float f) {
|
||||
return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f;
|
||||
}
|
||||
|
||||
#define F32_TO_INT8_SAT(_VAL) \
|
||||
((int)(color_saturate(_VAL) * 255.0f + \
|
||||
0.5f)) // Saturated, always output 0..255
|
||||
} // namespace
|
||||
|
||||
absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded) {
|
||||
static ImVec4 color = ImVec4(0, 0, 0, 255.f);
|
||||
static ImVec4 current_palette[256] = {};
|
||||
ImGuiColorEditFlags misc_flags = ImGuiColorEditFlags_AlphaPreview |
|
||||
ImGuiColorEditFlags_NoDragDrop |
|
||||
ImGuiColorEditFlags_NoOptions;
|
||||
|
||||
// Generate a default palette. The palette will persist and can be edited.
|
||||
static bool init = false;
|
||||
if (loaded && !init) {
|
||||
for (int n = 0; n < palette.size(); n++) {
|
||||
auto color = palette[n];
|
||||
current_palette[n].x = color.rgb().x / 255;
|
||||
current_palette[n].y = color.rgb().y / 255;
|
||||
current_palette[n].z = color.rgb().z / 255;
|
||||
current_palette[n].w = 255; // Alpha
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
|
||||
static ImVec4 backup_color;
|
||||
bool open_popup = ColorButton("MyColor##3b", color, misc_flags);
|
||||
SameLine(0, GetStyle().ItemInnerSpacing.x);
|
||||
open_popup |= Button("Palette");
|
||||
if (open_popup) {
|
||||
OpenPopup("mypicker");
|
||||
backup_color = color;
|
||||
}
|
||||
|
||||
if (BeginPopup("mypicker")) {
|
||||
TEXT_WITH_SEPARATOR("Current Overworld Palette");
|
||||
ColorPicker4("##picker", (float*)&color,
|
||||
misc_flags | ImGuiColorEditFlags_NoSidePreview |
|
||||
ImGuiColorEditFlags_NoSmallPreview);
|
||||
SameLine();
|
||||
|
||||
BeginGroup(); // Lock X position
|
||||
Text("Current ==>");
|
||||
SameLine();
|
||||
Text("Previous");
|
||||
|
||||
if (Button("Update Map Palette")) {
|
||||
}
|
||||
|
||||
ColorButton(
|
||||
"##current", color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40));
|
||||
SameLine();
|
||||
|
||||
if (ColorButton(
|
||||
"##previous", backup_color,
|
||||
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf,
|
||||
ImVec2(60, 40)))
|
||||
color = backup_color;
|
||||
|
||||
// List of Colors in Overworld Palette
|
||||
Separator();
|
||||
Text("Palette");
|
||||
for (int n = 0; n < IM_ARRAYSIZE(current_palette); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
if (ColorButton("##palette", current_palette[n], kPalButtonFlags,
|
||||
ImVec2(20, 20)))
|
||||
color = ImVec4(current_palette[n].x, current_palette[n].y,
|
||||
current_palette[n].z, color.w); // Preserve alpha!
|
||||
|
||||
if (BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F))
|
||||
memcpy((float*)¤t_palette[n], payload->Data, sizeof(float) * 3);
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F))
|
||||
memcpy((float*)¤t_palette[n], payload->Data, sizeof(float) * 4);
|
||||
EndDragDropTarget();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
EndGroup();
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::Initialize() {}
|
||||
|
||||
absl::Status PaletteEditor::Load() {
|
||||
if (rom()->is_loaded()) {
|
||||
// Initialize the labels
|
||||
for (int i = 0; i < kNumPalettes; i++) {
|
||||
rom()->resource_label()->CreateOrGetLabel(
|
||||
"Palette Group Name", std::to_string(i),
|
||||
std::string(kPaletteGroupNames[i]));
|
||||
}
|
||||
} else {
|
||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::Update() {
|
||||
static int current_palette_group = 0;
|
||||
if (BeginTable("paletteGroupsTable", 3, kPaletteTableFlags)) {
|
||||
TableSetupColumn("Categories", ImGuiTableColumnFlags_WidthFixed, 200);
|
||||
TableSetupColumn("Palette Editor", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Quick Access", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableHeadersRow();
|
||||
|
||||
TableNextRow();
|
||||
TableNextColumn();
|
||||
|
||||
static int selected_category = 0;
|
||||
BeginChild("CategoryList", ImVec2(0, GetContentRegionAvail().y), true);
|
||||
|
||||
for (int i = 0; i < kNumPalettes; i++) {
|
||||
const bool is_selected = (selected_category == i);
|
||||
if (Selectable(std::string(kPaletteCategoryNames[i]).c_str(),
|
||||
is_selected)) {
|
||||
selected_category = i;
|
||||
}
|
||||
}
|
||||
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
BeginChild("PaletteEditor", ImVec2(0, 0), true);
|
||||
|
||||
Text("%s", std::string(kPaletteCategoryNames[selected_category]).c_str());
|
||||
|
||||
Separator();
|
||||
|
||||
if (rom()->is_loaded()) {
|
||||
status_ = DrawPaletteGroup(selected_category, true);
|
||||
}
|
||||
|
||||
EndChild();
|
||||
|
||||
TableNextColumn();
|
||||
DrawQuickAccessTab();
|
||||
|
||||
EndTable();
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::DrawQuickAccessTab() {
|
||||
BeginChild("QuickAccessPalettes", ImVec2(0, 0), true);
|
||||
|
||||
Text("Custom Palette");
|
||||
DrawCustomPalette();
|
||||
|
||||
Separator();
|
||||
|
||||
// Current color picker with more options
|
||||
BeginGroup();
|
||||
Text("Current Color");
|
||||
gui::SnesColorEdit4("##CurrentColorPicker", ¤t_color_,
|
||||
kColorPopupFlags);
|
||||
|
||||
char buf[64];
|
||||
auto col = current_color_.rgb();
|
||||
int cr = F32_TO_INT8_SAT(col.x / 255.0f);
|
||||
int cg = F32_TO_INT8_SAT(col.y / 255.0f);
|
||||
int cb = F32_TO_INT8_SAT(col.z / 255.0f);
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "RGB: %d, %d, %d", cr, cg, cb);
|
||||
Text("%s", buf);
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "SNES: $%04X",
|
||||
current_color_.snes());
|
||||
Text("%s", buf);
|
||||
|
||||
if (Button("Copy to Clipboard")) {
|
||||
SetClipboardText(buf);
|
||||
}
|
||||
EndGroup();
|
||||
|
||||
Separator();
|
||||
|
||||
// Recently used colors
|
||||
Text("Recently Used Colors");
|
||||
for (int i = 0; i < recently_used_colors_.size(); i++) {
|
||||
PushID(i);
|
||||
if (i % 8 != 0) SameLine();
|
||||
ImVec4 displayColor =
|
||||
gui::ConvertSnesColorToImVec4(recently_used_colors_[i]);
|
||||
if (ImGui::ColorButton("##recent", displayColor)) {
|
||||
// Set as current color
|
||||
current_color_ = recently_used_colors_[i];
|
||||
}
|
||||
PopID();
|
||||
}
|
||||
|
||||
EndChild();
|
||||
}
|
||||
|
||||
void PaletteEditor::DrawCustomPalette() {
|
||||
if (BeginChild("ColorPalette", ImVec2(0, 40), ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
for (int i = 0; i < custom_palette_.size(); i++) {
|
||||
PushID(i);
|
||||
if (i > 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
// Add a context menu to each color
|
||||
ImVec4 displayColor = gui::ConvertSnesColorToImVec4(custom_palette_[i]);
|
||||
bool open_color_picker = ImGui::ColorButton(
|
||||
absl::StrFormat("##customPal%d", i).c_str(), displayColor);
|
||||
|
||||
if (open_color_picker) {
|
||||
current_color_ = custom_palette_[i];
|
||||
edit_palette_index_ = i;
|
||||
ImGui::OpenPopup("CustomPaletteColorEdit");
|
||||
}
|
||||
|
||||
if (BeginPopupContextItem()) {
|
||||
// Edit color directly in the popup
|
||||
SnesColor original_color = custom_palette_[i];
|
||||
if (gui::SnesColorEdit4("Edit Color", &custom_palette_[i],
|
||||
kColorPopupFlags)) {
|
||||
// Color was changed, add to recently used
|
||||
AddRecentlyUsedColor(custom_palette_[i]);
|
||||
}
|
||||
|
||||
if (Button("Delete", ImVec2(-1, 0))) {
|
||||
custom_palette_.erase(custom_palette_.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle drag/drop for palette rearrangement
|
||||
if (BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) {
|
||||
ImVec4 color;
|
||||
memcpy((float*)&color, payload->Data, sizeof(float) * 3);
|
||||
color.w = 1.0f; // Set alpha to 1.0
|
||||
custom_palette_[i] = SnesColor(color);
|
||||
AddRecentlyUsedColor(custom_palette_[i]);
|
||||
}
|
||||
EndDragDropTarget();
|
||||
}
|
||||
|
||||
PopID();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (ImGui::Button("+")) {
|
||||
custom_palette_.push_back(SnesColor(0x7FFF));
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (ImGui::Button("Clear")) {
|
||||
custom_palette_.clear();
|
||||
}
|
||||
|
||||
SameLine();
|
||||
if (ImGui::Button("Export")) {
|
||||
std::string clipboard;
|
||||
for (const auto& color : custom_palette_) {
|
||||
clipboard += absl::StrFormat("$%04X,", color.snes());
|
||||
}
|
||||
SetClipboardText(clipboard.c_str());
|
||||
}
|
||||
}
|
||||
EndChild();
|
||||
|
||||
// Color picker popup for custom palette editing
|
||||
if (ImGui::BeginPopup("CustomPaletteColorEdit")) {
|
||||
if (edit_palette_index_ >= 0 &&
|
||||
edit_palette_index_ < custom_palette_.size()) {
|
||||
SnesColor original_color = custom_palette_[edit_palette_index_];
|
||||
if (gui::SnesColorEdit4(
|
||||
"Edit Color", &custom_palette_[edit_palette_index_],
|
||||
kColorPopupFlags | ImGuiColorEditFlags_PickerHueWheel)) {
|
||||
// Color was changed, add to recently used
|
||||
AddRecentlyUsedColor(custom_palette_[edit_palette_index_]);
|
||||
}
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::DrawPaletteGroup(int category, bool right_side) {
|
||||
if (!rom()->is_loaded()) {
|
||||
return absl::NotFoundError("ROM not open, no palettes to display");
|
||||
}
|
||||
|
||||
auto palette_group_name = kPaletteGroupNames[category];
|
||||
gfx::PaletteGroup* palette_group =
|
||||
rom()->mutable_palette_group()->get_group(palette_group_name.data());
|
||||
const auto size = palette_group->size();
|
||||
|
||||
for (int j = 0; j < size; j++) {
|
||||
gfx::SnesPalette* palette = palette_group->mutable_palette(j);
|
||||
auto pal_size = palette->size();
|
||||
|
||||
BeginGroup();
|
||||
|
||||
PushID(j);
|
||||
BeginGroup();
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, palette_group_name.data(), /*key=*/std::to_string(j),
|
||||
"Unnamed Palette");
|
||||
EndGroup();
|
||||
|
||||
for (int n = 0; n < pal_size; n++) {
|
||||
PushID(n);
|
||||
if (n > 0 && n % 8 != 0) SameLine(0.0f, 2.0f);
|
||||
|
||||
auto popup_id =
|
||||
absl::StrCat(kPaletteCategoryNames[category].data(), j, "_", n);
|
||||
|
||||
ImVec4 displayColor = gui::ConvertSnesColorToImVec4((*palette)[n]);
|
||||
if (ImGui::ColorButton(popup_id.c_str(), displayColor)) {
|
||||
current_color_ = (*palette)[n];
|
||||
AddRecentlyUsedColor(current_color_);
|
||||
}
|
||||
|
||||
if (BeginPopupContextItem(popup_id.c_str())) {
|
||||
RETURN_IF_ERROR(HandleColorPopup(*palette, category, j, n))
|
||||
}
|
||||
PopID();
|
||||
}
|
||||
PopID();
|
||||
EndGroup();
|
||||
|
||||
if (j < size - 1) {
|
||||
Separator();
|
||||
}
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void PaletteEditor::AddRecentlyUsedColor(const SnesColor& color) {
|
||||
// Check if color already exists in recently used
|
||||
auto it = std::find_if(
|
||||
recently_used_colors_.begin(), recently_used_colors_.end(),
|
||||
[&color](const SnesColor& c) { return c.snes() == color.snes(); });
|
||||
|
||||
// If found, remove it to re-add at front
|
||||
if (it != recently_used_colors_.end()) {
|
||||
recently_used_colors_.erase(it);
|
||||
}
|
||||
|
||||
// Add at front
|
||||
recently_used_colors_.insert(recently_used_colors_.begin(), color);
|
||||
|
||||
// Limit size
|
||||
if (recently_used_colors_.size() > 16) {
|
||||
recently_used_colors_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::HandleColorPopup(gfx::SnesPalette& palette, int i,
|
||||
int j, int n) {
|
||||
auto col = gfx::ToFloatArray(palette[n]);
|
||||
auto original_color = palette[n];
|
||||
|
||||
if (gui::SnesColorEdit4("Edit Color", &palette[n], kColorPopupFlags)) {
|
||||
history_.RecordChange(/*group_name=*/std::string(kPaletteGroupNames[i]),
|
||||
/*palette_index=*/j, /*color_index=*/n,
|
||||
original_color, palette[n]);
|
||||
palette[n].set_modified(true);
|
||||
|
||||
// Add to recently used colors
|
||||
AddRecentlyUsedColor(palette[n]);
|
||||
}
|
||||
|
||||
// Color information display
|
||||
char buf[64];
|
||||
int cr = F32_TO_INT8_SAT(col[0]);
|
||||
int cg = F32_TO_INT8_SAT(col[1]);
|
||||
int cb = F32_TO_INT8_SAT(col[2]);
|
||||
|
||||
Text("RGB: %d, %d, %d", cr, cg, cb);
|
||||
Text("SNES: $%04X", palette[n].snes());
|
||||
|
||||
Separator();
|
||||
|
||||
if (Button("Copy as..", ImVec2(-1, 0))) OpenPopup("Copy");
|
||||
if (BeginPopup("Copy")) {
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff)", col[0],
|
||||
col[1], col[2]);
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d)", cr, cg, cb);
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", cr, cg, cb);
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
// SNES Format
|
||||
CustomFormatString(buf, IM_ARRAYSIZE(buf), "$%04X",
|
||||
ConvertRgbToSnes(ImVec4(col[0], col[1], col[2], 1.0f)));
|
||||
if (Selectable(buf)) SetClipboardText(buf);
|
||||
|
||||
EndPopup();
|
||||
}
|
||||
|
||||
// Add a button to add this color to custom palette
|
||||
if (Button("Add to Custom Palette", ImVec2(-1, 0))) {
|
||||
custom_palette_.push_back(palette[n]);
|
||||
}
|
||||
|
||||
EndPopup();
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::EditColorInPalette(gfx::SnesPalette& palette,
|
||||
int index) {
|
||||
if (index >= palette.size()) {
|
||||
return absl::InvalidArgumentError("Index out of bounds");
|
||||
}
|
||||
|
||||
// Get the current color
|
||||
auto color = palette[index];
|
||||
auto currentColor = color.rgb();
|
||||
if (ColorPicker4("Color Picker", (float*)&palette[index])) {
|
||||
// The color was modified, update it in the palette
|
||||
palette[index] = gui::ConvertImVec4ToSnesColor(currentColor);
|
||||
|
||||
// Add to recently used colors
|
||||
AddRecentlyUsedColor(palette[index]);
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status PaletteEditor::ResetColorToOriginal(
|
||||
gfx::SnesPalette& palette, int index,
|
||||
const gfx::SnesPalette& originalPalette) {
|
||||
if (index >= palette.size() || index >= originalPalette.size()) {
|
||||
return absl::InvalidArgumentError("Index out of bounds");
|
||||
}
|
||||
auto color = originalPalette[index];
|
||||
auto originalColor = color.rgb();
|
||||
palette[index] = gui::ConvertImVec4ToSnesColor(originalColor);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
@@ -1,134 +0,0 @@
|
||||
#ifndef YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
#define YAZE_APP_EDITOR_PALETTE_EDITOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/editor/graphics/gfx_group_editor.h"
|
||||
#include "app/gfx/snes_color.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
namespace palette_internal {
|
||||
|
||||
struct PaletteChange {
|
||||
std::string group_name;
|
||||
size_t palette_index;
|
||||
size_t color_index;
|
||||
gfx::SnesColor original_color;
|
||||
gfx::SnesColor new_color;
|
||||
};
|
||||
|
||||
class PaletteEditorHistory {
|
||||
public:
|
||||
void RecordChange(const std::string& group_name, size_t palette_index,
|
||||
size_t color_index, const gfx::SnesColor& original_color,
|
||||
const gfx::SnesColor& new_color) {
|
||||
if (recent_changes_.size() >= kMaxHistorySize) {
|
||||
recent_changes_.pop_front();
|
||||
}
|
||||
|
||||
recent_changes_.push_back(
|
||||
{group_name, palette_index, color_index, original_color, new_color});
|
||||
}
|
||||
|
||||
gfx::SnesColor RestoreOriginalColor(const std::string& group_name,
|
||||
size_t palette_index,
|
||||
size_t color_index) const {
|
||||
for (const auto& change : recent_changes_) {
|
||||
if (change.group_name == group_name &&
|
||||
change.palette_index == palette_index &&
|
||||
change.color_index == color_index) {
|
||||
return change.original_color;
|
||||
}
|
||||
}
|
||||
return gfx::SnesColor();
|
||||
}
|
||||
|
||||
auto size() const { return recent_changes_.size(); }
|
||||
|
||||
gfx::SnesColor& GetModifiedColor(size_t index) {
|
||||
return recent_changes_[index].new_color;
|
||||
}
|
||||
gfx::SnesColor& GetOriginalColor(size_t index) {
|
||||
return recent_changes_[index].original_color;
|
||||
}
|
||||
|
||||
const std::deque<PaletteChange>& GetRecentChanges() const {
|
||||
return recent_changes_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<PaletteChange> recent_changes_;
|
||||
static const size_t kMaxHistorySize = 50;
|
||||
};
|
||||
} // namespace palette_internal
|
||||
|
||||
absl::Status DisplayPalette(gfx::SnesPalette& palette, bool loaded);
|
||||
|
||||
/**
|
||||
* @class PaletteEditor
|
||||
* @brief Allows the user to view and edit in game palettes.
|
||||
*/
|
||||
class PaletteEditor : public Editor {
|
||||
public:
|
||||
explicit PaletteEditor(Rom* rom = nullptr) : rom_(rom) {
|
||||
type_ = EditorType::kPalette;
|
||||
custom_palette_.push_back(gfx::SnesColor(0x7FFF));
|
||||
}
|
||||
|
||||
void Initialize() override;
|
||||
absl::Status Load() override;
|
||||
absl::Status Update() override;
|
||||
absl::Status Cut() override { return absl::OkStatus(); }
|
||||
absl::Status Copy() override { return absl::OkStatus(); }
|
||||
absl::Status Paste() override { return absl::OkStatus(); }
|
||||
absl::Status Undo() override { return absl::OkStatus(); }
|
||||
absl::Status Redo() override { return absl::OkStatus(); }
|
||||
absl::Status Find() override { return absl::OkStatus(); }
|
||||
absl::Status Save() override { return absl::UnimplementedError("Save"); }
|
||||
|
||||
void DrawQuickAccessTab();
|
||||
|
||||
void DrawCustomPalette();
|
||||
absl::Status DrawPaletteGroup(int category, bool right_side = false);
|
||||
absl::Status EditColorInPalette(gfx::SnesPalette& palette, int index);
|
||||
absl::Status ResetColorToOriginal(gfx::SnesPalette& palette, int index,
|
||||
const gfx::SnesPalette& originalPalette);
|
||||
|
||||
void AddRecentlyUsedColor(const gfx::SnesColor& color);
|
||||
|
||||
void set_rom(Rom* rom) { rom_ = rom; }
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
private:
|
||||
absl::Status HandleColorPopup(gfx::SnesPalette& palette, int i, int j, int n);
|
||||
|
||||
absl::Status status_;
|
||||
gfx::SnesColor current_color_;
|
||||
|
||||
GfxGroupEditor gfx_group_editor_;
|
||||
|
||||
std::vector<gfx::SnesColor> custom_palette_;
|
||||
std::vector<gfx::SnesColor> recently_used_colors_;
|
||||
|
||||
int edit_palette_index_ = -1;
|
||||
|
||||
ImVec4 saved_palette_[256] = {};
|
||||
|
||||
palette_internal::PaletteEditorHistory history_;
|
||||
|
||||
Rom* rom_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
} // namespace yaze
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,13 +5,16 @@
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gfx/tilemap.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gfx/render/tilemap.h"
|
||||
#include "app/gui/canvas/canvas.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/zelda3/screen/dungeon_map.h"
|
||||
#include "app/zelda3/screen/inventory.h"
|
||||
#include "zelda3/screen/dungeon_map.h"
|
||||
#include "zelda3/screen/inventory.h"
|
||||
#include "zelda3/screen/title_screen.h"
|
||||
#include "zelda3/screen/overworld_map_screen.h"
|
||||
#include "app/gui/app/editor_layout.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -57,9 +60,17 @@ class ScreenEditor : public Editor {
|
||||
void DrawOverworldMapEditor();
|
||||
|
||||
void DrawInventoryMenuEditor();
|
||||
void DrawInventoryItemIcons();
|
||||
void DrawToolset();
|
||||
void DrawDungeonMapToolset();
|
||||
void DrawInventoryToolset();
|
||||
|
||||
// Title screen layer editing
|
||||
void DrawTitleScreenCompositeCanvas();
|
||||
void DrawTitleScreenBG1Canvas();
|
||||
void DrawTitleScreenBG2Canvas();
|
||||
void DrawTitleScreenBlocksetSelector();
|
||||
|
||||
absl::Status LoadDungeonMapTile16(const std::vector<uint8_t>& gfx_data,
|
||||
bool bin_mode = false);
|
||||
absl::Status SaveDungeonMapTile16();
|
||||
@@ -87,12 +98,12 @@ class ScreenEditor : public Editor {
|
||||
bool copy_button_pressed = false;
|
||||
bool paste_button_pressed = false;
|
||||
|
||||
std::vector<gfx::Bitmap> tile8_individual_;
|
||||
zelda3::DungeonMapLabels dungeon_map_labels_;
|
||||
|
||||
gfx::SnesPalette palette_;
|
||||
gfx::BitmapTable sheets_;
|
||||
gfx::Tilemap tile16_blockset_;
|
||||
gfx::Tilemap tile8_tilemap_; // Tilemap for 8x8 tiles with on-demand caching
|
||||
std::array<gfx::TileInfo, 4> current_tile16_info;
|
||||
|
||||
gui::Canvas current_tile_canvas_{"##CurrentTileCanvas", ImVec2(32, 32),
|
||||
@@ -102,7 +113,41 @@ class ScreenEditor : public Editor {
|
||||
gui::Canvas tilemap_canvas_{"##TilemapCanvas", ImVec2(128 + 2, (192) + 4),
|
||||
gui::CanvasGridSize::k8x8, 2.f};
|
||||
|
||||
// Title screen canvases
|
||||
// Title screen is 32x32 tiles at 8x8 pixels = 256x256 pixels total
|
||||
gui::Canvas title_bg1_canvas_{"##TitleBG1Canvas", ImVec2(256, 256),
|
||||
gui::CanvasGridSize::k8x8, 2.0f};
|
||||
gui::Canvas title_bg2_canvas_{"##TitleBG2Canvas", ImVec2(256, 256),
|
||||
gui::CanvasGridSize::k8x8, 2.0f};
|
||||
// Blockset is 128 pixels wide x 512 pixels tall (16x64 8x8 tiles)
|
||||
gui::Canvas title_blockset_canvas_{"##TitleBlocksetCanvas",
|
||||
ImVec2(128, 512),
|
||||
gui::CanvasGridSize::k8x8, 2.0f};
|
||||
|
||||
zelda3::Inventory inventory_;
|
||||
zelda3::TitleScreen title_screen_;
|
||||
zelda3::OverworldMapScreen ow_map_screen_;
|
||||
|
||||
// Title screen state
|
||||
int selected_title_tile16_ = 0;
|
||||
bool title_screen_loaded_ = false;
|
||||
bool title_h_flip_ = false;
|
||||
bool title_v_flip_ = false;
|
||||
int title_palette_ = 0;
|
||||
bool show_title_bg1_ = true;
|
||||
bool show_title_bg2_ = true;
|
||||
|
||||
// Overworld map screen state
|
||||
int selected_ow_tile_ = 0;
|
||||
bool ow_map_loaded_ = false;
|
||||
bool ow_show_dark_world_ = false;
|
||||
|
||||
// Overworld map canvases
|
||||
gui::Canvas ow_map_canvas_{"##OWMapCanvas", ImVec2(512, 512),
|
||||
gui::CanvasGridSize::k8x8, 1.0f};
|
||||
gui::Canvas ow_tileset_canvas_{"##OWTilesetCanvas", ImVec2(128, 128),
|
||||
gui::CanvasGridSize::k8x8, 2.0f};
|
||||
|
||||
Rom* rom_;
|
||||
absl::Status status_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user