Implement object rendering enhancements in DungeonObjectRenderer, including instruction execution limit to prevent infinite loops, improved tilemap initialization, and added palette setting functionality. Update header file with detailed method documentation.

This commit is contained in:
scawful
2025-04-16 22:35:15 -04:00
parent c2a0cd930c
commit 110106dc61
2 changed files with 128 additions and 22 deletions

View File

@@ -60,17 +60,39 @@ void DungeonObjectRenderer::ConfigureObject() {
*/
void DungeonObjectRenderer::RenderObject(uint32_t routine_ptr) {
cpu.PB = 0x01;
cpu.PC = routine_ptr;
// Push an initial value to the stack we can read later to confirm we are
// done
cpu.PushLong(0x01 << 16 | routine_ptr);
// Set up initial state for object drawing
cpu.Y = 0; // Start at the beginning of the tilemap
cpu.D = 0x7E; // Direct page register for memory access
int i = 0;
while (true) {
// Push return address to stack
cpu.PushLong(0x01 << 16 | 0xFFFF); // Push a dummy return address
// Set up a maximum instruction count to prevent infinite loops
const int MAX_INSTRUCTIONS = 10000;
int instruction_count = 0;
// Execute instructions until we hit a return instruction or max count
while (instruction_count < MAX_INSTRUCTIONS) {
uint8_t opcode = cpu.ReadByte(cpu.PB << 16 | cpu.PC);
cpu.ExecuteInstruction(opcode);
i++;
// Check for RTS (Return from Subroutine) instruction
if (opcode == 0x60) {
// Execute the RTS instruction
cpu.ExecuteInstruction(opcode);
break; // Exit the loop after RTS
}
// Execute the instruction
cpu.ExecuteInstruction(opcode);
instruction_count++;
}
// If we hit the max instruction count, log a warning
if (instruction_count >= MAX_INSTRUCTIONS) {
std::cerr << "Warning: Object rendering hit maximum instruction count"
<< std::endl;
}
UpdateObjectBitmap();
@@ -80,35 +102,58 @@ void DungeonObjectRenderer::RenderObject(uint32_t routine_ptr) {
// Layer 1 (BG2) in TILEMAPA
// Layer 2 (BG1) in TILEMAPB
void DungeonObjectRenderer::UpdateObjectBitmap() {
tilemap_.reserve(0x2000);
for (int i = 0; i < 0x2000; ++i) {
tilemap_.push_back(0);
}
int tilemap_offset = 0;
// Initialize the tilemap with zeros
tilemap_.resize(0x2000, 0);
// Iterate over tilemap in memory to read tile IDs
for (int tile_index = 0; tile_index < 512; tile_index++) {
// Read the tile ID from memory
int tile_id = memory_.ReadWord(0x7E2000 + tile_index);
std::cout << "Tile ID: " << std::hex << tile_id << std::endl;
uint16_t tile_id = memory_.ReadWord(0x7E2000 + tile_index * 2);
// Skip empty tiles (0x0000)
if (tile_id == 0) continue;
// Calculate sheet number (each sheet contains 32 tiles)
int sheet_number = tile_id / 32;
std::cout << "Sheet number: " << std::hex << sheet_number << std::endl;
int row = tile_id / 8;
int column = tile_id % 8;
// Ensure sheet number is valid
if (sheet_number >= vram_.sheets.size()) {
std::cerr << "Warning: Invalid sheet number " << sheet_number
<< std::endl;
continue;
}
int x = column * 8;
int y = row * 8;
// Calculate position in the tilemap
int tile_x = (tile_index % 32) * 8;
int tile_y = (tile_index / 32) * 8;
auto sheet = GraphicsSheetManager::GetInstance().mutable_gfx_sheets()->at(vram_.sheets[sheet_number]);
// Get the graphics sheet
auto& sheet = GraphicsSheetManager::GetInstance().mutable_gfx_sheets()->at(
vram_.sheets[sheet_number]);
// Copy the tile from VRAM using the read tile_id
sheet.Get8x8Tile(tile_id, x, y, tilemap_, tilemap_offset);
// Calculate the offset in the tilemap
int tilemap_offset = tile_y * 256 + tile_x;
// Copy the tile from the graphics sheet to the tilemap
sheet.Get8x8Tile(tile_id % 32, 0, 0, tilemap_, tilemap_offset);
}
// Create the bitmap from the tilemap
bitmap_.Create(256, 256, 8, tilemap_);
}
absl::Status DungeonObjectRenderer::SetPalette(const gfx::SnesPalette& palette,
size_t transparent_index) {
// Apply the palette to the bitmap
RETURN_IF_ERROR(
bitmap_.SetPaletteWithTransparent(palette, transparent_index));
// Store the palette in the VRAM structure for future reference
vram_.palettes.clear();
vram_.palettes.push_back(palette);
return absl::OkStatus();
}
} // namespace zelda3
} // namespace yaze

View File

@@ -11,22 +11,83 @@
namespace yaze {
namespace zelda3 {
/**
* @struct PseudoVram
* @brief Simulates the SNES VRAM for object rendering
*
* This structure holds the sheet IDs and palettes needed for rendering
* dungeon objects in Link to the Past.
*/
struct PseudoVram {
std::array<uint8_t, 16> sheets = { 0 };
std::vector<gfx::SnesPalette> palettes;
};
/**
* @class DungeonObjectRenderer
* @brief Renders dungeon objects from Link to the Past
*
* This class uses the emulator subsystem to simulate the SNES CPU
* drawing routines for dungeon objects. It captures the tile data
* written to memory and renders it to a bitmap.
*/
class DungeonObjectRenderer : public SharedRom {
public:
DungeonObjectRenderer() = default;
/**
* @brief Loads and renders a dungeon object
*
* @param routine_ptr Pointer to the drawing routine in ROM
* @param sheet_ids Array of graphics sheet IDs used by the object
*/
void LoadObject(uint32_t routine_ptr, std::array<uint8_t, 16>& sheet_ids);
/**
* @brief Configures the CPU state for object rendering
*/
void ConfigureObject();
/**
* @brief Executes the object drawing routine
*
* @param routine_ptr Pointer to the drawing routine in ROM
*/
void RenderObject(uint32_t routine_ptr);
/**
* @brief Updates the bitmap with the rendered object
*/
void UpdateObjectBitmap();
/**
* @brief Sets the palette for the rendered object
*
* @param palette The palette to use for the object
* @param transparent_index Index of the transparent color (default: 0)
* @return absl::Status Success or error status
*/
absl::Status SetPalette(const gfx::SnesPalette& palette, size_t transparent_index = 0);
/**
* @brief Gets the rendered bitmap
*
* @return gfx::Bitmap* Pointer to the bitmap
*/
gfx::Bitmap* bitmap() { return &bitmap_; }
/**
* @brief Gets the memory implementation
*
* @return Memory implementation
*/
auto memory() { return memory_; }
/**
* @brief Gets a mutable pointer to the memory implementation
*
* @return Mutable pointer to the memory implementation
*/
auto mutable_memory() { return &memory_; }
private: