Remove outdated Dungeon Graphics Rendering Pipeline Analysis document and update z3ed README with new CMake preset usage and streamlined core documentation references.

This commit is contained in:
scawful
2025-10-04 13:25:47 -04:00
parent 38ece34894
commit 0db71a71fe
5 changed files with 2 additions and 1790 deletions

View File

@@ -1,92 +0,0 @@
# Dungeon Canvas Blank Screen - Fix Summary
## Issue
The dungeon editor canvas was displaying only UI elements but the room graphics were blank, despite the rendering pipeline appearing to be set up correctly.
## Root Cause
The background buffers (`bg1` and `bg2`) were never being populated with tile indices from room objects before calling `DrawBackground()`.
### The Missing Step
1. `Room::RenderRoomGraphics()` would:
- Copy graphics data to `current_gfx16_`
- Draw floor pattern via `DrawFloor()`
- Call `DrawBackground()` to render tiles to pixels
2. **Problem**: `DrawBackground()` expects the buffer to contain tile indices, but only the floor pattern was in the buffer. Room objects were loaded but never converted to tile indices in the buffer!
### How ZScream Does It
- ZScream's `Room.loadLayoutObjects()` and object drawing methods populate `tilesBg1Buffer`/`tilesBg2Buffer` with tile indices
- `GraphicsManager.DrawBackground()` then converts those buffer indices to pixels
- Result: Complete room with floors, walls, and objects
## Solution Implemented
### Changes Made
**File**: `src/app/zelda3/dungeon/room.cc`
1. **Enhanced `RenderRoomGraphics()`** (line 286-315)
- Added call to `RenderObjectsToBackground()` after `DrawFloor()` but before `DrawBackground()`
2. **Implemented `RenderObjectsToBackground()`** (line 317-383)
- Iterates through all `tile_objects_`
- Ensures each object has tiles loaded via `EnsureTilesLoaded()`
- For each object:
- Determines correct layer (BG1 vs BG2)
- Converts each `Tile16` to 4 `TileInfo` sub-tiles
- Converts each `TileInfo` to word format using `TileInfoToWord()`
- Places tiles in background buffer using `SetTileAt()`
### Technical Details
**Tile Structure**:
- `Tile16` = 16x16 pixel tile made of 4 `TileInfo` (8x8) tiles
- Arranged as: `[tile0_][tile1_]` (top row), `[tile2_][tile3_]` (bottom row)
**Word Format** (per SNES specs):
```
Bit 15: Vertical flip
Bit 14: Horizontal flip
Bit 13: Priority/over
Bits 10-12: Palette (3 bits)
Bits 0-9: Tile ID (10 bits)
```
**Buffer Layout**:
- 64x64 tiles (512x512 pixels)
- Buffer index = `y * 64 + x`
- Each entry is a uint16_t word
## Testing
To verify the fix:
1. Build yaze with: `cmake --build build --target yaze`
2. Run yaze and open the dungeon editor
3. Select any room
4. Canvas should now display:
- ✅ Floor pattern
- ✅ Wall structures
- ✅ Room objects
- ✅ Correct layer separation
## Future Enhancements
- Add layout object rendering (walls from room layout data)
- Optimize tile placement algorithm for different object types
- Add support for animated tiles
- Implement object-specific draw routines (similar to ZScream's `DungeonObjectDraw.cs`)
## Related Files
- **Implementation**: `src/app/zelda3/dungeon/room.cc`
- **Header**: `src/app/zelda3/dungeon/room.h`
- **Buffer**: `src/app/gfx/background_buffer.cc`
- **Tiles**: `src/app/gfx/snes_tile.h`
- **Reference**: `ZeldaFullEditor/GraphicsManager.cs` (ZScream)
- **Reference**: `ZeldaFullEditor/Data/Types/DungeonObjectDraw.cs` (ZScream)
## Build Status
**Compilation**: Success (0 errors)
⚠️ **Warnings**: 21 warnings (pre-existing, unrelated to this fix)

View File

@@ -1,96 +0,0 @@
# Dungeon Editor Developer Guide
## 1. Implementation Plan
**Goal**: Implement fully functional dungeon room editing based on ZScream's proven approach.
### 1.1. Current State Analysis
- **Working**: Room header loading, basic class structures (`Room`, `RoomObject`), graphics loading, and UI framework.
- **Not Working**: Object parsing, drawing, placement, and saving. The core issue is the incomplete implementation of the 3-byte object encoding/decoding scheme and the failure to render object tiles to the background buffer.
### 1.2. ZScream's Approach
ZScream's implementation relies on several key concepts that need to be ported:
- **Object Encoding**: Three different 3-byte encoding formats for objects based on their ID range (Type 1, Type 2, Type 3).
- **Layer Separation**: Objects are organized into three layers (BG1, BG2, BG3) separated by `0xFFFF` markers in the ROM.
- **Object Loading**: A loop parses the object data, decodes each 3-byte entry based on its type, and handles layer and door markers.
- **Object Drawing**: Each object's constituent tiles are drawn to the correct background buffer (`tilesBg1Buffer` or `tilesBg2Buffer`), which is then used to render the final room image.
### 1.3. Implementation Tasks
1. **Core Object System (High Priority)**:
* Implement the three different object encoding and decoding schemes.
* Enhance the room object parser to correctly handle layers and door sections.
* Implement tile loading for objects from the ROM's subtype tables.
* Create an object drawing system that renders the loaded tiles to the correct background buffer.
2. **Editor UI Integration (Medium Priority)**:
* Implement object placement, selection, and drag-and-drop on the canvas.
* Create UI panels for editing object properties (position, size, layer).
3. **Save System (High Priority)**:
* Implement the serialization of room objects back into the 3-byte ROM format, respecting layers and markers.
* Implement the logic to write the serialized data back to the ROM, handling potential room size expansion.
---
## 2. Graphics Rendering Pipeline
This section provides a comprehensive analysis of how the dungeon editor renders room graphics.
### 2.1. Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────┐
│ ROM Data │
│ ┌────────────────────────┐ ┌──────────────────────────────────┐│
│ │ Room Headers │ │ Dungeon Palettes ││
│ │ - Palette ID │ │ - dungeon_main[id] ││
│ │ - Blockset ID │ │ - sprites_aux1[id] ││
│ └────────────────────────┘ └──────────────────────────────────┘│
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Room Loading (room.cc) │
│ ├─ Load 16 blocks (graphics sheets) │
│ └─ Copy graphics to a 32KB buffer (`current_gfx16_`) │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Room::RenderRoomGraphics() Pipeline │
│ │
│ Step 1: DrawFloor() on BG1 and BG2 buffers │
│ └─ Populates buffers with repeating floor tile pattern. │
│ │
│ Step 2: RenderObjectsToBackground() │
│ └─ Iterates room objects and draws their tiles into the BG1/BG2 buffers.│
│ │
│ Step 3: DrawBackground() on BG1 and BG2 │
│ └─ Converts tile words in buffers to 8bpp indexed pixel data in a Bitmap.│
│ │
│ Step 4: Apply Palette & Create/Update Texture │
│ ├─ Get dungeon_main palette for the room. │
│ ├─ Apply palette to the Bitmap. │
│ └─ Create or update an SDL_Texture from the paletted Bitmap. │
└─────────────────────────────────────────────────────────────────┘
```
### 2.2. Blank Canvas Bug: Root Cause Analysis
A critical "blank canvas" bug was diagnosed and fixed. The root cause was multifaceted:
1. **Missing Object Rendering**: The `RenderObjectsToBackground()` step was missing entirely. The background buffers only contained the floor pattern, so walls and objects were never drawn.
* **Fix**: Implemented `RenderObjectsToBackground()` to iterate through all loaded room objects and draw their constituent tiles into the correct background buffer (`bg1` or `bg2`).
2. **Missing Palette Application**: The indexed-pixel `Bitmap` was being converted to a texture without its palette. SDL had no information to map the color indices to actual colors, resulting in a blank texture.
* **Fix**: Added a call to `bitmap.SetPaletteWithTransparent()` *before* the `CreateAndRenderBitmap()` or `UpdateBitmap()` call.
3. **Incorrect Tile Word Format**: The floor drawing logic was writing only the tile ID to the buffer, discarding the necessary palette information within the 16-bit tile word.
* **Fix**: Modified the code to use `gfx::TileInfoToWord()` to ensure the full 16-bit value, including palette index, was written.
4. **Incorrect Palette ID**: The system was hardcoded to use palette `0` for all rooms, causing most dungeons to render with incorrect, "gargoyle-y" colors.
* **Fix**: Modified the code to use the specific `palette` ID loaded from each room's header.
**Key Takeaway**: The entire rendering pipeline, from buffer population to palette application, must be executed in the correct order for graphics to appear.

File diff suppressed because it is too large Load Diff

View File

@@ -1,420 +0,0 @@
# Dungeon Graphics Rendering Pipeline Analysis
## Overview
This document provides a comprehensive analysis of how the YAZE dungeon editor renders room graphics, including the interaction between bitmaps, arena buffers, palettes, and palette groups.
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────┐
│ ROM Data │
│ ┌────────────────────────┐ ┌──────────────────────────────────┐│
│ │ Room Headers │ │ Dungeon Palettes ││
│ │ - Palette ID │ │ - dungeon_main[id][180 colors] ││
│ │ - Blockset ID │ │ - sprites_aux1[id][colors] ││
│ │ - Spriteset ID │ │ - Palette Groups ││
│ │ - Background ID │ │ ││
│ └────────────────────────┘ └──────────────────────────────────┘│
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Room Loading (room.cc) │
│ │
│ LoadRoomFromRom() → LoadRoomGraphics() → Copy RoomGraphics │
│ ├─ Load 16 blocks (graphics sheets) │
│ ├─ blocks[0-7]: Main blockset │
│ ├─ blocks[8-11]: Static sprites (fairies, pots, etc.) │
│ └─ blocks[12-15]: Spriteset sprites │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Graphics Arena (arena.h/.cc) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ gfx_sheets_[223] │ │ Background │ │
│ │ (Bitmap objects) │ │ Buffers │ │
│ │ │ │ - bg1_ │ │
│ │ Each holds: │ │ - bg2_ │ │
│ │ - Pixel data │ │ │ │
│ │ - SDL Surface │ │ layer1_buffer_ │ │
│ │ - SDL Texture │ │ layer2_buffer_ │ │
│ │ - Palette │ │ [64x64 = 4096 │ │
│ └──────────────────┘ │ tile words] │ │
│ └──────────────────┘ │
└───────────────────────────┬─────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Room::RenderRoomGraphics() Pipeline │
│ │
│ Step 1: CopyRoomGraphicsToBuffer() │
│ └─ Copy 16 blocks to current_gfx16_[32768] buffer │
│ │
│ Step 2: DrawFloor() on both BG1 and BG2 │
│ ├─ Read floor tile IDs from ROM │
│ ├─ Create TileInfo objects (id, palette, mirror flags) │
│ └─ SetTileAt() in background buffers (repeating pattern) │
│ │
│ Step 3: RenderObjectsToBackground() ⚠️ NEW │
│ ├─ Iterate through tile_objects_ │
│ ├─ For each object, get its Tile16 array │
│ ├─ Each Tile16 contains 4 TileInfo (8x8 tiles) │
│ ├─ Convert TileInfo → 16-bit word: │
│ │ (vflip<<15) | (hflip<<14) | (palette<<10) | tile_id │
│ └─ SetTileAt() in correct layer (BG1 or BG2) │
│ │
│ Step 4: DrawBackground() on both BG1 and BG2 │
│ ├─ BackgroundBuffer::DrawBackground(current_gfx16_) │
│ ├─ For each tile in buffer_[4096]: │
│ │ ├─ Extract 16-bit word │
│ │ ├─ WordToTileInfo() → TileInfo │
│ │ └─ DrawTile() → Write 8x8 pixels to bitmap_.data_ │
│ └─ bitmap_.Create(512, 512, 8, pixel_data) │
│ │
│ Step 5: Apply Palette & Create/Update Texture │
│ ├─ Get dungeon_main palette for this room │
│ ├─ bitmap_.SetPaletteWithTransparent(palette, 0) │
│ ├─ If first time: │
│ │ └─ CreateAndRenderBitmap() → Create SDL_Texture │
│ └─ Else: │
│ └─ UpdateBitmap() → Update existing SDL_Texture │
└─────────────────────────────────────────────────────────────────┘
```
## Component Breakdown
### 1. Arena (gfx/arena.h/.cc)
**Purpose**: Global graphics resource manager using singleton pattern.
**Key Members**:
- `gfx_sheets_[223]`: Array of Bitmap objects (one for each graphics sheet in ROM)
- `bg1_`, `bg2_`: BackgroundBuffer objects for SNES layer 1 and layer 2
- `layer1_buffer_[4096]`, `layer2_buffer_[4096]`: Raw tile word arrays
**Responsibilities**:
- Resource pooling for SDL textures and surfaces
- Batch texture updates for performance
- Centralized access to graphics sheets
### 2. BackgroundBuffer (gfx/background_buffer.h/.cc)
**Purpose**: Manages a single SNES background layer (512x512 pixels = 64x64 tiles).
**Key Members**:
- `buffer_[4096]`: Array of 16-bit tile words (vflip|hflip|palette|tile_id)
- `bitmap_`: The Bitmap object that holds the rendered pixel data
- `width_`, `height_`: Dimensions in pixels (typically 512x512)
**Key Methods**:
#### `SetTileAt(int x, int y, uint16_t value)`
```cpp
// Sets a tile word at tile coordinates (x, y)
// x, y are in tile units (0-63)
buffer_[y * tiles_w + x] = value;
```
#### `DrawBackground(std::span<uint8_t> gfx16_data)`
```cpp
// Renders all tiles in buffer_ to bitmap_
1. Create bitmap (512x512, 8bpp)
2. For each tile (64x64 grid):
- Get tile word from buffer_[xx + yy * 64]
- WordToTileInfo() to extract: id, palette, hflip, vflip
- DrawTile() writes 64 pixels to bitmap at correct position
```
#### `DrawFloor()`
```cpp
// Special case: Draws floor pattern from ROM data
1. Read 8 floor tile IDs from ROM (2 rows of 4)
2. Repeat pattern across entire 64x64 grid
3. SetTileAt() for each position
```
### 3. Bitmap (gfx/bitmap.h/.cc)
**Purpose**: Represents a 2D image with SNES-specific features.
**Key Members**:
- `data_[width * height]`: Raw indexed pixel data (palette indices)
- `palette_`: SnesPalette object (15-bit RGB colors)
- `surface_`: SDL_Surface for pixel manipulation
- `texture_`: SDL_Texture for rendering to screen
- `active_`, `modified_`: State flags
**Key Methods**:
#### `Create(int w, int h, int depth, vector<uint8_t> data)`
```cpp
// Initialize bitmap with pixel data
width_ = w;
height_ = h;
depth_ = depth; // Usually 8 (bits per pixel)
data_ = data;
active_ = true;
```
#### `SetPaletteWithTransparent(SnesPalette palette, size_t index)`
```cpp
// Apply palette and make color[index] transparent
palette_ = palette;
// Update surface_->format->palette with SDL_Colors
// Set color[index] alpha to 0 for transparency
```
#### `CreateTexture(SDL_Renderer* renderer)` / `UpdateTexture()`
```cpp
// Convert surface_ to hardware-accelerated texture_
texture_ = SDL_CreateTextureFromSurface(renderer, surface_);
// or
SDL_UpdateTexture(texture_, nullptr, surface_->pixels, surface_->pitch);
```
### 4. Room (zelda3/dungeon/room.h/.cc)
**Purpose**: Represents a single dungeon room with all its data.
**Key Members**:
- `room_id_`: Room index (0-295)
- `palette`, `blockset`, `spriteset`: IDs from ROM header
- `blocks_[16]`: Graphics sheet indices for this room
- `current_gfx16_[32768]`: Raw graphics data for this room
- `tile_objects_`: Vector of RoomObject instances
- `rom_`: Pointer to ROM data
**Key Methods**:
#### `LoadRoomGraphics(uint8_t entrance_blockset)`
```cpp
// Load 16 graphics sheets for this room
blocks_[0-7]: Main blockset sheets
blocks_[8-11]: Static sprites (fairies, pots, etc.)
blocks_[12-15]: Spriteset sprites
```
#### `CopyRoomGraphicsToBuffer()`
```cpp
// Copy 16 blocks of 2KB each into current_gfx16_[32KB]
for (int i = 0; i < 16; i++) {
int block = blocks_[i];
memcpy(current_gfx16_ + i*2048,
graphics_buffer[block*2048],
2048);
}
LoadAnimatedGraphics(); // Overlay animated frames
```
#### `RenderRoomGraphics()` ⭐ **Main Rendering Method**
```cpp
void Room::RenderRoomGraphics() {
// Step 1: Copy graphics data from ROM
CopyRoomGraphicsToBuffer();
// Step 2: Draw floor pattern
Arena::Get().bg1().DrawFloor(rom->vector(), tile_address,
tile_address_floor, floor1_graphics_);
Arena::Get().bg2().DrawFloor(rom->vector(), tile_address,
tile_address_floor, floor2_graphics_);
// Step 3: ⚠️ NEW - Render room objects to buffers
RenderObjectsToBackground();
// Step 4: Convert tile buffers to bitmaps
Arena::Get().bg1().DrawBackground(span<uint8_t>(current_gfx16_));
Arena::Get().bg2().DrawBackground(span<uint8_t>(current_gfx16_));
// Step 5: Apply palette and create/update textures
auto palette = rom->palette_group().dungeon_main[palette_id][0];
if (!Arena::Get().bg1().bitmap().is_active()) {
Renderer::Get().CreateAndRenderBitmap(..., Arena::Get().bg1().bitmap(), palette);
Renderer::Get().CreateAndRenderBitmap(..., Arena::Get().bg2().bitmap(), palette);
} else {
Renderer::Get().UpdateBitmap(&Arena::Get().bg1().bitmap());
Renderer::Get().UpdateBitmap(&Arena::Get().bg2().bitmap());
}
}
```
#### `RenderObjectsToBackground()` ⚠️ **Critical New Method** ✅ **Fixed**
```cpp
void Room::RenderObjectsToBackground() {
auto& bg1 = Arena::Get().bg1();
auto& bg2 = Arena::Get().bg2();
for (const auto& obj : tile_objects_) {
// Ensure object has tiles loaded
obj.EnsureTilesLoaded();
auto tiles_result = obj.GetTiles(); // Returns span<const Tile16>
// Calculate the width of the object in Tile16 units
// Most objects are arranged in a grid, typically 1-8 tiles wide
int tiles_wide = 1;
if (tiles.size() > 1) {
// Try to determine optimal layout based on tile count
// Common patterns: 1x1, 2x2, 4x1, 2x4, 4x4, 8x1, etc.
int sq = static_cast<int>(std::sqrt(tiles.size()));
if (sq * sq == tiles.size()) {
tiles_wide = sq; // Perfect square (4, 9, 16, etc.)
} else if (tiles.size() <= 4) {
tiles_wide = tiles.size(); // Small objects laid out horizontally
} else {
// For larger objects, try common widths (4 or 8)
tiles_wide = (tiles.size() >= 8) ? 8 : 4;
}
}
// Each Tile16 is 16x16 (4 TileInfo of 8x8)
for (size_t i = 0; i < tiles.size(); i++) {
const auto& tile16 = tiles[i];
// Calculate base position using calculated width (in 8x8 units)
int base_x = obj.x_ + ((i % tiles_wide) * 2);
int base_y = obj.y_ + ((i / tiles_wide) * 2);
// Tile16.tiles_info[4] contains the 4 sub-tiles:
// [0][1] (top-left, top-right)
// [2][3] (bottom-left, bottom-right)
for (int sub = 0; sub < 4; sub++) {
int tile_x = base_x + (sub % 2);
int tile_y = base_y + (sub / 2);
// Bounds check
if (tile_x < 0 || tile_x >= 64 || tile_y < 0 || tile_y >= 64) {
continue;
}
// Convert TileInfo to 16-bit word
uint16_t word = TileInfoToWord(tile16.tiles_info[sub]);
// Set in correct layer
bool is_bg2 = (obj.layer_ == RoomObject::LayerType::BG2);
auto& buffer = is_bg2 ? bg2 : bg1;
buffer.SetTileAt(tile_x, tile_y, word);
}
}
}
}
```
## Palette System
### Palette Hierarchy
```
ROM Palette Data
├─ dungeon_main[palette_group_id]
│ └─ Large palette (180 colors)
│ └─ Split into PaletteGroup:
│ ├─ palette(0): Main dungeon palette
│ ├─ palette(1): Alternate palette 1
│ └─ palette(2-n): More palettes
└─ sprites_aux1[palette_id]
└─ Sprite auxiliary palettes
```
### Palette Loading Flow
```cpp
// In DungeonEditor::Load()
auto dungeon_pal_group = rom->palette_group().dungeon_main;
full_palette_ = dungeon_pal_group[current_palette_group_id_];
ASSIGN_OR_RETURN(current_palette_group_,
CreatePaletteGroupFromLargePalette(full_palette_));
// In DungeonCanvasViewer::LoadAndRenderRoomGraphics()
auto dungeon_palette_ptr = rom->paletteset_ids[room.palette][0];
auto palette_id = rom->ReadWord(0xDEC4B + dungeon_palette_ptr);
current_palette_group_id_ = palette_id.value() / 180;
full_palette = rom->palette_group().dungeon_main[current_palette_group_id_];
// Apply to graphics sheets
for (int i = 0; i < 8; i++) { // BG1 layers
int block = room.blocks()[i];
Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
current_palette_group_[current_palette_id_], 0);
}
for (int i = 8; i < 16; i++) { // BG2 layers (sprites)
int block = room.blocks()[i];
Arena::Get().gfx_sheets()[block].SetPaletteWithTransparent(
sprites_aux1_pal_group[current_palette_id_], 0);
}
```
---
# Appendix: Blank Canvas Bug - Detailed Root Cause Analysis
This section, merged from `dungeon_canvas_blank_fix.md`, details the investigation and resolution of the blank canvas bug.
## Problem
The DungeonEditor canvas displayed as blank white despite the rendering pipeline appearing to execute correctly.
## Diagnostic Output Analysis
Using a comprehensive diagnostic system, the data flow was traced through 8 steps. Steps 1-6 (ROM loading, buffer population, bitmap creation, texture creation) were all passing. The failure was in Step 7.
### ❌ Step 7: PALETTE MISSING
```
=== Step 7: Palette ===
Palette size: 0 colors ❌❌❌ ROOT CAUSE!
```
## Root Cause Analysis
Three distinct issues were identified and fixed in sequence:
### Cause 1: Missing Palette Application
**The palette was never applied to the bitmap objects!** The bitmaps contained indexed pixel data (e.g., color indices 8, 9, 12), but without a color palette, SDL couldn't map these indices to actual colors, resulting in a blank texture.
**The Fix (Location: `src/app/zelda3/dungeon/room.cc:319-322`)**:
Added `SetPaletteWithTransparent()` calls to the bitmaps *before* creating the SDL textures. This ensures the renderer has the color information it needs.
```cpp
// CRITICAL: Apply palette to bitmaps BEFORE creating/updating textures
bg1_bmp.SetPaletteWithTransparent(bg1_palette, 0);
bg2_bmp.SetPaletteWithTransparent(bg1_palette, 0);
// Now create/update textures (palette is already set in bitmap)
Renderer::Get().CreateAndRenderBitmap(..., bg1_bmp, bg1_palette);
```
### Cause 2: All Black Canvas (Incorrect Tile Word)
After the first fix, the canvas was all black. This was because `DrawFloor()` was only passing the tile ID to the background buffer, losing the crucial palette information.
**The Fix**:
Converted the `TileInfo` struct to a full 16-bit word (which includes palette bits) before writing it to the buffer.
```cpp
// CORRECT: Convert TileInfo to word with all metadata
uint16_t word1 = gfx::TileInfoToWord(floorTile1);
SetTileAt((xx * 4), (yy * 2), word1); // ✅ Now includes palette!
```
### Cause 3: Wrong Palette (All Rooms Look "Gargoyle-y")
After the second fix, all rooms rendered, but with the same incorrect palette (from the first dungeon).
**The Fix**:
Used the room's specific `palette` ID loaded from the ROM header instead of hardcoding palette index `0`.
```cpp
// ✅ CORRECT: Use the room's palette ID
auto bg1_palette =
rom()->mutable_palette_group()->get_group("dungeon_main")[0].palette(palette);
```
## Key Takeaway
**Always apply a palette to indexed-color bitmaps before creating SDL textures.** The rendering pipeline requires this step to translate color indices into visible pixels. Each subsequent fix ensured the *correct* palette information was being passed at each stage.

View File

@@ -183,6 +183,8 @@ cmake --build build --target z3ed
cmake -B build -DZ3ED_AI=ON
cmake --build build --target z3ed
# use macos-dev-z3ed-ai cmake preset
# Full build with AI agent AND testing suite
cmake -B build -DZ3ED_AI=ON -DYAZE_WITH_GRPC=ON
cmake --build build --target z3ed
@@ -297,13 +299,6 @@ Here are some example prompts you can try with either Ollama or Gemini:
## Core Documentation
### Essential Reads
1. **[BUILD_QUICK_REFERENCE.md](BUILD_QUICK_REFERENCE.md)** - **NEW!** Fast build guide with Z3ED_AI flag examples
2. **[AGENT-ROADMAP.md](AGENT-ROADMAP.md)** - The primary source of truth for the AI agent's strategic vision, architecture, and next steps
3. **[Z3ED_AI_FLAG_MIGRATION.md](Z3ED_AI_FLAG_MIGRATION.md)** - **NEW!** Complete guide to Z3ED_AI flag and crash fixes
4. **[E6-z3ed-cli-design.md](E6-z3ed-cli-design.md)** - Detailed architecture and design philosophy
5. **[E6-z3ed-reference.md](E6-z3ed-reference.md)** - Complete command reference and API documentation
## Current Status (October 3, 2025)
### ✅ Production Ready
@@ -330,9 +325,6 @@ Here are some example prompts you can try with either Ollama or Gemini:
2. **GUI Chat Widget** (6-8h): ImGui integration (TUI exists as reference)
3. **Tool Coverage Expansion** (8-10h): Dialogue, sprites, regions
### 📋 Next Steps
See [AGENT-ROADMAP.md](AGENT-ROADMAP.md) for detailed technical roadmap.
## AI Editing Focus Areas
z3ed is optimized for practical ROM editing workflows: