diff --git a/src/app/editor/dungeon/dungeon_canvas_viewer.cc b/src/app/editor/dungeon/dungeon_canvas_viewer.cc index 5d1e9d7d..85c47f08 100644 --- a/src/app/editor/dungeon/dungeon_canvas_viewer.cc +++ b/src/app/editor/dungeon/dungeon_canvas_viewer.cc @@ -283,13 +283,60 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) { } }); - // Show object bounds - debug_menu.subitems.push_back({ - ICON_MD_CROP_SQUARE " Show Object Bounds", + // Show object bounds with sub-menu for categories + gui::Canvas::ContextMenuItem object_bounds_menu; + object_bounds_menu.label = ICON_MD_CROP_SQUARE " Show Object Bounds"; + object_bounds_menu.callback = [this]() { + show_object_bounds_ = !show_object_bounds_; + }; + + // Sub-menu for filtering by type + object_bounds_menu.subitems.push_back({ + "Type 1 (0x00-0xFF)", [this]() { - show_object_bounds_ = !show_object_bounds_; + object_outline_toggles_.show_type1_objects = !object_outline_toggles_.show_type1_objects; } }); + object_bounds_menu.subitems.push_back({ + "Type 2 (0x100-0x1FF)", + [this]() { + object_outline_toggles_.show_type2_objects = !object_outline_toggles_.show_type2_objects; + } + }); + object_bounds_menu.subitems.push_back({ + "Type 3 (0xF00-0xFFF)", + [this]() { + object_outline_toggles_.show_type3_objects = !object_outline_toggles_.show_type3_objects; + } + }); + + // Separator + gui::Canvas::ContextMenuItem sep; + sep.label = "---"; + sep.enabled_condition = []() { return false; }; + object_bounds_menu.subitems.push_back(sep); + + // Sub-menu for filtering by layer + object_bounds_menu.subitems.push_back({ + "Layer 0 (BG1)", + [this]() { + object_outline_toggles_.show_layer0_objects = !object_outline_toggles_.show_layer0_objects; + } + }); + object_bounds_menu.subitems.push_back({ + "Layer 1 (BG2)", + [this]() { + object_outline_toggles_.show_layer1_objects = !object_outline_toggles_.show_layer1_objects; + } + }); + object_bounds_menu.subitems.push_back({ + "Layer 2 (BG3)", + [this]() { + object_outline_toggles_.show_layer2_objects = !object_outline_toggles_.show_layer2_objects; + } + }); + + debug_menu.subitems.push_back(object_bounds_menu); // Show layer info debug_menu.subitems.push_back({ @@ -360,6 +407,19 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) { ImGui::Checkbox("BG1 Visible", &layer_settings.bg1_visible); ImGui::Checkbox("BG2 Visible", &layer_settings.bg2_visible); ImGui::SliderInt("BG2 Type", &layer_settings.bg2_layer_type, 0, 4); + + if (show_object_bounds_) { + ImGui::Separator(); + ImGui::Text("Object Outline Filters"); + ImGui::Text("By Type:"); + ImGui::Checkbox("Type 1", &object_outline_toggles_.show_type1_objects); + ImGui::Checkbox("Type 2", &object_outline_toggles_.show_type2_objects); + ImGui::Checkbox("Type 3", &object_outline_toggles_.show_type3_objects); + ImGui::Text("By Layer:"); + ImGui::Checkbox("Layer 0", &object_outline_toggles_.show_layer0_objects); + ImGui::Checkbox("Layer 1", &object_outline_toggles_.show_layer1_objects); + ImGui::Checkbox("Layer 2", &object_outline_toggles_.show_layer2_objects); + } } ImGui::End(); } @@ -694,6 +754,33 @@ void DungeonCanvasViewer::DrawObjectPositionOutlines(const zelda3::Room& room) { const auto& objects = room.GetTileObjects(); for (const auto& obj : objects) { + // Filter by object type (default to true if unknown type) + bool show_this_type = true; // Default to showing + if (obj.id_ < 0x100) { + show_this_type = object_outline_toggles_.show_type1_objects; + } else if (obj.id_ >= 0x100 && obj.id_ < 0x200) { + show_this_type = object_outline_toggles_.show_type2_objects; + } else if (obj.id_ >= 0xF00) { + show_this_type = object_outline_toggles_.show_type3_objects; + } + // else: unknown type, use default (true) + + // Filter by layer (default to true if unknown layer) + bool show_this_layer = true; // Default to showing + if (obj.GetLayerValue() == 0) { + show_this_layer = object_outline_toggles_.show_layer0_objects; + } else if (obj.GetLayerValue() == 1) { + show_this_layer = object_outline_toggles_.show_layer1_objects; + } else if (obj.GetLayerValue() == 2) { + show_this_layer = object_outline_toggles_.show_layer2_objects; + } + // else: unknown layer, use default (true) + + // Skip if filtered out + if (!show_this_type || !show_this_layer) { + continue; + } + // Convert object position (tile coordinates) to canvas pixel coordinates (UNSCALED) auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(obj.x(), obj.y()); @@ -718,24 +805,19 @@ void DungeonCanvasViewer::DrawObjectPositionOutlines(const zelda3::Room& room) { // Color-code by layer ImVec4 outline_color; if (obj.GetLayerValue() == 0) { - outline_color = ImVec4(1.0f, 0.0f, 0.0f, 0.7f); // Red for layer 0 + outline_color = ImVec4(1.0f, 0.0f, 0.0f, 0.5f); // Red for layer 0 } else if (obj.GetLayerValue() == 1) { - outline_color = ImVec4(0.0f, 1.0f, 0.0f, 0.7f); // Green for layer 1 + outline_color = ImVec4(0.0f, 1.0f, 0.0f, 0.5f); // Green for layer 1 } else { - outline_color = ImVec4(0.0f, 0.0f, 1.0f, 0.7f); // Blue for layer 2 + outline_color = ImVec4(0.0f, 0.0f, 1.0f, 0.5f); // Blue for layer 2 } // Draw outline rectangle canvas_.DrawRect(canvas_x, canvas_y, width, height, outline_color); - // Draw object ID label - std::string label = absl::StrFormat("0x%02X", obj.id_); - canvas_.DrawText(label, canvas_x + 2, canvas_y + 2); - } - - // Log object count - if (!objects.empty()) { - LOG_DEBUG("[DrawObjectPositionOutlines]", "Drew %zu object outlines", objects.size()); + // Draw object ID label (smaller, less obtrusive) + std::string label = absl::StrFormat("%02X", obj.id_); + canvas_.DrawText(label, canvas_x + 1, canvas_y + 1); } } diff --git a/src/app/editor/dungeon/dungeon_canvas_viewer.h b/src/app/editor/dungeon/dungeon_canvas_viewer.h index d1f2493a..e7d4cec9 100644 --- a/src/app/editor/dungeon/dungeon_canvas_viewer.h +++ b/src/app/editor/dungeon/dungeon_canvas_viewer.h @@ -160,6 +160,17 @@ class DungeonCanvasViewer { bool show_texture_debug_ = false; bool show_object_bounds_ = false; bool show_layer_info_ = false; + + // Object outline category toggles + struct ObjectOutlineToggles { + bool show_type1_objects = true; // Standard objects (0x00-0xFF) + bool show_type2_objects = true; // Extended objects (0x100-0x1FF) + bool show_type3_objects = true; // Special objects (0xF00-0xFFF) + bool show_layer0_objects = true; // Layer 0 (BG1) + bool show_layer1_objects = true; // Layer 1 (BG2) + bool show_layer2_objects = true; // Layer 2 (BG3) + }; + ObjectOutlineToggles object_outline_toggles_; }; } // namespace editor diff --git a/src/app/gfx/snes_palette.cc b/src/app/gfx/snes_palette.cc index be47f0b1..cad10270 100644 --- a/src/app/gfx/snes_palette.cc +++ b/src/app/gfx/snes_palette.cc @@ -323,9 +323,14 @@ absl::StatusOr CreatePaletteGroupFromLargePalette( PaletteGroup palette_group; for (int i = 0; i < palette.size(); i += num_colors) { SnesPalette new_palette; - if (i + num_colors < palette.size()) { + if (i + num_colors <= palette.size()) { for (int j = 0; j < num_colors; j++) { - new_palette.AddColor(palette[i + j]); + auto color = palette[i + j]; + // Ensure first color of each sub-palette (index 0) is transparent + if (j == 0) { + color.set_transparent(true); + } + new_palette.AddColor(color); } } palette_group.AddPalette(new_palette); diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index c11b2dca..986bdd09 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -554,6 +554,15 @@ void Canvas::DrawContextMenu() { modals_->Render(); } + // CRITICAL: Render custom context menu items AFTER enhanced menu + // Don't return early - we need to show custom items too! + if (!context_menu_items_.empty() && ImGui::BeginPopupContextItem(context_id_.c_str())) { + for (const auto& item : context_menu_items_) { + DrawContextMenuItem(item); + } + ImGui::EndPopup(); + } + return; } diff --git a/src/app/zelda3/dungeon/room.cc b/src/app/zelda3/dungeon/room.cc index 42329c94..763c8835 100644 --- a/src/app/zelda3/dungeon/room.cc +++ b/src/app/zelda3/dungeon/room.cc @@ -314,8 +314,11 @@ void Room::RenderRoomGraphics() { int num_palettes = dungeon_pal_group.size(); int palette_id = palette; + LOG_DEBUG("[RenderRoomGraphics]", "Room palette byte: %d, num_palettes available: %d", + palette_id, num_palettes); + if (palette_id < 0 || palette_id >= num_palettes) { - LOG_DEBUG("[RenderRoomGraphics]", "WARNING: palette_id %d out of bounds, using %d", + LOG_DEBUG("[RenderRoomGraphics]", "WARNING: palette_id %d out of bounds, clamping to %d", palette_id, palette_id % num_palettes); palette_id = palette_id % num_palettes; } @@ -323,9 +326,22 @@ void Room::RenderRoomGraphics() { auto bg1_palette = dungeon_pal_group[palette_id]; if (bg1_palette.size() > 0) { + LOG_DEBUG("[RenderRoomGraphics]", "Applying palette_id=%d with %d colors to bitmaps", + palette_id, (int)bg1_palette.size()); + + // Debug: Log first few palette colors + for (int i = 0; i < std::min(16, (int)bg1_palette.size()); i++) { + auto rgb = bg1_palette[i].rgb(); + LOG_DEBUG("[RenderRoomGraphics]", " Palette[%d]: R=%.0f G=%.0f B=%.0f A=%.0f %s", + i, rgb.x, rgb.y, rgb.z, rgb.w, + bg1_palette[i].is_transparent() ? "(TRANSPARENT)" : ""); + } + // Apply FULL 90-color dungeon palette bg1_bmp.SetPalette(bg1_palette); bg2_bmp.SetPalette(bg1_palette); + } else { + LOG_DEBUG("[RenderRoomGraphics]", "ERROR: Palette is empty!"); } // Render objects ON TOP of background tiles (AFTER palette is set) diff --git a/src/app/zelda3/dungeon/room_layout.cc b/src/app/zelda3/dungeon/room_layout.cc index b0bc7804..1d928e4e 100644 --- a/src/app/zelda3/dungeon/room_layout.cc +++ b/src/app/zelda3/dungeon/room_layout.cc @@ -162,29 +162,18 @@ absl::Status RoomLayout::ParseLayoutData(const std::vector& data) { uint8_t tile_id = data[index]; // Determine object type based on tile ID + // NOTE: Layout format needs research - using simplified heuristics RoomLayoutObject::Type type = RoomLayoutObject::Type::kUnknown; + if (tile_id == 0) { // Empty space - skip continue; - } else if (tile_id >= 0x01 && tile_id <= 0x20) { - // Wall tiles - type = RoomLayoutObject::Type::kWall; - } else if (tile_id >= 0x21 && tile_id <= 0x40) { - // Floor tiles - type = RoomLayoutObject::Type::kFloor; - } else if (tile_id >= 0x41 && tile_id <= 0x60) { - // Ceiling tiles - type = RoomLayoutObject::Type::kCeiling; - } else if (tile_id >= 0x61 && tile_id <= 0x80) { - // Water tiles - type = RoomLayoutObject::Type::kWater; - } else if (tile_id >= 0x81 && tile_id <= 0xA0) { - // Stairs - type = RoomLayoutObject::Type::kStairs; - } else if (tile_id >= 0xA1 && tile_id <= 0xC0) { - // Doors - type = RoomLayoutObject::Type::kDoor; } + + // Just mark everything as unknown for now + // The room graphics bitmap handles the actual visual appearance + // Layout objects are just for structural information + type = RoomLayoutObject::Type::kUnknown; // Create layout object objects_.emplace_back(tile_id, x, y, type, 0);