From fcf6f00797c8127feda2b61e4fb3f0d4340122d1 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Fri, 22 Dec 2023 22:38:46 -0600 Subject: [PATCH] [nostalgia/core] Get TileSheetSet working --- src/nostalgia/modules/CMakeLists.txt | 38 ++++---- src/nostalgia/modules/core/CMakeLists.txt | 3 + .../core/include/nostalgia/core/gfx.hpp | 11 ++- src/nostalgia/modules/core/src/gba/gfx.cpp | 86 ++++++++++++++----- src/nostalgia/modules/core/src/opengl/gfx.cpp | 10 ++- .../modules/core/test/CMakeLists.txt | 11 +++ src/nostalgia/modules/core/test/tests.cpp | 38 ++++++++ src/nostalgia/player/app.cpp | 46 +++++++++- 8 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 src/nostalgia/modules/core/test/CMakeLists.txt create mode 100644 src/nostalgia/modules/core/test/tests.cpp diff --git a/src/nostalgia/modules/CMakeLists.txt b/src/nostalgia/modules/CMakeLists.txt index fa2b812d..6b5b7f2a 100644 --- a/src/nostalgia/modules/CMakeLists.txt +++ b/src/nostalgia/modules/CMakeLists.txt @@ -23,24 +23,26 @@ install( include/nostalgia/modules ) -# Studio -if(TURBINE_BUILD_TYPE STREQUAL "Native") - add_library( - NostalgiaStudioModules STATIC - studiomodules.cpp - ) - target_link_libraries( - NostalgiaStudioModules PUBLIC - StudioAppLib - NostalgiaCore-Studio-ImGui - NostalgiaScene-Studio - ) - install( - FILES - studiomodules.hpp - DESTINATION - include/nostalgia/modules - ) +if(${OLYMPIC_BUILD_STUDIO}) + # Studio + if(TURBINE_BUILD_TYPE STREQUAL "Native") + add_library( + NostalgiaStudioModules STATIC + studiomodules.cpp + ) + target_link_libraries( + NostalgiaStudioModules PUBLIC + StudioAppLib + NostalgiaCore-Studio-ImGui + NostalgiaScene-Studio + ) + install( + FILES + studiomodules.hpp + DESTINATION + include/nostalgia/modules + ) + endif() endif() add_library(NostalgiaProfile INTERFACE) diff --git a/src/nostalgia/modules/core/CMakeLists.txt b/src/nostalgia/modules/core/CMakeLists.txt index ea484df9..2d89fadf 100644 --- a/src/nostalgia/modules/core/CMakeLists.txt +++ b/src/nostalgia/modules/core/CMakeLists.txt @@ -1,4 +1,7 @@ add_subdirectory(src) +if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA") + add_subdirectory(test) +endif() install( DIRECTORY diff --git a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp index 70ffb244..aae3ef0f 100644 --- a/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp +++ b/src/nostalgia/modules/core/include/nostalgia/core/gfx.hpp @@ -45,7 +45,11 @@ struct TileSheetSetEntrySection { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection"; static constexpr auto TypeVersion = 1; int32_t begin = 0; - int32_t size = 0; + int32_t tiles = 0; + [[nodiscard]] + constexpr auto end() const noexcept { + return begin + tiles - 1; + } }; oxModelBegin(TileSheetSetEntrySection) @@ -68,6 +72,7 @@ oxModelEnd() struct TileSheetSet { static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet"; static constexpr auto TypeVersion = 1; + static constexpr auto Preloadable = true; int bpp = 0; ox::Vector entries; }; @@ -101,6 +106,10 @@ ox::Error loadSpriteTileSheet( ox::FileAddress const&tilesheetAddr, bool loadDefaultPalette = false) noexcept; +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 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 0c5e891c..c614e349 100644 --- a/src/nostalgia/modules/core/src/gba/gfx.cpp +++ b/src/nostalgia/modules/core/src/gba/gfx.cpp @@ -41,6 +41,7 @@ struct GbaTileMapTarget { // and only exist to communicate with the loading process size_t tileWriteIdx = 0; unsigned targetBpp = 0; + TileSheetSetEntry const*setEntry = nullptr; }; constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) noexcept { @@ -63,7 +64,6 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) oxReturnError(io->template setTypeInfo()); oxReturnError(io->field("bpp", &t->bpp)); oxReturnError(io->field("defaultPalette", &t->defaultPalette)); - uint16_t intermediate = 0; if (t->targetBpp == 0) { t->targetBpp = t->bpp; } @@ -71,32 +71,75 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith auto *t) 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) { + uint16_t intermediate = 0; + if (t->setEntry) { + // The following code is atrocious, but it works. + // It might be possible to clean it up a little, but it probably + // cannot be seriously optimized without preloading TileSheets. + // Consider building an array of bitmaps (Array) + // so this can be done with a single bitmap look up. + if (t->targetBpp == t->bpp) { + TileSheetSetEntrySection const*section = &t->setEntry->sections[0]; + size_t sectionIdx = 1; + size_t writeIt = 0; + size_t const fourBpp = t->bpp == 4; + const auto handleTileMap = [t, &intermediate, §ion, §ionIdx, &writeIt, fourBpp] + (std::size_t i, uint8_t const *tile) { + if (!section) { + return ox::Error{}; + } + auto const tileIdx = static_cast((i * (2 * fourBpp)) / PixelsPerTile); + if (tileIdx < section->begin) { + return ox::Error{}; + } + if (tileIdx > section->end()) { + if (sectionIdx >= t->setEntry->sections.size()) { + section = nullptr; + return ox::Error{}; + } + section = &t->setEntry->sections[sectionIdx]; + ++sectionIdx; + if (tileIdx < section->begin || tileIdx > section->end()) { + return ox::Error{}; + } + } + if (writeIt & 1) { // writeIt is odd + intermediate |= static_cast(*tile) << 8; + t->tileMap[writeIt / 2] = intermediate; + ++t->tileWriteIdx; + } else { // writeIt is even + intermediate = *tile & 0x00ff; + } + ++writeIt; + 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); + } + } else { + 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; - ++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{}; + return OxError(0); }; out = io->template field("tileMap", handleTileMap); } @@ -150,6 +193,7 @@ static ox::Error loadTileSheetSet( .bpp = bpp, .defaultPalette = {}, .tileMap = tileMapTargetMem + tileWriteIdx, + .setEntry = &entry, }; oxReturnError(ox::readMC(ts, static_cast(tsStat.size), &target)); tileWriteIdx += target.tileWriteIdx; diff --git a/src/nostalgia/modules/core/src/opengl/gfx.cpp b/src/nostalgia/modules/core/src/opengl/gfx.cpp index 279029c6..57d9100e 100644 --- a/src/nostalgia/modules/core/src/opengl/gfx.cpp +++ b/src/nostalgia/modules/core/src/opengl/gfx.cpp @@ -513,10 +513,14 @@ static ox::Result buildSetTsd( 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); + for (auto const&s : entry.sections) { + auto const size = s.tiles * PixelsPerTile; + for (auto i = 0; i < size; ++i) { + auto const srcIdx = static_cast(i) + static_cast(s.begin * PixelsPerTile); + setTsd.pixels.push_back(tsd.pixels[srcIdx]); + } + setTsd.height += TileHeight * s.tiles; } - setTsd.height += tsd.height; } return setTsd; } diff --git a/src/nostalgia/modules/core/test/CMakeLists.txt b/src/nostalgia/modules/core/test/CMakeLists.txt new file mode 100644 index 00000000..c9b56f07 --- /dev/null +++ b/src/nostalgia/modules/core/test/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable( + NostalgiaCoreTest + tests.cpp +) + +target_link_libraries( + NostalgiaCoreTest + NostalgiaCore +) + +add_test("[NostalgiaCore] readWriteTileSheet" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/NostalgiaCoreTest readWriteTileSheet) diff --git a/src/nostalgia/modules/core/test/tests.cpp b/src/nostalgia/modules/core/test/tests.cpp new file mode 100644 index 00000000..e91f4a4b --- /dev/null +++ b/src/nostalgia/modules/core/test/tests.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#undef NDEBUG + +#include +#include +#include +#include + +using namespace nostalgia; + +static std::map tests = { + { + "readWriteTileSheet", + []() -> ox::Error { + core::TileSheet in; + oxRequire(buff, ox::writeMC(in)); + oxRequire(out, ox::readMC(buff)); + oxAssert(in.subsheet.name == out.subsheet.name, "subsheet.name serialization broken"); + return {}; + } + }, +}; + +int main(int argc, const char **args) { + int retval = -1; + if (argc > 0) { + auto const testName = ox::StringView(args[1]); + if (tests.find(testName) != tests.end()) { + retval = static_cast(tests[testName]()); + } else { + retval = 1; + } + } + return retval; +} diff --git a/src/nostalgia/player/app.cpp b/src/nostalgia/player/app.cpp index e1ca6b57..19a82174 100644 --- a/src/nostalgia/player/app.cpp +++ b/src/nostalgia/player/app.cpp @@ -76,9 +76,47 @@ static ox::Error runTest(turbine::Context &tctx) { core::puts(*cctx, 10, 9, "DOPENESS!!!"); turbine::setUpdateHandler(tctx, testUpdateHandler); turbine::setKeyEventHandler(tctx, testKeyEventHandler); - //core::setBgStatus(*cctx, 1, true); - //core::setBgStatus(*cctx, 2, true); - //core::setBgStatus(*cctx, 3, true); + return turbine::run(tctx); +} + + +[[maybe_unused]] +static ox::Error runTileSheetSetTest(turbine::Context &tctx) { + // this should make the screen display 'ABCDB' + constexpr ox::FileAddress PaletteAddr = ox::StringLiteral("/Palettes/Charset.npal"); + oxRequireM(cctx, core::init(tctx)); + turbine::setApplicationData(tctx, cctx.get()); + oxRequire(tsStat, turbine::rom(tctx)->stat(PaletteAddr)); + core::TileSheetSet const set{ + .bpp = 4, + .entries = { + { .tilesheet = ox::StringLiteral("/TileSheets/Chester.ng"), .sections{{.begin = 0, .tiles = 1}} }, + { .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 0, .tiles = 2}} }, + { .tilesheet = ox::StringLiteral("/TileSheets/CD.ng"), .sections{{.begin = 0, .tiles = 2}} }, + { .tilesheet = ox::StringLiteral("/TileSheets/AB.ng"), .sections{{.begin = 1, .tiles = 1}} }, + }, + }; + oxReturnError(core::loadBgTileSheet(*cctx, 0, set)); + oxReturnError(core::loadSpriteTileSheet(*cctx, set)); + oxReturnError(core::loadBgPalette(*cctx, PaletteAddr)); + 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::setSprite(*cctx, 16, { + .enabled = true, + .x = 12 * 8, + .y = 9 * 8, + .tileIdx = 3, + }); + core::setSprite(*cctx, 17, { + .enabled = true, + .x = 14 * 8, + .y = 9 * 8, + .tileIdx = 5, + }); + turbine::setKeyEventHandler(tctx, testKeyEventHandler); return turbine::run(tctx); } @@ -116,5 +154,5 @@ static ox::Error runScene(turbine::Context &tctx) { ox::Error run(ox::UniquePtr &&fs) noexcept { oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia")); - return runTest(*tctx); + return runTileSheetSetTest(*tctx); }