From bb643bce42f2d37cc176b10ae0158e612f681377 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Tue, 3 Jan 2023 03:37:51 -0600 Subject: [PATCH] [nostalgia] Add implementation of single tile sprites on OpenGL --- src/nostalgia/core/context.hpp | 14 ++ src/nostalgia/core/gba/gfx.cpp | 6 +- src/nostalgia/core/gfx.hpp | 27 ++- src/nostalgia/core/glfw/core.cpp | 16 +- src/nostalgia/core/glfw/core.hpp | 2 +- src/nostalgia/core/headless/gfx.cpp | 5 +- src/nostalgia/core/userland/gfx.cpp | 63 +++-- src/nostalgia/core/userland/gfx.hpp | 6 +- src/nostalgia/core/userland/gfx_opengl.cpp | 256 +++++++++++++++++---- src/nostalgia/core/userland/media.cpp | 2 +- src/nostalgia/player/app.cpp | 14 +- 11 files changed, 312 insertions(+), 99 deletions(-) diff --git a/src/nostalgia/core/context.hpp b/src/nostalgia/core/context.hpp index b8acb028..7fc46dde 100644 --- a/src/nostalgia/core/context.hpp +++ b/src/nostalgia/core/context.hpp @@ -61,6 +61,11 @@ class Context { unsigned cbb, const ox::FileAddress &tilesheetPath, const ox::FileAddress &palettePath) noexcept; + friend ox::Error loadSpriteTileSheet(Context *ctx, + const ox::FileAddress &tilesheetAddr, + const ox::FileAddress &paletteAddr) noexcept; + friend ox::Result loadTileSheet(Context *ctx, + const struct CompactTileSheet &tilesheetAddr) noexcept; friend ox::Error run(Context *ctx) noexcept; friend void shutdown(Context *ctx) noexcept; friend ox::Result> init(ox::UniquePtr fs, ox::CRStringView appName) noexcept; @@ -77,6 +82,15 @@ class Context { friend constexpr void setKeyEventHandler(Context *ctx, KeyEventHandler h) noexcept; friend constexpr KeyEventHandler keyEventHandler(Context *ctx) noexcept; friend void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept; + friend void setSprite(Context *ctx, + unsigned idx, + int x, + int y, + unsigned tileIdx, + unsigned spriteShape, + unsigned spriteSize, + unsigned flipX) noexcept; + friend void hideSprite(Context *ctx, unsigned idx) noexcept; public: ox::UniquePtr rom; diff --git a/src/nostalgia/core/gba/gfx.cpp b/src/nostalgia/core/gba/gfx.cpp index 54ad80e7..f0fff621 100644 --- a/src/nostalgia/core/gba/gfx.cpp +++ b/src/nostalgia/core/gba/gfx.cpp @@ -253,15 +253,15 @@ void hideSprite(Context*, unsigned idx) noexcept { void setSprite(Context*, unsigned idx, - unsigned x, - unsigned y, + int x, + int y, unsigned tileIdx, unsigned spriteShape, unsigned spriteSize, unsigned flipX) noexcept { oxAssert(g_spriteUpdates < config::GbaSpriteBufferLen, "Sprite update buffer overflow"); GbaSpriteAttrUpdate oa; - oa.attr0 = static_cast(y & ox::onMask(0b111)) + oa.attr0 = static_cast(y & ox::onMask(0b111'1111)) | (static_cast(1) << 10) // enable alpha | (static_cast(spriteShape) << 14); oa.attr1 = (static_cast(x) & ox::onMask(8)) diff --git a/src/nostalgia/core/gfx.hpp b/src/nostalgia/core/gfx.hpp index 59a22b8d..861ca24d 100644 --- a/src/nostalgia/core/gfx.hpp +++ b/src/nostalgia/core/gfx.hpp @@ -120,7 +120,7 @@ struct TileSheet { } [[nodiscard]] - auto idx(const geo::Point &pt) const noexcept { + constexpr auto idx(const geo::Point &pt) const noexcept { return ptToIdx(pt, columns); } @@ -275,6 +275,7 @@ struct TileSheet { * @param pBpp bits per pixel, need for knowing how to count the pixels * @return a count of the pixels in this sheet */ + [[nodiscard]] constexpr auto pixelCnt(int8_t pBpp) const noexcept { return pBpp == 4 ? pixels.size() * 2 : pixels.size(); } @@ -287,13 +288,9 @@ struct TileSheet { SubSheet subsheet{"Root", 1, 1, bpp}; constexpr TileSheet() noexcept = default; - inline TileSheet(const TileSheet &other) noexcept: - bpp(other.bpp), - defaultPalette(other.defaultPalette), - subsheet(other.subsheet) { - } + TileSheet(const TileSheet &other) noexcept = default; inline TileSheet(TileSheet &&other) noexcept: - bpp(std::move(other.bpp)), + bpp(other.bpp), defaultPalette(std::move(other.defaultPalette)), subsheet(std::move(other.subsheet)) { } @@ -468,14 +465,24 @@ oxModelEnd() struct Sprite { unsigned idx = 0; - unsigned x = 0; - unsigned y = 0; + int x = 0; + int y = 0; unsigned tileIdx = 0; unsigned spriteShape = 0; unsigned spriteSize = 0; unsigned flipX = 0; }; +oxModelBegin(Sprite) + oxModelField(idx) + oxModelField(x) + oxModelField(y) + oxModelField(tileIdx) + oxModelField(spriteShape) + oxModelField(spriteSize) + oxModelField(flipX) +oxModelEnd() + ox::Error initGfx(Context *ctx) noexcept; void addCustomDrawer(Context *ctx, Drawer *cd) noexcept; @@ -526,7 +533,7 @@ void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept; void hideSprite(Context *ctx, unsigned) noexcept; -void setSprite(Context *ctx, unsigned idx, unsigned x, unsigned y, unsigned tileIdx, +void setSprite(Context *ctx, unsigned idx, int x, int y, unsigned tileIdx, unsigned spriteShape = 0, unsigned spriteSize = 0, unsigned flipX = 0) noexcept; void setSprite(Context *ctx, const Sprite &s) noexcept; diff --git a/src/nostalgia/core/glfw/core.cpp b/src/nostalgia/core/glfw/core.cpp index 69c7ddd2..3c528790 100644 --- a/src/nostalgia/core/glfw/core.cpp +++ b/src/nostalgia/core/glfw/core.cpp @@ -32,15 +32,13 @@ ox::Error run(Context *ctx) noexcept { while (!glfwWindowShouldClose(id->window)) { glfwPollEvents(); const auto ticks = ticksMs(ctx); - if (id->eventHandler) { - if (id->wakeupTime <= ticks) { - sleepTime = id->eventHandler(ctx); - if (sleepTime >= 0) { - id->wakeupTime = ticks + static_cast(sleepTime); - } else { - id->wakeupTime = ~uint64_t(0); - } - } + if (id->wakeupTime <= ticks) { + sleepTime = id->eventHandler(ctx); + if (sleepTime >= 0) { + id->wakeupTime = ticks + static_cast(sleepTime); + } else { + id->wakeupTime = ~uint64_t(0); + } } else { sleepTime = 10; } diff --git a/src/nostalgia/core/glfw/core.hpp b/src/nostalgia/core/glfw/core.hpp index 6a238aaa..b7edaa92 100644 --- a/src/nostalgia/core/glfw/core.hpp +++ b/src/nostalgia/core/glfw/core.hpp @@ -11,7 +11,7 @@ namespace nostalgia::core { struct GlfwImplData { struct GLFWwindow *window = nullptr; int64_t startTime = 0; - UpdateHandler eventHandler = nullptr; + UpdateHandler eventHandler = [](Context*) -> int {return 0;}; KeyEventHandler keyEventHandler = nullptr; uint64_t wakeupTime = 0; uint64_t keysDown = 0; diff --git a/src/nostalgia/core/headless/gfx.cpp b/src/nostalgia/core/headless/gfx.cpp index 3adb76e6..f55ef7a0 100644 --- a/src/nostalgia/core/headless/gfx.cpp +++ b/src/nostalgia/core/headless/gfx.cpp @@ -60,7 +60,6 @@ ox::Error loadBgTileSheet(Context*, } ox::Error loadSpriteTileSheet(Context*, - int, const ox::FileAddress&, const ox::FileAddress&) noexcept { return OxError(0); @@ -91,8 +90,8 @@ void hideSprite(Context*, unsigned) noexcept { void setSprite(Context*, unsigned, - unsigned, - unsigned, + int, + int, unsigned, unsigned, unsigned, diff --git a/src/nostalgia/core/userland/gfx.cpp b/src/nostalgia/core/userland/gfx.cpp index cccecafd..ce4b1c63 100644 --- a/src/nostalgia/core/userland/gfx.cpp +++ b/src/nostalgia/core/userland/gfx.cpp @@ -17,39 +17,58 @@ ox::Error initConsole(Context *ctx) noexcept { return loadBgTileSheet(ctx, 0, TilesheetAddr, PaletteAddr); } -ox::Error loadBgTileSheet(Context *ctx, - unsigned cbb, - const ox::FileAddress &tilesheetPath, - const ox::FileAddress &palettePath) noexcept { - oxRequire(tilesheet, readObj(ctx, tilesheetPath)); - oxRequire(palette, readObj(ctx, palettePath ? palettePath : tilesheet->defaultPalette)); - const unsigned bytesPerTile = tilesheet->bpp == 8 ? 64 : 32; - const auto tiles = tilesheet->pixels.size() / bytesPerTile; +struct TileSheetData { + ox::Vector pixels; + int width = 0; + int height = 0; +}; + +ox::Result loadTileSheet(Context *ctx, const CompactTileSheet &tilesheet) noexcept { + const unsigned bytesPerTile = tilesheet.bpp == 8 ? 64 : 32; + const auto tiles = tilesheet.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(tilesheet.pixels.size()); + for (std::size_t i = 0; i < tilesheet.pixels.size(); ++i) { + pixels[i] = tilesheet.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(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; } } const auto rd = ctx->rendererData(); - renderer::loadBgTexture(rd, cbb, pixels.data(), width, height); - renderer::loadBgPalette(rd, *palette); - return OxError(0); + renderer::loadSpriteTexture(rd, pixels.data(), width, height); + return TileSheetData{std::move(pixels), width, height}; } -ox::Error loadSpriteTileSheet(Context*, - const ox::FileAddress&, - const ox::FileAddress&) noexcept { - return OxError(0); +ox::Error loadBgTileSheet(Context *ctx, + unsigned cbb, + const ox::FileAddress &tilesheetAddr, + const ox::FileAddress &paletteAddr) noexcept { + oxRequire(tilesheet, readObj(ctx, tilesheetAddr)); + oxRequire(palette, readObj(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); + oxRequire(tsd, loadTileSheet(ctx, *tilesheet)); + const auto rd = ctx->rendererData(); + renderer::loadBgTexture(rd, cbb, tsd.pixels.data(), tsd.width, tsd.height); + renderer::loadBgPalette(rd, *palette); + return {}; +} + +ox::Error loadSpriteTileSheet(Context *ctx, + const ox::FileAddress &tilesheetAddr, + const ox::FileAddress &paletteAddr) noexcept { + oxRequire(tilesheet, readObj(ctx, tilesheetAddr)); + oxRequire(palette, readObj(ctx, paletteAddr ? paletteAddr : tilesheet->defaultPalette)); + oxRequire(tsd, loadTileSheet(ctx, *tilesheet)); + const auto rd = ctx->rendererData(); + renderer::loadSpriteTexture(rd, tsd.pixels.data(), tsd.width, tsd.height); + renderer::loadSpritePalette(rd, *palette); + return {}; } void puts(Context *ctx, int column, int row, ox::CRStringView str) noexcept { diff --git a/src/nostalgia/core/userland/gfx.hpp b/src/nostalgia/core/userland/gfx.hpp index b8c2ef90..2c6b0b0c 100644 --- a/src/nostalgia/core/userland/gfx.hpp +++ b/src/nostalgia/core/userland/gfx.hpp @@ -20,6 +20,10 @@ void shutdown(Context *ctx, void *rendererData) noexcept; void loadBgPalette(void *rendererData, const Palette &pal) noexcept; -void loadBgTexture(void *rendererData, unsigned cbb, void *pixels, int w, int h) noexcept; +void loadBgTexture(void *rendererData, unsigned cbb, const void *pixels, int w, int h) noexcept; + +void loadSpritePalette(void *rendererData, const Palette &pal) noexcept; + +void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept; } diff --git a/src/nostalgia/core/userland/gfx_opengl.cpp b/src/nostalgia/core/userland/gfx_opengl.cpp index b4db9534..5f66eeab 100644 --- a/src/nostalgia/core/userland/gfx_opengl.cpp +++ b/src/nostalgia/core/userland/gfx_opengl.cpp @@ -22,55 +22,96 @@ namespace renderer { constexpr uint64_t TileRows = 128; constexpr uint64_t TileColumns = 128; constexpr uint64_t TileCount = TileRows * TileColumns; +constexpr uint64_t SpriteCount = 128; constexpr uint64_t BgVertexVboRows = 4; constexpr uint64_t BgVertexVboRowLength = 4; constexpr uint64_t BgVertexVboLength = BgVertexVboRows * BgVertexVboRowLength; constexpr uint64_t BgVertexEboLength = 6; +constexpr uint64_t SpriteVertexVboRows = 4; +constexpr uint64_t SpriteVertexVboRowLength = 5; +constexpr uint64_t SpriteVertexVboLength = SpriteVertexVboRows * SpriteVertexVboRowLength; +constexpr uint64_t SpriteVertexEboLength = 6; struct CBB: public glutils::BufferSet { bool updated = false; - - CBB() noexcept { + constexpr CBB() noexcept { vertices.resize(TileCount * BgVertexVboLength); elements.resize(TileCount * BgVertexEboLength); } }; +struct SpriteBlockset: public glutils::BufferSet { + bool updated = false; + constexpr SpriteBlockset() noexcept { + vertices.resize(SpriteCount * SpriteVertexVboLength); + elements.resize(SpriteCount * SpriteVertexEboLength); + } +}; + struct Background { bool enabled = false; unsigned cbbIdx = 0; }; +struct Sprite { + bool enabled = false; +}; + struct GlImplData { glutils::GLProgram bgShader; + glutils::GLProgram spriteShader; int64_t prevFpsCheckTime = 0; uint64_t draws = 0; ox::Array cbbs; + SpriteBlockset spriteBlocks; + ox::Array spriteStates; ox::Array backgrounds; - static constexpr std::size_t ColorCnt = 256; - ox::Array palette{}; }; -constexpr const GLchar *bgvshad = R"( +constexpr ox::StringView bgvshadTmpl = R"( {} in vec2 vTexCoord; in vec2 vPosition; out vec2 fTexCoord; uniform float vTileHeight; void main() { - gl_Position = vec4(vPosition, 0.0, 1.0); - fTexCoord = vTexCoord * vec2(1, vTileHeight); + gl_Position = vec4(vPosition, 0.0, 1.0); + fTexCoord = vTexCoord * vec2(1, vTileHeight); })"; -constexpr const GLchar *bgfshad = R"( +constexpr ox::StringView bgfshadTmpl = R"( {} out vec4 outColor; in vec2 fTexCoord; uniform sampler2D image; - uniform vec3 fPalette[256]; + uniform vec4 fPalette[256]; void main() { int idx = int(texture(image, fTexCoord).rgb.r * 256); - outColor = vec4(fPalette[idx], 1.0); + outColor = fPalette[idx]; + //outColor = vec4(0.0, 0.7, 1.0, 1.0); + })"; + +constexpr ox::StringView spritevshadTmpl = R"( + {} + in float vEnabled; + in vec2 vTexCoord; + in vec2 vPosition; + out vec2 fTexCoord; + uniform float vTileHeight; + void main() { + gl_Position = vec4(vPosition, 0.0, 1.0); + fTexCoord = vTexCoord * vec2(1, vTileHeight) * vec2(vEnabled, vEnabled); + })"; + +constexpr ox::StringView spritefshadTmpl = R"( + {} + out vec4 outColor; + in vec2 fTexCoord; + uniform sampler2D image; + uniform vec4 fPalette[256]; + void main() { + int idx = int(texture(image, fTexCoord).rgb.r * 256); + outColor = fPalette[idx]; //outColor = vec4(0.0, 0.7, 1.0, 1.0); })"; @@ -79,8 +120,46 @@ static constexpr auto bgVertexRow(unsigned x, unsigned y) noexcept { return y * TileRows + x; } -static void -setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, float *vbo, GLuint *ebo) noexcept { +static void setSpriteBufferObject(Context *ctx, + unsigned vi, + float enabled, + float x, float y, + unsigned textureRow, + unsigned flipX, + float *vbo, + GLuint *ebo) noexcept { + // don't worry, this memcpy gets optimized to something much more ideal + const auto [sw, sh] = getScreenSize(ctx); + constexpr float ymod = 0.1f; + const auto xmod = ymod * static_cast(sh) / static_cast(sw); + x *= xmod; + y *= -ymod; + x -= 1.f; + y += 1.f - ymod; + const auto textureRowf = static_cast(textureRow); + const float L = flipX ? 1 : 0; + const float R = flipX ? 0 : 1; + const ox::Array vertices { + enabled, x, y, L, textureRowf + 1, // bottom left + enabled, x + xmod, y, R, textureRowf + 1, // bottom right + enabled, x + xmod, y + ymod, R, textureRowf + 0, // top right + enabled, x, y + ymod, L, textureRowf + 0, // top left + }; + memcpy(vbo, vertices.data(), sizeof(vertices)); + const ox::Array elms { + vi + 0, vi + 1, vi + 2, + vi + 2, vi + 3, vi + 0, + }; + memcpy(ebo, elms.data(), sizeof(elms)); +} + +static void setTileBufferObject(Context *ctx, + unsigned vi, + float x, + float y, + unsigned textureRow, + float *vbo, + GLuint *ebo) noexcept { // don't worry, this memcpy gets optimized to something much more ideal const auto [sw, sh] = getScreenSize(ctx); constexpr float ymod = 2.0f / 20.0f; @@ -89,11 +168,12 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, y *= -ymod; x -= 1.0f; y += 1.0f - ymod; + const auto textureRowf = static_cast(textureRow); const ox::Array vertices { - x, y, 0, static_cast(textureRow + 1), // bottom left - x + xmod, y, 1, static_cast(textureRow + 1), // bottom right - x + xmod, y + ymod, 1, static_cast(textureRow + 0), // top right - x, y + ymod, 0, static_cast(textureRow + 0), // top left + x, y, 0, textureRowf + 1, // bottom left + x + xmod, y, 1, textureRowf + 1, // bottom right + x + xmod, y + ymod, 1, textureRowf + 0, // top right + x, y + ymod, 0, textureRowf + 0, // top left }; memcpy(vbo, vertices.data(), sizeof(vertices)); const ox::Array elms { @@ -103,6 +183,14 @@ setTileBufferObject(Context *ctx, unsigned vi, float x, float y, int textureRow, memcpy(ebo, elms.data(), sizeof(elms)); } +static void initSpriteBufferObjects(Context *ctx, glutils::BufferSet *bs) noexcept { + for (auto i = 0u; i < SpriteCount; ++i) { + auto vbo = &bs->vertices[i * static_cast(SpriteVertexVboLength)]; + auto ebo = &bs->elements[i * static_cast(SpriteVertexEboLength)]; + setSpriteBufferObject(ctx, i * SpriteVertexVboRows, 0, 0, 0, 0, false, vbo, ebo); + } +} + static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) noexcept { for (auto x = 0u; x < TileColumns; ++x) { for (auto y = 0u; y < TileRows; ++y) { @@ -114,6 +202,30 @@ static void initBackgroundBufferObjects(Context *ctx, glutils::BufferSet *bg) no } } +static void initSpritesBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bs) noexcept { + // vao + bs->vao = glutils::generateVertexArrayObject(); + glBindVertexArray(bs->vao); + // vbo & ebo + bs->vbo = glutils::generateBuffer(); + bs->ebo = glutils::generateBuffer(); + initSpriteBufferObjects(ctx, bs); + glutils::sendVbo(*bs); + glutils::sendEbo(*bs); + // vbo layout + auto enabledAttr = static_cast(glGetAttribLocation(shader, "vEnabled")); + glEnableVertexAttribArray(enabledAttr); + glVertexAttribPointer(enabledAttr, 1, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), nullptr); + auto posAttr = static_cast(glGetAttribLocation(shader, "vPosition")); + glEnableVertexAttribArray(posAttr); + glVertexAttribPointer(posAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), + reinterpret_cast(1 * sizeof(float))); + auto texCoordAttr = static_cast(glGetAttribLocation(shader, "vTexCoord")); + glEnableVertexAttribArray(texCoordAttr); + glVertexAttribPointer(texCoordAttr, 2, GL_FLOAT, GL_FALSE, SpriteVertexVboRowLength * sizeof(float), + reinterpret_cast(3 * sizeof(float))); +} + static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::BufferSet *bg) noexcept { // vao bg->vao = glutils::generateVertexArrayObject(); @@ -134,7 +246,7 @@ static void initBackgroundBufferset(Context *ctx, GLuint shader, glutils::Buffer reinterpret_cast(2 * sizeof(float))); } -static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, void *pixels) noexcept { +static glutils::GLTexture loadTexture(GLsizei w, GLsizei h, const void *pixels) noexcept { GLuint texId = 0; glGenTextures(1, &texId); glutils::GLTexture tex(texId); @@ -179,10 +291,9 @@ static void drawBackground(CBB *cbb) noexcept { static void drawBackgrounds(GlImplData *id) noexcept { // load background shader and its uniforms glUseProgram(id->bgShader); - const auto uniformPalette = static_cast(glGetUniformLocation(id->bgShader, "fPalette")); const auto uniformTileHeight = static_cast(glGetUniformLocation(id->bgShader, "vTileHeight")); - glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data()); - for (auto &bg : id->backgrounds) { + //glUniform3fv(uniformPalette, GlImplData::ColorCnt, id->palette.data()); + for (const auto &bg : id->backgrounds) { if (bg.enabled) { auto &cbb = id->cbbs[bg.cbbIdx]; const auto tileRows = cbb.tex.height / TileHeight; @@ -192,16 +303,39 @@ static void drawBackgrounds(GlImplData *id) noexcept { } } +static void drawSprites(GlImplData *id) noexcept { + glUseProgram(id->spriteShader); + auto &sb = id->spriteBlocks; + const auto uniformTileHeight = static_cast(glGetUniformLocation(id->spriteShader, "vTileHeight")); + // update vbo + glBindVertexArray(sb.vao); + if (sb.updated) { + sb.updated = false; + glutils::sendVbo(sb); + } + // set vTileHeight uniform + const auto tileRows = sb.tex.height / TileHeight; + glUniform1f(uniformTileHeight, 1.0f / static_cast(tileRows)); + // draw + glBindTexture(GL_TEXTURE_2D, sb.tex); + glDrawElements(GL_TRIANGLES, static_cast(sb.elements.size()), GL_UNSIGNED_INT, nullptr); +} + ox::Error init(Context *ctx, void **rendererData) noexcept { - const auto vshad = ox::sfmt(bgvshad, glutils::GlslVersion); - const auto fshad = ox::sfmt(bgfshad, glutils::GlslVersion); - oxRequireM(bgShader, glutils::buildShaderProgram(vshad.c_str(), fshad.c_str())); - const auto id = new GlImplData; + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + const auto bgVshad = ox::sfmt(bgvshadTmpl, glutils::GlslVersion); + const auto bgFshad = ox::sfmt(bgfshadTmpl, glutils::GlslVersion); + const auto spriteVshad = ox::sfmt(spritevshadTmpl, glutils::GlslVersion); + const auto spriteFshad = ox::sfmt(spritefshadTmpl, glutils::GlslVersion); + const auto id = ox::make(); *rendererData = id; - id->bgShader = std::move(bgShader); + oxReturnError(glutils::buildShaderProgram(bgVshad.c_str(), bgFshad.c_str()).moveTo(&id->bgShader)); + oxReturnError(glutils::buildShaderProgram(spriteVshad.c_str(), spriteFshad.c_str()).moveTo(&id->spriteShader)); for (auto &bg : id->cbbs) { initBackgroundBufferset(ctx, id->bgShader, &bg); } + initSpritesBufferset(ctx, id->spriteShader, &id->spriteBlocks); ImGui_ImplOpenGL3_Init(glutils::GlslVersion); return OxError(0); } @@ -211,20 +345,43 @@ void shutdown(Context*, void *rendererData) noexcept { ox::safeDelete(id); } -void loadBgPalette(void *rendererData, const Palette &pal) noexcept { - const auto id = static_cast(rendererData); +static void loadPalette(GLuint shaderPgrm, const Palette &pal, bool firstIsTransparent = false) noexcept { + static constexpr std::size_t ColorCnt = 256; + ox::Array palette{}; for (auto i = 0u; const auto c : pal.colors) { - id->palette[i++] = redf(c); - id->palette[i++] = greenf(c); - id->palette[i++] = bluef(c); + palette[i++] = redf(c); + palette[i++] = greenf(c); + palette[i++] = bluef(c); + palette[i++] = 255; } + if (firstIsTransparent) { + palette[3] = 0; + } + glUseProgram(shaderPgrm); + const auto uniformPalette = static_cast(glGetUniformLocation(shaderPgrm, "fPalette")); + glUniform4fv(uniformPalette, ColorCnt, palette.data()); } -void loadBgTexture(void *rendererData, unsigned cbbIdx, void *pixels, int w, int h) noexcept { +void loadBgPalette(void *rendererData, const Palette &pal) noexcept { + const auto id = static_cast(rendererData); + loadPalette(id->bgShader, pal); +} + +void loadSpritePalette(void *rendererData, const Palette &pal) noexcept { + const auto id = static_cast(rendererData); + loadPalette(id->spriteShader, pal, true); +} + +void loadBgTexture(void *rendererData, unsigned cbbIdx, const void *pixels, int w, int h) noexcept { oxTracef("nostalgia::core::gfx::gl", "loadBgTexture: { cbbIdx: {}, w: {}, h: {} }", cbbIdx, w, h); const auto id = static_cast(rendererData); - auto &tex = id->cbbs[cbbIdx].tex; - tex = loadTexture(w, h, pixels); + id->cbbs[cbbIdx].tex = loadTexture(w, h, pixels); +} + +void loadSpriteTexture(void *rendererData, const void *pixels, int w, int h) noexcept { + oxTracef("nostalgia::core::gfx::gl", "loadSpriteTexture: { w: {}, h: {} }", w, h); + const auto id = static_cast(rendererData); + id->spriteBlocks.tex = loadTexture(w, h, pixels); } } @@ -275,6 +432,7 @@ void draw(Context *ctx) noexcept { glClear(GL_COLOR_BUFFER_BIT); // render renderer::drawBackgrounds(id); + renderer::drawSprites(id); for (const auto cd : ctx->drawers) { cd->draw(ctx); } @@ -291,17 +449,31 @@ void clearTileLayer(Context *ctx, unsigned bgIdx) noexcept { bg.updated = true; } -void hideSprite(Context*, unsigned) noexcept { +void hideSprite(Context *ctx, unsigned idx) noexcept { + auto &id = *ctx->rendererData(); + auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength]; + auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength]; + renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 0, + 0, 0, 0, false, vbo, ebo); + id.spriteBlocks.updated = true; } -void setSprite(Context*, - unsigned, - unsigned, - unsigned, - unsigned, - unsigned, - unsigned, - unsigned) noexcept { +void setSprite(Context *ctx, + unsigned idx, + int x, + int y, + unsigned tileIdx, + [[maybe_unused]] unsigned spriteShape, + [[maybe_unused]] unsigned spriteSize, + unsigned flipX) noexcept { + const auto uX = static_cast(x) % 255; + const auto uY = static_cast(y) % 127; + auto &id = *ctx->rendererData(); + auto vbo = &id.spriteBlocks.vertices[idx * renderer::SpriteVertexVboLength]; + auto ebo = &id.spriteBlocks.elements[idx * renderer::SpriteVertexEboLength]; + renderer::setSpriteBufferObject(ctx, idx * renderer::SpriteVertexVboRows, 1, + static_cast(uX) / 8, static_cast(uY) / 8, tileIdx, flipX, vbo, ebo); + id.spriteBlocks.updated = true; } void setTile(Context *ctx, unsigned bgIdx, int column, int row, uint8_t tile) noexcept { diff --git a/src/nostalgia/core/userland/media.cpp b/src/nostalgia/core/userland/media.cpp index 92ebe48b..5c695e51 100644 --- a/src/nostalgia/core/userland/media.cpp +++ b/src/nostalgia/core/userland/media.cpp @@ -11,7 +11,7 @@ namespace nostalgia::core { ox::Result loadRom(ox::CRStringView path) noexcept { - std::ifstream file(toStdStringView(path), std::ios::binary | std::ios::ate); + std::ifstream file(std::string(toStdStringView(path)), std::ios::binary | std::ios::ate); if (!file.good()) { oxErrorf("Could not find ROM file: {}", path); return OxError(1, "Could not find ROM file"); diff --git a/src/nostalgia/player/app.cpp b/src/nostalgia/player/app.cpp index 3adda211..a1a192ab 100644 --- a/src/nostalgia/player/app.cpp +++ b/src/nostalgia/player/app.cpp @@ -12,14 +12,14 @@ static int spriteY = 64; static int updateHandler(core::Context *ctx) noexcept { int xmod = 0; int ymod = 0; - if (core::buttonDown(ctx, core::GamePad_Right)) { + if (core::buttonDown(ctx, core::Alpha_D) || core::buttonDown(ctx, core::GamePad_Right)) { xmod = 2; - } else if (core::buttonDown(ctx, core::GamePad_Left)) { + } else if (core::buttonDown(ctx, core::Alpha_A) || core::buttonDown(ctx, core::GamePad_Left)) { xmod = -2; } - if (core::buttonDown(ctx, core::GamePad_Down)) { + if (core::buttonDown(ctx, core::Alpha_S) || core::buttonDown(ctx, core::GamePad_Down)) { ymod = 2; - } else if (core::buttonDown(ctx, core::GamePad_Up)) { + } else if (core::buttonDown(ctx, core::Alpha_W) || core::buttonDown(ctx, core::GamePad_Up)) { ymod = -2; } if (!xmod && !ymod) { @@ -27,10 +27,10 @@ static int updateHandler(core::Context *ctx) noexcept { } spriteX += xmod; spriteY += ymod; - constexpr auto s = "nostalgia"; + constexpr ox::StringView s = "nostalgia"; for (unsigned i = 0; s[i]; ++i) { const auto c = static_cast(s[i] - ('a' - 1)); - core::setSprite(ctx, i, static_cast(spriteX) + 8 * (i + 1), static_cast(spriteY), c); + core::setSprite(ctx, i, spriteX + 8 * (static_cast(i) + 1), spriteY, c); } return 16; } @@ -53,4 +53,4 @@ ox::Error run(ox::UniquePtr fs) noexcept { core::setUpdateHandler(ctx.get(), updateHandler); core::setKeyEventHandler(ctx.get(), keyEventHandler); return core::run(ctx.get()); -} \ No newline at end of file +}