refactor: Consolidate Canvas Documentation and Update Structure

- Streamlined the canvas documentation by consolidating multiple guides into a single comprehensive overview.
- Updated the canvas architecture section to reflect new features and interaction modes, enhancing clarity for users.
- Improved API patterns and integration steps for editors, ensuring consistency across documentation.
- Removed outdated content and added new sections on automation and debugging, aligning with recent code changes.
- Adjusted file paths in the documentation to match the current project structure, ensuring accurate references.
This commit is contained in:
scawful
2025-10-10 13:03:43 -04:00
parent d124ab962f
commit c96272296c
6 changed files with 79 additions and 553 deletions

View File

@@ -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