yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_canvas_room_render.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdio>
5
11#include "imgui/imgui.h"
12
13namespace yaze::editor {
14
15namespace {
16
17ImVec4 WithAlpha(ImVec4 color, float alpha) {
18 color.w = alpha;
19 return color;
20}
21
23 if (!canvas_rt.draw_list) {
24 return;
25 }
26
27 const auto& theme = AgentUI::GetTheme();
28 const float scale = std::max(canvas_rt.scale, 0.01f);
29 const ImVec2 room_min(canvas_rt.canvas_p0.x + canvas_rt.scrolling.x,
30 canvas_rt.canvas_p0.y + canvas_rt.scrolling.y);
31 const ImVec2 room_max(room_min.x + (512.0f * scale),
32 room_min.y + (512.0f * scale));
33 const ImVec2 canvas_max(canvas_rt.canvas_p0.x + canvas_rt.canvas_sz.x,
34 canvas_rt.canvas_p0.y + canvas_rt.canvas_sz.y);
35
36 canvas_rt.draw_list->PushClipRect(canvas_rt.canvas_p0, canvas_max, true);
37 canvas_rt.draw_list->AddRectFilled(
38 room_min, room_max,
39 ImGui::GetColorU32(WithAlpha(theme.editor_background, 0.92f)));
40 canvas_rt.draw_list->AddRect(room_min, room_max,
41 ImGui::GetColorU32(theme.panel_border_color),
42 0.0f, 0, 1.5f);
43 canvas_rt.draw_list->AddText(ImVec2(room_min.x + 12.0f, room_min.y + 12.0f),
44 ImGui::GetColorU32(theme.text_secondary_gray),
45 "Room image pending");
46 canvas_rt.draw_list->PopClipRect();
47}
48
49} // namespace
50
52 const gui::CanvasRuntime& canvas_rt) {
54 canvas_rt.canvas_sz.x > 1.0f && canvas_rt.canvas_sz.y > 1.0f;
56 return;
57 }
58
59 canvas_capture_x_ = static_cast<int>(canvas_rt.canvas_p0.x);
60 canvas_capture_y_ = static_cast<int>(canvas_rt.canvas_p0.y);
61 canvas_capture_width_ = static_cast<int>(canvas_rt.canvas_sz.x);
62 canvas_capture_height_ = static_cast<int>(canvas_rt.canvas_sz.y);
63}
64
66 const gui::CanvasRuntime& canvas_rt) {
67 if (!pending_scroll_target_.has_value()) {
68 return;
69 }
70
71 constexpr int kRoomPixelWidth = 512;
72 constexpr int kRoomPixelHeight = 512;
73 constexpr int kDungeonTileSize = 8;
74
75 const auto [target_x, target_y] = pending_scroll_target_.value();
76 float scale = canvas_.global_scale();
77 if (scale <= 0.0f) {
78 scale = 1.0f;
79 }
80
81 const float pixel_x = static_cast<float>(target_x * kDungeonTileSize) * scale;
82 const float pixel_y = static_cast<float>(target_y * kDungeonTileSize) * scale;
83 const ImVec2 view_size = canvas_rt.canvas_sz;
84 const ImVec2 content_size(static_cast<float>(kRoomPixelWidth) * scale,
85 static_cast<float>(kRoomPixelHeight) * scale);
86
87 const ImVec2 desired_scroll((view_size.x * 0.5f) - pixel_x,
88 (view_size.y * 0.5f) - pixel_y);
90 gui::ClampScroll(desired_scroll, content_size, view_size));
92}
93
96 return;
97 }
98
99 const auto& label = zelda3::GetRoomLabel(room_id);
100 char text1[160];
101 snprintf(text1, sizeof(text1), "[%03X] %s", room_id, label.c_str());
102
103 char text2[96] = {};
104 bool show_meta = false;
105 if (rooms_ && room_id >= 0 && room_id < static_cast<int>(rooms_->size())) {
106 if (const auto* room = rooms_->TryEnsureRoom(room_id)) {
108 snprintf(text2, sizeof(text2), "B:%02X P:%02X L:%02X S:%02X RO",
109 room->blockset(), room->palette(), room->layout_id(),
110 room->spriteset());
111 } else {
112 snprintf(text2, sizeof(text2), "B:%02X P:%02X L:%02X S:%02X",
113 room->blockset(), room->palette(), room->layout_id(),
114 room->spriteset());
115 }
116 show_meta = true;
117 }
118 } else if (!object_interaction_enabled_) {
119 snprintf(text2, sizeof(text2), "Read-only");
120 show_meta = true;
121 }
122
123 const float pad = 10.0f;
124 const ImVec2 hud_pos(canvas_.zero_point().x + pad,
125 canvas_.zero_point().y + pad);
126 const ImVec2 hud_size(0, 0);
127
128 gui::DrawCanvasHUD("##MetadataHUD", hud_pos, hud_size, [&]() {
129 ImGui::TextUnformatted(text1);
130 if (show_meta) {
131 ImGui::TextDisabled("%s", text2);
132 }
133 });
134}
135
137 int room_id) {
139
140 if (!room.AreObjectsLoaded()) {
141 room.LoadObjects();
142 }
143
144 if (!room.AreSpritesLoaded()) {
145 room.LoadSprites();
146 }
147
148 if (!room.ArePotItemsLoaded()) {
149 room.LoadPotItems();
150 }
151
152 // Existing room buffers can still be stale after palette, object, or layout
153 // edits. Let Room's dirty-state-aware preparation decide whether the cached
154 // graphics need to be rebuilt instead of treating an allocated BG1 surface
155 // as proof that the room is current.
157
158 if (rom_ && rom_->is_loaded()) {
160 }
161}
162
164 int room_id) {
166 if (gui::AcceptRoomObjectDrop(&obj_drop)) {
167 const DungeonCanvasTransform transform(
169 auto [tile_x, tile_y] = transform.ScreenToRoomTiles(
170 ImGui::GetMousePos(), dungeon_coords::kTileSize);
171 if (tile_x >= 0 && tile_x < 64 && tile_y >= 0 && tile_y < 64) {
172 const uint8_t object_size = obj_drop.size;
173 zelda3::RoomObject new_obj(static_cast<int16_t>(obj_drop.object_id),
174 static_cast<uint8_t>(tile_x),
175 static_cast<uint8_t>(tile_y), object_size, 0);
176 const size_t before = room.GetTileObjects().size();
178 room_id, new_obj, tile_x, tile_y);
179 if (room.GetTileObjects().size() > before) {
181 }
182 }
183 }
184
185 gui::SpriteDragPayload sprite_drop;
186 if (gui::AcceptSpriteDrop(&sprite_drop)) {
187 const DungeonCanvasTransform transform(
189 const auto [room_x, room_y] =
190 transform.ScreenToRoomPixelCoordinates(ImGui::GetMousePos());
191 if (dungeon_coords::IsWithinBounds(room_x, room_y)) {
192 const int sprite_x = room_x / dungeon_coords::kSpriteTileSize;
193 const int sprite_y = room_y / dungeon_coords::kSpriteTileSize;
194 zelda3::Sprite new_sprite(static_cast<uint8_t>(sprite_drop.sprite_id),
195 static_cast<uint8_t>(sprite_x),
196 static_cast<uint8_t>(sprite_y), 0, 0);
199 .context()) {
200 ctx->NotifyMutation(MutationDomain::kSprites);
201 }
202 room.GetSprites().push_back(new_sprite);
203 room.MarkSpritesDirty();
206 .context()) {
207 ctx->NotifyInvalidateCache(MutationDomain::kSprites);
208 }
209 }
210 }
211}
212
214 const gui::CanvasRuntime& canvas_rt, zelda3::Room& room, int room_id) {
215 PrepareRoomStateForCanvas(room, room_id);
216
217 const bool drew_background = DrawRoomBackgroundLayers(room_id);
218 if (!drew_background) {
219 DrawRoomRenderFallback(canvas_rt);
220 }
221
223 DrawMaskHighlights(canvas_rt, room);
224 }
225
226 RenderEntityOverlay(canvas_rt, room);
227
234
235 const auto selected = object_interaction_.GetSelectedObjectIndices();
236 if (selected.size() == 1) {
237 const auto& objects = room.GetTileObjects();
238 const size_t idx = selected.front();
239 if (idx < objects.size()) {
240 const auto& obj = objects[idx];
241 gui::BeginRoomObjectDragSource(static_cast<uint16_t>(obj.id_), room_id,
242 obj.x_, obj.y_, obj.size_);
243 }
244 }
245
247 const auto sel = object_interaction_.GetSelectedEntity();
248 if (sel.type == EntityType::Sprite) {
249 const auto& sprites = room.GetSprites();
250 if (sel.index < sprites.size()) {
251 const auto& sprite = sprites[sel.index];
252 gui::BeginSpriteDragSource(sprite.id(), room_id);
253 }
254 }
255 }
256
257 HandleTouchLongPressContextMenu(canvas_rt, room);
258 }
259
260 HandleRoomCanvasDropTargets(room, room_id);
261}
262
263} // namespace yaze::editor
bool is_loaded() const
Definition rom.h:144
InteractionContext * context() const
Get the interaction context.
std::pair< int, int > ScreenToRoomPixelCoordinates(ImVec2 screen) const
std::pair< int, int > ScreenToRoomTiles(ImVec2 screen, int tile_size) const
void HandleRoomCanvasDropTargets(zelda3::Room &room, int room_id)
std::optional< std::pair< int, int > > pending_scroll_target_
void SyncCanvasCaptureRegion(const gui::CanvasRuntime &canvas_rt)
DungeonObjectInteraction object_interaction_
void PrepareRoomStateForCanvas(zelda3::Room &room, int room_id)
void DrawRoomCanvasContent(const gui::CanvasRuntime &canvas_rt, zelda3::Room &room, int room_id)
void DrawMaskHighlights(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void ConsumePendingCanvasScroll(const gui::CanvasRuntime &canvas_rt)
void RenderEntityOverlay(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void HandleTouchLongPressContextMenu(const gui::CanvasRuntime &rt, const zelda3::Room &room)
void SetSelectedObjects(const std::vector< size_t > &indices)
void SetCurrentRoom(DungeonRoomStore *rooms, int room_id)
std::vector< size_t > GetSelectedObjectIndices() const
InteractionCoordinator & entity_coordinator()
Get the interaction coordinator for entity handling.
zelda3::Room * TryEnsureRoom(int room_id)
SpriteInteractionHandler & sprite_handler()
bool PlaceObjectAt(int room_id, const zelda3::RoomObject &object, int x, int y)
Place a new object. Returns false if blocked by ROM limits.
void ProcessTextureQueue(IRenderer *renderer)
Definition arena.cc:116
static Arena & Get()
Definition arena.cc:21
void set_scrolling(ImVec2 scroll)
Definition canvas.h:351
auto global_scale() const
Definition canvas.h:397
auto zero_point() const
Definition canvas.h:348
auto scrolling() const
Definition canvas.h:350
bool ArePotItemsLoaded() const
Definition room.h:876
void MarkSpritesDirty()
Definition room.h:256
const std::vector< zelda3::Sprite > & GetSprites() const
Definition room.h:253
void PrepareForRender(std::optional< uint8_t > entrance_blockset=std::nullopt)
Definition room.cc:850
const std::vector< RoomObject > & GetTileObjects() const
Definition room.h:382
void LoadObjects()
Definition room.cc:1623
void LoadPotItems()
Definition room.cc:3926
bool AreObjectsLoaded() const
Definition room.h:873
void LoadSprites()
Definition room.cc:2485
bool AreSpritesLoaded() const
Definition room.h:874
A class for managing sprites in the overworld and underworld.
Definition sprite.h:37
const AgentUITheme & GetTheme()
bool IsWithinBounds(int canvas_x, int canvas_y, int margin=0)
Check if coordinates are within room bounds.
Editors are the view controllers for the application.
bool BeginSpriteDragSource(int sprite_id, int room_id)
Definition drag_drop.h:76
ImVec2 ClampScroll(ImVec2 scroll, ImVec2 content_px, ImVec2 canvas_px)
void DrawCanvasHUD(const char *label, const ImVec2 &pos, const ImVec2 &size, std::function< void()> draw_content)
Draw a stylized Heads-Up Display (HUD) for canvas status.
bool AcceptRoomObjectDrop(RoomObjectDragPayload *out)
Definition drag_drop.h:171
bool AcceptSpriteDrop(SpriteDragPayload *out)
Definition drag_drop.h:145
bool BeginRoomObjectDragSource(uint16_t object_id, int room_id, int pos_x, int pos_y, uint8_t size=0)
Definition drag_drop.h:99
std::string GetRoomLabel(int id)
Convenience function to get a room label.