11 KiB
Dungeon Editor Design Plan
Overview
This document provides a comprehensive guide for future developers working on the Zelda 3 Dungeon Editor system. The dungeon editor has been refactored into a modular, component-based architecture that separates concerns and improves maintainability.
Architecture Overview
Core Components
The dungeon editor system consists of several key components:
- DungeonEditor - Main orchestrator class that manages the overall editor state
- DungeonRoomSelector - Handles room and entrance selection UI
- DungeonCanvasViewer - Manages the main canvas rendering and room display
- DungeonObjectSelector - Provides object selection, editing panels, and tile graphics
- ObjectRenderer - Core rendering engine for dungeon objects
- DungeonEditorSystem - High-level system for managing dungeon editing operations
File Structure
src/app/editor/dungeon/
├── dungeon_editor.h/cc # Main editor orchestrator
├── dungeon_room_selector.h/cc # Room/entrance selection component
├── dungeon_canvas_viewer.h/cc # Canvas rendering component
├── dungeon_object_selector.h/cc # Object editing component
└── dungeon_editor_system.h/cc # Core editing system
src/app/zelda3/dungeon/
├── object_renderer.h/cc # Object rendering engine
├── dungeon_object_editor.h/cc # Object editing logic
├── room.h/cc # Room data structures
├── room_object.h/cc # Object data structures
└── room_entrance.h/cc # Entrance data structures
Component Responsibilities
DungeonEditor (Main Orchestrator)
Responsibilities:
- Manages overall editor state and ROM data
- Coordinates between UI components
- Handles data initialization and propagation
- Implements the 3-column layout (Room Selector | Canvas | Object Selector)
Key Methods:
UpdateDungeonRoomView()- Main UI update loopLoad()- Initialize editor with ROM dataset_rom()- Update ROM reference across components
DungeonRoomSelector
Responsibilities:
- Room selection and navigation
- Entrance selection and editing
- Active room management
- Room list display with names
Key Methods:
Draw()- Main rendering methodDrawRoomSelector()- Room list and selectionDrawEntranceSelector()- Entrance editing interfaceset_rom(),set_rooms(),set_entrances()- Data access methods
DungeonCanvasViewer
Responsibilities:
- Main canvas rendering and display
- Room graphics loading and management
- Object rendering with proper coordinates
- Background layer management
- Coordinate conversion (room ↔ canvas)
Key Methods:
Draw(int room_id)- Main canvas renderingLoadAndRenderRoomGraphics()- Graphics loadingRenderObjectInCanvas()- Object renderingRoomToCanvasCoordinates()- Coordinate conversionRenderRoomBackgroundLayers()- Background rendering
DungeonObjectSelector
Responsibilities:
- Object selection and preview
- Tile graphics display
- Compact editing panels for all editor modes
- Object renderer integration
Key Methods:
Draw()- Main rendering with tabbed interfaceDrawRoomGraphics()- Tile graphics displayDrawIntegratedEditingPanels()- Editing interfaceDrawCompactObjectEditor()- Object editing controlsDrawCompactSpriteEditor()- Sprite editing controls- Similar methods for Items, Entrances, Doors, Chests, Properties
Data Flow
Initialization Flow
- ROM Loading:
DungeonEditor::Load()is called with ROM data - Component Setup: ROM and data pointers are propagated to all components
- Graphics Initialization: Room graphics are loaded and cached
- UI State Setup: Active rooms, palettes, and editor modes are initialized
Runtime Data Flow
- User Interaction: User selects rooms, objects, or editing modes
- State Updates: Components update their internal state
- Data Propagation: Changes are communicated between components
- Rendering: All components re-render with updated data
Key Data Structures
// Main editor state
std::array<zelda3::Room, 0x128> rooms_;
std::array<zelda3::RoomEntrance, 0x8C> entrances_;
ImVector<int> active_rooms_;
gfx::PaletteGroup current_palette_group_;
// Component instances
DungeonRoomSelector room_selector_;
DungeonCanvasViewer canvas_viewer_;
DungeonObjectSelector object_selector_;
Integration Patterns
Component Communication
Components communicate through:
- Direct method calls - Parent calls child methods
- Data sharing - Shared pointers to common data structures
- Event propagation - State changes trigger updates
ROM Data Management
// ROM propagation pattern
void DungeonEditor::set_rom(Rom* rom) {
rom_ = rom;
room_selector_.set_rom(rom);
canvas_viewer_.SetRom(rom);
object_selector_.SetRom(rom);
}
State Synchronization
Components maintain their own state but receive updates from the main editor:
- Room selection state is managed by
DungeonRoomSelector - Canvas rendering state is managed by
DungeonCanvasViewer - Object editing state is managed by
DungeonObjectSelector
UI Layout Architecture
3-Column Layout
The main editor uses a 3-column ImGui table layout:
if (BeginTable("#DungeonEditTable", 3, kDungeonTableFlags, ImVec2(0, 0))) {
TableSetupColumn("Room/Entrance Selector", ImGuiTableColumnFlags_WidthFixed, 250);
TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch);
TableSetupColumn("Object Selector/Editor", ImGuiTableColumnFlags_WidthFixed, 300);
// Column 1: Room Selector
TableNextColumn();
room_selector_.Draw();
// Column 2: Canvas
TableNextColumn();
canvas_viewer_.Draw(current_room);
// Column 3: Object Selector
TableNextColumn();
object_selector_.Draw();
}
Component Internal Layout
Each component manages its own internal layout:
- DungeonRoomSelector: Tabbed interface (Rooms | Entrances)
- DungeonCanvasViewer: Canvas with controls and debug popup
- DungeonObjectSelector: Tabbed interface (Graphics | Editor)
Coordinate System
Room Coordinates vs Canvas Coordinates
- Room Coordinates: 16x16 tile units (0-15 for a standard room)
- Canvas Coordinates: Pixel coordinates for rendering
- Conversion:
RoomToCanvasCoordinates(x, y) = (x * 16, y * 16)
Bounds Checking
All rendering operations include bounds checking:
bool IsWithinCanvasBounds(int canvas_x, int canvas_y, int margin = 32) const;
Error Handling & Validation
ROM Validation
All components validate ROM state before operations:
if (!rom_ || !rom_->is_loaded()) {
ImGui::Text("ROM not loaded");
return;
}
Bounds Validation
Graphics operations include bounds checking:
if (room_id < 0 || room_id >= rooms_->size()) {
return; // Skip invalid operations
}
Performance Considerations
Caching Strategy
- Object Render Cache: Cached rendered bitmaps to avoid re-rendering
- Graphics Cache: Cached graphics sheets for frequently accessed data
- Memory Pool: Efficient memory allocation for temporary objects
Rendering Optimization
- Viewport Culling: Objects outside visible area are not rendered
- Lazy Loading: Graphics are loaded only when needed
- Selective Updates: Only changed components re-render
Testing Strategy
Integration Tests
The system includes comprehensive integration tests:
dungeon_object_renderer_integration_test.cc- Core rendering testsdungeon_editor_system_integration_test.cc- System integration testsdungeon_object_renderer_mock_test.cc- Mock ROM testing
Test Categories
- Real ROM Tests: Tests with actual Zelda 3 ROM data
- Mock ROM Tests: Tests with simulated ROM data
- Performance Tests: Rendering performance benchmarks
- Error Handling Tests: Validation and error recovery
Future Development Guidelines
Adding New Features
- Identify Component: Determine which component should handle the feature
- Extend Interface: Add necessary methods to component header
- Implement Logic: Add implementation in component source file
- Update Integration: Modify main editor to use new functionality
- Add Tests: Create tests for new functionality
Component Extension Patterns
// Adding new data access method
void Component::SetNewData(const NewDataType& data) {
new_data_ = data;
}
// Adding new rendering method
void Component::DrawNewFeature() {
// Implementation
}
// Adding to main Draw method
void Component::Draw() {
// Existing code
DrawNewFeature();
}
Data Flow Extension
When adding new data types:
- Add to main editor state
- Create setter methods in relevant components
- Update initialization in
Load()method - Add to
set_rom()propagation if ROM-dependent
UI Layout Extension
For new UI elements:
- Determine placement (new tab, new panel, etc.)
- Follow existing ImGui patterns
- Maintain consistent spacing and styling
- Add to appropriate component's Draw method
Common Pitfalls & Solutions
Memory Management
- Issue: Dangling pointers to ROM data
- Solution: Always validate ROM state before use
Coordinate System
- Issue: Objects rendering at wrong positions
- Solution: Use coordinate conversion helper methods
State Synchronization
- Issue: Components showing stale data
- Solution: Ensure data propagation in setter methods
Performance Issues
- Issue: Slow rendering with many objects
- Solution: Implement viewport culling and caching
Debugging Tools
Debug Popup
The canvas viewer includes a comprehensive debug popup with:
- Object statistics and metadata
- Cache information
- Performance metrics
- Object type breakdowns
Logging
Key operations include logging for debugging:
std::cout << "Loading room graphics for room " << room_id << std::endl;
Build Integration
CMake Configuration
New components are automatically included via:
# In CMakeLists.txt
file(GLOB YAZE_SRC_FILES "src/app/editor/dungeon/*.cc")
Dependencies
Key dependencies:
- ImGui for UI rendering
- gfx library for graphics operations
- zelda3 library for ROM data structures
- absl for status handling
Conclusion
This modular architecture provides a solid foundation for future dungeon editor development. The separation of concerns makes the codebase maintainable, testable, and extensible. Future developers should follow the established patterns and extend components rather than modifying the main orchestrator class.
For questions or clarifications, refer to the existing integration tests and component implementations as examples of proper usage patterns.