feat: Implement room rendering diagnostics and integrate into Room rendering process (WIP)
This commit is contained in:
@@ -101,17 +101,27 @@ void BackgroundBuffer::DrawFloor(const std::vector<uint8_t>& rom_data,
|
||||
rom_data[tile_address_floor + f + 7]);
|
||||
|
||||
// Draw the floor tiles in a pattern
|
||||
// Convert TileInfo to 16-bit words with palette information
|
||||
uint16_t word1 = gfx::TileInfoToWord(floorTile1);
|
||||
uint16_t word2 = gfx::TileInfoToWord(floorTile2);
|
||||
uint16_t word3 = gfx::TileInfoToWord(floorTile3);
|
||||
uint16_t word4 = gfx::TileInfoToWord(floorTile4);
|
||||
uint16_t word5 = gfx::TileInfoToWord(floorTile5);
|
||||
uint16_t word6 = gfx::TileInfoToWord(floorTile6);
|
||||
uint16_t word7 = gfx::TileInfoToWord(floorTile7);
|
||||
uint16_t word8 = gfx::TileInfoToWord(floorTile8);
|
||||
|
||||
for (int xx = 0; xx < 16; xx++) {
|
||||
for (int yy = 0; yy < 32; yy++) {
|
||||
SetTileAt((xx * 4), (yy * 2), floorTile1.id_);
|
||||
SetTileAt((xx * 4) + 1, (yy * 2), floorTile2.id_);
|
||||
SetTileAt((xx * 4) + 2, (yy * 2), floorTile3.id_);
|
||||
SetTileAt((xx * 4) + 3, (yy * 2), floorTile4.id_);
|
||||
SetTileAt((xx * 4), (yy * 2), word1);
|
||||
SetTileAt((xx * 4) + 1, (yy * 2), word2);
|
||||
SetTileAt((xx * 4) + 2, (yy * 2), word3);
|
||||
SetTileAt((xx * 4) + 3, (yy * 2), word4);
|
||||
|
||||
SetTileAt((xx * 4), (yy * 2) + 1, floorTile5.id_);
|
||||
SetTileAt((xx * 4) + 1, (yy * 2) + 1, floorTile6.id_);
|
||||
SetTileAt((xx * 4) + 2, (yy * 2) + 1, floorTile7.id_);
|
||||
SetTileAt((xx * 4) + 3, (yy * 2) + 1, floorTile8.id_);
|
||||
SetTileAt((xx * 4), (yy * 2) + 1, word5);
|
||||
SetTileAt((xx * 4) + 1, (yy * 2) + 1, word6);
|
||||
SetTileAt((xx * 4) + 2, (yy * 2) + 1, word7);
|
||||
SetTileAt((xx * 4) + 3, (yy * 2) + 1, word8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <yaze.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@@ -10,6 +11,7 @@
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/rom.h"
|
||||
#include "app/snes.h"
|
||||
#include "app/zelda3/dungeon/room_diagnostic.h"
|
||||
#include "app/zelda3/dungeon/room_object.h"
|
||||
#include "app/zelda3/sprite/sprite.h"
|
||||
#include "util/log.h"
|
||||
@@ -308,26 +310,31 @@ void Room::RenderRoomGraphics() {
|
||||
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 the palette for this room
|
||||
auto bg1_palette =
|
||||
rom()->mutable_palette_group()->get_group("dungeon_main")[0].palette(0);
|
||||
|
||||
std::printf("5a. Palette loaded: size=%zu colors\n", bg1_palette.size());
|
||||
|
||||
if (!gfx::Arena::Get().bg1().bitmap().is_active()) {
|
||||
std::printf("6a. Creating new bitmap textures\n");
|
||||
core::Renderer::Get().CreateAndRenderBitmap(
|
||||
0x200, 0x200, 0x200, gfx::Arena::Get().bg1().bitmap().vector(),
|
||||
gfx::Arena::Get().bg1().bitmap(), bg1_palette);
|
||||
core::Renderer::Get().CreateAndRenderBitmap(
|
||||
0x200, 0x200, 0x200, gfx::Arena::Get().bg2().bitmap().vector(),
|
||||
gfx::Arena::Get().bg2().bitmap(), bg1_palette);
|
||||
} else {
|
||||
std::printf("6b. Updating existing bitmap textures\n");
|
||||
// Update the bitmap
|
||||
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().bg1().bitmap());
|
||||
core::Renderer::Get().UpdateBitmap(&gfx::Arena::Get().bg2().bitmap());
|
||||
}
|
||||
// CRITICAL: Apply palette to bitmaps BEFORE creating/updating textures
|
||||
bg1_bmp.SetPaletteWithTransparent(bg1_palette, 0);
|
||||
bg2_bmp.SetPaletteWithTransparent(bg1_palette, 0);
|
||||
std::printf("5b. Palette applied to bitmaps\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, 0x200, gfx::Arena::Get().bg1().bitmap().vector(),
|
||||
gfx::Arena::Get().bg1().bitmap(), bg1_palette);
|
||||
core::Renderer::Get().CreateAndRenderBitmap(
|
||||
0x200, 0x200, 0x200, 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);
|
||||
std::printf("=== RenderRoomGraphics Complete ===\n\n");
|
||||
|
||||
// Run comprehensive diagnostic
|
||||
DiagnoseRoomRendering(*this, room_id_);
|
||||
}
|
||||
|
||||
void Room::RenderObjectsToBackground() {
|
||||
@@ -372,14 +379,33 @@ void Room::RenderObjectsToBackground() {
|
||||
bool is_bg2 = (obj.layer_ == RoomObject::LayerType::BG2);
|
||||
auto& target_buffer = is_bg2 ? bg2 : bg1;
|
||||
|
||||
// Calculate the width of the object in Tile16 units
|
||||
// Most objects are arranged in a grid, typically 1-8 tiles wide
|
||||
// We calculate width based on square root for square objects,
|
||||
// or use a more flexible approach for rectangular objects
|
||||
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 == static_cast<int>(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;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw each Tile16 from the object
|
||||
// Each Tile16 is a 16x16 tile made of 4 TileInfo (8x8) tiles
|
||||
for (size_t i = 0; i < tiles.size(); i++) {
|
||||
const auto& tile16 = tiles[i];
|
||||
|
||||
// Calculate tile16 position (in 16x16 units, so multiply by 2 for 8x8 units)
|
||||
int base_x = obj_x + ((i % 4) * 2); // Assume 4-tile16 width for now
|
||||
int base_y = obj_y + ((i / 4) * 2);
|
||||
// Calculate tile16 position based on calculated width (in 16x16 units, so multiply by 2 for 8x8 units)
|
||||
int base_x = obj_x + ((i % tiles_wide) * 2);
|
||||
int base_y = obj_y + ((i / tiles_wide) * 2);
|
||||
|
||||
// Each Tile16 contains 4 TileInfo objects arranged as:
|
||||
// [0][1] (top-left, top-right)
|
||||
|
||||
193
src/app/zelda3/dungeon/room_diagnostic.cc
Normal file
193
src/app/zelda3/dungeon/room_diagnostic.cc
Normal file
@@ -0,0 +1,193 @@
|
||||
#include "room.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#include "app/gfx/arena.h"
|
||||
#include "app/gfx/bitmap.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
void DiagnoseRoomRendering(Room& room, int room_id) {
|
||||
std::printf("\n========== ROOM RENDERING DIAGNOSTIC ==========\n");
|
||||
std::printf("Room ID: %d\n\n", room_id);
|
||||
|
||||
// Step 1: Check ROM and graphics buffer
|
||||
std::printf("=== Step 1: ROM and Graphics Buffer ===\n");
|
||||
auto* rom = room.rom();
|
||||
if (!rom) {
|
||||
std::printf("❌ ROM pointer is NULL\n");
|
||||
return;
|
||||
}
|
||||
std::printf("✓ ROM pointer valid\n");
|
||||
std::printf(" ROM loaded: %s\n", rom->is_loaded() ? "YES" : "NO");
|
||||
std::printf(" ROM size: %zu bytes\n", rom->size());
|
||||
|
||||
auto* gfx_buffer = rom->mutable_graphics_buffer();
|
||||
if (!gfx_buffer || gfx_buffer->empty()) {
|
||||
std::printf("❌ Graphics buffer empty\n");
|
||||
return;
|
||||
}
|
||||
std::printf("✓ Graphics buffer loaded: %zu bytes\n", gfx_buffer->size());
|
||||
|
||||
// Step 2: Check room blocks
|
||||
std::printf("\n=== Step 2: Room Graphics Blocks ===\n");
|
||||
auto blocks = room.blocks();
|
||||
std::printf(" Blocks loaded: %s\n", blocks.empty() ? "NO" : "YES");
|
||||
if (!blocks.empty()) {
|
||||
std::printf(" Block indices: ");
|
||||
for (int i = 0; i < 16; i++) {
|
||||
std::printf("%d ", blocks[i]);
|
||||
if (i == 7) std::printf("\n ");
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
|
||||
// Step 3: Check current_gfx16_ buffer
|
||||
std::printf("\n=== Step 3: current_gfx16_ Buffer ===\n");
|
||||
// Sample first 100 bytes to check if populated
|
||||
bool has_data = false;
|
||||
int non_zero_count = 0;
|
||||
for (int i = 0; i < 100 && i < 32768; i++) {
|
||||
// Access through room's internal buffer would require making it accessible
|
||||
// For now, just note this step
|
||||
}
|
||||
std::printf(" Note: current_gfx16_ is internal, assuming populated after CopyRoomGraphicsToBuffer()\n");
|
||||
|
||||
// Step 4: Check background buffers in arena
|
||||
std::printf("\n=== Step 4: Background Buffers (Arena) ===\n");
|
||||
auto& bg1 = gfx::Arena::Get().bg1();
|
||||
auto& bg2 = gfx::Arena::Get().bg2();
|
||||
auto bg1_buffer = bg1.buffer();
|
||||
auto bg2_buffer = bg2.buffer();
|
||||
|
||||
std::printf("BG1 Buffer:\n");
|
||||
std::printf(" Size: %zu\n", bg1_buffer.size());
|
||||
int bg1_non_ff = 0;
|
||||
int bg1_non_zero = 0;
|
||||
for (const auto& word : bg1_buffer) {
|
||||
if (word != 0xFFFF) bg1_non_ff++;
|
||||
if (word != 0) bg1_non_zero++;
|
||||
}
|
||||
std::printf(" Non-0xFFFF tiles: %d / %zu\n", bg1_non_ff, bg1_buffer.size());
|
||||
std::printf(" Non-zero tiles: %d / %zu\n", bg1_non_zero, bg1_buffer.size());
|
||||
|
||||
// Sample first 10 non-0xFFFF tiles
|
||||
std::printf(" Sample tiles (first 10 non-0xFFFF): ");
|
||||
int sample_count = 0;
|
||||
for (size_t i = 0; i < bg1_buffer.size() && sample_count < 10; i++) {
|
||||
if (bg1_buffer[i] != 0xFFFF) {
|
||||
std::printf("0x%04X ", bg1_buffer[i]);
|
||||
sample_count++;
|
||||
}
|
||||
}
|
||||
std::printf("\n");
|
||||
|
||||
std::printf("\nBG2 Buffer:\n");
|
||||
std::printf(" Size: %zu\n", bg2_buffer.size());
|
||||
int bg2_non_ff = 0;
|
||||
int bg2_non_zero = 0;
|
||||
for (const auto& word : bg2_buffer) {
|
||||
if (word != 0xFFFF) bg2_non_ff++;
|
||||
if (word != 0) bg2_non_zero++;
|
||||
}
|
||||
std::printf(" Non-0xFFFF tiles: %d / %zu\n", bg2_non_ff, bg2_buffer.size());
|
||||
std::printf(" Non-zero tiles: %d / %zu\n", bg2_non_zero, bg2_buffer.size());
|
||||
|
||||
// Step 5: Check bitmaps
|
||||
std::printf("\n=== Step 5: Bitmaps ===\n");
|
||||
auto& bg1_bitmap = bg1.bitmap();
|
||||
auto& bg2_bitmap = bg2.bitmap();
|
||||
|
||||
std::printf("BG1 Bitmap:\n");
|
||||
std::printf(" Active: %s\n", bg1_bitmap.is_active() ? "YES" : "NO");
|
||||
std::printf(" Dimensions: %dx%d\n", bg1_bitmap.width(), bg1_bitmap.height());
|
||||
std::printf(" Data size: %zu bytes\n", bg1_bitmap.vector().size());
|
||||
std::printf(" Modified: %s\n", bg1_bitmap.modified() ? "YES" : "NO");
|
||||
|
||||
if (!bg1_bitmap.vector().empty()) {
|
||||
// Sample first 100 pixels
|
||||
int non_zero_pixels = 0;
|
||||
std::vector<uint8_t> unique_colors;
|
||||
for (size_t i = 0; i < 100 && i < bg1_bitmap.vector().size(); i++) {
|
||||
uint8_t pixel = bg1_bitmap.vector()[i];
|
||||
if (pixel != 0) non_zero_pixels++;
|
||||
if (std::find(unique_colors.begin(), unique_colors.end(), pixel) == unique_colors.end()) {
|
||||
unique_colors.push_back(pixel);
|
||||
}
|
||||
}
|
||||
std::printf(" First 100 pixels: %d non-zero, %zu unique colors\n",
|
||||
non_zero_pixels, unique_colors.size());
|
||||
std::printf(" Unique colors: ");
|
||||
for (size_t i = 0; i < std::min<size_t>(10, unique_colors.size()); i++) {
|
||||
std::printf("%d ", unique_colors[i]);
|
||||
}
|
||||
std::printf("\n");
|
||||
}
|
||||
|
||||
std::printf("\nBG2 Bitmap:\n");
|
||||
std::printf(" Active: %s\n", bg2_bitmap.is_active() ? "YES" : "NO");
|
||||
std::printf(" Dimensions: %dx%d\n", bg2_bitmap.width(), bg2_bitmap.height());
|
||||
std::printf(" Data size: %zu bytes\n", bg2_bitmap.vector().size());
|
||||
|
||||
// Step 6: Check textures
|
||||
std::printf("\n=== Step 6: SDL Textures ===\n");
|
||||
std::printf("BG1 Texture:\n");
|
||||
std::printf(" Texture pointer: %p\n", (void*)bg1_bitmap.texture());
|
||||
std::printf(" Texture valid: %s\n", bg1_bitmap.texture() != nullptr ? "YES" : "NO");
|
||||
|
||||
std::printf("\nBG2 Texture:\n");
|
||||
std::printf(" Texture pointer: %p\n", (void*)bg2_bitmap.texture());
|
||||
std::printf(" Texture valid: %s\n", bg2_bitmap.texture() != nullptr ? "YES" : "NO");
|
||||
|
||||
// Step 7: Check palette
|
||||
std::printf("\n=== Step 7: Palette ===\n");
|
||||
auto& palette = bg1_bitmap.palette();
|
||||
std::printf(" Palette size: %zu colors\n", palette.size());
|
||||
if (!palette.empty()) {
|
||||
std::printf(" First 8 colors (SNES format): ");
|
||||
for (size_t i = 0; i < 8 && i < palette.size(); i++) {
|
||||
std::printf("0x%04X ", palette[i].snes());
|
||||
}
|
||||
std::printf("\n");
|
||||
|
||||
// Check the actual colors being used by the bitmap pixels
|
||||
std::printf(" Colors at pixel indices being used:\n");
|
||||
std::vector<uint8_t> sample_indices;
|
||||
for (size_t i = 0; i < 100 && i < bg1_bitmap.vector().size(); i++) {
|
||||
uint8_t idx = bg1_bitmap.vector()[i];
|
||||
if (std::find(sample_indices.begin(), sample_indices.end(), idx) == sample_indices.end()) {
|
||||
sample_indices.push_back(idx);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < std::min<size_t>(10, sample_indices.size()); i++) {
|
||||
uint8_t idx = sample_indices[i];
|
||||
if (idx < palette.size()) {
|
||||
auto color = palette[idx];
|
||||
auto rgb_vec = color.rgb();
|
||||
std::printf(" [%d] = SNES:0x%04X RGB:(%.0f,%.0f,%.0f)\n",
|
||||
idx, color.snes(), rgb_vec.x * 255, rgb_vec.y * 255, rgb_vec.z * 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 8: Check room objects
|
||||
std::printf("\n=== Step 8: Room Objects ===\n");
|
||||
const auto& objects = room.GetTileObjects();
|
||||
std::printf(" Object count: %zu\n", objects.size());
|
||||
if (!objects.empty()) {
|
||||
std::printf(" First 5 objects:\n");
|
||||
for (size_t i = 0; i < 5 && i < objects.size(); i++) {
|
||||
const auto& obj = objects[i];
|
||||
std::printf(" [%zu] ID=0x%03X, Pos=(%d,%d), Size=%d, Layer=%d\n",
|
||||
i, obj.id_, obj.x_, obj.y_, obj.size_, obj.GetLayerValue());
|
||||
}
|
||||
}
|
||||
|
||||
std::printf("\n========== DIAGNOSTIC COMPLETE ==========\n\n");
|
||||
}
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
16
src/app/zelda3/dungeon/room_diagnostic.h
Normal file
16
src/app/zelda3/dungeon/room_diagnostic.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROOM_DIAGNOSTIC_H
|
||||
#define YAZE_APP_ZELDA3_DUNGEON_ROOM_DIAGNOSTIC_H
|
||||
|
||||
namespace yaze {
|
||||
namespace zelda3 {
|
||||
|
||||
class Room;
|
||||
|
||||
// Comprehensive diagnostic function to trace room rendering pipeline
|
||||
void DiagnoseRoomRendering(Room& room, int room_id);
|
||||
|
||||
} // namespace zelda3
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_APP_ZELDA3_DUNGEON_ROOM_DIAGNOSTIC_H
|
||||
|
||||
@@ -15,6 +15,7 @@ set(
|
||||
app/zelda3/dungeon/object_parser.cc
|
||||
app/zelda3/dungeon/object_renderer.cc
|
||||
app/zelda3/dungeon/room_layout.cc
|
||||
app/zelda3/dungeon/room_diagnostic.cc
|
||||
app/zelda3/dungeon/dungeon_editor_system.cc
|
||||
app/zelda3/dungeon/dungeon_object_editor.cc
|
||||
)
|
||||
Reference in New Issue
Block a user