feat: Refactor Dungeon Rendering to Use Room-Specific Background Buffers
- Updated DungeonCanvasViewer to check and render room-specific graphics, improving rendering efficiency. - Enhanced LoadAndRenderRoomGraphics to include detailed logging for better debugging. - Refactored room rendering methods to utilize individual background buffers instead of global arena buffers, ensuring accurate graphics representation. - Improved room diagnostic functionality to verify background buffer states specific to each room. - Added critical checks and logging in BackgroundBuffer to ensure proper tile rendering and bitmap management.
This commit is contained in:
@@ -102,8 +102,17 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
|||||||
if (rooms_ && rom_->is_loaded()) {
|
if (rooms_ && rom_->is_loaded()) {
|
||||||
auto& room = (*rooms_)[room_id];
|
auto& room = (*rooms_)[room_id];
|
||||||
|
|
||||||
// Automatically load room graphics if not already loaded
|
// Check if THIS ROOM's buffers need rendering (not global arena!)
|
||||||
if (room.blocks().empty()) {
|
auto& bg1_bitmap = room.bg1_buffer().bitmap();
|
||||||
|
bool needs_render = !bg1_bitmap.is_active() || bg1_bitmap.width() == 0;
|
||||||
|
|
||||||
|
printf("[DrawCanvas] Room %d: needs_render=%d, bg1_active=%d, blocks=%zu, objects=%zu\n",
|
||||||
|
room_id, needs_render, bg1_bitmap.is_active(),
|
||||||
|
room.blocks().size(), room.GetTileObjects().size());
|
||||||
|
|
||||||
|
// Render immediately if needed
|
||||||
|
if (needs_render) {
|
||||||
|
printf("[DrawCanvas] Rendering room %d graphics...\n", room_id);
|
||||||
(void)LoadAndRenderRoomGraphics(room_id);
|
(void)LoadAndRenderRoomGraphics(room_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,10 +121,7 @@ void DungeonCanvasViewer::DrawDungeonCanvas(int room_id) {
|
|||||||
room.LoadObjects();
|
room.LoadObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Don't draw objects here - RenderRoomBackgroundLayers() already does it
|
// Render the room's background layers
|
||||||
// via room.RenderRoomGraphics() which calls RenderObjectsToBackground()
|
|
||||||
|
|
||||||
// Render background layers from arena buffers
|
|
||||||
RenderRoomBackgroundLayers(room_id);
|
RenderRoomBackgroundLayers(room_id);
|
||||||
|
|
||||||
// Render room objects with proper graphics (old system as fallback)
|
// Render room objects with proper graphics (old system as fallback)
|
||||||
@@ -558,22 +564,30 @@ void DungeonCanvasViewer::CalculateWallDimensions(const zelda3::RoomObject& obje
|
|||||||
|
|
||||||
// Room graphics management methods
|
// Room graphics management methods
|
||||||
absl::Status DungeonCanvasViewer::LoadAndRenderRoomGraphics(int room_id) {
|
absl::Status DungeonCanvasViewer::LoadAndRenderRoomGraphics(int room_id) {
|
||||||
|
printf("[LoadAndRender] START room_id=%d\n", room_id);
|
||||||
|
|
||||||
if (room_id < 0 || room_id >= 128) {
|
if (room_id < 0 || room_id >= 128) {
|
||||||
|
printf("[LoadAndRender] ERROR: Invalid room ID\n");
|
||||||
return absl::InvalidArgumentError("Invalid room ID");
|
return absl::InvalidArgumentError("Invalid room ID");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rom_ || !rom_->is_loaded()) {
|
if (!rom_ || !rom_->is_loaded()) {
|
||||||
|
printf("[LoadAndRender] ERROR: ROM not loaded\n");
|
||||||
return absl::FailedPreconditionError("ROM not loaded");
|
return absl::FailedPreconditionError("ROM not loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rooms_) {
|
if (!rooms_) {
|
||||||
|
printf("[LoadAndRender] ERROR: Room data not available\n");
|
||||||
return absl::FailedPreconditionError("Room data not available");
|
return absl::FailedPreconditionError("Room data not available");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& room = (*rooms_)[room_id];
|
auto& room = (*rooms_)[room_id];
|
||||||
|
printf("[LoadAndRender] Got room reference\n");
|
||||||
|
|
||||||
// Load room graphics with proper blockset
|
// Load room graphics with proper blockset
|
||||||
|
printf("[LoadAndRender] Loading graphics for blockset %d\n", room.blockset);
|
||||||
room.LoadRoomGraphics(room.blockset);
|
room.LoadRoomGraphics(room.blockset);
|
||||||
|
printf("[LoadAndRender] Graphics loaded\n");
|
||||||
|
|
||||||
// Load the room's palette with bounds checking
|
// Load the room's palette with bounds checking
|
||||||
if (room.palette < rom_->paletteset_ids.size() &&
|
if (room.palette < rom_->paletteset_ids.size() &&
|
||||||
@@ -586,16 +600,22 @@ absl::Status DungeonCanvasViewer::LoadAndRenderRoomGraphics(int room_id) {
|
|||||||
auto full_palette = rom_->palette_group().dungeon_main[current_palette_group_id_];
|
auto full_palette = rom_->palette_group().dungeon_main[current_palette_group_id_];
|
||||||
ASSIGN_OR_RETURN(current_palette_group_,
|
ASSIGN_OR_RETURN(current_palette_group_,
|
||||||
gfx::CreatePaletteGroupFromLargePalette(full_palette));
|
gfx::CreatePaletteGroupFromLargePalette(full_palette));
|
||||||
|
printf("[LoadAndRender] Palette loaded: group_id=%zu\n", current_palette_group_id_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render the room graphics to the graphics arena
|
// Render the room graphics to the graphics arena
|
||||||
|
printf("[LoadAndRender] Calling room.RenderRoomGraphics()...\n");
|
||||||
room.RenderRoomGraphics();
|
room.RenderRoomGraphics();
|
||||||
|
printf("[LoadAndRender] RenderRoomGraphics() complete\n");
|
||||||
|
|
||||||
// Update the background layers with proper palette
|
// Update the background layers with proper palette
|
||||||
|
printf("[LoadAndRender] Updating background layers...\n");
|
||||||
RETURN_IF_ERROR(UpdateRoomBackgroundLayers(room_id));
|
RETURN_IF_ERROR(UpdateRoomBackgroundLayers(room_id));
|
||||||
|
printf("[LoadAndRender] UpdateRoomBackgroundLayers() complete\n");
|
||||||
|
|
||||||
|
printf("[LoadAndRender] SUCCESS\n");
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -655,86 +675,53 @@ absl::Status DungeonCanvasViewer::UpdateRoomBackgroundLayers(int room_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
void DungeonCanvasViewer::RenderRoomBackgroundLayers(int room_id) {
|
||||||
if (room_id < 0 || room_id >= 128) {
|
if (room_id < 0 || room_id >= 128 || !rooms_) return;
|
||||||
printf("[Canvas] Invalid room_id: %d\n", room_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rom_ || !rom_->is_loaded()) {
|
auto& room = (*rooms_)[room_id];
|
||||||
printf("[Canvas] ROM not loaded\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rooms_) {
|
// Use THIS room's own buffers, not global arena!
|
||||||
printf("[Canvas] Rooms pointer is null\n");
|
auto& bg1_bitmap = room.bg1_buffer().bitmap();
|
||||||
return;
|
auto& bg2_bitmap = room.bg2_buffer().bitmap();
|
||||||
}
|
|
||||||
|
|
||||||
// Get canvas dimensions
|
printf("[RenderLayers] Room %d: BG1 active=%d, texture=%p\n",
|
||||||
int canvas_width = canvas_.width();
|
room_id, bg1_bitmap.is_active(), (void*)bg1_bitmap.texture());
|
||||||
int canvas_height = canvas_.height();
|
|
||||||
|
|
||||||
printf("[Canvas] Canvas size: %dx%d\n", canvas_width, canvas_height);
|
|
||||||
|
|
||||||
if (canvas_width <= 0 || canvas_height <= 0) {
|
|
||||||
printf("[Canvas] Invalid canvas dimensions\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render BG1 (background layer 1) - main room graphics
|
|
||||||
auto& bg1_bitmap = gfx::Arena::Get().bg1().bitmap();
|
|
||||||
printf("[Canvas] BG1: active=%d, size=%dx%d, texture=%p\n",
|
|
||||||
bg1_bitmap.is_active(), bg1_bitmap.width(), bg1_bitmap.height(),
|
|
||||||
(void*)bg1_bitmap.texture());
|
|
||||||
|
|
||||||
if (bg1_bitmap.is_active() && bg1_bitmap.width() > 0 && bg1_bitmap.height() > 0) {
|
if (bg1_bitmap.is_active() && bg1_bitmap.width() > 0 && bg1_bitmap.height() > 0) {
|
||||||
// Ensure texture exists
|
|
||||||
if (!bg1_bitmap.texture()) {
|
if (!bg1_bitmap.texture()) {
|
||||||
printf("[Canvas] WARNING: BG1 has no texture, creating...\n");
|
printf("[RenderLayers] Creating BG1 texture...\n");
|
||||||
core::Renderer::Get().RenderBitmap(&bg1_bitmap);
|
core::Renderer::Get().RenderBitmap(&bg1_bitmap);
|
||||||
}
|
}
|
||||||
|
printf("[RenderLayers] Drawing BG1 bitmap: %dx%d, texture=%p\n",
|
||||||
|
bg1_bitmap.width(), bg1_bitmap.height(), (void*)bg1_bitmap.texture());
|
||||||
|
|
||||||
// Scale to fit canvas
|
// DEBUG: Check SDL texture format
|
||||||
float scale_x = static_cast<float>(canvas_width) / bg1_bitmap.width();
|
Uint32 format;
|
||||||
float scale_y = static_cast<float>(canvas_height) / bg1_bitmap.height();
|
int access, w, h;
|
||||||
float scale = std::min(scale_x, scale_y);
|
if (SDL_QueryTexture(bg1_bitmap.texture(), &format, &access, &w, &h) == 0) {
|
||||||
|
printf("[RenderLayers] BG1 texture format: %s (%u), access: %d, size: %dx%d\n",
|
||||||
|
SDL_GetPixelFormatName(format), format, access, w, h);
|
||||||
|
}
|
||||||
|
|
||||||
int scaled_width = static_cast<int>(bg1_bitmap.width() * scale);
|
canvas_.DrawBitmap(bg1_bitmap, 0, 0, 1.0f, 255);
|
||||||
int scaled_height = static_cast<int>(bg1_bitmap.height() * scale);
|
|
||||||
int offset_x = (canvas_width - scaled_width) / 2;
|
|
||||||
int offset_y = (canvas_height - scaled_height) / 2;
|
|
||||||
|
|
||||||
printf("[Canvas] Drawing BG1 at offset=(%d,%d), scaled_size=%dx%d, scale=%.2f\n",
|
|
||||||
offset_x, offset_y, scaled_width, scaled_height, scale);
|
|
||||||
|
|
||||||
canvas_.DrawBitmap(bg1_bitmap, offset_x, offset_y, scale, 255);
|
|
||||||
} else {
|
} else {
|
||||||
printf("[Canvas] BG1 not ready for rendering\n");
|
printf("[RenderLayers] BG1 not ready: active=%d, w=%d, h=%d\n",
|
||||||
|
bg1_bitmap.is_active(), bg1_bitmap.width(), bg1_bitmap.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render BG2 (background layer 2) - sprite graphics (overlay)
|
|
||||||
auto& bg2_bitmap = gfx::Arena::Get().bg2().bitmap();
|
|
||||||
if (bg2_bitmap.is_active() && bg2_bitmap.width() > 0 && bg2_bitmap.height() > 0) {
|
if (bg2_bitmap.is_active() && bg2_bitmap.width() > 0 && bg2_bitmap.height() > 0) {
|
||||||
if (!bg2_bitmap.texture()) {
|
if (!bg2_bitmap.texture()) {
|
||||||
core::Renderer::Get().RenderBitmap(&bg2_bitmap);
|
core::Renderer::Get().RenderBitmap(&bg2_bitmap);
|
||||||
}
|
}
|
||||||
|
canvas_.DrawBitmap(bg2_bitmap, 0, 0, 1.0f, 200);
|
||||||
float scale_x = static_cast<float>(canvas_width) / bg2_bitmap.width();
|
|
||||||
float scale_y = static_cast<float>(canvas_height) / bg2_bitmap.height();
|
|
||||||
float scale = std::min(scale_x, scale_y);
|
|
||||||
|
|
||||||
int scaled_width = static_cast<int>(bg2_bitmap.width() * scale);
|
|
||||||
int scaled_height = static_cast<int>(bg2_bitmap.height() * scale);
|
|
||||||
int offset_x = (canvas_width - scaled_width) / 2;
|
|
||||||
int offset_y = (canvas_height - scaled_height) / 2;
|
|
||||||
|
|
||||||
printf("[Canvas] Drawing BG2 at offset=(%d,%d), scaled_size=%dx%d, scale=%.2f\n",
|
|
||||||
offset_x, offset_y, scaled_width, scaled_height, scale);
|
|
||||||
|
|
||||||
canvas_.DrawBitmap(bg2_bitmap, offset_x, offset_y, scale, 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("[Canvas] RenderRoomBackgroundLayers complete\n");
|
// TEST: Draw a bright red rectangle to verify canvas drawing works
|
||||||
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
|
ImVec2 canvas_pos = ImGui::GetCursorScreenPos();
|
||||||
|
draw_list->AddRectFilled(
|
||||||
|
ImVec2(canvas_pos.x + 50, canvas_pos.y + 50),
|
||||||
|
ImVec2(canvas_pos.x + 150, canvas_pos.y + 150),
|
||||||
|
IM_COL32(255, 0, 0, 255)); // Bright red
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace yaze::editor
|
} // namespace yaze::editor
|
||||||
|
|||||||
@@ -35,53 +35,90 @@ void BackgroundBuffer::ClearBuffer() { std::ranges::fill(buffer_, 0); }
|
|||||||
|
|
||||||
void BackgroundBuffer::DrawTile(const TileInfo& tile, uint8_t* canvas,
|
void BackgroundBuffer::DrawTile(const TileInfo& tile, uint8_t* canvas,
|
||||||
const uint8_t* tiledata, int indexoffset) {
|
const uint8_t* tiledata, int indexoffset) {
|
||||||
int tx = (tile.id_ / 16 * 512) + ((tile.id_ & 0xF) * 4);
|
// tiledata is a 128-pixel-wide indexed bitmap (16 tiles/row * 8 pixels/tile)
|
||||||
|
// Calculate tile position in the tilesheet
|
||||||
|
int tile_x = (tile.id_ % 16) * 8; // 16 tiles per row, 8 pixels per tile
|
||||||
|
int tile_y = (tile.id_ / 16) * 8; // Each row is 16 tiles
|
||||||
|
|
||||||
// Clamp palette to 0-5 (90 colors / 16 = 5.625, so max palette is 5)
|
// Dungeon graphics are 3BPP: 8 colors per palette (0-7, 8-15, 16-23, etc.)
|
||||||
// Palettes 6-7 would require colors 96-127, which don't exist in dungeon palettes
|
// NOT 4BPP which would be 16 colors per palette!
|
||||||
uint8_t clamped_palette = tile.palette_ & 0x07; // Get palette 0-7
|
// Clamp palette to 0-10 (90 colors / 8 = 11.25, so max palette is 10)
|
||||||
if (clamped_palette > 5) {
|
uint8_t clamped_palette = tile.palette_ & 0x0F;
|
||||||
clamped_palette = clamped_palette % 6; // Wrap palette 6->0, 7->1
|
if (clamped_palette > 10) {
|
||||||
|
clamped_palette = clamped_palette % 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t palnibble = (uint8_t)(clamped_palette << 4);
|
// For 3BPP: palette offset = palette * 8 (not * 16!)
|
||||||
uint8_t r = tile.horizontal_mirror_ ? 1 : 0;
|
uint8_t palette_offset = (uint8_t)(clamped_palette * 8);
|
||||||
|
|
||||||
for (int yl = 0; yl < 512; yl += 64) {
|
// Copy 8x8 pixels from tiledata to canvas
|
||||||
int my = indexoffset + (tile.vertical_mirror_ ? 448 - yl : yl);
|
for (int py = 0; py < 8; py++) {
|
||||||
|
for (int px = 0; px < 8; px++) {
|
||||||
for (int xl = 0; xl < 4; xl++) {
|
// Apply mirroring
|
||||||
int mx = 2 * (tile.horizontal_mirror_ ? 3 - xl : xl);
|
int src_x = tile.horizontal_mirror_ ? (7 - px) : px;
|
||||||
|
int src_y = tile.vertical_mirror_ ? (7 - py) : py;
|
||||||
uint8_t pixel = tiledata[tx + yl + xl];
|
|
||||||
|
// Read pixel from tiledata (128-pixel-wide bitmap)
|
||||||
int index = mx + my;
|
int src_index = (tile_y + src_y) * 128 + (tile_x + src_x);
|
||||||
canvas[index + r ^ 1] = (uint8_t)((pixel & 0x0F) | palnibble);
|
uint8_t pixel_index = tiledata[src_index];
|
||||||
canvas[index + r] = (uint8_t)((pixel >> 4) | palnibble);
|
|
||||||
|
// Apply palette offset and write to canvas
|
||||||
|
// For 3BPP: final color = base_pixel (0-7) + palette_offset (0, 8, 16, 24, ...)
|
||||||
|
uint8_t final_color = pixel_index + palette_offset;
|
||||||
|
int dest_index = indexoffset + (py * width_) + px;
|
||||||
|
canvas[dest_index] = final_color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundBuffer::DrawBackground(std::span<uint8_t> gfx16_data) {
|
void BackgroundBuffer::DrawBackground(std::span<uint8_t> gfx16_data) {
|
||||||
// Initialize bitmap
|
|
||||||
bitmap_.Create(width_, height_, 8, std::vector<uint8_t>(width_ * height_, 0));
|
|
||||||
int tiles_w = width_ / 8;
|
int tiles_w = width_ / 8;
|
||||||
int tiles_h = height_ / 8;
|
int tiles_h = height_ / 8;
|
||||||
if ((int)buffer_.size() < tiles_w * tiles_h) {
|
if ((int)buffer_.size() < tiles_w * tiles_h) {
|
||||||
buffer_.resize(tiles_w * tiles_h);
|
buffer_.resize(tiles_w * tiles_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CRITICAL: Always create a fresh bitmap for each DrawBackground call
|
||||||
|
// This ensures we're rendering the current tilemap state, not stale data
|
||||||
|
printf("[BG:DrawBackground] Creating fresh bitmap for rendering\n");
|
||||||
|
bitmap_.Create(width_, height_, 8, std::vector<uint8_t>(width_ * height_, 0));
|
||||||
|
|
||||||
|
// DEBUG: Check if gfx16_data has actual graphics
|
||||||
|
if (gfx16_data.size() < 100) {
|
||||||
|
printf("[BG:DrawBackground] WARNING: gfx16_data is too small (%zu bytes)\n", gfx16_data.size());
|
||||||
|
} else {
|
||||||
|
// Sample first 32 bytes
|
||||||
|
printf("[BG:DrawBackground] gfx16_data size=%zu, first 32 bytes: ", gfx16_data.size());
|
||||||
|
for (size_t i = 0; i < 32 && i < gfx16_data.size(); i++) {
|
||||||
|
printf("%02X ", gfx16_data[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
// For each tile on the tile buffer
|
// For each tile on the tile buffer
|
||||||
|
int drawn_count = 0;
|
||||||
for (int yy = 0; yy < tiles_h; yy++) {
|
for (int yy = 0; yy < tiles_h; yy++) {
|
||||||
for (int xx = 0; xx < tiles_w; xx++) {
|
for (int xx = 0; xx < tiles_w; xx++) {
|
||||||
uint16_t word = buffer_[xx + yy * tiles_w];
|
uint16_t word = buffer_[xx + yy * tiles_w];
|
||||||
// Prevent draw if tile == 0xFFFF since it's 0 indexed
|
// Prevent draw if tile == 0xFFFF since it's 0 indexed
|
||||||
if (word == 0xFFFF) continue;
|
if (word == 0xFFFF) continue;
|
||||||
auto tile = gfx::WordToTileInfo(word);
|
auto tile = gfx::WordToTileInfo(word);
|
||||||
DrawTile(tile, bitmap_.mutable_data().data(), gfx16_data.data(),
|
|
||||||
(yy * 512) + (xx * 8));
|
// Calculate pixel offset for tile position (xx, yy) in the 512x512 bitmap
|
||||||
|
// Each tile is 8x8, so pixel Y = yy * 8, pixel X = xx * 8
|
||||||
|
// Linear offset = (pixel_y * width) + pixel_x = (yy * 8 * 512) + (xx * 8)
|
||||||
|
int tile_offset = (yy * 8 * width_) + (xx * 8);
|
||||||
|
DrawTile(tile, bitmap_.mutable_data().data(), gfx16_data.data(), tile_offset);
|
||||||
|
drawn_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// CRITICAL: Sync bitmap data back to SDL surface!
|
||||||
|
// DrawTile() writes to bitmap_.mutable_data(), but the SDL surface needs updating
|
||||||
|
if (bitmap_.surface() && bitmap_.mutable_data().size() > 0) {
|
||||||
|
SDL_LockSurface(bitmap_.surface());
|
||||||
|
memcpy(bitmap_.surface()->pixels, bitmap_.mutable_data().data(), bitmap_.mutable_data().size());
|
||||||
|
SDL_UnlockSurface(bitmap_.surface());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackgroundBuffer::DrawFloor(const std::vector<uint8_t>& rom_data,
|
void BackgroundBuffer::DrawFloor(const std::vector<uint8_t>& rom_data,
|
||||||
|
|||||||
@@ -260,9 +260,12 @@ void Room::CopyRoomGraphicsToBuffer() {
|
|||||||
int buffer_index = data + block_offset;
|
int buffer_index = data + block_offset;
|
||||||
if (buffer_index >= 0 && buffer_index < static_cast<int>(gfx_buffer_data->size())) {
|
if (buffer_index >= 0 && buffer_index < static_cast<int>(gfx_buffer_data->size())) {
|
||||||
uint8_t map_byte = (*gfx_buffer_data)[buffer_index];
|
uint8_t map_byte = (*gfx_buffer_data)[buffer_index];
|
||||||
if (i < 4) {
|
// NOTE: DO NOT apply sprite offset here!
|
||||||
map_byte += kGfxBufferRoomSpriteLastLineOffset;
|
// current_gfx16_ holds pixel data (palette indices 0-7), not tile IDs.
|
||||||
}
|
// The 0x88 offset is for tile IDs in tilemaps, not raw pixel data.
|
||||||
|
// if (i < 4) {
|
||||||
|
// map_byte += kGfxBufferRoomSpriteLastLineOffset;
|
||||||
|
// }
|
||||||
|
|
||||||
// Validate current_gfx16_ access
|
// Validate current_gfx16_ access
|
||||||
int gfx_index = data + sheet_pos;
|
int gfx_index = data + sheet_pos;
|
||||||
@@ -280,75 +283,45 @@ void Room::CopyRoomGraphicsToBuffer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Room::RenderRoomGraphics() {
|
void Room::RenderRoomGraphics() {
|
||||||
std::printf("\n=== RenderRoomGraphics Room %d ===\n", room_id_);
|
|
||||||
|
|
||||||
CopyRoomGraphicsToBuffer();
|
CopyRoomGraphicsToBuffer();
|
||||||
std::printf("1. Graphics buffer copied\n");
|
|
||||||
|
|
||||||
gfx::Arena::Get().bg1().DrawFloor(rom()->vector(), tile_address,
|
bg1_buffer_.DrawFloor(rom()->vector(), tile_address,
|
||||||
tile_address_floor, floor1_graphics_);
|
tile_address_floor, floor1_graphics_);
|
||||||
gfx::Arena::Get().bg2().DrawFloor(rom()->vector(), tile_address,
|
bg2_buffer_.DrawFloor(rom()->vector(), tile_address,
|
||||||
tile_address_floor, floor2_graphics_);
|
tile_address_floor, floor2_graphics_);
|
||||||
std::printf("2. Floor pattern drawn\n");
|
|
||||||
|
|
||||||
// Render layout and object tiles to background buffers
|
// Render layout and object tiles to background buffers
|
||||||
RenderObjectsToBackground();
|
RenderObjectsToBackground();
|
||||||
std::printf("3. Objects rendered to buffer\n");
|
|
||||||
|
|
||||||
gfx::Arena::Get().bg1().DrawBackground(std::span<uint8_t>(current_gfx16_));
|
bg1_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
|
||||||
gfx::Arena::Get().bg2().DrawBackground(std::span<uint8_t>(current_gfx16_));
|
bg2_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
|
||||||
std::printf("4. Background drawn from buffer\n");
|
|
||||||
|
|
||||||
auto& bg1_bmp = gfx::Arena::Get().bg1().bitmap();
|
auto& bg1_bmp = bg1_buffer_.bitmap();
|
||||||
auto& bg2_bmp = gfx::Arena::Get().bg2().bitmap();
|
auto& bg2_bmp = bg2_buffer_.bitmap();
|
||||||
std::printf("5. BG1 bitmap: active=%d, size=%dx%d, data_size=%zu\n",
|
|
||||||
bg1_bmp.is_active(), bg1_bmp.width(), bg1_bmp.height(), bg1_bmp.vector().size());
|
// Get and apply palette FIRST (before marking modified)
|
||||||
|
|
||||||
// Get the palette for this room - just use the 90-color palette as-is
|
|
||||||
// The SNES will index into this palette correctly without needing expansion
|
|
||||||
auto& dungeon_pal_group = rom()->mutable_palette_group()->dungeon_main;
|
auto& dungeon_pal_group = rom()->mutable_palette_group()->dungeon_main;
|
||||||
int num_palettes = dungeon_pal_group.size();
|
int num_palettes = dungeon_pal_group.size();
|
||||||
int palette_id = palette;
|
int palette_id = palette;
|
||||||
|
|
||||||
std::printf("5a. Dungeon palette group has %d palettes total\n", num_palettes);
|
|
||||||
|
|
||||||
// Validate palette ID and fall back to palette 0 if invalid
|
|
||||||
if (palette_id < 0 || palette_id >= num_palettes) {
|
if (palette_id < 0 || palette_id >= num_palettes) {
|
||||||
std::printf("5a. WARNING: palette_id %d is out of bounds [0, %d), using palette 0\n",
|
std::printf("5. WARNING: palette_id %d is out of bounds [0, %d), using palette 0\n",
|
||||||
palette_id, num_palettes);
|
palette_id, num_palettes);
|
||||||
palette_id = 0;
|
palette_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the 90-color dungeon palette directly
|
auto bg1_palette = dungeon_pal_group[palette_id];
|
||||||
// The palette contains colors for BG layers - sprite colors are handled separately
|
|
||||||
auto bg1_palette = dungeon_pal_group[palette_id]; // Use operator[] to get a proper reference
|
|
||||||
|
|
||||||
std::printf("5a. Palette loaded: room palette_id=%d (requested=%d), size=%zu colors\n",
|
|
||||||
palette_id, palette, bg1_palette.size());
|
|
||||||
|
|
||||||
// CRITICAL: Only apply palette if it's valid
|
|
||||||
if (bg1_palette.size() > 0) {
|
if (bg1_palette.size() > 0) {
|
||||||
bg1_bmp.SetPaletteWithTransparent(bg1_palette, 0);
|
// Use SetPalette() to apply the FULL 90-color dungeon palette
|
||||||
bg2_bmp.SetPaletteWithTransparent(bg1_palette, 0);
|
// SetPaletteWithTransparent() only extracts 8 colors, which is wrong for dungeons!
|
||||||
std::printf("5b. Palette applied to bitmaps\n");
|
bg1_bmp.SetPalette(bg1_palette);
|
||||||
} else {
|
bg2_bmp.SetPalette(bg1_palette);
|
||||||
std::printf("5b. WARNING: Palette is empty, skipping SetPalette\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ALWAYS recreate textures when palette changes (UpdateBitmap doesn't update palette!)
|
|
||||||
std::printf("6. Recreating bitmap textures with new palette\n");
|
|
||||||
core::Renderer::Get().CreateAndRenderBitmap(
|
|
||||||
0x200, 0x200, 8, gfx::Arena::Get().bg1().bitmap().vector(),
|
|
||||||
gfx::Arena::Get().bg1().bitmap(), bg1_palette);
|
|
||||||
core::Renderer::Get().CreateAndRenderBitmap(
|
|
||||||
0x200, 0x200, 8, gfx::Arena::Get().bg2().bitmap().vector(),
|
|
||||||
gfx::Arena::Get().bg2().bitmap(), bg1_palette);
|
|
||||||
|
|
||||||
std::printf("7. BG1 has texture: %d\n", bg1_bmp.texture() != nullptr);
|
// CRITICAL: Recreate textures with the palette applied!
|
||||||
std::printf("=== RenderRoomGraphics Complete ===\n\n");
|
core::Renderer::Get().RenderBitmap(&bg1_buffer_.bitmap());
|
||||||
|
core::Renderer::Get().RenderBitmap(&bg2_buffer_.bitmap());
|
||||||
// Run comprehensive diagnostic
|
|
||||||
DiagnoseRoomRendering(*this, room_id_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Room::RenderObjectsToBackground() {
|
void Room::RenderObjectsToBackground() {
|
||||||
@@ -359,9 +332,9 @@ void Room::RenderObjectsToBackground() {
|
|||||||
|
|
||||||
std::printf("RenderObjectsToBackground: Room %d has %zu objects\n", room_id_, tile_objects_.size());
|
std::printf("RenderObjectsToBackground: Room %d has %zu objects\n", room_id_, tile_objects_.size());
|
||||||
|
|
||||||
// Get references to the background buffers
|
// Get references to THIS room's background buffers
|
||||||
auto& bg1 = gfx::Arena::Get().bg1();
|
auto& bg1 = bg1_buffer_;
|
||||||
auto& bg2 = gfx::Arena::Get().bg2();
|
auto& bg2 = bg2_buffer_;
|
||||||
|
|
||||||
// Render tile objects to their respective layers
|
// Render tile objects to their respective layers
|
||||||
int rendered_count = 0;
|
int rendered_count = 0;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
#include "app/gfx/background_buffer.h"
|
||||||
#include "app/zelda3/dungeon/room_layout.h"
|
#include "app/zelda3/dungeon/room_layout.h"
|
||||||
#include "app/zelda3/dungeon/room_object.h"
|
#include "app/zelda3/dungeon/room_object.h"
|
||||||
#include "app/zelda3/sprite/sprite.h"
|
#include "app/zelda3/sprite/sprite.h"
|
||||||
@@ -348,11 +349,21 @@ class Room {
|
|||||||
auto rom() { return rom_; }
|
auto rom() { return rom_; }
|
||||||
auto mutable_rom() { return rom_; }
|
auto mutable_rom() { return rom_; }
|
||||||
const std::array<uint8_t, 0x4000>& get_gfx_buffer() const { return current_gfx16_; }
|
const std::array<uint8_t, 0x4000>& get_gfx_buffer() const { return current_gfx16_; }
|
||||||
|
|
||||||
|
// Per-room background buffers (not shared via arena!)
|
||||||
|
auto& bg1_buffer() { return bg1_buffer_; }
|
||||||
|
auto& bg2_buffer() { return bg2_buffer_; }
|
||||||
|
const auto& bg1_buffer() const { return bg1_buffer_; }
|
||||||
|
const auto& bg2_buffer() const { return bg2_buffer_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Rom* rom_;
|
Rom* rom_;
|
||||||
|
|
||||||
std::array<uint8_t, 0x4000> current_gfx16_;
|
std::array<uint8_t, 0x4000> current_gfx16_;
|
||||||
|
|
||||||
|
// Each room has its OWN background buffers and bitmaps
|
||||||
|
gfx::BackgroundBuffer bg1_buffer_{512, 512};
|
||||||
|
gfx::BackgroundBuffer bg2_buffer_{512, 512};
|
||||||
|
|
||||||
bool is_light_;
|
bool is_light_;
|
||||||
bool is_loaded_ = false;
|
bool is_loaded_ = false;
|
||||||
|
|||||||
@@ -55,10 +55,10 @@ void DiagnoseRoomRendering(Room& room, int room_id) {
|
|||||||
}
|
}
|
||||||
std::printf(" Note: current_gfx16_ is internal, assuming populated after CopyRoomGraphicsToBuffer()\n");
|
std::printf(" Note: current_gfx16_ is internal, assuming populated after CopyRoomGraphicsToBuffer()\n");
|
||||||
|
|
||||||
// Step 4: Check background buffers in arena
|
// Step 4: Check THIS ROOM's background buffers (not arena!)
|
||||||
std::printf("\n=== Step 4: Background Buffers (Arena) ===\n");
|
std::printf("\n=== Step 4: Room Background Buffers ===\n");
|
||||||
auto& bg1 = gfx::Arena::Get().bg1();
|
auto& bg1 = room.bg1_buffer();
|
||||||
auto& bg2 = gfx::Arena::Get().bg2();
|
auto& bg2 = room.bg2_buffer();
|
||||||
auto bg1_buffer = bg1.buffer();
|
auto bg1_buffer = bg1.buffer();
|
||||||
auto bg2_buffer = bg2.buffer();
|
auto bg2_buffer = bg2.buffer();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user