diff --git a/docs/F2-dungeon-editor-v2-guide.md b/docs/F2-dungeon-editor-v2-guide.md index e3366c29..b562afe4 100644 --- a/docs/F2-dungeon-editor-v2-guide.md +++ b/docs/F2-dungeon-editor-v2-guide.md @@ -1,8 +1,6 @@ # F2: Dungeon Editor v2 - Complete Guide -**Version**: v0.4.0 **Last Updated**: October 10, 2025 -**Status**: ✅ Production Ready **Related**: [E2-development-guide.md](E2-development-guide.md), [E5-debugging-guide.md](E5-debugging-guide.md) --- @@ -21,54 +19,6 @@ The Dungeon Editor uses a modern card-based architecture (DungeonEditorV2) with --- -## Recent Refactoring (Oct 9-10, 2025) - -### Critical Bugs Fixed ✅ - -#### Bug #1: Segfault on Startup -**Cause**: `ImGui::GetID()` called before ImGui context ready -**Fix**: Moved to `Update()` when ImGui is initialized -**File**: `dungeon_editor_v2.cc:160-163` - -#### Bug #2: Floor Values Always Zero -**Cause**: `RenderRoomGraphics()` called before `LoadObjects()` -**Fix**: Correct loading sequence: -```cpp -// CORRECT ORDER: -if (room.blocks().empty()) { - room.LoadRoomGraphics(room.blockset); // 1. Load blocks from ROM -} -if (room.GetTileObjects().empty()) { - room.LoadObjects(); // 2. Load objects (SETS floor1_graphics_!) -} -if (needs_render || !bg1_bitmap.is_active()) { - room.RenderRoomGraphics(); // 3. Render with correct floor values -} -``` -**Impact**: Floor graphics now load correctly (4, 8, etc. instead of 0) - -#### Bug #3: Duplicate Floor Variables -**Cause**: `floor1` (public) vs `floor1_graphics_` (private) - two sources of truth -**Fix**: Removed public members, added accessors: `floor1()`, `set_floor1()` -**Impact**: UI floor edits now trigger immediate re-render - -#### Bug #4: Wall Graphics Not Rendering -**Cause**: Textures created BEFORE objects drawn, never updated -**Fix**: Added UPDATE commands after `RenderObjectsToBackground()` -```cpp -// room.cc:327-344 -RenderObjectsToBackground(); // Draw objects to bitmaps - -// Update textures with new data -if (bg1_bmp.texture()) { - gfx::Arena::Get().QueueTextureCommand(UPDATE, &bg1_bmp); - gfx::Arena::Get().QueueTextureCommand(UPDATE, &bg2_bmp); -} else { - gfx::Arena::Get().QueueTextureCommand(CREATE, &bg1_bmp); - gfx::Arena::Get().QueueTextureCommand(CREATE, &bg2_bmp); -} -``` - ### Architecture Improvements ✅ 1. **Room Buffers Decoupled** - No dependency on Arena graphics sheets 2. **ObjectRenderer Removed** - Standardized on ObjectDrawer (~1000 lines deleted) @@ -112,6 +62,8 @@ Room (Data Layer) ### Room Rendering Pipeline +TODO: Update this to latest code. + ``` 1. LoadRoomGraphics(blockset) └─> Reads blocks[] from ROM @@ -146,9 +98,8 @@ Understanding ALTTP dungeon composition is critical: ``` Room Composition: ├─ Room Layout (BASE LAYER - immovable) -│ ├─ Walls (structural boundaries) -│ ├─ Floors (walkable areas) -│ └─ Pits (holes/damage zones) +│ ├─ Walls (structural boundaries, 7 configurations of squares in 2x2 grid) +│ ├─ Floors (walkable areas, repeated tile pattern set to BG1/BG2) ├─ Layer 0 Objects (floor decorations, some walls) ├─ Layer 1 Objects (chests, decorations) └─ Layer 2 Objects (stairs, transitions) @@ -164,43 +115,7 @@ Doors: Positioned at room edges to connect rooms ### High Priority (Must Do) -#### 1. Implement Room Layout Base Layer Rendering -**File**: `dungeon_canvas_viewer.cc:377-391` (stub exists) - -**What**: Render the immovable room structure (walls, floors, pits) - -**Implementation**: -```cpp -void DungeonCanvasViewer::DrawRoomLayout(const zelda3::Room& room) { - const auto& layout = room.GetLayout(); - - // TODO: Load layout if not loaded - // layout.LoadLayout(room_id); - - // Get structural elements - auto walls = layout.GetObjectsByType(RoomLayoutObject::Type::kWall); - auto floors = layout.GetObjectsByType(RoomLayoutObject::Type::kFloor); - auto pits = layout.GetObjectsByType(RoomLayoutObject::Type::kPit); - - // Draw walls (dark gray, semi-transparent) - for (const auto& wall : walls) { - auto [x, y] = RoomToCanvasCoordinates(wall.x(), wall.y()); - canvas_.DrawRect(x, y, 8, 8, ImVec4(0.3f, 0.3f, 0.3f, 0.6f)); - } - - // Draw pits (orange warning) - for (const auto& pit : pits) { - auto [x, y] = RoomToCanvasCoordinates(pit.x(), pit.y()); - canvas_.DrawRect(x, y, 8, 8, ImVec4(1.0f, 0.5f, 0.0f, 0.7f)); - } -} -``` - -**Reference**: `src/app/zelda3/dungeon/room_layout.h/cc` for LoadLayout() logic - ---- - -#### 2. Door Rendering at Room Edges +#### 1. Door Rendering at Room Edges **What**: Render doors with proper patterns at room connections **Pattern Reference**: ZScream's door drawing patterns @@ -220,7 +135,7 @@ void DungeonCanvasViewer::DrawDoors(const zelda3::Room& room) { --- -#### 3. Object Name Labels from String Array +#### 2. Object Name Labels from String Array **File**: `dungeon_canvas_viewer.cc:416` (DrawObjectPositionOutlines) **What**: Show real object names instead of just IDs @@ -275,19 +190,6 @@ if (ImGui::BeginPopup("SelectRoomToOpen")) { ### Medium Priority (Should Do) -#### 5. Update current_room_id on Card Hover -**What**: Update DungeonEditorV2::current_room_id_ when hovering room cards - -**Implementation**: -```cpp -// In dungeon_editor_v2.cc, after room_card->Begin(): -if (ImGui::IsWindowHovered()) { - current_room_id_ = room_id; -} -``` - ---- - #### 6. Fix InputHexByte +/- Button Events **File**: `src/app/gui/input.cc` (likely) @@ -298,36 +200,9 @@ if (ImGui::IsWindowHovered()) { - Verify event logic matches working examples - Keep existing event style if it works elsewhere ---- - -#### 7. Update Room Graphics Card -**File**: `dungeon_editor_v2.cc:856-920` - -**What**: Show per-room graphics from `current_gfx16_` instead of Arena sheets - -**Implementation**: -```cpp -// Instead of Arena sheets: -auto& gfx_sheet = gfx::Arena::Get().gfx_sheets()[block]; - -// Use room's current_gfx16_: -const auto& gfx_buffer = room.get_gfx_buffer(); // Returns current_gfx16_ -// Extract 128x128 block from gfx_buffer -// Display as 128x32 strips (16 blocks, 2 columns) -``` - ---- ### Lower Priority (Nice to Have) -#### 8. Selection System with Primitive Squares -**What**: Allow selecting objects even if graphics don't render - -**Current**: Selection works on bitmaps -**Enhancement**: Selection works on position outlines - ---- - #### 9. Move Backend Logic to DungeonEditorSystem **What**: Separate UI (V2) from data operations (System) @@ -341,13 +216,6 @@ const auto& gfx_buffer = room.get_gfx_buffer(); // Returns current_gfx16_ --- -#### 10. Extract ROM Addresses to Separate File -**File**: `room.h` lines 18-84 (66 lines of constants) - -**Action**: Move to `dungeon_rom_addresses.h` - ---- - ## Quick Start ### Build & Run @@ -363,16 +231,6 @@ cmake --build build_ai --target yaze -j12 ./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0x00" ``` -### Expected Visuals -- ✅ **Floor tiles**: Proper dungeon graphics with correct colors -- ✅ **Floor values**: Show 4, 8, etc. (not 0!) -- ✅ **Object outlines**: Colored rectangles by layer - - 🟥 Red = Layer 0 (walls, floors) - - 🟩 Green = Layer 1 (decorations, chests) - - 🟦 Blue = Layer 2 (stairs, transitions) -- ✅ **Object IDs**: Labels like "0x10", "0x20" -- ⏳ **Wall graphics**: Should render inside rectangles (needs verification) - --- ## Testing & Verification @@ -391,230 +249,6 @@ cmake --build build_ai --target yaze -j12 ./build_ai/bin/yaze.app/Contents/MacOS/yaze --rom_file=zelda3.sfc --editor=Dungeon 2>&1 | grep "Writing Tile16" ``` -### Visual Checklist -- [ ] Floor tiles render with correct colors -- [ ] Object position outlines visible -- [ ] Room ID shows in card title as `[000] Ganon` -- [ ] Properties in clean table layout (4 columns) -- [ ] Layer controls compact (1 row) -- [ ] Can edit floor1/floor2 values -- [ ] Changes update canvas immediately -- [ ] Room graphics card height correct (257px, not 1025px) - ---- - -## Technical Reference - -### Correct Loading Order -The loading sequence is **critical**: - -```cpp -1. LoadRoomGraphics(blockset) - Loads blocks[], current_gfx16_ -2. LoadObjects() - Parses objects, SETS floor graphics -3. RenderRoomGraphics() - Uses floor graphics from step 2 -``` - -**Why**: `LoadObjects()` sets `floor1_graphics_` and `floor2_graphics_` during parsing. If you render before loading objects, floor values are still 0! - -### Floor Graphics Accessors -```cpp -// room.h:341-350 -uint8_t floor1() const { return floor1_graphics_; } -uint8_t floor2() const { return floor2_graphics_; } -void set_floor1(uint8_t value) { - floor1_graphics_ = value; - // UI code triggers re-render when changed -} -``` - -### Object Visualization -```cpp -// dungeon_canvas_viewer.cc:394-425 -void DungeonCanvasViewer::DrawObjectPositionOutlines(const zelda3::Room& room) { - for (const auto& obj : room.GetTileObjects()) { - auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(obj.x(), obj.y()); - - // Size from object.size_ field - int width = ((obj.size() & 0x0F) + 1) * 8; - int height = (((obj.size() >> 4) & 0x0F) + 1) * 8; - - // Color by layer - ImVec4 color = (layer == 0) ? Red : (layer == 1) ? Green : Blue; - - canvas_.DrawRect(canvas_x, canvas_y, width, height, color); - canvas_.DrawText(absl::StrFormat("0x%02X", obj.id_), canvas_x + 2, canvas_y + 2); - } -} -``` - -### Texture Atlas (Future-Proof Stub) -```cpp -// src/app/gfx/texture_atlas.h -class TextureAtlas { - AtlasRegion* AllocateRegion(int source_id, int width, int height); - absl::Status PackBitmap(const Bitmap& src, const AtlasRegion& region); - absl::Status DrawRegion(int source_id, int dest_x, int dest_y); -}; - -// Future usage: -TextureAtlas atlas(2048, 2048); -auto* region = atlas.AllocateRegion(room_id, 512, 512); -atlas.PackBitmap(room.bg1_buffer().bitmap(), *region); -atlas.DrawRegion(room_id, x, y); -``` - -When implemented: -- Single GPU texture for all rooms -- Fewer texture binds per frame -- Better performance with many rooms - ---- - -## Files Modified (16 files) - -### Dungeon Editor -``` -✅ src/app/editor/dungeon/dungeon_editor_v2.cc - - Room ID in title `[003] Room Name` - - Object count in status bar - - Room graphics canvas 257x257 (fixed from 1025 tall) - - Loading order fix (CRITICAL) - -✅ src/app/editor/dungeon/dungeon_canvas_viewer.h/cc - - Properties in table layout - - Compact layer controls - - DrawRoomLayout() stub - - DrawObjectPositionOutlines() working - - Removed ObjectRenderer - -✅ src/app/editor/dungeon/dungeon_object_selector.h/cc - - Removed ObjectRenderer - - TODO for ObjectDrawer-based preview -``` - -### Room System -``` -✅ src/app/zelda3/dungeon/room.h - - floor1()/floor2() accessors - - Removed LoadGraphicsSheetsIntoArena() - -✅ src/app/zelda3/dungeon/room.cc - - Removed LoadGraphicsSheetsIntoArena() impl - - Added UPDATE texture commands - - Palette before objects (correct order) - - Debug logging -``` - -### Graphics Infrastructure -``` -✅ src/app/gfx/texture_atlas.h - NEW -✅ src/app/gfx/texture_atlas.cc - NEW -✅ src/app/gfx/gfx_library.cmake - Added texture_atlas.cc -``` - -### Tests -``` -❌ test/unit/zelda3/dungeon_object_renderer_mock_test.cc - DELETED -❌ test/integration/zelda3/dungeon_object_renderer_integration_test.cc - DELETED -✅ test/CMakeLists.txt - Updated -✅ test/unit/zelda3/test_dungeon_objects.cc - ObjectDrawer -✅ test/integration/zelda3/dungeon_object_rendering_tests.cc - Simplified -``` - ---- - -## Statistics - -``` -Tasks Completed: 13/20 (65%) -Code Deleted: ~1600 lines (tests + obsolete methods) -Code Added: ~400 lines (fixes + features + atlas) -Net Change: -1200 lines -Files Modified: 16 -Files Deleted: 2 (tests) -Files Created: 2 (atlas.h/cc) -Documentation: Consolidated 4 guides → 1 -Build Status: ✅ Core libraries compile -User Verification: ✅ "it does render correct now" -``` - ---- - -## Troubleshooting - -### Floor tiles blank/wrong? -**Check**: Debug output should show `floor1=4, floor2=8` (NOT 0) -**Fix**: Verify loading order in `dungeon_editor_v2.cc:442-460` - -### Objects not visible? -**Check**: Object outlines should show colored rectangles -**If no outlines**: LoadObjects() failed -**If outlines but no graphics**: ObjectDrawer or tile data issue - -### Wall graphics not rendering? -**Check**: Texture UPDATE commands in `room.cc:332-344` -**Debug**: Check ObjectDrawer logs for "Writing Tile16" -**Verify**: Objects drawn to bitmaps before texture update - -### Performance issues? -**Cause**: Each room = ~2MB (2x 512x512 bitmaps) -**Solution**: Close unused room windows or implement texture pooling - ---- - -## Session Summary - -### Accomplished This Session -- ✅ Fixed 6 critical bugs (segfault, loading order, floor variables, property detection, wall rendering, ObjectRenderer confusion) -- ✅ Decoupled room buffers from Arena -- ✅ Deleted 1270 lines of redundant test code -- ✅ UI improvements (tables, titles, compact controls) -- ✅ Object position visualization -- ✅ Texture atlas infrastructure -- ✅ Documentation consolidated - -### Statistics -- **Lines Deleted**: ~1600 -- **Lines Added**: ~400 -- **Net Change**: -1200 lines -- **Build Status**: ✅ Success -- **Test Status**: ✅ Core libraries pass - ---- - -## Code Reference - -### Property Table (NEW) -```cpp -// dungeon_canvas_viewer.cc:45-86 -if (ImGui::BeginTable("##RoomProperties", 4, ...)) { - // Graphics | Layout | Floors | Message - gui::InputHexByte("Gfx", &room.blockset); - gui::InputHexByte("Sprite", &room.spriteset); - gui::InputHexByte("Palette", &room.palette); - // ... etc -} -``` - -### Compact Layer Controls (NEW) -```cpp -// dungeon_canvas_viewer.cc:90-107 -if (ImGui::BeginTable("##LayerControls", 3, ...)) { - ImGui::Checkbox("Show BG1", &layer_settings.bg1_visible); - ImGui::Checkbox("Show BG2", &layer_settings.bg2_visible); - ImGui::Combo("##BG2Type", &layer_settings.bg2_layer_type, ...); -} -``` - -### Room ID in Title (NEW) -```cpp -// dungeon_editor_v2.cc:378 -base_name = absl::StrFormat("[%03X] %s", room_id, zelda3::kRoomNames[room_id]); -// Result: "[002] Behind Sanctuary (Switch)" -``` - ---- - ## Related Documentation - **E2-development-guide.md** - Core architectural patterns diff --git a/docs/G1-canvas-guide.md b/docs/G1-canvas-guide.md index e58c48ad..302d8a1a 100644 --- a/docs/G1-canvas-guide.md +++ b/docs/G1-canvas-guide.md @@ -1,179 +1,70 @@ -# G1 - Canvas System and Automation +# Canvas System Overview -This document provides a comprehensive guide to the Canvas system in yaze, including its architecture, features, and the powerful automation API that enables programmatic control for the `z3ed` CLI, AI agents, and GUI testing. +## Canvas Architecture +- **Canvas States**: track `canvas`, `content`, and `draw` rectangles independently; expose size/scale through `CanvasState` inspection panel +- **Layer Stack**: background ➝ bitmaps ➝ entity overlays ➝ selection/tooltip layers +- **Interaction Modes**: Tile Paint, Tile Select, Rectangle Select, Entity Manipulation, Palette Editing, Diagnostics +- **Context Menu**: persistent menu with material icon sections (Mode, View, Info, Bitmap, Palette, BPP, Performance, Layout, Custom) -## 1. Core Concepts +## Core API Patterns +- Modern usage: `Begin/End` (auto grid/overlay, persistent context menu) +- Legacy helpers still available (`DrawBackground`, `DrawGrid`, `DrawSelectRect`, etc.) +- Unified state snapshot: `CanvasState` exposes geometry, zoom, scroll +- Interaction handler manages mode-specific tools (tile brush, select rect, entity gizmo) -### Canvas Structure -- **Background**: Drawing surface with border and optional scrolling -- **Content Layer**: Bitmaps, tiles, custom graphics -- **Grid Overlay**: Optional grid with hex labels -- **Interaction Layer**: Hover previews, selection rectangles +## Context Menu Sections +- **Mode Selector**: switch modes with icons (Brush, Select, Rect, Bitmap, Palette, BPP, Perf) +- **View & Grid**: reset/zoom, toggle grid/labels, advanced/scaling dialogs +- **Canvas Info**: real-time canvas/content size, scale, scroll, mouse position +- **Bitmap/Palette/BPP**: format conversion, palette analysis, BPP workflows with persistent modals +- **Performance**: profiler metrics, dashboard, usage report +- **Layout**: draggable toggle, auto resize, grid step +- **Custom Actions**: consumer-provided menu items -### Coordinate Systems -- **Screen Space**: ImGui window coordinates -- **Canvas Space**: Relative to canvas origin (0,0) -- **Tile Space**: Grid-aligned tile indices -- **World Space**: Overworld 4096x4096 large map coordinates +## Interaction Modes & Capabilities +- **Tile Painting**: tile16 painter, brush size, finish stroke callbacks + - Operations: finish_paint, reset_view, zoom, grid, scaling +- **Tile Selection**: multi-select rectangle, copy/paste selection + - Operations: select_all, clear_selection, reset_view, zoom, grid, scaling +- **Rectangle Selection**: drag-select area, clear selection + - Operations: clear_selection, reset_view, zoom, grid, scaling +- **Bitmap Editing**: format conversion, bitmap manipulation + - Operations: bitmap_convert, palette_edit, bpp_analysis, reset_view, zoom, grid, scaling +- **Palette Editing**: inline palette editor, ROM palette picker, color analysis + - Operations: palette_edit, palette_analysis, reset_palette, reset_view, zoom, grid, scaling +- **BPP Conversion**: format analysis, conversion workflows + - Operations: bpp_analysis, bpp_conversion, bitmap_convert, reset_view, zoom, grid, scaling +- **Performance Mode**: diagnostics, texture queue, performance overlays + - Operations: performance, usage_report, copy_metrics, reset_view, zoom, grid, scaling -## 2. Canvas API and Usage +## Debug & Diagnostics +- Persistent modals (`View→Advanced`, `View→Scaling`, `Palette`, `BPP`) stay open until closed +- Texture inspector shows current bitmap, VRAM sheet, palette group, usage stats +- State overlay: canvas size, content size, global scale, scroll, highlight entity +- Performance HUD: operation counts, timing graphs, usage recommendations -### Modern Begin/End Pattern -The recommended way to use the canvas is with the `Begin()`/`End()` pattern, which handles setup and teardown automatically. +## Automation API +- CanvasAutomationAPI: tile operations (`SetTileAt`, `SelectRect`), view control (`ScrollToTile`, `SetZoom`), entity manipulation hooks +- Exposed through CLI (`z3ed`) and gRPC service, matching UI modes -```cpp -canvas.Begin(ImVec2(512, 512)); -canvas.DrawBitmap(bitmap, 0, 0, 2.0f); -canvas.End(); // Automatic grid + overlay -``` +## Integration Steps for Editors +1. Construct `Canvas`, set renderer (optional) and ID +2. Call `InitializePaletteEditor` and `SetUsageMode` +3. Configure available modes: `SetAvailableModes({kTilePainting, kTileSelecting})` +4. Register mode callbacks (tile paint finish, selection clear, etc.) +5. During frame: `canvas.Begin(size)` → draw bitmaps/entities → `canvas.End()` +6. Provide custom menu items via `AddMenuItem`/`AddMenuItem(item, usage)` +7. Use `GetConfig()`/`GetSelection()` for state; respond to context menu commands via callback lambda in `Render` -### Feature: Tile Painting -The canvas provides methods for painting single tiles, painting from a tilemap, and painting with solid colors. +## Migration Checklist +- Replace direct `DrawContextMenu` logic with new render callback signature +- Move palette/BPP helpers into `canvas/` module; update includes +- Ensure persistent modals wired (advanced/scaling/palette/bpp/perf) +- Update usage tracker integrations to record mode switches +- Validate overworld/tile16/dungeon editors in tile paint, select, entity modes -```cpp -if (canvas.DrawTilePainter(current_tile_bitmap, 16, 2.0f)) { - ImVec2 paint_pos = canvas.drawn_tile_position(); - ApplyTileToMap(paint_pos, current_tile_id); -} -``` - -### Feature: Tile Selection -The canvas supports both single-tile and multi-tile rectangle selection. - -```cpp -canvas.DrawSelectRect(current_map_id, 16, 1.0f); - -if (canvas.select_rect_active()) { - const auto& selected_tiles = canvas.selected_tiles(); - // Process selection... -} -``` - -### Feature: Large Map Support -The canvas can handle large maps with multiple local maps, including boundary clamping to prevent selection wrapping. - -```cpp -canvas.SetClampRectToLocalMaps(true); // Default - prevents wrapping -``` - -### Feature: Context Menu -The canvas has a customizable context menu. - -```cpp -canvas.AddContextMenuItem({ - "My Action", - [this]() { DoAction(); } -}); -``` - -## 3. Canvas Automation API - -The `CanvasAutomationAPI` provides a programmatic interface for controlling canvas operations, enabling scripted editing, AI agent integration, and automated testing. - -### Accessing the API -```cpp -auto* api = canvas.GetAutomationAPI(); -``` - -### Tile Operations -```cpp -// Paint a single tile -bool SetTileAt(int x, int y, int tile_id); - -// Get the tile ID at a specific location -int GetTileAt(int x, int y) const; - -// Paint multiple tiles in a batch -bool SetTiles(const std::vector>& tiles); -``` - -### Selection Operations -```cpp -// Select a single tile -void SelectTile(int x, int y); - -// Select a rectangular region of tiles -void SelectTileRect(int x1, int y1, int x2, int y2); - -// Get the current selection state -SelectionState GetSelection() const; - -// Clear the current selection -void ClearSelection(); -``` - -### View Operations -```cpp -// Scroll the canvas to make a tile visible -void ScrollToTile(int x, int y); - -// Set the canvas zoom level -void SetZoom(float zoom_level); - -// Get the current zoom level -float GetZoom() const; - -// Center the canvas view on a specific coordinate -void CenterOn(int x, int y); -``` - -### Query Operations -```cpp -// Get the dimensions of the canvas in tiles -CanvasDimensions GetDimensions() const; - -// Get the currently visible region of the canvas -VisibleRegion GetVisibleRegion() const; - -// Check if a tile is currently visible -bool IsTileVisible(int x, int y) const; - -// Get the cursor position in logical tile coordinates -ImVec2 GetCursorPosition() const; -``` - -### Simulation Operations (for GUI Automation) -```cpp -// Simulate a mouse click at a specific tile -void SimulateClick(int x, int y, ImGuiMouseButton button = ImGuiMouseButton_Left); - -// Simulate a drag operation between two tiles -void SimulateDrag(int x1, int y1, int x2, int y2); - -// Wait for the canvas to finish processing -void WaitForIdle(); -``` - -## 4. `z3ed` CLI Integration - -The Canvas Automation API is exposed through the `z3ed` CLI, allowing for scripted overworld editing. - -```bash -# Set a tile -z3ed overworld set-tile --map 0 --x 10 --y 10 --tile-id 0x0042 --rom zelda3.sfc - -# Get a tile -z3ed overworld get-tile --map 0 --x 10 --y 10 --rom zelda3.sfc - -# Select a rectangle -z3ed overworld select-rect --map 0 --x1 5 --y1 5 --x2 15 --y2 15 --rom zelda3.sfc - -# Scroll to a tile -z3ed overworld scroll-to --map 0 --x 20 --y 20 --center --rom zelda3.sfc -``` - -## 5. gRPC Service - -The Canvas Automation API is also exposed via a gRPC service, allowing for remote control of the canvas. - -**Proto Definition (`protos/canvas_automation.proto`):** -```protobuf -service CanvasAutomation { - rpc SetTileAt(SetTileRequest) returns (SetTileResponse); - rpc GetTileAt(GetTileRequest) returns (GetTileResponse); - rpc SelectTileRect(SelectTileRectRequest) returns (SelectTileRectResponse); - // ... and so on for all API methods -} -``` - -This service is hosted by the `UnifiedGRPCServer` in the main yaze application, allowing tools like `grpcurl` or custom clients to interact with the canvas remotely. +## Testing Notes +- Manual regression: overworld paint/select, tile16 painter, dungeon entity drag +- Verify context menu persists and modals remain until closed +- Ensure palette/BPP modals populate with correct bitmap/palette data +- Automation: run CanvasAutomation API tests/end-to-end scripts for overworld edits diff --git a/docs/G3-renderer-migration-complete.md b/docs/G3-renderer-migration-complete.md index d18e3c58..d225f091 100644 --- a/docs/G3-renderer-migration-complete.md +++ b/docs/G3-renderer-migration-complete.md @@ -440,7 +440,7 @@ SDL_Surface* Arena::AllocateSurface(int w, int h, int depth, int format) { - `src/app/emu/emu.cc` - Standalone emulator with SDL2Renderer ### GUI/Widget Files -- `src/app/gui/canvas_utils.cc` - Fixed palette application logic +- `src/app/gui/canvas/canvas_utils.cc` - Fixed palette application logic - `src/app/gui/canvas/canvas_context_menu.cc` - Arena queue for bitmap ops - `src/app/gui/widgets/palette_widget.cc` - Arena queue for palette changes - `src/app/gui/widgets/dungeon_object_emulator_preview.{h,cc}` - Optional renderer diff --git a/src/app/editor/overworld/overworld_editor.cc b/src/app/editor/overworld/overworld_editor.cc index d6fca05e..17d5f4b6 100644 --- a/src/app/editor/overworld/overworld_editor.cc +++ b/src/app/editor/overworld/overworld_editor.cc @@ -1115,7 +1115,6 @@ absl::Status OverworldEditor::CheckForCurrentMap() { break; case AreaSizeEnum::SmallArea: default: - // 1x1 grid (512x512) ow_map_canvas_.DrawOutline(parent_map_x * kOverworldMapSize, parent_map_y * kOverworldMapSize, kOverworldMapSize, kOverworldMapSize); @@ -1123,6 +1122,7 @@ absl::Status OverworldEditor::CheckForCurrentMap() { } } else { // Legacy logic for vanilla and v2 ROMs + int world_offset = current_world_ * 0x40; if (overworld_.overworld_map(current_map_)->is_large_map() || overworld_.overworld_map(current_map_)->large_index() != 0) { const int highlight_parent = @@ -1141,6 +1141,8 @@ absl::Status OverworldEditor::CheckForCurrentMap() { current_map_x = current_highlighted_map % 8; current_map_y = current_highlighted_map / 8; } else if (current_world_ == 1) { + // TODO: Vanilla fix dark world large map outline + // When switching to dark world, large maps dont outline properly. // Dark World (0x40-0x7F) current_map_x = (current_highlighted_map - 0x40) % 8; current_map_y = (current_highlighted_map - 0x40) / 8; @@ -2352,14 +2354,13 @@ void OverworldEditor::ScrollBlocksetCanvasToCurrentTile() { float tile_x = static_cast(tile_col * kTileDisplaySize); float tile_y = static_cast(tile_row * kTileDisplaySize); - ImVec2 canvas_size = blockset_canvas_.canvas_size(); - float scroll_x = tile_x - (canvas_size.x / 2.0F) + (kTileDisplaySize / 2.0F); - float scroll_y = tile_y - (canvas_size.y / 2.0F) + (kTileDisplaySize / 2.0F); + const ImVec2 window_size = ImGui::GetWindowSize(); + float scroll_x = tile_x - (window_size.x / 2.0F) + (kTileDisplaySize / 2.0F); + float scroll_y = tile_y - (window_size.y / 2.0F) + (kTileDisplaySize / 2.0F); - if (scroll_x < 0) scroll_x = 0; - if (scroll_y < 0) scroll_y = 0; - - blockset_canvas_.set_scrolling(ImVec2(-scroll_x, -scroll_y)); + // Use ImGui scroll API so both the canvas and child scroll view stay in sync. + ImGui::SetScrollX(std::max(0.0f, scroll_x)); + ImGui::SetScrollY(std::max(0.0f, scroll_y)); } void OverworldEditor::DrawOverworldProperties() { diff --git a/src/app/gui/canvas/canvas_modals.cc b/src/app/gui/canvas/canvas_modals.cc index 52362164..2387c09c 100644 --- a/src/app/gui/canvas/canvas_modals.cc +++ b/src/app/gui/canvas/canvas_modals.cc @@ -7,7 +7,7 @@ #include "app/gfx/performance/performance_profiler.h" #include "app/gfx/performance/performance_dashboard.h" #include "app/gui/widgets/palette_widget.h" -#include "app/gui/bpp_format_ui.h" +#include "app/gui/canvas/bpp_format_ui.h" #include "app/gui/icons.h" #include "imgui/imgui.h" diff --git a/src/app/gui/canvas/canvas_modals.h b/src/app/gui/canvas/canvas_modals.h index 2baf2814..affd0b6b 100644 --- a/src/app/gui/canvas/canvas_modals.h +++ b/src/app/gui/canvas/canvas_modals.h @@ -7,7 +7,7 @@ #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" #include "app/gfx/bpp_format_manager.h" -#include "app/gui/canvas_utils.h" +#include "app/gui/canvas/canvas_utils.h" #include "imgui/imgui.h" namespace yaze {