From c08386801e4289d42d1d9370a09f95696f857e58 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 3 May 2025 23:40:41 -0400 Subject: [PATCH] Add BackgroundBuffer class for managing SNES background graphics Introduce the BackgroundBuffer class to handle tile management and rendering for SNES backgrounds. This includes methods for setting and getting tile values, clearing the buffer, and drawing tiles and backgrounds using provided graphics data. The class is designed to facilitate efficient graphics rendering in the application. --- src/app/gfx/background_buffer.cc | 119 +++++++++++++++++++++++++++++++ src/app/gfx/background_buffer.h | 45 ++++++++++++ src/app/gfx/gfx.cmake | 3 + 3 files changed, 167 insertions(+) create mode 100644 src/app/gfx/background_buffer.cc create mode 100644 src/app/gfx/background_buffer.h diff --git a/src/app/gfx/background_buffer.cc b/src/app/gfx/background_buffer.cc new file mode 100644 index 00000000..88de5b64 --- /dev/null +++ b/src/app/gfx/background_buffer.cc @@ -0,0 +1,119 @@ +#include "app/gfx/background_buffer.h" + +#include +#include +#include + +#include "absl/log/log.h" +#include "app/gfx/bitmap.h" +#include "app/gfx/snes_tile.h" + +namespace yaze { +namespace gfx { + +BackgroundBuffer::BackgroundBuffer(int width, int height) + : width_(width), height_(height) { + // Initialize buffer with size for SNES layers + const int total_tiles = (width / 8) * (height / 8); + buffer_.resize(total_tiles, 0); +} + +void BackgroundBuffer::SetTileAt(int x, int y, uint16_t value) { + if (x >= 0 && x < width_ / 8 && y >= 0 && y < height_ / 8) { + buffer_[y * (width_ / 8) + x] = value; + } +} + +uint16_t BackgroundBuffer::GetTileAt(int x, int y) const { + if (x >= 0 && x < width_ / 8 && y >= 0 && y < height_ / 8) { + return buffer_[y * (width_ / 8) + x]; + } + return 0; +} + +void BackgroundBuffer::ClearBuffer() { + std::fill(buffer_.begin(), buffer_.end(), 0); +} + +void BackgroundBuffer::DrawTile(const TileInfo& tile, uint8_t* canvas, + const uint8_t* tiledata, int indexoffset) { + int tx = (tile.id_ / 16 * 512) + ((tile.id_ & 0xF) * 4); + uint8_t palnibble = (uint8_t)(tile.palette_ << 4); + uint8_t r = tile.horizontal_mirror_ ? 1 : 0; + + for (int yl = 0; yl < 512; yl += 64) { + int my = indexoffset + (tile.vertical_mirror_ ? 448 - yl : yl); + + for (int xl = 0; xl < 4; xl++) { + int mx = 2 * (tile.horizontal_mirror_ ? 3 - xl : xl); + + uint8_t pixel = tiledata[tx + yl + xl]; + + int index = mx + my; + canvas[index + r ^ 1] = (uint8_t)((pixel & 0x0F) | palnibble); + canvas[index + r] = (uint8_t)((pixel >> 4) | palnibble); + } + } +} + +void BackgroundBuffer::DrawBackground(std::span gfx16_data) { + // Initialize bitmap + bitmap_.Create(width_, height_, 8, std::vector(width_ * height_, 0)); + if (buffer_.size() < width_ * height_) { + buffer_.resize(width_ * height_); + } + + // For each tile on the tile buffer + for (int yy = 0; yy < (height_ / 8) * (width_ / 8); yy += (width_ / 8)) { + for (int xx = 0; xx < width_ / 8; xx++) { + // Prevent draw if tile == 0xFFFF since it's 0 indexed + if (buffer_[xx + yy] != 0xFFFF) { + auto tile = gfx::GetTilesInfo(buffer_[xx + yy]); + DrawTile(tile, bitmap_.mutable_data().data(), gfx16_data.data(), 0); + } + } + } +} + +void BackgroundBuffer::DrawFloor(const std::vector& rom_data, + int tile_address, int tile_address_floor, + uint8_t floor_graphics) { + uint8_t f = (uint8_t)(floor_graphics << 4); + + // Create floor tiles from ROM data + gfx::TileInfo floorTile1(rom_data[tile_address + f], + rom_data[tile_address + f + 1]); + gfx::TileInfo floorTile2(rom_data[tile_address + f + 2], + rom_data[tile_address + f + 3]); + gfx::TileInfo floorTile3(rom_data[tile_address + f + 4], + rom_data[tile_address + f + 5]); + gfx::TileInfo floorTile4(rom_data[tile_address + f + 6], + rom_data[tile_address + f + 7]); + + gfx::TileInfo floorTile5(rom_data[tile_address_floor + f], + rom_data[tile_address_floor + f + 1]); + gfx::TileInfo floorTile6(rom_data[tile_address_floor + f + 2], + rom_data[tile_address_floor + f + 3]); + gfx::TileInfo floorTile7(rom_data[tile_address_floor + f + 4], + rom_data[tile_address_floor + f + 5]); + gfx::TileInfo floorTile8(rom_data[tile_address_floor + f + 6], + rom_data[tile_address_floor + f + 7]); + + // Draw the floor tiles in a pattern + 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) + 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_); + } + } +} + +} // namespace gfx +} // namespace yaze \ No newline at end of file diff --git a/src/app/gfx/background_buffer.h b/src/app/gfx/background_buffer.h new file mode 100644 index 00000000..f08442f1 --- /dev/null +++ b/src/app/gfx/background_buffer.h @@ -0,0 +1,45 @@ +#ifndef YAZE_APP_GFX_BACKGROUND_BUFFER_H +#define YAZE_APP_GFX_BACKGROUND_BUFFER_H + +#include +#include + +#include "app/gfx/bitmap.h" +#include "app/gfx/snes_tile.h" + +namespace yaze { +namespace gfx { + +class BackgroundBuffer { + public: + BackgroundBuffer(int width = 512, int height = 512); + + // Buffer manipulation methods + void SetTileAt(int x, int y, uint16_t value); + uint16_t GetTileAt(int x, int y) const; + void ClearBuffer(); + + // Drawing methods + void DrawTile(const TileInfo& tile_info, uint8_t* canvas, + const uint8_t* tiledata, int indexoffset); + void DrawBackground(std::span gfx16_data); + + // Floor drawing methods + void DrawFloor(const std::vector& rom_data, int tile_address, + int tile_address_floor, uint8_t floor_graphics); + + // Accessors + auto buffer() { return buffer_; } + auto& bitmap() { return bitmap_; } + + private: + std::vector buffer_; + gfx::Bitmap bitmap_; + int width_; + int height_; +}; + +} // namespace gfx +} // namespace yaze + +#endif // YAZE_APP_GFX_BACKGROUND_BUFFER_H \ No newline at end of file diff --git a/src/app/gfx/gfx.cmake b/src/app/gfx/gfx.cmake index 5ebabdf0..83b22e11 100644 --- a/src/app/gfx/gfx.cmake +++ b/src/app/gfx/gfx.cmake @@ -1,10 +1,13 @@ set( YAZE_APP_GFX_SRC + app/gfx/arena.cc + app/gfx/background_buffer.cc app/gfx/bitmap.cc app/gfx/compression.cc app/gfx/scad_format.cc app/gfx/snes_palette.cc app/gfx/snes_tile.cc app/gfx/snes_color.cc + app/gfx/tilemap.cc app/gfx/tilesheet.cc ) \ No newline at end of file