yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
room_graphics_content.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <cstdint>
6#include <vector>
7
13#include "imgui/imgui.h"
14
15namespace yaze {
16namespace editor {
17
19 for (auto& preview : sheet_previews_) {
21 }
22}
23
25 const auto blocks = room.blocks();
26 if (blocks.size() < sheet_previews_.size()) {
28 return;
29 }
30
31 bool needs_refresh = palette_dirty_ || !preview_cache_valid_ ||
32 preview_room_id_ != room.id() ||
34 if (!needs_refresh) {
35 for (size_t i = 0; i < preview_block_ids_.size(); ++i) {
36 if (preview_block_ids_[i] != blocks[i]) {
37 needs_refresh = true;
38 break;
39 }
40 }
41 }
42 if (!needs_refresh) {
43 return;
44 }
45
46 std::vector<SDL_Color> palette_colors;
47 if (SDL_Surface* bg_surface = room.bg1_buffer().bitmap().surface()) {
48 if (SDL_Palette* palette = platform::GetSurfacePalette(bg_surface)) {
49 palette_colors.assign(palette->colors,
50 palette->colors + palette->ncolors);
51 }
52 }
53
54 const auto& gfx_buffer = room.get_gfx_buffer();
55 constexpr size_t kSheetBytes = 128 * 32;
56 for (size_t i = 0; i < sheet_previews_.size(); ++i) {
57 preview_block_ids_[i] = blocks[i];
58 const size_t offset = i * kSheetBytes;
59 auto& meta = sheet_preview_metadata_[i];
60 meta = {};
61 meta.block_id = blocks[i];
62 meta.source_offset = offset;
63 if (offset + kSheetBytes > gfx_buffer.size()) {
66 continue;
67 }
68
69 std::vector<uint8_t> sheet_pixels(
70 gfx_buffer.begin() + offset, gfx_buffer.begin() + offset + kSheetBytes);
71 meta.nonzero_pixels = static_cast<int>(
72 std::count_if(sheet_pixels.begin(), sheet_pixels.end(),
73 [](uint8_t value) { return value != 0; }));
74 sheet_previews_[i].Create(128, 32, 8,
75 static_cast<int>(gfx::BitmapFormat::kIndexed),
76 sheet_pixels);
77 if (!palette_colors.empty()) {
78 sheet_previews_[i].SetPalette(palette_colors);
79 }
80 // Bitmap::Create keeps an existing texture and advances its generation,
81 // superseding pending commands. Updating preserves handles that may already
82 // appear in this frame's draw list instead of destroying them during Draw.
83 if (sheet_previews_[i].texture() != nullptr) {
84 sheet_previews_[i].UpdateTexture();
85 } else {
86 sheet_previews_[i].CreateTexture();
87 }
88 meta.width = sheet_previews_[i].width();
89 meta.height = sheet_previews_[i].height();
90 meta.bitmap_active = sheet_previews_[i].is_active();
91 meta.has_surface = sheet_previews_[i].surface() != nullptr;
92 meta.palette_colors = static_cast<int>(palette_colors.size());
93 meta.has_texture = sheet_previews_[i].texture() != nullptr;
94 }
95
96 preview_room_id_ = room.id();
99 palette_dirty_ = false;
100}
101
102void RoomGraphicsContent::Draw(bool* p_open) {
103 (void)p_open;
104 // Resolve the active dungeon editor lazily when this panel is hosted through
105 // the workspace window system instead of the legacy direct constructor.
106 if (current_room_id_ == nullptr || rooms_ == nullptr) {
107 const auto ctx = CurrentDungeonWindowContext();
108 if (ctx) {
109 current_room_id_ = ctx.editor->mutable_current_room_id();
110 rooms_ = &ctx.editor->rooms();
111 renderer_ = ctx.editor->renderer();
112 }
113 }
114
115 if (current_room_id_ == nullptr || rooms_ == nullptr) {
116 ImGui::TextDisabled(tr("No room data available"));
117 return;
118 }
119
120 const int active_room_id = *current_room_id_;
121 if (active_room_id < 0 ||
122 active_room_id >= static_cast<int>(rooms_->size())) {
123 ImGui::TextDisabled(tr("Invalid room ID: %d"), active_room_id);
124 return;
125 }
126
127 auto& room = (*rooms_)[active_room_id];
128 bool needs_render = false;
129 if (room.blocks().empty()) {
130 room.LoadRoomGraphics();
131 needs_render = true;
132 }
133 if (!room.AreObjectsLoaded()) {
134 room.LoadObjects();
135 needs_render = true;
136 }
137 auto& bg1_bitmap = room.bg1_buffer().bitmap();
138 if (needs_render || !bg1_bitmap.is_active() || bg1_bitmap.width() == 0) {
139 room.RenderRoomGraphics();
140 }
141 // Keep room-sheet assignments in sync with the active room header.
142 room.LoadRoomGraphics();
144
145 if (renderer_ != nullptr) {
147 }
148 auto blocks = room.blocks();
149
150 constexpr float kBlockWidth = 128.0f;
151 constexpr float kBlockHeight = 32.0f;
152 constexpr int kBlocksPerRow = 2;
153 constexpr float kPadding = 4.0f;
154
155 const int block_count = static_cast<int>(blocks.size());
156 const int row_count =
157 std::max(1, (block_count + kBlocksPerRow - 1) / kBlocksPerRow);
158 const ImVec2 canvas_size(
159 std::max(ImGui::GetContentRegionAvail().x,
160 kPadding + (kBlockWidth + kPadding) *
161 static_cast<float>(kBlocksPerRow)),
162 kPadding + (kBlockHeight + kPadding) * static_cast<float>(row_count));
163
164 ImGui::Text(tr("Room %03X Graphics Blocks"), active_room_id);
165 ImGui::TextDisabled(tr("Blockset %02X | Spriteset %02X"), room.blockset(),
166 room.spriteset());
167 ImGui::SameLine();
168 ImGui::Checkbox(tr("Source Trace"), &show_source_trace_);
169 ImGui::Separator();
170
171 gui::CanvasFrameOptions frame_opts;
172 frame_opts.canvas_size = canvas_size;
173 frame_opts.draw_grid = false;
174 frame_opts.draw_overlay = false;
175 frame_opts.draw_context_menu = false;
176 frame_opts.render_popups = false;
177
178 auto rt = gui::BeginCanvas(room_gfx_canvas_, frame_opts);
179
180 const float grid_width =
181 kPadding + (kBlockWidth + kPadding) * static_cast<float>(kBlocksPerRow);
182 const float x_offset = std::max(0.0f, (canvas_size.x - grid_width) * 0.5f);
183 ImDrawList* draw_list = ImGui::GetWindowDrawList();
184 for (int i = 0; i < block_count; ++i) {
185 const uint8_t block_id = blocks[static_cast<size_t>(i)];
186 const int row = i / kBlocksPerRow;
187 const int col = i % kBlocksPerRow;
188 const ImVec2 local_pos(x_offset + kPadding + col * (kBlockWidth + kPadding),
189 kPadding + row * (kBlockHeight + kPadding));
190
191 if (i < static_cast<int>(sheet_previews_.size()) &&
192 sheet_previews_[static_cast<size_t>(i)].texture() != 0) {
193 gui::BitmapPreviewOptions preview_opts;
194 preview_opts.dest_pos = local_pos;
195 preview_opts.dest_size = ImVec2(kBlockWidth, kBlockHeight);
196 preview_opts.draw_context_menu = false;
197 preview_opts.draw_grid = false;
198 preview_opts.draw_overlay = false;
199 preview_opts.render_popups = false;
200 preview_opts.ensure_texture = true;
201 gui::DrawBitmapPreview(rt, sheet_previews_[static_cast<size_t>(i)],
202 preview_opts);
203 } else {
204 const ImVec2 zero = room_gfx_canvas_.zero_point();
205 const float scale = room_gfx_canvas_.global_scale();
206 const ImVec2 screen_pos(zero.x + local_pos.x, zero.y + local_pos.y);
207 const ImVec2 screen_end(screen_pos.x + kBlockWidth * scale,
208 screen_pos.y + kBlockHeight * scale);
209 draw_list->AddRect(screen_pos, screen_end,
210 ImGui::GetColorU32(gui::GetOutlineVec4()));
211 draw_list->AddText(ImVec2(screen_pos.x + 6.0f, screen_pos.y + 6.0f),
212 ImGui::GetColorU32(gui::GetTextSecondaryVec4()),
213 block_id == 0 ? "Empty" : "Missing");
214 }
215 }
216
217 gui::EndCanvas(room_gfx_canvas_, rt, frame_opts);
218
219 if (show_source_trace_ &&
220 ImGui::CollapsingHeader(ICON_MD_BUG_REPORT " Source Trace",
221 ImGuiTreeNodeFlags_DefaultOpen)) {
222 ImGui::TextDisabled(tr(
223 "Source: Room::get_gfx_buffer() -> preview bitmap -> queued texture"));
224 ImGui::TextDisabled(tr("Room buffer bytes: %zu"),
225 room.get_gfx_buffer().size());
226 ImGui::Separator();
227 for (int i = 0;
228 i < std::min(block_count,
229 static_cast<int>(sheet_preview_metadata_.size()));
230 ++i) {
231 const auto& meta = sheet_preview_metadata_[static_cast<size_t>(i)];
232 const uintptr_t texture_value = reinterpret_cast<uintptr_t>(
233 sheet_previews_[static_cast<size_t>(i)].texture());
234 ImGui::PushID(i);
235 ImGui::Text(
236 tr("Slot %02d | Block %02X | Buf +0x%04zX | %dx%d | nz=%d | pal=%d | "
237 "active=%d surf=%d tex=%d (0x%zx)"),
238 i, meta.block_id, meta.source_offset, meta.width, meta.height,
239 meta.nonzero_pixels, meta.palette_colors, meta.bitmap_active,
240 meta.has_surface, meta.has_texture, texture_value);
241 ImGui::PopID();
242 }
243 }
244}
245
246} // namespace editor
247} // namespace yaze
std::array< uint8_t, 16 > preview_block_ids_
void RefreshSheetPreviews(const zelda3::Room &room)
void Draw(bool *p_open) override
Draw the panel content.
std::array< gfx::Bitmap, 16 > sheet_previews_
std::array< SheetPreviewMetadata, 16 > sheet_preview_metadata_
void RetireBitmap(Bitmap &bitmap)
Explicitly retire resources owned by a bitmap that is being erased.
Definition arena.cc:63
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:211
static Arena & Get()
Definition arena.cc:24
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:69
auto global_scale() const
Definition canvas.h:403
auto zero_point() const
Definition canvas.h:354
const std::array< uint8_t, 0x10000 > & get_gfx_buffer() const
Definition room.h:1019
uint64_t graphics_revision() const
Definition room.h:1024
auto & bg1_buffer()
Definition room.h:1039
auto blocks() const
Definition room.h:1005
int id() const
Definition room.h:948
#define ICON_MD_BUG_REPORT
Definition icons.h:327
DungeonWindowContext CurrentDungeonWindowContext()
@ kIndexed
Definition bitmap.h:38
void EndCanvas(Canvas &canvas)
void DrawBitmapPreview(const CanvasRuntime &rt, gfx::Bitmap &bitmap, const BitmapPreviewOptions &options)
void BeginCanvas(Canvas &canvas, ImVec2 child_size)
ImVec4 GetTextSecondaryVec4()
ImVec4 GetOutlineVec4()
SDL_Palette * GetSurfacePalette(SDL_Surface *surface)
Get the palette attached to a surface.
Definition sdl_compat.h:392
SDL2/SDL3 compatibility layer.