[nostalgia/core] Get TileSheetSet working

This commit is contained in:
Gary Talent 2023-12-22 22:38:46 -06:00
parent ef6e3af735
commit fcf6f00797
8 changed files with 196 additions and 47 deletions

View File

@ -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)

View File

@ -1,4 +1,7 @@
add_subdirectory(src)
if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA")
add_subdirectory(test)
endif()
install(
DIRECTORY

View File

@ -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<TileSheetSetEntry> 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;

View File

@ -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<GbaPaletteTarget> auto *t) noexcept {
@ -63,7 +64,6 @@ constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaTileMapTarget> auto *t)
oxReturnError(io->template setTypeInfo<CompactTileSheet>());
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<GbaTileMapTarget> 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<uint64_t, 4>)
// 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, &section, &sectionIdx, &writeIt, fourBpp]
(std::size_t i, uint8_t const *tile) {
if (!section) {
return ox::Error{};
}
auto const tileIdx = static_cast<int>((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<uint16_t>(*tile) << 8;
t->tileMap[writeIt / 2] = intermediate;
++t->tileWriteIdx;
} else { // writeIt is even
intermediate = *tile & 0x00ff;
}
++writeIt;
return ox::Error{};
};
out = io->template field<uint8_t, decltype(handleTileMap)>("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<uint16_t>(*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<uint8_t, decltype(handleTileMap)>("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<uint16_t>(*tile) << 8;
t->tileMap[i / 2] = intermediate;
++t->tileWriteIdx;
} else { // i is even
intermediate = *tile & 0x00ff;
}
return ox::Error{};
};
out = io->template field<uint8_t, decltype(handleTileMap)>("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<uint16_t>(*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<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
}
@ -150,6 +193,7 @@ static ox::Error loadTileSheetSet(
.bpp = bpp,
.defaultPalette = {},
.tileMap = tileMapTargetMem + tileWriteIdx,
.setEntry = &entry,
};
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
tileWriteIdx += target.tileWriteIdx;

View File

@ -513,10 +513,14 @@ static ox::Result<TileSheetData> buildSetTsd(
for (auto const&entry : set.entries) {
oxRequire(tilesheet, readObj<CompactTileSheet>(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<size_t>(i) + static_cast<size_t>(s.begin * PixelsPerTile);
setTsd.pixels.push_back(tsd.pixels[srcIdx]);
}
setTsd.height += TileHeight * s.tiles;
}
setTsd.height += tsd.height;
}
return setTsd;
}

View File

@ -0,0 +1,11 @@
add_executable(
NostalgiaCoreTest
tests.cpp
)
target_link_libraries(
NostalgiaCoreTest
NostalgiaCore
)
add_test("[NostalgiaCore] readWriteTileSheet" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/NostalgiaCoreTest readWriteTileSheet)

View File

@ -0,0 +1,38 @@
/*
* Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
*/
#undef NDEBUG
#include <map>
#include <ox/std/error.hpp>
#include <ox/mc/mc.hpp>
#include <nostalgia/core/core.hpp>
using namespace nostalgia;
static std::map<ox::StringView, ox::Error(*)()> tests = {
{
"readWriteTileSheet",
[]() -> ox::Error {
core::TileSheet in;
oxRequire(buff, ox::writeMC(in));
oxRequire(out, ox::readMC<core::TileSheet>(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<int>(tests[testName]());
} else {
retval = 1;
}
}
return retval;
}

View File

@ -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<ox::FileSystem> &&fs) noexcept {
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
return runTest(*tctx);
return runTileSheetSetTest(*tctx);
}