epic: refactor SDL2_Renderer usage to IRenderer and queued texture rendering
- Updated the testing guide to clarify the testing framework's organization and execution methods, improving user understanding. - Refactored CMakeLists to include new platform-specific files, ensuring proper integration of the rendering backend. - Modified main application files to utilize the new IRenderer interface, enhancing flexibility in rendering operations. - Implemented deferred texture management in various components, allowing for more efficient graphics handling and improved performance. - Introduced new methods for texture creation and updates, streamlining the rendering process across the application. - Enhanced logging and error handling in the rendering pipeline to facilitate better debugging and diagnostics.
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
#include "dungeon_canvas_viewer.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/snes_palette.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.h"
|
||||
#include "util/log.h"
|
||||
#include "app/zelda3/dungeon/object_drawer.h"
|
||||
#include "app/zelda3/dungeon/object_renderer.h"
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
@@ -148,6 +144,7 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
||||
object_interaction_.HandleCanvasMouseInput();
|
||||
object_interaction_.CheckForObjectSelection();
|
||||
object_interaction_.DrawSelectBox();
|
||||
object_interaction_.DrawSelectionHighlights(); // Draw selection highlights on top
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +192,9 @@ void DungeonCanvasViewer::RenderObjectInCanvas(const zelda3::RoomObject &object,
|
||||
// Ensure the bitmap is valid and has content
|
||||
if (object_bitmap.width() > 0 && object_bitmap.height() > 0) {
|
||||
object_bitmap.SetPalette(palette);
|
||||
core::Renderer::Get().RenderBitmap(&object_bitmap);
|
||||
// Queue texture creation for the object bitmap via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &object_bitmap);
|
||||
canvas_.DrawBitmap(object_bitmap, canvas_x, canvas_y, 1.0f, 255);
|
||||
return;
|
||||
}
|
||||
@@ -656,7 +655,10 @@ absl::Status DungeonCanvasViewer::UpdateRoomBackgroundLayers(int room_id) {
|
||||
if (current_palette_id_ < current_palette_group_.size()) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
current_palette_group_[current_palette_id_], 0);
|
||||
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,7 +673,10 @@ absl::Status DungeonCanvasViewer::UpdateRoomBackgroundLayers(int room_id) {
|
||||
if (block >= 0 && block < gfx::Arena::Get().gfx_sheets().size()) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0);
|
||||
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -691,14 +696,9 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
|
||||
if (bg1_bitmap.is_active() && bg1_bitmap.width() > 0 && bg1_bitmap.height() > 0) {
|
||||
if (!bg1_bitmap.texture()) {
|
||||
core::Renderer::Get().RenderBitmap(&bg1_bitmap);
|
||||
}
|
||||
|
||||
// DEBUG: Check SDL texture format
|
||||
Uint32 format;
|
||||
int access, w, h;
|
||||
if (SDL_QueryTexture(bg1_bitmap.texture(), &format, &access, &w, &h) == 0) {
|
||||
const char* format_name_cstr = SDL_GetPixelFormatName(format);
|
||||
// Queue texture creation for background layer 1 via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bg1_bitmap);
|
||||
}
|
||||
|
||||
canvas_.DrawBitmap(bg1_bitmap, 0, 0, 1.0f, 255);
|
||||
@@ -706,7 +706,9 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
|
||||
if (bg2_bitmap.is_active() && bg2_bitmap.width() > 0 && bg2_bitmap.height() > 0) {
|
||||
if (!bg2_bitmap.texture()) {
|
||||
core::Renderer::Get().RenderBitmap(&bg2_bitmap);
|
||||
// Queue texture creation for background layer 2 via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &bg2_bitmap);
|
||||
}
|
||||
canvas_.DrawBitmap(bg2_bitmap, 0, 0, 1.0f, 200);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ class DungeonCanvasViewer {
|
||||
int current_active_room_tab_ = 0;
|
||||
|
||||
// Object interaction state
|
||||
bool object_interaction_enabled_ = false;
|
||||
bool object_interaction_enabled_ = true;
|
||||
|
||||
// Palette data
|
||||
uint64_t current_palette_group_id_ = 0;
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
using ImGui::BeginTabBar;
|
||||
using ImGui::BeginTabItem;
|
||||
using ImGui::BeginTable;
|
||||
@@ -185,11 +183,15 @@ absl::Status DungeonEditor::Save() {
|
||||
}
|
||||
|
||||
absl::Status DungeonEditor::RefreshGraphics() {
|
||||
// Update graphics sheet textures via Arena's deferred texture queue
|
||||
std::for_each_n(
|
||||
rooms_[current_room_id_].blocks().begin(), 8, [this](int block) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
current_palette_group_[current_palette_id_], 0);
|
||||
Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update for the modified graphics sheet
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
});
|
||||
|
||||
auto sprites_aux1_pal_group = rom()->palette_group().sprites_aux1;
|
||||
@@ -198,7 +200,10 @@ absl::Status DungeonEditor::RefreshGraphics() {
|
||||
[this, &sprites_aux1_pal_group](int block) {
|
||||
gfx::Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
|
||||
sprites_aux1_pal_group[current_palette_id_], 0);
|
||||
Renderer::Get().UpdateBitmap(&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
// Queue texture update for the modified graphics sheet
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::UPDATE,
|
||||
&gfx::Arena::Get().gfx_sheets()[block]);
|
||||
});
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,9 @@ namespace yaze::editor {
|
||||
|
||||
// No table layout needed - all cards are independent
|
||||
|
||||
void DungeonEditorV2::Initialize() {
|
||||
void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) {
|
||||
renderer_ = renderer;
|
||||
rom_ = rom;
|
||||
// Don't initialize emulator preview yet - ROM might not be loaded
|
||||
// Will be initialized in Load() instead
|
||||
|
||||
@@ -24,6 +26,8 @@ void DungeonEditorV2::Initialize() {
|
||||
room_window_class_.DockingAllowUnclassed = false; // Room windows dock together
|
||||
}
|
||||
|
||||
void DungeonEditorV2::Initialize() {}
|
||||
|
||||
absl::Status DungeonEditorV2::Load() {
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
return absl::FailedPreconditionError("ROM not loaded");
|
||||
@@ -55,13 +59,13 @@ absl::Status DungeonEditorV2::Load() {
|
||||
object_selector_.set_rooms(&rooms_);
|
||||
|
||||
// NOW initialize emulator preview with loaded ROM
|
||||
object_emulator_preview_.Initialize(rom_);
|
||||
object_emulator_preview_.Initialize(renderer_, rom_);
|
||||
|
||||
// Initialize palette editor with loaded ROM
|
||||
palette_editor_.Initialize(rom_);
|
||||
|
||||
// Initialize unified object editor card
|
||||
object_editor_card_ = std::make_unique<ObjectEditorCard>(rom_, &canvas_viewer_);
|
||||
object_editor_card_ = std::make_unique<ObjectEditorCard>(renderer_, rom_, &canvas_viewer_);
|
||||
|
||||
// Wire palette changes to trigger room re-renders
|
||||
palette_editor_.SetOnPaletteChanged([this](int /*palette_id*/) {
|
||||
@@ -97,7 +101,8 @@ absl::Status DungeonEditorV2::Update() {
|
||||
"This parent window can be minimized or closed.");
|
||||
|
||||
// Render all independent cards (these create their own top-level windows)
|
||||
object_emulator_preview_.Render();
|
||||
// NOTE: Emulator preview is now integrated into ObjectEditorCard
|
||||
// object_emulator_preview_.Render(); // Removed - causing performance issues
|
||||
DrawLayout();
|
||||
|
||||
return absl::OkStatus();
|
||||
@@ -148,11 +153,7 @@ void DungeonEditorV2::DrawToolset() {
|
||||
|
||||
toolbar.AddSeparator();
|
||||
|
||||
if (toolbar.AddToggle(ICON_MD_CATEGORY, &show_object_selector_, "Toggle Object Selector (Legacy)")) {
|
||||
// Toggled
|
||||
}
|
||||
|
||||
if (toolbar.AddToggle(ICON_MD_CONSTRUCTION, &show_object_editor_, "Toggle Object Editor (Unified)")) {
|
||||
if (toolbar.AddToggle(ICON_MD_CONSTRUCTION, &show_object_editor_, "Toggle Object Editor")) {
|
||||
// Toggled
|
||||
}
|
||||
|
||||
@@ -186,18 +187,7 @@ void DungeonEditorV2::DrawLayout() {
|
||||
DrawRoomGraphicsCard();
|
||||
}
|
||||
|
||||
// 4. Legacy Object Selector Card (independent, dockable)
|
||||
if (show_object_selector_) {
|
||||
gui::EditorCard object_card(
|
||||
MakeCardTitle("Object Selector").c_str(),
|
||||
ICON_MD_CATEGORY, &show_object_selector_);
|
||||
if (object_card.Begin()) {
|
||||
object_selector_.Draw();
|
||||
}
|
||||
object_card.End();
|
||||
}
|
||||
|
||||
// 4b. Unified Object Editor Card (new, combines selector + preview + interaction)
|
||||
// 4. Unified Object Editor Card
|
||||
if (show_object_editor_ && object_editor_card_) {
|
||||
object_editor_card_->Draw(&show_object_editor_);
|
||||
}
|
||||
@@ -234,11 +224,16 @@ void DungeonEditorV2::DrawLayout() {
|
||||
room_cards_[room_id] = std::make_shared<gui::EditorCard>(
|
||||
card_name_str.c_str(), ICON_MD_GRID_ON, &open);
|
||||
room_cards_[room_id]->SetDefaultSize(700, 600);
|
||||
|
||||
// Set default position for first room to be docked with main window
|
||||
if (active_rooms_.Size == 1) {
|
||||
room_cards_[room_id]->SetPosition(gui::EditorCard::Position::Floating);
|
||||
}
|
||||
}
|
||||
|
||||
auto& room_card = room_cards_[room_id];
|
||||
|
||||
// Use docking class to make room cards dock together
|
||||
// CRITICAL: Use docking class BEFORE Begin() to make rooms tab together
|
||||
ImGui::SetNextWindowClass(&room_window_class_);
|
||||
|
||||
if (room_card->Begin(&open)) {
|
||||
@@ -500,15 +495,15 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
MakeCardTitle("Room Matrix").c_str(),
|
||||
ICON_MD_GRID_VIEW, &show_room_matrix_);
|
||||
|
||||
matrix_card.SetDefaultSize(520, 620);
|
||||
matrix_card.SetDefaultSize(440, 520);
|
||||
|
||||
if (matrix_card.Begin()) {
|
||||
// 16 wide x 19 tall = 304 cells (295 rooms + 9 empty)
|
||||
// 16 wide x 19 tall = 304 cells (296 rooms + 8 empty)
|
||||
constexpr int kRoomsPerRow = 16;
|
||||
constexpr int kRoomsPerCol = 19;
|
||||
constexpr int kTotalRooms = 0x128; // 296 rooms (0x00-0x127)
|
||||
constexpr float kRoomCellSize = 28.0f; // Compact cells
|
||||
constexpr float kCellSpacing = 2.0f;
|
||||
constexpr float kRoomCellSize = 24.0f; // Smaller cells like ZScream
|
||||
constexpr float kCellSpacing = 1.0f; // Tighter spacing
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||
@@ -527,30 +522,34 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
cell_min.y + kRoomCellSize);
|
||||
|
||||
if (is_valid_room) {
|
||||
// ALWAYS use palette-based color (lazy load if needed)
|
||||
ImU32 bg_color = IM_COL32(60, 60, 70, 255); // Fallback
|
||||
// Use simple deterministic color based on room ID (no loading needed)
|
||||
ImU32 bg_color;
|
||||
|
||||
// Try to get palette color
|
||||
uint8_t palette = 0;
|
||||
if (room_id < static_cast<int>(rooms_.size())) {
|
||||
// Lazy load room if needed to get palette
|
||||
if (!rooms_[room_id].IsLoaded()) {
|
||||
auto status = room_loader_.LoadRoom(room_id, rooms_[room_id]);
|
||||
if (status.ok()) {
|
||||
palette = rooms_[room_id].palette;
|
||||
}
|
||||
} else {
|
||||
palette = rooms_[room_id].palette;
|
||||
}
|
||||
|
||||
// Create a color variation based on palette ID
|
||||
float r = 0.3f + (palette % 4) * 0.15f;
|
||||
float g = 0.3f + ((palette / 4) % 4) * 0.15f;
|
||||
float b = 0.4f + ((palette / 16) % 2) * 0.2f;
|
||||
bg_color = IM_COL32(
|
||||
static_cast<int>(r * 255),
|
||||
static_cast<int>(g * 255),
|
||||
static_cast<int>(b * 255), 255);
|
||||
// Generate color from room ID - much faster than loading
|
||||
int hue = (room_id * 37) % 360; // Distribute colors across spectrum
|
||||
int saturation = 40 + (room_id % 3) * 15;
|
||||
int brightness = 50 + (room_id % 5) * 10;
|
||||
|
||||
// Convert HSV to RGB
|
||||
float h = hue / 60.0f;
|
||||
float s = saturation / 100.0f;
|
||||
float v = brightness / 100.0f;
|
||||
|
||||
int i = static_cast<int>(h);
|
||||
float f = h - i;
|
||||
int p = static_cast<int>(v * (1 - s) * 255);
|
||||
int q = static_cast<int>(v * (1 - s * f) * 255);
|
||||
int t = static_cast<int>(v * (1 - s * (1 - f)) * 255);
|
||||
int val = static_cast<int>(v * 255);
|
||||
|
||||
switch (i % 6) {
|
||||
case 0: bg_color = IM_COL32(val, t, p, 255); break;
|
||||
case 1: bg_color = IM_COL32(q, val, p, 255); break;
|
||||
case 2: bg_color = IM_COL32(p, val, t, 255); break;
|
||||
case 3: bg_color = IM_COL32(p, q, val, 255); break;
|
||||
case 4: bg_color = IM_COL32(t, p, val, 255); break;
|
||||
case 5: bg_color = IM_COL32(val, p, q, 255); break;
|
||||
default: bg_color = IM_COL32(60, 60, 70, 255); break;
|
||||
}
|
||||
|
||||
// Check if room is currently selected
|
||||
@@ -604,7 +603,7 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
OnRoomSelected(room_id);
|
||||
}
|
||||
|
||||
// Hover tooltip with room name
|
||||
// Hover tooltip with room name and status
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
if (room_id < static_cast<int>(std::size(zelda3::kRoomNames))) {
|
||||
@@ -612,9 +611,8 @@ void DungeonEditorV2::DrawRoomMatrixCard() {
|
||||
} else {
|
||||
ImGui::Text("Room %03X", room_id);
|
||||
}
|
||||
ImGui::Text("Palette: %02X",
|
||||
room_id < static_cast<int>(rooms_.size()) ?
|
||||
rooms_[room_id].palette : 0);
|
||||
ImGui::Text("Status: %s", is_open ? "Open" : is_current ? "Current" : "Closed");
|
||||
ImGui::Text("Click to %s", is_open ? "focus" : "open");
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -51,8 +51,9 @@ class DungeonEditorV2 : public Editor {
|
||||
}
|
||||
|
||||
// Editor interface
|
||||
void Initialize(gfx::IRenderer* renderer, Rom* rom);
|
||||
void Initialize() override;
|
||||
absl::Status Load() override;
|
||||
absl::Status Load();
|
||||
absl::Status Update() override;
|
||||
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
|
||||
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
|
||||
@@ -69,7 +70,7 @@ class DungeonEditorV2 : public Editor {
|
||||
room_selector_.set_rom(rom);
|
||||
canvas_viewer_.SetRom(rom);
|
||||
object_selector_.SetRom(rom);
|
||||
object_emulator_preview_.Initialize(rom);
|
||||
object_emulator_preview_.Initialize(renderer_, rom);
|
||||
}
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
@@ -86,6 +87,7 @@ class DungeonEditorV2 : public Editor {
|
||||
}
|
||||
|
||||
private:
|
||||
gfx::IRenderer* renderer_ = nullptr;
|
||||
// Simple UI layout
|
||||
void DrawLayout();
|
||||
void DrawRoomTab(int room_id);
|
||||
@@ -117,8 +119,7 @@ class DungeonEditorV2 : public Editor {
|
||||
bool show_room_matrix_ = false;
|
||||
bool show_entrances_list_ = false;
|
||||
bool show_room_graphics_ = false; // Room graphics card
|
||||
bool show_object_selector_ = true; // Legacy object selector
|
||||
bool show_object_editor_ = true; // New unified object editor card
|
||||
bool show_object_editor_ = true; // Unified object editor card
|
||||
bool show_palette_editor_ = true;
|
||||
|
||||
// Palette management
|
||||
|
||||
@@ -150,25 +150,59 @@ void DungeonObjectInteraction::SelectObjectsInRect() {
|
||||
selected_object_indices_.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DungeonObjectInteraction::DrawSelectionHighlights() {
|
||||
if (!rooms_ || current_room_id_ < 0 || current_room_id_ >= 296) return;
|
||||
|
||||
// Highlight selected objects
|
||||
if (!selected_object_indices_.empty()) {
|
||||
for (size_t index : selected_object_indices_) {
|
||||
if (index < objects.size()) {
|
||||
const auto& object = objects[index];
|
||||
auto [canvas_x, canvas_y] =
|
||||
RoomToCanvasCoordinates(object.x_, object.y_);
|
||||
|
||||
// Draw selection highlight
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = canvas_->zero_point();
|
||||
ImVec2 obj_start(canvas_pos.x + canvas_x - 2,
|
||||
canvas_pos.y + canvas_y - 2);
|
||||
ImVec2 obj_end(canvas_pos.x + canvas_x + 18,
|
||||
canvas_pos.y + canvas_y + 18);
|
||||
draw_list->AddRect(obj_start, obj_end, IM_COL32(0, 255, 255, 255), 0.0f,
|
||||
0, 2.0f);
|
||||
}
|
||||
auto& room = (*rooms_)[current_room_id_];
|
||||
const auto& objects = room.GetTileObjects();
|
||||
|
||||
// Draw highlights for all selected objects
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 canvas_pos = canvas_->zero_point();
|
||||
|
||||
for (size_t index : selected_object_indices_) {
|
||||
if (index < objects.size()) {
|
||||
const auto& object = objects[index];
|
||||
auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(object.x_, object.y_);
|
||||
|
||||
// Calculate object size for highlight
|
||||
int obj_width = 8 + (object.size_ & 0x0F) * 4;
|
||||
int obj_height = 8 + ((object.size_ >> 4) & 0x0F) * 4;
|
||||
obj_width = std::min(obj_width, 64);
|
||||
obj_height = std::min(obj_height, 64);
|
||||
|
||||
// Draw cyan selection highlight
|
||||
ImVec2 obj_start(canvas_pos.x + canvas_x - 2,
|
||||
canvas_pos.y + canvas_y - 2);
|
||||
ImVec2 obj_end(canvas_pos.x + canvas_x + obj_width + 2,
|
||||
canvas_pos.y + canvas_y + obj_height + 2);
|
||||
|
||||
// Animated selection (pulsing effect)
|
||||
float pulse = 0.7f + 0.3f * std::sin(static_cast<float>(ImGui::GetTime()) * 4.0f);
|
||||
draw_list->AddRect(obj_start, obj_end,
|
||||
IM_COL32(0, static_cast<int>(255 * pulse), 255, 255),
|
||||
0.0f, 0, 2.5f);
|
||||
|
||||
// Draw corner handles for selected objects
|
||||
constexpr float handle_size = 4.0f;
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_start.x - handle_size/2, obj_start.y - handle_size/2),
|
||||
ImVec2(obj_start.x + handle_size/2, obj_start.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_end.x - handle_size/2, obj_start.y - handle_size/2),
|
||||
ImVec2(obj_end.x + handle_size/2, obj_start.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_start.x - handle_size/2, obj_end.y - handle_size/2),
|
||||
ImVec2(obj_start.x + handle_size/2, obj_end.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(obj_end.x - handle_size/2, obj_end.y - handle_size/2),
|
||||
ImVec2(obj_end.x + handle_size/2, obj_end.y + handle_size/2),
|
||||
IM_COL32(0, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class DungeonObjectInteraction {
|
||||
// Selection rectangle (like OverworldEditor)
|
||||
void DrawObjectSelectRect();
|
||||
void SelectObjectsInRect();
|
||||
void DrawSelectionHighlights(); // Draw highlights for selected objects
|
||||
|
||||
// Drag and select box functionality
|
||||
void DrawSelectBox();
|
||||
|
||||
@@ -90,7 +90,9 @@ void DungeonObjectSelector::DrawObjectRenderer() {
|
||||
auto preview_bitmap = std::move(preview_result.value());
|
||||
if (preview_bitmap.width() > 0 && preview_bitmap.height() > 0) {
|
||||
preview_bitmap.SetPalette(preview_palette_);
|
||||
core::Renderer::Get().RenderBitmap(&preview_bitmap);
|
||||
// Queue texture creation via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &preview_bitmap);
|
||||
object_canvas_.DrawBitmap(preview_bitmap, preview_x, preview_y, 1.0f, 255);
|
||||
} else {
|
||||
// Fallback: Draw primitive shape
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#include "dungeon_renderer.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/core/window.h"
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gui/color.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using core::Renderer;
|
||||
|
||||
void DungeonRenderer::RenderObjectInCanvas(const zelda3::RoomObject& object,
|
||||
const gfx::SnesPalette& palette) {
|
||||
// Validate ROM is loaded
|
||||
@@ -55,7 +51,9 @@ void DungeonRenderer::RenderObjectInCanvas(const zelda3::RoomObject& object,
|
||||
if (object_bitmap.width() > 0 && object_bitmap.height() > 0 &&
|
||||
object_bitmap.data() != nullptr) {
|
||||
object_bitmap.SetPalette(palette);
|
||||
core::Renderer::Get().RenderBitmap(&object_bitmap);
|
||||
// Queue texture creation for the object bitmap via Arena's deferred system
|
||||
gfx::Arena::Get().QueueTextureCommand(
|
||||
gfx::Arena::TextureCommandType::CREATE, &object_bitmap);
|
||||
canvas_->DrawBitmap(object_bitmap, canvas_x, canvas_y, 1.0f, 255);
|
||||
// Cache the successfully rendered bitmap
|
||||
ObjectRenderCache cache_entry;
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include "object_editor_card.h"
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gui/icons.h"
|
||||
#include "app/gui/ui_helpers.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
ObjectEditorCard::ObjectEditorCard(Rom* rom, DungeonCanvasViewer* canvas_viewer)
|
||||
: rom_(rom), canvas_viewer_(canvas_viewer), object_selector_(rom) {
|
||||
emulator_preview_.Initialize(rom);
|
||||
ObjectEditorCard::ObjectEditorCard(gfx::IRenderer* renderer, Rom* rom, DungeonCanvasViewer* canvas_viewer)
|
||||
: renderer_(renderer), rom_(rom), canvas_viewer_(canvas_viewer), object_selector_(rom) {
|
||||
emulator_preview_.Initialize(renderer, rom);
|
||||
}
|
||||
|
||||
void ObjectEditorCard::Draw(bool* p_open) {
|
||||
@@ -93,7 +94,7 @@ void ObjectEditorCard::DrawObjectSelector() {
|
||||
ImGui::Separator();
|
||||
|
||||
// Object list with categories
|
||||
if (ImGui::BeginChild("##ObjectList", ImVec2(0, -100), true)) {
|
||||
if (ImGui::BeginChild("##ObjectList", ImVec2(0, 0), true)) {
|
||||
// Floor objects
|
||||
if (ImGui::CollapsingHeader(ICON_MD_GRID_ON " Floor Objects",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
@@ -268,8 +269,10 @@ void ObjectEditorCard::DrawObjectPreviewIcon(int object_id, const ImVec2& size)
|
||||
|
||||
void ObjectEditorCard::DrawSelectedObjectInfo() {
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// Show current object for placement
|
||||
ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
|
||||
ICON_MD_INFO " Current Object:");
|
||||
ICON_MD_INFO " Current:");
|
||||
|
||||
if (has_preview_object_) {
|
||||
ImGui::SameLine();
|
||||
@@ -278,14 +281,35 @@ void ObjectEditorCard::DrawSelectedObjectInfo() {
|
||||
ImGui::Text("Layer: %s",
|
||||
preview_object_.layer_ == zelda3::RoomObject::BG1 ? "BG1" :
|
||||
preview_object_.layer_ == zelda3::RoomObject::BG2 ? "BG2" : "BG3");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Mode: %s",
|
||||
interaction_mode_ == InteractionMode::Place ? "Place" :
|
||||
interaction_mode_ == InteractionMode::Select ? "Select" :
|
||||
interaction_mode_ == InteractionMode::Delete ? "Delete" : "None");
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("None selected");
|
||||
ImGui::TextDisabled("None");
|
||||
}
|
||||
|
||||
// Show selection count
|
||||
auto& interaction = canvas_viewer_->object_interaction();
|
||||
const auto& selected = interaction.GetSelectedObjectIndices();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("|");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.4f, 1.0f),
|
||||
ICON_MD_CHECKLIST " Selected: %zu", selected.size());
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("|");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Mode: %s",
|
||||
interaction_mode_ == InteractionMode::Place ? ICON_MD_ADD_BOX " Place" :
|
||||
interaction_mode_ == InteractionMode::Select ? ICON_MD_CHECK_BOX " Select" :
|
||||
interaction_mode_ == InteractionMode::Delete ? ICON_MD_DELETE " Delete" : "None");
|
||||
|
||||
// Show quick actions for selections
|
||||
if (!selected.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton(ICON_MD_CLEAR " Clear")) {
|
||||
interaction.ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <unordered_map>
|
||||
|
||||
#include "app/editor/dungeon/dungeon_canvas_viewer.h"
|
||||
#include "app/gfx/backend/irenderer.h"
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/editor/dungeon/dungeon_object_selector.h"
|
||||
#include "app/gui/editor_layout.h"
|
||||
@@ -27,7 +28,7 @@ namespace editor {
|
||||
*/
|
||||
class ObjectEditorCard {
|
||||
public:
|
||||
ObjectEditorCard(Rom* rom, DungeonCanvasViewer* canvas_viewer);
|
||||
ObjectEditorCard(gfx::IRenderer* renderer, Rom* rom, DungeonCanvasViewer* canvas_viewer);
|
||||
|
||||
// Main update function
|
||||
void Draw(bool* p_open);
|
||||
@@ -59,7 +60,7 @@ class ObjectEditorCard {
|
||||
|
||||
// UI state
|
||||
int selected_tab_ = 0;
|
||||
bool show_emulator_preview_ = true;
|
||||
bool show_emulator_preview_ = false; // Disabled by default for performance
|
||||
bool show_object_list_ = true;
|
||||
bool show_interaction_controls_ = true;
|
||||
|
||||
@@ -75,6 +76,7 @@ class ObjectEditorCard {
|
||||
// Selected object for placement
|
||||
zelda3::RoomObject preview_object_{0, 0, 0, 0, 0};
|
||||
bool has_preview_object_ = false;
|
||||
gfx::IRenderer* renderer_;
|
||||
};
|
||||
|
||||
} // namespace editor
|
||||
|
||||
Reference in New Issue
Block a user