From 8526b3fa519cc8f6b57e788823f51d8b1a7605fa Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 22 Dec 2023 01:03:41 -0600 Subject: [PATCH] [nostalgia/core] Add loading multiple tile sheets --- .../core/include/nostalgia/core/gfx.hpp | 43 ++++++ src/nostalgia/modules/core/src/gba/gfx.cpp | 123 +++++++++++++++--- src/nostalgia/modules/core/src/opengl/gfx.cpp | 34 +++++ 3 files changed, 185 insertions(+), 15 deletions(-) diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index 3af19bfe..70ffb244 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -14,6 +14,8 @@ namespace nostalgia::core { struct Sprite { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.Sprite"; + static constexpr auto TypeVersion = 1; bool enabled = false; int x = 0; int y = 0; @@ -39,6 +41,42 @@ oxModelBegin(Sprite) oxModelField(priority) oxModelEnd() +struct TileSheetSetEntrySection { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection"; + static constexpr auto TypeVersion = 1; + int32_t begin = 0; + int32_t size = 0; +}; + +oxModelBegin(TileSheetSetEntrySection) + oxModelField(begin) + oxModelField(size) +oxModelEnd() + +struct TileSheetSetEntry { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntry"; + static constexpr auto TypeVersion = 1; + ox::FileAddress tilesheet; + ox::Vector sections; +}; + +oxModelBegin(TileSheetSetEntry) + oxModelField(tilesheet) + oxModelField(sections) +oxModelEnd() + +struct TileSheetSet { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet"; + static constexpr auto TypeVersion = 1; + int bpp = 0; + ox::Vector entries; +}; + +oxModelBegin(TileSheetSet) + oxModelField(bpp) + oxModelField(entries) +oxModelEnd() + ox::Error loadBgPalette( Context &ctx, ox::FileAddress const&paletteAddr) noexcept; @@ -47,6 +85,11 @@ ox::Error loadSpritePalette( Context &ctx, ox::FileAddress const&paletteAddr) noexcept; +ox::Error loadBgTileSheet( + Context &ctx, + unsigned cbb, + TileSheetSet const&set) noexcept; + ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, diff --git a/src/nostalgia/modules/core/src/gba/gfx.cpp b/src/nostalgia/modules/core/src/gba/gfx.cpp index 1ecf0536..0c5e891c 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -23,6 +23,7 @@ namespace nostalgia::core { constexpr auto GbaTileColumns = 32; constexpr auto GbaTileRows = 32; +constexpr auto SpriteCount = 128; struct GbaPaletteTarget { static constexpr auto TypeName = Palette::TypeName; @@ -33,9 +34,13 @@ struct GbaPaletteTarget { struct GbaTileMapTarget { static constexpr auto TypeName = CompactTileSheet::TypeName; static constexpr auto TypeVersion = CompactTileSheet::TypeVersion; - BgCbbData *cbbData = nullptr; + unsigned &bpp; ox::FileAddress defaultPalette; volatile uint16_t *tileMap = nullptr; + // the following values are not actually in CompactTileSheet, + // and only exist to communicate with the loading process + size_t tileWriteIdx = 0; + unsigned targetBpp = 0; }; constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) noexcept { @@ -56,25 +61,56 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) noexcept { oxReturnError(io->template setTypeInfo()); - oxReturnError(io->field("bpp", &t->cbbData->bpp)); + oxReturnError(io->field("bpp", &t->bpp)); oxReturnError(io->field("defaultPalette", &t->defaultPalette)); uint16_t intermediate = 0; - const auto handleTileMap = [t, &intermediate](std::size_t i, const uint8_t *tile) { - if (i & 1) { // i is odd - intermediate |= static_cast(*tile) << 8; - t->tileMap[i / 2] = intermediate; - } else { // i is even - intermediate = *tile & 0x00ff; - } - return OxError(0); - }; - return io->template field("tileMap", handleTileMap); + if (t->targetBpp == 0) { + t->targetBpp = t->bpp; + } + if (t->targetBpp != t->bpp && t->bpp == 8) { + return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB"); + } + ox::Error out{}; + if (t->targetBpp == t->bpp) { + const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const*tile) { + if (i & 1) { // i is odd + intermediate |= static_cast(*tile) << 8; + t->tileMap[i / 2] = intermediate; + ++t->tileWriteIdx; + } else { // i is even + intermediate = *tile & 0x00ff; + } + return ox::Error{}; + }; + out = io->template field("tileMap", handleTileMap); + } else if (t->targetBpp > t->bpp) { // 4 -> 8 bits + const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const*tile) { + if (i & 1) { // i is odd + intermediate |= static_cast(*tile) << 8; + // write 4 pixels from intermediate + t->tileMap[i - 3] = (intermediate & 0b0000'0000'0000'1111) >> 0; + t->tileMap[i - 2] = (intermediate & 0b0000'0000'1111'0000) >> 4; + t->tileMap[i - 1] = (intermediate & 0b0000'1111'0000'0000) >> 8; + t->tileMap[i - 0] = (intermediate & 0b1111'0000'0000'0000) >> 14; + ++t->tileWriteIdx; + } else { // i is even + intermediate = *tile & 0x00ff; + } + return ox::Error{}; + }; + out = io->template field("tileMap", handleTileMap); + } + return out; } ox::Error initGfx(Context&, InitParams const&) noexcept { for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { teagba::bgSetSbb(*bgCtl, 28); } + for (uint16_t i = 0; i < SpriteCount; ++i) { + auto &sa = teagba::spriteAttr(i); + sa.idx = i; + } return {}; } @@ -100,6 +136,27 @@ ox::Error loadSpritePalette( return {}; } +static ox::Error loadTileSheetSet( + Context &ctx, + unsigned &bpp, + uint16_t *tileMapTargetMem, + TileSheetSet const&set) noexcept { + auto &rom = ctx.rom(); + size_t tileWriteIdx = 0; + for (auto const&entry : set.entries) { + oxRequire(tsStat, rom.stat(entry.tilesheet)); + oxRequire(ts, rom.directAccess(entry.tilesheet)); + GbaTileMapTarget target{ + .bpp = bpp, + .defaultPalette = {}, + .tileMap = tileMapTargetMem + tileWriteIdx, + }; + oxReturnError(ox::readMC(ts, static_cast(tsStat.size), &target)); + tileWriteIdx += target.tileWriteIdx; + } + return {}; +} + ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, @@ -109,7 +166,7 @@ ox::Error loadBgTileSheet( oxRequire(tsStat, rom.stat(tilesheetAddr)); oxRequire(ts, rom.directAccess(tilesheetAddr)); GbaTileMapTarget target{ - .cbbData = &ctx.cbbData[cbb], + .bpp = ctx.cbbData[cbb].bpp, .defaultPalette = {}, .tileMap = MEM_BG_TILES[cbb].data(), }; @@ -127,14 +184,40 @@ ox::Error loadBgTileSheet( return {}; } +ox::Error loadBgTileSheet( + Context &ctx, + unsigned cbb, + TileSheetSet const&set) noexcept { + auto &bpp = ctx.cbbData[cbb].bpp; + oxReturnError(loadTileSheetSet(ctx, bpp, MEM_BG_TILES[cbb].data(), set)); + // update bpp of all bgs with the updated cbb + teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) { + if (teagba::bgCbb(bgCtl) == cbb) { + teagba::bgSetBpp(bgCtl, bpp); + } + }); + return {}; +} + +static void setSpritesBpp(unsigned const bpp) noexcept { + auto const eightBpp = static_cast(bpp == 8); + for (auto i = 0u; i < SpriteCount; ++i) { + auto &sa = teagba::spriteAttr(i); + auto &a = sa.attr0; + a |= static_cast((a & ~static_cast(1u << 13)) | (eightBpp << 13)); + } +} + ox::Error loadSpriteTileSheet( Context &ctx, ox::FileAddress const&tilesheetAddr, bool loadDefaultPalette) noexcept { auto &rom = ctx.rom(); - oxRequire(tsStat, ctx.rom().stat(tilesheetAddr)); + oxRequire(tsStat, rom.stat(tilesheetAddr)); oxRequire(ts, rom.directAccess(tilesheetAddr)); + unsigned bpp{}; GbaTileMapTarget target{ + .bpp = bpp, .defaultPalette = {}, .tileMap = MEM_SPRITE_TILES, }; @@ -142,6 +225,16 @@ ox::Error loadSpriteTileSheet( if (loadDefaultPalette && target.defaultPalette) { oxReturnError(loadSpritePalette(ctx, target.defaultPalette)); } + setSpritesBpp(bpp); + return {}; +} + +ox::Error loadSpriteTileSheet( + Context &ctx, + TileSheetSet const&set) noexcept { + unsigned bpp{}; + oxReturnError(loadTileSheetSet(ctx, bpp, MEM_SPRITE_TILES, set)); + setSpritesBpp(bpp); return {}; } @@ -220,7 +313,7 @@ void setSprite(Context&, uint_t idx, Sprite const&s) noexcept { } uint_t spriteCount(Context&) noexcept { - return 128; + return SpriteCount; } } diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index 2872e7a3..279029c6 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -504,6 +504,23 @@ ox::Error loadSpritePalette( return {}; } +static ox::Result buildSetTsd( + Context &ctx, + TileSheetSet const&set) noexcept { + auto &kctx = keelCtx(ctx.turbineCtx); + TileSheetData setTsd; + setTsd.width = TileWidth; + for (auto const&entry : set.entries) { + oxRequire(tilesheet, readObj(kctx, entry.tilesheet)); + oxRequire(tsd, normalizeTileSheet(*tilesheet)); + for (auto const p : tsd.pixels) { + setTsd.pixels.push_back(p); + } + setTsd.height += tsd.height; + } + return setTsd; +} + ox::Error loadBgTileSheet( Context &ctx, uint_t cbb, @@ -520,6 +537,15 @@ ox::Error loadBgTileSheet( return {}; } +ox::Error loadBgTileSheet( + Context &ctx, + unsigned cbb, + TileSheetSet const&set) noexcept { + oxRequire(setTsd, buildSetTsd(ctx, set)); + ctx.cbbs[cbb].tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data()); + return {}; +} + ox::Error loadSpriteTileSheet( Context &ctx, ox::FileAddress const&tilesheetAddr, @@ -535,6 +561,14 @@ ox::Error loadSpriteTileSheet( return {}; } +ox::Error loadSpriteTileSheet( + Context &ctx, + TileSheetSet const&set) noexcept { + oxRequire(setTsd, buildSetTsd(ctx, set)); + ctx.spriteBlocks.tex = renderer::createTexture(setTsd.width, setTsd.height, setTsd.pixels.data()); + return {}; +} + void setBgTile( Context &ctx, uint_t bgIdx,