backend-infra-engineer: Release v0.3.3 snapshot
This commit is contained in:
@@ -112,7 +112,7 @@ void GfxGroupEditor::DrawBlocksetViewer(bool sheet_only) {
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int sheet_id = rom()->main_blockset_ids[selected_blockset_][i];
|
||||
auto &sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
|
||||
auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
|
||||
gui::BitmapCanvasPipeline(blockset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 22);
|
||||
}
|
||||
@@ -165,7 +165,7 @@ void GfxGroupEditor::DrawRoomsetViewer() {
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->room_blockset_ids[selected_roomset_][i];
|
||||
auto &sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
|
||||
auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
|
||||
gui::BitmapCanvasPipeline(roomset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 23);
|
||||
}
|
||||
@@ -203,7 +203,7 @@ void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
|
||||
BeginGroup();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int sheet_id = rom()->spriteset_ids[selected_spriteset_][i];
|
||||
auto &sheet =
|
||||
auto& sheet =
|
||||
gfx::Arena::Get().mutable_gfx_sheets()->at(115 + sheet_id);
|
||||
gui::BitmapCanvasPipeline(spriteset_canvas_, sheet, 256, 0x10 * 0x04,
|
||||
0x20, true, false, 24);
|
||||
@@ -215,20 +215,20 @@ void GfxGroupEditor::DrawSpritesetViewer(bool sheet_only) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void DrawPaletteFromPaletteGroup(gfx::SnesPalette &palette) {
|
||||
void DrawPaletteFromPaletteGroup(gfx::SnesPalette& palette) {
|
||||
if (palette.empty()) {
|
||||
return;
|
||||
}
|
||||
for (size_t n = 0; n < palette.size(); n++) {
|
||||
PushID(n);
|
||||
if ((n % 8) != 0) SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
if ((n % 8) != 0)
|
||||
SameLine(0.0f, GetStyle().ItemSpacing.y);
|
||||
|
||||
// Small icon of the color in the palette
|
||||
if (gui::SnesColorButton(absl::StrCat("Palette", n), palette[n],
|
||||
ImGuiColorEditFlags_NoAlpha |
|
||||
ImGuiColorEditFlags_NoPicker |
|
||||
ImGuiColorEditFlags_NoTooltip)) {
|
||||
}
|
||||
ImGuiColorEditFlags_NoTooltip)) {}
|
||||
|
||||
PopID();
|
||||
}
|
||||
@@ -247,13 +247,13 @@ void GfxGroupEditor::DrawPaletteViewer() {
|
||||
false, "paletteset", "0x" + std::to_string(selected_paletteset_),
|
||||
"Paletteset " + std::to_string(selected_paletteset_));
|
||||
|
||||
uint8_t &dungeon_main_palette_val =
|
||||
uint8_t& dungeon_main_palette_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][0];
|
||||
uint8_t &dungeon_spr_pal_1_val =
|
||||
uint8_t& dungeon_spr_pal_1_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][1];
|
||||
uint8_t &dungeon_spr_pal_2_val =
|
||||
uint8_t& dungeon_spr_pal_2_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][2];
|
||||
uint8_t &dungeon_spr_pal_3_val =
|
||||
uint8_t& dungeon_spr_pal_3_val =
|
||||
rom()->paletteset_ids[selected_paletteset_][3];
|
||||
|
||||
gui::InputHexByte("Dungeon Main", &dungeon_main_palette_val);
|
||||
@@ -261,13 +261,13 @@ void GfxGroupEditor::DrawPaletteViewer() {
|
||||
rom()->resource_label()->SelectableLabelWithNameEdit(
|
||||
false, kPaletteGroupNames[PaletteCategory::kDungeons].data(),
|
||||
std::to_string(dungeon_main_palette_val), "Unnamed dungeon palette");
|
||||
auto &palette = *rom()->mutable_palette_group()->dungeon_main.mutable_palette(
|
||||
auto& palette = *rom()->mutable_palette_group()->dungeon_main.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][0]);
|
||||
DrawPaletteFromPaletteGroup(palette);
|
||||
Separator();
|
||||
|
||||
gui::InputHexByte("Dungeon Spr Pal 1", &dungeon_spr_pal_1_val);
|
||||
auto &spr_aux_pal1 =
|
||||
auto& spr_aux_pal1 =
|
||||
*rom()->mutable_palette_group()->sprites_aux1.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][1]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal1);
|
||||
@@ -278,7 +278,7 @@ void GfxGroupEditor::DrawPaletteViewer() {
|
||||
Separator();
|
||||
|
||||
gui::InputHexByte("Dungeon Spr Pal 2", &dungeon_spr_pal_2_val);
|
||||
auto &spr_aux_pal2 =
|
||||
auto& spr_aux_pal2 =
|
||||
*rom()->mutable_palette_group()->sprites_aux2.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][2]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal2);
|
||||
@@ -289,7 +289,7 @@ void GfxGroupEditor::DrawPaletteViewer() {
|
||||
Separator();
|
||||
|
||||
gui::InputHexByte("Dungeon Spr Pal 3", &dungeon_spr_pal_3_val);
|
||||
auto &spr_aux_pal3 =
|
||||
auto& spr_aux_pal3 =
|
||||
*rom()->mutable_palette_group()->sprites_aux3.mutable_palette(
|
||||
rom()->paletteset_ids[selected_paletteset_][3]);
|
||||
DrawPaletteFromPaletteGroup(spr_aux_pal3);
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
#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/gui/core/ui_helpers.h"
|
||||
#include "util/file_util.h"
|
||||
#include "app/platform/window.h"
|
||||
#include "app/gfx/resource/arena.h"
|
||||
#include "app/editor/system/editor_card_registry.h"
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/util/compression.h"
|
||||
#include "app/gfx/util/scad_format.h"
|
||||
#include "app/gfx/debug/performance/performance_profiler.h"
|
||||
#include "app/gfx/resource/arena.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gfx/types/snes_tile.h"
|
||||
#include "app/gfx/util/compression.h"
|
||||
#include "app/gfx/util/scad_format.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/gui/core/ui_helpers.h"
|
||||
#include "app/gui/widgets/asset_browser.h"
|
||||
#include "app/platform/window.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/file_util.h"
|
||||
#include "util/log.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -44,47 +44,61 @@ constexpr ImGuiTableFlags kGfxEditTableFlags =
|
||||
ImGuiTableFlags_SizingFixedFit;
|
||||
|
||||
void GraphicsEditor::Initialize() {
|
||||
if (!dependencies_.card_registry) return;
|
||||
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});
|
||||
|
||||
|
||||
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() {
|
||||
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);
|
||||
|
||||
|
||||
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()) {
|
||||
@@ -93,29 +107,34 @@ absl::Status GraphicsEditor::Load() {
|
||||
sheets_queued++;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets", sheets_queued);
|
||||
|
||||
LOG_INFO("GraphicsEditor", "Queued texture creation for %d graphics sheets",
|
||||
sheets_queued);
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::Update() {
|
||||
if (!dependencies_.card_registry) return absl::OkStatus();
|
||||
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);
|
||||
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");
|
||||
// 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();
|
||||
@@ -123,8 +142,10 @@ absl::Status GraphicsEditor::Update() {
|
||||
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");
|
||||
// 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) {
|
||||
@@ -135,8 +156,10 @@ absl::Status GraphicsEditor::Update() {
|
||||
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");
|
||||
// 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();
|
||||
@@ -144,8 +167,10 @@ absl::Status GraphicsEditor::Update() {
|
||||
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");
|
||||
// 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();
|
||||
@@ -158,42 +183,42 @@ absl::Status GraphicsEditor::Update() {
|
||||
}
|
||||
|
||||
absl::Status GraphicsEditor::UpdateGfxEdit() {
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name :
|
||||
{"Tilesheets", "Current Graphics", "Palette Controls"})
|
||||
ImGui::TableSetupColumn(name);
|
||||
if (ImGui::BeginTable("##GfxEditTable", 3, kGfxEditTableFlags,
|
||||
ImVec2(0, 0))) {
|
||||
for (const auto& name :
|
||||
{"Tilesheets", "Current Graphics", "Palette Controls"})
|
||||
ImGui::TableSetupColumn(name);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextColumn();
|
||||
status_ = UpdateGfxSheetList();
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableNextColumn();
|
||||
status_ = UpdateGfxSheetList();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (rom()->is_loaded()) {
|
||||
DrawGfxEditToolset();
|
||||
status_ = UpdateGfxTabView();
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (rom()->is_loaded()) {
|
||||
status_ = UpdatePaletteColumn();
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
if (rom()->is_loaded()) {
|
||||
DrawGfxEditToolset();
|
||||
status_ = UpdateGfxTabView();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
if (rom()->is_loaded()) {
|
||||
status_ = UpdatePaletteColumn();
|
||||
}
|
||||
}
|
||||
ImGui::EndTable();
|
||||
|
||||
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
|
||||
@@ -254,31 +279,32 @@ void GraphicsEditor::DrawGfxEditToolset() {
|
||||
// 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::NewLine(); // New row every 8 colors (SNES palette standard)
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
// 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);
|
||||
|
||||
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, 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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +348,7 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &value);
|
||||
}
|
||||
|
||||
|
||||
auto texture = value.texture();
|
||||
if (texture) {
|
||||
graphics_bin_canvas_.draw_list()->AddImage(
|
||||
@@ -347,8 +373,8 @@ absl::Status GraphicsEditor::UpdateGfxSheetList() {
|
||||
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()->AddRectFilled(rect_min, rect_max,
|
||||
IM_COL32(0, 125, 0, 128));
|
||||
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),
|
||||
@@ -371,7 +397,7 @@ 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 |
|
||||
@@ -412,22 +438,25 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
auto draw_tile_event = [&]() {
|
||||
current_sheet_canvas_.DrawTileOnBitmap(tile_size_, ¤t_bitmap,
|
||||
current_color_);
|
||||
// Notify Arena that this sheet has been modified for cross-editor synchronization
|
||||
gfx::Arena::Get().NotifySheetModified(sheet_id);
|
||||
// Notify Arena that this sheet has been modified for cross-editor
|
||||
// synchronization
|
||||
gfx::Arena::Get().NotifySheetModified(sheet_id);
|
||||
};
|
||||
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
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
|
||||
// Notify Arena that this sheet has been modified for cross-editor
|
||||
// synchronization
|
||||
gfx::Arena::Get().NotifySheetModified(sheet_id);
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (!open) release_queue_.push(sheet_id);
|
||||
if (!open)
|
||||
release_queue_.push(sheet_id);
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
@@ -453,7 +482,8 @@ absl::Status GraphicsEditor::UpdateGfxTabView() {
|
||||
current_sheet_ = id;
|
||||
// ImVec2(0x100, 0x40),
|
||||
current_sheet_canvas_.UpdateColorPainter(
|
||||
nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(id), current_color_,
|
||||
nullptr, gfx::Arena::Get().mutable_gfx_sheets()->at(id),
|
||||
current_color_,
|
||||
[&]() {
|
||||
|
||||
},
|
||||
@@ -478,7 +508,7 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
kPaletteGroupAddressesKeys[edit_palette_group_name_index_]);
|
||||
auto palette = palette_group.palette(edit_palette_index_);
|
||||
gui::TextWithSeparators("ROM Palette Management");
|
||||
|
||||
|
||||
// Quick palette presets for common SNES graphics types
|
||||
ImGui::Text("Quick Presets:");
|
||||
if (ImGui::Button("Overworld")) {
|
||||
@@ -499,7 +529,7 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
refresh_graphics_ = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
// Apply current palette to current sheet
|
||||
if (ImGui::Button("Apply to Current Sheet") && !open_sheets_.empty()) {
|
||||
refresh_graphics_ = true;
|
||||
@@ -517,7 +547,7 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
ImGui::SetNextItemWidth(150.f);
|
||||
ImGui::Combo("Palette Group", (int*)&edit_palette_group_name_index_,
|
||||
kPaletteGroupAddressesKeys,
|
||||
@@ -531,7 +561,8 @@ absl::Status GraphicsEditor::UpdatePaletteColumn() {
|
||||
palette);
|
||||
|
||||
if (refresh_graphics_ && !open_sheets_.empty()) {
|
||||
auto& current = 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
|
||||
@@ -613,7 +644,9 @@ absl::Status GraphicsEditor::UpdateScadView() {
|
||||
status_ = DrawExperimentalFeatures();
|
||||
}
|
||||
|
||||
NEXT_COLUMN() { status_ = DrawPaletteControls(); }
|
||||
NEXT_COLUMN() {
|
||||
status_ = DrawPaletteControls();
|
||||
}
|
||||
|
||||
NEXT_COLUMN()
|
||||
gui::BitmapCanvasPipeline(scr_canvas_, scr_bitmap_, 0x200, 0x200, 0x20,
|
||||
@@ -908,8 +941,8 @@ absl::Status GraphicsEditor::DecompressImportData(int size) {
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &bin_bitmap_);
|
||||
gfx::Arena::Get().QueueTextureCommand(gfx::Arena::TextureCommandType::UPDATE,
|
||||
&bin_bitmap_);
|
||||
gfx_loaded_ = true;
|
||||
|
||||
return absl::OkStatus();
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
#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/canvas/canvas.h"
|
||||
#include "app/gui/widgets/asset_browser.h"
|
||||
#include "app/rom.h"
|
||||
#include "zelda3/overworld/overworld.h"
|
||||
#include "imgui/imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "zelda3/overworld/overworld.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
@@ -57,8 +57,8 @@ const std::string kSuperDonkeySprites[] = {
|
||||
*/
|
||||
class GraphicsEditor : public Editor {
|
||||
public:
|
||||
explicit GraphicsEditor(Rom* rom = nullptr) : rom_(rom) {
|
||||
type_ = EditorType::kGraphics;
|
||||
explicit GraphicsEditor(Rom* rom = nullptr) : rom_(rom) {
|
||||
type_ = EditorType::kGraphics;
|
||||
}
|
||||
|
||||
void Initialize() override;
|
||||
@@ -71,10 +71,10 @@ class GraphicsEditor : public Editor {
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
absl::Status Find() override { return absl::UnimplementedError("Find"); }
|
||||
|
||||
|
||||
// Set the ROM pointer
|
||||
void set_rom(Rom* rom) { rom_ = rom; }
|
||||
|
||||
|
||||
// Get the ROM pointer
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
|
||||
@@ -1,50 +1,65 @@
|
||||
#include "screen_editor.h"
|
||||
#include "app/editor/system/editor_card_registry.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gfx/debug/performance/performance_profiler.h"
|
||||
#include "util/file_util.h"
|
||||
#include "app/gfx/resource/arena.h"
|
||||
#include "app/editor/system/editor_card_registry.h"
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/debug/performance/performance_profiler.h"
|
||||
#include "app/gfx/resource/arena.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 "imgui/imgui.h"
|
||||
#include "util/file_util.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/macro.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
|
||||
|
||||
constexpr uint32_t kRedPen = 0xFF0000FF;
|
||||
|
||||
void ScreenEditor::Initialize() {
|
||||
if (!dependencies_.card_registry) return;
|
||||
if (!dependencies_.card_registry)
|
||||
return;
|
||||
auto* card_registry = dependencies_.card_registry;
|
||||
|
||||
card_registry->RegisterCard({.card_id = "screen.dungeon_maps", .display_name = "Dungeon Maps",
|
||||
.icon = ICON_MD_MAP, .category = "Screen",
|
||||
.shortcut_hint = "Alt+1", .priority = 10});
|
||||
card_registry->RegisterCard({.card_id = "screen.inventory_menu", .display_name = "Inventory Menu",
|
||||
.icon = ICON_MD_INVENTORY, .category = "Screen",
|
||||
.shortcut_hint = "Alt+2", .priority = 20});
|
||||
card_registry->RegisterCard({.card_id = "screen.overworld_map", .display_name = "Overworld Map",
|
||||
.icon = ICON_MD_PUBLIC, .category = "Screen",
|
||||
.shortcut_hint = "Alt+3", .priority = 30});
|
||||
card_registry->RegisterCard({.card_id = "screen.title_screen", .display_name = "Title Screen",
|
||||
.icon = ICON_MD_TITLE, .category = "Screen",
|
||||
.shortcut_hint = "Alt+4", .priority = 40});
|
||||
card_registry->RegisterCard({.card_id = "screen.naming_screen", .display_name = "Naming Screen",
|
||||
.icon = ICON_MD_EDIT, .category = "Screen",
|
||||
.shortcut_hint = "Alt+5", .priority = 50});
|
||||
|
||||
|
||||
card_registry->RegisterCard({.card_id = "screen.dungeon_maps",
|
||||
.display_name = "Dungeon Maps",
|
||||
.icon = ICON_MD_MAP,
|
||||
.category = "Screen",
|
||||
.shortcut_hint = "Alt+1",
|
||||
.priority = 10});
|
||||
card_registry->RegisterCard({.card_id = "screen.inventory_menu",
|
||||
.display_name = "Inventory Menu",
|
||||
.icon = ICON_MD_INVENTORY,
|
||||
.category = "Screen",
|
||||
.shortcut_hint = "Alt+2",
|
||||
.priority = 20});
|
||||
card_registry->RegisterCard({.card_id = "screen.overworld_map",
|
||||
.display_name = "Overworld Map",
|
||||
.icon = ICON_MD_PUBLIC,
|
||||
.category = "Screen",
|
||||
.shortcut_hint = "Alt+3",
|
||||
.priority = 30});
|
||||
card_registry->RegisterCard({.card_id = "screen.title_screen",
|
||||
.display_name = "Title Screen",
|
||||
.icon = ICON_MD_TITLE,
|
||||
.category = "Screen",
|
||||
.shortcut_hint = "Alt+4",
|
||||
.priority = 40});
|
||||
card_registry->RegisterCard({.card_id = "screen.naming_screen",
|
||||
.display_name = "Naming Screen",
|
||||
.icon = ICON_MD_EDIT,
|
||||
.category = "Screen",
|
||||
.shortcut_hint = "Alt+5",
|
||||
.priority = 50});
|
||||
|
||||
// Show title screen by default
|
||||
card_registry->ShowCard("screen.title_screen");
|
||||
}
|
||||
@@ -76,45 +91,48 @@ absl::Status ScreenEditor::Load() {
|
||||
const int tile8_width = 128;
|
||||
const int tile8_height = 128; // 4 sheets × 32 pixels each
|
||||
std::vector<uint8_t> tile8_data(tile8_width * tile8_height);
|
||||
|
||||
|
||||
// Copy data from all 4 sheets into the combined bitmap
|
||||
for (int sheet_idx = 0; sheet_idx < 4; sheet_idx++) {
|
||||
const auto& sheet = sheets_[sheet_idx];
|
||||
int dest_y_offset = sheet_idx * 32; // Each sheet is 32 pixels tall
|
||||
|
||||
|
||||
for (int y = 0; y < 32; y++) {
|
||||
for (int x = 0; x < 128; x++) {
|
||||
int src_index = y * 128 + x;
|
||||
int dest_index = (dest_y_offset + y) * 128 + x;
|
||||
|
||||
|
||||
if (src_index < sheet.size() && dest_index < tile8_data.size()) {
|
||||
tile8_data[dest_index] = sheet.data()[src_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create tilemap with 8x8 tile size
|
||||
tile8_tilemap_.tile_size = {8, 8};
|
||||
tile8_tilemap_.map_size = {256, 256}; // Logical size for tile count
|
||||
tile8_tilemap_.atlas.Create(tile8_width, tile8_height, 8, tile8_data);
|
||||
tile8_tilemap_.atlas.SetPalette(*rom()->mutable_dungeon_palette(3));
|
||||
|
||||
|
||||
// Queue single texture creation for the atlas (not individual tiles)
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &tile8_tilemap_.atlas);
|
||||
gfx::Arena::Get().QueueTextureCommand(gfx::Arena::TextureCommandType::CREATE,
|
||||
&tile8_tilemap_.atlas);
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status ScreenEditor::Update() {
|
||||
if (!dependencies_.card_registry) return absl::OkStatus();
|
||||
if (!dependencies_.card_registry)
|
||||
return absl::OkStatus();
|
||||
auto* card_registry = dependencies_.card_registry;
|
||||
|
||||
static gui::EditorCard dungeon_maps_card("Dungeon Maps", ICON_MD_MAP);
|
||||
static gui::EditorCard inventory_menu_card("Inventory Menu", ICON_MD_INVENTORY);
|
||||
static gui::EditorCard inventory_menu_card("Inventory Menu",
|
||||
ICON_MD_INVENTORY);
|
||||
static gui::EditorCard overworld_map_card("Overworld Map", ICON_MD_PUBLIC);
|
||||
static gui::EditorCard title_screen_card("Title Screen", ICON_MD_TITLE);
|
||||
static gui::EditorCard naming_screen_card("Naming Screen", ICON_MD_EDIT_ATTRIBUTES);
|
||||
static gui::EditorCard naming_screen_card("Naming Screen",
|
||||
ICON_MD_EDIT_ATTRIBUTES);
|
||||
|
||||
dungeon_maps_card.SetDefaultSize(800, 600);
|
||||
inventory_menu_card.SetDefaultSize(800, 600);
|
||||
@@ -122,44 +140,54 @@ absl::Status ScreenEditor::Update() {
|
||||
title_screen_card.SetDefaultSize(600, 500);
|
||||
naming_screen_card.SetDefaultSize(500, 400);
|
||||
|
||||
// Dungeon Maps Card - Check visibility flag exists and is true before rendering
|
||||
bool* dungeon_maps_visible = card_registry->GetVisibilityFlag("screen.dungeon_maps");
|
||||
// Dungeon Maps Card - Check visibility flag exists and is true before
|
||||
// rendering
|
||||
bool* dungeon_maps_visible =
|
||||
card_registry->GetVisibilityFlag("screen.dungeon_maps");
|
||||
if (dungeon_maps_visible && *dungeon_maps_visible) {
|
||||
if (dungeon_maps_card.Begin(dungeon_maps_visible)) {
|
||||
DrawDungeonMapsEditor();
|
||||
}
|
||||
dungeon_maps_card.End();
|
||||
}
|
||||
|
||||
// Inventory Menu Card - Check visibility flag exists and is true before rendering
|
||||
bool* inventory_menu_visible = card_registry->GetVisibilityFlag("screen.inventory_menu");
|
||||
|
||||
// Inventory Menu Card - Check visibility flag exists and is true before
|
||||
// rendering
|
||||
bool* inventory_menu_visible =
|
||||
card_registry->GetVisibilityFlag("screen.inventory_menu");
|
||||
if (inventory_menu_visible && *inventory_menu_visible) {
|
||||
if (inventory_menu_card.Begin(inventory_menu_visible)) {
|
||||
DrawInventoryMenuEditor();
|
||||
}
|
||||
inventory_menu_card.End();
|
||||
}
|
||||
|
||||
// Overworld Map Card - Check visibility flag exists and is true before rendering
|
||||
bool* overworld_map_visible = card_registry->GetVisibilityFlag("screen.overworld_map");
|
||||
|
||||
// Overworld Map Card - Check visibility flag exists and is true before
|
||||
// rendering
|
||||
bool* overworld_map_visible =
|
||||
card_registry->GetVisibilityFlag("screen.overworld_map");
|
||||
if (overworld_map_visible && *overworld_map_visible) {
|
||||
if (overworld_map_card.Begin(overworld_map_visible)) {
|
||||
DrawOverworldMapEditor();
|
||||
}
|
||||
overworld_map_card.End();
|
||||
}
|
||||
|
||||
// Title Screen Card - Check visibility flag exists and is true before rendering
|
||||
bool* title_screen_visible = card_registry->GetVisibilityFlag("screen.title_screen");
|
||||
|
||||
// Title Screen Card - Check visibility flag exists and is true before
|
||||
// rendering
|
||||
bool* title_screen_visible =
|
||||
card_registry->GetVisibilityFlag("screen.title_screen");
|
||||
if (title_screen_visible && *title_screen_visible) {
|
||||
if (title_screen_card.Begin(title_screen_visible)) {
|
||||
DrawTitleScreenEditor();
|
||||
}
|
||||
title_screen_card.End();
|
||||
}
|
||||
|
||||
// Naming Screen Card - Check visibility flag exists and is true before rendering
|
||||
bool* naming_screen_visible = card_registry->GetVisibilityFlag("screen.naming_screen");
|
||||
|
||||
// Naming Screen Card - Check visibility flag exists and is true before
|
||||
// rendering
|
||||
bool* naming_screen_visible =
|
||||
card_registry->GetVisibilityFlag("screen.naming_screen");
|
||||
if (naming_screen_visible && *naming_screen_visible) {
|
||||
if (naming_screen_card.Begin(naming_screen_visible)) {
|
||||
DrawNamingScreenEditor();
|
||||
@@ -176,58 +204,58 @@ void ScreenEditor::DrawToolset() {
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawInventoryMenuEditor() {
|
||||
static bool create = false;
|
||||
if (!create && rom()->is_loaded()) {
|
||||
status_ = inventory_.Create(rom());
|
||||
if (status_.ok()) {
|
||||
palette_ = inventory_.palette();
|
||||
create = true;
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error loading inventory: %s",
|
||||
status_.message().data());
|
||||
return;
|
||||
}
|
||||
static bool create = false;
|
||||
if (!create && rom()->is_loaded()) {
|
||||
status_ = inventory_.Create(rom());
|
||||
if (status_.ok()) {
|
||||
palette_ = inventory_.palette();
|
||||
create = true;
|
||||
} else {
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error loading inventory: %s",
|
||||
status_.message().data());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DrawInventoryToolset();
|
||||
DrawInventoryToolset();
|
||||
|
||||
if (ImGui::BeginTable("InventoryScreen", 4, ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("Canvas");
|
||||
ImGui::TableSetupColumn("Tilesheet");
|
||||
ImGui::TableSetupColumn("Item Icons");
|
||||
ImGui::TableSetupColumn("Palette");
|
||||
ImGui::TableHeadersRow();
|
||||
if (ImGui::BeginTable("InventoryScreen", 4, ImGuiTableFlags_Resizable)) {
|
||||
ImGui::TableSetupColumn("Canvas");
|
||||
ImGui::TableSetupColumn("Tilesheet");
|
||||
ImGui::TableSetupColumn("Item Icons");
|
||||
ImGui::TableSetupColumn("Palette");
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
screen_canvas_.DrawBackground();
|
||||
screen_canvas_.DrawContextMenu();
|
||||
screen_canvas_.DrawBitmap(inventory_.bitmap(), 2, create);
|
||||
screen_canvas_.DrawGrid(32.0f);
|
||||
screen_canvas_.DrawOverlay();
|
||||
ImGui::TableNextColumn();
|
||||
screen_canvas_.DrawBackground();
|
||||
screen_canvas_.DrawContextMenu();
|
||||
screen_canvas_.DrawBitmap(inventory_.bitmap(), 2, create);
|
||||
screen_canvas_.DrawGrid(32.0f);
|
||||
screen_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
|
||||
tilesheet_canvas_.DrawContextMenu();
|
||||
tilesheet_canvas_.DrawBitmap(inventory_.tilesheet(), 2, create);
|
||||
tilesheet_canvas_.DrawGrid(16.0f);
|
||||
tilesheet_canvas_.DrawOverlay();
|
||||
ImGui::TableNextColumn();
|
||||
tilesheet_canvas_.DrawBackground(ImVec2(128 * 2 + 2, (192 * 2) + 4));
|
||||
tilesheet_canvas_.DrawContextMenu();
|
||||
tilesheet_canvas_.DrawBitmap(inventory_.tilesheet(), 2, create);
|
||||
tilesheet_canvas_.DrawGrid(16.0f);
|
||||
tilesheet_canvas_.DrawOverlay();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
DrawInventoryItemIcons();
|
||||
ImGui::TableNextColumn();
|
||||
DrawInventoryItemIcons();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
gui::DisplayPalette(palette_, create);
|
||||
ImGui::TableNextColumn();
|
||||
gui::DisplayPalette(palette_, create);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::Separator();
|
||||
|
||||
// TODO(scawful): Future Oracle of Secrets menu editor integration
|
||||
// - Full inventory screen layout editor
|
||||
// - Item slot assignment and positioning
|
||||
// - Heart container and magic meter editor
|
||||
// - Equipment display customization
|
||||
// - A/B button equipment quick-select editor
|
||||
// TODO(scawful): Future Oracle of Secrets menu editor integration
|
||||
// - Full inventory screen layout editor
|
||||
// - Item slot assignment and positioning
|
||||
// - Heart container and magic meter editor
|
||||
// - Equipment display customization
|
||||
// - A/B button equipment quick-select editor
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawInventoryToolset() {
|
||||
@@ -283,8 +311,9 @@ void ScreenEditor::DrawInventoryItemIcons() {
|
||||
|
||||
auto& icons = inventory_.item_icons();
|
||||
if (icons.empty()) {
|
||||
ImGui::TextWrapped("No item icons loaded. Icons will be loaded when the "
|
||||
"inventory is initialized.");
|
||||
ImGui::TextWrapped(
|
||||
"No item icons loaded. Icons will be loaded when the "
|
||||
"inventory is initialized.");
|
||||
ImGui::EndChild();
|
||||
return;
|
||||
}
|
||||
@@ -366,11 +395,12 @@ void ScreenEditor::DrawDungeonMapScreen(int i) {
|
||||
const int tiles_per_row = tile16_blockset_.atlas.width() / 16;
|
||||
const int tile_x = (tile16_id % tiles_per_row) * 16;
|
||||
const int tile_y = (tile16_id / tiles_per_row) * 16;
|
||||
|
||||
|
||||
std::vector<uint8_t> tile_data(16 * 16);
|
||||
int tile_data_offset = 0;
|
||||
tile16_blockset_.atlas.Get16x16Tile(tile_x, tile_y, tile_data, tile_data_offset);
|
||||
|
||||
tile16_blockset_.atlas.Get16x16Tile(tile_x, tile_y, tile_data,
|
||||
tile_data_offset);
|
||||
|
||||
// Create or update cached tile
|
||||
auto* cached_tile = tile16_blockset_.tile_cache.GetTile(tile16_id);
|
||||
if (!cached_tile) {
|
||||
@@ -383,7 +413,7 @@ void ScreenEditor::DrawDungeonMapScreen(int i) {
|
||||
// Update existing cached tile data
|
||||
cached_tile->set_data(tile_data);
|
||||
}
|
||||
|
||||
|
||||
if (cached_tile && cached_tile->is_active()) {
|
||||
// Ensure the cached tile has a valid texture
|
||||
if (!cached_tile->texture()) {
|
||||
@@ -488,14 +518,14 @@ void ScreenEditor::DrawDungeonMapsTabs() {
|
||||
|
||||
/**
|
||||
* @brief Draw dungeon room graphics editor with enhanced tile16 editing
|
||||
*
|
||||
*
|
||||
* Enhanced Features:
|
||||
* - Interactive tile16 selector with visual feedback
|
||||
* - Real-time tile16 composition from 4x 8x8 tiles
|
||||
* - Tile metadata editing (mirroring, palette, etc.)
|
||||
* - Integration with ROM graphics buffer
|
||||
* - Undo/redo support for tile modifications
|
||||
*
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Cached tile16 rendering to avoid repeated composition
|
||||
* - Efficient tile selector with grid-based snapping
|
||||
@@ -535,17 +565,18 @@ void ScreenEditor::DrawDungeonMapsRoomGfx() {
|
||||
ImGui::Separator();
|
||||
current_tile_canvas_.DrawBackground(); // ImVec2(64 * 2 + 2, 64 * 2 + 4));
|
||||
current_tile_canvas_.DrawContextMenu();
|
||||
|
||||
|
||||
// Get tile8 from cache on-demand (only create texture when needed)
|
||||
if (selected_tile8_ >= 0 && selected_tile8_ < 256) {
|
||||
auto* cached_tile8 = tile8_tilemap_.tile_cache.GetTile(selected_tile8_);
|
||||
|
||||
|
||||
if (!cached_tile8) {
|
||||
// Extract tile from atlas and cache it
|
||||
const int tiles_per_row = tile8_tilemap_.atlas.width() / 8; // 128 / 8 = 16
|
||||
const int tiles_per_row =
|
||||
tile8_tilemap_.atlas.width() / 8; // 128 / 8 = 16
|
||||
const int tile_x = (selected_tile8_ % tiles_per_row) * 8;
|
||||
const int tile_y = (selected_tile8_ / tiles_per_row) * 8;
|
||||
|
||||
|
||||
// Extract 8x8 tile data from atlas
|
||||
std::vector<uint8_t> tile_data(64);
|
||||
for (int py = 0; py < 8; py++) {
|
||||
@@ -554,28 +585,30 @@ void ScreenEditor::DrawDungeonMapsRoomGfx() {
|
||||
int src_y = tile_y + py;
|
||||
int src_index = src_y * tile8_tilemap_.atlas.width() + src_x;
|
||||
int dst_index = py * 8 + px;
|
||||
|
||||
|
||||
if (src_index < tile8_tilemap_.atlas.size() && dst_index < 64) {
|
||||
tile_data[dst_index] = tile8_tilemap_.atlas.data()[src_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gfx::Bitmap new_tile8(8, 8, 8, tile_data);
|
||||
new_tile8.SetPalette(tile8_tilemap_.atlas.palette());
|
||||
tile8_tilemap_.tile_cache.CacheTile(selected_tile8_, std::move(new_tile8));
|
||||
tile8_tilemap_.tile_cache.CacheTile(selected_tile8_,
|
||||
std::move(new_tile8));
|
||||
cached_tile8 = tile8_tilemap_.tile_cache.GetTile(selected_tile8_);
|
||||
}
|
||||
|
||||
|
||||
if (cached_tile8 && cached_tile8->is_active()) {
|
||||
// Create texture on-demand only when needed
|
||||
if (!cached_tile8->texture()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, cached_tile8);
|
||||
}
|
||||
|
||||
|
||||
if (current_tile_canvas_.DrawTilePainter(*cached_tile8, 16)) {
|
||||
// Modify the tile16 based on the selected tile and current_tile16_info
|
||||
// Modify the tile16 based on the selected tile and
|
||||
// current_tile16_info
|
||||
gfx::ModifyTile16(tile16_blockset_, rom()->graphics_buffer(),
|
||||
current_tile16_info[0], current_tile16_info[1],
|
||||
current_tile16_info[2], current_tile16_info[3], 212,
|
||||
@@ -618,14 +651,14 @@ void ScreenEditor::DrawDungeonMapsRoomGfx() {
|
||||
|
||||
/**
|
||||
* @brief Draw dungeon maps editor with enhanced ROM hacking features
|
||||
*
|
||||
*
|
||||
* Enhanced Features:
|
||||
* - Multi-mode editing (DRAW, EDIT, SELECT)
|
||||
* - Real-time tile16 preview and editing
|
||||
* - Floor/basement management for complex dungeons
|
||||
* - Copy/paste operations for floor layouts
|
||||
* - Integration with ROM tile16 data
|
||||
*
|
||||
*
|
||||
* Performance Notes:
|
||||
* - Lazy loading of dungeon graphics
|
||||
* - Cached tile16 rendering for fast updates
|
||||
@@ -775,13 +808,14 @@ void ScreenEditor::DrawTitleScreenEditor() {
|
||||
ImGui::Checkbox("Show BG1", &show_title_bg1_);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("Show BG2", &show_title_bg2_);
|
||||
|
||||
|
||||
// Re-render composite if visibility changed
|
||||
if (prev_bg1 != show_title_bg1_ || prev_bg2 != show_title_bg2_) {
|
||||
status_ = title_screen_.RenderCompositeLayer(show_title_bg1_, show_title_bg2_);
|
||||
status_ =
|
||||
title_screen_.RenderCompositeLayer(show_title_bg1_, show_title_bg2_);
|
||||
if (status_.ok()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&title_screen_.composite_bitmap());
|
||||
}
|
||||
}
|
||||
@@ -822,27 +856,30 @@ void ScreenEditor::DrawTitleScreenCompositeCanvas() {
|
||||
auto click_pos = title_bg1_canvas_.points().front();
|
||||
int tile_x = static_cast<int>(click_pos.x) / 8;
|
||||
int tile_y = static_cast<int>(click_pos.y) / 8;
|
||||
|
||||
|
||||
if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
|
||||
int tilemap_index = tile_y * 32 + tile_x;
|
||||
|
||||
|
||||
// Create tile word: tile_id | (palette << 10) | h_flip | v_flip
|
||||
uint16_t tile_word = selected_title_tile16_ & 0x3FF;
|
||||
tile_word |= (title_palette_ & 0x07) << 10;
|
||||
if (title_h_flip_) tile_word |= 0x4000;
|
||||
if (title_v_flip_) tile_word |= 0x8000;
|
||||
|
||||
if (title_h_flip_)
|
||||
tile_word |= 0x4000;
|
||||
if (title_v_flip_)
|
||||
tile_word |= 0x8000;
|
||||
|
||||
// Update BG1 buffer and re-render both layers and composite
|
||||
title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
|
||||
status_ = title_screen_.RenderBG1Layer();
|
||||
if (status_.ok()) {
|
||||
// Update BG1 texture
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&title_screen_.bg1_bitmap());
|
||||
|
||||
|
||||
// Re-render and update composite
|
||||
status_ = title_screen_.RenderCompositeLayer(show_title_bg1_, show_title_bg2_);
|
||||
status_ = title_screen_.RenderCompositeLayer(show_title_bg1_,
|
||||
show_title_bg2_);
|
||||
if (status_.ok()) {
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE, &composite_bitmap);
|
||||
@@ -874,16 +911,18 @@ void ScreenEditor::DrawTitleScreenBG1Canvas() {
|
||||
auto click_pos = title_bg1_canvas_.points().front();
|
||||
int tile_x = static_cast<int>(click_pos.x) / 8;
|
||||
int tile_y = static_cast<int>(click_pos.y) / 8;
|
||||
|
||||
|
||||
if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
|
||||
int tilemap_index = tile_y * 32 + tile_x;
|
||||
|
||||
|
||||
// Create tile word: tile_id | (palette << 10) | h_flip | v_flip
|
||||
uint16_t tile_word = selected_title_tile16_ & 0x3FF;
|
||||
tile_word |= (title_palette_ & 0x07) << 10;
|
||||
if (title_h_flip_) tile_word |= 0x4000;
|
||||
if (title_v_flip_) tile_word |= 0x8000;
|
||||
|
||||
if (title_h_flip_)
|
||||
tile_word |= 0x4000;
|
||||
if (title_v_flip_)
|
||||
tile_word |= 0x8000;
|
||||
|
||||
// Update buffer and re-render
|
||||
title_screen_.mutable_bg1_buffer()[tilemap_index] = tile_word;
|
||||
status_ = title_screen_.RenderBG1Layer();
|
||||
@@ -917,16 +956,18 @@ void ScreenEditor::DrawTitleScreenBG2Canvas() {
|
||||
auto click_pos = title_bg2_canvas_.points().front();
|
||||
int tile_x = static_cast<int>(click_pos.x) / 8;
|
||||
int tile_y = static_cast<int>(click_pos.y) / 8;
|
||||
|
||||
|
||||
if (tile_x >= 0 && tile_x < 32 && tile_y >= 0 && tile_y < 32) {
|
||||
int tilemap_index = tile_y * 32 + tile_x;
|
||||
|
||||
|
||||
// Create tile word: tile_id | (palette << 10) | h_flip | v_flip
|
||||
uint16_t tile_word = selected_title_tile16_ & 0x3FF;
|
||||
tile_word |= (title_palette_ & 0x07) << 10;
|
||||
if (title_h_flip_) tile_word |= 0x4000;
|
||||
if (title_v_flip_) tile_word |= 0x8000;
|
||||
|
||||
if (title_h_flip_)
|
||||
tile_word |= 0x4000;
|
||||
if (title_v_flip_)
|
||||
tile_word |= 0x8000;
|
||||
|
||||
// Update buffer and re-render
|
||||
title_screen_.mutable_bg2_buffer()[tilemap_index] = tile_word;
|
||||
status_ = title_screen_.RenderBG2Layer();
|
||||
@@ -971,20 +1012,19 @@ void ScreenEditor::DrawTitleScreenBlocksetSelector() {
|
||||
// Show selected tile preview and controls
|
||||
if (selected_title_tile16_ >= 0) {
|
||||
ImGui::Text("Selected Tile: %d", selected_title_tile16_);
|
||||
|
||||
|
||||
// Flip controls
|
||||
ImGui::Checkbox("H Flip", &title_h_flip_);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("V Flip", &title_v_flip_);
|
||||
|
||||
|
||||
// Palette selector (0-7 for 3BPP graphics)
|
||||
ImGui::SetNextItemWidth(100);
|
||||
ImGui::SliderInt("Palette", &title_palette_, 0, 7);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenEditor::DrawNamingScreenEditor() {
|
||||
}
|
||||
void ScreenEditor::DrawNamingScreenEditor() {}
|
||||
|
||||
void ScreenEditor::DrawOverworldMapEditor() {
|
||||
// Initialize overworld map on first draw
|
||||
@@ -1015,7 +1055,7 @@ void ScreenEditor::DrawOverworldMapEditor() {
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
// World toggle
|
||||
if (ImGui::Button(ow_show_dark_world_ ? "Dark World" : "Light World")) {
|
||||
ow_show_dark_world_ = !ow_show_dark_world_;
|
||||
@@ -1027,7 +1067,7 @@ void ScreenEditor::DrawOverworldMapEditor() {
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
|
||||
// Custom map load/save buttons
|
||||
if (ImGui::Button("Load Custom Map...")) {
|
||||
std::string path = util::FileDialogWrapper::ShowOpenFileDialog();
|
||||
@@ -1048,10 +1088,10 @@ void ScreenEditor::DrawOverworldMapEditor() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Selected Tile: %d", selected_ow_tile_);
|
||||
|
||||
|
||||
// Custom map error/success popups
|
||||
if (ImGui::BeginPopup("CustomMapLoadError")) {
|
||||
ImGui::Text("Error loading custom map: %s", status_.message().data());
|
||||
@@ -1093,17 +1133,17 @@ void ScreenEditor::DrawOverworldMapEditor() {
|
||||
auto click_pos = ow_map_canvas_.points().front();
|
||||
int tile_x = static_cast<int>(click_pos.x) / 8;
|
||||
int tile_y = static_cast<int>(click_pos.y) / 8;
|
||||
|
||||
|
||||
if (tile_x >= 0 && tile_x < 64 && tile_y >= 0 && tile_y < 64) {
|
||||
int tile_index = tile_x + (tile_y * 64);
|
||||
|
||||
|
||||
// Update appropriate world's tile data
|
||||
if (ow_show_dark_world_) {
|
||||
ow_map_screen_.mutable_dw_tiles()[tile_index] = selected_ow_tile_;
|
||||
} else {
|
||||
ow_map_screen_.mutable_lw_tiles()[tile_index] = selected_ow_tile_;
|
||||
}
|
||||
|
||||
|
||||
// Re-render map
|
||||
status_ = ow_map_screen_.RenderMapLayer(ow_show_dark_world_);
|
||||
if (status_.ok()) {
|
||||
@@ -1143,7 +1183,7 @@ void ScreenEditor::DrawOverworldMapEditor() {
|
||||
|
||||
// Column 3: Palette Display
|
||||
ImGui::TableNextColumn();
|
||||
auto& palette = ow_show_dark_world_ ? ow_map_screen_.dw_palette()
|
||||
auto& palette = ow_show_dark_world_ ? ow_map_screen_.dw_palette()
|
||||
: ow_map_screen_.lw_palette();
|
||||
// Use inline palette editor for full 128-color palette
|
||||
gui::InlinePaletteEditor(palette, "Overworld Map Palette");
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
#include "absl/status/status.h"
|
||||
#include "app/editor/editor.h"
|
||||
#include "app/gfx/core/bitmap.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gfx/render/tilemap.h"
|
||||
#include "app/gfx/types/snes_palette.h"
|
||||
#include "app/gui/app/editor_layout.h"
|
||||
#include "app/gui/canvas/canvas.h"
|
||||
#include "app/rom.h"
|
||||
#include "imgui/imgui.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"
|
||||
#include "zelda3/screen/title_screen.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace editor {
|
||||
@@ -120,8 +120,7 @@ class ScreenEditor : public Editor {
|
||||
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::Canvas title_blockset_canvas_{"##TitleBlocksetCanvas", ImVec2(128, 512),
|
||||
gui::CanvasGridSize::k8x8, 2.0f};
|
||||
|
||||
zelda3::Inventory inventory_;
|
||||
|
||||
Reference in New Issue
Block a user