yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
gfx_group_editor.cc
Go to the documentation of this file.
1#include "gfx_group_editor.h"
2
3#include "absl/status/status.h"
4#include "absl/strings/str_cat.h"
5#include "absl/strings/str_format.h"
10#include "app/gui/core/color.h"
11#include "app/gui/core/icons.h"
12#include "app/gui/core/input.h"
13#include "imgui/imgui.h"
14#include "rom/rom.h"
15
16namespace yaze {
17namespace editor {
18
19using ImGui::BeginChild;
20using ImGui::BeginCombo;
21using ImGui::BeginGroup;
22using ImGui::BeginTabBar;
23using ImGui::BeginTabItem;
24using ImGui::BeginTable;
25using ImGui::EndChild;
26using ImGui::EndCombo;
27using ImGui::EndGroup;
28using ImGui::EndTabBar;
29using ImGui::EndTabItem;
30using ImGui::EndTable;
31using ImGui::GetContentRegionAvail;
32using ImGui::GetStyle;
33using ImGui::IsItemClicked;
34using ImGui::PopID;
35using ImGui::PushID;
36using ImGui::SameLine;
37using ImGui::Selectable;
38using ImGui::Separator;
39using ImGui::SetNextItemWidth;
40using ImGui::SliderFloat;
41using ImGui::TableHeadersRow;
42using ImGui::TableNextColumn;
43using ImGui::TableNextRow;
44using ImGui::TableSetupColumn;
45using ImGui::Text;
46
47using gfx::kPaletteGroupNames;
49
50namespace {
51
52// Constants for sheet display
53constexpr int kSheetDisplayWidth = 256; // 2x scale from 128px sheets
54constexpr int kSheetDisplayHeight = 64; // 2x scale from 32px sheets
55constexpr float kDefaultScale = 2.0f;
56constexpr int kTileSize = 16; // 8px tiles at 2x scale
57
58// Draw a single sheet with proper scaling and unique ID
59void DrawScaledSheet(gui::Canvas& canvas, gfx::Bitmap& sheet, int unique_id,
60 float scale = kDefaultScale) {
61 PushID(unique_id);
62
63 // Calculate scaled dimensions
64 int display_width = static_cast<int>(gfx::kTilesheetWidth * scale);
65 int display_height = static_cast<int>(gfx::kTilesheetHeight * scale);
66
67 // Draw canvas background
68 canvas.DrawBackground(ImVec2(display_width + 1, display_height + 1));
69 canvas.DrawContextMenu();
70
71 // Draw bitmap with proper scale
72 canvas.DrawBitmap(sheet, 2, scale);
73
74 // Draw grid at scaled tile size
75 canvas.DrawGrid(static_cast<int>(8 * scale));
76 canvas.DrawOverlay();
77
78 PopID();
79}
80
81} // namespace
82
83absl::Status GfxGroupEditor::Update() {
84 // Palette controls at top for all tabs
86 Separator();
87
88 if (BeginTabBar("##GfxGroupEditorTabs")) {
89 if (BeginTabItem("Blocksets")) {
90 gui::InputHexByte("Selected Blockset", &selected_blockset_,
91 static_cast<uint8_t>(0x24));
93 false, "blockset", "0x" + std::to_string(selected_blockset_),
94 "Blockset " + std::to_string(selected_blockset_));
96 EndTabItem();
97 }
98
99 if (BeginTabItem("Roomsets")) {
100 gui::InputHexByte("Selected Roomset", &selected_roomset_,
101 static_cast<uint8_t>(81));
103 false, "roomset", "0x" + std::to_string(selected_roomset_),
104 "Roomset " + std::to_string(selected_roomset_));
106 EndTabItem();
107 }
108
109 if (BeginTabItem("Spritesets")) {
110 gui::InputHexByte("Selected Spriteset", &selected_spriteset_,
111 static_cast<uint8_t>(143));
113 false, "spriteset", "0x" + std::to_string(selected_spriteset_),
114 "Spriteset " + std::to_string(selected_spriteset_));
116 EndTabItem();
117 }
118
119 EndTabBar();
120 }
121
122 return absl::OkStatus();
123}
124
126 if (!game_data()) {
127 Text("No game data loaded");
128 return;
129 }
130
131 PushID("BlocksetViewer");
132
133 if (BeginTable("##BlocksetTable", sheet_only ? 1 : 2,
134 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
135 ImVec2(0, 0))) {
136 if (!sheet_only) {
137 TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
138 GetContentRegionAvail().x);
139 }
140
141 TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed,
142 kSheetDisplayWidth + 16);
143 TableHeadersRow();
144 TableNextRow();
145
146 if (!sheet_only) {
147 TableNextColumn();
148 BeginGroup();
149 for (int idx = 0; idx < 8; idx++) {
150 SetNextItemWidth(100.f);
152 ("Slot " + std::to_string(idx)).c_str(),
153 &game_data()->main_blockset_ids[selected_blockset_][idx]);
154 }
155 EndGroup();
156 }
157
158 TableNextColumn();
159 BeginGroup();
160 for (int idx = 0; idx < 8; idx++) {
161 int sheet_id = game_data()->main_blockset_ids[selected_blockset_][idx];
162 auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
163
164 // Apply current palette if selected
166 sheet.SetPalette(*current_palette_);
168 }
169
170 // Unique ID combining blockset, slot, and sheet
171 int unique_id = (selected_blockset_ << 16) | (idx << 8) | sheet_id;
172 DrawScaledSheet(blockset_canvases_[idx], sheet, unique_id, view_scale_);
173 }
174 EndGroup();
175 EndTable();
176 }
177
178 PopID();
179}
180
182 if (!game_data()) {
183 Text("No game data loaded");
184 return;
185 }
186
187 PushID("RoomsetViewer");
188 Text("Roomsets overwrite slots 4-7 of the main blockset");
189
190 if (BeginTable("##RoomsTable", 3,
191 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
192 ImVec2(0, 0))) {
193 TableSetupColumn("List", ImGuiTableColumnFlags_WidthFixed, 120);
194 TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
195 GetContentRegionAvail().x);
196 TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed,
197 kSheetDisplayWidth + 16);
198 TableHeadersRow();
199 TableNextRow();
200
201 // Roomset list column
202 TableNextColumn();
203 if (BeginChild("##RoomsetListChild", ImVec2(0, 300))) {
204 for (int idx = 0; idx < 0x51; idx++) {
205 PushID(idx);
206 std::string roomset_label = absl::StrFormat("0x%02X", idx);
207 bool is_selected = (selected_roomset_ == idx);
208 if (Selectable(roomset_label.c_str(), is_selected)) {
209 selected_roomset_ = idx;
210 }
211 PopID();
212 }
213 }
214 EndChild();
215
216 // Inputs column
217 TableNextColumn();
218 BeginGroup();
219 Text("Sheet IDs (overwrites slots 4-7):");
220 for (int idx = 0; idx < 4; idx++) {
221 SetNextItemWidth(100.f);
223 ("Slot " + std::to_string(idx + 4)).c_str(),
224 &game_data()->room_blockset_ids[selected_roomset_][idx]);
225 }
226 EndGroup();
227
228 // Sheets column
229 TableNextColumn();
230 BeginGroup();
231 for (int idx = 0; idx < 4; idx++) {
232 int sheet_id = game_data()->room_blockset_ids[selected_roomset_][idx];
233 auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
234
235 // Apply current palette if selected
237 sheet.SetPalette(*current_palette_);
239 }
240
241 // Unique ID combining roomset, slot, and sheet
242 int unique_id =
243 (0x1000) | (selected_roomset_ << 8) | (idx << 4) | sheet_id;
244 DrawScaledSheet(roomset_canvases_[idx], sheet, unique_id, view_scale_);
245 }
246 EndGroup();
247 EndTable();
248 }
249
250 PopID();
251}
252
254 if (!game_data()) {
255 Text("No game data loaded");
256 return;
257 }
258
259 PushID("SpritesetViewer");
260
261 if (BeginTable("##SpritesTable", sheet_only ? 1 : 2,
262 ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable,
263 ImVec2(0, 0))) {
264 if (!sheet_only) {
265 TableSetupColumn("Inputs", ImGuiTableColumnFlags_WidthStretch,
266 GetContentRegionAvail().x);
267 }
268 TableSetupColumn("Sheets", ImGuiTableColumnFlags_WidthFixed,
269 kSheetDisplayWidth + 16);
270 TableHeadersRow();
271 TableNextRow();
272
273 if (!sheet_only) {
274 TableNextColumn();
275 BeginGroup();
276 Text("Sprite sheet IDs (base 115+):");
277 for (int idx = 0; idx < 4; idx++) {
278 SetNextItemWidth(100.f);
280 ("Slot " + std::to_string(idx)).c_str(),
281 &game_data()->spriteset_ids[selected_spriteset_][idx]);
282 }
283 EndGroup();
284 }
285
286 TableNextColumn();
287 BeginGroup();
288 for (int idx = 0; idx < 4; idx++) {
289 int sheet_offset = game_data()->spriteset_ids[selected_spriteset_][idx];
290 int sheet_id = 115 + sheet_offset;
291 auto& sheet = gfx::Arena::Get().mutable_gfx_sheets()->at(sheet_id);
292
293 // Apply current palette if selected
295 sheet.SetPalette(*current_palette_);
297 }
298
299 // Unique ID combining spriteset, slot, and sheet
300 int unique_id =
301 (0x2000) | (selected_spriteset_ << 8) | (idx << 4) | sheet_offset;
302 DrawScaledSheet(spriteset_canvases_[idx], sheet, unique_id, view_scale_);
303 }
304 EndGroup();
305 EndTable();
306 }
307
308 PopID();
309}
310
311namespace {
313 if (palette.empty()) {
314 return;
315 }
316 for (size_t color_idx = 0; color_idx < palette.size(); color_idx++) {
317 PushID(static_cast<int>(color_idx));
318 if ((color_idx % 8) != 0) {
319 SameLine(0.0f, GetStyle().ItemSpacing.y);
320 }
321
322 // Small icon of the color in the palette
323 gui::SnesColorButton(absl::StrCat("Palette", color_idx), palette[color_idx],
324 ImGuiColorEditFlags_NoAlpha |
325 ImGuiColorEditFlags_NoPicker |
326 ImGuiColorEditFlags_NoTooltip);
327
328 PopID();
329 }
330}
331} // namespace
332
334 if (!game_data()) {
335 return;
336 }
337
338 // View scale control
339 Text(ICON_MD_ZOOM_IN " View");
340 SameLine();
341 SetNextItemWidth(100.f);
342 SliderFloat("##ViewScale", &view_scale_, 1.0f, 4.0f, "%.1fx");
343 SameLine();
344
345 // Palette category selector
346 Text(ICON_MD_PALETTE " Palette");
347 SameLine();
348 SetNextItemWidth(150.f);
349
350 // Use the category names array for display
351 static constexpr int kNumPaletteCategories = 14;
352 if (BeginCombo(
353 "##PaletteCategory",
354 gfx::kPaletteCategoryNames[selected_palette_category_].data())) {
355 for (int cat = 0; cat < kNumPaletteCategories; cat++) {
356 auto category = static_cast<PaletteCategory>(cat);
357 bool is_selected = (selected_palette_category_ == category);
358 if (Selectable(gfx::kPaletteCategoryNames[category].data(),
359 is_selected)) {
363 }
364 if (is_selected) {
365 ImGui::SetItemDefaultFocus();
366 }
367 }
368 EndCombo();
369 }
370
371 SameLine();
372 SetNextItemWidth(80.f);
373 if (gui::InputHexByte("##PaletteIndex", &selected_palette_index_)) {
375 }
376
377 SameLine();
378 ImGui::Checkbox("Apply", &use_custom_palette_);
379 if (ImGui::IsItemHovered()) {
380 ImGui::SetTooltip("Apply selected palette to sheet previews");
381 }
382
383 // Show current palette preview
385 SameLine();
386 DrawPaletteFromPaletteGroup(*current_palette_);
387 }
388}
389
391 if (!game_data()) {
392 current_palette_ = nullptr;
393 return;
394 }
395
396 auto& groups = game_data()->palette_groups;
398 case PaletteCategory::kSword:
399 current_palette_ = groups.swords.mutable_palette(selected_palette_index_);
400 break;
401 case PaletteCategory::kShield:
403 groups.shields.mutable_palette(selected_palette_index_);
404 break;
405 case PaletteCategory::kClothes:
406 current_palette_ = groups.armors.mutable_palette(selected_palette_index_);
407 break;
408 case PaletteCategory::kWorldColors:
410 groups.overworld_main.mutable_palette(selected_palette_index_);
411 break;
412 case PaletteCategory::kAreaColors:
414 groups.overworld_aux.mutable_palette(selected_palette_index_);
415 break;
416 case PaletteCategory::kGlobalSprites:
418 groups.global_sprites.mutable_palette(selected_palette_index_);
419 break;
420 case PaletteCategory::kSpritesAux1:
422 groups.sprites_aux1.mutable_palette(selected_palette_index_);
423 break;
424 case PaletteCategory::kSpritesAux2:
426 groups.sprites_aux2.mutable_palette(selected_palette_index_);
427 break;
428 case PaletteCategory::kSpritesAux3:
430 groups.sprites_aux3.mutable_palette(selected_palette_index_);
431 break;
432 case PaletteCategory::kDungeons:
434 groups.dungeon_main.mutable_palette(selected_palette_index_);
435 break;
436 case PaletteCategory::kWorldMap:
437 case PaletteCategory::kDungeonMap:
439 groups.overworld_mini_map.mutable_palette(selected_palette_index_);
440 break;
441 case PaletteCategory::kTriforce:
442 case PaletteCategory::kCrystal:
444 groups.object_3d.mutable_palette(selected_palette_index_);
445 break;
446 default:
447 current_palette_ = nullptr;
448 break;
449 }
450}
451
452} // namespace editor
453} // namespace yaze
project::ResourceLabelManager * resource_label()
Definition rom.h:146
std::array< gui::Canvas, 8 > blockset_canvases_
zelda3::GameData * game_data() const
gfx::PaletteCategory selected_palette_category_
std::array< gui::Canvas, 4 > roomset_canvases_
gfx::SnesPalette * current_palette_
void DrawSpritesetViewer(bool sheet_only=false)
std::array< gui::Canvas, 4 > spriteset_canvases_
void DrawBlocksetViewer(bool sheet_only=false)
auto mutable_gfx_sheets()
Get mutable reference to all graphics sheets.
Definition arena.h:128
void NotifySheetModified(int sheet_index)
Notify Arena that a graphics sheet has been modified.
Definition arena.cc:323
static Arena & Get()
Definition arena.cc:20
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:67
Represents a palette of colors for the Super Nintendo Entertainment System (SNES).
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:150
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1075
void DrawContextMenu()
Definition canvas.cc:602
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0))
Definition canvas.cc:547
void DrawGrid(float grid_step=64.0f, int tile_id_offset=8)
Definition canvas.cc:1398
#define ICON_MD_ZOOM_IN
Definition icons.h:2194
#define ICON_MD_PALETTE
Definition icons.h:1370
void DrawScaledSheet(gui::Canvas &canvas, gfx::Bitmap &sheet, int unique_id, float scale=kDefaultScale)
PaletteCategory
Categories for organizing palette groups in the UI.
constexpr int kTilesheetHeight
Definition snes_tile.h:17
constexpr int kTilesheetWidth
Definition snes_tile.h:16
IMGUI_API bool SnesColorButton(absl::string_view id, gfx::SnesColor &color, ImGuiColorEditFlags flags, const ImVec2 &size_arg)
Definition color.cc:38
bool InputHexByte(const char *label, uint8_t *data, float input_width, bool no_step)
Definition input.cc:370
void SelectableLabelWithNameEdit(bool selected, const std::string &type, const std::string &key, const std::string &defaultValue)
Definition project.cc:1310
std::array< std::array< uint8_t, 4 >, kNumSpritesets > spriteset_ids
Definition game_data.h:93
std::array< std::array< uint8_t, 4 >, kNumRoomBlocksets > room_blockset_ids
Definition game_data.h:92
gfx::PaletteGroupMap palette_groups
Definition game_data.h:89
std::array< std::array< uint8_t, 8 >, kNumMainBlocksets > main_blockset_ids
Definition game_data.h:91