diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index 10d64268..8cc6882a 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -43,6 +43,22 @@ oxModelBegin(Sprite) oxModelField(priority) oxModelEnd() +struct BgTile { + static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.BgTile"; + static constexpr auto TypeVersion = 1; + unsigned tileIdx = 0; + unsigned palBank = 0; + unsigned horizontalFlip = false; + unsigned verticalFlip = false; +}; + +oxModelBegin(BgTile) + oxModelField(tileIdx) + oxModelField(palBank) + oxModelField(horizontalFlip) + oxModelField(verticalFlip) +oxModelEnd() + struct TileSheetSetEntrySection { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection"; static constexpr auto TypeVersion = 1; @@ -86,6 +102,7 @@ oxModelEnd() ox::Error loadBgPalette( Context &ctx, + size_t palBank, ox::FileAddress const&paletteAddr) noexcept; ox::Error loadSpritePalette( @@ -101,7 +118,7 @@ ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, ox::FileAddress const&tilesheetAddr, - bool loadDefaultPalette = false) noexcept; + ox::Optional const&paletteBank = {}) noexcept; ox::Error loadSpriteTileSheet( Context &ctx, @@ -112,7 +129,9 @@ ox::Error loadSpriteTileSheet( Context &ctx, TileSheetSet const&set) noexcept; -void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, uint8_t tile) noexcept; +void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, unsigned tile, unsigned palBank = 0) noexcept; + +void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, BgTile const&tile) noexcept; void clearBg(Context &ctx, uint_t bgIdx) noexcept; diff --git a/src/nostalgia/modules/core/src/gba/gfx.cpp b/src/nostalgia/modules/core/src/gba/gfx.cpp index 39530f60..dcd33ba2 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -158,9 +158,10 @@ ox::Error initGfx(Context&, InitParams const&) noexcept { ox::Error loadBgPalette( Context &ctx, + size_t palBank, ox::FileAddress const&paletteAddr) noexcept { auto &rom = ctx.rom(); - GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE}; + GbaPaletteTarget const palTarget{.palette = MEM_BG_PALETTE + palBank * 16}; oxRequire(palStat, rom.stat(paletteAddr)); oxRequire(pal, rom.directAccess(paletteAddr)); oxReturnError(ox::readMC(pal, static_cast(palStat.size), &palTarget)); @@ -189,11 +190,11 @@ static ox::Error loadTileSheetSet( oxRequire(ts, rom.directAccess(entry.tilesheet)); unsigned tilesheetBpp{}; GbaTileMapTarget target{ - .bpp = tilesheetBpp, - .defaultPalette = {}, - .tileMap = tileMapTargetMem + tileWriteIdx, - .targetBpp = static_cast(set.bpp), - .setEntry = &entry, + .bpp = tilesheetBpp, + .defaultPalette = {}, + .tileMap = tileMapTargetMem + tileWriteIdx, + .targetBpp = static_cast(set.bpp), + .setEntry = &entry, }; oxReturnError(ox::readMC(ts, static_cast(tsStat.size), &target)); tileWriteIdx += target.tileWriteIdx; @@ -205,7 +206,7 @@ ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, ox::FileAddress const&tilesheetAddr, - bool loadDefaultPalette) noexcept { + ox::Optional const&paletteBank) noexcept { auto &rom = ctx.rom(); oxRequire(tsStat, rom.stat(tilesheetAddr)); oxRequire(ts, rom.directAccess(tilesheetAddr)); @@ -222,8 +223,8 @@ ox::Error loadBgTileSheet( teagba::bgSetBpp(bgCtl, bpp); } }); - if (loadDefaultPalette && target.defaultPalette) { - oxReturnError(loadBgPalette(ctx, target.defaultPalette)); + if (paletteBank.has_value() && target.defaultPalette) { + oxReturnError(loadBgPalette(ctx, *paletteBank, target.defaultPalette)); } return {}; } @@ -283,9 +284,14 @@ ox::Error loadSpriteTileSheet( return {}; } -void setBgTile(Context&, uint_t bgIdx, int column, int row, uint8_t tile) noexcept { - const auto tileIdx = static_cast(row * GbaTileColumns + column); - MEM_BG_MAP[bgIdx][tileIdx] = tile; +void setBgTile(Context&, uint_t bgIdx, int column, int row, BgTile const&tile) noexcept { + auto const tileIdx = static_cast(row * GbaTileColumns + column); + // see Tonc 9.3 + MEM_BG_MAP[bgIdx][tileIdx] = + static_cast(tile.tileIdx & 0b1'1111'1111) | + static_cast(tile.horizontalFlip << 0xa) | + static_cast(tile.verticalFlip << 0xb) | + static_cast(tile.palBank << 0xc); } void clearBg(Context&, uint_t bgIdx) noexcept { diff --git a/src/nostalgia/modules/core/src/gfx.cpp b/src/nostalgia/modules/core/src/gfx.cpp index db44ed66..2628e964 100644 --- a/src/nostalgia/modules/core/src/gfx.cpp +++ b/src/nostalgia/modules/core/src/gfx.cpp @@ -137,13 +137,20 @@ constexpr ox::Array charMap = { 50, // ~ }; +void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, unsigned tile, unsigned palBank) noexcept { + setBgTile(ctx, bgIdx, column, row, { + .tileIdx = tile, + .palBank = palBank, + }); +} + ox::Error initConsole(Context &ctx) noexcept { constexpr ox::FileAddress TilesheetAddr = ox::StringLiteral("/TileSheets/Charset.ng"); constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal"); setBgStatus(ctx, 0b0001); setBgCbb(ctx, 0, 0); oxReturnError(loadBgTileSheet(ctx, 0, TilesheetAddr)); - return loadBgPalette(ctx, PaletteAddr); + return loadBgPalette(ctx, 0, PaletteAddr); } void puts( diff --git a/src/nostalgia/modules/core/src/opengl/context.hpp b/src/nostalgia/modules/core/src/opengl/context.hpp index f100a23a..e6727c8d 100644 --- a/src/nostalgia/modules/core/src/opengl/context.hpp +++ b/src/nostalgia/modules/core/src/opengl/context.hpp @@ -24,6 +24,7 @@ class Context { ox::Array cbbs; renderer::SpriteBlockset spriteBlocks; ox::Array spriteStates; + ox::Array bgPalette; ox::Array backgrounds; renderer::Drawer drawer; uint_t spriteCount = 0; diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index 188251a3..602a80bd 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -35,7 +35,9 @@ constexpr ox::CStringView bgvshadTmpl = R"glsl( in vec2 vTexCoord; in vec3 vPosition; in float vTileIdx; + in float vPalOffset; out vec2 fTexCoord; + out float fPalOffset; uniform float vXScale; uniform float vTileHeight; uniform float vBgIdx; @@ -49,17 +51,19 @@ constexpr ox::CStringView bgvshadTmpl = R"glsl( fTexCoord = vec2( vTexCoord.x, vTexCoord.y * vTileHeight + vTileIdx * vTileHeight); + fPalOffset = vPalOffset; })glsl"; constexpr ox::CStringView bgfshadTmpl = R"glsl( {} out vec4 outColor; + in float fPalOffset; in vec2 fTexCoord; uniform sampler2D image; uniform vec2 fSrcImgSz; uniform vec4 fPalette[256]; void main() { - outColor = fPalette[int(texture(image, fTexCoord).rgb.r * 256)]; + outColor = fPalette[int(texture(image, fTexCoord).rgb.r * 256) + int(fPalOffset)]; //outColor = vec4(0.0, 0.7, 1.0, 1.0); if (outColor.a == 0) { discard; @@ -147,6 +151,7 @@ static void setTileBufferObject( float y, float textureTileIdx, float priority, + float palOffset, float *vbo, GLuint *ebo) noexcept { // don't worry, this memcpy gets optimized to something much more ideal @@ -158,10 +163,10 @@ static void setTileBufferObject( y += 1.0f - ymod; auto const prif = priority * PriorityScale; ox::Array const vertices { - x, y, prif, 0, 1, textureTileIdx, // bottom left - x + xmod, y, prif, 1, 1, textureTileIdx, // bottom right - x + xmod, y + ymod, prif, 1, 0, textureTileIdx, // top right - x, y + ymod, prif, 0, 0, textureTileIdx, // top left + x, y, prif, 0, 1, textureTileIdx, palOffset, // bottom left + x + xmod, y, prif, 1, 1, textureTileIdx, palOffset, // bottom right + x + xmod, y + ymod, prif, 1, 0, textureTileIdx, palOffset, // top right + x, y + ymod, prif, 0, 0, textureTileIdx, palOffset, // top left }; memcpy(vbo, vertices.data(), sizeof(vertices)); ox::Array const elms { @@ -191,6 +196,7 @@ static void initBackgroundBufferObjects(glutils::BufferSet &bs) noexcept { static_cast(y), 0, 0, + 0, vbo, ebo); } @@ -252,6 +258,11 @@ static void initBackgroundBufferset( glVertexAttribPointer( heightMultAttr, 1, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), std::bit_cast(uintptr_t{5 * sizeof(float)})); + auto const palBankAttr = static_cast(glGetAttribLocation(shader, "vPalOffset")); + glEnableVertexAttribArray(palBankAttr); + glVertexAttribPointer( + palBankAttr, 1, GL_FLOAT, GL_FALSE, BgVertexVboRowLength * sizeof(float), + std::bit_cast(uintptr_t{6 * sizeof(float)})); } static glutils::GLTexture createTexture( @@ -337,18 +348,19 @@ static void drawSprites(Context &ctx, ox::Size const&renderSz) noexcept { } static void loadPalette( + ox::Array &palette, + size_t palOffset, GLuint shaderPgrm, Palette const&pal) noexcept { static constexpr std::size_t ColorCnt = 256; - ox::Array palette{}; - for (auto i = 0u; const auto c : pal.colors) { + for (auto i = palOffset; auto const c : pal.colors) { palette[i++] = redf(c); palette[i++] = greenf(c); palette[i++] = bluef(c); palette[i++] = 255; } // make first color transparent - palette[3] = 0; + palette[palOffset + 3] = 0; glUseProgram(shaderPgrm); const auto uniformPalette = static_cast(glGetUniformLocation(shaderPgrm, "fPalette")); glUniform4fv(uniformPalette, ColorCnt, palette.data()); @@ -488,10 +500,11 @@ static ox::Result normalizeTileSheet( ox::Error loadBgPalette( Context &ctx, + size_t palBank, ox::FileAddress const&paletteAddr) noexcept { auto &kctx = keelCtx(ctx.turbineCtx); oxRequire(palette, readObj(kctx, paletteAddr)); - renderer::loadPalette(ctx.bgShader, *palette); + renderer::loadPalette(ctx.bgPalette, palBank * 16 * 4, ctx.bgShader, *palette); return {}; } @@ -500,7 +513,8 @@ ox::Error loadSpritePalette( ox::FileAddress const&paletteAddr) noexcept { auto &kctx = keelCtx(ctx.turbineCtx); oxRequire(palette, readObj(kctx, paletteAddr)); - renderer::loadPalette(ctx.spriteShader, *palette); + ox::Array pal; + renderer::loadPalette(pal, 0, ctx.spriteShader, *palette); return {}; } @@ -529,14 +543,14 @@ ox::Error loadBgTileSheet( Context &ctx, uint_t cbb, ox::FileAddress const&tilesheetAddr, - bool loadDefaultPalette) noexcept { + ox::Optional const&paletteBank) noexcept { auto &kctx = keelCtx(ctx.turbineCtx); oxRequire(tilesheet, readObj(kctx, tilesheetAddr)); oxRequire(tsd, normalizeTileSheet(*tilesheet)); oxTracef("nostalgia.core.gfx.gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbb, tsd.width, tsd.height); ctx.cbbs[cbb].tex = renderer::createTexture(tsd.width, tsd.height, tsd.pixels.data()); - if (loadDefaultPalette) { - oxReturnError(loadBgPalette(ctx, tilesheet->defaultPalette)); + if (paletteBank.has_value() && tilesheet->defaultPalette) { + oxReturnError(loadBgPalette(ctx, *paletteBank, tilesheet->defaultPalette)); } return {}; } @@ -578,7 +592,8 @@ void setBgTile( uint_t bgIdx, int column, int row, - uint8_t tile) noexcept { + unsigned tile, + unsigned palBank) noexcept { oxTracef( "nostalgia.core.gfx.setBgTile", "bgIdx: {}, column: {}, row: {}, tile: {}", @@ -597,6 +612,7 @@ void setBgTile( static_cast(y), static_cast(tile), bg.priority, + static_cast(palBank * 16), vbo, ebo); cbb.updated = true; diff --git a/src/nostalgia/modules/core/src/opengl/gfx.hpp b/src/nostalgia/modules/core/src/opengl/gfx.hpp index 03649ac1..2e248ec4 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.hpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.hpp @@ -18,7 +18,7 @@ constexpr uint64_t TileRows = 128; constexpr uint64_t TileColumns = 128; constexpr uint64_t TileCount = TileRows * TileColumns; constexpr uint64_t BgVertexVboRows = 4; -constexpr uint64_t BgVertexVboRowLength = 6; +constexpr uint64_t BgVertexVboRowLength = 7; constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; constexpr uint64_t BgVertexEboLength = 6; constexpr uint64_t SpriteVertexVboRows = 4; diff --git a/src/nostalgia/modules/scene/src/scene.cpp b/src/nostalgia/modules/scene/src/scene.cpp index 46ec2f83..877858ce 100644 --- a/src/nostalgia/modules/scene/src/scene.cpp +++ b/src/nostalgia/modules/scene/src/scene.cpp @@ -18,7 +18,7 @@ ox::Error Scene::setupDisplay(core::Context &ctx) const noexcept { } auto const&palette = m_sceneStatic.palettes[0]; oxReturnError(core::loadBgTileSheet(ctx, 0, m_sceneStatic.tilesheet)); - oxReturnError(core::loadBgPalette(ctx, palette)); + oxReturnError(core::loadBgPalette(ctx, 0, palette)); // disable all backgrounds core::setBgStatus(ctx, 0); for (auto layerNo = 0u; auto const&layer : m_sceneStatic.tileMapIdx) { diff --git a/src/nostalgia/player/app.cpp b/src/nostalgia/player/app.cpp index 3af8ebe0..acf26912 100644 --- a/src/nostalgia/player/app.cpp +++ b/src/nostalgia/player/app.cpp @@ -96,14 +96,16 @@ static ox::Error runTileSheetSetTest(turbine::Context &tctx) { { .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 1, .tiles = 1}} }, }, }; + constexpr auto bgPalBank = 1; oxReturnError(core::loadBgTileSheet(*cctx, 0, set)); oxReturnError(core::loadSpriteTileSheet(*cctx, set)); - oxReturnError(core::loadBgPalette(*cctx, PaletteAddr)); + oxReturnError(core::loadBgPalette(*cctx, bgPalBank, PaletteAddr)); + oxReturnError(core::loadBgPalette(*cctx, 0, ox::StringLiteral("/Palettes/Chester.npal"))); oxReturnError(core::loadSpritePalette(*cctx, PaletteAddr)); core::setBgStatus(*cctx, 0, true); - core::setBgTile(*cctx, 0, 10, 9, 1); - core::setBgTile(*cctx, 0, 11, 9, 2); - core::setBgTile(*cctx, 0, 13, 9, 4); + core::setBgTile(*cctx, 0, 10, 9, 1, bgPalBank); + core::setBgTile(*cctx, 0, 11, 9, 2, bgPalBank); + core::setBgTile(*cctx, 0, 13, 9, 4, bgPalBank); core::setSprite(*cctx, 16, { .enabled = true, .x = 12 * 8,