[nostalgia/core] Get TileSheetSet working
This commit is contained in:
parent
ef6e3af735
commit
fcf6f00797
@ -23,24 +23,26 @@ install(
|
|||||||
include/nostalgia/modules
|
include/nostalgia/modules
|
||||||
)
|
)
|
||||||
|
|
||||||
# Studio
|
if(${OLYMPIC_BUILD_STUDIO})
|
||||||
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
# Studio
|
||||||
add_library(
|
if(TURBINE_BUILD_TYPE STREQUAL "Native")
|
||||||
NostalgiaStudioModules STATIC
|
add_library(
|
||||||
studiomodules.cpp
|
NostalgiaStudioModules STATIC
|
||||||
)
|
studiomodules.cpp
|
||||||
target_link_libraries(
|
)
|
||||||
NostalgiaStudioModules PUBLIC
|
target_link_libraries(
|
||||||
StudioAppLib
|
NostalgiaStudioModules PUBLIC
|
||||||
NostalgiaCore-Studio-ImGui
|
StudioAppLib
|
||||||
NostalgiaScene-Studio
|
NostalgiaCore-Studio-ImGui
|
||||||
)
|
NostalgiaScene-Studio
|
||||||
install(
|
)
|
||||||
FILES
|
install(
|
||||||
studiomodules.hpp
|
FILES
|
||||||
DESTINATION
|
studiomodules.hpp
|
||||||
include/nostalgia/modules
|
DESTINATION
|
||||||
)
|
include/nostalgia/modules
|
||||||
|
)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(NostalgiaProfile INTERFACE)
|
add_library(NostalgiaProfile INTERFACE)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
if(NOT TURBINE_BUILD_TYPE STREQUAL "GBA")
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
|
||||||
install(
|
install(
|
||||||
DIRECTORY
|
DIRECTORY
|
||||||
|
@ -45,7 +45,11 @@ struct TileSheetSetEntrySection {
|
|||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection";
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSetEntrySection";
|
||||||
static constexpr auto TypeVersion = 1;
|
static constexpr auto TypeVersion = 1;
|
||||||
int32_t begin = 0;
|
int32_t begin = 0;
|
||||||
int32_t size = 0;
|
int32_t tiles = 0;
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr auto end() const noexcept {
|
||||||
|
return begin + tiles - 1;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
oxModelBegin(TileSheetSetEntrySection)
|
oxModelBegin(TileSheetSetEntrySection)
|
||||||
@ -68,6 +72,7 @@ oxModelEnd()
|
|||||||
struct TileSheetSet {
|
struct TileSheetSet {
|
||||||
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet";
|
static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.TileSheetSet";
|
||||||
static constexpr auto TypeVersion = 1;
|
static constexpr auto TypeVersion = 1;
|
||||||
|
static constexpr auto Preloadable = true;
|
||||||
int bpp = 0;
|
int bpp = 0;
|
||||||
ox::Vector<TileSheetSetEntry> entries;
|
ox::Vector<TileSheetSetEntry> entries;
|
||||||
};
|
};
|
||||||
@ -101,6 +106,10 @@ ox::Error loadSpriteTileSheet(
|
|||||||
ox::FileAddress const&tilesheetAddr,
|
ox::FileAddress const&tilesheetAddr,
|
||||||
bool loadDefaultPalette = false) noexcept;
|
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 setBgTile(Context &ctx, uint_t bgIdx, int column, int row, uint8_t tile) noexcept;
|
||||||
|
|
||||||
void clearBg(Context &ctx, uint_t bgIdx) noexcept;
|
void clearBg(Context &ctx, uint_t bgIdx) noexcept;
|
||||||
|
@ -41,6 +41,7 @@ struct GbaTileMapTarget {
|
|||||||
// and only exist to communicate with the loading process
|
// and only exist to communicate with the loading process
|
||||||
size_t tileWriteIdx = 0;
|
size_t tileWriteIdx = 0;
|
||||||
unsigned targetBpp = 0;
|
unsigned targetBpp = 0;
|
||||||
|
TileSheetSetEntry const*setEntry = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr ox::Error model(auto *io, ox::CommonPtrWith<GbaPaletteTarget> auto *t) noexcept {
|
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->template setTypeInfo<CompactTileSheet>());
|
||||||
oxReturnError(io->field("bpp", &t->bpp));
|
oxReturnError(io->field("bpp", &t->bpp));
|
||||||
oxReturnError(io->field("defaultPalette", &t->defaultPalette));
|
oxReturnError(io->field("defaultPalette", &t->defaultPalette));
|
||||||
uint16_t intermediate = 0;
|
|
||||||
if (t->targetBpp == 0) {
|
if (t->targetBpp == 0) {
|
||||||
t->targetBpp = t->bpp;
|
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");
|
return OxError(1, "Cannot load an 8 BPP tilesheet into a 4 BPP CBB");
|
||||||
}
|
}
|
||||||
ox::Error out{};
|
ox::Error out{};
|
||||||
if (t->targetBpp == t->bpp) {
|
uint16_t intermediate = 0;
|
||||||
const auto handleTileMap = [t, &intermediate](std::size_t i, uint8_t const*tile) {
|
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, §ion, §ionIdx, &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
|
if (i & 1) { // i is odd
|
||||||
intermediate |= static_cast<uint16_t>(*tile) << 8;
|
intermediate |= static_cast<uint16_t>(*tile) << 8;
|
||||||
t->tileMap[i / 2] = intermediate;
|
t->tileMap[i / 2] = intermediate;
|
||||||
++t->tileWriteIdx;
|
|
||||||
} else { // i is even
|
} else { // i is even
|
||||||
intermediate = *tile & 0x00ff;
|
intermediate = *tile & 0x00ff;
|
||||||
}
|
}
|
||||||
return ox::Error{};
|
return OxError(0);
|
||||||
};
|
|
||||||
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);
|
out = io->template field<uint8_t, decltype(handleTileMap)>("tileMap", handleTileMap);
|
||||||
}
|
}
|
||||||
@ -150,6 +193,7 @@ static ox::Error loadTileSheetSet(
|
|||||||
.bpp = bpp,
|
.bpp = bpp,
|
||||||
.defaultPalette = {},
|
.defaultPalette = {},
|
||||||
.tileMap = tileMapTargetMem + tileWriteIdx,
|
.tileMap = tileMapTargetMem + tileWriteIdx,
|
||||||
|
.setEntry = &entry,
|
||||||
};
|
};
|
||||||
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
|
oxReturnError(ox::readMC(ts, static_cast<std::size_t>(tsStat.size), &target));
|
||||||
tileWriteIdx += target.tileWriteIdx;
|
tileWriteIdx += target.tileWriteIdx;
|
||||||
|
@ -513,10 +513,14 @@ static ox::Result<TileSheetData> buildSetTsd(
|
|||||||
for (auto const&entry : set.entries) {
|
for (auto const&entry : set.entries) {
|
||||||
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, entry.tilesheet));
|
oxRequire(tilesheet, readObj<CompactTileSheet>(kctx, entry.tilesheet));
|
||||||
oxRequire(tsd, normalizeTileSheet(*tilesheet));
|
oxRequire(tsd, normalizeTileSheet(*tilesheet));
|
||||||
for (auto const p : tsd.pixels) {
|
for (auto const&s : entry.sections) {
|
||||||
setTsd.pixels.push_back(p);
|
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;
|
return setTsd;
|
||||||
}
|
}
|
||||||
|
11
src/nostalgia/modules/core/test/CMakeLists.txt
Normal file
11
src/nostalgia/modules/core/test/CMakeLists.txt
Normal 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)
|
38
src/nostalgia/modules/core/test/tests.cpp
Normal file
38
src/nostalgia/modules/core/test/tests.cpp
Normal 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;
|
||||||
|
}
|
@ -76,9 +76,47 @@ static ox::Error runTest(turbine::Context &tctx) {
|
|||||||
core::puts(*cctx, 10, 9, "DOPENESS!!!");
|
core::puts(*cctx, 10, 9, "DOPENESS!!!");
|
||||||
turbine::setUpdateHandler(tctx, testUpdateHandler);
|
turbine::setUpdateHandler(tctx, testUpdateHandler);
|
||||||
turbine::setKeyEventHandler(tctx, testKeyEventHandler);
|
turbine::setKeyEventHandler(tctx, testKeyEventHandler);
|
||||||
//core::setBgStatus(*cctx, 1, true);
|
return turbine::run(tctx);
|
||||||
//core::setBgStatus(*cctx, 2, true);
|
}
|
||||||
//core::setBgStatus(*cctx, 3, true);
|
|
||||||
|
|
||||||
|
[[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);
|
return turbine::run(tctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,5 +154,5 @@ static ox::Error runScene(turbine::Context &tctx) {
|
|||||||
|
|
||||||
ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
|
ox::Error run(ox::UniquePtr<ox::FileSystem> &&fs) noexcept {
|
||||||
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
|
oxRequireM(tctx, turbine::init(std::move(fs), "Nostalgia"));
|
||||||
return runTest(*tctx);
|
return runTileSheetSetTest(*tctx);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user