feat: Introduce Dungeon Object Emulator Preview and Object Drawing Enhancements
- Added DungeonObjectEmulatorPreview for rendering dungeon objects using the SNES emulator, allowing real-time visualization of object graphics. - Implemented ObjectDrawer class to handle drawing of various object types to background buffers, utilizing game-specific patterns. - Updated DungeonCanvasViewer to integrate object rendering and improve background layer management. - Enhanced DungeonEditorV2 to support the new emulator preview, providing a more interactive editing experience. - Improved error handling and logging for better debugging during object rendering operations.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "app/gui/canvas.h"
|
||||
#include "app/gui/input.h"
|
||||
#include "app/rom.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"
|
||||
@@ -111,10 +112,13 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
||||
room.LoadObjects();
|
||||
}
|
||||
|
||||
// Render background layers with proper positioning
|
||||
// NOTE: Don't draw objects here - RenderRoomBackgroundLayers() already does it
|
||||
// via room.RenderRoomGraphics() which calls RenderObjectsToBackground()
|
||||
|
||||
// Render background layers from arena buffers
|
||||
RenderRoomBackgroundLayers(room_id);
|
||||
|
||||
// Render room objects with proper graphics
|
||||
// Render room objects with proper graphics (old system as fallback)
|
||||
if (current_palette_id_ < current_palette_group_.size()) {
|
||||
auto room_palette = current_palette_group_[current_palette_id_];
|
||||
|
||||
@@ -652,31 +656,45 @@ absl::Status DungeonCanvasViewer::UpdateRoomBackgroundLayers(int room_id) {
|
||||
|
||||
void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
if (room_id < 0 || room_id >= 128) {
|
||||
printf("[Canvas] Invalid room_id: %d\n", room_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rom_ || !rom_->is_loaded()) {
|
||||
printf("[Canvas] ROM not loaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!rooms_) {
|
||||
printf("[Canvas] Rooms pointer is null\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get canvas dimensions to limit rendering
|
||||
// Get canvas dimensions
|
||||
int canvas_width = canvas_.width();
|
||||
int canvas_height = canvas_.height();
|
||||
|
||||
// Validate canvas dimensions
|
||||
printf("[Canvas] Canvas size: %dx%d\n", canvas_width, canvas_height);
|
||||
|
||||
if (canvas_width <= 0 || canvas_height <= 0) {
|
||||
printf("[Canvas] Invalid canvas dimensions\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Render the room's background layers using the graphics arena
|
||||
// BG1 (background layer 1) - main room graphics
|
||||
// Render BG1 (background layer 1) - main room graphics
|
||||
auto& bg1_bitmap = gfx::Arena::Get().bg1().bitmap();
|
||||
printf("[Canvas] BG1: active=%d, size=%dx%d, texture=%p\n",
|
||||
bg1_bitmap.is_active(), bg1_bitmap.width(), bg1_bitmap.height(),
|
||||
(void*)bg1_bitmap.texture());
|
||||
|
||||
if (bg1_bitmap.is_active() && bg1_bitmap.width() > 0 && bg1_bitmap.height() > 0) {
|
||||
// Scale the background to fit the canvas
|
||||
// Ensure texture exists
|
||||
if (!bg1_bitmap.texture()) {
|
||||
printf("[Canvas] WARNING: BG1 has no texture, creating...\n");
|
||||
core::Renderer::Get().RenderBitmap(&bg1_bitmap);
|
||||
}
|
||||
|
||||
// Scale to fit canvas
|
||||
float scale_x = static_cast<float>(canvas_width) / bg1_bitmap.width();
|
||||
float scale_y = static_cast<float>(canvas_height) / bg1_bitmap.height();
|
||||
float scale = std::min(scale_x, scale_y);
|
||||
@@ -686,13 +704,21 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
int offset_x = (canvas_width - scaled_width) / 2;
|
||||
int offset_y = (canvas_height - scaled_height) / 2;
|
||||
|
||||
printf("[Canvas] Drawing BG1 at offset=(%d,%d), scaled_size=%dx%d, scale=%.2f\n",
|
||||
offset_x, offset_y, scaled_width, scaled_height, scale);
|
||||
|
||||
canvas_.DrawBitmap(bg1_bitmap, offset_x, offset_y, scale, 255);
|
||||
} else {
|
||||
printf("[Canvas] BG1 not ready for rendering\n");
|
||||
}
|
||||
|
||||
// BG2 (background layer 2) - sprite graphics (overlay)
|
||||
// Render BG2 (background layer 2) - sprite graphics (overlay)
|
||||
auto& bg2_bitmap = gfx::Arena::Get().bg2().bitmap();
|
||||
if (bg2_bitmap.is_active() && bg2_bitmap.width() > 0 && bg2_bitmap.height() > 0) {
|
||||
// Scale the background to fit the canvas
|
||||
if (!bg2_bitmap.texture()) {
|
||||
core::Renderer::Get().RenderBitmap(&bg2_bitmap);
|
||||
}
|
||||
|
||||
float scale_x = static_cast<float>(canvas_width) / bg2_bitmap.width();
|
||||
float scale_y = static_cast<float>(canvas_height) / bg2_bitmap.height();
|
||||
float scale = std::min(scale_x, scale_y);
|
||||
@@ -702,8 +728,13 @@ void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||
int offset_x = (canvas_width - scaled_width) / 2;
|
||||
int offset_y = (canvas_height - scaled_height) / 2;
|
||||
|
||||
canvas_.DrawBitmap(bg2_bitmap, offset_x, offset_y, scale, 200); // Semi-transparent overlay
|
||||
printf("[Canvas] Drawing BG2 at offset=(%d,%d), scaled_size=%dx%d, scale=%.2f\n",
|
||||
offset_x, offset_y, scaled_width, scaled_height, scale);
|
||||
|
||||
canvas_.DrawBitmap(bg2_bitmap, offset_x, offset_y, scale, 200);
|
||||
}
|
||||
|
||||
printf("[Canvas] RenderRoomBackgroundLayers complete\n");
|
||||
}
|
||||
|
||||
} // namespace yaze::editor
|
||||
|
||||
@@ -11,15 +11,11 @@
|
||||
|
||||
namespace yaze::editor {
|
||||
|
||||
using ImGui::BeginTable;
|
||||
using ImGui::EndTable;
|
||||
using ImGui::TableHeadersRow;
|
||||
using ImGui::TableNextColumn;
|
||||
using ImGui::TableNextRow;
|
||||
using ImGui::TableSetupColumn;
|
||||
// No table layout needed - all cards are independent
|
||||
|
||||
void DungeonEditorV2::Initialize() {
|
||||
// No complex initialization needed - components handle themselves
|
||||
// Don't initialize emulator preview yet - ROM might not be loaded
|
||||
// Will be initialized in Load() instead
|
||||
}
|
||||
|
||||
absl::Status DungeonEditorV2::Load() {
|
||||
@@ -52,20 +48,35 @@ absl::Status DungeonEditorV2::Load() {
|
||||
object_selector_.SetCurrentPaletteId(current_palette_id_);
|
||||
object_selector_.set_rooms(&rooms_);
|
||||
|
||||
// NOW initialize emulator preview with loaded ROM
|
||||
object_emulator_preview_.Initialize(rom_);
|
||||
|
||||
is_loaded_ = true;
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status DungeonEditorV2::Update() {
|
||||
if (!is_loaded_) {
|
||||
ImGui::Text("Loading...");
|
||||
// Show minimal loading message in parent window
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f), "Dungeon Editor Loading...");
|
||||
ImGui::TextWrapped("Independent editor cards will appear once ROM data is loaded.");
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
// Minimize parent window content - just show a toolbar
|
||||
DrawToolset();
|
||||
gui::VerticalSpacing(2.0f);
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
|
||||
"Editor cards are independent windows - dock them anywhere!");
|
||||
ImGui::TextWrapped(
|
||||
"Room Selector, Object Selector, and Room cards can be freely arranged. "
|
||||
"This parent window can be minimized or closed.");
|
||||
|
||||
// Render all independent cards (these create their own top-level windows)
|
||||
object_emulator_preview_.Render();
|
||||
DrawLayout();
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
@@ -98,32 +109,33 @@ void DungeonEditorV2::DrawToolset() {
|
||||
}
|
||||
|
||||
void DungeonEditorV2::DrawLayout() {
|
||||
// Simple 3-column layout as designed
|
||||
if (BeginTable("##DungeonEditTable", 3,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV,
|
||||
ImVec2(0, 0))) {
|
||||
TableSetupColumn("Room Selector", ImGuiTableColumnFlags_WidthFixed, 250);
|
||||
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch);
|
||||
TableSetupColumn("Object Selector", ImGuiTableColumnFlags_WidthFixed, 300);
|
||||
TableHeadersRow();
|
||||
TableNextRow();
|
||||
|
||||
// Column 1: Room Selector (fully delegated)
|
||||
TableNextColumn();
|
||||
room_selector_.Draw();
|
||||
|
||||
// Column 2: Canvas area for active room cards
|
||||
TableNextColumn();
|
||||
// This column is now just a docking space. The cards themselves are independent windows.
|
||||
|
||||
// Column 3: Object Selector (fully delegated)
|
||||
TableNextColumn();
|
||||
object_selector_.Draw();
|
||||
|
||||
EndTable();
|
||||
// NO TABLE LAYOUT - All independent dockable EditorCards
|
||||
|
||||
// 1. Room Selector Card (independent, dockable)
|
||||
{
|
||||
static bool show_room_selector = true;
|
||||
gui::EditorCard selector_card(
|
||||
MakeCardTitle("Room Selector").c_str(),
|
||||
ICON_MD_LIST, &show_room_selector);
|
||||
if (selector_card.Begin()) {
|
||||
room_selector_.Draw();
|
||||
}
|
||||
selector_card.End();
|
||||
}
|
||||
|
||||
// Draw active rooms as individual, dockable EditorCards
|
||||
// 2. Object Selector/Manager Card (independent, dockable)
|
||||
{
|
||||
static bool show_object_selector = true;
|
||||
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();
|
||||
}
|
||||
|
||||
// 3. Active Room Cards (independent, dockable, no inheritance)
|
||||
for (int i = 0; i < active_rooms_.Size; i++) {
|
||||
int room_id = active_rooms_[i];
|
||||
bool open = true;
|
||||
@@ -138,13 +150,13 @@ void DungeonEditorV2::DrawLayout() {
|
||||
|
||||
std::string card_name_str = absl::StrFormat("%s###RoomCard%d",
|
||||
MakeCardTitle(base_name).c_str(), room_id);
|
||||
const char* card_name = card_name_str.c_str();
|
||||
|
||||
gui::EditorCard room_card(card_name, ICON_MD_GRID_ON, &open);
|
||||
// Each room card is COMPLETELY independent - no parent windows
|
||||
gui::EditorCard room_card(card_name_str.c_str(), ICON_MD_GRID_ON, &open);
|
||||
if (room_card.Begin()) {
|
||||
DrawRoomTab(room_id);
|
||||
}
|
||||
room_card.End(); // ALWAYS call End after Begin
|
||||
room_card.End();
|
||||
|
||||
if (!open) {
|
||||
active_rooms_.erase(active_rooms_.Data + i);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "app/zelda3/dungeon/room.h"
|
||||
#include "app/zelda3/dungeon/room_entrance.h"
|
||||
#include "app/gui/editor_layout.h"
|
||||
#include "app/gui/widgets/dungeon_object_emulator_preview.h"
|
||||
#include "imgui/imgui.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -39,7 +40,8 @@ class DungeonEditorV2 : public Editor {
|
||||
room_loader_(rom),
|
||||
room_selector_(rom),
|
||||
canvas_viewer_(rom),
|
||||
object_selector_(rom) {
|
||||
object_selector_(rom),
|
||||
object_emulator_preview_() {
|
||||
type_ = EditorType::kDungeon;
|
||||
}
|
||||
|
||||
@@ -62,6 +64,7 @@ class DungeonEditorV2 : public Editor {
|
||||
room_selector_.set_rom(rom);
|
||||
canvas_viewer_.SetRom(rom);
|
||||
object_selector_.SetRom(rom);
|
||||
object_emulator_preview_.Initialize(rom);
|
||||
}
|
||||
Rom* rom() const { return rom_; }
|
||||
|
||||
@@ -105,6 +108,7 @@ class DungeonEditorV2 : public Editor {
|
||||
DungeonRoomSelector room_selector_;
|
||||
DungeonCanvasViewer canvas_viewer_;
|
||||
DungeonObjectSelector object_selector_;
|
||||
gui::DungeonObjectEmulatorPreview object_emulator_preview_;
|
||||
|
||||
bool is_loaded_ = false;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user