diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index 0e104b25..b6bb81c7 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -139,7 +139,15 @@ ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, CompactTileSheet const&ts, - ox::Optional const&paletteBank) noexcept; + size_t dstTileIdx, + size_t srcTileIdx, + size_t tileCnt) noexcept; + +ox::Error loadBgTileSheet( + Context &ctx, + unsigned cbb, + CompactTileSheet const&ts, + ox::Optional const&paletteBank = {}) noexcept; ox::Error loadBgTileSheet( Context &ctx, diff --git a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp index fc31a47a..9e4b09a3 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/tilesheet.hpp @@ -31,6 +31,12 @@ struct TileSheetV1 { ox::Vector pixels = {}; }; +[[nodiscard]] +constexpr bool valid(TileSheetV1 const&ts) noexcept { + return ts.bpp == 4 || ts.bpp == 8; +} + + struct TileSheetV2 { using SubSheetIdx = ox::Vector; @@ -43,8 +49,8 @@ struct TileSheetV2 { ox::Vector subsheets; ox::Vector pixels; constexpr SubSheet() noexcept = default; - constexpr SubSheet(ox::CRStringView pName, int pColumns, int pRows, int bpp) noexcept: - name(pName), + constexpr SubSheet(ox::StringParam pName, int pColumns, int pRows, int bpp) noexcept: + name(std::move(pName)), columns(pColumns), rows(pRows), pixels(static_cast(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { @@ -59,6 +65,12 @@ struct TileSheetV2 { }; +[[nodiscard]] +constexpr bool valid(TileSheetV2 const&ts) noexcept { + return ts.bpp == 4 || ts.bpp == 8; +} + + using SubSheetId = int32_t; struct TileSheetV3 { @@ -74,14 +86,14 @@ struct TileSheetV3 { ox::Vector subsheets; ox::Vector pixels; constexpr SubSheet() noexcept = default; - inline SubSheet( + SubSheet( SubSheetId pId, - ox::CRStringView pName, + ox::StringParam pName, int pColumns, int pRows, int bpp) noexcept: id(pId), - name(pName), + name(std::move(pName)), columns(pColumns), rows(pRows), pixels(static_cast(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { @@ -98,7 +110,13 @@ struct TileSheetV3 { }; -struct TileSheet { +[[nodiscard]] +constexpr bool valid(TileSheetV3 const&ts) noexcept { + return ts.bpp == 4 || ts.bpp == 8; +} + + +struct TileSheetV4 { using SubSheetIdx = ox::Vector; struct SubSheet { @@ -112,26 +130,26 @@ struct TileSheet { ox::Vector pixels; constexpr SubSheet() noexcept = default; - inline SubSheet( + SubSheet( SubSheetId pId, - ox::CRStringView pName, + ox::StringParam pName, int pColumns, int pRows, int bpp) noexcept: id(pId), - name(pName), + name(std::move(pName)), columns(pColumns), rows(pRows), pixels(static_cast(columns * rows * PixelsPerTile) / (bpp == 4 ? 2u : 1u)) { } - inline SubSheet( + SubSheet( SubSheetId pId, - ox::CRStringView pName, + ox::StringParam pName, int pColumns, int pRows, ox::Vector pPixels) noexcept: id(pId), - name(pName), + name(std::move(pName)), columns(pColumns), rows(pRows), pixels(std::move(pPixels)) { @@ -151,10 +169,18 @@ struct TileSheet { ox::FileAddress defaultPalette; SubSheet subsheet{0, "Root", 1, 1, bpp}; - constexpr TileSheet() noexcept = default; + constexpr TileSheetV4() noexcept = default; }; +[[nodiscard]] +constexpr bool valid(TileSheetV4 const&ts) noexcept { + return ts.bpp == 4 || ts.bpp == 8; +} + + +using TileSheet = TileSheetV4; + [[nodiscard]] std::size_t idx(TileSheet::SubSheet const&ss, ox::Point const&pt) noexcept; @@ -286,12 +312,11 @@ ox::Result getTileOffset(TileSheet const&ts, ox::CRStringView pNamePat ox::Result getNameFor(TileSheet::SubSheet const&ss, SubSheetId pId) noexcept; -ox::Result getNameFor(TileSheet const&ss, SubSheetId pId) noexcept; +ox::Result getNameFor(TileSheet const&ts, SubSheetId pId) noexcept; [[nodiscard]] ox::Vector pixels(TileSheet &ts) noexcept; -using TileSheetV4 = TileSheet; struct CompactTileSheetV1 { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.CompactTileSheet"; @@ -307,8 +332,29 @@ constexpr bool valid(CompactTileSheetV1 const&ts) noexcept { return ts.bpp == 4 || ts.bpp == 8; } + using CompactTileSheet = CompactTileSheetV1; +[[nodiscard]] +uint8_t getPixel4Bpp( + CompactTileSheet const&ts, + size_t idx) noexcept; + +[[nodiscard]] +uint8_t getPixel8Bpp( + CompactTileSheet const&ts, + size_t idx) noexcept; + +[[nodiscard]] +ox::Pair get2Pixels4Bpp( + CompactTileSheet const&ts, + size_t idx) noexcept; + +[[nodiscard]] +ox::Pair get2Pixels8Bpp( + CompactTileSheet const&ts, + size_t idx) noexcept; + oxModelBegin(TileSheetV1) oxModelField(bpp) oxModelField(rows) diff --git a/src/nostalgia/modules/core/src/gba/gfx.cpp b/src/nostalgia/modules/core/src/gba/gfx.cpp index 7255f558..8b87084c 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -3,7 +3,6 @@ */ #include -#include #include #include @@ -11,9 +10,7 @@ #include #include -#include -#include #include #include #include @@ -22,7 +19,7 @@ namespace nostalgia::core { -constexpr auto SpriteCount = 128; +static constexpr auto SpriteCount = 128; ox::Error initGfx(Context&, InitParams const&) noexcept { for (auto bgCtl = ®_BG0CTL; bgCtl <= ®_BG3CTL; bgCtl += 2) { @@ -81,7 +78,7 @@ ox::Error loadSpritePalette( static ox::Error loadTileSheetSet( Context &ctx, - uint16_t *tileMapTargetMem, + ox::Span tileMapTargetMem, TileSheetSet const&set) noexcept { size_t tileWriteIdx = 0; size_t const bppMod = set.bpp == 4; @@ -106,6 +103,34 @@ static ox::Error loadTileSheetSet( return {}; } +ox::Error loadBgTileSheet( + Context &ctx, + unsigned const cbb, + CompactTileSheet const&ts, + size_t const dstTileIdx, + size_t const srcTileIdx, + size_t const tileCnt) noexcept { + size_t const bppMod = ts.bpp == 4; + size_t const bytesPerTile = PixelsPerTile >> bppMod; + auto const pixCnt = tileCnt * bytesPerTile; + auto const srcPxIdx = srcTileIdx * bytesPerTile; + auto const dstPxIdx = (dstTileIdx * bytesPerTile) / 2; + for (size_t i = 0; i < pixCnt; ++i) { + auto const srcIdx = srcPxIdx + i * 2; + auto const p1 = static_cast(ts.pixels[srcIdx]); + auto const p2 = static_cast(ts.pixels[srcIdx + 1]); + MEM_BG_TILES[cbb][dstPxIdx + i] = static_cast(p1 | (p2 << 8)); + } + // update bpp of all bgs with the updated cbb + auto const bpp = ctx.cbbData[cbb].bpp; + teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) { + if (teagba::bgCbb(bgCtl) == cbb) { + teagba::bgSetBpp(bgCtl, bpp); + } + }); + return {}; +} + ox::Error loadBgTileSheet( Context &ctx, unsigned cbb, @@ -142,10 +167,10 @@ ox::Error loadBgTileSheet( ox::Error loadBgTileSheet( Context &ctx, - unsigned cbb, + unsigned const cbb, TileSheetSet const&set) noexcept { auto const bpp = static_cast(set.bpp); - oxReturnError(loadTileSheetSet(ctx, MEM_BG_TILES[cbb].data(), set)); + oxReturnError(loadTileSheetSet(ctx, MEM_BG_TILES[cbb], set)); // update bpp of all bgs with the updated cbb ctx.cbbData[cbb].bpp = bpp; teagba::iterateBgCtl([bpp, cbb](volatile BgCtl &bgCtl) { @@ -193,7 +218,7 @@ ox::Error loadSpriteTileSheet( Context &ctx, TileSheetSet const&set) noexcept { auto const bpp = static_cast(set.bpp); - oxReturnError(loadTileSheetSet(ctx, MEM_SPRITE_TILES, set)); + oxReturnError(loadTileSheetSet(ctx, {MEM_SPRITE_TILES, 32 * ox::units::KB}, set)); setSpritesBpp(bpp); return {}; } diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index e5dee25c..a86dde82 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -22,8 +22,8 @@ namespace nostalgia::core { namespace renderer { -constexpr auto Scale = 1; -constexpr auto PriorityScale = 0.01f; +static constexpr auto Scale = 1; +static constexpr auto PriorityScale = 0.01f; Drawer::Drawer(Context &ctx) noexcept: m_ctx(ctx) {} @@ -31,7 +31,7 @@ void Drawer::draw(turbine::Context &tctx) noexcept { core::gl::draw(m_ctx, turbine::getScreenSize(tctx)); } -constexpr ox::CStringView bgvshadTmpl = R"glsl( +static constexpr ox::CStringView bgvshadTmpl = R"glsl( {} in vec2 vTexCoord; in vec3 vPosition; @@ -55,7 +55,7 @@ constexpr ox::CStringView bgvshadTmpl = R"glsl( fPalOffset = vPalOffset; })glsl"; -constexpr ox::CStringView bgfshadTmpl = R"glsl( +static constexpr ox::CStringView bgfshadTmpl = R"glsl( {} out vec4 outColor; in float fPalOffset; @@ -71,7 +71,7 @@ constexpr ox::CStringView bgfshadTmpl = R"glsl( } })glsl"; -constexpr ox::CStringView spritevshadTmpl = R"glsl( +static constexpr ox::CStringView spritevshadTmpl = R"glsl( {} in float vEnabled; in vec3 vPosition; @@ -90,7 +90,7 @@ constexpr ox::CStringView spritevshadTmpl = R"glsl( fTexCoord = vTexCoord * vec2(1, vTileHeight); })glsl"; -constexpr ox::CStringView spritefshadTmpl = R"glsl( +static constexpr ox::CStringView spritefshadTmpl = R"glsl( {} out vec4 outColor; in vec2 fTexCoord; @@ -279,7 +279,7 @@ static void initBackgroundBufferset( static glutils::GLTexture createTexture( GLsizei w, GLsizei h, - const void *pixels) noexcept { + void const*pixels) noexcept { GLuint texId = 0; glGenTextures(1, &texId); glutils::GLTexture tex(texId); @@ -492,22 +492,22 @@ struct TileSheetData { }; static ox::Result normalizeTileSheet( - CompactTileSheet const&tilesheet) noexcept { - const uint_t bytesPerTile = tilesheet.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2; - const auto tiles = tilesheet.pixels.size() / bytesPerTile; + CompactTileSheet const&ts) noexcept { + const uint_t bytesPerTile = ts.bpp == 8 ? PixelsPerTile : PixelsPerTile / 2; + const auto tiles = ts.pixels.size() / bytesPerTile; constexpr int width = 8; const int height = 8 * static_cast(tiles); ox::Vector pixels; if (bytesPerTile == 64) { // 8 BPP - pixels.resize(tilesheet.pixels.size()); - for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) { - pixels[i] = tilesheet.pixels[i]; + pixels.resize(ts.pixels.size()); + for (std::size_t i = 0; i < ts.pixels.size(); ++i) { + pixels[i] = ts.pixels[i]; } } else { // 4 BPP - pixels.resize(tilesheet.pixels.size() * 2); - for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) { - pixels[i * 2 + 0] = tilesheet.pixels[i] & 0xF; - pixels[i * 2 + 1] = tilesheet.pixels[i] >> 4; + pixels.resize(ts.pixels.size() * 2); + for (std::size_t i = 0; i < ts.pixels.size(); ++i) { + pixels[i * 2 + 0] = ts.pixels[i] & 0xF; + pixels[i * 2 + 1] = ts.pixels[i] >> 4; } } return TileSheetData{std::move(pixels), width, height}; @@ -572,14 +572,58 @@ static ox::Result buildSetTsd( return setTsd; } +static void copyPixels( + CompactTileSheet const&ts, + uint32_t *dst, + size_t const srcPxIdx, + size_t pxlCnt) noexcept { + if (ts.bpp == 4) { + for (size_t i = 0; i < pxlCnt; i += 2) { + auto const [a, b] = get2Pixels4Bpp(ts, i + srcPxIdx); + *(dst++) = a; + *(dst++) = b; + } + } else if (ts.bpp == 8) { + for (size_t i = 0; i < pxlCnt; i += 2) { + auto const [a, b] = get2Pixels8Bpp(ts, i + srcPxIdx); + *(dst++) = a; + *(dst++) = b; + } + } +} + +ox::Error loadBgTileSheet( + Context &ctx, + unsigned const cbb, + CompactTileSheet const&ts, + size_t const dstTileIdx, + size_t const srcTileIdx, + size_t const tileCnt) noexcept { + auto &cbbPxls = ctx.cbbs[cbb].pixels; + auto const bytesPerTile = static_cast(PixelsPerTile / (1 + (ts.bpp == 4))); + auto const pxlCnt = tileCnt * PixelsPerTile; + auto const srcPxIdx = srcTileIdx * PixelsPerTile; + auto const dstPxIdx = dstTileIdx * PixelsPerTile; + if (dstPxIdx + pxlCnt >= cbbPxls.size()) { + return OxError(1, "video mem dst overflow"); + } + auto const dst = &cbbPxls[dstPxIdx]; + copyPixels(ts, dst, srcPxIdx, pxlCnt); + auto const cbbTiles = cbbPxls.size() / bytesPerTile; + int constexpr cbbWidth = 8; + int const cbbHeight = 8 * static_cast(cbbTiles); + ctx.cbbs[cbb].tex = renderer::createTexture(cbbWidth, cbbHeight, cbbPxls.data()); + return {}; +} + ox::Error loadBgTileSheet( Context &ctx, uint_t cbb, CompactTileSheet const&ts, ox::Optional const&paletteBank) noexcept { - oxRequire(tsd, normalizeTileSheet(ts)); - 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()); + auto const bytesPerTile = static_cast(PixelsPerTile / (1 + (ts.bpp == 4))); + auto const tiles = ts.pixels.size() / bytesPerTile; + oxReturnError(loadBgTileSheet(ctx, cbb, ts, 0, 0, tiles)); if (paletteBank.has_value() && ts.defaultPalette) { oxReturnError(loadBgPalette(ctx, *paletteBank, ts.defaultPalette)); } diff --git a/src/nostalgia/modules/core/src/opengl/gfx.hpp b/src/nostalgia/modules/core/src/opengl/gfx.hpp index 0fefa6f6..81200d7a 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.hpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.hpp @@ -28,6 +28,7 @@ constexpr uint64_t SpriteVertexEboLength = 6; struct CBB: public glutils::BufferSet { bool updated = false; + ox::Array pixels; constexpr CBB() noexcept { vertices.resize(TileCount * BgVertexVboLength); elements.resize(TileCount * BgVertexEboLength); diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp index 01293371..5dece149 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/commands/cutpastecommand.hpp @@ -10,6 +10,8 @@ namespace nostalgia::core { +oxModelFwdDecl(class TileSheetClipboard); + class TileSheetClipboard: public turbine::ClipboardObject { public: static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.studio.TileSheetClipboard"; diff --git a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp index d8df5f5a..82745217 100644 --- a/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp +++ b/src/nostalgia/modules/core/src/studio/tilesheeteditor/tilesheeteditor-imgui.cpp @@ -312,9 +312,8 @@ void TileSheetEditorImGui::drawSubsheetSelector( for (auto i = 0ul; auto &child : subsheet.subsheets) { path.push_back(i); ImGui::PushID(static_cast(i)); - ImGui::Indent(-indentReduce); + ig::IndentStackItem const indentStackItem{-indentReduce}; drawSubsheetSelector(child, path); - ImGui::Indent(indentReduce); ImGui::PopID(); path.pop_back(); ++i; @@ -440,7 +439,7 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept { } auto const pages = m_model.pal().pages.size(); if (pages > 1) { - ImGui::Indent(20); + ig::IndentStackItem const indentStackItem{20}; using Str = ox::IString<55>; auto numStr = ox::sfmt( "{} - {}", m_model.palettePage() + 1, m_model.pal().pages[m_model.palettePage()].name); @@ -458,7 +457,6 @@ void TileSheetEditorImGui::drawPaletteSelector() noexcept { } ImGui::EndCombo(); } - ImGui::Indent(-20); } // header if (ImGui::BeginTable( diff --git a/src/nostalgia/modules/core/src/tilesheet.cpp b/src/nostalgia/modules/core/src/tilesheet.cpp index fb336f79..ad58bcd2 100644 --- a/src/nostalgia/modules/core/src/tilesheet.cpp +++ b/src/nostalgia/modules/core/src/tilesheet.cpp @@ -280,7 +280,7 @@ uint8_t getPixel4Bpp( TileSheet::SubSheetIdx const&subsheetIdx) noexcept { oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp"); auto &s = getSubSheet(ts, subsheetIdx); - const auto idx = ptToIdx(pt, s.columns); + auto const idx = ptToIdx(pt, s.columns); return getPixel4Bpp(s, idx); } @@ -290,10 +290,49 @@ uint8_t getPixel8Bpp( TileSheet::SubSheetIdx const&subsheetIdx) noexcept { oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp"); auto &s = getSubSheet(ts, subsheetIdx); - const auto idx = ptToIdx(pt, s.columns); + auto const idx = ptToIdx(pt, s.columns); return getPixel8Bpp(s, idx); } +uint8_t getPixel4Bpp( + CompactTileSheet const&ts, + size_t const idx) noexcept { + oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp"); + if (idx & 1) { + return ts.pixels[idx / 2] >> 4; + } else { + return ts.pixels[idx / 2] & 0b0000'1111; + } +} + +uint8_t getPixel8Bpp( + CompactTileSheet const&ts, + size_t const idx) noexcept { + oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp"); + return ts.pixels[idx]; +} + +ox::Pair get2Pixels4Bpp( + CompactTileSheet const&ts, + size_t const idx) noexcept { + oxAssert(ts.bpp == 4, "TileSheet::getPixel4Bpp: wrong bpp"); + auto const out = ts.pixels[idx / 2]; + return { + static_cast(out & 0x0f), + static_cast(out >> 4), + }; +} + +ox::Pair get2Pixels8Bpp( + CompactTileSheet const&ts, + size_t const idx) noexcept { + oxAssert(ts.bpp == 8, "TileSheet::getPixel8Bpp: wrong bpp"); + return { + static_cast(ts.pixels[idx]), + static_cast(ts.pixels[idx + 1]), + }; +} + static ox::Result getIdFor( TileSheet::SubSheet const&ss, ox::SpanView const&pNamePath,